From 430d60baf909a01c2b167c65071f0e003d770aaf Mon Sep 17 00:00:00 2001 From: mattkae Date: Sun, 8 May 2022 16:17:33 -0400 Subject: Hopping bunng animation --- themes/Renderer3d.cpp | 4 +- themes/dist/output.wasm | Bin 116476 -> 128673 bytes themes/main.cpp | 158 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 156 insertions(+), 6 deletions(-) diff --git a/themes/Renderer3d.cpp b/themes/Renderer3d.cpp index 771d778..a7779e2 100644 --- a/themes/Renderer3d.cpp +++ b/themes/Renderer3d.cpp @@ -51,7 +51,7 @@ void Renderer3D::load(WebglContext* inContext) { uniforms.view = getShaderUniform(shader, "view"); uniforms.model = getShaderUniform(shader, "model"); projection = Mat4x4().getPerspectiveProjection(0.1, 1000.f, 0.872f, static_cast(context->width) / static_cast(context->height)); - view = Mat4x4().getLookAt({ 0, 25, 25 }, { 0, 0, 0 }, { 0, 1, 0 }); + view = Mat4x4().getLookAt({ 0, 25, 100 }, { 0, 15, 0 }, { 0, 1, 0 }); logger_info("Renderer2d shader compiled.\n"); @@ -238,4 +238,4 @@ void Mesh3d::render(Renderer3D* renderer) { glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, indices.numElements, GL_UNSIGNED_INT, 0); glBindVertexArray(0); -} \ No newline at end of file +} diff --git a/themes/dist/output.wasm b/themes/dist/output.wasm index 4c50e4a..51ace5b 100755 Binary files a/themes/dist/output.wasm and b/themes/dist/output.wasm differ diff --git a/themes/main.cpp b/themes/main.cpp index ef7d18b..ae91ba7 100644 --- a/themes/main.cpp +++ b/themes/main.cpp @@ -2,6 +2,7 @@ #include "MainLoop.h" #include "Renderer2d.h" #include "Renderer3d.h" +#include "mathlib.h" #include "types.h" #include "TreeShape.h" #include "LeafParticleRender.h" @@ -36,8 +37,28 @@ struct WinterTheme { void unload(); }; +enum class BunnyAnimationState { + Loading = 0, + Loaded, + PreHop, + Hopping, + Idle +}; + struct SpringTheme { - bool canRenderBunny = false; + 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); @@ -230,7 +251,7 @@ void WinterTheme::unload() { // -- Spring theme void onBunnySuccess(emscripten_fetch_t *fetch) { - springTheme.canRenderBunny = true; + 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); @@ -244,7 +265,7 @@ void onBunnyFail(emscripten_fetch_t *fetch) { } void SpringTheme::load(Renderer3D* renderer) { - canRenderBunny = false; + springTheme.state = BunnyAnimationState::Loading; renderer->clearColor = Vector4(160, 231, 160, 255.f).toNormalizedColor(); emscripten_fetch_attr_t attr; @@ -256,12 +277,141 @@ void SpringTheme::load(Renderer3D* renderer) { 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 (canRenderBunny) { + if (state != BunnyAnimationState::Loading) { bunnyMesh.render(renderer); } } -- cgit v1.2.1