-
-
Notifications
You must be signed in to change notification settings - Fork 278
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
Individual Mesh-Mesh Collision #318
Comments
I guess you are in the same scenario as me... I need to verify if a box will suffer a collision before actually moving it to do some actions... I'm trying with ray traces but it's not so robust... |
See the Two annoyances:
If the tests are something you'd want to do every single timestep, you could consider adding a body or static into the simulation to collect collision information without generating constraints (by returning false from the There are also sweep tests (as in the |
Thanks a lot! The |
In the context of queries against the simulation, the broad phase can efficiently provide candidates for testing by using its acceleration structure. The built-in simulation queries use it. If you already know the specific pairs you'd like to test, there's no need for the broad phase. Notably, (The Here's an example of using the sweep in a more isolated context, using the same var testShapeA = DemoMeshHelper.LoadModel(content, BufferPool, @"Content\newt.obj", new Vector3(1, 1, 1));
var testShapeB = new Box(1, 1, 1);
var poseA = new RigidPose(new Vector3(0, 0, 0));
var poseB = new RigidPose(new Vector3(1f, 0, 0));
Simulation.Statics.Add(new StaticDescription(poseA, Simulation.Shapes.Add(testShapeA)));
Simulation.Statics.Add(new StaticDescription(poseB, Simulation.Shapes.Add(testShapeB)));
unsafe bool Intersects<TShapeA, TShapeB>(TShapeA a, RigidPose poseA, TShapeB b, RigidPose poseB)
where TShapeA : struct, IShape
where TShapeB : struct, IShape
{
var filter = new Filter();
var task = Simulation.NarrowPhase.SweepTaskRegistry.GetTask(TShapeA.TypeId, TShapeB.TypeId);
if (task == null)
return false;
return task.Sweep(
Unsafe.AsPointer(ref a), TShapeA.TypeId, poseA.Orientation, default,
Unsafe.AsPointer(ref b), TShapeB.TypeId, poseB.Position - poseA.Position, poseB.Orientation, default,
0f, 1e-2f, 1e-5f, 25, ref filter, Simulation.Shapes, Simulation.NarrowPhase.SweepTaskRegistry, BufferPool,
out _, out _, out _, out _);
}
var intersects = Intersects(testShapeA, poseA, testShapeB, poseB);
Console.WriteLine($"intersects: {intersects}"); Note that you don't have to have a simulation to use this function; you could just create your own with |
It looks like you read my mind! That works very perfectly for me! Maybe we just need some small BepuPhysics.EasyWay library to handle some of those hard things! hahaha :D If you accept, I will try to compile some utilities and send you an MR! |
I often have some difficult-to-predict requirements that make that sort of broad contribution difficult to preapprove, but one thing that is definitely useful is just giving me a list of the helpers you ended up using in your project. No need for the actual implementations, either; the primary value is in knowing your specific use cases. Can't guarantee I'll fill them all in "officially," but it helps prioritize and I would appreciate it. |
I really appreciate the input (and, of course, the software itself)! So if I can get by with a For mesh against mesh, I could use most of the code from the I already played around with the code and it seemed that the Thanks! |
Yup.
If you know the exact pair you want to test, you can skip the broad phase entirely and just use the For example: /// <summary>
/// Adds a shape pair to the collision batcher for testing. Does not flush the batcher.
/// </summary>
/// <typeparam name="TShapeA">Type of the first shape to test.</typeparam>
/// <typeparam name="TShapeB">Type of the second shape to test.</typeparam>
/// <typeparam name="TCallbacks">Type of the callbacks to use for the collision tests.</typeparam>
/// <param name="a">First shape to test.</param>
/// <param name="poseA">Pose of the first shape to test.</param>
/// <param name="b">Second shape to test.</param>
/// <param name="poseB">Pose of the second shape to test.</param>
/// <param name="speculativeMargin">Speculative margin to use for the collision test.</param>
/// <param name="pairId">Pair id to associate with this test. The pair id will be reported in the callback when the batcher finishes the pair.</param>
/// <param name="batcher">Batcher to add the pair to.</param>
public unsafe void AddPairToBatcher<TShapeA, TShapeB, TCallbacks>(TShapeA a, RigidPose poseA, TShapeB b, RigidPose poseB, float speculativeMargin, int pairId, ref CollisionBatcher<TCallbacks> batcher)
where TShapeA : unmanaged, IShape
where TShapeB : unmanaged, IShape
where TCallbacks : struct, ICollisionCallbacks
{
batcher.Add(TShapeA.TypeId, TShapeB.TypeId, sizeof(TShapeA), sizeof(TShapeB), &a, &b, poseB.Position - poseA.Position, poseA.Orientation, poseB.Orientation, speculativeMargin, pairId);
} This does not directly run the pair test. The test will only be run when enough pairs have been added to the batcher to warrant a batch execution. You can also force all pairs to be tested immediately by calling If you wanted to use it in a nonbatched way for a boolean test, you could do something like: /// <summary>
/// Simple callbacks for a single unbatched boolean test.
/// </summary>
struct BooleanCallbacks : ICollisionCallbacks
{
/// <summary>
/// The intersection state of the tested pair. Since there's only one test, there's no need for fancier state.
/// </summary>
public bool Intersected;
public bool AllowCollisionTesting(int pairId, int childA, int childB) => true;
public void OnChildPairCompleted(int pairId, int childA, int childB, ref ConvexContactManifold manifold) { }
public void OnPairCompleted<TManifold>(int pairId, ref TManifold manifold) where TManifold : unmanaged, IContactManifold<TManifold>
{
for (int i = 0; i < manifold.Count; ++i)
{
if (manifold.GetDepth(i) >= 0)
{
Intersected = true;
return;
}
}
}
}
/// <summary>
/// Tests whether to shapes touch.
/// </summary>
/// <remarks>This is a simple unbatched test. It will be much slower than running many tests in vectorized bundles, but shows how it could work conceptually.</remarks>
/// <typeparam name="TShapeA">Type of the first shape to test.</typeparam>
/// <typeparam name="TShapeB">Type of the second shape to test.</typeparam>
/// <typeparam name="TCallbacks">Type of the callbacks to use for the collision tests.</typeparam>
/// <param name="a">First shape to test.</param>
/// <param name="poseA">Pose of the first shape to test.</param>
/// <param name="b">Second shape to test.</param>
/// <param name="poseB">Pose of the second shape to test.</param>
/// <param name="shapes">Shapes collection to look up child shapes. Needed for compounds.</param>
/// <param name="collisionTaskRegistry">Collision task registry defining collision tasks for type pairs.</param>
/// <returns>True if the pair touches, false otherwise.</returns>
public unsafe bool PairTouches<TShapeA, TShapeB>(TShapeA a, RigidPose poseA, TShapeB b, RigidPose poseB, Shapes shapes, CollisionTaskRegistry collisionTaskRegistry)
where TShapeA : unmanaged, IShape
where TShapeB : unmanaged, IShape
{
var collisionBatcher = new CollisionBatcher<BooleanCallbacks>(BufferPool, shapes, collisionTaskRegistry, 0, new BooleanCallbacks());
collisionBatcher.Add(TShapeA.TypeId, TShapeB.TypeId, sizeof(TShapeA), sizeof(TShapeB), &a, &b, poseB.Position - poseA.Position, poseA.Orientation, poseB.Orientation, 0, 0);
collisionBatcher.Flush();
return collisionBatcher.Callbacks.Intersected;
} I should also mention that |
@jbraumann I did the following. I'm still evolving the code but maybe it can help you. public struct CollisionQuery(Simulation simulation)
{
private const float SweepMinimumProgression = 1e-1f;
private const float SweepConvergenceThreshold = 1e-5f;
private const int SweepMaximumIterationCount = 1;
public float BroadPhaseSearchRadius { get; init; } = 1;
public CollidableMobility TargetMobility { get; init; } = CollidableMobility.Dynamic;
public IEnumerable<BodyReference> GetCollisionsAtDirection(
BodyReference body,
Vector3 translation
)
{
var enumerator = new BroadPhaseOverlapEnumerator(simulation)
{
References = new QuickList<CollidableReference>()
};
GetBodiesWithinRadius(body.Pose.Position, ref enumerator);
var shape = simulation.Shapes.GetShape<Box>(body.Collidable.Shape.Index);
foreach (var reference in enumerator.References)
{
if (reference.Mobility != TargetMobility || reference.BodyHandle == body.Handle)
continue;
var otherBody = simulation.Bodies.GetBodyReference(reference.BodyHandle);
var otherShapeType = otherBody.Collidable.Shape;
var otherShape = simulation.Shapes.GetShape<Box>(otherShapeType.Index);
if (WillOverlap(body, ref shape, otherBody, ref otherShape, translation))
{
yield return otherBody;
}
}
enumerator.Dispose();
}
private void GetBodiesWithinRadius(
in Vector3 offset,
scoped ref BroadPhaseOverlapEnumerator enumerator
)
{
var area = new Sphere(BroadPhaseSearchRadius);
area.ComputeBounds(Quaternion.Identity, out var min, out var max);
min += offset;
max += offset;
simulation.BroadPhase.GetOverlaps(min, max, ref enumerator);
}
private bool WillOverlap(
in BodyReference body1,
ref Box shape1,
in BodyReference body2,
ref Box shape2,
in Vector3 translation
)
{
unsafe
{
var task = simulation.NarrowPhase.SweepTaskRegistry.GetTask(Box.TypeId, Box.TypeId);
var filter = Filters.TrueForAll;
if (task is null)
return false;
var expectedPosition = body1.Pose.Position + translation;
var target = body2.Pose.Position - expectedPosition;
var overlaps = task.Sweep(
shapeDataA: Unsafe.AsPointer(ref shape1),
shapeTypeA: Box.TypeId,
orientationA: body1.Pose.Orientation,
velocityA: default,
shapeDataB: Unsafe.AsPointer(ref shape2),
shapeTypeB: Box.TypeId,
offsetB: target,
orientationB: body2.Pose.Orientation,
velocityB: default,
maximumT: 0,
minimumProgression: SweepMinimumProgression,
convergenceThreshold: SweepConvergenceThreshold,
maximumIterationCount: SweepMaximumIterationCount,
filter: ref filter,
shapes: simulation.Shapes,
sweepTasks: simulation.NarrowPhase.SweepTaskRegistry,
pool: simulation.BufferPool,
t0: out _,
t1: out _,
hitLocation: out _,
hitNormal: out _
);
return overlaps;
}
}
} I will check your last message and evaluate that against what I did, @RossNordby ! Thank you! |
I just realized IEnumerable will be on the heap... so maybe I can use the same idea of your enumerator to keep them on the stack instead. |
@danfma Thanks a lot for the code, I'll try it out later today! The application isn't super performance-centric as it only needs to check once, rather than for every frame. |
I would like to check a series of - if necessary convex - meshes for collisions. It is not necessary for my use case for them to react to this collision or to provide parameters like the depth, a boolean value is sufficient.
I am struggling to find a reliable approach to realize that. The best case would be a function where I could just reference the two meshes to check, is there something close to that?
Thanks,
Johannes
The text was updated successfully, but these errors were encountered: