Replies: 4 comments 11 replies
-
It is possible to have some quality level, but I think there are some caveats:
Yes, that is another complication. Currently the collector only gets information when starting/finishing a new body (
Technically we have Another thing that I can think of is to have a completely seperate code path for sensors in
Obviously I'd prefer it when Sensors keep getting used. The 'parallelism' you'd lose is easy to implement: Jolt basically does 'parallel for each sensor do |
Beta Was this translation helpful? Give feedback.
-
I have been thinking about how we'd go about implementing this and I think the extra code path in JoltPhysics/Jolt/Physics/PhysicsSystem.cpp Line 1242 in 5220a7f will have an extra condition to select a 3rd collision function. I was thinking of the following options:
What do you think? I think I'm leaning towards 1 since it allows people to completely bypass the collision detection system and implement whatever they want. The API is a bit more complex to grasp though. Note that for sensor vs soft body we're tracking only 1 contact per body so I don't think anything is needed there. |
Beta Was this translation helpful? Give feedback.
-
WIP: https://github.com/jrouwe/JoltPhysics/tree/feature/sim_collide_shape (still need to provide an example and the collide function) |
Beta Was this translation helpful? Give feedback.
-
I have something working in #1489 2025-01-31.17-07-28.mp4It's a big sensor box. The scene toggles through all 5 modes that you mentioned above. You should mostly ignore the dynamic bodies, they're there to show that they're not affected by this change. Focus on the green triangles + yellow arrows on the floor that indicate collision. Each 'pyramid' is a sub shape of a compound shape. The collide function itself is quite simple at the moment: template <class LeafCollector>
static void sCollideBodyVsBodyPerLeaf(const Body &inBody1, const Body &inBody2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, CollideShapeSettings &ioCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
{
if (inBody1.IsSensor() || inBody2.IsSensor())
{
// Tracks information we need about a leaf shape
struct LeafShape
{
...
AABox mBounds;
Mat44 mCenterOfMassTransform;
Vec3 mScale;
const Shape * mShape;
SubShapeIDCreator mSubShapeIDCreator;
};
// A collector that stores the information we need from a leaf shape in an array that is usually on the stack but can fall back to the heap if needed
class MyCollector : public TransformedShapeCollector
{
public:
void AddHit(const TransformedShape &inShape) override
{
mHits.emplace_back(inShape.GetWorldSpaceBounds(), inShape.GetCenterOfMassTransform().ToMat44(), inShape.GetShapeScale(), inShape.mShape, inShape.mSubShapeIDCreator);
}
Array<LeafShape, STLLocalAllocator<LeafShape, 32>> mHits;
};
// Get bounds of both shapes
AABox bounds1 = inBody1.GetShape()->GetWorldSpaceBounds(inCenterOfMassTransform1, Vec3::sOne());
AABox bounds2 = inBody2.GetShape()->GetWorldSpaceBounds(inCenterOfMassTransform2, Vec3::sOne());
// Get leaf shapes that overlap with the bounds of the other shape
SubShapeIDCreator part1, part2;
MyCollector leaf_shapes1, leaf_shapes2;
inBody1.GetShape()->CollectTransformedShapes(bounds2, inCenterOfMassTransform1.GetTranslation(), inCenterOfMassTransform1.GetQuaternion(), Vec3::sOne(), part1, leaf_shapes1, inShapeFilter);
inBody2.GetShape()->CollectTransformedShapes(bounds1, inCenterOfMassTransform2.GetTranslation(), inCenterOfMassTransform2.GetQuaternion(), Vec3::sOne(), part2, leaf_shapes2, inShapeFilter);
// Now test each leaf shape against each other leaf
for (const LeafShape &leaf1 : leaf_shapes1.mHits)
for (const LeafShape &leaf2 : leaf_shapes2.mHits)
if (leaf1.mBounds.Overlaps(leaf2.mBounds))
{
LeafCollector collector;
CollisionDispatch::sCollideShapeVsShape(leaf1.mShape, leaf2.mShape, leaf1.mScale, leaf2.mScale, leaf1.mCenterOfMassTransform, leaf2.mCenterOfMassTransform, leaf1.mSubShapeIDCreator, leaf2.mSubShapeIDCreator, ioCollideShapeSettings, collector);
if (collector.HadHit())
ioCollector.AddHit(collector.mHit);
}
}
else
{
// If not a sensor: fall back to the default
PhysicsSystem::sDefaultSimCollideBodyVsBody(inBody1, inBody2, inCenterOfMassTransform1, inCenterOfMassTransform2, ioCollideShapeSettings, ioCollector, inShapeFilter);
}
}
Edit: I've updated the code to implement what I suggested earlier. I'm not sure if it makes sense for me to move this function into Jolt itself as it's not that big and quite custom for what we're trying to achieve here. |
Beta Was this translation helpful? Give feedback.
-
This might be a dumb idea, but I'm curious to hear your take on it.
From what I understand, based on previous conversations and passing mentions, part of the reason for the performance caveat with
mCollideKinematicVsNonDynamic
is the fact that Jolt collects and maintains all of the contacts of these kinds of collisions in its contact cache, which becomes problematic when overlapping potentially intricate shapes, likeMeshShape
.However, the recently added
ClosestHitPerBodyCollisionCollector
got me thinking. Would it be possible to control the underlying collector used during collision detection for bodies, so that you could then effectively control the fidelity of the contacts stored?I imagine you'd be able to choose different levels of fidelity, such as:
You might think of better levels, but hopefully you get my point.
Now, from what I understand, manifolds exist in the context of a pair, so I imagine there would have to be some kind of "smallest common denominator" when picking the collector. Perhaps this is fundamentally problematic for some reason.
I should also say that my comprehension of sub-shapes is a bit murky right now. So, just to be clear, when I say "one contact per sub-shape" I generally mean
JPH::Shape
, i.e. sub-shapes in a compound shape, but not individual triangles of aMeshShape
. Maybe that's a problematic distinction, I'm not sure.My reason for asking is that I've been seeing a worrying amount of projects seemingly just blindly enabling the "Areas Detect Static Bodies" project setting (which enables
mCollideKinematicVsNonDynamic
for sensors) in both the Godot extension and the new Godot module, ever since it was first introduced. I fear that it's become this magic checkbox that gets passed around as "you just need to enable this for things to work", without stopping to ask why they need it in the first place, or what the implications of it are.There are several other problems at play here as well, of course:
Area3D
in Godot quite heavily for stuff that you traditionally could/should use plain shape queries for.StaticBody3D
for stuff that you could also use a kinematic body for.So this is arguably a communication problem to a large degree, but even if we were to put a scary popup whenever you enable the project setting you're likely going to have a certain amount of people who click right past it, find that their project still runs fine (in that moment) and then find themselves in trouble later down the line, without understanding the correlation.
Anyway, I figured since
Area3D
in Godot doesn't actually need all the contacts that make intersections withMeshShape
problematic, nor does it care about where a contact is, since it just needs to know that two shapes overlapped at all, maybe we could just pick a simpler collector and negate some (much?) of the performance impact? Perhaps even to such a degree that we could just remove the setting completely and have it always be enabled?I guess there's also the "last resort" to consider, which we've talked about once before, where we just ditch sensors altogether and re-implement
Area3D
to be a bunch of narrow phase queries, similar to how it was done for Bullet in Godot 3, but from what I understand we'd end up losing some of the parallelism we get to enjoy by using sensors. Frankly, I couldn't even begin to reason about the effective performance differences between the two methods, without actually trying it first.Beta Was this translation helpful? Give feedback.
All reactions