From 7228b2e1a2d0a8399facce3493d71a3569d250d5 Mon Sep 17 00:00:00 2001 From: mattkae Date: Fri, 23 Dec 2022 12:47:10 -0500 Subject: Improved the makefile considerably --- themes/src/main.cpp | 445 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 themes/src/main.cpp (limited to 'themes/src/main.cpp') diff --git a/themes/src/main.cpp b/themes/src/main.cpp new file mode 100644 index 0000000..f8771d4 --- /dev/null +++ b/themes/src/main.cpp @@ -0,0 +1,445 @@ +#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 +#include + + +enum Theme { + Default = 0, + Autumn, + Winter, + Spring, + 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); +EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); + +WebglContext context; +Renderer2d renderer2d; +Renderer3D renderer3d; +MainLoop mainLoop; +Theme activeTheme = Theme::Default; +AutumnTheme autumnTheme; +WinterTheme winterTheme; +SpringTheme springTheme; +SummerTheme summerTheme; + +int main() { + context.init("#theme_canvas"); + emscripten_set_click_callback("#theme_button_default", NULL, false, selectNone); + emscripten_set_click_callback("#theme_button_autumn", NULL, false, selectAutumn); + emscripten_set_click_callback("#theme_button_winter", NULL, false, selectWinter); + emscripten_set_click_callback("#theme_button_spring", NULL, false, selectSpring); + emscripten_set_click_callback("#theme_button_summer", NULL, false, selectSummer); + + return 0; +} + +// -- Scene loading, updating, and unloading logic +void load(Theme theme) { + if (activeTheme == theme) { + printf("This theme is already active.\n"); + return; + } + + unload(); // Try and unload before we load, so that we start fresh + + activeTheme = theme; + mainLoop.run(update); + + switch (activeTheme) { + case Theme::Autumn: + renderer2d.load(&context); + autumnTheme.load(&renderer2d); + break; + case Theme::Winter: + renderer2d.load(&context); + winterTheme.load(&renderer2d); + break; + case Theme::Spring: + renderer3d.load(&context); + springTheme.load(&renderer3d); + break; + case Theme::Summer: + renderer2d.load(&context); + summerTheme.load(&renderer2d); + break; + default: + break; + } +} + +void update(f32 dtSeconds, void* userData) { + // -- Update + switch (activeTheme) { + case Theme::Autumn: + autumnTheme.update(dtSeconds); + break; + case Theme::Winter: + winterTheme.update(dtSeconds); + break; + case Theme::Spring: + springTheme.update(dtSeconds); + break; + case Theme::Summer: + summerTheme.update(dtSeconds); + break; + default: + break; + } + + // -- Render + switch (activeTheme) { + case Theme::Autumn: + renderer2d.render(); + autumnTheme.render(&renderer2d); + break; + case Theme::Winter: + renderer2d.render(); + winterTheme.render(&renderer2d); + break; + case Theme::Spring: + renderer3d.render(); + springTheme.render(&renderer3d); + break; + case Theme::Summer: + renderer2d.render(); + summerTheme.render(&renderer2d); + break; + default: + break; + } +} + +void unload() { + switch (activeTheme) { + case Theme::Autumn: + autumnTheme.unload(); + break; + case Theme::Winter: + winterTheme.unload(); + break; + case Theme::Spring: + springTheme.unload(); + break; + case Theme::Summer: + summerTheme.unload(); + break; + default: + break; + } + + activeTheme = Theme::Default; + if (mainLoop.isRunning) { + mainLoop.stop(); + renderer2d.unload(); + renderer3d.unload(); + } +} + +// -- HTML5 callbacks +EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Default theme selected\n"); + unload(); + return true; +} + +EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Autumn theme selected\n"); + load(Theme::Autumn); + return true; +} + +EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Winter theme selected\n"); + load(Theme::Winter); + return true; +} + +EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Spring theme selected\n"); + load(Theme::Spring); + return true; +} + +EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + 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(); +} -- cgit v1.2.1