Skip to content

Commit 67a22b5

Browse files
committed
ContactGroup: Group multiple contacts generated from compound shapes into the Contacts passed to On*Touching
1 parent 0d23f2f commit 67a22b5

File tree

14 files changed

+348
-313
lines changed

14 files changed

+348
-313
lines changed

sources/engine/Stride.BepuPhysics/Stride.BepuPhysics.Tests/BepuTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,12 +271,12 @@ private class Trigger : IContactHandler
271271

272272
public event Action? StartedTouching, StoppedTouching;
273273

274-
public void OnStartedTouching<TManifold>(ContactData<TManifold> manifold) where TManifold : unmanaged, IContactManifold<TManifold>
274+
public void OnStartedTouching<TManifold>(Contacts<TManifold> manifold) where TManifold : unmanaged, IContactManifold<TManifold>
275275
{
276276
StartedTouching?.Invoke();
277277
}
278278

279-
public void OnStoppedTouching<TManifold>(ContactData<TManifold> manifold) where TManifold : unmanaged, IContactManifold<TManifold>
279+
public void OnStoppedTouching<TManifold>(Contacts<TManifold> manifold) where TManifold : unmanaged, IContactManifold<TManifold>
280280
{
281281
StoppedTouching?.Invoke();
282282
}

sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BepuSimulation.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ public sealed class BepuSimulation : IDisposable
5454
internal List<BodyComponent?> Bodies { get; } = new();
5555
internal List<StaticComponent?> Statics { get; } = new();
5656

57-
/// <summary> Required when a component is removed from the simulation and must have its contacts flushed </summary>
58-
internal (int value, CollidableComponent? component) TemporaryDetachedLookup { get; set; }
59-
6057
/// <inheritdoc cref="Stride.BepuPhysics.Definitions.CollisionMatrix"/>
6158
[DataMemberIgnore]
6259
public CollisionMatrix CollisionMatrix = CollisionMatrix.All; // Keep this as a field, user need ref access for writes
@@ -322,19 +319,13 @@ public CollidableComponent GetComponent(CollidableReference collidable)
322319
[MethodImpl(MethodImplOptions.AggressiveInlining)]
323320
public BodyComponent GetComponent(BodyHandle handle)
324321
{
325-
if (TemporaryDetachedLookup.component is BodyComponent detachedBody && handle.Value == TemporaryDetachedLookup.value)
326-
return detachedBody;
327-
328322
var body = Bodies[handle.Value];
329323
Debug.Assert(body is not null, "Handle is invalid, Bepu's array indexing strategy might have changed under us");
330324
return body;
331325
}
332326

333327
public StaticComponent GetComponent(StaticHandle handle)
334328
{
335-
if (TemporaryDetachedLookup.component is StaticComponent detachedStatic && handle.Value == TemporaryDetachedLookup.value)
336-
return detachedStatic;
337-
338329
var statics = Statics[handle.Value];
339330
Debug.Assert(statics is not null, "Handle is invalid, Bepu's array indexing strategy might have changed under us");
340331
return statics;

sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BodyComponent.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -488,14 +488,6 @@ protected override void DetachInner()
488488
BodyReference = null;
489489
}
490490

491-
protected override int GetHandleValue()
492-
{
493-
if (BodyReference is { } bRef)
494-
return bRef.Handle.Value;
495-
496-
throw new InvalidOperationException();
497-
}
498-
499491
/// <summary>
500492
/// A special variant taking the center of mass into consideration
501493
/// </summary>

sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/CharacterComponent.cs

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -163,75 +163,55 @@ protected bool GroundTest(NVector3 groundNormal, float threshold = 0f)
163163
}
164164

165165
bool IContactHandler.NoContactResponse => NoContactResponse;
166-
void IContactHandler.OnStartedTouching<TManifold>(ContactData<TManifold> contactData) => OnStartedTouching(contactData);
167-
void IContactHandler.OnTouching<TManifold>(ContactData<TManifold> contactData) => OnTouching(contactData);
168-
void IContactHandler.OnStoppedTouching<TManifold>(ContactData<TManifold> contactData) => OnStoppedTouching(contactData);
166+
void IContactHandler.OnStartedTouching<TManifold>(Contacts<TManifold> contacts) => OnStartedTouching(contacts);
167+
void IContactHandler.OnTouching<TManifold>(Contacts<TManifold> contacts) => OnTouching(contacts);
168+
void IContactHandler.OnStoppedTouching<TManifold>(Contacts<TManifold> contacts) => OnStoppedTouching(contacts);
169169

170170

171171
protected bool NoContactResponse => false;
172172

