Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigating physics precision loss with increasing distance from world origin. #3034

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

ohlidalp
Copy link
Member

@ohlidalp ohlidalp commented Apr 8, 2023

It was recently brought up on Discord#development that physics accuracy degrades very quickly with distance from the world origin: https://discord.com/channels/136544456244461568/189904947649708032/1093130839350583306. I was very surprised because I know that internally the node positions are stored and updated relative to local physics origin which is always kept close to the vehicle.

Test on master branch
I did a test: load 'simple2' terrain, load 'DAF semitruck', press J to pause physics, align camera on the side, do goto 777777 777777 777777 in the console, press Shift+C for free cam and Alt+C for fixed-freecam, set slow motion ratio to 0.045 (top menubar / settings / slow motion), press K to activate skeletonview and finally J to unpause physics. On master branch, you'll see this:
dafNodesAbs777777

Corrected skeletonview on this branch
I realized the skeletonview shows node's absolute positions, not the relative positions the physics simulation uses. That's why, even though the motion of individual nodes is jerky, the overall motion is quite continuous. After a bit of reading on transformation matrices, I fixed the skeletonview to display the actual node positions. If you perform the same test with this branch, you'll get this result:
dafNodesRel777777
Now the skeletonview moves smoothly but everything else is jerky. This is because both props and cab meshes use absolute node positions. Same applies to flexbodies, flex airfoils and flextires. The camera position is also absolute and follows vehicle's absolute position, that's why it gets crazy jumpy in large distances (the gifs above use fixed-freecam).

Is it all just a visual glitch then? Well, node forces and velocities are always small and centered around their node, so no problem there. However, collisions are all calculated in world space as I saw. Same with airfoils (wings). And there are more minor calculations (fusedrag) which also uses absolute position. I need to do a lot more research.

Reading
Links that helped me make this real:

Links that gave me confidence for further fixing:

@ohlidalp
Copy link
Member Author

ohlidalp commented Apr 9, 2023

I fixed the cab mesh to display undistorted anywhere. I took advantage of the fact the mesh is regenerated every frame. Instead of choosing an arbitrary mesh origin and generating the mesh from node absolute positions, I place the mesh origin at physics origin (which only moves on occasion) and generate the mesh from node relative positions. This creates an illusion of smooth movement within world space while the mesh is actualy not moving, only vertices within the mesh are moving. The same fix can be applied to flextires, flexbodies and flex airfoil, but this commit only fixes the cab mesh as proof of concept.
dafCabmeshRel777777

@CuriousMike56
Copy link
Collaborator

CuriousMike56 commented Apr 10, 2023

