summaryrefslogtreecommitdiff
path: root/themes/src
diff options
context:
space:
mode:
Diffstat (limited to 'themes/src')
-rw-r--r--themes/src/Renderer3d.cpp42
-rw-r--r--themes/src/Renderer3d.h12
-rw-r--r--themes/src/autumn/AutumnTheme.cpp23
-rw-r--r--themes/src/autumn/AutumnTheme.hpp20
-rw-r--r--themes/src/autumn/LeafParticleRender.cpp (renamed from themes/src/LeafParticleRender.cpp)6
-rw-r--r--themes/src/autumn/LeafParticleRender.h (renamed from themes/src/LeafParticleRender.h)6
-rw-r--r--themes/src/autumn/TreeShape.cpp (renamed from themes/src/TreeShape.cpp)2
-rw-r--r--themes/src/autumn/TreeShape.h (renamed from themes/src/TreeShape.h)6
-rw-r--r--themes/src/main.cpp283
-rw-r--r--themes/src/shader_fetcher.cpp69
-rw-r--r--themes/src/shader_fetcher.hpp19
-rw-r--r--themes/src/shaders/renderer2d.frag0
-rw-r--r--themes/src/shaders/renderer2d.vert0
-rw-r--r--themes/src/shaders/renderer3d.frag7
-rw-r--r--themes/src/shaders/renderer3d.vert15
-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
-rw-r--r--themes/src/summer/SummerTheme.cpp (renamed from themes/src/SummerTheme.cpp)6
-rw-r--r--themes/src/summer/SummerTheme.h (renamed from themes/src/SummerTheme.h)4
-rw-r--r--themes/src/winter/Snowflake.cpp (renamed from themes/src/Snowflake.cpp)8
-rw-r--r--themes/src/winter/Snowflake.h (renamed from themes/src/Snowflake.h)8
-rw-r--r--themes/src/winter/Windfield.cpp (renamed from themes/src/Windfield.cpp)4
-rw-r--r--themes/src/winter/Windfield.hpp (renamed from themes/src/Windfield.hpp)5
-rw-r--r--themes/src/winter/WinterTheme.cpp20
-rw-r--r--themes/src/winter/WinterTheme.hpp18
27 files changed, 544 insertions, 340 deletions
diff --git a/themes/src/Renderer3d.cpp b/themes/src/Renderer3d.cpp
index 5f9ce88..00315de 100644
--- a/themes/src/Renderer3d.cpp
+++ b/themes/src/Renderer3d.cpp
@@ -8,32 +8,8 @@
// Note: In the 'transform' attribute, the transform.x is the scale,
// transform.y is the rotation, and transform.zw is the translatiob.
-const char* vertexShader =
- "attribute vec4 position; \n"
- "attribute vec4 color; \n"
- "attribute vec4 normal; \n"
- "uniform mat4 projection; \n"
- "uniform mat4 view; \n"
- "uniform mat4 model; \n"
- "varying lowp vec4 VertexColor; \n"
- "varying lowp vec4 VertexNormal; \n"
- "void main() { \n"
- " vec4 fragmentPosition = projection * view * model * position; \n"
- " gl_Position = fragmentPosition; \n"
- " VertexColor = color; \n"
- " VertexNormal = normal; \n"
- "}";
-
-const char* fragmentShader =
- "varying lowp vec4 VertexColor; \n"
- "varying lowp vec4 VertexNormal; \n"
- "void main() { \n"
- " const lowp vec3 lightDirection = vec3(0.0, 1.0, 0.0);\n"
- " gl_FragColor = vec4(VertexColor.xyz * dot(VertexNormal.xyz, lightDirection), 1); \n"
- "}";
-
EM_BOOL onScreenSizeChanged_3D(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) {
- Renderer3D* renderer = (Renderer3D*)userData;
+ Renderer3d* renderer = (Renderer3d*)userData;
EMSCRIPTEN_RESULT result = emscripten_set_canvas_element_size( renderer->context->query, uiEvent->documentBodyClientWidth, uiEvent->documentBodyClientHeight);
if (result != EMSCRIPTEN_RESULT_SUCCESS) {
@@ -44,9 +20,9 @@ EM_BOOL onScreenSizeChanged_3D(int eventType, const EmscriptenUiEvent *uiEvent,
return true;
}
-void Renderer3D::load(WebglContext* inContext) {
+void Renderer3d::load(WebglContext* inContext, const char* vertexShader, const char* fragmentShader) {
context = inContext;
- printf("Compiling Renderer2d shader...\n");
+ printf("Compiling Renderer3d shader...\n");
shader = loadShader(vertexShader, fragmentShader);
useShader(shader);
@@ -59,12 +35,12 @@ void Renderer3D::load(WebglContext* inContext) {
projection = Mat4x4().getPerspectiveProjection(0.1, 1000.f, 0.872f, static_cast<f32>(context->width) / static_cast<f32>(context->height));
view = Mat4x4().getLookAt({ 0, 25, 75 }, { 0, 15, 0 }, { 0, 1, 0 });
- logger_info("Renderer2d shader compiled.\n");
+ logger_info("Renderer3d shader compiled.\n");
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, onScreenSizeChanged_3D);
}
-void Renderer3D::render() {
+void Renderer3d::render() {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
@@ -78,7 +54,7 @@ void Renderer3D::render() {
setShaderMat4(uniforms.view, view);
}
-void Renderer3D::unload() {
+void Renderer3d::unload() {
glClearColor(0.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
glDeleteProgram(shader);
@@ -126,7 +102,7 @@ inline i32 readToken(i32 i, const char* content, char* output) {
return i;
}
-Mesh3d Mesh3d_fromObj(Renderer3D* renderer, const char* content, const i32 len) {
+Mesh3d Mesh3d_fromObj(Renderer3d* renderer, const char* content, const i32 len) {
Mesh3d result;
result.vertices.allocate(2048);
result.indices.allocate(2048);
@@ -218,7 +194,7 @@ Mesh3d Mesh3d_fromObj(Renderer3D* renderer, const char* content, const i32 len)
return result;
}
-void Mesh3d::load(Renderer3D* renderer) {
+void Mesh3d::load(Renderer3d* renderer) {
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
@@ -254,7 +230,7 @@ void Mesh3d::unload() {
indices.deallocate();
}
-void Mesh3d::render(Renderer3D* renderer) {
+void Mesh3d::render(Renderer3d* renderer) {
setShaderMat4(renderer->uniforms.model, model);
glBindVertexArray(vao);
diff --git a/themes/src/Renderer3d.h b/themes/src/Renderer3d.h
index 7e89c93..5b2c8c8 100644
--- a/themes/src/Renderer3d.h
+++ b/themes/src/Renderer3d.h
@@ -5,7 +5,7 @@
#include "types.h"
#include <string>
-struct Renderer3D;
+struct Renderer3d;
struct Vertex3d {
Vector4 position;
@@ -21,13 +21,13 @@ struct Mesh3d {
matte::List<u32> indices;
Mat4x4 model;
- void load(Renderer3D* renderer);
- void render(Renderer3D* renderer);
+ void load(Renderer3d* renderer);
+ void render(Renderer3d* renderer);
void unload();
};
struct WebglContext;
-struct Renderer3D {
+struct Renderer3d {
WebglContext* context = NULL;
Mat4x4 projection;
Mat4x4 view;
@@ -46,11 +46,11 @@ struct Renderer3D {
i32 model;
} uniforms;
- void load(WebglContext* context);
+ void load(WebglContext* context, const char* vertexShader, const char* fragmentShader);
void render();
void unload();
};
-Mesh3d Mesh3d_fromObj(Renderer3D* renderer, const char* content, const i32 len);
+Mesh3d Mesh3d_fromObj(Renderer3d* renderer, const char* content, const i32 len);
#endif
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/LeafParticleRender.cpp b/themes/src/autumn/LeafParticleRender.cpp
index 0c6fbca..fee3df2 100644
--- a/themes/src/LeafParticleRender.cpp
+++ b/themes/src/autumn/LeafParticleRender.cpp
@@ -1,8 +1,8 @@
#include "LeafParticleRender.h"
-#include "Renderer2d.h"
-#include "mathlib.h"
+#include "../Renderer2d.h"
+#include "../mathlib.h"
#include "TreeShape.h"
-#include "types.h"
+#include "../types.h"
#include <math.h>
const i32 verticesPerLeaf = 6;
diff --git a/themes/src/LeafParticleRender.h b/themes/src/autumn/LeafParticleRender.h
index 713d9f6..f6efe1f 100644
--- a/themes/src/LeafParticleRender.h
+++ b/themes/src/autumn/LeafParticleRender.h
@@ -1,6 +1,6 @@
-#include "Renderer2d.h"
-#include "mathlib.h"
-#include "types.h"
+#include "../Renderer2d.h"
+#include "../mathlib.h"
+#include "../types.h"
struct TreeShapeLoadResult;
diff --git a/themes/src/TreeShape.cpp b/themes/src/autumn/TreeShape.cpp
index a3ae8f7..9738fd5 100644
--- a/themes/src/TreeShape.cpp
+++ b/themes/src/autumn/TreeShape.cpp
@@ -1,5 +1,5 @@
#include "TreeShape.h"
-#include "mathlib.h"
+#include "../mathlib.h"
#include <cstdio>
#include <cstdlib>
#include <cfloat>
diff --git a/themes/src/TreeShape.h b/themes/src/autumn/TreeShape.h
index 32b00d3..fc0d11e 100644
--- a/themes/src/TreeShape.h
+++ b/themes/src/autumn/TreeShape.h
@@ -1,6 +1,6 @@
-#include "Renderer2d.h"
-#include "types.h"
-#include "mathlib.h"
+#include "../Renderer2d.h"
+#include "../types.h"
+#include "../mathlib.h"
struct TreeLoadData {
f32 trunkHeight = 96.f; // Height of the trunk start
diff --git a/themes/src/main.cpp b/themes/src/main.cpp
index f8771d4..4e1a646 100644
--- a/themes/src/main.cpp
+++ b/themes/src/main.cpp
@@ -1,13 +1,12 @@
#include "WebglContext.h"
#include "MainLoop.h"
#include "Renderer2d.h"
-#include "Renderer3d.h"
#include "mathlib.h"
#include "types.h"
-#include "TreeShape.h"
-#include "SummerTheme.h"
-#include "LeafParticleRender.h"
-#include "Snowflake.h"
+#include "summer/SummerTheme.h"
+#include "autumn/AutumnTheme.hpp"
+#include "spring/SpringTheme.hpp"
+#include "winter/WinterTheme.hpp"
#include <cstdio>
#include <emscripten/fetch.h>
@@ -20,55 +19,6 @@ enum Theme {
Summer
};
-struct AutumnTheme {
- TreeShape tree;
- LeafParticleRender leafParticles;
-
- void load(Renderer2d* renderer);
- void update(f32 dtSeconds);
- void render(Renderer2d* renderer);
- void unload();
-};
-
-struct WinterTheme {
- SnowflakeParticleRenderer spr;
-
- void load(Renderer2d* renderer);
- void update(f32 dtSeconds);
- void render(Renderer2d* renderer);
- void unload();
-};
-
-enum class BunnyAnimationState {
- Loading = 0,
- Loaded,
- PreHop,
- Hopping,
- Idle
-};
-
-struct SpringTheme {
- BunnyAnimationState 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(Renderer3D* renderer);
- void update(f32 dtSeconds);
- void render(Renderer3D* renderer);
- void unload();
-};
-
void load(Theme theme);
void unload();
void update(f32 dtSeconds, void* userData);
@@ -80,7 +30,6 @@ EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void
WebglContext context;
Renderer2d renderer2d;
-Renderer3D renderer3d;
MainLoop mainLoop;
Theme activeTheme = Theme::Default;
AutumnTheme autumnTheme;
@@ -120,10 +69,10 @@ void load(Theme theme) {
renderer2d.load(&context);
winterTheme.load(&renderer2d);
break;
- case Theme::Spring:
- renderer3d.load(&context);
- springTheme.load(&renderer3d);
+ case Theme::Spring: {
+ springTheme.load(&context);
break;
+ }
case Theme::Summer:
renderer2d.load(&context);
summerTheme.load(&renderer2d);
@@ -163,8 +112,7 @@ void update(f32 dtSeconds, void* userData) {
winterTheme.render(&renderer2d);
break;
case Theme::Spring:
- renderer3d.render();
- springTheme.render(&renderer3d);
+ springTheme.render();
break;
case Theme::Summer:
renderer2d.render();
@@ -197,7 +145,6 @@ void unload() {
if (mainLoop.isRunning) {
mainLoop.stop();
renderer2d.unload();
- renderer3d.unload();
}
}
@@ -230,216 +177,4 @@ EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void
printf("Summer theme selected\n");
load(Theme::Summer);
return true;
-}
-
-// -- Autumn theme
-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();
-}
-
-// -- Winter theme
-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();
-}
-
-// -- Spring theme
-void onBunnySuccess(emscripten_fetch_t *fetch) {
- springTheme.state = BunnyAnimationState::Loaded;
- printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
- const i32 len = fetch->numBytes;
- springTheme.bunnyMesh = Mesh3d_fromObj(&renderer3d, 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.
-}
-
-void SpringTheme::load(Renderer3D* renderer) {
- springTheme.state = BunnyAnimationState::Loading;
- renderer->clearColor = Vector4(160, 231, 160, 255.f).toNormalizedColor();
-
- 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;
- emscripten_fetch(&attr, "themes/resources/bunny.obj");
-}
-
-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 BunnyAnimationState::Loading: return;
- case BunnyAnimationState::Loaded:
- state = BunnyAnimationState::Idle;
- stateTimer = 0.f;
- bunnyHopAnimationTimer = 0.f;
- break;
- case BunnyAnimationState::Idle: {
- bunnyHopAnimationTimer += dtSeconds;
- const f32 HOP_FREQUENCY = 6.f;
-
- if (bunnyHopAnimationTimer > stateTimer) {
- state = BunnyAnimationState::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 BunnyAnimationState::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 = BunnyAnimationState::Hopping;
- }
- break;
- }
- case BunnyAnimationState::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 = BunnyAnimationState::Idle;
- stateTimer = randomFloatBetween(0.5f, 1.f);
- }
- break;
- }
- }
-}
-
-void SpringTheme::render(Renderer3D* renderer) {
- renderer->render();
- if (state != BunnyAnimationState::Loading) {
- bunnyMesh.render(renderer);
- }
-}
-
-void SpringTheme::unload() {
- bunnyMesh.unload();
-}
+} \ No newline at end of file
diff --git a/themes/src/shader_fetcher.cpp b/themes/src/shader_fetcher.cpp
new file mode 100644
index 0000000..19ef983
--- /dev/null
+++ b/themes/src/shader_fetcher.cpp
@@ -0,0 +1,69 @@
+#include "shader_fetcher.hpp"
+#include "types.h"
+#include <cstdio>
+#include <emscripten/fetch.h>
+
+struct FetchtimeData {
+ ShaderFetchResult result;
+ ShaderFetchPaths paths_data;
+ void (*cb)(ShaderFetchResult*);
+};
+
+void on_failure(emscripten_fetch_t *fetch) {
+ FetchtimeData* ftd = (FetchtimeData*)fetch->userData;
+ printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
+ emscripten_fetch_close(fetch); // Also free data on failure.
+ ftd->cb(nullptr);
+}
+
+void on_fragment_shader(emscripten_fetch_t *fetch) {
+ FetchtimeData* ftd = (FetchtimeData*)fetch->userData;
+ printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
+ const i32 len = fetch->numBytes;
+ char* data = (char*)fetch->data;
+ data[len - 1] = '\0';
+ ftd->result.fragment = data;
+ emscripten_fetch_close(fetch); // Free data associated with the fetch.
+
+ ftd->cb(&ftd->result);
+ delete ftd;
+}
+
+void on_vertex_shader(emscripten_fetch_t *fetch) {
+
+ FetchtimeData* ftd = (FetchtimeData*)fetch->userData;
+ printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
+ const i32 len = fetch->numBytes;
+ char* data = (char*)fetch->data;
+ data[len - 1] = '\0';
+
+ ftd->result.vertex = data;
+ emscripten_fetch_close(fetch); // Free data associated with the fetch.
+
+ // Fetch fragment shader next
+ emscripten_fetch_attr_t attr;
+ emscripten_fetch_attr_init(&attr);
+ strcpy(attr.requestMethod, "GET");
+ attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
+ attr.onsuccess = on_fragment_shader;
+ attr.onerror = on_failure;
+ auto* request = emscripten_fetch(&attr, ftd->paths_data.fragment);
+ request->userData = ftd;
+}
+
+void fetch_shader(ShaderFetchPaths paths, void (*cb)(ShaderFetchResult*), void* user_data) {
+ FetchtimeData* ftd = new FetchtimeData();
+ ftd->cb = cb;
+ ftd->paths_data = paths;
+ ftd->result.user_data = user_data;
+
+ // Fetch vertex shader
+ emscripten_fetch_attr_t attr;
+ emscripten_fetch_attr_init(&attr);
+ strcpy(attr.requestMethod, "GET");
+ attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
+ attr.onsuccess = on_vertex_shader;
+ attr.onerror = on_failure;
+ auto* request = emscripten_fetch(&attr, paths.vertex);
+ request->userData = ftd;
+} \ No newline at end of file
diff --git a/themes/src/shader_fetcher.hpp b/themes/src/shader_fetcher.hpp
new file mode 100644
index 0000000..aef25b4
--- /dev/null
+++ b/themes/src/shader_fetcher.hpp
@@ -0,0 +1,19 @@
+#ifndef SHADER_FETCHER_HPP
+#define SHADER_FETCHER_HPP
+
+#include <string>
+
+struct ShaderFetchPaths {
+ const char* vertex;
+ const char* fragment;
+};
+
+struct ShaderFetchResult {
+ std::string vertex;
+ std::string fragment;
+ void* user_data;
+};
+
+void fetch_shader(ShaderFetchPaths, void (*cb)(ShaderFetchResult*), void* user_data = nullptr);
+
+#endif \ No newline at end of file
diff --git a/themes/src/shaders/renderer2d.frag b/themes/src/shaders/renderer2d.frag
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/shaders/renderer2d.frag
diff --git a/themes/src/shaders/renderer2d.vert b/themes/src/shaders/renderer2d.vert
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/shaders/renderer2d.vert
diff --git a/themes/src/shaders/renderer3d.frag b/themes/src/shaders/renderer3d.frag
new file mode 100644
index 0000000..2f50347
--- /dev/null
+++ b/themes/src/shaders/renderer3d.frag
@@ -0,0 +1,7 @@
+varying lowp vec4 VertexColor;
+varying lowp vec4 VertexNormal;
+
+void main() {
+ const lowp vec3 lightDirection = vec3(0.0, 1.0, 0.0);
+ gl_FragColor = vec4(VertexColor.xyz * dot(VertexNormal.xyz, lightDirection), 1);
+}
diff --git a/themes/src/shaders/renderer3d.vert b/themes/src/shaders/renderer3d.vert
new file mode 100644
index 0000000..026285f
--- /dev/null
+++ b/themes/src/shaders/renderer3d.vert
@@ -0,0 +1,15 @@
+attribute vec4 position;
+attribute vec4 color;
+attribute vec4 normal;
+uniform mat4 projection;
+uniform mat4 view;
+uniform mat4 model;
+varying lowp vec4 VertexColor;
+varying lowp vec4 VertexNormal;
+
+void main() {
+ vec4 fragmentPosition = projection * view * model * position;
+ gl_Position = fragmentPosition;
+ VertexColor = color;
+ VertexNormal = normal;
+}
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
diff --git a/themes/src/SummerTheme.cpp b/themes/src/summer/SummerTheme.cpp
index 20bb310..406cd22 100644
--- a/themes/src/SummerTheme.cpp
+++ b/themes/src/summer/SummerTheme.cpp
@@ -1,7 +1,7 @@
#include "SummerTheme.h"
-#include "Renderer2d.h"
-#include "list.h"
-#include "mathlib.h"
+#include "../Renderer2d.h"
+#include "../list.h"
+#include "../mathlib.h"
#include <vector>
void SummerTheme::load(Renderer2d* renderer) {
diff --git a/themes/src/SummerTheme.h b/themes/src/summer/SummerTheme.h
index 1d9093a..4a9f76b 100644
--- a/themes/src/SummerTheme.h
+++ b/themes/src/summer/SummerTheme.h
@@ -1,6 +1,6 @@
#pragma once
-#include "types.h"
-#include "Renderer2d.h"
+#include "../types.h"
+#include "../Renderer2d.h"
#include <vector>
struct Sun {
diff --git a/themes/src/Snowflake.cpp b/themes/src/winter/Snowflake.cpp
index 452a716..57f1a8f 100644
--- a/themes/src/Snowflake.cpp
+++ b/themes/src/winter/Snowflake.cpp
@@ -1,7 +1,7 @@
#include "Snowflake.h"
-#include "Renderer2d.h"
-#include "mathlib.h"
-#include "list.h"
+#include "../Renderer2d.h"
+#include "../mathlib.h"
+#include "../list.h"
#include <cstdio>
/*
@@ -142,7 +142,7 @@ inline void resetFlake(SnowflakeParticleRenderer* renderer, SnowflakeUpdateData*
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;
+ //if (addWind) ud->velocity += renderer->windSpeed;
ud->position += ud->velocity * dtSeconds;
ud->rotation += ud->rotateVelocity * dtSeconds;
diff --git a/themes/src/Snowflake.h b/themes/src/winter/Snowflake.h
index c147469..ad027f6 100644
--- a/themes/src/Snowflake.h
+++ b/themes/src/winter/Snowflake.h
@@ -1,9 +1,9 @@
#ifndef SNOWFLAKE_H
#define SNOWFLAKE_H
-#include "types.h"
-#include "mathlib.h"
-#include "list.h"
+#include "../types.h"
+#include "../mathlib.h"
+#include "../list.h"
#include "Windfield.hpp"
struct Renderer2d;
@@ -31,7 +31,7 @@ struct SnowflakeParticleRenderer {
f32 windIntervalSeconds = 1.5;
i32 numSnowflakes = 0;
f32 timeUntilNextWindSeconds = 0;
- WindField wind;
+ WindField<100, 100, 10> wind;
SnowflakeUpdateData* updateData;
u32 vao;
diff --git a/themes/src/Windfield.cpp b/themes/src/winter/Windfield.cpp
index 3a7563f..88fb74b 100644
--- a/themes/src/Windfield.cpp
+++ b/themes/src/winter/Windfield.cpp
@@ -2,7 +2,7 @@
template <i32 Width, i32 Height, i32 CellDimension>
-void WindField<Width, Height, CellDimension>::load(f32 ttl, Vector2 origin) {
+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);
@@ -21,7 +21,7 @@ Vector2 WindField<Width, Height, CellDimension>::getWindFactor(Vector2& v) {
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 field[cellX][cellY];
}
return Vector2();
diff --git a/themes/src/Windfield.hpp b/themes/src/winter/Windfield.hpp
index 5935c5d..5bf0c38 100644
--- a/themes/src/Windfield.hpp
+++ b/themes/src/winter/Windfield.hpp
@@ -1,13 +1,14 @@
#ifndef WIND_FIELD_HPP
#define WIND_FIELD_HPP
-#include "types.h"
-#include "mathlib.h"
+#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;
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