Browse Source

Documented reasons why cached body bounds may be out of sync

Also refactored the code so that there is only 1 assert instead of 2 that check this condition.

See #1522
Jorrit Rouwe 7 months ago
parent
commit
8e654999a4

+ 19 - 0
Jolt/Physics/Body/Body.h

@@ -285,6 +285,25 @@ public:
 	/// Get world space bounding box
 	inline const AABox &	GetWorldSpaceBounds() const										{ return mBounds; }
 
+#ifdef JPH_ENABLE_ASSERTS
+	/// Validate that the cached bounding box of the body matches the actual bounding box of the body.
+	/// If this check fails then there are a number of possible causes:
+	/// 1. Shape is being modified without notifying the system of the change. E.g. if you modify a MutableCompoundShape
+	/// without calling BodyInterface::NotifyShapeChanged then there will be a mismatch between the cached bounding box
+	/// in the broad phase and the bounding box of the Shape.
+	/// 2. You are calling functions postfixed with 'Internal' which are not meant to be called by the application.
+	/// 3. If the actual bounds and cached bounds are very close, it could mean that you have a mismatch in floating
+	/// point unit state between threads. E.g. one thread has flush to zero (FTZ) or denormals are zero (DAZ) set and
+	/// the other thread does not. Or if the rounding mode differs between threads. This can cause small differences
+	/// in floating point calculations. If you are using JobSystemThreadPool you can use JobSystemThreadPool::SetThreadInitFunction
+	/// to initialize the floating point unit state.
+	inline void				ValidateCachedBounds() const
+	{
+		AABox actual_body_bounds = mShape->GetWorldSpaceBounds(GetCenterOfMassTransform(), Vec3::sOne());
+		JPH_ASSERT(actual_body_bounds == mBounds, "Mismatch between cached bounding box and actual bounding box");
+	}
+#endif // JPH_ENABLE_ASSERTS
+
 	/// Access to the motion properties
 	const MotionProperties *GetMotionProperties() const										{ JPH_ASSERT(!IsStatic()); return mMotionProperties; }
 	MotionProperties *		GetMotionProperties()											{ JPH_ASSERT(!IsStatic()); return mMotionProperties; }

+ 1 - 3
Jolt/Physics/Body/BodyManager.cpp

@@ -1146,9 +1146,7 @@ void BodyManager::ValidateActiveBodyBounds()
 		for (BodyID *id = mActiveBodies[type], *id_end = mActiveBodies[type] + mNumActiveBodies[type].load(memory_order_relaxed); id < id_end; ++id)
 		{
 			const Body *body = mBodies[id->GetIndex()];
-			AABox cached = body->GetWorldSpaceBounds();
-			AABox calculated = body->GetShape()->GetWorldSpaceBounds(body->GetCenterOfMassTransform(), Vec3::sOne());
-			JPH_ASSERT(cached == calculated);
+			body->ValidateCachedBounds();
 		}
 }
 #endif // JPH_DEBUG

+ 6 - 6
Jolt/Physics/Collision/BroadPhase/QuadTree.cpp

@@ -1538,14 +1538,14 @@ void QuadTree::ValidateTree(const BodyVector &inBodies, const TrackingVector &in
 					JPH_ASSERT(node_idx == cur_stack.mNodeIndex);
 					JPH_ASSERT(child_idx == i);
 
-					// Validate that the body bounds are bigger or equal to the bounds in the tree
+					// Validate that the body cached bounds still match the actual bounds
+					const Body *body = inBodies[child_node_id.GetBodyID().GetIndex()];
+					body->ValidateCachedBounds();
+
+					// Validate that the node bounds are bigger or equal to the body bounds
 					AABox body_bounds;
 					node.GetChildBounds(i, body_bounds);
-					const Body *body = inBodies[child_node_id.GetBodyID().GetIndex()];
-					AABox cached_body_bounds = body->GetWorldSpaceBounds();
-					AABox real_body_bounds = body->GetShape()->GetWorldSpaceBounds(body->GetCenterOfMassTransform(), Vec3::sOne());
-					JPH_ASSERT(cached_body_bounds == real_body_bounds); // Check that cached body bounds are up to date
-					JPH_ASSERT(body_bounds.Contains(real_body_bounds));
+					JPH_ASSERT(body_bounds.Contains(body->GetWorldSpaceBounds()));
 				}
 			}
 		}