Physics Performance Demo: Domino Stress Test
As you may recall, we recently published an in-depth article about how we’ve sped up ROBLOX’s physics by 2-4x as a result of modeling collisions using impulses rather than springs. The “impulse solver,” together with three recent bug fixes, has drastically improved the performance and realism of one of our favorite physics-engine stress tests: Ultimate Dominoes! by armitroner. In this test, a single force cascades through thousands of standing parts, causing them to topple in quick succession.
The video above demonstrates the domino stress test with the impulse solver and three physics bug fixes turned on. As you can see, the entire test runs smoothly, with only a rendering glitch (because rendering and physics do not update at the same frequency) and slight simulation slowdown. Contrast that with an older video from the same place, where the impulse solver and bug fixes are not running.
The drastically improved performance and accuracy of this particular test was not a goal, but a side effect of fixing collision bugs discovered via a memory depletion and our regularly scheduled unit testing. In the remainder of this article, we’ll describe the bugs and our fixes.
A NaN value is essentially an infinitely large number – like one divided by zero. According to Wikipedia, it is a “numeric data type value representing an undefined or unrepresentable value, especially in floating-point calculations.” NaN values can creep into ROBLOX’s physics simulation when two parts have edges that are very close to parallel. This is because when we try to compute their point of intersection, the point falls so far outside the actual bodies that the number is infinitely large and cannot be represented in the simulation. In a massive collision (e.g., the domino stress test), this becomes a significant problem because we’ll substitute the same floating-point value for all NaN values, thus aliasing all affected parts to one spot (e.g., 0,0,0). All the parts then have the same contact points – essentially, everything collides with everything – which quickly eats available memory.
What we’ve done is reduce the threshold at which we calculate intersection points for edges that are very close to parallel. This means we’re calculating fewer intersection points — particularly in the case that we’d introduce a NaN values. We avoid the degenerative case altogether, eliminating the memory depletion in massive collisions.
Our fix is to snap the contact points to the nearest edge as soon as they are calculated. This is better – we’re still computing the intersection between parts to pre-empt collisions, but we’re also snapping the intersection points to the nearest edge to increase accuracy and stability. You can see the difference in the following video.
As you can see, the falling part instead penetrates the object in the lower-right and the physics engine consequently applies a strong contact force to compensate, as visualized by the yellow arrows. This is the result of our old solution, where we used an algorithm to estimate which of the faces (of each stationary part) intersect with the edge (of the falling part). While the prediction was usually correct, computing the intersection point in real time is very difficult when the face is nearly parallel to the edge. That’s why the falling part enters the geometry of the stationary part.
Our fix is “alternative face checking,” where we check both of the faces that meet to form an edge. This gives us a higher chance of locating the intersection point between parallel parts in real time, and modeling a realistic and stable contact force. In the following video, you can see the yellow contact points across all intersections and the resulting stability.
We’re always working toward a bigger, better, more realistic physics simulation for ROBLOX. While all of these fixes address relatively minor bugs, they, together with the impulse solver, enable a big step forward in our engine’s collision component.
The drastically improved performance and accuracy of this particular test was not a goal, but a side effect of fixing collision bugs discovered via a memory depletion and our regularly scheduled unit testing. In the remainder of this article, we’ll describe the bugs and our fixes.
1. NaN values in massive collisions
We initiated our first of the three fixes after noticing a memory depletion associated with massive collisions of ROBLOX parts and tracing the problem back to a geometry-computation bug. Before getting to the fix, we should describe the problem, which stems from excessive NaN (Not a Number) values.A NaN value is essentially an infinitely large number – like one divided by zero. According to Wikipedia, it is a “numeric data type value representing an undefined or unrepresentable value, especially in floating-point calculations.” NaN values can creep into ROBLOX’s physics simulation when two parts have edges that are very close to parallel. This is because when we try to compute their point of intersection, the point falls so far outside the actual bodies that the number is infinitely large and cannot be represented in the simulation. In a massive collision (e.g., the domino stress test), this becomes a significant problem because we’ll substitute the same floating-point value for all NaN values, thus aliasing all affected parts to one spot (e.g., 0,0,0). All the parts then have the same contact points – essentially, everything collides with everything – which quickly eats available memory.
What we’ve done is reduce the threshold at which we calculate intersection points for edges that are very close to parallel. This means we’re calculating fewer intersection points — particularly in the case that we’d introduce a NaN values. We avoid the degenerative case altogether, eliminating the memory depletion in massive collisions.
2. Contact points outside of intersecting parts
The second bug we fixed also involves parallel edges between ROBLOX parts. Again, let’s say we have two parts with edges that are very close to parallel. Originally, we would calculate their contact points to be outside the two bodies, as shown above. We need to do this because it lets us pre-empt collisions that will be happening soon (because the physics simulation happens in discrete steps) and apply an impulse that keeps their geometry from penetrating one another. However, this causes some inaccuracy; in the real world, two bodies cannot touch one another outside themselves.Our fix is to snap the contact points to the nearest edge as soon as they are calculated. This is better – we’re still computing the intersection between parts to pre-empt collisions, but we’re also snapping the intersection points to the nearest edge to increase accuracy and stability. You can see the difference in the following video.
Without this fix, the contact points would jump around the space before settling in their proper place.
3. Multiple contact points for parallel edges
The best way to visualize this bug is to see it. In the following video, the edge of the falling part should collide with the nearest edge of the two stationary parts.As you can see, the falling part instead penetrates the object in the lower-right and the physics engine consequently applies a strong contact force to compensate, as visualized by the yellow arrows. This is the result of our old solution, where we used an algorithm to estimate which of the faces (of each stationary part) intersect with the edge (of the falling part). While the prediction was usually correct, computing the intersection point in real time is very difficult when the face is nearly parallel to the edge. That’s why the falling part enters the geometry of the stationary part.
Our fix is “alternative face checking,” where we check both of the faces that meet to form an edge. This gives us a higher chance of locating the intersection point between parallel parts in real time, and modeling a realistic and stable contact force. In the following video, you can see the yellow contact points across all intersections and the resulting stability.
We’re always working toward a bigger, better, more realistic physics simulation for ROBLOX. While all of these fixes address relatively minor bugs, they, together with the impulse solver, enable a big step forward in our engine’s collision component.
No comments:
Post a Comment