Skip to content

Commit 77ee163

Browse files
committed
improving MinimalTriangle hit testing
1 parent 47276fd commit 77ee163

2 files changed

Lines changed: 34 additions & 21 deletions

File tree

PolygonMesh/Mesh.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,13 @@ public void MarkAsChanged()
590590
{
591591
transformedAabbCache.Changed();
592592
cachedAABB = null;
593+
594+
// Clear any cached mesh trace/BVH data that depends on previous geometry.
595+
// This keeps hit testing in sync after mesh edits while preserving cache behavior.
596+
if (PropertyBag.ContainsKey("MeshTraceData"))
597+
{
598+
PropertyBag.Remove("MeshTraceData");
599+
}
593600
ChangedCount++;
594601
Changed?.Invoke(this, null);
595602
}

PolygonMesh/RayTracer/Primitive/Shapes/MinimalTriangle.cs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,10 @@ public MinimalTriangle(Func<int, int, Vector3Float> vertexFunc, int faceIndex)
3939
double distanceFromOrigin = vertex(0).Dot(planeNormal);
4040
Plane = new PlaneFloat(new Vector3Float(planeNormal), (float)distanceFromOrigin);
4141

42-
center = new Vector3Float((vertex(0) + vertex(1) + vertex(2)) / 3);
43-
var aabbMinXYZ = vertex(0).ComponentMin(vertex(1)).ComponentMin(vertex(2));
44-
var aabbMaxXYZ = vertex(0).ComponentMax(vertex(1)).ComponentMax(vertex(2));
45-
var aabb = new AxisAlignedBoundingBox(aabbMinXYZ, aabbMaxXYZ);
46-
aabbSize = new Vector3Float(aabb.Size);
42+
var aabbMin = vertex(0).ComponentMin(vertex(1)).ComponentMin(vertex(2));
43+
var aabbMax = vertex(0).ComponentMax(vertex(1)).ComponentMax(vertex(2));
44+
center = new Vector3Float((aabbMin + aabbMax) / 2);
45+
aabbSize = aabbMax - aabbMin;
4746

4847
var normalLengths = new[] { Math.Abs(planeNormal.X), Math.Abs(planeNormal.Y), Math.Abs(planeNormal.Z) };
4948
MajorAxis = (byte)normalLengths.Select((v, i) => new { Axis = i, Value = Math.Abs(v) }).OrderBy(o => o.Value).Last().Axis;
@@ -101,7 +100,8 @@ public int FindSideOfLine(Vector2 sidePoint0, Vector2 sidePoint1, Vector2 testPo
101100

102101
public AxisAlignedBoundingBox GetAxisAlignedBoundingBox()
103102
{
104-
return new AxisAlignedBoundingBox(center - aabbSize, center + aabbSize);
103+
Vector3Float halfSize = aabbSize / 2;
104+
return new AxisAlignedBoundingBox(center - halfSize, center + halfSize);
105105
}
106106

107107
public double GetAxisCenter(int axis)
@@ -243,24 +243,30 @@ public override string ToString()
243243

244244
private bool Check2DHitOnMajorAxis(double x, double y)
245245
{
246-
// check the bounding rect
247-
if (x >= boundsOnMajorAxis.Left && x <= boundsOnMajorAxis.Right &&
248-
y >= boundsOnMajorAxis.Bottom && y <= boundsOnMajorAxis.Top)
246+
// quick reject against projected triangle bounds
247+
const double boundsEpsilon = 1e-8;
248+
if (!(x >= boundsOnMajorAxis.Left - boundsEpsilon && x <= boundsOnMajorAxis.Right + boundsEpsilon
249+
&& y >= boundsOnMajorAxis.Bottom - boundsEpsilon && y <= boundsOnMajorAxis.Top + boundsEpsilon))
249250
{
250-
Vector2 vertex0 = new Vector2(vertex(0)[xForMajorAxis], vertex(0)[yForMajorAxis]);
251-
Vector2 vertex1 = new Vector2(vertex(1)[xForMajorAxis], vertex(1)[yForMajorAxis]);
252-
Vector2 vertex2 = new Vector2(vertex(2)[xForMajorAxis], vertex(2)[yForMajorAxis]);
253-
Vector2 hitPosition = new Vector2(x, y);
254-
int sumOfLineSides = FindSideOfLine(vertex0, vertex1, hitPosition);
255-
sumOfLineSides += FindSideOfLine(vertex1, vertex2, hitPosition);
256-
sumOfLineSides += FindSideOfLine(vertex2, vertex0, hitPosition);
257-
if (sumOfLineSides == -3 || sumOfLineSides == 3)
258-
{
259-
return true;
260-
}
251+
return false;
261252
}
262253

263-
return false;
254+
Vector2 v0 = new Vector2(vertex(0)[xForMajorAxis], vertex(0)[yForMajorAxis]);
255+
Vector2 v1 = new Vector2(vertex(1)[xForMajorAxis], vertex(1)[yForMajorAxis]);
256+
Vector2 v2 = new Vector2(vertex(2)[xForMajorAxis], vertex(2)[yForMajorAxis]);
257+
Vector2 p = new Vector2(x, y);
258+
259+
// Robust edge-inclusive test. This avoids misses when the ray lands exactly
260+
// on a shared edge/vertex (common with AABB-center clicks on pointed geometry).
261+
const double epsilon = 1e-9;
262+
double c0 = Vector2.Cross(p - v0, v1 - v0);
263+
double c1 = Vector2.Cross(p - v1, v2 - v1);
264+
double c2 = Vector2.Cross(p - v2, v0 - v2);
265+
266+
bool hasNeg = c0 < -epsilon || c1 < -epsilon || c2 < -epsilon;
267+
bool hasPos = c0 > epsilon || c1 > epsilon || c2 > epsilon;
268+
269+
return !(hasNeg && hasPos);
264270
}
265271

266272
private Vector3Float vertex(int i)

0 commit comments

Comments
 (0)