From 05c4522e5ff424c65aab7cd36c7a15313630ac61 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Sun, 25 Jul 2021 20:20:10 -0400 Subject: (mkosarek) Fix for wrong timestep --- 3d/rigidbody.html | 51 ++++++++++++-------- 3d/rigidbody.html.content | 51 ++++++++++++-------- 3d/rigidbody/dist/output.wasm | Bin 43167 -> 48113 bytes 3d/rigidbody/main.cpp | 110 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 166 insertions(+), 46 deletions(-) (limited to '3d') diff --git a/3d/rigidbody.html b/3d/rigidbody.html index db91bd0..68244de 100644 --- a/3d/rigidbody.html +++ b/3d/rigidbody.html @@ -69,26 +69,37 @@

Rigidbody in 3D

-
-

- Live Example -

-
- - - -
- -
-

References

-
    -
-
-
+
+

Quaternion Time

+

+ I hate to start off the article like this. I really do. But it really wouldn't be a discussion of 3D simulation if we didn't jump right into quaternions. Before I start, however, I recommend you take a look at this article. Quaternions have been written about over and over throughout the years (and I have read many articles about them over and over throughout the years), so I will not waste your time describing every single detail here. The article that I linked to should provide you enough information to begin to understand what quaternions are and why they're important. That being said, I will provide a brief intro to quaternions here, as well as my own C++ implementation of their algebra. (This article is also quite useful: Dance's With Code Website) +

+ +

+
+
+

+ Live Example +

+

+ +

+
+ + + +
+ +
+

References

+
    +
+
+
diff --git a/3d/rigidbody.html.content b/3d/rigidbody.html.content index 8bbf294..5a3eca2 100644 --- a/3d/rigidbody.html.content +++ b/3d/rigidbody.html.content @@ -17,24 +17,35 @@

Rigidbody in 3D

-
-

- Live Example -

-
- - - -
- -
-

References

-
    -
-
-
+
+

Quaternion Time

+

+ I hate to start off the article like this. I really do. But it really wouldn't be a discussion of 3D simulation if we didn't jump right into quaternions. Before I start, however, I recommend you take a look at this article. Quaternions have been written about over and over throughout the years (and I have read many articles about them over and over throughout the years), so I will not waste your time describing every single detail here. The article that I linked to should provide you enough information to begin to understand what quaternions are and why they're important. That being said, I will provide a brief intro to quaternions here, as well as my own C++ implementation of their algebra. (This article is also quite useful: Dance's With Code Website) +

+ +

+
+
+

+ Live Example +

+

+ +

+
+ + + +
+ +
+

References

+
    +
