summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattkae <mattkae@protonmail.com>2022-05-08 16:17:33 -0400
committermattkae <mattkae@protonmail.com>2022-05-08 16:17:33 -0400
commit430d60baf909a01c2b167c65071f0e003d770aaf (patch)
tree34eb63d1d3c5790ec9b093d72dc1ddf4acec4ff4
parent0b8596a5ebec39f797fe8a14eed1bda3c2e3a93d (diff)
Hopping bunng animation
-rw-r--r--themes/Renderer3d.cpp4
-rwxr-xr-xthemes/dist/output.wasmbin116476 -> 128673 bytes
-rw-r--r--themes/main.cpp158
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<f32>(context->width) / static_cast<f32>(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
--- a/themes/dist/output.wasm
+++ b/themes/dist/output.wasm
Binary files 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);
}
}