Definitely not just a visual glitch, N/Bs become increasingly unstable the further away you are from world origin:
RoR_2023-04-09_20-20-02
(1 beam broken, 100+ deformed)
image
On Grenoble (https://forum.rigsofrods.org/resources/grenoble.399/), everything shakes like crazy, including the first person character camera.

EDIT:
Ignore most of that, you wrote I want to eliminate the graphical glitches first so I can clearly see the physical glitches. on Discord.

@ohlidalp
Copy link
Member Author

ohlidalp commented Apr 11, 2023

Indeed not just a visual glitch, my first test was with Gavril MV4 and it shaked itself apart. But the fact is, visuals make it look way worse than it actually is. I suspect ground collisions and other external forces (wings, fusedrag) to be the culprit, because when I put DAF semitruck in mid-air (notice the tests above are 777777 on all axes) the N/B seems perfectly stable, only visually distorted.

To sum up what I learned so far:

  • 3rdperson camera is jumpy for 2 reasons: A) it's own position is absolute B) it tracks absolute position of the vehicle/character. I have a theoretical idea how to work around both issues at once, but that involves using a custom viewmatrix - I suppose there is a way to do it in OGRE, but I haven't checked yet.
  • The code for N/B integration (just naked mass-spring-damp without any collisions and external forces) works entirely in local space around the actor. The physics origin is always less than 10Km away from the N/B, see Actor::UpdatePhysicsOrigin() in Actor.cpp. That means the physics accuracy fluctuates between micrometer-precision (physics origin is just a few meters away) and millimeter-precision (the 10Km mark). Not great, not terrible.
  • All collisions are done entirely in world space - the input is world positions of nodes, the heightmap is sampled at world precision, we don't record collision forces but update node forces directly (I know because I separated it once for the networked collisions test). I'm fairly sure this is the entire problem. The solution would be something I've wanted to code for a long time (for performance reasons): actor-local collision cache, meaning a snapshot of the surrounding collision geometry + heightmap, all relative to the physics origin. This would also let us fix support for multi-page terrains which broke in circa 2015 when Ulteq optimized the heightmap sampling performance by bringing the code over from OGRE to the game, see TerrainGeometryManager.cpp, line 155 getHeightAtTerrainPosition(). Note that we call this for every node in the N/B for every physics step, and even in this state it still samples the entire heightmap. Pre-sampling just a few surrounding height-pixels and keeping them as collision triangles would surely improve performance further, but I haven't measured anything yet.
  • The math for projecting world positions on screen works roughly this way: mesh verts are in "object space" (= relative to a local origin) and their world transform (position, rotation, scale) is given by "model matrix". For the camera, world transform is given by "view matrix". There's also "projection matrix" which adds perspective and matches things to screen size. The magic property of the transformation matrices is that multiplying modelmatrix*viewmatrix gives you modelviewmatrix which does the objectspace->viewspace translation in one step - meaning, as long as the camera is close to the object, it doesn't matter how far away in world space they are because the matrix multiplication effectivelly cancels that distance out, leaving only a transform over the short object->camera distance. I realized this when looking at props and the character model - although they jittered around the screen, their verts stayed perfectly aligned with each other, even in case of skeletal animations on the character. This is how I fixed the skeletonview (I constructed my own modelmatrix from the actor's physics origin and I use node relative positions) and the cab mesh (I made it stationary at physics origin with the verts following node relative positions) - in both cases, it's the same principle, modelmatrix is fixated at actor's physics origin. Yes, the entire cabmesh/skeleton position in the world is slightly off due to the physics origin being in world space, but you can't see it until it's position updates which happens once in a long time. I assume it will cause a very visible flick, but I'll deal with that later.
  • Theoretically props can be fixed in the same way, but I'm not sure how best do it in OGRE, I see 3 possibilities:
    • Place the prop at physics origin and use vertex shader to put it at the final animated position - it would mean programmatically modifying the material.
    • Programmatically modify the mesh to give it a skeleton with just 1 bone controlling all verts, and essentially do the same as above.
    • Somehow force OGRE to use a customized modelview matrix for that entity. I'm not sure how to do it, maybe via overlays. Either way it would be a big hack.

@paroj Can you please read the above and check if it makes any sense at all? This is the first time I'm digging this deep into 3D. Some of our jargon to make sense of it:

  • props = entities attached to scenenodes and programmatically updated to follow vehicle (rear view mirrors, dashboard, steering wheel...)
  • N/B = Node/Beam - the mass-spring-damper soft body structure of our vehicles.
  • skeletonview = visualization of the N/B.

@CuriousMike56 Hopefully it's not all just babble to you, I'm not entirely sure what I'm doing myself :D

@paroj
Copy link
Contributor

paroj commented Apr 11, 2023

see https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_scene_manager.html#af24677a56a91713c40c3bdb9025da38d

    // Nodes are drawn directly using RelPosition, so that the debugview is accurate even in great distances.
    // To do that, we construct a custom modelview matrix.
I took advantage of the fact the mesh is regenerated every frame. Instead of choosing an arbitrary mesh origin and generating the mesh from node absolute positions, I place the mesh origin at physics origin and generate the mesh from node relative positions. This creates an illusion of smooth movement within world space while the mesh is actualy not moving, only vertices within the mesh are moving. The same fix can be applied to flextires, flexbodies and flex airfoil, but this commit only fixes the cab mesh as proof of concept.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants