summaryrefslogtreecommitdiff
path: root/themes/src/autumn
diff options
context:
space:
mode:
authormattkae <mattkae@protonmail.com>2023-04-03 06:35:27 -0400
committermattkae <mattkae@protonmail.com>2023-04-03 06:35:27 -0400
commit5d3ff36045bfb11af0f373d5ecee7c9b54e4c4f9 (patch)
tree1877c85fc652603312c1093ff31678ab807616b2 /themes/src/autumn
parent9e21251ea16e594c8ef416cf8a6b8ebbbb42a858 (diff)
Folderized all of the seasons and loading the shaders from a remote source
Diffstat (limited to 'themes/src/autumn')
-rw-r--r--themes/src/autumn/AutumnTheme.cpp23
-rw-r--r--themes/src/autumn/AutumnTheme.hpp20
-rw-r--r--themes/src/autumn/LeafParticleRender.cpp166
-rw-r--r--themes/src/autumn/LeafParticleRender.h58
-rw-r--r--themes/src/autumn/TreeShape.cpp214
-rw-r--r--themes/src/autumn/TreeShape.h74
6 files changed, 555 insertions, 0 deletions
diff --git a/themes/src/autumn/AutumnTheme.cpp b/themes/src/autumn/AutumnTheme.cpp
new file mode 100644
index 0000000..6e6fe2b
--- /dev/null
+++ b/themes/src/autumn/AutumnTheme.cpp
@@ -0,0 +1,23 @@
+#include "AutumnTheme.hpp"
+#include "../Renderer2d.h"
+
+void AutumnTheme::load(Renderer2d* renderer) {
+ renderer->clearColor = Vector4(252, 210, 153, 255).toNormalizedColor();
+ auto lr = tree.load(renderer);
+ leafParticles.load(renderer, &lr);
+}
+
+void AutumnTheme::update(f32 dtSeconds) {
+ tree.update(dtSeconds);
+ leafParticles.update(dtSeconds);
+}
+
+void AutumnTheme::render(Renderer2d* renderer) {
+ tree.render(renderer);
+ leafParticles.render(renderer);
+}
+
+void AutumnTheme::unload() {
+ tree.unload();
+ leafParticles.unload();
+} \ No newline at end of file
diff --git a/themes/src/autumn/AutumnTheme.hpp b/themes/src/autumn/AutumnTheme.hpp
new file mode 100644
index 0000000..18da959
--- /dev/null
+++ b/themes/src/autumn/AutumnTheme.hpp
@@ -0,0 +1,20 @@
+#ifndef AUTUMN_THEME_HPP
+#define AUTUMN_THEME_HPP
+
+#include "TreeShape.h"
+#include "LeafParticleRender.h"
+#include "../types.h"
+
+struct Renderer2d;
+
+struct AutumnTheme {
+ TreeShape tree;
+ LeafParticleRender leafParticles;
+
+ void load(Renderer2d* renderer);
+ void update(f32 dtSeconds);
+ void render(Renderer2d* renderer);
+ void unload();
+};
+
+#endif \ No newline at end of file
diff --git a/themes/src/autumn/LeafParticleRender.cpp b/themes/src/autumn/LeafParticleRender.cpp
new file mode 100644
index 0000000..fee3df2
--- /dev/null
+++ b/themes/src/autumn/LeafParticleRender.cpp
@@ -0,0 +1,166 @@
+#include "LeafParticleRender.h"
+#include "../Renderer2d.h"
+#include "../mathlib.h"
+#include "TreeShape.h"
+#include "../types.h"
+#include <math.h>
+
+const i32 verticesPerLeaf = 6;
+const f32 leafRadius = 3.f;
+const i32 fallChanceMax = 100;
+
+inline void updateLeaf(Vertex2D* vertices, Vector2 position, Vector4 color, f32 scale) {
+ f32 radius = scale * leafRadius;
+ Vector2 bottomLeft = Vector2(-radius, -radius) + position;
+ Vector2 bottomRight = Vector2(radius, -radius) + position;
+ Vector2 topLeft = Vector2(-radius, radius) + position;
+ Vector2 topRight = Vector2(radius, radius) + position;
+
+ vertices[0] = { bottomLeft, color, Mat4x4() };
+ vertices[1] = { bottomRight, color, Mat4x4() };
+ vertices[2] = { topLeft, color, Mat4x4() };
+ vertices[3] = { topLeft, color, Mat4x4() };
+ vertices[4] = { topRight, color, Mat4x4() };
+ vertices[5] = { bottomRight, color, Mat4x4() };
+}
+
+void LeafParticleRender::load(Renderer2d *renderer, TreeShapeLoadResult* lr) {
+ LeafParticleLoadData ld;
+ ld.numLeaves = 256;
+ numLeaves = ld.numLeaves;
+ numVertices = ld.numLeaves * verticesPerLeaf;
+
+ updateData = new LeafParticleUpdateData[numLeaves];
+ vertices = new Vertex2D[numVertices];
+
+ for (i32 leafIdx = 0; leafIdx < numLeaves; leafIdx++) {
+ i32 randomBranch = randomIntBetween(0, lr->numBranches);
+ i32 randomVertex = randomIntBetween(0, 6); // TODO: Manually entering num vertices per branch.
+ updateData[leafIdx].vertexToFollow = &lr->updateData[randomBranch].vertices[randomVertex];
+ updateData[leafIdx].fallChance = randomIntBetween(0, fallChanceMax);
+ updateData[leafIdx].color = Vector4(randomFloatBetween(0.3, 0.9), randomFloatBetween(0.1, 0.6), 0, 1);
+ updateData[leafIdx].vertexPtr = &vertices[leafIdx * verticesPerLeaf];
+ updateData[leafIdx].resetTime = randomFloatBetween(4.f, 6.f);
+ }
+
+ useShader(renderer->shader);
+
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ glGenBuffers(1, &vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(Vertex2D), &vertices[0], GL_DYNAMIC_DRAW);
+
+ glEnableVertexAttribArray(renderer->attributes.position);
+ glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0);
+
+ glEnableVertexAttribArray(renderer->attributes.color);
+ glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color));
+
+ for (i32 idx = 0; idx < 4; idx++) {
+ glEnableVertexAttribArray(renderer->attributes.vMatrix + idx);
+ glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)(offsetof(Vertex2D, vMatrix) + (idx * 16)));
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+}
+
+void LeafParticleRender::update(f32 dtSeconds) {
+ elapsedTimeSeconds += dtSeconds;
+
+ // Every time the fallIntervalSeconds passes, we remove one leaf
+ // from the tree and send it barrelling towards the earth.
+ i32 fallRoll;
+ bool didGenerateFall = false;
+ if (elapsedTimeSeconds >= fallIntervalSeconds) {
+ fallRoll = randomIntBetween(0, fallChanceMax);
+ didGenerateFall = true;
+ elapsedTimeSeconds = 0;
+ }
+
+ for (i32 leafIdx = 0; leafIdx < numLeaves; leafIdx++) {
+ auto updateDataItem = &updateData[leafIdx];
+
+ if (didGenerateFall) {
+ if (updateDataItem->state == LeafParticleState::OnTree && updateDataItem->fallChance == fallRoll) {
+ updateDataItem->state = LeafParticleState::Falling;
+ updateDataItem->fallPosition = updateDataItem->vertexToFollow->position;
+ updateDataItem->fallVerticalVelocity = -randomFloatBetween(15.f, 25.f);
+ updateDataItem->fallHorizontalFrequency = randomFloatBetween(3.f, 5.f);
+ }
+ }
+
+ switch (updateDataItem->state) {
+ case (LeafParticleState::Remerging): {
+ updateDataItem->timeElapsedSeconds += dtSeconds;
+
+ if (updateDataItem->timeElapsedSeconds >= updateDataItem->resetTime) {
+ updateDataItem->timeElapsedSeconds = 0.f;
+ updateDataItem->state = LeafParticleState::OnTree;
+ updateDataItem->color.w = 1.f;
+ updateDataItem->scale = 1.f;
+ }
+ else {
+ updateDataItem->color.w = (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime);
+ updateDataItem->scale = (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime);
+ }
+
+ updateLeaf(updateDataItem->vertexPtr, updateDataItem->vertexToFollow->position, updateDataItem->color, updateDataItem->scale);
+ break;
+ }
+ case (LeafParticleState::OnGround): {
+ updateDataItem->timeElapsedSeconds += dtSeconds;
+
+ if (updateDataItem->timeElapsedSeconds >= updateDataItem->resetTime) {
+ updateDataItem->timeElapsedSeconds = 0.f;
+ updateDataItem->color.w = 0.f;
+ updateDataItem->state = LeafParticleState::Remerging;
+ }
+ else {
+ updateDataItem->color.w = 1.f - (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime);
+ updateLeaf(updateDataItem->vertexPtr, updateDataItem->fallPosition, updateDataItem->color, updateDataItem->scale);
+ }
+ break;
+ }
+ case (LeafParticleState::Falling): {
+ updateDataItem->timeElapsedSeconds += dtSeconds;
+ const f32 xPosUpdate = cosf(updateDataItem->fallHorizontalFrequency * updateDataItem->timeElapsedSeconds);
+ updateDataItem->fallPosition.x += xPosUpdate;
+ updateDataItem->fallPosition.y += updateDataItem->fallVerticalVelocity * dtSeconds;
+ if (updateDataItem->fallPosition.y <= 50.f) { // TODO: Hardcoded ground for now
+ updateDataItem->fallPosition.y = 50.f;
+ updateDataItem->state = LeafParticleState::OnGround;
+ updateDataItem->timeElapsedSeconds = 0;
+ updateDataItem->resetTime = randomFloatBetween(2.f, 5.f); // TODO: Hardcoded reset interval
+ }
+ updateLeaf(updateDataItem->vertexPtr, updateDataItem->fallPosition, updateDataItem->color, updateDataItem->scale);
+ break;
+ }
+ case (LeafParticleState::OnTree): {
+ updateLeaf(updateDataItem->vertexPtr, updateDataItem->vertexToFollow->position, updateDataItem->color, updateDataItem->scale);
+ break;
+ }
+ }
+ }
+}
+
+void LeafParticleRender::render(Renderer2d *renderer) {
+ setShaderMat4(renderer->uniforms.model, model);
+
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Vertex2D), &vertices[0]);
+
+ glBindVertexArray(vao);
+ glDrawArrays(GL_TRIANGLES, 0, numVertices);
+ glBindVertexArray(0);
+}
+
+void LeafParticleRender::unload() {
+ glDeleteVertexArrays(1, &vao);
+ glDeleteBuffers(1, &vbo);
+ delete [] vertices;
+
+ elapsedTimeSeconds = 0;
+}
diff --git a/themes/src/autumn/LeafParticleRender.h b/themes/src/autumn/LeafParticleRender.h
new file mode 100644
index 0000000..f6efe1f
--- /dev/null
+++ b/themes/src/autumn/LeafParticleRender.h
@@ -0,0 +1,58 @@
+#include "../Renderer2d.h"
+#include "../mathlib.h"
+#include "../types.h"
+
+struct TreeShapeLoadResult;
+
+struct LeafParticleLoadData {
+ Vector2 initPosition;
+ Vector4 initColor;
+ int numLeaves = 48;
+};
+
+enum LeafParticleState {
+ OnTree,
+ Falling,
+ OnGround,
+ Remerging
+};
+
+struct LeafParticleUpdateData {
+ LeafParticleState state = LeafParticleState::Remerging;
+
+ Vertex2D* vertexToFollow = NULL;
+ Vector4 color = Vector4(1.f, 0.f, 0.f, 0.f);
+ f32 scale = 1.f;
+
+ f32 timeElapsedSeconds = 0.f;
+ i32 fallChance = -1;
+ Vector2 fallPosition;
+ f32 fallVerticalVelocity;
+ f32 fallHorizontalFrequency;
+
+ f32 resetTime = 0.f;
+
+ Vertex2D* vertexPtr = NULL;
+};
+
+struct LeafParticleRender {
+ f32 elapsedTimeSeconds = 0.5;
+ f32 fallIntervalSeconds = 1.f;
+
+ // Update data
+ i32 numLeaves = 0;
+
+ LeafParticleUpdateData* updateData = NULL;
+ Vertex2D* vertices = NULL;
+
+ // Render data
+ u32 vao;
+ u32 vbo;
+ u32 numVertices = 0;
+ Mat4x4 model;
+
+ void load(Renderer2d* renderer, TreeShapeLoadResult* lr);
+ void update(f32 dtSeconds);
+ void render(Renderer2d* renderer);
+ void unload();
+}; \ No newline at end of file
diff --git a/themes/src/autumn/TreeShape.cpp b/themes/src/autumn/TreeShape.cpp
new file mode 100644
index 0000000..9738fd5
--- /dev/null
+++ b/themes/src/autumn/TreeShape.cpp
@@ -0,0 +1,214 @@
+#include "TreeShape.h"
+#include "../mathlib.h"
+#include <cstdio>
+#include <cstdlib>
+#include <cfloat>
+#include <ctime>
+
+void TreeBranchLoadData::fillVertices(Vertex2D* vertices, int branchTier) {
+ bottomLeft = Vector2 { position.x - width / 2.f, position.y }.rotateAround(rotation, position);
+ bottomRight = Vector2 { position.x + width / 2.f, position.y }.rotateAround(rotation, position);
+ topLeft = (Vector2 { position.x - width / 2.f, position.y + height }).rotateAround(rotation, position);
+ topRight = (Vector2 { position.x + width / 2.f, position.y + height }).rotateAround(rotation, position);
+
+ topMidpoint = topLeft + (topRight - topLeft) / 2.f;
+
+ vertices[0] = { bottomLeft, color};
+ vertices[1] = { bottomRight, color};
+ vertices[2] = { topLeft, color};
+ vertices[3] = { topLeft, color};
+ vertices[4] = { topRight, color};
+ vertices[5] = { bottomRight, color};
+};
+
+TreeShapeLoadResult TreeShape::load(Renderer2d* renderer) {
+ srand ( time(NULL) );
+
+ timeElapsedSeconds = 0;
+
+ TreeLoadData ld;
+
+ numBranches = pow(ld.divisionsPerBranch, ld.numBranchLevels + 1);
+ numVertices = 6 * numBranches;
+
+ TreeBranchLoadData* generationData = new TreeBranchLoadData[numBranches];
+ updateData = new TreeBranchUpdateData[numBranches];
+ vertices = new Vertex2D[numVertices];
+
+ // The load result will contain information that we can pass on to our leaf renderer.
+ TreeShapeLoadResult lr;
+ lr.lowerBounds = Vector2(FLT_MAX, FLT_MAX);
+ lr.upperBounds = Vector2(FLT_MIN, FLT_MIN);
+ lr.updateData = updateData;
+ lr.numBranches = numBranches;
+ i32 branchIndex = 0;
+ createBranch(&ld, generationData, numBranches, &branchIndex, 0, ld.trunkWidth, ld.trunkHeight, Vector2 { 300.f, 50.f }, 0, NULL, vertices, &lr);
+
+ useShader(renderer->shader);
+
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ glGenBuffers(1, &vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(Vertex2D), &vertices[0], GL_DYNAMIC_DRAW);
+
+ glEnableVertexAttribArray(renderer->attributes.position);
+ glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0);
+
+ glEnableVertexAttribArray(renderer->attributes.color);
+ glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color));
+
+ for (i32 idx = 0; idx < 4; idx++) {
+ glEnableVertexAttribArray(renderer->attributes.vMatrix + idx);
+ glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)(offsetof(Vertex2D, vMatrix) + (idx * 16)));
+ glVertexAttribDivisor(renderer->attributes.vMatrix + idx, 1);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+
+ delete [] generationData;
+
+ return lr;
+}
+
+const f32 ninetyDegreeRotation = PI / 2.f;
+
+void TreeShape::createBranch(TreeLoadData* ld, TreeBranchLoadData* generationData, i32 numBranches, i32* branchIndex,
+ i32 branchLevel, f32 width, f32 height, Vector2 position, f32 rotation,
+ TreeBranchUpdateData* parent, Vertex2D* vertices, TreeShapeLoadResult* lr) {
+ TreeBranchLoadData* branchLoadData = &generationData[*branchIndex];
+ branchLoadData->width = width;
+ branchLoadData->height = height;
+ branchLoadData->position = position;
+ branchLoadData->rotation = rotation;
+ branchLoadData->fillVertices(&vertices[(*branchIndex) * 6], branchLevel);
+
+ // Fil in the bounds for the LeafRenderer later.
+ if (branchLoadData->topMidpoint.x > lr->upperBounds.x) {
+ lr->upperBounds.x = branchLoadData->topMidpoint.x;
+ }
+ if (branchLoadData->topMidpoint.y > lr->upperBounds.y) {
+ lr->upperBounds.y = branchLoadData->topMidpoint.y;
+ }
+ if (branchLoadData->topMidpoint.x < lr->lowerBounds.x) {
+ lr->lowerBounds.x = branchLoadData->topMidpoint.x;
+ }
+ if (branchLoadData->topMidpoint.y < lr->lowerBounds.y) {
+ lr->lowerBounds.y = branchLoadData->topMidpoint.y;
+ }
+
+ TreeBranchUpdateData* branchUpdateData = &updateData[*branchIndex];
+ branchUpdateData->tier = branchLevel;
+ branchUpdateData->periodOffset = randomFloatBetween(0.f, 2.f * PI);
+ branchUpdateData->period = randomFloatBetween(3.f, 5.f);
+ branchUpdateData->amplitude = randomFloatBetween(0.01f, 0.05f);
+ branchUpdateData->branchToFollow = parent;
+ branchUpdateData->vertices = &vertices[(*branchIndex) * 6];
+
+ if (branchLevel == ld->numBranchLevels) {
+ return;
+ }
+
+ for (int division = 0; division < ld->divisionsPerBranch; division++) {
+ // Weight between [0, 1]
+ float weight = static_cast<f32>(division) / static_cast<f32>(ld->divisionsPerBranch - 1);
+
+ // Normalize the weight between [-1, 1]
+ f32 normalizedWeight = (0.5f - (weight)) * 2.f;
+
+ // We want a rotation that takes the current rotation of the branch, and averages it between the two branches.
+ f32 branchRotationAmount = randomFloatBetween(PI / 8.f, PI / 3.f);
+ f32 branchRotation = branchLoadData->rotation + (normalizedWeight * branchRotationAmount);
+
+ // Since trees are taller vertically, we will find a normalized value that describes how far the direction is from
+ // being horizontal. If it is closer to 1, we will make the branch taller on average.
+ f32 verticalHeightScaler = (fabs(fabs(branchRotation) - ninetyDegreeRotation) / ninetyDegreeRotation) * 0.1;
+ f32 branchWidth = width * randomFloatBetween(ld->trunkWidthScalerMin, ld->trunkWidthScalerMax);
+ f32 branchHeight = height * randomFloatBetween(ld->trunkHeightScalerMin + verticalHeightScaler, ld->trunkHeightScalerMax + verticalHeightScaler);
+
+
+ // We want the branch to start within the previous branch, so we drop it down into it based off of the rotation.
+ Vector2 branchOffsetVertical = Vector2{ 0, branchWidth }.rotate(branchRotation);
+
+ Vector2 branchPosition = branchLoadData->topLeft + ((branchLoadData->topRight - branchLoadData->topLeft) * weight) - branchOffsetVertical; // Position of branch along the top of the parent branch
+
+ (*branchIndex)++;
+ createBranch(ld, generationData, numBranches, branchIndex, branchLevel + 1, branchWidth, branchHeight, branchPosition, branchRotation, branchUpdateData, vertices, lr);
+ }
+}
+
+void TreeShape::update(f32 dtSeconds) {
+ timeElapsedSeconds += dtSeconds;
+
+ for (i32 bIdx = 0; bIdx < numBranches; bIdx++) {
+ TreeBranchUpdateData* branchUpdataData = &updateData[bIdx];
+
+ // Fade in simulation. We fade in based on the tier.
+ f32 animationStart = (branchUpdataData->tier * animateStaggerPerTier);
+ f32 animationEnd = animationStart + animateTimePerTier;
+
+ f32 alpha = 0.f;
+ if (timeElapsedSeconds < animationStart) {
+ alpha = 0.f;
+ }
+ else if (timeElapsedSeconds > animationEnd) {
+ alpha = 1.f;
+ }
+ else {
+ alpha = (1.f - (animationEnd - timeElapsedSeconds)) / animateTimePerTier;
+ }
+
+ i32 startParentIndex = bIdx * 6;
+
+ branchUpdataData->currentOffset.x = branchUpdataData->amplitude * cosf(branchUpdataData->periodOffset + branchUpdataData->period * timeElapsedSeconds);
+ branchUpdataData->currentOffset.y = branchUpdataData->amplitude * sinf(branchUpdataData->periodOffset + branchUpdataData->period * timeElapsedSeconds);
+
+ if (branchUpdataData->branchToFollow != NULL) {
+ branchUpdataData->currentOffset += branchUpdataData->branchToFollow->currentOffset;
+
+ // The root of the branch only moves according to the change of the end of the parent.
+ branchUpdataData->vertices[0].color.w = alpha;
+ branchUpdataData->vertices[0].position += branchUpdataData->branchToFollow->currentOffset;
+ branchUpdataData->vertices[1].color.w = alpha;
+ branchUpdataData->vertices[1].position += branchUpdataData->branchToFollow->currentOffset;
+ branchUpdataData->vertices[5].color.w = alpha;
+ branchUpdataData->vertices[5].position += branchUpdataData->branchToFollow->currentOffset;
+ }
+ else {
+ branchUpdataData->vertices[0].color.w = alpha;
+ branchUpdataData->vertices[1].color.w = alpha;
+ branchUpdataData->vertices[5].color.w = alpha;
+ }
+
+
+ branchUpdataData->vertices[2].color.w = alpha;
+ branchUpdataData->vertices[2].position += branchUpdataData->currentOffset;
+ branchUpdataData->vertices[3].color.w = alpha;
+ branchUpdataData->vertices[3].position += branchUpdataData->currentOffset;
+ branchUpdataData->vertices[4].color.w = alpha;
+ branchUpdataData->vertices[4].position += branchUpdataData->currentOffset;
+ }
+}
+
+void TreeShape::render(Renderer2d* renderer) {
+ setShaderMat4(renderer->uniforms.model, model);
+
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Vertex2D), &vertices[0]);
+
+ glBindVertexArray(vao);
+ glDrawArrays(GL_TRIANGLES, 0, numVertices);
+ glBindVertexArray(0);
+}
+
+void TreeShape::unload() {
+ glDeleteVertexArrays(1, &vao);
+ glDeleteBuffers(1, &vbo);
+ delete[] vertices;
+ delete [] updateData;
+ timeElapsedSeconds = 0;
+ vertices = NULL;
+ updateData = NULL;
+}
diff --git a/themes/src/autumn/TreeShape.h b/themes/src/autumn/TreeShape.h
new file mode 100644
index 0000000..fc0d11e
--- /dev/null
+++ b/themes/src/autumn/TreeShape.h
@@ -0,0 +1,74 @@
+#include "../Renderer2d.h"
+#include "../types.h"
+#include "../mathlib.h"
+
+struct TreeLoadData {
+ f32 trunkHeight = 96.f; // Height of the trunk start
+ f32 trunkWidth = 32.f; // Width of the trunk start
+ f32 trunkHeightScalerMin = 0.7f;
+ f32 trunkHeightScalerMax = 0.8f;
+ f32 trunkWidthScalerMin = 0.35f;
+ f32 trunkWidthScalerMax = 0.75f;
+ i32 divisionsPerBranch = 2; // How many branches to split into at each branch split
+ i32 numBranchLevels = 8; // How many branch levels to display
+};
+
+struct TreeBranchLoadData {
+ f32 width = 0.f;
+ f32 height = 0.f;
+ Vector2 position; // Center point
+ f32 rotation = 0; // How much we are rotated off of the center point in radians
+ Vector4 color = Vector4(101,56,24, 1.f).toNormalizedColor();
+
+ // Calculated while filling in vertices
+ Vector2 bottomLeft;
+ Vector2 bottomRight;
+ Vector2 topLeft;
+ Vector2 topRight;
+ Vector2 topMidpoint;
+
+ void fillVertices(Vertex2D* vertices, int branchTier);
+};
+
+struct TreeBranchUpdateData {
+ i32 tier = 0;
+ f32 periodOffset = 0;
+ f32 period = 0;
+ f32 amplitude = 0;
+ Vector2 currentOffset;
+ Vertex2D* vertices = NULL;
+ TreeBranchUpdateData* branchToFollow = NULL;
+};
+
+struct TreeShapeLoadResult {
+ Vector2 lowerBounds;
+ Vector2 upperBounds;
+ Vector2 center;
+ TreeBranchUpdateData* updateData;
+ u32 numBranches = 0;
+};
+
+struct TreeShape {
+ // Update data
+ TreeBranchUpdateData* updateData = NULL;
+ Vertex2D* vertices = NULL;
+ f32 timeElapsedSeconds = 0.f;
+ f32 animateTimePerTier = 1.f;
+ f32 animateStaggerPerTier = 0.2f;
+ u32 numBranches = 0;
+
+ // Render data
+ u32 vao;
+ u32 vbo;
+ u32 numVertices = 0;
+ Mat4x4 model;
+
+ TreeShapeLoadResult load(Renderer2d* renderer);
+ void createBranch(TreeLoadData* ld, TreeBranchLoadData* branchList, i32 numBranches,
+ i32* branchIndex, i32 branchLevel, f32 width, f32 height,
+ Vector2 position, f32 rotation, TreeBranchUpdateData* parent, Vertex2D* vertices, TreeShapeLoadResult* lr);
+ void update(f32 dtSeconds);
+ void render(Renderer2d* renderer);
+ void unload();
+};
+