173173
/// <inheritdoc cref="IContactHandler.OnStartedTouching{TManifold}"/>
174-
protected virtual void OnStartedTouching<TManifold>(ContactData<TManifold> contactData) where TManifold : unmanaged, IContactManifold<TManifold>
174+
protected virtual void OnStartedTouching<TManifold>(Contacts<TManifold> contacts) where TManifold : unmanaged, IContactManifold<TManifold>
175175
{
176-
foreach (var contact in contactData)
176+
foreach (var contact in contacts)
177177
{
178-
Contacts.Add((contactData.Other, new Contact
178+
Contacts.Add((contacts.Other, new Contact
179179
{
180180
Normal = contact.Normal,
181181
Depth = contact.Depth,
182182
FeatureId = contact.FeatureId,
183-
Offset = contact.Point - (Vector3)contactData.EventSource.Pose!.Value.Position,
183+
Offset = contact.Point - (Vector3)contacts.EventSource.Pose!.Value.Position,
184184
}));
185185
}
186186
}
187187

188188
/// <inheritdoc cref="IContactHandler.OnTouching{TManifold}"/>
189-
protected virtual void OnTouching<TManifold>(ContactData<TManifold> contactData) where TManifold : unmanaged, IContactManifold<TManifold>
189+
protected virtual void OnTouching<TManifold>(Contacts<TManifold> contacts) where TManifold : unmanaged, IContactManifold<TManifold>
190190
{
191-
int contactsRetained = 0;
192191
for (int i = Contacts.Count - 1; i >= 0; i--)
193192
{
194-
var contact = Contacts[i];
195-
if (contact.Source != contactData.Other)
196-
{
197-
foreach (var newContact in contactData)
198-
{
199-
if (newContact.FeatureId == contact.Contact.FeatureId)
200-
{
201-
contactsRetained |= 1 << newContact.Index;
202-
goto RETAIN_CONTACT;
203-
}
204-
}
205-
206-
Contacts.RemoveAt(i);
207-
}
208-
209-
RETAIN_CONTACT:
210-
{
211-
}
193+
if (Contacts[i].Source == contacts.Other)
194+
Contacts.SwapRemoveAt(i);
212195
}
213196

214-
foreach (var contact in contactData)
197+
foreach (var contact in contacts)
215198
{
216-
if ((contactsRetained & (1 << contact.Index)) == 0)
199+
Contacts.Add((contacts.Other, new Contact
217200
{
218-
Contacts.Add((contactData.Other, new Contact
219-
{
220-
Normal = contact.Normal,
221-
Depth = contact.Depth,
222-
FeatureId = contact.FeatureId,
223-
Offset = contact.Point - (Vector3)contactData.EventSource.Pose!.Value.Position,
224-
}));
225-
}
201+
Normal = contact.Normal,
202+
Depth = contact.Depth,
203+
FeatureId = contact.FeatureId,
204+
Offset = contact.Point - (Vector3)contacts.EventSource.Pose!.Value.Position,
205+
}));
226206
}
227207
}
228208

229209
/// <inheritdoc cref="IContactHandler.OnStoppedTouching{TManifold}"/>
230-
protected virtual void OnStoppedTouching<TManifold>(ContactData<TManifold> contactData) where TManifold : unmanaged, IContactManifold<TManifold>
210+
protected virtual void OnStoppedTouching<TManifold>(Contacts<TManifold> contacts) where TManifold : unmanaged, IContactManifold<TManifold>
231211
{
232212
for (int i = Contacts.Count - 1; i >= 0; i--)
233213
{
234-
if (Contacts[i].Source == contactData.Other)
214+
if (Contacts[i].Source == contacts.Other)
235215
Contacts.SwapRemoveAt(i);
236216
}
237217
}

sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/CollidableComponent.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ internal void TryUpdateFeatures()
263263
internal void ReAttach(BepuSimulation onSimulation)
264264
{
265265
Versioning = Interlocked.Increment(ref VersioningCounter);
266-
Detach(true);
266+
Detach();
267267

268268
Debug.Assert(Processor is not null);
269269

@@ -291,12 +291,12 @@ internal void ReAttach(BepuSimulation onSimulation)
291291
Processor?.OnPostAdd?.Invoke(this);
292292
}
293293

294-
internal void Detach(bool reAttaching)
294+
internal void Detach()
295295
{
296296
if (Simulation is null)
297297
return;
298298

299-
int getHandleValue = GetHandleValue();
299+
uint handleValue = CollidableReference!.Value.Packed;
300300

301301
Versioning = Interlocked.Increment(ref VersioningCounter);
302302
Processor?.OnPreRemove?.Invoke(this);
@@ -315,12 +315,7 @@ internal void Detach(bool reAttaching)
315315
}
316316

317317
DetachInner();
318-
if (reAttaching == false)
319-
{
320-
Simulation.TemporaryDetachedLookup = (getHandleValue, this);
321-
Simulation.ContactEvents.ClearCollisionsOf(this); // Ensure that removing this collidable sends the appropriate contact events to listeners
322-
Simulation.TemporaryDetachedLookup = (-1, null);
323-
}
318+
Simulation.ContactEvents.ClearCollisionsOf(this, handleValue); // Ensure that removing this collidable sends the appropriate contact events to listeners
324319

325320
Simulation = null;
326321
}
@@ -363,8 +358,6 @@ protected void TryUpdateMaterialProperties()
363358
/// </remarks>
364359
protected abstract void DetachInner();
365360

