/// <summary> /// Triangulate this csg and pack the triangulated data into buffers /// appropriate for use with gltf. /// </summary> internal static GraphicsBuffers Tessellate(this Csg.Solid csg) { var buffers = new GraphicsBuffers(); ushort iCursor = 0; (Vector3 U, Vector3 V)basis; const float SEARCH_RADIUS = 0.001f; // Setup the octree to fit around the csg. // This requires one expensive initialization. var verts = csg.Polygons.SelectMany(p => p.Vertices).Select(v => v.Pos.ToElementsVector()).ToArray(); var bounds = new BBox3(verts); var center = bounds.Center(); var origin = new Point((float)center.X, (float)center.Y, (float)center.Z); var size = (float)bounds.Max.DistanceTo(bounds.Min); var octree = new PointOctree <(Vector3 position, Vector3 normal, ushort index)>(size, origin, SEARCH_RADIUS); foreach (var p in csg.Polygons) { var vertexIndices = new ushort[p.Vertices.Count]; var a = p.Vertices[0].Pos.ToElementsVector(); var b = p.Vertices[1].Pos.ToElementsVector(); var c = p.Vertices[2].Pos.ToElementsVector(); basis = ComputeBasisAndNormalForTriangle(a, b, c, out Vector3 normal); // Anything with 3 vertices is a triangle. Manually // tesselate triangles. For everything else, use // the tessellator. if (p.Vertices.Count > 2 && p.Vertices.Count <= 3) { for (var i = 0; i < p.Vertices.Count; i++) { var v = p.Vertices[i]; var op = new Point((float)v.Pos.X, (float)v.Pos.Y, (float)v.Pos.Z); var ep = v.Pos.ToElementsVector(); if (TryGetExistingVertex(op, ep, octree, normal, SEARCH_RADIUS, out ushort vertexIndex)) { vertexIndices[i] = vertexIndex; continue; } vertexIndices[i] = iCursor; iCursor++; var uu = basis.U.Dot(ep); var vv = basis.V.Dot(ep); buffers.AddVertex(ep, normal, new UV(uu, vv)); octree.Add((ep, normal, vertexIndices[i]), op);
/// <summary> /// Does this line touches or intersects the provided box in 3D? /// </summary> /// <param name="box"></param> /// <param name="results"></param> /// <param name="infinite">Treat the line as infinite?</param> /// <returns>True if the line touches or intersects the box at least at one point, false otherwise.</returns> public bool Intersects(BBox3 box, out List <Vector3> results, bool infinite = false) { var d = End - Start; results = new List <Vector3>(); // Solving the t parameter on line were it intersects planes of box in different coordinates. // If vector has no change in particular coordinate - just skip it as infinity. var t0x = double.NegativeInfinity; var t1x = double.PositiveInfinity; if (Math.Abs(d.X) > 1e-6) { t0x = (box.Min.X - Start.X) / d.X; t1x = (box.Max.X - Start.X) / d.X; // Line can reach min plane of box before reaching max. if (t1x < t0x) { (t0x, t1x) = (t1x, t0x); } } var t0y = double.NegativeInfinity; var t1y = double.PositiveInfinity; if (Math.Abs(d.Y) > 1e-6) { t0y = (box.Min.Y - Start.Y) / d.Y; t1y = (box.Max.Y - Start.Y) / d.Y; if (t1y < t0y) { (t0y, t1y) = (t1y, t0y); } } // If max hit of one coordinate is smaller then min hit of other - line hits planes outside the box. // In other words line just goes by. if (t0x > t1y || t0y > t1x) { return(false); } var tMin = Math.Max(t0x, t0y); var tMax = Math.Min(t1x, t1y); if (Math.Abs(d.Z) > 1e-6) { var t0z = (box.Min.Z - Start.Z) / d.Z; var t1z = (box.Max.Z - Start.Z) / d.Z; if (t1z < t0z) { (t0z, t1z) = (t1z, t0z); } if (t0z > tMax || t1z < tMin) { return(false); } tMin = Math.Max(t0z, tMin); tMax = Math.Min(t1z, tMax); } if (tMin == double.NegativeInfinity || tMin == double.PositiveInfinity) { return(false); } // Check if found parameters are within normalized line range. if (infinite || (tMin > -Vector3.EPSILON && tMin < 1 + Vector3.EPSILON)) { results.Add(Start + d * tMin); } if (Math.Abs(tMax - tMin) > Vector3.EPSILON && (infinite || (tMax > -Vector3.EPSILON && tMax < 1 + Vector3.EPSILON))) { results.Add(Start + d * tMax); } return(results.Any()); }