summaryrefslogtreecommitdiff
path: root/themes/src/winter
diff options
context:
space:
mode:
Diffstat (limited to 'themes/src/winter')
-rw-r--r--themes/src/winter/Snowflake.cpp189
-rw-r--r--themes/src/winter/Snowflake.h48
-rw-r--r--themes/src/winter/Windfield.cpp28
-rw-r--r--themes/src/winter/Windfield.hpp39
-rw-r--r--themes/src/winter/WinterTheme.cpp20
-rw-r--r--themes/src/winter/WinterTheme.hpp18
6 files changed, 342 insertions, 0 deletions
diff --git a/themes/src/winter/Snowflake.cpp b/themes/src/winter/Snowflake.cpp
new file mode 100644
index 0000000..57f1a8f
--- /dev/null
+++ b/themes/src/winter/Snowflake.cpp
@@ -0,0 +1,189 @@
+#include "Snowflake.h"
+#include "../Renderer2d.h"
+#include "../mathlib.h"
+#include "../list.h"
+#include <cstdio>
+
+/*
+
+ What else to do?
+
+ - Windstream that blows a certain selection of snowflakes in a loop-dee-loop pattern
+ - Snowflakes that land on the ground and melt
+ - Snowflakes that spin along the Y-axis for a three dimensional effect
+
+ */
+
+const Vector4 snowColor = Vector4(1.0, 0.98, 0.98, 1);
+const Vector2 NUM_ARMS_RANGE = Vector2(6.f, 8.f);
+const Vector2 RADIUS_RANGE = Vector2(8.f, 32.f);
+const Vector2 VELOCITY_RANGE_X = Vector2(-10.f, 10.f);
+const Vector2 VELOCITY_RANGE_Y = Vector2(-100.f, -85.f);
+const Vector2 ROTATION_VELOCITY_RANGE = Vector2(-PI / 8.f, PI / 8.f);
+const Vector2 WIND_VELOCITY_RANGE_X = Vector2(-3.f, 3.f);
+const Vector2 WIND_VELOCITY_RANGE_Y = Vector2(3.f, 10.f);
+const f32 GRAVITY = 5.f;
+
+inline void generateSnowflakeArm(f32 width, f32 height, f32 angle, matte::List<Vertex2D>* vertices, Mat4x4 transform = Mat4x4()) {
+ f32 halfWidth = width / 2.f;
+ Vector2 leftStart = transform * Vector2(-halfWidth, 0).rotate(angle);
+ Vector2 leftEnd = transform * Vector2(-halfWidth, height).rotate(angle);
+ Vector2 rightStart = transform * Vector2(halfWidth, 0).rotate(angle);
+ Vector2 rightEnd = transform * Vector2(halfWidth, height).rotate(angle);
+
+ vertices->add({ leftStart, snowColor, Mat4x4() });
+ vertices->add({ leftEnd, snowColor, Mat4x4() });
+ vertices->add({ rightEnd, snowColor, Mat4x4() });
+ vertices->add({ leftStart, snowColor, Mat4x4() });
+ vertices->add({ rightEnd, snowColor, Mat4x4() });
+ vertices->add({ rightStart, snowColor, Mat4x4() });
+}
+
+/**
+ Fills in the vertices array vertices that represent a snowflake shape. The snowflake shape consists
+ of numArms jutting out of the center radially. The center of the flake is connected. The radius is
+ used to determine the length of the arms. The first third of each arm is barren, after which branches
+ extends on either side of the arm at an angle of about 60 degrees. Each branch can itself have tiny
+ sub branches jutting out of it, but these should be not nearly as large as the regular branches.
+
+ With all of this in mind, we should be able to build a convincing snowflake.
+
+ :param vertices List of vertices to be filled in
+ :param numArms Number of arms radially sticking out of the snowflake
+ :param radius Length of the snowflake arms
+ */
+inline void generateSnowflakeShape(matte::List<Vertex2D>* vertices, i32 numArms, f32 radius, f32 armWidthRatio = 0.08f) {
+ f32 innerRadius = 0;
+ f32 outerRadius = 2 * radius;
+ f32 dx = ((2 * PI) / numArms);
+ for (i32 armIndex = 0; armIndex < numArms; armIndex++) {
+ f32 armAngle = dx * armIndex;
+ generateSnowflakeArm(armWidthRatio * radius, radius, armAngle, vertices);
+ f32 armLeftAngle = DEG_TO_RAD(60.f);
+ f32 armRightAngle = DEG_TO_RAD(-60.f);
+
+ const i32 NUM_SUB_ARMS = 4;
+ for (i32 subArmIndex = 0; subArmIndex < NUM_SUB_ARMS; subArmIndex++) {
+ f32 height = (radius / static_cast<f32>(subArmIndex));
+ f32 width = (armWidthRatio / (subArmIndex + 1)) * height;
+ f32 transY = (radius / (NUM_SUB_ARMS + 1)) * (subArmIndex + 1);
+ Vector2 translation = Vector2(0, transY).rotate(armAngle);
+ generateSnowflakeArm(width, height, armAngle, vertices, Mat4x4().translateByVec2(translation).rotate2D(armLeftAngle));
+ generateSnowflakeArm(width, height, armAngle, vertices, Mat4x4().translateByVec2(translation).rotate2D(armRightAngle));
+ }
+ }
+}
+
+inline void initFlake(SnowflakeParticleRenderer* renderer, SnowflakeUpdateData* ud) {
+ ud->radius = randomFloatBetween(RADIUS_RANGE.x, RADIUS_RANGE.y);
+ ud->vtxIdx = renderer->vertices.numElements;
+ generateSnowflakeShape(&renderer->vertices,
+ randomFloatBetween(NUM_ARMS_RANGE.x, NUM_ARMS_RANGE.y),
+ ud->radius);
+
+ ud->numVertices = renderer->vertices.numElements - ud->vtxIdx;
+ ud->velocity = Vector2(randomFloatBetween(VELOCITY_RANGE_X.x, VELOCITY_RANGE_X.y), randomFloatBetween(VELOCITY_RANGE_Y.x, VELOCITY_RANGE_Y.y));
+ ud->position = Vector2(randomFloatBetween(0, renderer->xMax), randomFloatBetween(renderer->yMax, 4 * renderer->yMax));
+ ud->rotateVelocity = randomFloatBetween(ROTATION_VELOCITY_RANGE.x, ROTATION_VELOCITY_RANGE.y);
+}
+
+void SnowflakeParticleRenderer::load(SnowflakeLoadParameters params, Renderer2d* renderer) {
+ numSnowflakes = params.numSnowflakes;
+
+ updateData = new SnowflakeUpdateData[params.numSnowflakes];
+
+ xMax = static_cast<f32>(renderer->context->width);
+ yMax = static_cast<f32>(renderer->context->height);
+
+ vertices.deallocate();
+ vertices.growDynamically = true;
+
+ // Initialize each snow flake with its shape
+ for (i32 s = 0; s < numSnowflakes; s++) {
+ auto ud = &updateData[s];
+ initFlake(this, ud);
+ }
+
+ useShader(renderer->shader);
+
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ glGenBuffers(1, &vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferData(GL_ARRAY_BUFFER, vertices.numElements * sizeof(Vertex2D), &vertices.data[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++) {
+ i32 offset = (4 * sizeof(f32)) * idx;
+ glEnableVertexAttribArray(renderer->attributes.vMatrix + idx);
+ glVertexAttribPointer(renderer->attributes.vMatrix + idx,
+ 4,
+ GL_FLOAT,
+ GL_FALSE,
+ sizeof(Vertex2D),
+ (GLvoid *)(offsetof(Vertex2D, vMatrix) + offset));
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+}
+
+inline void resetFlake(SnowflakeParticleRenderer* renderer, SnowflakeUpdateData* ud) {
+ ud->position.y = 2 * renderer->yMax;
+ ud->velocity = Vector2(randomFloatBetween(-10, 10), randomFloatBetween(-100, -85));
+ ud->rotation = 0;
+}
+
+inline void updateFlake(SnowflakeParticleRenderer* renderer, SnowflakeUpdateData* ud, i32 s, f32 dtSeconds) {
+ ud->velocity = ud->velocity + Vector2(0, -(GRAVITY * dtSeconds));
+ //if (addWind) ud->velocity += renderer->windSpeed;
+ ud->position += ud->velocity * dtSeconds;
+ ud->rotation += ud->rotateVelocity * dtSeconds;
+
+ Mat4x4 m = Mat4x4().translateByVec2(ud->position).rotate2D(ud->rotation);
+ for (i32 v = ud->vtxIdx; v < (ud->vtxIdx + ud->numVertices); v++) {
+ renderer->vertices.data[v].vMatrix = m;
+ }
+
+ if (ud->position.y <= -ud->radius) {
+ resetFlake(renderer, ud);
+ }
+}
+
+void SnowflakeParticleRenderer::update(f32 dtSeconds) {
+ timeUntilNextWindSeconds -= dtSeconds;
+ if (timeUntilNextWindSeconds < 0) {
+ timeUntilNextWindSeconds = randomFloatBetween(2.5f, 10.f);
+ }
+
+ for (i32 s = 0; s < numSnowflakes; s++) {
+ SnowflakeUpdateData* ud = &updateData[s];
+ updateFlake(this, ud, s, dtSeconds);
+ }
+}
+
+void SnowflakeParticleRenderer::render(Renderer2d* renderer) {
+ setShaderMat4(renderer->uniforms.model, model);
+
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.numElements * sizeof(Vertex2D), &vertices.data[0]);
+
+ glBindVertexArray(vao);
+ glDrawArrays(GL_TRIANGLES, 0, vertices.numElements);
+ glBindVertexArray(0);
+}
+
+void SnowflakeParticleRenderer::unload() {
+ glDeleteVertexArrays(1, &vao);
+ glDeleteBuffers(1, &vbo);
+ vao = 0;
+ vbo = 0;
+ vertices.deallocate();
+ delete [] updateData;
+}
diff --git a/themes/src/winter/Snowflake.h b/themes/src/winter/Snowflake.h
new file mode 100644
index 0000000..ad027f6
--- /dev/null
+++ b/themes/src/winter/Snowflake.h
@@ -0,0 +1,48 @@
+#ifndef SNOWFLAKE_H
+#define SNOWFLAKE_H
+
+#include "../types.h"
+#include "../mathlib.h"
+#include "../list.h"
+#include "Windfield.hpp"
+
+struct Renderer2d;
+struct Vertex2D;
+
+struct SnowflakeLoadParameters {
+ i32 numSnowflakes = 480;
+ f32 windIntervalSeconds = 1.5f;
+};
+
+struct SnowflakeUpdateData {
+ Vector2 velocity;
+ Vector2 position;
+ f32 rotateVelocity = 0.f;
+ f32 rotation = 0;
+ f32 radius;
+
+ i32 vtxIdx = 0;
+ i32 numVertices = 0;
+};
+
+struct SnowflakeParticleRenderer {
+ f32 xMax = 0;
+ f32 yMax = 0;
+ f32 windIntervalSeconds = 1.5;
+ i32 numSnowflakes = 0;
+ f32 timeUntilNextWindSeconds = 0;
+ WindField<100, 100, 10> wind;
+ SnowflakeUpdateData* updateData;
+
+ u32 vao;
+ u32 vbo;
+ Mat4x4 model;
+ matte::List<Vertex2D> vertices;
+
+ void load(SnowflakeLoadParameters params, Renderer2d* renderer);
+ void update(f32 dtSeconds);
+ void render(Renderer2d* renderer);
+ void unload();
+};
+
+#endif
diff --git a/themes/src/winter/Windfield.cpp b/themes/src/winter/Windfield.cpp
new file mode 100644
index 0000000..88fb74b
--- /dev/null
+++ b/themes/src/winter/Windfield.cpp
@@ -0,0 +1,28 @@
+#include "Windfield.hpp"
+
+
+template <i32 Width, i32 Height, i32 CellDimension>
+void WindField<Width, Height, CellDimension>::load(f32 cellSizePixels, i32 fieldWithCells, i32 fieldHeightCells, f32 ttl, Vector2 origin) {
+ this->ttl = ttl;
+ this->origin = origin;
+ this->end = this->origin + Vector2(Width * CellDimension, Height * CellDimension);
+}
+
+template <i32 Width, i32 Height, i32 CellDimension>
+bool WindField<Width, Height, CellDimension>::addVector(i32 x, i32 y, Vector2& v) {
+ field[x][y] = v;
+ return false;
+}
+
+template <i32 Width, i32 Height, i32 CellDimension>
+Vector2 WindField<Width, Height, CellDimension>::getWindFactor(Vector2& v) {
+ if (v.x >= origin.x && v.x <= end.x
+ && v.y >= origin.y && v.y <= end.y) {
+ Vector2 positionInField = v - this->origin;
+ i32 cellX = static_cast<i32>(Width / positionInField.x);
+ i32 cellY = static_cast<i32>(Height / positionInField.y);
+ return field[cellX][cellY];
+ }
+
+ return Vector2();
+}
diff --git a/themes/src/winter/Windfield.hpp b/themes/src/winter/Windfield.hpp
new file mode 100644
index 0000000..5bf0c38
--- /dev/null
+++ b/themes/src/winter/Windfield.hpp
@@ -0,0 +1,39 @@
+#ifndef WIND_FIELD_HPP
+#define WIND_FIELD_HPP
+#include "../types.h"
+#include "../mathlib.h"
+
+/**
+ A Windfield represents a field of vectors in a rectangular region.
+ The Width and Height are given in units of CellDimenions. The CellDimension
+ is given in pixels.
+ */
+template <i32 Width, i32 Height, i32 CellDimension>
+struct WindField {
+ f32 ttl = 0.f;
+ Vector2 origin;
+ Vector2 end;
+
+ /*
+ Granularity of each cell in pixels.
+ */
+ const f32 cellDimension = CellDimension;
+
+ /*
+ Width of the vector field in CellDimensions.
+ */
+ const f32 width = Width;
+
+ /*
+ Height of the vector vield in CellDimensions.
+ */
+ const f32 height = Height;
+
+ Vector2** field;
+
+ void load(f32 cellSizePixels, i32 fieldWithCells, i32 fieldHeightCells, f32 ttl, Vector2 origin);
+ bool addVector(i32 x, i32 y, Vector2& v);
+ Vector2 getWindFactor(Vector2& v);
+};
+
+#endif
diff --git a/themes/src/winter/WinterTheme.cpp b/themes/src/winter/WinterTheme.cpp
new file mode 100644
index 0000000..2686988
--- /dev/null
+++ b/themes/src/winter/WinterTheme.cpp
@@ -0,0 +1,20 @@
+#include "WinterTheme.hpp"
+#include "../Renderer2d.h"
+
+void WinterTheme::load(Renderer2d* renderer) {
+ renderer->clearColor = Vector4(200, 229, 239, 255).toNormalizedColor();
+ SnowflakeLoadParameters lp;
+ spr.load(lp, renderer);
+}
+
+void WinterTheme::update(f32 dtSeconds) {
+ spr.update(dtSeconds);
+}
+
+void WinterTheme::render(Renderer2d* renderer) {
+ spr.render(renderer);
+}
+
+void WinterTheme::unload() {
+ spr.unload();
+}
diff --git a/themes/src/winter/WinterTheme.hpp b/themes/src/winter/WinterTheme.hpp
new file mode 100644
index 0000000..5b8cc95
--- /dev/null
+++ b/themes/src/winter/WinterTheme.hpp
@@ -0,0 +1,18 @@
+#ifndef WINTER_THEME_HPP
+#define WINTER_THEME_HPP
+
+#include "Snowflake.h"
+#include "../types.h"
+
+struct Renderer2d;
+
+struct WinterTheme {
+ SnowflakeParticleRenderer spr;
+
+ void load(Renderer2d* renderer);
+ void update(f32 dtSeconds);
+ void render(Renderer2d* renderer);
+ void unload();
+};
+
+#endif \ No newline at end of file