static bool CreateExtrudedSubMesh(CSGBrushSubMesh subMesh, int segments, int[] segmentDescriptionIndices, int[] segmentAssetIndices, int segmentTopIndex, int segmentBottomIndex, Vector3[] vertices, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions) { if (vertices.Length < 3) { return(false); } // TODO: vertex reverse winding when it's not clockwise // TODO: handle duplicate vertices, remove them or avoid them being created in the first place (maybe use indices?) var segmentTopology = new SegmentTopology[segments]; var edgeIndices = new int[segments * 2]; var polygonCount = 2; var edgeOffset = segments + segments; for (int p = segments - 2, e = segments - 1, n = 0; n < segments; p = e, e = n, n++) { // t0 // ------> -------> -----> // v0 * * v1 // <----- <------- <----- // ^ | e2 ^ | // | | | | // q1 p0| |e3 q2 e5| |n0 q3 // | | | | // | v e4 | v // ------> -------> -----> // v3 * * v2 // <----- <------- <----- // b0 // var v0 = vertices[p]; var v1 = vertices[e]; var v2 = vertices[e + segments]; var v3 = vertices[p + segments]; var equals03 = (v0 - v3).sqrMagnitude < 0.0001f; var equals12 = (v1 - v2).sqrMagnitude < 0.0001f; if (equals03) { if (equals12) { segmentTopology[n] = SegmentTopology.None; } else { segmentTopology[n] = SegmentTopology.TriangleNegative; } } else if (equals12) { segmentTopology[n] = SegmentTopology.TrianglePositive; } else { var plane = new Plane(v0, v1, v3); var dist = plane.GetDistanceToPoint(v2); if (UnityEngine.Mathf.Abs(dist) < 0.001f) { segmentTopology[n] = SegmentTopology.Quad; } else { segmentTopology[n] = (SegmentTopology)UnityEngine.Mathf.Sign(dist); } } switch (segmentTopology[n]) { case SegmentTopology.Quad: { edgeIndices[(n * 2) + 0] = edgeOffset + 1; polygonCount++; edgeOffset += 4; edgeIndices[(n * 2) + 1] = edgeOffset - 1; break; } case SegmentTopology.TrianglesNegative: case SegmentTopology.TrianglesPositive: { edgeIndices[(n * 2) + 0] = edgeOffset + 1; polygonCount += 2; edgeOffset += 6; edgeIndices[(n * 2) + 1] = edgeOffset - 1; break; } case SegmentTopology.TriangleNegative: case SegmentTopology.TrianglePositive: { edgeIndices[(n * 2) + 0] = edgeOffset + 1; polygonCount++; edgeOffset += 3; edgeIndices[(n * 2) + 1] = edgeOffset - 1; break; } case SegmentTopology.None: edgeIndices[(n * 2) + 0] = 0; edgeIndices[(n * 2) + 1] = 0; break; } } var polygons = new CSGBrushSubMesh.Polygon[polygonCount]; var surfaceDescription0 = surfaceDescriptions[segmentTopIndex]; var surfaceDescription1 = surfaceDescriptions[segmentBottomIndex]; var surfaceAsset0 = surfaceAssets[segmentTopIndex]; var surfaceAsset1 = surfaceAssets[segmentBottomIndex]; polygons[0] = new CSGBrushSubMesh.Polygon { surfaceID = 0, firstEdge = 0, edgeCount = segments, description = surfaceDescription0, surfaceAsset = surfaceAsset0 }; polygons[1] = new CSGBrushSubMesh.Polygon { surfaceID = 1, firstEdge = segments, edgeCount = segments, description = surfaceDescription1, surfaceAsset = surfaceAsset1 }; for (int s = 0, surfaceID = 2; s < segments; s++) { var descriptionIndex = (segmentDescriptionIndices == null) ? s + 2 : (segmentDescriptionIndices[s]); var assetIndex = (segmentAssetIndices == null) ? 2 : (segmentAssetIndices[s]); var firstEdge = edgeIndices[(s * 2) + 0] - 1; switch (segmentTopology[s]) { case SegmentTopology.Quad: { polygons[surfaceID] = new CSGBrushSubMesh.Polygon { surfaceID = surfaceID, firstEdge = firstEdge, edgeCount = 4, description = surfaceDescriptions[descriptionIndex], surfaceAsset = surfaceAssets[assetIndex] }; polygons[surfaceID].description.smoothingGroup = (uint)0; surfaceID++; break; } case SegmentTopology.TrianglesNegative: case SegmentTopology.TrianglesPositive: { var smoothingGroup = surfaceID + 1; // TODO: create an unique smoothing group for faceted surfaces that are split in two, // unless there's already a smoothing group for this edge; then use that polygons[surfaceID + 0] = new CSGBrushSubMesh.Polygon { surfaceID = surfaceID + 0, firstEdge = firstEdge, edgeCount = 3, description = surfaceDescriptions[descriptionIndex], surfaceAsset = surfaceAssets[assetIndex] }; polygons[surfaceID + 0].description.smoothingGroup = (uint)smoothingGroup; polygons[surfaceID + 1] = new CSGBrushSubMesh.Polygon { surfaceID = surfaceID + 1, firstEdge = firstEdge + 3, edgeCount = 3, description = surfaceDescriptions[descriptionIndex], surfaceAsset = surfaceAssets[assetIndex] }; polygons[surfaceID + 1].description.smoothingGroup = (uint)smoothingGroup; surfaceID += 2; break; } case SegmentTopology.TriangleNegative: case SegmentTopology.TrianglePositive: { polygons[surfaceID] = new CSGBrushSubMesh.Polygon { surfaceID = surfaceID, firstEdge = firstEdge, edgeCount = 3, description = surfaceDescriptions[descriptionIndex], surfaceAsset = surfaceAssets[assetIndex] }; polygons[surfaceID].description.smoothingGroup = (uint)0; surfaceID++; break; } case SegmentTopology.None: { break; } } } var halfEdges = new BrushMesh.HalfEdge[edgeOffset]; for (int p = segments - 2, e = segments - 1, n = 0; n < segments; p = e, e = n, n++) { //var vi0 = p; var vi1 = e; var vi2 = e + segments; var vi3 = p + segments; var t0 = e; var b0 = ((segments - 1) - e) + segments; switch (segmentTopology[n]) { case SegmentTopology.None: { halfEdges[t0] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = b0 }; halfEdges[b0] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = t0 }; break; } case SegmentTopology.TrianglePositive: { halfEdges[t0] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = -1 }; halfEdges[b0] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = -1 }; break; } case SegmentTopology.TriangleNegative: default: { halfEdges[t0] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = -1 }; halfEdges[b0] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = -1 }; break; } } } for (int p = segments - 2, e = segments - 1, n = 0; n < segments; p = e, e = n, n++) { var vi0 = p; var vi1 = e; var vi2 = e + segments; var vi3 = p + segments; var t0 = e; var b0 = ((segments - 1) - e) + segments; var nn = (n + 1) % segments; var p0 = edgeIndices[(e * 2) + 1]; var n0 = edgeIndices[(nn * 2) + 0]; switch (segmentTopology[n]) { case SegmentTopology.None: { continue; } case SegmentTopology.Quad: { // t0 // ------> -------> -----> // v0 * * v1 // <----- <------- <----- // ^ | e2 ^ | // | | | | // q1 p0| |e3 q2 e5| |n0 q3 // | | | | // | v e4 | v // ------> -------> -----> // v3 * * v2 // <----- <------- <----- // b0 // var q2 = edgeIndices[(n * 2) + 0]; var e2 = q2 - 1; var e3 = q2 + 0; var e4 = q2 + 1; var e5 = q2 + 2; halfEdges[t0].twinIndex = e2; halfEdges[b0].twinIndex = e4; halfEdges[e2] = new BrushMesh.HalfEdge { vertexIndex = vi0, twinIndex = t0 }; halfEdges[e3] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = p0 }; halfEdges[e4] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = b0 }; halfEdges[e5] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = n0 }; break; } case SegmentTopology.TrianglesNegative: { // t0 // ------> -----------> -----> // v0 * * v1 // <----- <----------- <----- // ^ | e2 ^ ^ | // | | / / | | // | | e4/ / | | // q1 p0| |e3 / / e7| |n0 q3 // | | / /e5 | | // | | / / | | // | v/v e6 | v // ------> -----------> -----> // v3 * * v2 // <----- <----------- <----- // b0 // var q2 = edgeIndices[(n * 2) + 0]; var e2 = q2 - 1; var e3 = q2 + 0; var e4 = q2 + 1; var e5 = q2 + 2; var e6 = q2 + 3; var e7 = q2 + 4; halfEdges[t0].twinIndex = e2; halfEdges[b0].twinIndex = e6; halfEdges[e2] = new BrushMesh.HalfEdge { vertexIndex = vi0, twinIndex = t0 }; halfEdges[e3] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = p0 }; halfEdges[e4] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = e5 }; halfEdges[e5] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = e4 }; halfEdges[e6] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = b0 }; halfEdges[e7] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = n0 }; break; } case SegmentTopology.TrianglesPositive: { // t0 // ------> -----------> -----> // v0 * * v1 // <----- <----------- <----- // ^ |^\ e5 ^ | // | | \ \ | | // | | \ \ e6 | | // q1 p0| |e3 e2\ \ e7| |n0 q3 // | | \ \ | | // | | \ \ | | // | v e4 \ v| v // ------> -----------> -----> // v3 * * v2 // <----- <----------- <----- // b0 // var q2 = edgeIndices[(n * 2) + 0]; var e2 = q2 - 1; var e3 = q2 + 0; var e4 = q2 + 1; var e5 = q2 + 2; var e6 = q2 + 3; var e7 = q2 + 4; halfEdges[t0].twinIndex = e5; halfEdges[b0].twinIndex = e4; halfEdges[e2] = new BrushMesh.HalfEdge { vertexIndex = vi0, twinIndex = e6 }; halfEdges[e3] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = p0 }; halfEdges[e4] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = b0 }; halfEdges[e5] = new BrushMesh.HalfEdge { vertexIndex = vi0, twinIndex = t0 }; halfEdges[e6] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = e2 }; halfEdges[e7] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = n0 }; break; } case SegmentTopology.TriangleNegative: { // t0 // ---> -------> // \ ^ * v1 // <--- \ / ^ <----- // \ \ / / | | // \ \ / / | | // \ \ t0 / / | | // \ \ / /e2 | | n0 // \ \ / / | | // \ \ / / q2 e4| | q3 // \ \ / / | | // v \/ v e3 | v // ------------> -------> -----> // v3 * * v2 // <----------- <------- <----- // b0 // var q2 = edgeIndices[(n * 2) + 0]; var e2 = q2 - 1; var e3 = q2 + 0; var e4 = q2 + 1; halfEdges[t0].twinIndex = e2; halfEdges[b0].twinIndex = e3; // vi0 / vi3 halfEdges[e2] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = t0 }; halfEdges[e3] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = b0 }; halfEdges[e4] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = n0 }; break; } case SegmentTopology.TrianglePositive: { // // -------> ----> // v0 * \ ^ // <----- \ / <--- // ^ |^ \ / / // | | \ \ / / // | | \ \ / / // | | \ \ t0 / / // q1 | | e2 \ \ / / // | | \ \ / / // p0| |e3 \ \ / / // | | q2 \ \ / / // | | \ \ / / // | v e4 \ v / v // ------> -----------> -----> // v3 * * v2 // <----- <----------- <----- // b0 // var q2 = edgeIndices[(n * 2) + 0]; var e2 = q2 - 1; var e3 = q2 + 0; var e4 = q2 + 1; halfEdges[t0].twinIndex = e2; halfEdges[b0].twinIndex = e4; halfEdges[e2] = new BrushMesh.HalfEdge { vertexIndex = vi0, twinIndex = t0 }; halfEdges[e3] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = p0 }; // vi1 / vi2 halfEdges[e4] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = b0 }; break; } } } subMesh.Polygons = polygons; subMesh.HalfEdges = halfEdges; subMesh.Vertices = vertices; subMesh.CreateOrUpdateBrushMesh(); return(true); }
static void CreateExtrudedSubMesh(CSGBrushSubMesh subMesh, Vector3[] sideVertices, Vector3 extrusion, int[] segmentDescriptionIndices, int[] segmentAssetIndices, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions) { const float distanceEpsilon = 0.0000001f; for (int i = sideVertices.Length - 1; i >= 0; i--) { var j = (i - 1 + sideVertices.Length) % sideVertices.Length; var magnitude = (sideVertices[j] - sideVertices[i]).sqrMagnitude; if (magnitude < distanceEpsilon) { // TODO: improve on this var tmp = sideVertices.ToList(); tmp.RemoveAt(i); sideVertices = tmp.ToArray(); } } var segments = sideVertices.Length; var isSegmentConvex = new sbyte[segments]; var edgeIndices = new int[segments * 2]; var vertices = new Vector3[segments * 2]; var polygonCount = 2; var edgeOffset = segments + segments; for (int p = segments - 2, e = segments - 1, n = 0; n < segments; p = e, e = n, n++) { // t0 // ------> -------> -----> // v0 * * v1 // <----- <------- <----- // ^ | e2 ^ | // | | | | // q1 p0| |e3 q2 e5| |n0 q3 // | | | | // | v e4 | v // ------> -------> -----> // v3 * * v2 // <----- <------- <----- // b0 // var v0 = sideVertices[p]; var v1 = sideVertices[e]; var v2 = sideVertices[e] + extrusion; var v3 = sideVertices[p] + extrusion; vertices[p] = v0; vertices[e] = v1; vertices[e + segments] = v2; vertices[p + segments] = v3; var plane = new Plane(v0, v1, v3); var dist = plane.GetDistanceToPoint(v2); if (UnityEngine.Mathf.Abs(dist) < 0.001f) { isSegmentConvex[n] = 0; } else { isSegmentConvex[n] = (sbyte)UnityEngine.Mathf.Sign(dist); } edgeIndices[(n * 2) + 0] = edgeOffset + 1; if (isSegmentConvex[n] == 0) { polygonCount++; edgeOffset += 4; } else { polygonCount += 2; edgeOffset += 6; } edgeIndices[(n * 2) + 1] = edgeOffset - 1; } var polygons = new CSGBrushSubMesh.Polygon[polygonCount]; var descriptionIndex0 = (segmentDescriptionIndices == null) ? 0 : (segmentDescriptionIndices[0]); var descriptionIndex1 = (segmentDescriptionIndices == null) ? 1 : (segmentDescriptionIndices[1]); var assetIndex0 = (segmentAssetIndices == null) ? 0 : (segmentAssetIndices[0]); var assetIndex1 = (segmentAssetIndices == null) ? 1 : (segmentAssetIndices[1]); var surfaceDescription0 = surfaceDescriptions[descriptionIndex0]; var surfaceDescription1 = surfaceDescriptions[descriptionIndex1]; var surfaceAsset0 = surfaceAssets[assetIndex0]; var surfaceAsset1 = surfaceAssets[assetIndex1]; polygons[0] = new CSGBrushSubMesh.Polygon { surfaceID = 0, firstEdge = 0, edgeCount = segments, description = surfaceDescription0, surfaceAsset = surfaceAsset0 }; polygons[1] = new CSGBrushSubMesh.Polygon { surfaceID = 1, firstEdge = segments, edgeCount = segments, description = surfaceDescription1, surfaceAsset = surfaceAsset1 }; for (int s = 0, surfaceID = 2; s < segments; s++) { var descriptionIndex = (segmentDescriptionIndices == null) ? s + 2 : (segmentDescriptionIndices[s + 2]); var assetIndex = (segmentAssetIndices == null) ? 2 : (segmentAssetIndices[s + 2]); var firstEdge = edgeIndices[(s * 2) + 0] - 1; if (isSegmentConvex[s] == 0) { polygons[surfaceID] = new CSGBrushSubMesh.Polygon { surfaceID = surfaceID, firstEdge = firstEdge, edgeCount = 4, description = surfaceDescriptions[descriptionIndex], surfaceAsset = surfaceAssets[assetIndex] }; polygons[surfaceID].description.smoothingGroup = (uint)0; surfaceID++; } else { var smoothingGroup = surfaceID + 1; // TODO: create an unique smoothing group for faceted surfaces that are split in two, // unless there's already a smoothing group for this edge; then use that polygons[surfaceID + 0] = new CSGBrushSubMesh.Polygon { surfaceID = surfaceID + 0, firstEdge = firstEdge, edgeCount = 3, description = surfaceDescriptions[descriptionIndex], surfaceAsset = surfaceAssets[assetIndex] }; polygons[surfaceID + 0].description.smoothingGroup = (uint)smoothingGroup; polygons[surfaceID + 1] = new CSGBrushSubMesh.Polygon { surfaceID = surfaceID + 1, firstEdge = firstEdge + 3, edgeCount = 3, description = surfaceDescriptions[descriptionIndex], surfaceAsset = surfaceAssets[assetIndex] }; polygons[surfaceID + 1].description.smoothingGroup = (uint)smoothingGroup; surfaceID += 2; } } var halfEdges = new BrushMesh.HalfEdge[edgeOffset]; for (int p = segments - 2, e = segments - 1, n = 0; n < segments; p = e, e = n, n++) { var vi1 = e; var vi3 = p + segments; var t0 = e; var b0 = ((segments - 1) - e) + segments; halfEdges[t0] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = -1 }; halfEdges[b0] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = -1 }; } for (int p = segments - 2, e = segments - 1, n = 0; n < segments; p = e, e = n, n++) { var vi0 = p; var vi1 = e; var vi2 = e + segments; var vi3 = p + segments; var t0 = e; var b0 = ((segments - 1) - e) + segments; var p0 = edgeIndices[(p * 2) + 1]; var n0 = edgeIndices[(n * 2) + 0]; if (isSegmentConvex[e] == 0) { // t0 // ------> -------> -----> // v0 * * v1 // <----- <------- <----- // ^ | e2 ^ | // | | | | // q1 p0| |e3 q2 e5| |n0 q3 // | | | | // | v e4 | v // ------> -------> -----> // v3 * * v2 // <----- <------- <----- // b0 // var q2 = edgeIndices[(e * 2) + 0]; var e2 = q2 - 1; var e3 = q2 + 0; var e4 = q2 + 1; var e5 = q2 + 2; halfEdges[t0].twinIndex = e2; halfEdges[b0].twinIndex = e4; halfEdges[e2] = new BrushMesh.HalfEdge { vertexIndex = vi0, twinIndex = t0 }; halfEdges[e3] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = p0 }; halfEdges[e4] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = b0 }; halfEdges[e5] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = n0 }; } else if (isSegmentConvex[e] == -1) { // t0 // ------> -----------> -----> // v0 * * v1 // <----- <----------- <----- // ^ | e2 ^ ^ | // | | / / | | // | | e4/ / | | // q1 p0| |e3 / / e7| |n0 q3 // | | / /e5 | | // | | / / | | // | v/v e6 | v // ------> -----------> -----> // v3 * * v2 // <----- <----------- <----- // b0 // var q2 = edgeIndices[(e * 2) + 0]; var e2 = q2 - 1; var e3 = q2 + 0; var e4 = q2 + 1; var e5 = q2 + 2; var e6 = q2 + 3; var e7 = q2 + 4; halfEdges[t0].twinIndex = e2; halfEdges[b0].twinIndex = e6; halfEdges[e2] = new BrushMesh.HalfEdge { vertexIndex = vi0, twinIndex = t0 }; halfEdges[e3] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = p0 }; halfEdges[e4] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = e5 }; halfEdges[e5] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = e4 }; halfEdges[e6] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = b0 }; halfEdges[e7] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = n0 }; } else if (isSegmentConvex[e] == 1) { // t0 // ------> -----------> -----> // v0 * * v1 // <----- <----------- <----- // ^ |^\ e5 ^ | // | | \ \ | | // | | \ \ e6 | | // q1 p0| |e3 e2\ \ e7| |n0 q3 // | | \ \ | | // | | \ \ | | // | v e4 \ v| v // ------> -----------> -----> // v3 * * v2 // <----- <----------- <----- // b0 // var q2 = edgeIndices[(e * 2) + 0]; var e2 = q2 - 1; var e3 = q2 + 0; var e4 = q2 + 1; var e5 = q2 + 2; var e6 = q2 + 3; var e7 = q2 + 4; halfEdges[t0].twinIndex = e5; halfEdges[b0].twinIndex = e4; halfEdges[e2] = new BrushMesh.HalfEdge { vertexIndex = vi0, twinIndex = e6 }; halfEdges[e3] = new BrushMesh.HalfEdge { vertexIndex = vi3, twinIndex = p0 }; halfEdges[e4] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = b0 }; halfEdges[e5] = new BrushMesh.HalfEdge { vertexIndex = vi0, twinIndex = t0 }; halfEdges[e6] = new BrushMesh.HalfEdge { vertexIndex = vi2, twinIndex = e2 }; halfEdges[e7] = new BrushMesh.HalfEdge { vertexIndex = vi1, twinIndex = n0 }; } } subMesh.Polygons = polygons; subMesh.HalfEdges = halfEdges; subMesh.Vertices = vertices; subMesh.CreateOrUpdateBrushMesh(); }
static void CreateConeSubMesh(CSGBrushSubMesh subMesh, int segments, int[] segmentDescriptionIndices, int[] segmentAssetIndices, Vector3[] vertices, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions) { var surfaceDescription0 = surfaceDescriptions[0]; var surfaceAsset0 = surfaceAssets[0]; var polygons = new CSGBrushSubMesh.Polygon[segments + 1]; polygons[0] = new CSGBrushSubMesh.Polygon { surfaceID = 0, firstEdge = 0, edgeCount = segments, description = surfaceDescription0, surfaceAsset = surfaceAsset0 }; for (int s = 0, surfaceID = 1; s < segments; s++) { var descriptionIndex = (segmentDescriptionIndices == null) ? s + 2 : (segmentDescriptionIndices[s + 2]); var assetIndex = (segmentAssetIndices == null) ? 2 : (segmentAssetIndices [s + 2]); polygons[surfaceID] = new CSGBrushSubMesh.Polygon { surfaceID = surfaceID, firstEdge = segments + (s * 3), edgeCount = 3, description = surfaceDescriptions[descriptionIndex], surfaceAsset = surfaceAssets[assetIndex] }; polygons[surfaceID].description.smoothingGroup = (uint)0; surfaceID++; } var halfEdges = new BrushMesh.HalfEdge[4 * segments]; for (int n = 0, e = segments - 1; n < segments; e = n, n++) { var p = (n + 1) % segments; // TODO: optimize away // v0 // * // ^ /^ \ // / / \ \ // / / e2 \ \ // / / \ \ // t2 / /e3 q2 e2\ \ t3 // / / \ \ // / v e1 \ v // ------> -----------> -----> // v2 * * v1 // <----- <---------- <----- // e0 var v0 = 0; var v1 = n + 1; var v2 = e + 1; var e0 = n; var e1 = ((n * 3) + segments) + 0; var e2 = ((n * 3) + segments) + 1; var e3 = ((n * 3) + segments) + 2; var t3 = ((e * 3) + segments) + 2; var t2 = ((p * 3) + segments) + 1; // bottom polygon halfEdges[e0] = new BrushMesh.HalfEdge { vertexIndex = v1, twinIndex = e1 }; // triangle halfEdges[e1] = new BrushMesh.HalfEdge { vertexIndex = v2, twinIndex = e0 }; halfEdges[e2] = new BrushMesh.HalfEdge { vertexIndex = v0, twinIndex = t3 }; halfEdges[e3] = new BrushMesh.HalfEdge { vertexIndex = v1, twinIndex = t2 }; } subMesh.Polygons = polygons; subMesh.HalfEdges = halfEdges; subMesh.Vertices = vertices; subMesh.CreateOrUpdateBrushMesh(); }