Skip to content

Commit

Permalink
Further poking.
Browse files Browse the repository at this point in the history
  • Loading branch information
RossNordby committed Nov 9, 2024
1 parent 646ed34 commit f3737d6
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 149 deletions.
165 changes: 19 additions & 146 deletions BepuPhysics/Collidables/ConvexHullHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -525,35 +525,28 @@ static void AddIfNotPresent(ref QuickList<int> list, int value, BufferPool pool)
public struct DebugStep
{
public EdgeEndpoints SourceEdge;
public List<int> Raw;
public List<int> Reduced;
public int[] Raw;
public int[] Premerged;
public int[] Reduced;
public bool[] AllowVertex;
public Vector3 FaceNormal;
public Vector3 BasisX;
public Vector3 BasisY;
public List<int> FaceStarts;
public List<int> FaceIndices;
public bool[] FaceDeleted;
public int[] MergedFaceIndices;
public int FaceIndex;
public Vector3[] FaceNormals;

internal DebugStep(EdgeEndpoints sourceEdge, ref QuickList<int> raw, Vector3 faceNormal, Vector3 basisX, Vector3 basisY, ref QuickList<int> reduced, ref Buffer<int> allowVertex, ref QuickList<EarlyFace> faces, Span<int> mergedFaceIndices, int faceIndex)
internal DebugStep(EdgeEndpoints sourceEdge, ref QuickList<int> raw, Vector3 faceNormal, Vector3 basisX, Vector3 basisY, ref QuickList<int> reduced, ref Buffer<int> allowVertex, ref QuickList<EarlyFace> faces, int faceIndex)
{
SourceEdge = sourceEdge;
FaceNormal = faceNormal;
BasisX = basisX;
BasisY = basisY;
Raw = new List<int>();
for (int i = 0; i < raw.Count; ++i)
{
Raw.Add(raw[i]);
}
Reduced = new List<int>();
for (int i = 0; i < reduced.Count; ++i)
{
Reduced.Add(reduced[i]);
}
((Span<int>)raw).ToArray();
((Span<int>)reduced).ToArray();
Premerged = null;
AllowVertex = new bool[allowVertex.Length];
for (int i = 0; i < allowVertex.Length; ++i)
{
Expand All @@ -572,9 +565,14 @@ internal DebugStep(EdgeEndpoints sourceEdge, ref QuickList<int> raw, Vector3 fac
FaceDeleted[i] = face.Deleted;
FaceNormals[i] = face.Normal;
}
MergedFaceIndices = mergedFaceIndices.ToArray();
FaceIndex = faceIndex;
}

internal void UpdateForFaceMerge(Span<int> mergedFaceIndices)
{
PremergeFaceIndices = this.MergedFaceIndices;

Check failure on line 573 in BepuPhysics/Collidables/ConvexHullHelper.cs

View workflow job for this annotation

GitHub Actions / build

The name 'PremergeFaceIndices' does not exist in the current context

Check failure on line 573 in BepuPhysics/Collidables/ConvexHullHelper.cs

View workflow job for this annotation

GitHub Actions / build

'ConvexHullHelper.DebugStep' does not contain a definition for 'MergedFaceIndices' and no accessible extension method 'MergedFaceIndices' accepting a first argument of type 'ConvexHullHelper.DebugStep' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 573 in BepuPhysics/Collidables/ConvexHullHelper.cs

View workflow job for this annotation

GitHub Actions / build

The name 'PremergeFaceIndices' does not exist in the current context

Check failure on line 573 in BepuPhysics/Collidables/ConvexHullHelper.cs

View workflow job for this annotation

GitHub Actions / build

'ConvexHullHelper.DebugStep' does not contain a definition for 'MergedFaceIndices' and no accessible extension method 'MergedFaceIndices' accepting a first argument of type 'ConvexHullHelper.DebugStep' could be found (are you missing a using directive or an assembly reference?)
this.MergedFaceIndices = mergedFaceIndices.ToArray();

Check failure on line 574 in BepuPhysics/Collidables/ConvexHullHelper.cs

View workflow job for this annotation

GitHub Actions / build

'ConvexHullHelper.DebugStep' does not contain a definition for 'MergedFaceIndices' and no accessible extension method 'MergedFaceIndices' accepting a first argument of type 'ConvexHullHelper.DebugStep' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 574 in BepuPhysics/Collidables/ConvexHullHelper.cs

View workflow job for this annotation

GitHub Actions / build

'ConvexHullHelper.DebugStep' does not contain a definition for 'MergedFaceIndices' and no accessible extension method 'MergedFaceIndices' accepting a first argument of type 'ConvexHullHelper.DebugStep' could be found (are you missing a using directive or an assembly reference?)
}
}
/// <summary>
/// Computes the convex hull of a set of points.
Expand Down Expand Up @@ -720,7 +718,6 @@ public static void ComputeHull(Span<Vector3> points, BufferPool pool, out HullDa
var faces = new QuickList<EarlyFace>(points.Length, pool);
var edgesToTest = new QuickList<EdgeToTest>(points.Length, pool);
var submittedEdgeTests = new QuickSet<EdgeEndpoints, EdgeEndpoints>(points.Length, pool);
var facesNeedingMerge = new QuickList<int>(32, pool);
if (reducedFaceIndices.Count >= 3)
{
//The initial face search found an actual face! That's a bit surprising since we didn't start from an edge offset, but rather an arbitrary direction.
Expand Down Expand Up @@ -754,7 +751,7 @@ public static void ComputeHull(Span<Vector3> points, BufferPool pool, out HullDa
}
Vector3Wide.ReadFirst(initialBasisX, out var debugInitialBasisX);
Vector3Wide.ReadFirst(initialBasisY, out var debugInitialBasisY);
steps.Add(new DebugStep(initialSourceEdge, ref rawFaceVertexIndices, initialFaceNormal, debugInitialBasisX, debugInitialBasisY, ref reducedFaceIndices, ref allowVertices, ref faces, default, reducedFaceIndices.Count >= 3 ? 0 : -1));
steps.Add(new DebugStep(initialSourceEdge, ref rawFaceVertexIndices, initialFaceNormal, debugInitialBasisX, debugInitialBasisY, ref reducedFaceIndices, ref allowVertices, ref faces, reducedFaceIndices.Count >= 3 ? 0 : -1));

int facesDeletedCount = 0;

Expand Down Expand Up @@ -783,7 +780,7 @@ public static void ComputeHull(Span<Vector3> points, BufferPool pool, out HullDa

if (reducedFaceIndices.Count < 3)
{
steps.Add(new DebugStep(edgeToTest.Endpoints, ref rawFaceVertexIndices, faceNormal, basisX, basisY, ref reducedFaceIndices, ref allowVertices, ref faces, default, -1));
steps.Add(new DebugStep(edgeToTest.Endpoints, ref rawFaceVertexIndices, faceNormal, basisX, basisY, ref reducedFaceIndices, ref allowVertices, ref faces, -1));
//Degenerate face found; don't bother creating work for it.
continue;
}
Expand Down Expand Up @@ -814,146 +811,22 @@ public static void ComputeHull(Span<Vector3> points, BufferPool pool, out HullDa
}
}
// Rerun reduction for the merged face.
var step = new DebugStep(edgeToTest.Endpoints, ref rawFaceVertexIndices, faceNormal, basisX, basisY, ref reducedFaceIndices, ref allowVertices, ref faces, i);
face.VertexIndices.Count = 0;
facePoints.Count = 0;
//steps.Add(new DebugStep(edgeToTest.Endpoints, ref rawFaceVertexIndices, faceNormal, basisX, basisY, ref reducedFaceIndices, ref allowVertices, ref faces, facesNeedingMerge, i));
face.VertexIndices.EnsureCapacity(rawFaceVertexIndices.Count, pool);
ReduceFace(ref rawFaceVertexIndices, faceNormal, points, planeEpsilonNarrow, ref facePoints, ref allowVertices, ref face.VertexIndices);
steps.Add(step);
mergedFace = true;
break;
}
}
var faceCountPriorToAdd = faces.Count;

//facesNeedingMerge.Count = 0;
//while (true)
//{
// //This implementation bites the bullet pretty hard on numerical problems. They most frequently arise from near coplanar vertices.
// //It's possible that two iterations see different subsets of 'coplanar' vertices, either causing two faces with near equal normal or
// //even causing an edge to have more than two faces associated with it.
// //Before making any modifications to the existing data, iterate over all the edges in the current face to check for any such edges.

// //While the usual flow here will be to reduce the vertices we just found and accept the result immediately,
// //it's possible for the just-detected face to end up coplanar with an existing face because of numerical issues that prevented detecting the whole face earlier.
// //Rather than using more precise math, we detect the failure and merge faces after the fact.
// //Unfortunately, this can happen recursively! Merging two redundant faces could reveal a third face that also needs to be merged.
// //This is uncommon, but it needs to be covered.
// //So, we just stick everything into a while loop and let it iterate until it's done.
// //The good news is that the merging process won't loop forever- every merge results in a face that is at least as large as any contributor, and any internal points
// //that get reduced out of consideration are marked with allowVertex[whateverInteriorPointIndex] = false.
// int previousFaceMergeCount = facesNeedingMerge.Count;
// var previousEndpointIndex = reducedFaceIndices[reducedFaceIndices.Count - 1];
// for (int i = 0; i < reducedFaceIndices.Count; ++i)
// {
// EdgeEndpoints edgeEndpoints;
// edgeEndpoints.A = previousEndpointIndex;
// edgeEndpoints.B = reducedFaceIndices[i];
// previousEndpointIndex = edgeEndpoints.B;

// if (facesForEdges.GetTableIndices(ref edgeEndpoints, out var tableIndex, out var elementIndex))
// {
// //There is already at least one face associated with this edge. Do we need to merge?
// ref var edgeFaces = ref facesForEdges.Values[elementIndex];
// Debug.Assert(edgeFaces.FaceA >= 0);
// var aDot = Vector3.Dot(faces[edgeFaces.FaceA].Normal, faceNormal);
// if (edgeFaces.FaceB >= 0)
// {
// //The edge already has two faces associated with it. This is definitely a numerical error; separate coplanar faces were generated.
// //We *must* merge at least one pair of faces.
// //Note that technically both faces can end up being included, even though we've already tested A and B;
// //the new face could be a bridge that's close enough to both, even if they were barely too far from each other.
// var bDot = Vector3.Dot(faces[edgeFaces.FaceB].Normal, faceNormal);
// if (aDot >= normalCoplanarityEpsilon && bDot >= normalCoplanarityEpsilon)
// {
// AddIfNotPresent(ref facesNeedingMerge, edgeFaces.FaceA, pool);
// AddIfNotPresent(ref facesNeedingMerge, edgeFaces.FaceB, pool);
// }
// else
// {
// //If both aren't merge candidates, then just pick the one that's closer.
// AddIfNotPresent(ref facesNeedingMerge, aDot > bDot ? edgeFaces.FaceA : edgeFaces.FaceB, pool);
// }
// }
// else
// {
// //Only one face already present. Check if it's coplanar.
// if (aDot >= normalCoplanarityEpsilon)
// AddIfNotPresent(ref facesNeedingMerge, edgeFaces.FaceA, pool);
// }
// }
// }

// if (facesNeedingMerge.Count > previousFaceMergeCount)
// {
// //This iteration has found more faces which should be merged together.
// //Accumulate the vertices.
// for (int i = previousFaceMergeCount; i < facesNeedingMerge.Count; ++i)
// {
// ref var sourceFace = ref faces[facesNeedingMerge[i]];
// for (int j = 0; j < sourceFace.VertexIndices.Count; ++j)
// {
// var vertexIndex = sourceFace.VertexIndices[j];
// if (allowVertices[vertexIndex] != 0 && !rawFaceVertexIndices.Contains(vertexIndex))
// {
// rawFaceVertexIndices.Allocate(pool) = vertexIndex;
// }
// }
// }

// //Re-reduce.
// reducedFaceIndices.Count = 0;
// facePoints.Count = 0;
// ReduceFace(ref rawFaceVertexIndices, faceNormal, points, planeEpsilonNarrow, ref facePoints, ref allowVertices, ref reducedFaceIndices);
// }
// else
// {
// //No more faces need to be merged! Go ahead and apply the changes.
// for (int i = 0; i < facesNeedingMerge.Count; ++i)
// {
// //Note that we do not merge into an existing face; for simplicity, we just merge into the face we're going to append and mark the old faces as being deleted.
// //Note that we do *not* remove deleted faces from the faces list. That would break all the face indices that we've accumulated.
// var faceIndex = facesNeedingMerge[i];
// ref var faceToRemove = ref faces[faceIndex];
// faceToRemove.Deleted = true;

// //For every edge of any face we're merging, remove the face from the edge lists.
// var previousIndex = faceToRemove.VertexIndices[faceToRemove.VertexIndices.Count - 1];
// for (int j = 0; j < faceToRemove.VertexIndices.Count; ++j)
// {
// RemoveFaceFromEdge(previousIndex, faceToRemove.VertexIndices[j], faceIndex, ref facesForEdges);
// previousIndex = faceToRemove.VertexIndices[j];
// }
// //Remove any references to this face in the edges to test.
// int removedEdgesToTestCount = 0;
// for (int j = 0; j < edgesToTest.Count; ++j)
// {
// if (edgesToTest[j].FaceIndex == faceIndex)
// {
// ++removedEdgesToTestCount;
// }
// else if (removedEdgesToTestCount > 0)
// {
// //Scoot edges to test back.
// edgesToTest[j - removedEdgesToTestCount] = edgesToTest[j];
// }
// }
// edgesToTest.Count -= removedEdgesToTestCount;
// }

// //Retain a count of the deleted faces so the post process can handle them more easily.
// facesDeletedCount += facesNeedingMerge.Count;

// AddFace(ref faces, pool, faceNormal, reducedFaceIndices);
// AddFaceToEdgesAndTestList(pool, ref reducedFaceIndices, ref edgesToTest, ref facesForEdges, faceNormal, faceCountPriorToAdd);
// steps.Add(new DebugStep(edgeToTest.Endpoints, ref rawFaceVertexIndices, faceNormal, basisX, basisY, ref reducedFaceIndices, ref allowVertices, ref faces, facesNeedingMerge, faceCountPriorToAdd));
// break;
// }
//}
if (!mergedFace)
{
var faceCountPriorToAdd = faces.Count;
AddFace(ref faces, pool, faceNormal, reducedFaceIndices);
AddFaceEdgesToTestList(pool, ref reducedFaceIndices, ref edgesToTest, ref submittedEdgeTests, faceNormal, faceCountPriorToAdd);
steps.Add(new DebugStep(edgeToTest.Endpoints, ref rawFaceVertexIndices, faceNormal, basisX, basisY, ref reducedFaceIndices, ref allowVertices, ref faces, facesNeedingMerge, faceCountPriorToAdd));
steps.Add(new DebugStep(edgeToTest.Endpoints, ref rawFaceVertexIndices, faceNormal, basisX, basisY, ref reducedFaceIndices, ref allowVertices, ref faces, faceCountPriorToAdd));
}

if (steps.Count > 500)
Expand Down
8 changes: 5 additions & 3 deletions Demos/SpecializedTests/ConvexHullTestDemo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -528,10 +528,10 @@ public override void Initialize(ContentArchive content, Camera camera)
//{
// hullPoints[i] *= 0.03f;
//}
//var hullPoints = CreateRandomConvexHullPoints();
var hullPoints = CreateRandomConvexHullPoints();
//var hullPoints = CreateMeshConvexHull(content.Load<MeshContent>(@"Content\newt.obj"), new Vector3(1, 1.5f, 1f));
//var hullPoints = CreateHellCube(200);
var hullPoints = CreateBwaa();
//var hullPoints = CreateBwaa();
//var hullPoints = CreatePlaneish();
//var hullPoints = CreateDistantPlane();
//var hullPoints = CreateTestConvexHull();
Expand All @@ -558,7 +558,9 @@ public override void Initialize(ContentArchive content, Camera camera)
//Console.WriteLine($"Largest error: {largestError}");

ConvexHullHelper.ComputeHull(hullPoints, BufferPool, out var hullData, out debugSteps);
this.points = hullPoints;
this.points = new Vector3[hullPoints.Length];
hullPoints.CopyTo(0, this.points, 0, this.points.Length);
//this.points = hullPoints;

var boxHullPoints = CreateBoxConvexHull(2);
var boxHullShape = new ConvexHull(boxHullPoints, BufferPool, out _);
Expand Down

0 comments on commit f3737d6

Please sign in to comment.