+
+
diff --git a/3d/rigidbody/dist/output.wasm b/3d/rigidbody/dist/output.wasm index 98d25e5..8ebe0dd 100755 Binary files a/3d/rigidbody/dist/output.wasm and b/3d/rigidbody/dist/output.wasm differ diff --git a/3d/rigidbody/main.cpp b/3d/rigidbody/main.cpp index c83018b..90c6bc8 100644 --- a/3d/rigidbody/main.cpp +++ b/3d/rigidbody/main.cpp @@ -13,17 +13,96 @@ #include #include +struct Impulse { + Vector3 force = { 0, 0, 0 }; + Vector3 pointOfApplication = { 0, 0, 0 }; + float32 timeOfApplicationSeconds = 0.016f; + float32 timeAppliedSeconds = 0.f; + bool isDead = false; +}; + +const int32 MAX_IMPULSES = 4; + struct Rigidbody3d { + int32 numImpulses = 0; + Impulse activeImpulses[MAX_IMPULSES]; Vector3 velocity; Vector3 position; + float32 mass = 1.f; + + Vector3 rotationalVelocity; + Quaternion rotation; + float32 momentOfInertia = 1.f; + + void reset() { + numImpulses = 0; + velocity = { 0, 0, 0}; + rotationalVelocity = { 0, 0, 0 }; + } + + void applyImpulse(Impulse i) { + if (numImpulses > MAX_IMPULSES) { + printf("Unable to apply impulse. Buffer full.\n"); + return; + } + + activeImpulses[numImpulses] = i; + numImpulses++; + } + + void update(float32 deltaTimeSeconds) { + // Apply gravity + //velocity += (Vector3 { 0.f, -9.8f, 0.f } * deltaTimeSeconds); + + Vector3 force = { 0.f, 0.f, 0.f }; + Vector3 torque = { 0.f, 0.f, 0.f }; + for (int32 idx = 0; idx < numImpulses; idx++) { + Impulse& i = activeImpulses[idx]; - void update(float32 dtSeconds) { + float32 nextTimeAppliedSeconds = i.timeAppliedSeconds + deltaTimeSeconds; + if (nextTimeAppliedSeconds >= i.timeOfApplicationSeconds) { + nextTimeAppliedSeconds = i.timeOfApplicationSeconds; + i.isDead = true; + } + + float32 impulseDtSeconds = nextTimeAppliedSeconds - i.timeAppliedSeconds; + Vector3 forceToApply = i.force * (impulseDtSeconds / i.timeOfApplicationSeconds); + force += forceToApply; + + force.printDebug("Force applied\n"); + + torque += i.pointOfApplication.cross(force).dot(forceToApply); + i.timeAppliedSeconds = nextTimeAppliedSeconds; + } + Vector3 acceleration = force / mass; + velocity += (acceleration * deltaTimeSeconds); + position += (velocity * deltaTimeSeconds); + + Vector3 rotationalAcceleration = torque / momentOfInertia; + rotationalVelocity += (rotationalAcceleration * deltaTimeSeconds); + + Quaternion rotationVelocityQuat = { 0, rotationalVelocity.x, rotationalVelocity.y, rotationalVelocity.z }; + Quaternion dqDt = (rotationVelocityQuat * rotation) * 0.5f; + rotation = rotation + (dqDt * deltaTimeSeconds); + + for (int32 idx = 0; idx < numImpulses; idx++) { + if (activeImpulses[idx].isDead) { + for (int j = idx + 1; j < numImpulses; j++) { + activeImpulses[j - 1] = activeImpulses[j]; + } + + idx = idx - 1; + numImpulses--; + } + } } }; struct Cube { Mesh3d mesh; + Rigidbody3d body; + float32 scale = 10.f; void load(Renderer3d* renderer) { Vertex3d cubeVertices[] = { @@ -87,12 +166,18 @@ struct Cube { mesh.load(&cubeVertices[0], 8, cubeIndices, 36, renderer); + + body.position = Vector3 { 0.f, 0.f, 0.f }; + body.velocity = Vector3 { 0.f, 0.f, 0.f }; + mesh.model = Mat4x4().scale(scale); + + float32 singleFaceArea = scale * scale; + body.momentOfInertia = (body.mass * singleFaceArea) / 6.f; } void update(float32 dtSeconds) { - float32 multiplier = (PI / 8.f) * dtSeconds; - Vector3 currentRotation = Vector3 { multiplier, multiplier, multiplier }; - mesh.model = mesh.model.rotate(currentRotation.x, currentRotation.y, currentRotation.z); + body.update(dtSeconds); + mesh.model = mesh.model.translate(body.position) * body.rotation.toMatrix(); } void render(Renderer3d* renderer) { @@ -106,6 +191,7 @@ struct Cube { EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL onForceApplicationRequested(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); void load(); void update(float32 time, void* userData); @@ -122,14 +208,17 @@ int main() { context.init("#gl_canvas"); emscripten_set_click_callback("#gl_canvas_play", NULL, false, onPlayClicked); emscripten_set_click_callback("#gl_canvas_stop", NULL, false, onStopClicked); + emscripten_set_click_callback("#force_apply", NULL, false, onForceApplicationRequested); return 0; } void load() { renderer.load(&context); + cube.scale = 12.f; + cube.body.mass = 100.f; cube.load(&renderer); - camera.projection = Mat4x4().getPerspectiveProjection(0.1f, 100.f, PI / 2.f, 800.f / 600.f); - camera.view = Mat4x4().translate({ 0, 0, -10 }); + camera.projection = Mat4x4().getPerspectiveProjection(0.1f, 10000.f, DEG_TO_RAD(60.f), 800.f / 600.f); + camera.view = Mat4x4().translate({ 0, 0, -250 }); mainLoop.run(update); } @@ -162,3 +251,12 @@ EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, voi unload(); return true; } + +EM_BOOL onForceApplicationRequested(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Force applied\n"); + Impulse base; + base.force = { 0, 1000, 0 }; + base.pointOfApplication = { 0, 1, 0 }; + cube.body.applyImpulse(base); + return true; +} -- cgit v1.2.1