stride3d/stride · error · InvalidOperationException
Tetrahedron not in positive order
Error message
Tetrahedron not in positive order
What it means
During initialization of the super-tetrahedron that encloses all input vertices, BowyerWatsonTetrahedralization verifies the tetrahedron's vertex ordering yields a positive signed volume via IsTetrahedronPositiveOrder. A degenerate or badly ordered vertex set throws InvalidOperationException('Tetrahedron not in positive order').
Solutions
- Ensure probe positions span a non-degenerate 3D volume
- Remove duplicate/coincident probe positions
- Check that the computed bounding extent has positive size on all axes before tetrahedralizing
Example fix
// before
var tetrahedralization = new BowyerWatsonTetrahedralization(coplanarProbes);
// after
if (probes.Distinct().Count() >= 4 && HasPositiveExtent(probes))
var tetrahedralization = new BowyerWatsonTetrahedralization(probes); Defensive patterns
Strategy: validation
Validate before calling
bool IsNonDegenerate(IEnumerable<Vector3> pts) {
var a = pts.Distinct().ToList();
if (a.Count < 4) return false;
// require points off a common plane
var p0 = a[0]; var p1 = a[1]; var p2 = a[2];
var n = Vector3.Cross(p1 - p0, p2 - p0);
return a.Skip(3).Any(p => Math.Abs(Vector3.Dot(p - p0, n)) > 1e-4f);
} Try / catch
try { new BowyerWatsonTetrahedralization(vertices); }
catch (InvalidOperationException e) { logger.Error(e, "Degenerate probe layout"); } Prevention
- Avoid all-coplanar or duplicated probe positions
- Deduplicate vertices before tetrahedralization
- Validate minimum bounding extent of probe groups
When it happens
Trigger: Running tetrahedralization (BowyerWatsonTetrahedralization constructor) on light probe positions where the computed bounding extent is degenerate — e.g. all probes coplanar, duplicated, or with zero extent along an axis.
Common situations: A LightProbeGroup with all probes on a single plane, probes at identical positions, or an empty/degenerate group being baked.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Inconsistency: two tetrahedra don't agree on their…
- Invalid state when computing tetrahedron
- A file path cannot end with with directory char '\' or '/'…
- A Gear constraint requires two rigidbodies.
- A MaterialPass can only belong to a single Material
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/66b450d5fbe4c752.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs:435
// Store additional vertices in extra space
// Note: we build 3 extra vertices on a plane with normal (1,1,1) to makes computation easier
// As an example, (assuming 2D), if B=(1,1) or (0.5,1.5), we can encompass it with (2,0) and (0,2))
var combinedSize = boundingBox.Extent.X + boundingBox.Extent.Y + boundingBox.Extent.Z;
vertices[vertices.Length - 4] = boundingBox.Minimum - boundingBox.Extent * 1000.0f;
vertices[vertices.Length - 3] = boundingBox.Minimum + Vector3.UnitZ * combinedSize * 1000.0f;
vertices[vertices.Length - 2] = boundingBox.Minimum + Vector3.UnitY * combinedSize * 1000.0f;
vertices[vertices.Length - 1] = boundingBox.Minimum + Vector3.UnitX * combinedSize * 1000.0f;
// Create super tetrahedron
var superTetrahedron = new Tetrahedron();
for (int i = 0; i < 4; ++i)
{
superTetrahedron.Vertices[i] = vertices.Length - 4 + i;
superTetrahedron.Neighbours[i] = -1; // No neighbour
}
if (!IsTetrahedronPositiveOrder(vertices, ref superTetrahedron))
throw new InvalidOperationException("Tetrahedron not in positive order");
tetrahedralization.Add(superTetrahedron);
}
}
private unsafe void AddVertex(int vertexIndex)
{
// Clears reused structures (avoid reallocation every vertex)
badTetrahedra.Clear();
holeFaces.Clear();
edges.Clear();
var vertex = vertices[vertexIndex];
fixed (Tetrahedron* tetrahedra = tetrahedralization.Items)
{
// First, find all the triangles that are no longer valid due to the insertion
// TODO: Currently O(N^2); "By using the connectivity of the triangulation to efficiently locate triangles to remove, the algorithm can take O(N log N)"View on GitHub (pinned to 96fad776d2)