diff options
Diffstat (limited to 'themes/src')
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 |