Tater Physics Engine banner

Tater Physics Engine

10 devlogs
59h 1m 21s

A 2D impulse-based physics engine designed around data-driven constraints and optimizations without compromising on accuracy. This is a port/expansion of a physics engine I spent over 2 years making into java after its scale became unsustainable o…

A 2D impulse-based physics engine designed around data-driven constraints and optimizations without compromising on accuracy. This is a port/expansion of a physics engine I spent over 2 years making into java after its scale became unsustainable on the original platform.

This project uses AI

I use GitHub Copilot code completion and when stuck occasionally use Claude to help debug problems. AI also helped create the INSTALL.md file and compile/run scripts.

Demo Repository

Loading README...

Chanied

Shipped this project!

I made a 2D-Impulse based data-driven physics engine. It is written in Java and based on a previous physics engine I created. I made sure to make all debug tools accessible as features in the engine rather than just locked behind modifying the engine. For example, the rewind functionality was originally developed to help spot single-frame errors and was later turned into a user friendly button. This was my first project in Java, and I definitely learned a lot.

Chanied

Ship Update:
Added build stuff for javac and updated the readme.

Attachment
0
Chanied

Interactions Part 2:
This final update during Flavortown continues the development of the GUI. Now, the shape creation menus have less steps to create shapes. In addition, you are now able to draw convex polygons with custom shapes. To make this work, it ignores any non convex set of points and flips any counter-clockwise set of points.
I changed shapes to store their unique ID rather than their index in their parent list to better fit with future updates after the end of Flavortown.
I fixed outlines of hovered shapes to now also work on circles.
Follow the GitHub for future updates like ropes, constraints, and glued shapes.

Attachment
0
Chanied

Interactions Part 1:
This update was focused on adding interactivity to the physics engine. Most of the content in this update was already possible to some extent by modifying the code, and the new UI allows users to easily access it. A lot of the buttons are based on the buttons I created for my first physics engine. I lowered the pixel count and stuck to a simpler red and black color scheme to fit the recording buttons.
I added a new class for screen elements where the button code runs. There are three button menus. The left side is an expandable drop-down for shape creation, the left side is a drop-down for recording and playback, and the middle left is a pause/play button.
Now, when you stop a recording playback, it rewinds the engine back to the state it was in during that frame.
Last update, I ran out of time/forgot to make circles collide with other circles. Either way, I added a whole new lightweight collision system to detect circle collisions at a fraction of the computational cost of polygon-circle/polygon-polygon collisions.
I made the recording object save itself on the Main Thread instead of the Event Thread (rendering stuff) to allow for better interactions with other classes.
When a new shape is created through the UI, it picks a random color from a new set list of color options. In addition, all shapes created through code now generate using a specific color from that list.
For now, you can only create rectangles or polygons. The next and final (for real this time guys I promise :P) will be focused on customizable shape creations.

Attachment
0
Chanied

Circle and Static Shapes:
I updated the shape initialization to allow for easier polygon creation and allow for circles. Currently, only polygon-polygon and circle-polygon collisions work, as the circles borrow collision checks from polygons. Next update I will create a new lightweight check for circle-circle, as it does not need to be as complex as polygons.
Instead of manually adding points to create non-rectangle polygons, you can now specify the number of points as well as the width and height. This can create most simple polygons, but still leaves the option to specify points for more complex shapes.
Shapes can now be created as linear static, angular static, or both. Static prevents forces from other shapes applying that respective force. Instead, that force will be either converted to it’s non-static force type or applied to the non-static shape.
In the previous update, I said that it would be the last one due to flavortown ending. Turns out, flavortown got extended another month. Next update will probably be the real final update during flavortown, but follow the GitHub for future updates. In preparation for this final update, I added a trash button. For now, it just resets the environment back to its starting position.

Attachment
0
Chanied

Friction Calculations:
I reused the code that generates separating impulses to calculate friction. Since friction happens across surfaces, it uses the tangent to the normal. Static friction is used as a stopping force, and kinetic friction works as a slowing force. That means that static holds the object still while kinetic brings it closer to stopping. The forces are applied in the same way, and just add on to the velocities to be updated later.
Since this is the final update during Flavortown, I spent a lot of time fixing any bugs I could find as well as generally improving the engine. I made the separating vectors apply with the changed velocities to prevent race conditions. In addition to the large rectangle and small triangle, I also added some small squares. EPA now uses epsilons to better account for inaccuracies.
Continuing with my development in dev tools that pause and slow down time over the past few devlogs, I have created a tool to rewind time. While holding the Z key, all elements rendered are saved. After running the engine for a bit, you can hold M to enter rewind mode. Left and right arrow keys parse through individual frames, while up and down quickly move through them. Once M is released, the engine returns back to wherever it was before you started rewinding.

Attachment
0
Chanied

