Skip to content

Commit eec56a5

Browse files
committed
fix: Refactor bepu contact management, support compound shape child index lookup
1 parent 03ab659 commit eec56a5

File tree

6 files changed

+286
-204
lines changed

6 files changed

+286
-204
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public abstract class CollidableComponent : EntityComponent
4646
private CollisionGroup _collisionGroup;
4747

4848
private ICollider _collider;
49-
private IContactEventHandler? _trigger;
49+
private IContactHandler? _trigger;
5050
private ISimulationSelector _simulationSelector = SceneBasedSimulationSelector.Shared;
5151

5252
[DataMemberIgnore]
@@ -215,7 +215,7 @@ public CollisionGroup CollisionGroup
215215
/// Provides the ability to collect and mutate contact data when this object collides with other objects.
216216
/// </summary>
217217
[Display(category: CategoryContacts)]
218-
public IContactEventHandler? ContactEventHandler
218+
public IContactHandler? ContactEventHandler
219219
{
220220
get
221221
{
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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+
/// Enumerate over this structure to get individual contacts
11+
/// </summary>
12+
/// <code>
13+
/// <![CDATA[
14+
/// void OnStartedTouching<TManifold>(ContactData<TManifold> contactData) where TManifold : unmanaged, IContactManifold<TManifold>
15+
/// {
16+
/// foreach (var contact in contactData)
17+
/// {
18+
/// contact.Normal ...
19+
/// }
20+
/// }
21+
/// ]]>
22+
/// </code>
23+
public readonly ref struct ContactData<TManifold> where TManifold : unmanaged, IContactManifold<TManifold>
24+
{
25+
/// <summary>
26+
/// The collidable which is bound to this <see cref="IContactHandler"/>
27+
/// </summary>
28+
public CollidableComponent EventSource { get; init; }
29+
30+
/// <summary>
31+
/// The other collidable
32+
/// </summary>
33+
public CollidableComponent Other { get; init; }
34+
35+
/// <summary>
36+
/// The simulation this contact occured in
37+
/// </summary>
38+
public BepuSimulation Simulation { get; init; }
39+
40+
/// <summary>
41+
/// The raw contact manifold
42+
/// </summary>
43+
/// <remarks>
44+
/// Make sure that you understand and handle <see cref="FlippedManifold"/> before using this property
45+
/// </remarks>
46+
public TManifold Manifold { get; init; }
47+
48+
/// <summary>
49+
/// Whether the data within <see cref="Manifold"/> should be treated as flipped from <see cref="EventSource"/>'s perspective,
50+
/// e.g.: the normals in <see cref="Manifold"/> should be inverted.
51+
/// Use <see cref="GetEnumerator"/> instead.
52+
/// </summary>
53+
public bool FlippedManifold { get; init; }
54+
55+
/// <summary>
56+
/// When <see cref="EventSource"/> has a <see cref="Stride.BepuPhysics.Definitions.Colliders.CompoundCollider"/>,
57+
/// this is the index of the collider in that collection which <see cref="Other"/> collided with.
58+
/// </summary>
59+
public int ChildIndexSource { get; init; }
60+
61+
/// <summary>
62+
/// When <see cref="Other"/> has a <see cref="Stride.BepuPhysics.Definitions.Colliders.CompoundCollider"/>,
63+
/// this is the index of the collider in that collection which <see cref="EventSource"/> collided with.
64+
/// </summary>
65+
public int ChildIndexOther { get; init; }
66+
67+
/// <inheritdoc cref="ContactData{TManifold}"/>
68+
public Enumerator GetEnumerator() => new(this);
69+
70+
/// <summary>
71+
/// The enumerator for <see cref="ContactData{TManifold}"/>
72+
/// </summary>
73+
/// <inheritdoc cref="ContactData{TManifold}"/>
74+
public ref struct Enumerator(ContactData<TManifold> data)
75+
{
76+
private int _index = -1;
77+
private ContactData<TManifold> _data = data;
78+
79+
public bool MoveNext()
80+
{
81+
while (_index + 1 < _data.Manifold.Count)
82+
{
83+
_index += 1;
84+
if (_data.Manifold.GetDepth(_index) >= 0)
85+
return true;
86+
}
87+
88+
return false;
89+
}
90+
91+
public Contact Current => new(_index, _data);
92+
}
93+
94+
/// <summary>
95+
/// An individual contact
96+
/// </summary>
97+
/// <inheritdoc cref="ContactData{TManifold}"/>
98+
public readonly ref struct Contact(int index, ContactData<TManifold> data)
99+
{
100+
public int Index { get; } = index;
101+
public ContactData<TManifold> Data { get; } = data;
102+
103+
/// <summary>
104+
/// The contact's normal, oriented based on <see cref="ContactData{TManifold}.EventSource"/>
105+
/// </summary>
106+
public Vector3 Normal => Data.FlippedManifold ? -Data.Manifold.GetNormal(Index) : Data.Manifold.GetNormal(Index);
107+
108+
/// <summary> How far the two collidables intersect </summary>
109+
public float Depth => Data.Manifold.GetDepth(Index);
110+
111+
/// <summary> The position at which the contact occured </summary>
112+
/// <remarks> This may not be accurate if either collidables are not part of the simulation anymore </remarks>
113+
public Vector3 Point
114+
{
115+
get
116+
{
117+
// Pose! is not safe as the component may not be part of the physics simulation anymore, but there's no straightforward fix for this;
118+
// We collect contacts during the physics tick, after the tick, we send contact events.
119+
// At that point, both objects may not be at the same position they made contact at,
120+
// so we can't make this more robust by storing the position they were at on contact within the physics tick.
121+
if (Data.FlippedManifold)
122+
return Data.Other.Pose!.Value.Position + Data.Manifold.GetOffset(Index);
123+
return Data.EventSource.Pose!.Value.Position + Data.Manifold.GetOffset(Index);
124+
}
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)