public static void GenerateCuboid(Cuboid shapeDesc, Transform transform, Vector2 texScale, bool insideOut, out IList <CSGVertex> vertices, out IList <uint> indices) { CSGPlane[] planes = new CSGPlane[6]; #region Construct Non-Transformed Planes Plane facePlane; CSGVertex[] faceVertices; Vector3 faceNormal; // Top face faceNormal = Vector3.UP; facePlane = new Plane(faceNormal, shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontTopLeft)); faceVertices = new[] { new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackTopLeft), faceNormal, new Vector2(0f, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackTopRight), faceNormal, new Vector2(shapeDesc.Width / texScale.X, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontTopRight), faceNormal, new Vector2(shapeDesc.Width / texScale.X, shapeDesc.Depth / texScale.Y) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontTopLeft), faceNormal, new Vector2(0f, shapeDesc.Depth / texScale.Y) ), }; planes[0] = new CSGPlane(facePlane, faceVertices); // Bottom face faceNormal = Vector3.DOWN; facePlane = new Plane(faceNormal, shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontBottomLeft)); faceVertices = new[] { new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontBottomRight), faceNormal, new Vector2(0f, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackBottomRight), faceNormal, new Vector2(0f, shapeDesc.Depth / texScale.Y) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackBottomLeft), faceNormal, new Vector2(shapeDesc.Width / texScale.X, shapeDesc.Depth / texScale.Y) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontBottomLeft), faceNormal, new Vector2(shapeDesc.Width / texScale.X, 0f) ), }; planes[1] = new CSGPlane(facePlane, faceVertices); // Front face faceNormal = Vector3.BACKWARD; facePlane = new Plane(faceNormal, shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontBottomLeft)); faceVertices = new[] { new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontTopLeft), faceNormal, new Vector2(0f, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontTopRight), faceNormal, new Vector2(shapeDesc.Width / texScale.X, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontBottomRight), faceNormal, new Vector2(shapeDesc.Width / texScale.X, shapeDesc.Height / texScale.Y) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontBottomLeft), faceNormal, new Vector2(0f, shapeDesc.Height / texScale.Y) ), }; planes[2] = new CSGPlane(facePlane, faceVertices); // Back face faceNormal = Vector3.FORWARD; facePlane = new Plane(faceNormal, shapeDesc.GetCorner(Cuboid.CuboidCorner.BackBottomLeft)); faceVertices = new[] { new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackTopRight), faceNormal, new Vector2(0f, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackTopLeft), faceNormal, new Vector2(shapeDesc.Width / texScale.X, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackBottomLeft), faceNormal, new Vector2(shapeDesc.Width / texScale.X, shapeDesc.Height / texScale.Y) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackBottomRight), faceNormal, new Vector2(0f, shapeDesc.Height / texScale.Y) ), }; planes[3] = new CSGPlane(facePlane, faceVertices); // Left face faceNormal = Vector3.LEFT; facePlane = new Plane(faceNormal, shapeDesc.GetCorner(Cuboid.CuboidCorner.BackBottomLeft)); faceVertices = new[] { new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackTopLeft), faceNormal, new Vector2(0f, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontTopLeft), faceNormal, new Vector2(shapeDesc.Depth / texScale.X, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontBottomLeft), faceNormal, new Vector2(shapeDesc.Depth / texScale.X, shapeDesc.Height / texScale.Y) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackBottomLeft), faceNormal, new Vector2(0f, shapeDesc.Height / texScale.Y) ), }; planes[4] = new CSGPlane(facePlane, faceVertices); // Right face faceNormal = Vector3.RIGHT; facePlane = new Plane(faceNormal, shapeDesc.GetCorner(Cuboid.CuboidCorner.BackBottomRight)); faceVertices = new[] { new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontTopRight), faceNormal, new Vector2(0f, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackTopRight), faceNormal, new Vector2(shapeDesc.Depth / texScale.X, 0f) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.BackBottomRight), faceNormal, new Vector2(shapeDesc.Depth / texScale.X, shapeDesc.Height / texScale.Y) ), new CSGVertex( shapeDesc.GetCorner(Cuboid.CuboidCorner.FrontBottomRight), faceNormal, new Vector2(0f, shapeDesc.Height / texScale.Y) ), }; planes[5] = new CSGPlane(facePlane, faceVertices); #endregion for (int i = 0; i < planes.Length; ++i) { planes[i] = planes[i].Transform(transform, shapeDesc.CenterPoint); } CreateVBAndIB(planes, out vertices, out indices, insideOut); }
private static unsafe CSGPlane[] GeneratePlanes(CurveDesc shapeDesc, Transform transform, Vector2 texScale, CurveSegment[] segments) { float depthPerSegment = shapeDesc.StartingCuboid.Depth / shapeDesc.NumSegments; CSGPlane[] planes = new CSGPlane[shapeDesc.NumSegments * 8 + 2]; #region UV Planes Vector2 invTexScale = 1f / texScale; float minX, minY, minZ, maxX, maxY, maxZ; minX = minY = minZ = Single.MaxValue; maxX = maxY = maxZ = Single.MinValue; Action <Vector3> testAction = vec => { if (vec.X < minX) { minX = vec.X; } if (vec.X > maxX) { maxX = vec.X; } if (vec.Y < minY) { minY = vec.Y; } if (vec.Y > maxY) { maxY = vec.Y; } if (vec.Z < minZ) { minZ = vec.Z; } if (vec.Z > maxZ) { maxZ = vec.Z; } }; foreach (var segment in segments) { testAction(segment.FrontTopLeft); testAction(segment.BackTopLeft); testAction(segment.FrontBottomLeft); testAction(segment.BackBottomLeft); testAction(segment.FrontTopRight); testAction(segment.BackTopRight); testAction(segment.FrontBottomRight); testAction(segment.BackBottomRight); } float xMid = minX + (maxX - minX) * 0.5f; float yMid = minY + (maxY - minY) * 0.5f; float zMid = minZ + (maxZ - minZ) * 0.5f; #endregion for (int segmentIndex = 0, planeIndex = 0; segmentIndex < shapeDesc.NumSegments; ++segmentIndex) { CurveSegment segment = segments[segmentIndex]; Plane facePlane; CSGVertex[] faceVertices; Vector3 faceNormal; #region Back if (segmentIndex == 0) { facePlane = Plane.FromPoints(segment.BackBottomLeft, segment.BackTopRight, segment.BackBottomRight); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.BackBottomLeft, faceNormal, new Vector2(segment.BackBottomLeft.X - xMid, segment.BackBottomLeft.Y - yMid).Scale(invTexScale) ), new CSGVertex( segment.BackBottomRight, faceNormal, new Vector2(segment.BackBottomRight.X - xMid, segment.BackBottomRight.Y - yMid).Scale(invTexScale) ), new CSGVertex( segment.BackTopRight, faceNormal, new Vector2(segment.BackTopRight.X - xMid, segment.BackTopRight.Y - yMid).Scale(invTexScale) ), new CSGVertex( segment.BackTopLeft, faceNormal, new Vector2(segment.BackTopLeft.X - xMid, segment.BackTopLeft.Y - yMid).Scale(invTexScale) ), }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); } #endregion #region Top facePlane = Plane.FromPoints(segment.BackTopLeft, segment.FrontTopLeft, segment.FrontTopRight); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.BackTopLeft, faceNormal, new Vector2(segment.BackTopLeft.X - xMid, segment.BackTopLeft.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.FrontTopLeft, faceNormal, new Vector2(segment.FrontTopLeft.X - xMid, segment.FrontTopLeft.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.FrontTopRight, faceNormal, new Vector2(segment.FrontTopRight.X - xMid, segment.FrontTopRight.Z - zMid).Scale(invTexScale) ), }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); facePlane = Plane.FromPoints(segment.FrontTopRight, segment.BackTopRight, segment.BackTopLeft); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.FrontTopRight, faceNormal, new Vector2(segment.FrontTopRight.X - xMid, segment.FrontTopRight.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.BackTopRight, faceNormal, new Vector2(segment.BackTopRight.X - xMid, segment.BackTopRight.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.BackTopLeft, faceNormal, new Vector2(segment.BackTopLeft.X - xMid, segment.BackTopLeft.Z - zMid).Scale(invTexScale) ), }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); #endregion #region Bottom facePlane = Plane.FromPoints(segment.FrontBottomRight, segment.FrontBottomLeft, segment.BackBottomLeft); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.FrontBottomRight, faceNormal, new Vector2(segment.FrontBottomRight.X - xMid, segment.FrontBottomRight.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.FrontBottomLeft, faceNormal, new Vector2(segment.FrontBottomLeft.X - xMid, segment.FrontBottomLeft.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.BackBottomLeft, faceNormal, new Vector2(segment.BackBottomLeft.X - xMid, segment.BackBottomLeft.Z - zMid).Scale(invTexScale) ), }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); facePlane = Plane.FromPoints(segment.BackBottomLeft, segment.BackBottomRight, segment.FrontBottomRight); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.BackBottomLeft, faceNormal, new Vector2(segment.BackBottomLeft.X - xMid, segment.BackBottomLeft.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.BackBottomRight, faceNormal, new Vector2(segment.BackBottomRight.X - xMid, segment.BackBottomRight.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.FrontBottomRight, faceNormal, new Vector2(segment.FrontBottomRight.X - xMid, segment.FrontBottomRight.Z - zMid).Scale(invTexScale) ), }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); #endregion #region Left facePlane = Plane.FromPoints(segment.BackBottomLeft, segment.FrontBottomLeft, segment.FrontTopLeft); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.BackBottomLeft, faceNormal, new Vector2(segment.BackBottomLeft.Y - yMid, segment.BackBottomLeft.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.FrontBottomLeft, faceNormal, new Vector2(segment.FrontBottomLeft.Y - yMid, segment.FrontBottomLeft.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.FrontTopLeft, faceNormal, new Vector2(segment.FrontTopLeft.Y - yMid, segment.FrontTopLeft.Z - zMid).Scale(invTexScale) ), }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); facePlane = Plane.FromPoints(segment.FrontTopLeft, segment.BackTopLeft, segment.BackBottomLeft); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.FrontTopLeft, faceNormal, new Vector2(segment.FrontTopLeft.Y - yMid, segment.FrontTopLeft.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.BackTopLeft, faceNormal, new Vector2(segment.BackTopLeft.Y - yMid, segment.BackTopLeft.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.BackBottomLeft, faceNormal, new Vector2(segment.BackBottomLeft.Y - yMid, segment.BackBottomLeft.Z - zMid).Scale(invTexScale) ), }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); #endregion #region Right facePlane = Plane.FromPoints(segment.FrontTopRight, segment.FrontBottomRight, segment.BackBottomRight); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.FrontTopRight, faceNormal, new Vector2(segment.FrontTopRight.Y - yMid, segment.FrontTopRight.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.FrontBottomRight, faceNormal, new Vector2(segment.FrontBottomRight.Y - yMid, segment.FrontBottomRight.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.BackBottomRight, faceNormal, new Vector2(segment.BackBottomRight.Y - yMid, segment.BackBottomRight.Z - zMid).Scale(invTexScale) ), }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); facePlane = Plane.FromPoints(segment.BackBottomRight, segment.BackTopRight, segment.FrontTopRight); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.BackBottomRight, faceNormal, new Vector2(segment.BackBottomRight.Y - yMid, segment.BackBottomRight.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.BackTopRight, faceNormal, new Vector2(segment.BackTopRight.Y - yMid, segment.BackTopRight.Z - zMid).Scale(invTexScale) ), new CSGVertex( segment.FrontTopRight, faceNormal, new Vector2(segment.FrontTopRight.Y - yMid, segment.FrontTopRight.Z - zMid).Scale(invTexScale) ), }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); #endregion #region Front if (segmentIndex == segments.Length - 1) { facePlane = Plane.FromPoints(segment.FrontBottomRight, segment.FrontTopLeft, segment.FrontBottomLeft); faceNormal = facePlane.Normal; faceVertices = new[] { new CSGVertex( segment.FrontBottomRight, faceNormal, new Vector2(segment.FrontBottomRight.X - xMid, segment.FrontBottomRight.Y - yMid).Scale(invTexScale) ), new CSGVertex( segment.FrontBottomLeft, faceNormal, new Vector2(segment.FrontBottomLeft.X - xMid, segment.FrontBottomLeft.Y - yMid).Scale(invTexScale) ), new CSGVertex( segment.FrontTopLeft, faceNormal, new Vector2(segment.FrontTopLeft.X - xMid, segment.FrontTopLeft.Y - yMid).Scale(invTexScale) ), new CSGVertex( segment.FrontTopRight, faceNormal, new Vector2(segment.FrontTopRight.X - xMid, segment.FrontTopRight.Y - yMid).Scale(invTexScale) ) }; planes[planeIndex++] = new CSGPlane(facePlane, faceVertices); } #endregion } for (int i = 0; i < planes.Length; ++i) { planes[i] = planes[i].Transform(transform, shapeDesc.StartingCuboid.CenterPoint); } return(planes); }
private static IList <uint> TriangulatePlane(CSGPlane plane, bool insideOut) { CSGVertexConnectionGraph connectionGraph = new CSGVertexConnectionGraph(); // Add the initial connections Ray[] initialConnectionRays = new Ray[plane.NumVertices]; for (uint i = 0U; i < plane.NumVertices; ++i) { connectionGraph.AddConnection(plane[i], plane[(i + 1) % plane.NumVertices]); initialConnectionRays[i] = Ray.FromStartAndEndPoint(plane[i].Position, plane[(i + 1) % plane.NumVertices].Position); } // Add the algorithmic connections for (uint a = 0U; a < plane.NumVertices; ++a) { for (uint b = 0U; b < plane.NumVertices; ++b) { if (a == b) { continue; } CSGVertex vertA = plane[a]; CSGVertex vertB = plane[b]; // Skip existing connections if (connectionGraph.CheckForConnection(vertA, vertB)) { continue; } // Is a triangle formable by drawing a connection between A and B? if (!connectionGraph .GetConnections(vertA) .Select(conn => conn.B) .Intersect(connectionGraph.GetConnections(vertB).Select(conn => conn.B)) .Any()) { continue; } // Does the connection go outside the polygon? If so, it's no good. CSGConnection potentialConnection = new CSGConnection(vertA, vertB); Vector3 connectionCentre = potentialConnection.Ray.StartPoint + (potentialConnection.Ray.EndPoint.Value - potentialConnection.Ray.StartPoint) / 2f; if (connectionCentre != plane.Center) { Ray connectionToCentreRay = new Ray(connectionCentre, plane.Center - connectionCentre); if ((initialConnectionRays.Count(ray => { Vector3?potentialIntersection = ray.IntersectionWith(connectionToCentreRay); // We check that we're not intersecting the start point because that means this ray is going through a vertex; // so by rejecting intersections through the start point we're not accidentally bumping the number of edge crossings // by 2 return(potentialIntersection.HasValue && potentialIntersection != ray.StartPoint); }) & 1) == 0) { continue; } } // Does the connection intersect any other connection? If it does, we would have to create more vertices, so ignore it. if (connectionGraph.GetAllConnections().Any(conn => { Vector3?intersectionPoint = conn.Ray.IntersectionWith(potentialConnection.Ray); if (intersectionPoint.HasValue) { float minDist = Math.Min( Vector3.DistanceSquared(intersectionPoint.Value, potentialConnection.A.Position), Vector3.DistanceSquared(intersectionPoint.Value, potentialConnection.B.Position) ); if (minDist > MathUtils.FlopsErrorMargin) { return(true); } return(conn == potentialConnection); } return(false); })) { continue; } // Add this connection. connectionGraph.AddConnection(potentialConnection); } } // Compose the triangles by looking for three-cycles List <CSGTriangle> formulatedTriangles = new List <CSGTriangle>(); for (uint v = 0U; v < plane.NumVertices; ++v) { CSGVertex sourceVertex = plane[v]; IEnumerable <CSGConnection> primaryConnections = connectionGraph.GetConnections(sourceVertex, true); foreach (CSGConnection primary in primaryConnections) { IEnumerable <CSGConnection> secondaryConnections = connectionGraph.GetConnections(primary.B, true) .Where(sConn => sConn.B != sourceVertex && primaryConnections.Any(pConn => pConn.B == sConn.B)); // Now we have a set of triangles (source -> pConn.B -> sConn.B), let's find the ones we haven't already // added and add those! foreach (CSGConnection secondary in secondaryConnections) { CSGTriangle potentialTriangle = new CSGTriangle(v, plane.IndexOf(secondary.A), plane.IndexOf(secondary.B)); if (!formulatedTriangles.Contains(potentialTriangle)) { formulatedTriangles.Add(potentialTriangle); } } } } List <uint> result = new List <uint>(); foreach (CSGTriangle triangle in formulatedTriangles) { Vector3 aPos = plane[triangle.A].Position; Vector3 bPos = plane[triangle.B].Position; Vector3 triCentre = (aPos + bPos + plane[triangle.C].Position) / 3f; bool triIsClockwise = plane.Plane.Normal.Dot(Vector3.Cross(aPos - triCentre, bPos - triCentre)) > 0f; if (insideOut) { triIsClockwise = !triIsClockwise; } result.Add(triangle.A); if (triIsClockwise) { result.Add(triangle.B); result.Add(triangle.C); } else { result.Add(triangle.C); result.Add(triangle.B); } } return(result); }
public static void GeneratePlane(IList <Vector3> coplanarPoints, Transform transform, Vector2 texScale, bool doubleSided, out IList <CSGVertex> vertices, out IList <uint> indices) { if (coplanarPoints.Count < 2) { throw new ArgumentException("Must supply at least 3 points.", "coplanarPoints"); } CSGPlane[] planes = new CSGPlane[2]; Vector3 barycentre = Vector3.ZERO; coplanarPoints.ForEach(point => barycentre += point); barycentre /= coplanarPoints.Count; CSGVertex[] csgVertices; Plane plane = Plane.FromPoints(coplanarPoints[0], coplanarPoints[1], coplanarPoints[2]); Vector3 uvX = (coplanarPoints[0] - barycentre).ToUnit(); Vector3 uvY = uvX.Cross(plane.Normal); Vector2 invTexDimensions = new Vector2(1f / texScale.X, 1f / texScale.Y); #region Plane A csgVertices = new CSGVertex[coplanarPoints.Count]; plane = -plane; for (int i = 0; i < coplanarPoints.Count; ++i) { Vector3 centreOffset = coplanarPoints[i] - barycentre; csgVertices[csgVertices.Length - (i + 1)] = new CSGVertex( coplanarPoints[i], plane.Normal, new Vector2(centreOffset.Dot(uvX), centreOffset.Dot(uvY)).Scale(invTexDimensions) ); } planes[0] = new CSGPlane(plane, csgVertices, barycentre); #endregion #region Plane B if (doubleSided) { csgVertices = new CSGVertex[coplanarPoints.Count]; for (int i = 0; i < coplanarPoints.Count; ++i) { Vector3 centreOffset = coplanarPoints[i] - barycentre; csgVertices[i] = new CSGVertex( coplanarPoints[i], plane.Normal, new Vector2(centreOffset.Dot(uvX), centreOffset.Dot(uvY)).Scale(invTexDimensions) ); } planes[1] = new CSGPlane(plane, csgVertices, barycentre); } #endregion for (int i = 0; i < (doubleSided ? 2 : 1); ++i) { planes[i] = planes[i].Transform(transform, planes[i].Center); } CreateVBAndIB(planes.Take(doubleSided ? 2 : 1).ToList(), out vertices, out indices, false); }
public static void GenerateCone(Cone shapeDesc, Transform transform, Vector2 texScale, uint extrapolation, bool insideOut, out IList <CSGVertex> vertices, out IList <uint> indices) { if (extrapolation < 3U) { throw new ArgumentOutOfRangeException("extrapolation", extrapolation, "Value must be at least 3."); } if (shapeDesc.TopRadius < MathUtils.FlopsErrorMargin && shapeDesc.BottomRadius < MathUtils.FlopsErrorMargin) { throw new ArgumentException("Cone is too thin.", "shapeDesc"); } uint numCircularFaces = (shapeDesc.TopRadius > 0f ? 1U : 0U) + (shapeDesc.BottomRadius > 0f ? 1U : 0U); CSGPlane[] planes = new CSGPlane[numCircularFaces + extrapolation]; Vector3[] topPoints = new Vector3[extrapolation]; Vector3[] bottomPoints = new Vector3[extrapolation]; float radiusCoveredPerFace = MathUtils.TWO_PI / extrapolation; float radialOffset = 0f; for (uint i = 0U; i < extrapolation; ++i, radialOffset += radiusCoveredPerFace) { Vector3 rotVec = (Vector3.FORWARD * Quaternion.FromAxialRotation(Vector3.DOWN, radialOffset)).ToUnit(); topPoints[i] = shapeDesc.TopCenter + shapeDesc.TopRadius * rotVec; bottomPoints[i] = shapeDesc.BottomCenter + shapeDesc.BottomRadius * rotVec; } Vector2 invTexScale = new Vector2(1f / texScale.X, 1f / texScale.Y); #region Side planes float topCirc = new Circle(Vector2.ZERO, shapeDesc.TopRadius).Circumference; float bottomCirc = new Circle(Vector2.ZERO, shapeDesc.BottomRadius).Circumference; for (uint side = 0; side < extrapolation; ++side) { uint advSide = (side + 1) % extrapolation; Vector3 normal = bottomPoints[side] + (bottomPoints[advSide] - bottomPoints[side]) * 0.5f - shapeDesc.BottomCenter; if (normal == Vector3.ZERO) { normal = topPoints[side] + (topPoints[advSide] - topPoints[side]) * 0.5f - shapeDesc.TopCenter; } normal = normal != Vector3.ZERO ? normal.ToUnit() : Vector3.UP; List <CSGVertex> sideVertices = new List <CSGVertex>(); Vector2 sideTexPoint = Vector3.Distance(shapeDesc.TopCenter, topPoints[side]) > Vector3.Distance(shapeDesc.BottomCenter, bottomPoints[side]) ? new Vector2(topPoints[side].X, topPoints[side].Z) : new Vector2(bottomPoints[side].X, bottomPoints[side].Z); float sideTexU = MathUtils.NormalizeRadians( Vector2.AngleBetween(Vector2.UP, sideTexPoint), MathUtils.RadianNormalizationRange.PositiveFullRange ) / MathUtils.TWO_PI; Vector2 advSideTexPoint = Vector3.Distance(shapeDesc.TopCenter, topPoints[advSide]) > Vector3.Distance(shapeDesc.BottomCenter, bottomPoints[advSide]) ? new Vector2(topPoints[advSide].X, topPoints[advSide].Z) : new Vector2(bottomPoints[advSide].X, bottomPoints[advSide].Z); float advSideTexU = MathUtils.NormalizeRadians( Vector2.AngleBetween(Vector2.UP, advSideTexPoint), MathUtils.RadianNormalizationRange.PositiveFullRange ) / MathUtils.TWO_PI; if (shapeDesc.TopRadius > 0f) { sideVertices.Add( new CSGVertex(topPoints[advSide], normal, new Vector2(advSideTexU * topCirc, 0f).Scale(invTexScale)) ); sideVertices.Add( new CSGVertex(topPoints[side], normal, new Vector2(sideTexU * topCirc, 0f).Scale(invTexScale)) ); sideVertices.Add( new CSGVertex(bottomPoints[side], normal, new Vector2(sideTexU * bottomCirc, shapeDesc.Height).Scale(invTexScale)) ); if (shapeDesc.BottomRadius > 0f) { sideVertices.Add( new CSGVertex(bottomPoints[advSide], normal, new Vector2(advSideTexU * bottomCirc, shapeDesc.Height).Scale(invTexScale)) ); } } else { sideVertices.Add( new CSGVertex(topPoints[side], normal, new Vector2(sideTexU * topCirc, 0f).Scale(invTexScale)) ); sideVertices.Add( new CSGVertex(bottomPoints[side], normal, new Vector2(sideTexU * bottomCirc, shapeDesc.Height).Scale(invTexScale)) ); sideVertices.Add( new CSGVertex(bottomPoints[advSide], normal, new Vector2(advSideTexU * bottomCirc, shapeDesc.Height).Scale(invTexScale)) ); } planes[side] = new CSGPlane(new Plane(normal, topPoints[side]), sideVertices.ToArray()); } #endregion #region Top and bottom planes if (shapeDesc.TopRadius > 0f) { planes[extrapolation] = new CSGPlane(new Plane(Vector3.UP, shapeDesc.TopCenter), topPoints.Select(point => { Vector2 uv = new Vector2( point.X - shapeDesc.TopCenter.X, point.Z - shapeDesc.TopCenter.Z ).Scale(invTexScale); return(new CSGVertex(point, Vector3.UP, uv)); }).ToArray()); } if (shapeDesc.BottomRadius > 0f) { planes[extrapolation + numCircularFaces - 1U] = new CSGPlane(new Plane(Vector3.DOWN, shapeDesc.BottomCenter), bottomPoints.Reverse().Select(point => { Vector2 uv = new Vector2( point.X - shapeDesc.BottomCenter.X, point.Z - shapeDesc.BottomCenter.Z ).Scale(invTexScale); return(new CSGVertex(point, Vector3.DOWN, uv)); }).ToArray()); } #endregion for (int i = 0; i < planes.Length; ++i) { // Translate by half the height to make the centre/origin the centre of the shape planes[i] = planes[i].Transform(transform.TranslateBy(Vector3.UP * (shapeDesc.Height * 0.5f)), new Vector3(shapeDesc.BottomCenter, y: shapeDesc.BottomCenter.Y + shapeDesc.Height / 2f)); } CreateVBAndIB(planes, out vertices, out indices, insideOut); }