Troubleshooting physics issues
Minie uses assert
statements to verify assumptions made while coding.
During development, Java assertions should be enabled using:
-
the "-ea" JVM argument or
-
the
enableAssertions
setting of GradleJavaExec
tasks.
Similarly, native-code assertions should be enabled during development, by specifying a Minie library of the "debug" type, such as "Minie-8.2.0+debug".
When a physics simulation doesn’t behave as expected, debug visualization should be enabled to uncover gross issues such as incorrect collision shapes, incorrect initial positions, bodies that become deactivated, and collision objects not added to the space.
If further details are desired, temporary print statements may be added at key points. To streamline this process, Minie provides a configurable dumper to print out app states, physics spaces, collision shapes, multibodies, viewports, and scene graphs.
Debug visualization tips and tricks
Debug visualization is disabled by default.
To enable it, configure
the BulletAppState
thus:
bulletAppState.setDebugEnabled(true);
For an example, see the HelloRigidBody tutorial.
High-res convex shapes
By default, debug visualization renders convex collision shapes using meshes with up to 42 vertex locations. Alternatively, it can generate debug meshes with up to 256 vertex locations. To override the low-resolution default on a per-object basis:
collisionObject.setDebugMeshResolution(DebugShapeFactory.highResolution);
This setting is effective only if the object has a convex shape. |
Mesh caching
Debug visualization caches the debug mesh of every non-compound collision shape it renders. To clear the cache, use:
DebugShapeFactory.clearCache();
Viewports
By default, debug visualization renders only to the
application’s main ViewPort
.
To specify a different viewport (or an array of viewports) use:
bulletAppState.setDebugViewPorts(viewPortArray);
For example, see TestDebugToPost.
Which features are rendered
By default, debug visualization renders the shape of every collision object. In addition to the shape, Minie can render other features, such as:
-
its angular velocity,
-
its axis-aligned bounding box,
-
its gravity vector,
-
its CCD swept sphere, and
-
its velocity vector.
However, these features are hidden by default. To override these defaults, set filters to specify for which collision objects each feature should be visualized:
BulletDebugAppState.DebugAppStateFilter all = new FilterAll(true);
BulletDebugAppState.DebugAppStateFilter none = new FilterAll(false);
bulletAppState.setDebugAngularVelocityFilter(none); // no angular velocities
bulletAppState.setDebugBoundingBoxFilter(all); // all bounding boxes
bulletAppState.setDebugFilter(none); // no collision shapes
bulletAppState.setDebugGravityVectorFilter(none); // no gravity vectors
bulletAppState.setDebugSweptSphereFilter(all); // all swept spheres
bulletAppState.setDebugVelocityVectorFilter(none); // no velocity vectors
Centers and local axes
By default, debug visualization doesn’t indicate the centers or local axes of collision objects. To override this default, set the debug axis length to a positive value:
bulletAppState.setAxisLength(1f);
If local axes are visualized, then by default the arrows are one pixel wide. If your graphics driver supports it, you can specify wider arrows:
bulletAppState.setDebugAxisLineWidth(3f); // axis arrows 3 pixels wide
Alternatively, you can specify "solid" arrows:
bulletAppState.setDebugAxisLineWidth(0f); // solid arrows
Materials
By default, Minie visualizes the shapes of collision objects using single-sided wireframe materials.
On Android platforms, the default debug materials are solid colors instead of wireframes. |
By default, the material color indicates the type of object:
-
yellow for a collision object without contact response, including any
PhysicsGhostObject
, -
magenta for a rigid body or
MultiBodyCollider
that’s dynamic, responsive, and active, -
blue for a rigid body or collider (with contact response) that’s static or kinematic or inactive,
-
pink for a
PhysicsCharacter
(with contact response), -
red for a
PhysicsSoftBody
with faces, and -
orange for a
PhysicsSoftBody
with links but no faces.
Number of sides
Some collision objects are best visualized using double-sided materials. You can override the single-sided default on a per-object basis:
collisionObject.setDebugNumSides(2);
setDebugNumSides(0) makes the object’s shape invisible
in debug visualization,
even if the object is selected by the debug filter.
|
Meshes
The default debug materials don’t need lighting, normals, or texture coordinates. By default, debug visualization doesn’t provide these amenities. However, a custom debug material might require them.
Normals
You can override the no-normals default on a per-object basis:
collisionObject1.setDebugMeshNormals(MeshNormals.Facet);
collisionObject2.setDebugMeshNormals(MeshNormals.Smooth);
collisionObject3.setDebugMeshNormals(MeshNormals.Sphere);
Index buffers
Generating index buffers for meshes usually reduces the number of vertices that must be rendered. However, generating index buffers for large meshes can take a long time. By default, Minie doesn’t generate index buffers for debug meshes with than 6,000 vertices.
You can tune this threshold:
DebugShapeFactory.setIndexBuffers(900);
The threshold has no effect on debug meshes previously generated. To make this setting retroactive, clear the debug-mesh cache.
Lighting and shadows
BulletAppState
invokes a callback during initialization.
You can use this callback
to provide lighting and/or shadows for debug visualization.
HelloCustomDebug is a simple application that demonstrates customization of debug materials, debug meshes, and lighting.
Texture coordinates
BulletAppState
invokes a callback each time it generates a debug mesh.
You can use this callback to add texture coordinates to the mesh:
DebugInitListener callbackObject = new DebugMeshInitListener() {
public void debugMeshInit(Mesh debugMesh) {
VertexBuffer pos = debugMesh.getBuffer(VertexBuffer.Type.Position);
int numVertices = pos.getNumElements();
FloatBuffer positions = (FloatBuffer) pos.getDataReadOnly();
FloatBuffer uvs = BufferUtils.createFloatBuffer(2 * numVertices);
// TODO: fill the UV buffer with data
debugMesh.setBuffer(VertexBuffer.Type.TexCoord, 2, uvs);
uvs.flip();
}
};
collisionObject.setDebugMeshInitListener(callbackObject);
Without texture coordinates, objects with plane collision shapes are especially tricky to visualize. For such objects, Minie provides a standard callback class:
float tileSize = 1f;
PlaneDmiListener callbackObject = new PlaneDmiListener(tileSize);
collisionObject.setDebugMeshInitListener(callbackObject);
An introduction to PhysicsDumper
The following temporary statements could be used to dump
(to System.out
) all collision objects in a physics space:
PhysicsDumper dumper = new PhysicsDumper();
dumper.dump(physicsSpace);
Here is sample output for a space containing 2 rigid bodies and nothing else:
PhysicsSoftSpace with 0 chars, 0 ghosts, 0 joints, 2 rigids, 0 softs, 0 vehicles bphase=DBVT grav[y=-30] timeStep[0.0166667 maxSS=4] listeners[c=0 cg=0 t=1] solver[SI iters=10 cfm=0 batch=128 splitImp[th=global erp=0.1] mode=WarmStart,VelocityDependent,SIMD,Cone] conf[epa maxM=4096] rayTest=SubSimplex,HeightfieldAccel SbwInfo grav[y=-30] offset=0 norm[xyz=0] water=0 air=1.2 maxDisp=1000 Rigid Dyn(mass=1) aData=Material"drop2" msLoc[x=-0.824232 y=1.15373 z=-0.322077] loc[x=-0.824511 y=1.15376 z=-0.322339] orient[x=0.229 y=0.945 z=0.091 w=-0.215] contact[fric=0.5 rest=0.3 damp=0.1 pth=1e+18 stiff=1e+18] grav[y=-30] NOTprotected ccd[mth=1 r=2.16983] damp[l=0.6 a=0.6] sleep[lth=0.1 ath=0.1 time=1.93333] v[x=-0.0186754 y=0.00228578 z=-0.017551] force[xyz=0] inert[x=1.38189 y=1.44997 z=1.7007] w[x=-0.0149519 y=0.000702213 z=0.0161844] torq[xyz=0] MultiSphere r[1.15392 0.888288 1.03246] marg=0.04 with 0 ignores and 0 joints Rigid Sta aData=Material"platform" msLoc[y=-2] contact[fric=0.5 rest=0.3 damp=0.1 pth=1e+18 stiff=1e+18] Box he[x=20 y=2 z=20] marg=0.04 with 0 ignores and 0 joints
2-space indentation indicates the hierarchy of spaces/objects/joints. Single-space indentation indicates additional description of the foregoing object. Related values are enclosed in square brackets.
To dump a physics space to a text file:
PrintStream dumpStream = new PrintStream("dump.txt");
PhysicsDumper dumper = new PhysicsDumper(dumpStream);
dumper.dump(physicsSpace);
What is dumped
You can dump an entire BulletAppState
,
including its physics space:
dumper.dump(bulletAppState);
You can dump specific collision objects:
dumper.dump(character);
dumper.dump(multiBodyCollider);
dumper.dump(ghostObject);
dumper.dump(rigidBody);
dumper.dump(softBody);
You can dump specific collision shapes:
dumper.dump(collisionShape, "");
When dumping a space, the default is to describe every collision object; physics joints are counted but not described in detail. To describe the joints in each body, configure the dumper like so:
dumper.setEnabled(DumpFlags.JointsInBodies, true); // default=false
To describe the motors in each joint, configure the dumper like so:
dumper.setEnabled(DumpFlags.Motors, true); // default=false
To dump just the physics joints (no collision objects):
dumper.setEnabled(DumpFlags.Pcos, false); // default=true
dumper.setEnabled(DumpFlags.JointsInSpaces, true); // default=false
When dumping a space, you can apply a filter to restrict which physics objects are listed. For instance, to dump only those physics objects that lack a user object:
String indent = "";
UserFilter noUser = new UserFilter(null);
dumper.dump(physicsSpace, indent, noUser);
Other dump flags can be set to describe the nodes or clusters in each soft body or the child shapes in each compound collision shape.