summaryrefslogtreecommitdiff
path: root/2d
diff options
context:
space:
mode:
authorMatthew Kosarek <mattkae@protonmail.com>2021-06-22 20:23:33 -0400
committerMatthew Kosarek <mattkae@protonmail.com>2021-06-22 20:23:33 -0400
commitf34116f1da8465851d684620b6b94e0a3f3c0fbc (patch)
treed17c3531f017caa3c60bc66a96d804d03fa60b21 /2d
parenta36f425491aaf019243a31179e80cb10ea62db59 (diff)
Impulses, and entirely finished with rigid body demo #1
Diffstat (limited to '2d')
-rw-r--r--2d/rigidbody/rigidbody_1.html137
-rw-r--r--2d/rigidbody/rigidbody_1.html.content57
-rwxr-xr-x2d/rigidbody/rigidbody_1/dist/output.wasmbin47394 -> 48588 bytes
-rw-r--r--2d/rigidbody/rigidbody_1/main.cpp73
-rw-r--r--2d/rigidbody/rigidbody_1/snippet1.cpp1
-rw-r--r--2d/rigidbody/rigidbody_1/snippet2.cpp16
-rw-r--r--2d/rigidbody/rigidbody_1/snippet3.cpp66
7 files changed, 293 insertions, 57 deletions
diff --git a/2d/rigidbody/rigidbody_1.html b/2d/rigidbody/rigidbody_1.html
index 0751499..bf703d2 100644
--- a/2d/rigidbody/rigidbody_1.html
+++ b/2d/rigidbody/rigidbody_1.html
@@ -78,7 +78,7 @@
<ul>
<li>
- <b>Kinematics</b> is the study of how an object's movement changes over time. These are the classic position, velocity, and acceleration equations that you're most likely familiar with from high school or college physics. Kinematics doesn't care about <i>how</i> a system entered into the state that it is out, but rather that the system <i>is</i> in that state.
+ <b>Kinematics</b> is the study of how an object's movement changes over time. These are the classic position, velocity, and acceleration equations that you're most likely familiar with from high school or college physics. Kinematics doesn't care about <i>how</i> a system entered into the state that it is in, but rather that the system <i>is</i> in that state.
</li>
<li>
<b>Dynamics</b> is the study of whats <i>causes</i> kinematic movement. These are the classic force and momentum equations that you may already be familiar with as well. Whereas kinematics only worries itself with the current state of the system, dynamics wants to know <i>how</i> the system entered the state that it is currently in.
@@ -91,23 +91,22 @@
<section>
<h2>The Kinematics Data Structure</h2>
<p>
- Now that we have that understanding, we can begin setting up our rigidbody data structure.
+ Now that we have an understanding of these two fundamental fields of physics, we can begin setting up our rigidbody data structure.
<pre><code><span class="code_keyword">struct</span> Rigidbody {
<span class="code_keyword">Vector2</span> force = { 0, 0 };
- <span class="code_keyword">Vector2</span> acceleration = { 0, 0 };
<span class="code_keyword">Vector2</span> velocity = { 0, 0 };
<span class="code_keyword">Vector2</span> position = { 0, 0 };
<span class="code_keyword">float32</span> mass = 1.f;
};
</code></pre>
- As you can see, the base data structure exactly mirrors what we already know from 2D newtonian physics.
+ As you can see, the base data structure exactly mirrors what we already know from 2D newtonian physics. Every frame, we will have some <i>force</i> applied to our rigidbody. We will use that <i>force</i> to get <i>accleration</i>, which, when differentied with respect to time, yields velocity and, ultimately, the new position of our rigidbody. For all of this to work, of course, we need a constant <i>mass</i> for this object.
</p>
</section>
<section>
<h2>The Dynamics Functions</h2>
<p>
- Now, let's put that Rigidbody data structure to work! As I mentioned earlier, you can think of dynamics as the <i>input</i> to the system. What we're going to do now is add a way to
+ Now, let's put that Rigidbody data structure to work! As I mentioned earlier, you can think of dynamics as the <i>input</i> to the system. What we're going to do now is add a way apply some sort of force to our rigidbody instantaneously.
<pre><code><span class="code_keyword">struct</span> Rigidbody {
<span class="code_keyword">Vector2</span> force = { 0, 0 };
@@ -115,31 +114,131 @@
<span class="code_keyword">Vector2</span> position = { 0, 0 };
<span class="code_keyword">float32</span> mass = 1.f;
- <span class="code_keyword">void</span> applyForce(Vector2 f) {
- force += f;
+ <span class="code_keyword">void</span> update(float32 deltaTimeSeconds) {
+ applyGravity(deltaTimeSeconds);
+
+ <span class="code_keyword">Vector2</span> acceleration = force / mass;
+ velocity += (acceleration * deltaTimeSeconds);
+ position += (velocity * deltaTimeSeconds);
+ force = <span class="code_keyword">Vector2</span> { 0.f, 0.f };
}
<span class="code_keyword">void</span> applyGravity(float32 deltaTimeSeconds) {
- velocity += (Vector2 { 0.f, -50.f } * deltaTimeSeconds);
+ velocity += (Vector2 { 0.f, -9.8.f } * deltaTimeSeconds);
}
+ <span class="code_keyword">void</span> applyForce(Vector2 f) {
+ force += f;
+ }
+};
+</code></pre>
+ We have three new functions here:
+ <ul>
+ <li><b>update</b>: This function will run every single frame during our simulation. In it, we use the total force being applied on the object at this time (found by reordering the classic equation <i>F = ma</i>). From there, we differentiate once with respect to time to get the velocity, and again to get the position. Finally, we reset the total force to zero so that we can start fresh on the next frame.</li>
+ <li><b>applyGravity</b>: We all know that gravity is a constant 9.8 m/s<sup>2</sup>, so we can simply add that to the velocity every single frame. (Note that the gravity in your game may not 9.8; don't be afraid to mess around with this number).</li>
+ <li><b>applyForce</b>: This function provies a way for external forces to affect our rigidbody directly.</li>
+ </ul>
+
+ Voila! We have everything we need to start applying forces. But wait! There is one more thing to consider...
+ </p>
+ </section>
+ <section>
+ <h2>Impulses & Frame-Rate Independence</h2>
+ <p>
+ Although it might be good enough for your use case, allow me to explain why the previous approach is neither realistic nor reliable:<br/><br/>
+
+ When a force is applied in the real world, it doesn't just get applied for a single moment (i.e. frame) in time: it gets applied <i>over</i> time, or for a given <i>duration</i> of time. At the moment, our current implementation fails to account for this. forces are applied for a given frame, and then forgotten about.<br/><br/>
+
+ Our current approach has another problem too: the applied force is <b>not</b> frame-rate independent. If you were to apply a force of 50N in the Y direction right now, slower computers would experience larger resultant velocities because their <i>deltaTimeSeconds</i> would be much larger. This is generally something that you'd want to avoid in most applications.<br/><br/>
+
+ One potential fix for this is to use <b>impulses</b>:
+
+ <pre><code><span class="code_keyword">struct</span> Impulse {
+ <span class="code_keyword">Vector2</span> force = { 0, 0 };
+ <span class="code_keyword">float32</span> timeOfApplicationSeconds = 0.25f;
+ <span class="code_keyword">float32</span> timeAppliedSeconds = 0.f;
+ <span class="code_keyword">bool</span> isDead = false;
+};
+
+const <span class="code_keyword">int32</span> NUM_IMPULSES = 4;
+
+<span class="code_keyword">struct</span> Rigidbody {
+ <span class="code_keyword">int32</span> numImpulses = 0;
+ Impulse activeImpulses[NUM_IMPULSES];
+ <span class="code_keyword">Vector2</span> velocity = { 0, 0 };
+ <span class="code_keyword">Vector2</span> position = { 0, 0 };
+ <span class="code_keyword">float32</span> mass = 1.f;
+
<span class="code_keyword">void</span> update(float32 deltaTimeSeconds) {
applyGravity(deltaTimeSeconds);
-
+
+ // Add up all of the forces acting at this moment
+ <span class="code_keyword">Vector2</span> force;
+ for (int32 idx = 0; idx < numImpulses; idx++) {
+ Impulse& i = activeImpulses[idx];
+
+ <span class="code_keyword">float32</span> nextTimeAppliedSeconds = i.timeAppliedSeconds + deltaTimeSeconds;
+ if (nextTimeAppliedSeconds >= i.timeOfApplicationSeconds) {
+ nextTimeAppliedSeconds = i.timeOfApplicationSeconds;
+ i.isDead = true;
+ }
+
+ <span class="code_keyword">float32</span> impulseDtSeconds = nextTimeAppliedSeconds - i.timeAppliedSeconds;
+ force += i.force * impulseDtSeconds;
+ i.timeAppliedSeconds = nextTimeAppliedSeconds;
+ }
+
<span class="code_keyword">Vector2</span> acceleration = force / mass;
- velocity += (acceleration * deltaTimeSeconds);
+ velocity += (acceleration * impulseDtSeconds);
position += (velocity * deltaTimeSeconds);
- force = <span class="code_keyword">Vector2</span> { 0.f, 0.f };
+
+ // Cleanup any impulses that have expired in the mean time
+ 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--;
+ }
+ }
+ }
+
+ <span class="code_keyword">void</span> applyGravity(float32 deltaTimeSeconds) {
+ velocity += (Vector2 { 0.f, -9.8f } * deltaTimeSeconds);
+ }
+
+ <span class="code_keyword">void</span> applyImpulse(Impulse i) {
+ if (numImpulses > NUM_IMPULSES) {
+ printf("Unable to apply impulse. Buffer full.\n");
+ return;
+ }
+
+ activeImpulses[numImpulses] = i;
+ numImpulses++;
}
};
-</code></pre> </p>
+</code></pre>
+ While a bit more verbose than our previous example, this approach has more reliable behavior. Forces are no longer treated as single moments in time, but rather "forces applied <i>over</i> time". Because we ensure that the force is applied over time, we guarantee that all users see the same amount of force applied, regardless of frame-rate.<br/><br/>
+
+ For anyone interested, the algorithm for using impulses is as follows:
+ <ul>
+ <li>Get all impulses acting on the rigidbody at this moment</li>
+ <li>For each impulse, find out how much time is remaining. Apply the remaining force, and mark those that have expired as dead</li>
+ <li>Calculate the acceleration, velocity, and position as before</li>
+ <li>Remove any dead impulses from the list</li>
+ </ul>
+
+ Feel free to look at the example program below (and browse/download the code) if you want to see it in action.
+ </p>
</section>
<section>
<h2>
Live Example
</h2>
<p>
- That's all there is to a rigidbody system with 2D linear forces. Now let's see it in action. Click 'Play' on the WebAssembly demo below to see a square bouncing around the screen. When you drag the pointer through the square, we will apply a force equivalent to how fast you were moving your mouse in the direction that you were moving it. (The speed is capped in the demo, or else things get a little out of hand.)
+ Click 'Play' on the WebAssembly demo below to see a square bouncing around the screen. When you drag the pointer through the square, we will apply an impulse equivalent to how fast you were moving your mouse in the direction that you were moving it.
</p>
<div class="opengl_canvas_container">
<canvas id="gl_canvas" width="800" height="600"></canvas>
@@ -150,13 +249,13 @@
Stop
</button>
</div>
-
- <footer id="references">
- <h2>References</h2>
- <ul>
- </ul>
- </footer>
</section>
+ <footer id="references">
+ <h2>References</h2>
+ <ul>
+ <li><a href='https://www.thoughtco.com/impulse-2698956'>Impulse Information</a></li>
+ </ul>
+ </footer>
</article>
</main>
</body>
diff --git a/2d/rigidbody/rigidbody_1.html.content b/2d/rigidbody/rigidbody_1.html.content
index de3898a..d61acaf 100644
--- a/2d/rigidbody/rigidbody_1.html.content
+++ b/2d/rigidbody/rigidbody_1.html.content
@@ -28,7 +28,7 @@
<ul>
<li>
- <b>Kinematics</b> is the study of how an object's movement changes over time. These are the classic position, velocity, and acceleration equations that you're most likely familiar with from high school or college physics. Kinematics doesn't care about <i>how</i> a system entered into the state that it is out, but rather that the system <i>is</i> in that state.
+ <b>Kinematics</b> is the study of how an object's movement changes over time. These are the classic position, velocity, and acceleration equations that you're most likely familiar with from high school or college physics. Kinematics doesn't care about <i>how</i> a system entered into the state that it is in, but rather that the system <i>is</i> in that state.
</li>
<li>
<b>Dynamics</b> is the study of whats <i>causes</i> kinematic movement. These are the classic force and momentum equations that you may already be familiar with as well. Whereas kinematics only worries itself with the current state of the system, dynamics wants to know <i>how</i> the system entered the state that it is currently in.
@@ -41,19 +41,54 @@
<section>
<h2>The Kinematics Data Structure</h2>
<p>
- Now that we have that understanding, we can begin setting up our rigidbody data structure.
+ Now that we have an understanding of these two fundamental fields of physics, we can begin setting up our rigidbody data structure.
#SNIPPET rigidbody_1/snippet1.cpp
- As you can see, the base data structure exactly mirrors what we already know from 2D newtonian physics.
+ As you can see, the base data structure exactly mirrors what we already know from 2D newtonian physics. Every frame, we will have some <i>force</i> applied to our rigidbody. We will use that <i>force</i> to get <i>accleration</i>, which, when differentied with respect to time, yields velocity and, ultimately, the new position of our rigidbody. For all of this to work, of course, we need a constant <i>mass</i> for this object.
</p>
</section>
<section>
<h2>The Dynamics Functions</h2>
<p>
- Now, let's put that Rigidbody data structure to work! As I mentioned earlier, you can think of dynamics as the <i>input</i> to the system. What we're going to do now is add a way to
+ Now, let's put that Rigidbody data structure to work! As I mentioned earlier, you can think of dynamics as the <i>input</i> to the system. What we're going to do now is add a way apply some sort of force to our rigidbody instantaneously.
#SNIPPET rigidbody_1/snippet2.cpp
+
+ We have three new functions here:
+ <ul>
+ <li><b>update</b>: This function will run every single frame during our simulation. In it, we use the total force being applied on the object at this time (found by reordering the classic equation <i>F = ma</i>). From there, we differentiate once with respect to time to get the velocity, and again to get the position. Finally, we reset the total force to zero so that we can start fresh on the next frame.</li>
+ <li><b>applyGravity</b>: We all know that gravity is a constant 9.8 m/s<sup>2</sup>, so we can simply add that to the velocity every single frame. (Note that the gravity in your game may not 9.8; don't be afraid to mess around with this number).</li>
+ <li><b>applyForce</b>: This function provies a way for external forces to affect our rigidbody directly.</li>
+ </ul>
+
+ Voila! We have everything we need to start applying forces. But wait! There is one more thing to consider...
+ </p>
+ </section>
+ <section>
+ <h2>Impulses & Frame-Rate Independence</h2>
+ <p>
+ Although it might be good enough for your use case, allow me to explain why the previous approach is neither realistic nor reliable:<br/><br/>
+
+ When a force is applied in the real world, it doesn't just get applied for a single moment (i.e. frame) in time: it gets applied <i>over</i> time, or for a given <i>duration</i> of time. At the moment, our current implementation fails to account for this. forces are applied for a given frame, and then forgotten about.<br/><br/>
+
+ Our current approach has another problem too: the applied force is <b>not</b> frame-rate independent. If you were to apply a force of 50N in the Y direction right now, slower computers would experience larger resultant velocities because their <i>deltaTimeSeconds</i> would be much larger. This is generally something that you'd want to avoid in most applications.<br/><br/>
+
+ One potential fix for this is to use <b>impulses</b>:
+
+ #SNIPPET rigidbody_1/snippet3.cpp
+
+ While a bit more verbose than our previous example, this approach has more reliable behavior. Forces are no longer treated as single moments in time, but rather "forces applied <i>over</i> time". Because we ensure that the force is applied over time, we guarantee that all users see the same amount of force applied, regardless of frame-rate.<br/><br/>
+
+ For anyone interested, the algorithm for using impulses is as follows:
+ <ul>
+ <li>Get all impulses acting on the rigidbody at this moment</li>
+ <li>For each impulse, find out how much time is remaining. Apply the remaining force, and mark those that have expired as dead</li>
+ <li>Calculate the acceleration, velocity, and position as before</li>
+ <li>Remove any dead impulses from the list</li>
+ </ul>
+
+ Feel free to look at the example program below (and browse/download the code) if you want to see it in action.
</p>
</section>
<section>
@@ -61,7 +96,7 @@
Live Example
</h2>
<p>
- That's all there is to a rigidbody system with 2D linear forces. Now let's see it in action. Click 'Play' on the WebAssembly demo below to see a square bouncing around the screen. When you drag the pointer through the square, we will apply a force equivalent to how fast you were moving your mouse in the direction that you were moving it. (The speed is capped in the demo, or else things get a little out of hand.)
+ Click 'Play' on the WebAssembly demo below to see a square bouncing around the screen. When you drag the pointer through the square, we will apply an impulse equivalent to how fast you were moving your mouse in the direction that you were moving it.
</p>
<div class="opengl_canvas_container">
<canvas id="gl_canvas" width="800" height="600"></canvas>
@@ -72,11 +107,11 @@
Stop
</button>
</div>
-
- <footer id="references">
- <h2>References</h2>
- <ul>
- </ul>
- </footer>
</section>
+ <footer id="references">
+ <h2>References</h2>
+ <ul>
+ <li><a href='https://www.thoughtco.com/impulse-2698956'>Impulse Information</a></li>
+ </ul>
+ </footer>
</article>
diff --git a/2d/rigidbody/rigidbody_1/dist/output.wasm b/2d/rigidbody/rigidbody_1/dist/output.wasm
index c2fa2dd..5ceed51 100755
--- a/2d/rigidbody/rigidbody_1/dist/output.wasm
+++ b/2d/rigidbody/rigidbody_1/dist/output.wasm
Binary files differ
diff --git a/2d/rigidbody/rigidbody_1/main.cpp b/2d/rigidbody/rigidbody_1/main.cpp
index 321e3e5..b3ed359 100644
--- a/2d/rigidbody/rigidbody_1/main.cpp
+++ b/2d/rigidbody/rigidbody_1/main.cpp
@@ -11,42 +11,76 @@
#include <cmath>
#include <cfloat>
-const float32 MAX_VELOCITY = 200.f;
+struct Impulse {
+ Vector2 force = { 0, 0 };
+ float32 timeOfApplicationSeconds = 0.25f;
+ float32 timeAppliedSeconds = 0.f;
+ bool isDead = false;
+};
+
+const int32 NUM_IMPULSES = 4;
struct Rigidbody {
- Vector2 force = { 0, 0 };
+ int32 numImpulses = 0;
+ Impulse activeImpulses[NUM_IMPULSES];
Vector2 velocity = { 0, 0 };
Vector2 position = { 0, 0 };
float32 mass = 1.f;
void reset() {
- force = { 0, 0 };
+ numImpulses = 0;
velocity = { 0, 0 };
}
- void applyForce(Vector2 f) {
- force += f;
+ void applyImpulse(Impulse i) {
+ if (numImpulses > NUM_IMPULSES) {
+ printf("Unable to apply impulse. Buffer full.\n");
+ return;
+ }
+
+ activeImpulses[numImpulses] = i;
+ numImpulses++;
}
void applyGravity(float32 deltaTimeSeconds) {
- velocity += (Vector2 { 0.f, -50.f } * deltaTimeSeconds);
+ velocity += (Vector2 { 0.f, -9.8f } * deltaTimeSeconds);
}
void update(float32 deltaTimeSeconds) {
applyGravity(deltaTimeSeconds);
-
- Vector2 acceleration = force / mass;
- velocity += (acceleration * deltaTimeSeconds);
- if (ABS(velocity.x) > MAX_VELOCITY) {
- velocity.x = SIGN(velocity.x) * MAX_VELOCITY;
- }
- if (ABS(velocity.y) > MAX_VELOCITY) {
- velocity.y = SIGN(velocity.y) * MAX_VELOCITY;
+ Vector2 force;
+ for (int32 idx = 0; idx < numImpulses; idx++) {
+ Impulse& i = activeImpulses[idx];
+
+ float32 nextTimeAppliedSeconds = i.timeAppliedSeconds + deltaTimeSeconds;
+ if (nextTimeAppliedSeconds >= i.timeOfApplicationSeconds) {
+ nextTimeAppliedSeconds = i.timeOfApplicationSeconds; // Do the remainder of the time
+ i.isDead = true;
+ }
+
+ float32 impulseDtSeconds = nextTimeAppliedSeconds - i.timeAppliedSeconds;
+ force += i.force * impulseDtSeconds;
+
+
+ i.timeAppliedSeconds = nextTimeAppliedSeconds;
}
+ Vector2 acceleration = force / mass;
+ velocity += (acceleration * impulseDtSeconds);
position += (velocity * deltaTimeSeconds);
- force = Vector2 { 0.f, 0.f };
+
+ // Cleanup any impulses that have expired in the mean time
+ 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--;
+ }
+ }
}
};
@@ -106,6 +140,7 @@ struct Rectangle {
struct Circle {
OrthographicShape shape;
Rigidbody body;
+ Vector2 force;
float32 radius = 5.f;
@@ -204,7 +239,9 @@ void update(float32 deltaTimeSeconds, void* userData) {
if (isPointInRectangle(pointer.body.position, rectangle)) {
if (!isIntersectingPointer) {
isIntersectingPointer = true;
- rectangle.body.force += pointer.body.force;
+ Impulse i;
+ i.force = pointer.force;
+ rectangle.body.applyImpulse(i);
}
} else if (isIntersectingPointer) {
isIntersectingPointer = false;
@@ -263,8 +300,8 @@ EM_BOOL onMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void
return true;
}
- pointer.body.force.x = static_cast<float32>(mouseEvent->movementX) * 1000.f;
- pointer.body.force.y = static_cast<float32>(-mouseEvent->movementY) * 1000.f;
+ pointer.force.x = static_cast<float32>(mouseEvent->movementX) * 2000.f;
+ pointer.force.y = static_cast<float32>(-mouseEvent->movementY) * 2000.f;
pointer.body.position.x = static_cast<float32>(mouseEvent->targetX);
pointer.body.position.y = static_cast<float32>(600.f - mouseEvent->targetY);
diff --git a/2d/rigidbody/rigidbody_1/snippet1.cpp b/2d/rigidbody/rigidbody_1/snippet1.cpp
index 77a81b7..dd07254 100644
--- a/2d/rigidbody/rigidbody_1/snippet1.cpp
+++ b/2d/rigidbody/rigidbody_1/snippet1.cpp
@@ -1,7 +1,6 @@
struct Rigidbody {
Vector2 force = { 0, 0 };
- Vector2 acceleration = { 0, 0 };
Vector2 velocity = { 0, 0 };
Vector2 position = { 0, 0 };
float32 mass = 1.f;
diff --git a/2d/rigidbody/rigidbody_1/snippet2.cpp b/2d/rigidbody/rigidbody_1/snippet2.cpp
index 8ad468c..1288209 100644
--- a/2d/rigidbody/rigidbody_1/snippet2.cpp
+++ b/2d/rigidbody/rigidbody_1/snippet2.cpp
@@ -6,14 +6,6 @@ struct Rigidbody {
Vector2 position = { 0, 0 };
float32 mass = 1.f;
- void applyForce(Vector2 f) {
- force += f;
- }
-
- void applyGravity(float32 deltaTimeSeconds) {
- velocity += (Vector2 { 0.f, -50.f } * deltaTimeSeconds);
- }
-
void update(float32 deltaTimeSeconds) {
applyGravity(deltaTimeSeconds);
@@ -22,4 +14,12 @@ struct Rigidbody {
position += (velocity * deltaTimeSeconds);
force = Vector2 { 0.f, 0.f };
}
+
+ void applyGravity(float32 deltaTimeSeconds) {
+ velocity += (Vector2 { 0.f, -9.8.f } * deltaTimeSeconds);
+ }
+
+ void applyForce(Vector2 f) {
+ force += f;
+ }
};
diff --git a/2d/rigidbody/rigidbody_1/snippet3.cpp b/2d/rigidbody/rigidbody_1/snippet3.cpp
new file mode 100644
index 0000000..a43c884
--- /dev/null
+++ b/2d/rigidbody/rigidbody_1/snippet3.cpp
@@ -0,0 +1,66 @@
+struct Impulse {
+ Vector2 force = { 0, 0 };
+ float32 timeOfApplicationSeconds = 0.25f;
+ float32 timeAppliedSeconds = 0.f;
+ bool isDead = false;
+};
+
+const int32 NUM_IMPULSES = 4;
+
+struct Rigidbody {
+ int32 numImpulses = 0;
+ Impulse activeImpulses[NUM_IMPULSES];
+ Vector2 velocity = { 0, 0 };
+ Vector2 position = { 0, 0 };
+ float32 mass = 1.f;
+
+ void update(float32 deltaTimeSeconds) {
+ applyGravity(deltaTimeSeconds);
+
+ // Add up all of the forces acting at this moment
+ Vector2 force;
+ for (int32 idx = 0; idx < numImpulses; idx++) {
+ Impulse& i = activeImpulses[idx];
+
+ float32 nextTimeAppliedSeconds = i.timeAppliedSeconds + deltaTimeSeconds;
+ if (nextTimeAppliedSeconds >= i.timeOfApplicationSeconds) {
+ nextTimeAppliedSeconds = i.timeOfApplicationSeconds;
+ i.isDead = true;
+ }
+
+ float32 impulseDtSeconds = nextTimeAppliedSeconds - i.timeAppliedSeconds;
+ force += i.force * impulseDtSeconds;
+ i.timeAppliedSeconds = nextTimeAppliedSeconds;
+ }
+
+ Vector2 acceleration = force / mass;
+ velocity += (acceleration * impulseDtSeconds);
+ position += (velocity * deltaTimeSeconds);
+
+ // Cleanup any impulses that have expired in the mean time
+ 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--;
+ }
+ }
+ }
+
+ void applyGravity(float32 deltaTimeSeconds) {
+ velocity += (Vector2 { 0.f, -9.8f } * deltaTimeSeconds);
+ }
+
+ void applyImpulse(Impulse i) {
+ if (numImpulses > NUM_IMPULSES) {
+ printf("Unable to apply impulse. Buffer full.\n");
+ return;
+ }
+
+ activeImpulses[numImpulses] = i;
+ numImpulses++;
+ }
+};