Impulse Calculations:
This final step applies velocities based on the original velocities and the collision to separate the shapes. It uses the MTV to establish a normal to base the direction of the collision off of. It then uses the contact point to calculate the relative velocity at that point as a combination of linear and angular velocity. It also calculates how aligned the relative velocity is with the the normal using the dot product. Only the amount of velocity that is aligned with the collision is counted. Once it has the needed information ready, it is able to calculate the base impulse, or strength of the collision in that same direction.
The applied forces are split into 2 parts: Linear and Angular. The linear force is the impulse multiplied by the inverse mass (1/mass) of that shape. This tells the shape how much it should react to the force (lighter shapes go faster). The angular force is the inverse inertia (1/inertia) multiplied by the cross product of the contact point and the impulse. The more misaligned the center of mass and contact point are to the normal, the more angular momentum the shape gets. All of these forces are saved to the object and applied at the same time later to prevent new velocities affecting further collision calculations in the same step.
The O key now slows down the step rate to allow for seeing issues and being able to easily pause on the problem frames.

Attachment
0
Chanied

Contact Points:
Using the Minimum Translation Vector, or MTV, we got from EPA, I am able to separate the shapes till they are near a collision rather than intersecting. Then, I scan all pairs of points on shape B for the closest line on shape A. The smallest distance is kept, but if there is a close second (within a set tolerance) it will keep both points. Then, it flips it to check all lines on shape B and points on A. If a significantly smaller distance is found, the previously found contact points are replaced. Otherwise, it will steal the second place distance if it was inside the tolerance. Then, the average of the two (if there were two) contact points is found. Instead of applying all forces in the next step on each contact point, it can be approximated at the average of the two points for efficiency and reliability.
When you hover over a shape, it now shows the number of that shape in the corner for debugging in addition to highlighting it. If holding shift, it will now highlight the closest point on that shape and display its index for further debugging.
I modified the pause functionality further to allow certain processes to continue even while paused. This includes shape highlighting, shape dragging, shape throwing (velocity is applied after unpause), and the new shape point highlighting.

Attachment
0
Chanied

EPA Algorithm:
The majority of this update was focused on the Expanding Polytype Algorithm.
The algorithim starts with the 3 point simplex that GJK terminated with (Cyan). It then iteratively finds the closes point on any line in the simplex (Yellow), and adds the GJK point furthest in that direction to the simplex. Once the GJK point it finds is already contained within the simplex, the original point it found on the line is the Minimum Translation Vector, or MTV. This represents the minimum distance that can be applied to the shapes to separate them. The MTV is the closest approximation to how the shapes moved since colliding and once applied allows for the contact points to be found. In some cases, an MTV can not be found. It will terminate early to save performance if the shapes are perfectly colliding or the same closest point is found twice in a row. Otherwise, it will terminate after a set number of attempts. The default attempts is 30, but it may need to be higher for complex shapes.
I split up the code to find a point using the Minkowski Difference from the code that sets and checks new GJK points. This allowed me to reuse the now general purpose code to generate points for EPA as well.
I added a new class dedicated to controlling the rendering and logging of debug information. Instead of commenting out lines that I don’t need anymore, but may need again in the future, this class controls what debug info is allowed to print or display. For now, it is vague categories and is hard on the eyes, but I plan to create functions to easily integrate these toggles with future code.
In addition to the debug toggles, I also allowed drawn lines and points to have configurable sizes. This allows for stacking lines and points of various colors and seeing both as well as the order they were created in.
Last update I enabled pausing physics calculations by holding the space key, and now I added the P key as a pause toggle.

Attachment
0
Chanied

GJK Algorithm:
I worked on porting my implementation of GJK over to the new Java physics engine.
I added a system of saving points and lines to be rendered each frame for debugging. To save these, I created new classes to save color data along with their respective points and lines. With the introduction of drawing specific data, I split the multi-threading synchronized lock into separate drawing data and object data locks.
I added keyboard input logging. It keeps track of when keys are pressed and released. So far, it is only used to pause the simulation using the space key. This was important for inspecting the data of individual frames to find errors. Soon, I plan to add more frame controlling keys to quickly recreate and debug issues.
With the help of the new debug tools, I was able to pause and inspect frames for what went wrong instead of attempting to make sense of hundreds of console debug lines.
GJK works off of a simple idea to determine collisions. Imagine a shape where a point was created from all possible combinations of shape A’s points subtracted from shape B’s points. If that new shape contained the coordinates (0,0), then the two shapes are colliding. However, calculating all of these (mostly interior) points is quite computationally expensive. Instead, you can find the points furthest in opposite directions to guarantee a perimeter point. In addition to that, by specifying the target direction (relative to the other found points and the origin) you can find whether the new shape contains the origin with only 3 points. Although does occasionally require a few iterations to create a working simplex, it is significantly more efficient.

Attachment
0
Chanied

Base engine:
I started porting my engine a little over a week before I decided to submit the file to flavor town.
I started off learning Java Swing and Jframe, as well as how to keep my code thread-safe. Once I had a solid base to transfer information back and forth between threads, I began working on a rendering system.

While working on the rendering, I also started defining the class for physics objects. To simplify functions and allow the return of both x and y coordinates from them, I created a Vector class that holds both coordinates and functions that use or modify coordinates.

To help test future collisions, I added the ability to move/throw shapes by dragging them.
Of the three stages of Collision (AABB, GJK, and EPA), so far I have completed AABB, or Axis Aligned Bounding Box. This is a lightweight check designed to identify objects that could not be touching. Currently, I’m working on implementing GJK and EPA to detect collisions between objects.

Attachment
0