366-
protected abstract int GetHandleValue();
367-
368361

369362
protected void RegisterContactHandler()
370363
{
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
2+
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
3+
4+
using BepuPhysics.CollisionDetection;
5+
using Stride.Core.Mathematics;
6+
7+
namespace Stride.BepuPhysics.Definitions.Contacts;
8+
9+
/// <summary>
10+
/// An individual contact
11+
/// </summary>
12+
/// <inheritdoc cref="Contacts{TManifold}"/>
13+
public ref struct Contact<TManifold> where TManifold : unmanaged, IContactManifold<TManifold>
14+
{
15+
/// <summary>
16+
/// The index used when reading into this group's manifold to retrieve this contact
17+
/// </summary>
18+
public readonly int Index;
19+
20+
/// <summary>
21+
/// The contact info pair this contact is a part of
22+
/// </summary>
23+
public readonly Contacts<TManifold> Contacts;
24+
25+
// This is not readonly specifically because we're calling instance method on this
26+
// object which may cause the JIT to do a copy before each call
27+
/// <summary>
28+
/// The group this contact is a part of
29+
/// </summary>
30+
public ContactGroup<TManifold> ContactGroup;
31+
32+
internal Contact(int index, Contacts<TManifold> contacts, in ContactGroup<TManifold> contactGroup)
33+
{
34+
Index = index;
35+
Contacts = contacts;
36+
ContactGroup = contactGroup;
37+
}
38+
39+
/// <summary> How far the two collidables intersect </summary>
40+
public float Depth => ContactGroup.Manifold.GetDepth(Index);
41+
42+
/// <summary> Gets the feature id associated with this contact </summary>
43+
public int FeatureId => ContactGroup.Manifold.GetFeatureId(Index);
44+
45+
/// <summary>
46+
/// The contact's normal, oriented based on <see cref="Contacts{TManifold}.EventSource"/>
47+
/// </summary>
48+
public Vector3 Normal => Contacts.IsSourceOriginalA ? -ContactGroup.Manifold.GetNormal(Index) : ContactGroup.Manifold.GetNormal(Index);
49+
50+
/// <summary>
51+
/// When <see cref="Contacts{TManifold}.EventSource"/> has a <see cref="Stride.BepuPhysics.Definitions.Colliders.CompoundCollider"/>,
52+
/// this is the index of the collider in that collection which <see cref="Contacts{TManifold}.Other"/> collided with.
53+
/// </summary>
54+
public int SourceChildIndex => Contacts.IsSourceOriginalA ? ContactGroup.ChildIndexA : ContactGroup.ChildIndexB;
55+
56+
/// <summary>
57+
/// When <see cref="Contacts{TManifold}.Other"/> has a <see cref="Stride.BepuPhysics.Definitions.Colliders.CompoundCollider"/>,
58+
/// this is the index of the collider in that collection which <see cref="Contacts{TManifold}.EventSource"/> collided with.
59+
/// </summary>
60+
public int OtherChildIndex => Contacts.IsSourceOriginalA ? ContactGroup.ChildIndexB : ContactGroup.ChildIndexA;
61+
62+
/// <summary> The position at which the contact occured </summary>
63+
/// <remarks> This may not be accurate if either collidables are not part of the simulation anymore </remarks>
64+
public Vector3 Point
65+
{
66+
get
67+
{
68+
// Pose! is not safe as the component may not be part of the physics simulation anymore, but there's no straightforward fix for this;
69+
// We collect contacts during the physics tick, after the tick, we send contact events.
70+
// At that point, both objects may not be at the same position they made contact at,
71+
// so we can't make this more robust by storing the position they were at on contact within the physics tick.
72+
return (Contacts.IsSourceOriginalA ? Contacts.EventSource.Pose!.Value.Position : Contacts.Other.Pose!.Value.Position) + ContactGroup.Manifold.GetOffset(Index);
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)