From bf4b3a5c35152c1292757134123b3363d0f81bf6 Mon Sep 17 00:00:00 2001 From: Matt Kosarek Date: Mon, 29 Dec 2025 09:34:00 -0500 Subject: Renamed PascalCase files to snake_case --- themes/src/autumn/AutumnTheme.cpp | 73 ---------- themes/src/autumn/AutumnTheme.hpp | 33 ----- themes/src/autumn/LeafParticleRender.cpp | 166 ---------------------- themes/src/autumn/LeafParticleRender.h | 58 -------- themes/src/autumn/TreeShape.cpp | 214 ----------------------------- themes/src/autumn/TreeShape.h | 74 ---------- themes/src/autumn/autumn_theme.cpp | 73 ++++++++++ themes/src/autumn/autumn_theme.hpp | 33 +++++ themes/src/autumn/leaf_particle_render.cpp | 166 ++++++++++++++++++++++ themes/src/autumn/leaf_particle_render.h | 58 ++++++++ themes/src/autumn/tree_shape.cpp | 214 +++++++++++++++++++++++++++++ themes/src/autumn/tree_shape.h | 74 ++++++++++ 12 files changed, 618 insertions(+), 618 deletions(-) delete mode 100644 themes/src/autumn/AutumnTheme.cpp delete mode 100644 themes/src/autumn/AutumnTheme.hpp delete mode 100644 themes/src/autumn/LeafParticleRender.cpp delete mode 100644 themes/src/autumn/LeafParticleRender.h delete mode 100644 themes/src/autumn/TreeShape.cpp delete mode 100644 themes/src/autumn/TreeShape.h create mode 100644 themes/src/autumn/autumn_theme.cpp create mode 100644 themes/src/autumn/autumn_theme.hpp create mode 100644 themes/src/autumn/leaf_particle_render.cpp create mode 100644 themes/src/autumn/leaf_particle_render.h create mode 100644 themes/src/autumn/tree_shape.cpp create mode 100644 themes/src/autumn/tree_shape.h (limited to 'themes/src/autumn') diff --git a/themes/src/autumn/AutumnTheme.cpp b/themes/src/autumn/AutumnTheme.cpp deleted file mode 100644 index 4b7a2e2..0000000 --- a/themes/src/autumn/AutumnTheme.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "AutumnTheme.hpp" -#include "../shapes_2d.h" -#include - -namespace -{ - const int NUM_HILLS = 3; -} - -AutumnTheme::AutumnTheme(WebglContext* context) -{ - renderer.load(context); - load(); -} - -AutumnTheme::~AutumnTheme() -{ - unload(); -} - -void AutumnTheme::load() { - renderer.clearColor = Vector4(252, 210, 153, 255).toNormalizedColor(); - auto lr = tree.load(&renderer); - leafParticles.load(&renderer, &lr); - background = new RectangularGradient( - renderer, - Vector4{135, 206, 235, 255}.toNormalizedColor(), - Vector4(252, 210, 153, 255).toNormalizedColor(), - renderer.get_width(), - renderer.get_height(), - {0, 0}); - - - background_hill = new Circleish( - renderer, - 1000, - Vector4(137, 129, 33, 255).toNormalizedColor(), - 100, - 0, - 50); - background_hill->mesh.model = background_hill->mesh.model.translateByVec2({1200, -700}); - - tree_hill = new Circleish( - renderer, - 500, - Vector4{ 76, 75, 22, 255 }.toNormalizedColor(), - 100, - 0, - 50); - tree_hill->mesh.model = tree_hill->mesh.model.translateByVec2(Vector2(300, -290)); -} - -void AutumnTheme::update(f32 dtSeconds) { - tree.update(dtSeconds); - leafParticles.update(dtSeconds); -} - -void AutumnTheme::render() { - renderer.render(); - background->render(); - background_hill->render(); - tree.render(&renderer); - tree_hill->render(); - leafParticles.render(&renderer); -} - -void AutumnTheme::unload() { - tree.unload(); - leafParticles.unload(); - delete background; - delete background_hill; - delete tree_hill; -} diff --git a/themes/src/autumn/AutumnTheme.hpp b/themes/src/autumn/AutumnTheme.hpp deleted file mode 100644 index e3f5748..0000000 --- a/themes/src/autumn/AutumnTheme.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef AUTUMN_THEME_HPP -#define AUTUMN_THEME_HPP - -#include "TreeShape.h" -#include "LeafParticleRender.h" -#include "../types.h" -#include "../theme.h" -#include "../Renderer2d.h" -#include -#include - -class RectangularGradient; -class Circleish; - -class AutumnTheme : public Theme { -public: - AutumnTheme(WebglContext*); - ~AutumnTheme(); - TreeShape tree; - LeafParticleRender leafParticles; - RectangularGradient* background; - Circleish* tree_hill; - Circleish* background_hill; - - void load(); - void update(f32 dtSeconds); - void render(); - void unload(); -private: - Renderer2d renderer; -}; - -#endif diff --git a/themes/src/autumn/LeafParticleRender.cpp b/themes/src/autumn/LeafParticleRender.cpp deleted file mode 100644 index fee3df2..0000000 --- a/themes/src/autumn/LeafParticleRender.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "LeafParticleRender.h" -#include "../Renderer2d.h" -#include "../mathlib.h" -#include "TreeShape.h" -#include "../types.h" -#include - -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 deleted file mode 100644 index f6efe1f..0000000 --- a/themes/src/autumn/LeafParticleRender.h +++ /dev/null @@ -1,58 +0,0 @@ -#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 deleted file mode 100644 index 7c80929..0000000 --- a/themes/src/autumn/TreeShape.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "TreeShape.h" -#include "../mathlib.h" -#include -#include -#include -#include - -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, 200.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(division) / static_cast(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 deleted file mode 100644 index fc0d11e..0000000 --- a/themes/src/autumn/TreeShape.h +++ /dev/null @@ -1,74 +0,0 @@ -#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(); -}; - diff --git a/themes/src/autumn/autumn_theme.cpp b/themes/src/autumn/autumn_theme.cpp new file mode 100644 index 0000000..d88b265 --- /dev/null +++ b/themes/src/autumn/autumn_theme.cpp @@ -0,0 +1,73 @@ +#include "autumn_theme.hpp" +#include "../shapes_2d.h" +#include + +namespace +{ + const int NUM_HILLS = 3; +} + +AutumnTheme::AutumnTheme(WebglContext* context) +{ + renderer.load(context); + load(); +} + +AutumnTheme::~AutumnTheme() +{ + unload(); +} + +void AutumnTheme::load() { + renderer.clearColor = Vector4(252, 210, 153, 255).toNormalizedColor(); + auto lr = tree.load(&renderer); + leafParticles.load(&renderer, &lr); + background = new RectangularGradient( + renderer, + Vector4{135, 206, 235, 255}.toNormalizedColor(), + Vector4(252, 210, 153, 255).toNormalizedColor(), + renderer.get_width(), + renderer.get_height(), + {0, 0}); + + + background_hill = new Circleish( + renderer, + 1000, + Vector4(137, 129, 33, 255).toNormalizedColor(), + 100, + 0, + 50); + background_hill->mesh.model = background_hill->mesh.model.translateByVec2({1200, -700}); + + tree_hill = new Circleish( + renderer, + 500, + Vector4{ 76, 75, 22, 255 }.toNormalizedColor(), + 100, + 0, + 50); + tree_hill->mesh.model = tree_hill->mesh.model.translateByVec2(Vector2(300, -290)); +} + +void AutumnTheme::update(f32 dtSeconds) { + tree.update(dtSeconds); + leafParticles.update(dtSeconds); +} + +void AutumnTheme::render() { + renderer.render(); + background->render(); + background_hill->render(); + tree.render(&renderer); + tree_hill->render(); + leafParticles.render(&renderer); +} + +void AutumnTheme::unload() { + tree.unload(); + leafParticles.unload(); + delete background; + delete background_hill; + delete tree_hill; +} diff --git a/themes/src/autumn/autumn_theme.hpp b/themes/src/autumn/autumn_theme.hpp new file mode 100644 index 0000000..b61c0f3 --- /dev/null +++ b/themes/src/autumn/autumn_theme.hpp @@ -0,0 +1,33 @@ +#ifndef AUTUMN_THEME_HPP +#define AUTUMN_THEME_HPP + +#include "tree_shape.h" +#include "leaf_particle_render.h" +#include "../types.h" +#include "../theme.h" +#include "../renderer_2d.h" +#include +#include + +class RectangularGradient; +class Circleish; + +class AutumnTheme : public Theme { +public: + AutumnTheme(WebglContext*); + ~AutumnTheme(); + TreeShape tree; + LeafParticleRender leafParticles; + RectangularGradient* background; + Circleish* tree_hill; + Circleish* background_hill; + + void load(); + void update(f32 dtSeconds); + void render(); + void unload(); +private: + Renderer2d renderer; +}; + +#endif diff --git a/themes/src/autumn/leaf_particle_render.cpp b/themes/src/autumn/leaf_particle_render.cpp new file mode 100644 index 0000000..569bb2d --- /dev/null +++ b/themes/src/autumn/leaf_particle_render.cpp @@ -0,0 +1,166 @@ +#include "leaf_particle_render.h" +#include "../renderer_2d.h" +#include "../mathlib.h" +#include "tree_shape.h" +#include "../types.h" +#include + +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/leaf_particle_render.h b/themes/src/autumn/leaf_particle_render.h new file mode 100644 index 0000000..1209e1b --- /dev/null +++ b/themes/src/autumn/leaf_particle_render.h @@ -0,0 +1,58 @@ +#include "../renderer_2d.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/tree_shape.cpp b/themes/src/autumn/tree_shape.cpp new file mode 100644 index 0000000..622751b --- /dev/null +++ b/themes/src/autumn/tree_shape.cpp @@ -0,0 +1,214 @@ +#include "tree_shape.h" +#include "../mathlib.h" +#include +#include +#include +#include + +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, 200.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(division) / static_cast(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/tree_shape.h b/themes/src/autumn/tree_shape.h new file mode 100644 index 0000000..0d18415 --- /dev/null +++ b/themes/src/autumn/tree_shape.h @@ -0,0 +1,74 @@ +#include "../renderer_2d.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(); +}; + -- cgit v1.2.1