summaryrefslogtreecommitdiff
path: root/themes/src/spring
diff options
context:
space:
mode:
Diffstat (limited to 'themes/src/spring')
-rw-r--r--themes/src/spring/GrassRenderer.cpp29
-rw-r--r--themes/src/spring/GrassRenderer.hpp33
-rw-r--r--themes/src/spring/SpringTheme.cpp198
-rw-r--r--themes/src/spring/SpringTheme.hpp41
4 files changed, 301 insertions, 0 deletions
diff --git a/themes/src/spring/GrassRenderer.cpp b/themes/src/spring/GrassRenderer.cpp
new file mode 100644
index 0000000..b69d111
--- /dev/null
+++ b/themes/src/spring/GrassRenderer.cpp
@@ -0,0 +1,29 @@
+#include "GrassRenderer.hpp"
+#include "Renderer3d.h"
+
+void GrassRenderer::load(GrassRendererLoadData params, Renderer3d* renderer) {
+ const f32 COLUMN_INCREMENT = GRASS_BLADES_PER_COL / params.area.x;
+ const f32 ROW_INCREMENT = GRASS_BLADES_PER_ROW / params.area.y;
+ for (i32 r = 0; r < GRASS_BLADES_PER_ROW; r++) {
+ i32 indexOffset = r * GRASS_BLADES_PER_ROW;
+ f32 y = ROW_INCREMENT * r;
+ for (i32 c = 0; c < GRASS_BLADES_PER_COL; c++) {
+ f32 x = COLUMN_INCREMENT * c;
+ i32 index = indexOffset + c;
+ grassBlades[index].position = Vector3(x, y, 0);
+ grassBlades[index].top_offset = Vector2(0, 0);
+ }
+ }
+}
+
+void GrassRenderer::update(f32 seconds) {
+
+}
+
+void GrassRenderer::render(Renderer3d* renderer) {
+
+}
+
+void GrassRenderer::unload() {
+
+}
diff --git a/themes/src/spring/GrassRenderer.hpp b/themes/src/spring/GrassRenderer.hpp
new file mode 100644
index 0000000..8c96724
--- /dev/null
+++ b/themes/src/spring/GrassRenderer.hpp
@@ -0,0 +1,33 @@
+#ifndef GRASS_RENDERER_HPP
+#define GRASS_RENDERER_HPP
+
+#include "Renderer3d.h"
+#include "mathlib.h"
+#include "types.h"
+
+const i32 GRASS_BLADES_PER_ROW = 24;
+const i32 GRASS_BLADES_PER_COL = 24;
+const i32 NUM_GRASS_BLADES = GRASS_BLADES_PER_ROW * GRASS_BLADES_PER_COL;
+
+struct GrassRendererLoadData {
+ Vector2 origin = Vector2(0, 0);
+ Vector2 area = Vector2(480, 480);
+ f32 grassHeight = 12.f;
+};
+
+struct GrassUpdateData {
+ Vector3 position;
+ Vector2 top_offset;
+};
+
+struct GrassRenderer {
+
+ GrassUpdateData grassBlades[NUM_GRASS_BLADES];
+
+ void load(GrassRendererLoadData params, Renderer3d* renderer);
+ void update(f32 dtSeconds);
+ void render(Renderer3d* renderer);
+ void unload();
+};
+
+#endif
diff --git a/themes/src/spring/SpringTheme.cpp b/themes/src/spring/SpringTheme.cpp
new file mode 100644
index 0000000..abe8c6e
--- /dev/null
+++ b/themes/src/spring/SpringTheme.cpp
@@ -0,0 +1,198 @@
+#include "SpringTheme.hpp"
+#include "../Renderer3d.h"
+#include "../shader_fetcher.hpp"
+#include <cstdio>
+#include <emscripten/fetch.h>
+
+void onBunnySuccess(emscripten_fetch_t *fetch) {
+ SpringTheme* springTheme = (SpringTheme*)fetch->userData;
+ springTheme->state = SpringThemeState::LoadedBunny;
+ printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
+ const i32 len = fetch->numBytes;
+ springTheme->bunnyMesh = Mesh3d_fromObj(&springTheme->renderer, fetch->data, len);
+ // The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1];
+ emscripten_fetch_close(fetch); // Free data associated with the fetch.
+}
+
+void onBunnyFail(emscripten_fetch_t *fetch) {
+ printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
+ emscripten_fetch_close(fetch); // Also free data on failure.
+}
+
+inline void fetch_bunny(SpringTheme* theme) {
+ emscripten_fetch_attr_t attr;
+ emscripten_fetch_attr_init(&attr);
+ strcpy(attr.requestMethod, "GET");
+ attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
+ attr.onsuccess = onBunnySuccess;
+ attr.onerror = onBunnyFail;
+ auto* bunny_fetch = emscripten_fetch(&attr, "themes/resources/bunny.obj");
+ bunny_fetch->userData = theme;
+}
+
+inline void on_shaders_loader(ShaderFetchResult* result) {
+ SpringTheme* theme = (SpringTheme*)result->user_data;
+ theme->renderer.load(theme->renderer.context, result->vertex.c_str(), result->fragment.c_str());
+ theme->state = SpringThemeState::LoadedShader;
+ fetch_bunny(theme);
+}
+
+void SpringTheme::load(WebglContext* context) {
+ state = SpringThemeState::Loading;
+ renderer.context = context;
+ renderer.clearColor = Vector4(160, 231, 160, 255.f).toNormalizedColor();
+
+ fetch_shader(
+ {
+ "themes/src/shaders/renderer3d.vert",
+ "themes/src/shaders/renderer3d.frag"
+ },
+ on_shaders_loader,
+ this
+ );
+}
+
+inline Vector3 bunnyLerp(Vector3& start, Vector3& target, f32 t) {
+ t = 3 * t *t - 2 * t * t * t;
+ return start + ((target - start) * t);
+}
+
+inline f32 verticalHopLerp(f32 start, f32 target, f32 t) {
+ f32 ogt = t;
+ t = 3 * t *t - 2 * t * t * t;
+ if (ogt >= 0.5f) t = 1.f - t;
+ return start + ((target - start) * t);
+}
+
+inline f32 rotationLerp(f32 start, f32 target, f32 t) {
+ return start + ((target - start) * t);
+}
+
+void SpringTheme::update(f32 dtSeconds) {
+ switch (state) {
+ case SpringThemeState::Loading: return;
+ case SpringThemeState::LoadedShader: return;
+ case SpringThemeState::LoadedBunny:
+ state = SpringThemeState::Idle;
+ stateTimer = 0.f;
+ bunnyHopAnimationTimer = 0.f;
+ break;
+ case SpringThemeState::Idle: {
+ bunnyHopAnimationTimer += dtSeconds;
+ const f32 HOP_FREQUENCY = 6.f;
+
+ if (bunnyHopAnimationTimer > stateTimer) {
+ state = SpringThemeState::PreHop;
+ f32 xDir = 1;
+ f32 yDir = 1;
+ if (bunnyTarget.x > 0) xDir = -1;
+ if (bunnyTarget.z > 0) yDir = -1;
+ bunnyTarget = bunnyPosition + Vector3(randomFloatBetween(0, xDir * 25), 0, randomFloatBetween(0, yDir * 25));
+ auto direction = (bunnyTarget - bunnyPosition);
+ auto distance = direction.length();
+ direction = direction.normalize();
+ numHops = ceil(distance / HOP_FREQUENCY);
+ hopCount = 0;
+
+ targetRotation = PI - atan2(direction.y, direction.x);
+ stateTimer = ((bunnyTarget - bunnyPosition).length() / bunnySpeed) / numHops;
+ bunnyHopAnimationTimer = 0.f;
+ hopIncrement = (bunnyTarget - bunnyPosition) / numHops;
+ }
+ break;
+ }
+ case SpringThemeState::PreHop: {
+ const f32 ROTATION_TIME = 0.5f;
+ bunnyHopAnimationTimer += dtSeconds;
+ f32 current = bunnyRotation + (targetRotation - bunnyRotation) * (bunnyHopAnimationTimer / ROTATION_TIME);
+ bunnyMesh.model = Mat4x4().rotate(0, current, 0).translate(bunnyPosition);
+
+ if (bunnyHopAnimationTimer > ROTATION_TIME) {
+ bunnyRotation = targetRotation;
+ bunnyHopAnimationTimer = 0;
+ state = SpringThemeState::Hopping;
+ }
+ break;
+ }
+ case SpringThemeState::Hopping: {
+ bunnyHopAnimationTimer += dtSeconds;
+ f32 t = bunnyHopAnimationTimer / stateTimer;
+
+ Vector3 nextPosition = bunnyPosition + hopIncrement;
+ auto renderPos = bunnyLerp(bunnyPosition, nextPosition, t);
+ if ((renderPos - nextPosition).length() < 0.01f) {
+ hopCount += 1;
+ bunnyHopAnimationTimer = 0.f;
+ bunnyPosition = nextPosition;
+ }
+
+ renderPos.y = verticalHopLerp(0.f, 4.f, t);
+
+ const f32 RMAX = PI / 16.f;
+ f32 zRotation = 0;
+ f32 start = 0.f;
+ f32 end = PI / 8.f;
+ f32 startTime = 0.f;
+ f32 endTime = 0.f;
+ bool disableRot = false;
+
+ if (t >= 0.9f) {
+ disableRot = true;
+ }
+ else if (t >= 0.7f) {
+ start = -RMAX;
+ end = 0.f;
+ startTime = 0.7f;
+ endTime = 0.9f;
+ }
+ else if (t >= 0.50f) {
+ start = 0.f;
+ end = -RMAX;
+ startTime = 0.50f;
+ endTime = 0.70f;
+ }
+ else if (t >= 0.40f) {
+ disableRot = true;
+ }
+ else if (t >= 0.20f) {
+ start = RMAX;
+ end = 0.f;
+ startTime = 0.20f;
+ endTime = 0.40f;
+ }
+ else {
+ start = 0.f;
+ end = RMAX;
+ startTime = 0.f;
+ endTime = 0.20f;
+ }
+
+
+ if (!disableRot) {
+ f32 totalTime = endTime - startTime;
+ zRotation = rotationLerp(start, end, (totalTime - (endTime - t)) / totalTime);
+ }
+
+ bunnyMesh.model = Mat4x4().getZRotationMatrix(zRotation).rotate(0, bunnyRotation, 0).translate(renderPos);
+ if (hopCount == numHops) {
+ bunnyPosition = bunnyTarget;
+ bunnyHopAnimationTimer = 0.f;
+ state = SpringThemeState::Idle;
+ stateTimer = randomFloatBetween(0.5f, 1.f);
+ }
+ break;
+ }
+ }
+}
+
+void SpringTheme::render() {
+ renderer.render();
+ if (state != SpringThemeState::Loading) {
+ bunnyMesh.render(&renderer);
+ }
+}
+
+void SpringTheme::unload() {
+ renderer.unload();
+ bunnyMesh.unload();
+}
diff --git a/themes/src/spring/SpringTheme.hpp b/themes/src/spring/SpringTheme.hpp
new file mode 100644
index 0000000..0866921
--- /dev/null
+++ b/themes/src/spring/SpringTheme.hpp
@@ -0,0 +1,41 @@
+#ifndef SPRING_THEME_HPP
+#define SPRING_THEME_HPP
+
+#include "../mathlib.h"
+#include "../types.h"
+#include "../Renderer3d.h"
+
+
+enum class SpringThemeState {
+ Loading = 0,
+ LoadedShader,
+ LoadedBunny,
+ PreHop,
+ Hopping,
+ Idle
+};
+
+struct SpringTheme {
+ Renderer3d renderer;
+ SpringThemeState state;
+ f32 bunnySpeed = 5.f;
+ Vector3 bunnyPosition = Vector3(0, 0, 0);
+ Vector3 bunnyTarget = Vector3(0, 0, 0);
+ Vector3 hopIncrement = Vector3(0, 0, 0);
+
+ f32 numHops = 0;
+ f32 hopCount = 0;
+ f32 bunnyHopAnimationTimer = 0.f;
+ f32 stateTimer = 0.f;
+ f32 bunnyRotation = 0.f;
+ f32 targetRotation = 0.f;
+
+ Mesh3d bunnyMesh;
+
+ void load(WebglContext*);
+ void update(f32 dtSeconds);
+ void render();
+ void unload();
+};
+
+#endif \ No newline at end of file