示例#1
0
        public static bool GenerateBoxSubMesh(CSGBrushSubMesh subMesh, UnityEngine.Vector3 min, UnityEngine.Vector3 max, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
        {
            if (!BoundsExtensions.IsValid(min, max))
            {
                return(false);
            }

            if (surfaceAssets.Length != 6 ||
                surfaceDescriptions.Length != 6)
            {
                return(false);
            }

            if (min.x > max.x)
            {
                float x = min.x; min.x = max.x; max.x = x;
            }
            if (min.y > max.y)
            {
                float y = min.y; min.y = max.y; max.y = y;
            }
            if (min.z > max.z)
            {
                float z = min.z; min.z = max.z; max.z = z;
            }

            subMesh.Polygons  = CreateBoxAssetPolygons(surfaceAssets, surfaceDescriptions);
            subMesh.HalfEdges = boxHalfEdges.ToArray();
            subMesh.Vertices  = BrushMeshFactory.CreateBoxVertices(min, max);
            return(true);
        }
        public static bool GenerateSphereSubMesh(CSGBrushSubMesh subMesh, CSGSphereDefinition definition)
        {
            definition.Validate();
            var transform = Matrix4x4.TRS(Vector3.zero, Quaternion.AngleAxis(definition.rotation, Vector3.up), Vector3.one);

            return(GenerateSphereSubMesh(subMesh, definition.diameterXYZ, definition.offsetY, definition.generateFromCenter, transform, definition.horizontalSegments, definition.verticalSegments, definition.surfaceAssets, definition.surfaceDescriptions));
        }
        public static bool GenerateSphereSubMesh(CSGBrushSubMesh subMesh, Vector3 diameterXYZ, float offsetY, bool generateFromCenter, Matrix4x4 transform, int horzSegments, int vertSegments, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
        {
            if (diameterXYZ.x == 0 ||
                diameterXYZ.y == 0 ||
                diameterXYZ.z == 0)
            {
                subMesh.Clear();
                return(false);
            }

            var brushMesh = BrushMeshFactory.CreateSphere(diameterXYZ, offsetY, generateFromCenter, horzSegments, vertSegments);

            subMesh.HalfEdges = brushMesh.halfEdges;
            subMesh.Vertices  = brushMesh.vertices;
            subMesh.Polygons  = new CSGBrushSubMesh.Polygon[brushMesh.polygons.Length];

            for (int i = 0; i < brushMesh.polygons.Length; i++)
            {
                subMesh.Polygons[i] = new CSGBrushSubMesh.Polygon
                {
                    surfaceID    = i,
                    edgeCount    = brushMesh.polygons[i].edgeCount,
                    firstEdge    = brushMesh.polygons[i].firstEdge,
                    surfaceAsset = i < surfaceAssets.Length ? surfaceAssets[i] : surfaceAssets[0],
                    description  = i < surfaceDescriptions.Length ? surfaceDescriptions[i] : surfaceDescriptions[0],
                };
            }

            return(true);
        }
示例#4
0
        public static bool GenerateConicalFrustumAsset(CSGBrushMeshAsset brushMeshAsset, CSGCircleDefinition bottom, CSGCircleDefinition top, float rotation, int segments, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
        {
            if (segments < 3 || (top.height - bottom.height) == 0 || (bottom.diameterX == 0 && top.diameterX == 0) || (bottom.diameterZ == 0 && top.diameterZ == 0))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            if (surfaceAssets.Length != 3 ||
                surfaceDescriptions.Length != segments + 2)
            {
                brushMeshAsset.Clear();
                return(false);
            }

            var subMesh = new CSGBrushSubMesh();

            if (!GenerateConicalFrustumSubMesh(subMesh, bottom, top, rotation, segments, surfaceAssets, surfaceDescriptions))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            brushMeshAsset.SubMeshes = new CSGBrushSubMesh[] { subMesh };
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
示例#5
0
        public static bool GenerateCylinderSubMesh(CSGBrushSubMesh subMesh, CSGCircleDefinition bottom, float topHeight, float rotation, int sides, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
        {
            CSGCircleDefinition top;

            top.diameterX = bottom.diameterX;
            top.diameterZ = bottom.diameterZ;
            top.height    = topHeight;
            return(GenerateConicalFrustumSubMesh(subMesh, bottom, top, rotation, sides, surfaceAssets, surfaceDescriptions));
        }
示例#6
0
        public static bool GenerateConicalFrustumSubMesh(CSGBrushSubMesh subMesh, CSGCircleDefinition bottom, CSGCircleDefinition top, float rotation, int segments, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
        {
            if (segments < 3 || (top.height - bottom.height) == 0 || (bottom.diameterX == 0 && top.diameterX == 0) || (bottom.diameterZ == 0 && top.diameterZ == 0))
            {
                subMesh.Clear();
                return(false);
            }

            if (surfaceAssets.Length < 3 ||
                surfaceDescriptions.Length < segments + 2)
            {
                subMesh.Clear();
                return(false);
            }
            // TODO: handle situation where diameterX & diameterZ are 0 (only create one vertex)

            if (top.diameterX == 0)
            {
                var vertices = new Vector3[segments + 1];
                GetConeFrustumVertices(bottom, top.height, rotation, segments, ref vertices, inverse: false);

                // TODO: the polygon/half-edge part would be the same for any extruded shape and should be re-used
                CreateConeSubMesh(subMesh, segments, null, vertices, surfaceAssets, surfaceDescriptions);
            }
            else
            if (bottom.diameterX == 0)
            {
                var vertices = new Vector3[segments + 1];
                GetConeFrustumVertices(top, bottom.height, rotation, segments, ref vertices, inverse: true);

                // TODO: the polygon/half-edge part would be the same for any extruded shape and should be re-used
                CreateConeSubMesh(subMesh, segments, null, vertices, surfaceAssets, surfaceDescriptions);
            }
            else
            {
                if (top.height > bottom.height)
                {
                    var temp = top; top = bottom; bottom = temp;
                }

                var vertices = new Vector3[segments * 2];
                GetConicalFrustumVertices(bottom, top, rotation, segments, ref vertices);

                // TODO: the polygon/half-edge part would be the same for any extruded shape and should be re-used
                CreateExtrudedSubMesh(subMesh, segments, null, 0, 1, vertices, surfaceAssets, surfaceDescriptions);
            }

            return(true);
        }
        public static bool GenerateSphereAsset(CSGBrushMeshAsset brushMeshAsset, CSGSphereDefinition definition)
        {
            var subMesh = new CSGBrushSubMesh();

            if (!GenerateSphereSubMesh(subMesh, definition))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            brushMeshAsset.SubMeshes = new[] { subMesh };
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
        public static bool GenerateLinearStairsAsset(CSGBrushMeshAsset brushMeshAsset, CSGLinearStairsDefinition definition)
        {
            definition.Validate();
            int subMeshCount = GetLinearStairsSubMeshCount(definition, definition.leftSide, definition.rightSide);

            if (subMeshCount == 0)
            {
                brushMeshAsset.Clear();
                return(false);
            }

            CSGBrushSubMesh[] subMeshes;
            if (brushMeshAsset.SubMeshCount != subMeshCount)
            {
                subMeshes = new CSGBrushSubMesh[subMeshCount];
                for (int i = 0; i < subMeshCount; i++)
                {
                    subMeshes[i] = new CSGBrushSubMesh();
                }
            }
            else
            {
                subMeshes = brushMeshAsset.SubMeshes;
            }

            if (!GenerateLinearStairsSubMeshes(subMeshes, definition, definition.leftSide, definition.rightSide, 0))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            brushMeshAsset.SubMeshes = subMeshes;

            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
示例#9
0
        public static bool GenerateHemisphereSubMesh(CSGBrushSubMesh subMesh, Vector3 diameterXYZ, Matrix4x4 transform, int horzSegments, int vertSegments, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
        {
            if (diameterXYZ.x == 0 ||
                diameterXYZ.y == 0 ||
                diameterXYZ.z == 0)
            {
                subMesh.Clear();
                return(false);
            }

            var bottomCap     = true;
            var topCap        = false;
            var extraVertices = ((!bottomCap) ? 1 : 0) + ((!topCap) ? 1 : 0);

            var rings       = (vertSegments) + (topCap ? 1 : 0);
            var vertexCount = (horzSegments * rings) + extraVertices;

            var topVertex    = 0;
            var bottomVertex = (!topCap) ? 1 : 0;
            var radius       = new Vector3(diameterXYZ.x * 0.5f,
                                           diameterXYZ.y,
                                           diameterXYZ.z * 0.5f);

            var   heightY = radius.y;
            float topY, bottomY;

            if (heightY < 0)
            {
                topY    = 0;
                bottomY = heightY;
            }
            else
            {
                topY    = heightY;
                bottomY = 0;
            }

            var vertices = new Vector3[vertexCount];

            if (!topCap)
            {
                vertices[topVertex] = transform.MultiplyPoint(Vector3.up * topY);                   // top
            }
            if (!bottomCap)
            {
                vertices[bottomVertex] = transform.MultiplyPoint(Vector3.up * bottomY);             // bottom
            }
            var degreePerSegment = (360.0f / horzSegments) * Mathf.Deg2Rad;
            var angleOffset      = ((horzSegments & 1) == 1) ? 0.0f : 0.5f * degreePerSegment;
            var vertexIndex      = extraVertices;

            if (heightY < 0)
            {
                for (int h = horzSegments - 1; h >= 0; h--, vertexIndex++)
                {
                    var hRad = (h * degreePerSegment) + angleOffset;
                    vertices[vertexIndex] = transform.MultiplyPoint(new Vector3(Mathf.Cos(hRad) * radius.x,
                                                                                0.0f,
                                                                                Mathf.Sin(hRad) * radius.z));
                }
            }
            else
            {
                for (int h = 0; h < horzSegments; h++, vertexIndex++)
                {
                    var hRad = (h * degreePerSegment) + angleOffset;
                    vertices[vertexIndex] = transform.MultiplyPoint(new Vector3(Mathf.Cos(hRad) * radius.x,
                                                                                0.0f,
                                                                                Mathf.Sin(hRad) * radius.z));
                }
            }
            for (int v = 1; v < rings; v++)
            {
                var segmentFactor = ((v - (rings / 2.0f)) / rings) + 0.5f;                      // [0.0f ... 1.0f]
                var segmentDegree = (segmentFactor * 90);                                       // [0 .. 90]
                var segmentHeight = Mathf.Sin(segmentDegree * Mathf.Deg2Rad) * heightY;
                var segmentRadius = Mathf.Cos(segmentDegree * Mathf.Deg2Rad);                   // [0 .. 0.707 .. 1 .. 0.707 .. 0]
                for (int h = 0; h < horzSegments; h++, vertexIndex++)
                {
                    vertices[vertexIndex].x = vertices[h + extraVertices].x * segmentRadius;
                    vertices[vertexIndex].y = segmentHeight;
                    vertices[vertexIndex].z = vertices[h + extraVertices].z * segmentRadius;
                }
            }

            return(GenerateSegmentedSubMesh(subMesh, horzSegments, vertSegments, vertices, bottomCap, topCap, bottomVertex, topVertex, surfaceAssets, surfaceDescriptions));
        }
        public static bool GenerateRevolvedShapeAsset(CSGBrushMeshAsset brushMeshAsset, CSGRevolvedShapeDefinition definition)
        {
            definition.Validate();
            var surfaces     = definition.surfaceAssets;
            var descriptions = definition.surfaceDescriptions;


            var shapeVertices       = new List <Vector2>();
            var shapeSegmentIndices = new List <int>();

            GetPathVertices(definition.shape, definition.curveSegments, shapeVertices, shapeSegmentIndices);

            Vector2[][] polygonVerticesArray;
            int[][]     polygonIndicesArray;

            if (!Decomposition.ConvexPartition(shapeVertices, shapeSegmentIndices,
                                               out polygonVerticesArray,
                                               out polygonIndicesArray))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            // TODO: splitting it before we do the composition would be better
            var polygonVerticesList = polygonVerticesArray.ToList();

            for (int i = polygonVerticesList.Count - 1; i >= 0; i--)
            {
                SplitPolygon(polygonVerticesList, i);
            }

            var subMeshes = new List <CSGBrushSubMesh>();

            var horzSegments         = definition.revolveSegments;           //horizontalSegments;
            var horzDegreePerSegment = definition.totalAngle / horzSegments;


            // TODO: make this work when intersecting rotation axis
            //			1. split polygons along rotation axis
            //			2. if edge lies on rotation axis, make sure we don't create infinitely thin quad
            //					collapse this quad, or prevent this from happening
            // TODO: share this code with torus generator
            for (int p = 0; p < polygonVerticesList.Count; p++)
            {
                var polygonVertices = polygonVerticesList[p];
//				var segmentIndices		= polygonIndicesArray[p];
                var shapeSegments = polygonVertices.Length;

                var vertSegments     = polygonVertices.Length;
                var descriptionIndex = new int[2 + vertSegments];

                descriptionIndex[0] = 0;
                descriptionIndex[1] = 1;

                for (int v = 0; v < vertSegments; v++)
                {
                    descriptionIndex[v + 2] = 2;
                }

                var horzOffset = definition.startAngle;
                for (int h = 1, pr = 0; h < horzSegments + 1; pr = h, h++)
                {
                    var hDegree0        = (pr * horzDegreePerSegment) + horzOffset;
                    var hDegree1        = (h * horzDegreePerSegment) + horzOffset;
                    var rotation0       = Quaternion.AngleAxis(hDegree0, Vector3.forward);
                    var rotation1       = Quaternion.AngleAxis(hDegree1, Vector3.forward);
                    var subMeshVertices = new Vector3[vertSegments * 2];
                    for (int v = 0; v < vertSegments; v++)
                    {
                        subMeshVertices[v + vertSegments] = rotation0 * new Vector3(polygonVertices[v].x, 0, polygonVertices[v].y);
                        subMeshVertices[v] = rotation1 * new Vector3(polygonVertices[v].x, 0, polygonVertices[v].y);
                    }

                    var subMesh = new CSGBrushSubMesh();
                    if (!CreateExtrudedSubMesh(subMesh, vertSegments, descriptionIndex, descriptionIndex, 0, 1, subMeshVertices, surfaces, descriptions))
                    {
                        continue;
                    }

                    if (!subMesh.Validate())
                    {
                        brushMeshAsset.Clear();
                        return(false);
                    }
                    subMeshes.Add(subMesh);
                }
            }

            brushMeshAsset.SubMeshes = subMeshes.ToArray();

            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
        public static bool GeneratePathedStairsAsset(CSGBrushMeshAsset brushMeshAsset, CSGPathedStairsDefinition definition)
        {
            definition.Validate();

            var shapeVertices       = new List <Vector2>();
            var shapeSegmentIndices = new List <int>();

            GetPathVertices(definition.shape, definition.curveSegments, shapeVertices, shapeSegmentIndices);

            var totalSubMeshCount = 0;

            for (int i = 0; i < shapeVertices.Count; i++)
            {
                if (i == 0 && !definition.shape.closed)
                {
                    continue;
                }

                var leftSide  = (!definition.shape.closed && i == 1) ? definition.stairs.leftSide  : StairsSideType.None;
                var rightSide = (!definition.shape.closed && i == shapeVertices.Count - 1) ? definition.stairs.rightSide : StairsSideType.None;

                totalSubMeshCount += GetLinearStairsSubMeshCount(definition.stairs, leftSide, rightSide);
            }
            if (totalSubMeshCount == 0)
            {
                brushMeshAsset.Clear();
                return(false);
            }

//			var stairDirections = definition.shape.closed ? shapeVertices.Count : (shapeVertices.Count - 1);

            // TODO: use list instead?
            CSGBrushSubMesh[] subMeshes;
            if (brushMeshAsset.SubMeshCount != totalSubMeshCount)
            {
                subMeshes = new CSGBrushSubMesh[totalSubMeshCount];
                for (int i = 0; i < totalSubMeshCount; i++)
                {
                    subMeshes[i] = new CSGBrushSubMesh();
                }
            }
            else
            {
                subMeshes = brushMeshAsset.SubMeshes;
            }

            var depth  = definition.stairs.depth;
            var height = definition.stairs.height;

            var halfDepth  = depth * 0.5f;
            var halfHeight = height * 0.5f;

            int subMeshIndex = 0;

            for (int vi0 = shapeVertices.Count - 3, vi1 = shapeVertices.Count - 2, vi2 = shapeVertices.Count - 1, vi3 = 0; vi3 < shapeVertices.Count; vi0 = vi1, vi1 = vi2, vi2 = vi3, vi3++)
            {
                if (vi2 == 0 && !definition.shape.closed)
                {
                    continue;
                }

                // TODO: optimize this, we're probably redoing a lot of stuff for every iteration
                var v0 = shapeVertices[vi0];
                var v1 = shapeVertices[vi1];
                var v2 = shapeVertices[vi2];
                var v3 = shapeVertices[vi3];

                var m0 = (v0 + v1) * 0.5f;
                var m1 = (v1 + v2) * 0.5f;
                var m2 = (v2 + v3) * 0.5f;

                var d0 = (v1 - v0);
                var d1 = (v2 - v1);
                var d2 = (v3 - v2);

                var maxWidth0  = d0.magnitude;
                var maxWidth1  = d1.magnitude;
                var maxWidth2  = d2.magnitude;
                var halfWidth1 = d1 * 0.5f;

                d0 /= maxWidth0;
                d1 /= maxWidth1;
                d2 /= maxWidth2;

                var depthVector = new Vector3(d1.y, 0, -d1.x);
                var lineCenter  = new Vector3(m1.x, halfHeight, m1.y) - (depthVector * halfDepth);

                var depthVector0 = new Vector2(d0.y, -d0.x) * depth;
                var depthVector1 = new Vector2(d1.y, -d1.x) * depth;
                var depthVector2 = new Vector2(d2.y, -d2.x) * depth;

                m0 -= depthVector0;
                m1 -= depthVector1;
                m2 -= depthVector2;

                Vector2 output;
                var     leftShear  = Intersect(m1, d1, m0, d0, out output) ?  Vector2.Dot(d1, (output - (m1 - halfWidth1))) : 0;
                var     rightShear = Intersect(m1, d1, m2, d2, out output) ? -Vector2.Dot(d1, (output - (m1 + halfWidth1))) : 0;

                var transform = Matrix4x4.TRS(lineCenter,                                       // move to center of line
                                              Quaternion.LookRotation(depthVector, Vector3.up), // rotate to align with line
                                              Vector3.one);

                // set the width to the width of the line
                definition.stairs.width       = maxWidth1;
                definition.stairs.nosingWidth = 0;

                var leftSide     = (!definition.shape.closed && vi2 == 1) ? definition.stairs.leftSide  : StairsSideType.None;
                var rightSide    = (!definition.shape.closed && vi2 == shapeVertices.Count - 1) ? definition.stairs.rightSide : StairsSideType.None;
                var subMeshCount = GetLinearStairsSubMeshCount(definition.stairs, leftSide, rightSide);
                if (subMeshCount == 0)
                {
                    continue;
                }

                if (!GenerateLinearStairsSubMeshes(subMeshes, definition.stairs, leftSide, rightSide, subMeshIndex))
                {
                    brushMeshAsset.Clear();
                    return(false);
                }

                var halfWidth = maxWidth1 * 0.5f;
                for (int m = 0; m < subMeshCount; m++)
                {
                    var vertices = subMeshes[subMeshIndex + m].Vertices;
                    for (int v = 0; v < vertices.Length; v++)
                    {
                        // TODO: is it possible to put all of this in a single matrix?
                        // lerp the stairs to go from less wide to wider depending on the depth of the vertex
                        var depthFactor = 1.0f - ((vertices[v].z / definition.stairs.depth) + 0.5f);
                        var wideFactor  = (vertices[v].x / halfWidth) + 0.5f;
                        var scale       = (vertices[v].x / halfWidth);

                        // lerp the stairs width depending on if it's on the left or right side of the stairs
                        vertices[v].x = Mathf.Lerp(scale * (halfWidth - (rightShear * depthFactor)),
                                                   scale * (halfWidth - (leftShear * depthFactor)),
                                                   wideFactor);
                        vertices[v] = transform.MultiplyPoint(vertices[v]);
                    }
                }

                subMeshIndex += subMeshCount;
            }

            brushMeshAsset.SubMeshes = subMeshes;

            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(false);
        }
示例#12
0
        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();
        }
示例#13
0
 static void CreateConeSubMesh(CSGBrushSubMesh subMesh, int segments, int[] segmentDescriptionIndices, Vector3[] vertices, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
 {
     CreateConeSubMesh(subMesh, segments, segmentDescriptionIndices, null, vertices, surfaceAssets, surfaceDescriptions);
 }
示例#14
0
        public static bool GenerateTorusAsset(CSGBrushMeshAsset brushMeshAsset, CSGTorusDefinition definition)
        {
            Vector3[] vertices = null;
            if (!GenerateTorusVertices(definition, ref vertices))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            definition.Validate();
            var surfaces     = definition.surfaceAssets;
            var descriptions = definition.surfaceDescriptions;
            var tubeRadiusX  = (definition.tubeWidth * 0.5f);
            var tubeRadiusY  = (definition.tubeHeight * 0.5f);
            var torusRadius  = (definition.outerDiameter * 0.5f) - tubeRadiusX;


            var horzSegments = definition.horizontalSegments;
            var vertSegments = definition.verticalSegments;

            var horzDegreePerSegment = (definition.totalAngle / horzSegments);
            var vertDegreePerSegment = (360.0f / vertSegments) * Mathf.Deg2Rad;
            var descriptionIndex     = new int[2 + vertSegments];

            descriptionIndex[0] = 0;
            descriptionIndex[1] = 1;

            var circleVertices = new Vector2[vertSegments];

            var min             = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
            var max             = new Vector2(float.NegativeInfinity, float.NegativeInfinity);
            var tubeAngleOffset = ((((vertSegments & 1) == 1) ? 0.0f : ((360.0f / vertSegments) * 0.5f)) + definition.tubeRotation) * Mathf.Deg2Rad;

            for (int v = 0; v < vertSegments; v++)
            {
                var vRad = tubeAngleOffset + (v * vertDegreePerSegment);
                circleVertices[v] = new Vector2((Mathf.Cos(vRad) * tubeRadiusX) - torusRadius,
                                                (Mathf.Sin(vRad) * tubeRadiusY));
                min.x = Mathf.Min(min.x, circleVertices[v].x);
                min.y = Mathf.Min(min.y, circleVertices[v].y);
                max.x = Mathf.Max(max.x, circleVertices[v].x);
                max.y = Mathf.Max(max.y, circleVertices[v].y);
                descriptionIndex[v + 2] = 2;
            }

            if (definition.fitCircle)
            {
                var center = (max + min) * 0.5f;
                var size   = (max - min) * 0.5f;
                size.x = tubeRadiusX / size.x;
                size.y = tubeRadiusY / size.y;
                for (int v = 0; v < vertSegments; v++)
                {
                    circleVertices[v].x  = (circleVertices[v].x - center.x) * size.x;
                    circleVertices[v].y  = (circleVertices[v].y - center.y) * size.y;
                    circleVertices[v].x -= torusRadius;
                }
            }

            var subMeshes  = new CSGBrushSubMesh[horzSegments];
            var horzOffset = definition.startAngle;

            for (int h = 1, p = 0; h < horzSegments + 1; p = h, h++)
            {
                var hDegree0        = (p * horzDegreePerSegment) + horzOffset;
                var hDegree1        = (h * horzDegreePerSegment) + horzOffset;
                var rotation0       = Quaternion.AngleAxis(hDegree0, Vector3.up);
                var rotation1       = Quaternion.AngleAxis(hDegree1, Vector3.up);
                var subMeshVertices = new Vector3[vertSegments * 2];
                for (int v = 0; v < vertSegments; v++)
                {
                    subMeshVertices[v + vertSegments] = rotation0 * circleVertices[v];
                    subMeshVertices[v] = rotation1 * circleVertices[v];
                }

                var subMesh = new CSGBrushSubMesh();
                CreateExtrudedSubMesh(subMesh, vertSegments, descriptionIndex, descriptionIndex, 0, 1, subMeshVertices, surfaces, descriptions);
                if (!subMesh.Validate())
                {
                    brushMeshAsset.Clear();
                    return(false);
                }
                subMeshes[h - 1] = subMesh;
            }

            brushMeshAsset.SubMeshes = subMeshes;

            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
示例#15
0
 static bool CreateExtrudedSubMesh(CSGBrushSubMesh subMesh, int segments, int[] segmentDescriptionIndices, int segmentTopIndex, int segmentBottomIndex, Vector3[] vertices, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
 {
     return(CreateExtrudedSubMesh(subMesh, segments, segmentDescriptionIndices, null, segmentTopIndex, segmentBottomIndex, vertices, surfaceAssets, surfaceDescriptions));
 }
示例#16
0
        public static bool GenerateSpiralStairsAsset(CSGBrushMeshAsset brushMeshAsset, ref CSGSpiralStairsDefinition definition, CSGSurfaceAsset[] surfaceAssets, ref SurfaceDescription[] surfaceDescriptions)
        {
            if (surfaceAssets == null ||
                surfaceDescriptions == null ||
                surfaceAssets.Length != 6 ||
                surfaceDescriptions.Length != 6)
            {
                brushMeshAsset.Clear();
                return(false);
            }

            definition.Validate();

            const float kEpsilon = 0.001f;

            var nosingDepth = definition.nosingDepth;
            var treadHeight = (nosingDepth < kEpsilon) ? 0 : definition.treadHeight;
            var haveTread   = (treadHeight >= kEpsilon);

            var innerDiameter = definition.innerDiameter;
            var haveInnerCyl  = (innerDiameter >= kEpsilon);

            var riserType = definition.riserType;
            var haveRiser = riserType != StairsRiserType.None;

            if (!haveRiser && !haveTread)
            {
                brushMeshAsset.Clear();
                return(false);
            }


            var origin = definition.origin;

            var startAngle   = definition.startAngle * Mathf.Deg2Rad;
            var anglePerStep = definition.AnglePerStep * Mathf.Deg2Rad;

            var nosingWidth   = definition.nosingWidth;
            var outerDiameter = definition.outerDiameter;

            var p0 = new Vector2(Mathf.Sin(0), Mathf.Cos(0));
            var p1 = new Vector2(Mathf.Sin(anglePerStep), Mathf.Cos(anglePerStep));
            var pm = new Vector2(Mathf.Sin(anglePerStep * 0.5f), Mathf.Cos(anglePerStep * 0.5f));
            var pn = (p0 + p1) * 0.5f;

            var stepOuterDiameter = outerDiameter + ((pm.magnitude - pn.magnitude) * (outerDiameter * 1.25f));               // TODO: figure out why we need the 1.25 magic number to fit the step in the outerDiameter?
            var stepOuterRadius   = stepOuterDiameter * 0.5f;
            var stepInnerRadius   = 0.0f;
            var stepHeight        = definition.stepHeight;
            var height            = definition.height;
            var stepCount         = definition.StepCount;

            if (height < 0)
            {
                origin.y += height;
                height    = -height;
            }

            // TODO: expose this to user
            var smoothSubDivisions = 3;

            var cylinderSubMeshCount = haveInnerCyl ? 2 : 1;
            var subMeshPerRiser      = (riserType == StairsRiserType.None) ? 0 :
                                       (riserType == StairsRiserType.Smooth) ? (2 * smoothSubDivisions)
                                                                                : 1;
            var riserSubMeshCount = (stepCount * subMeshPerRiser) + ((riserType == StairsRiserType.None) ? 0 : cylinderSubMeshCount);
            var treadSubMeshCount = (haveTread ? stepCount + cylinderSubMeshCount : 0);
            var subMeshCount      = (treadSubMeshCount + riserSubMeshCount);

            var treadStart = !haveRiser ? 0 : riserSubMeshCount;
            var innerSides = definition.innerSegments;
            var outerSides = definition.outerSegments;
            var riserDepth = definition.riserDepth;

            CSGBrushSubMesh[] subMeshes;
            if (brushMeshAsset.SubMeshCount != subMeshCount)
            {
                subMeshes = new CSGBrushSubMesh[subMeshCount];
                for (int i = 0; i < subMeshCount; i++)
                {
                    subMeshes[i] = new CSGBrushSubMesh();
                }
            }
            else
            {
                subMeshes = brushMeshAsset.SubMeshes;
            }

            if (haveRiser)
            {
                if (riserType == StairsRiserType.ThinRiser)
                {
                    var     minY = origin.y;
                    var     maxY = origin.y + stepHeight - treadHeight;
                    Vector2 o0, o1;
                    float   angle = startAngle;
                    var     c1    = Mathf.Sin(angle) * stepOuterRadius;
                    var     s1    = Mathf.Cos(angle) * stepOuterRadius;
                    for (int i = 0; i < stepCount; i++)
                    {
                        var c0 = c1;
                        var s0 = s1;
                        angle += anglePerStep;
                        c1     = Mathf.Sin(angle) * stepOuterRadius;
                        s1     = Mathf.Cos(angle) * stepOuterRadius;

                        o0 = new Vector2(origin.x + c0, origin.z + s0);
                        o1 = new Vector2(origin.x, origin.z);

                        var riserVector = (new Vector2((c0 - c1), (s0 - s1)).normalized) * riserDepth;

                        var i0 = o0 - riserVector;
                        var i1 = o1 - riserVector;

                        var vertices = new[] {
                            new Vector3(i0.x, maxY, i0.y),                      // 0
                            new Vector3(i1.x, maxY, i1.y),                      // 1
                            new Vector3(o1.x, maxY, o1.y),                      // 2
                            new Vector3(o0.x, maxY, o0.y),                      // 3

                            new Vector3(i0.x, minY, i0.y),                      // 4
                            new Vector3(i1.x, minY, i1.y),                      // 5
                            new Vector3(o1.x, minY, o1.y),                      // 6
                            new Vector3(o0.x, minY, o0.y),                      // 7
                        };

                        if (i == 0)
                        {
                            subMeshes[i].Polygons = CreateBoxAssetPolygons(surfaceAssets, surfaceDescriptions);
                            minY -= treadHeight;
                        }
                        else
                        {
                            subMeshes[i].Polygons = subMeshes[0].Polygons.ToArray();
                        }
                        subMeshes[i].HalfEdges = (anglePerStep > 0) ? invertedBoxHalfEdges.ToArray() : boxHalfEdges.ToArray();
                        subMeshes[i].Vertices  = vertices;

                        minY += stepHeight;
                        maxY += stepHeight;
                    }
                }
                else
                if (riserType == StairsRiserType.Smooth)
                {
                    //var stepY = stepHeight;
                    var   minY  = origin.y;
                    var   maxY  = origin.y + stepHeight - treadHeight;
                    var   maxY2 = origin.y + (stepHeight * 2) - treadHeight;
                    float angle = startAngle;
                    var   c1    = Mathf.Sin(angle);
                    var   s1    = Mathf.Cos(angle);
                    angle += anglePerStep;
                    var c2 = Mathf.Sin(angle);
                    var s2 = Mathf.Cos(angle);

                    for (int i = 0; i < riserSubMeshCount; i += subMeshPerRiser)
                    {
                        var c0 = c1;
                        var s0 = s1;
                        c1     = c2;
                        s1     = s2;
                        angle += anglePerStep;
                        c2     = Mathf.Sin(angle);
                        s2     = Mathf.Cos(angle);

                        var c0o = c0 * stepOuterRadius;
                        var c1o = c1 * stepOuterRadius;
                        var s0o = s0 * stepOuterRadius;
                        var s1o = s1 * stepOuterRadius;

                        var o0 = new Vector2(origin.x + c0o, origin.z + s0o);
                        var o1 = new Vector2(origin.x + c1o, origin.z + s1o);

                        var i0 = o0;
                        var i1 = o1;

                        int subMeshIndex = i;
                        for (int subDiv = 1; subDiv < smoothSubDivisions; subDiv++)
                        {
                            // TODO: need to space the subdivisions from smallest spaces to bigger spaces
                            float stepMidRadius;
                            stepMidRadius = (((outerDiameter * 0.5f) * (1.0f / (smoothSubDivisions + 1))) * ((smoothSubDivisions - 1) - (subDiv - 1)));
                            if (subDiv == (smoothSubDivisions - 1))
                            {
                                var innerRadius = (innerDiameter * 0.5f) - 0.1f;
                                stepMidRadius = (innerRadius < 0.1f) ? stepMidRadius : innerRadius;
                            }

                            var c0i = c0 * stepMidRadius;
                            var c1i = c1 * stepMidRadius;
                            var s0i = s0 * stepMidRadius;
                            var s1i = s1 * stepMidRadius;

                            i0 = new Vector2(origin.x + c0i, origin.z + s0i);
                            i1 = new Vector2(origin.x + c1i, origin.z + s1i);

                            {
                                var vertices = new[] {
                                    new Vector3(i0.x, maxY, i0.y),                        // 0
                                    new Vector3(i0.x, minY, i0.y),                        // 1
                                    new Vector3(o0.x, minY, o0.y),                        // 2
                                    new Vector3(o0.x, maxY, o0.y),                        // 3

                                    new Vector3(o1.x, maxY, o1.y),                        // 4
                                };

                                if (i == 0)
                                {
                                    subMeshes[subMeshIndex].Polygons = CreateSquarePyramidAssetPolygons(surfaceAssets, surfaceDescriptions);
                                }
                                else
                                {
                                    subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray();
                                }
                                subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedSquarePyramidHalfEdges.ToArray() : squarePyramidHalfEdges.ToArray();
                                subMeshes[subMeshIndex].Vertices  = vertices;
                                subMeshIndex++;
                            }

                            {
                                var vertices = new[] {
                                    new Vector3(i0.x, maxY, i0.y),                        // 0
                                    new Vector3(i0.x, minY, i0.y),                        // 1
                                    new Vector3(i1.x, maxY, i1.y),                        // 2

                                    new Vector3(o1.x, maxY, o1.y),                        // 3
                                };

                                if (i == 0)
                                {
                                    subMeshes[subMeshIndex].Polygons = CreateTriangularPyramidAssetPolygons(surfaceAssets, surfaceDescriptions);
                                }
                                else
                                {
                                    subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray();
                                }
                                subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedTriangularPyramidHalfEdges.ToArray() : triangularPyramidHalfEdges.ToArray();
                                subMeshes[subMeshIndex].Vertices  = vertices;
                                subMeshIndex++;
                            }

                            o0 = i0;
                            o1 = i1;
                        }

                        {
                            var vertices = new[] {
                                new Vector3(i0.x, maxY, i0.y),                        // 0
                                new Vector3(i1.x, maxY, i1.y),                        // 2
                                new Vector3(i0.x, minY, i0.y),                        // 1

                                new Vector3(origin.x, minY, origin.y),                // 3
                            };

                            if (i == 0)
                            {
                                subMeshes[subMeshIndex].Polygons = CreateTriangularPyramidAssetPolygons(surfaceAssets, surfaceDescriptions);
                            }
                            else
                            {
                                subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray();
                            }
                            subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedTriangularPyramidHalfEdges.ToArray() : triangularPyramidHalfEdges.ToArray();
                            subMeshes[subMeshIndex].Vertices  = vertices;
                            subMeshIndex++;
                        }

                        {
                            var vertices = new[] {
                                new Vector3(i1.x, maxY, i1.y),                        // 2
                                new Vector3(i0.x, maxY, i0.y),                        // 0
                                new Vector3(origin.x, maxY, origin.y),                // 1

                                new Vector3(origin.x, minY, origin.y),                // 3
                            };

                            if (i == 0)
                            {
                                subMeshes[subMeshIndex].Polygons = CreateTriangularPyramidAssetPolygons(surfaceAssets, surfaceDescriptions);
                            }
                            else
                            {
                                subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray();
                            }
                            subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedTriangularPyramidHalfEdges.ToArray() : triangularPyramidHalfEdges.ToArray();
                            subMeshes[subMeshIndex].Vertices  = vertices;
                            subMeshIndex++;
                        }

                        if (i == 0)
                        {
                            minY -= treadHeight;
                        }

                        minY  += stepHeight;
                        maxY  += stepHeight;
                        maxY2 += stepHeight;
                    }
                }
                else
                {
                    var     minY = origin.y;
                    var     maxY = origin.y + stepHeight - treadHeight;
                    Vector2 o0, o1;
                    float   angle = startAngle;
                    var     c1    = Mathf.Sin(angle) * stepOuterRadius;
                    var     s1    = Mathf.Cos(angle) * stepOuterRadius;
                    for (int i = 0; i < stepCount; i++)
                    {
                        var c0 = c1;
                        var s0 = s1;
                        angle += anglePerStep;
                        c1     = Mathf.Sin(angle) * stepOuterRadius;
                        s1     = Mathf.Cos(angle) * stepOuterRadius;

                        o0 = new Vector2(origin.x + c0, origin.z + s0);
                        o1 = new Vector2(origin.x + c1, origin.z + s1);
                        var vertices = new[] {
                            new Vector3(origin.x, maxY, origin.z),                // 0
                            new Vector3(o1.x, maxY, o1.y),                        // 1
                            new Vector3(o0.x, maxY, o0.y),                        // 2

                            new Vector3(origin.x, minY, origin.z),                // 3
                            new Vector3(o1.x, minY, o1.y),                        // 4
                            new Vector3(o0.x, minY, o0.y),                        // 5
                        };

                        if (i == 0)
                        {
                            subMeshes[i].Polygons = CreateWedgeAssetPolygons(surfaceAssets, surfaceDescriptions);
                            minY -= treadHeight;
                        }
                        else
                        {
                            subMeshes[i].Polygons = subMeshes[0].Polygons.ToArray();
                        }
                        subMeshes[i].HalfEdges = (anglePerStep > 0) ? invertedWedgeHalfEdges.ToArray() : wedgeHalfEdges.ToArray();
                        subMeshes[i].Vertices  = vertices;

                        if (riserType != StairsRiserType.FillDown)
                        {
                            minY += stepHeight;
                        }
                        maxY += stepHeight;
                    }
                }

                {
                    var subMeshIndex          = treadStart - cylinderSubMeshCount;
                    var cylinderSurfaceAssets = new CSGSurfaceAsset[3] {
                        surfaceAssets[0], surfaceAssets[1], surfaceAssets[2]
                    };
                    var cylinderSurfaceDescriptions = new SurfaceDescription[outerSides + 2]; //surfaceDescriptions[0]
                    cylinderSurfaceDescriptions[0] = surfaceDescriptions[0];
                    cylinderSurfaceDescriptions[1] = surfaceDescriptions[1];
                    for (int i = 0; i < outerSides; i++)
                    {
                        cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2];
                    }
                    GenerateCylinderSubMesh(subMeshes[subMeshIndex], outerDiameter, origin.y, origin.y + height, 0, outerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions);
                    subMeshes[subMeshIndex].Operation = CSGOperationType.Intersecting;
                }

                if (haveInnerCyl)
                {
                    var subMeshIndex          = treadStart - 1;
                    var cylinderSurfaceAssets = new CSGSurfaceAsset[3] {
                        surfaceAssets[0], surfaceAssets[1], surfaceAssets[2]
                    };
                    var cylinderSurfaceDescriptions = new SurfaceDescription[innerSides + 2]; //surfaceDescriptions[0]
                    cylinderSurfaceDescriptions[0] = surfaceDescriptions[0];
                    cylinderSurfaceDescriptions[1] = surfaceDescriptions[1];
                    for (int i = 0; i < innerSides; i++)
                    {
                        cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2];
                    }
                    GenerateCylinderSubMesh(subMeshes[subMeshIndex], innerDiameter, origin.y, origin.y + height, 0, innerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions);
                    subMeshes[subMeshIndex].Operation = CSGOperationType.Subtractive;
                }
            }

            if (haveTread)
            {
                var     minY = origin.y + stepHeight - treadHeight;
                var     maxY = origin.y + stepHeight;
                Vector2 i0, i1, o0, o1;
                float   angle      = startAngle;
                var     c1         = Mathf.Sin(angle);
                var     s1         = Mathf.Cos(angle);
                var     startIndex = treadStart;
                for (int n = 0, i = startIndex; n < stepCount; n++, i++)
                {
                    var c0 = c1;
                    var s0 = s1;
                    angle += anglePerStep;
                    c1     = Mathf.Sin(angle);
                    s1     = Mathf.Cos(angle);

                    i0 = new Vector2(origin.x + (c0 * (stepInnerRadius)), origin.z + (s0 * (stepInnerRadius)));
                    i1 = new Vector2(origin.x + (c1 * (stepInnerRadius)), origin.z + (s1 * (stepInnerRadius)));
                    o0 = new Vector2(origin.x + (c0 * (stepOuterRadius + nosingWidth)), origin.z + (s0 * (stepOuterRadius + nosingWidth)));
                    o1 = new Vector2(origin.x + (c1 * (stepOuterRadius + nosingWidth)), origin.z + (s1 * (stepOuterRadius + nosingWidth)));

                    var noseSizeDeep = (new Vector2((c0 - c1), (s0 - s1)).normalized) * nosingDepth;
                    i0 += noseSizeDeep;
                    o0 += noseSizeDeep;

                    var vertices = new[] {
                        new Vector3(i1.x, maxY, i1.y),                      // 1
                        new Vector3(i0.x, maxY, i0.y),                      // 0
                        new Vector3(o0.x, maxY, o0.y),                      // 3
                        new Vector3(o1.x, maxY, o1.y),                      // 2

                        new Vector3(i1.x, minY, i1.y),                      // 5
                        new Vector3(i0.x, minY, i0.y),                      // 4
                        new Vector3(o0.x, minY, o0.y),                      // 7
                        new Vector3(o1.x, minY, o1.y),                      // 6
                    };

                    if (n == 0)
                    {
                        subMeshes[i].Polygons = CreateBoxAssetPolygons(surfaceAssets, surfaceDescriptions);
                    }
                    else
                    {
                        subMeshes[i].Polygons = subMeshes[startIndex].Polygons.ToArray();
                    }

                    subMeshes[i].HalfEdges = (anglePerStep > 0) ? invertedBoxHalfEdges.ToArray() : boxHalfEdges.ToArray();
                    subMeshes[i].Vertices  = vertices;

                    minY += stepHeight;
                    maxY += stepHeight;
                }
            }


            {
                var subMeshIndex          = subMeshCount - cylinderSubMeshCount;
                var cylinderSurfaceAssets = new CSGSurfaceAsset[3] {
                    surfaceAssets[0], surfaceAssets[1], surfaceAssets[2]
                };
                var cylinderSurfaceDescriptions = new SurfaceDescription[outerSides + 2]; //surfaceDescriptions[0]
                cylinderSurfaceDescriptions[0] = surfaceDescriptions[0];
                cylinderSurfaceDescriptions[1] = surfaceDescriptions[1];
                for (int i = 0; i < outerSides; i++)
                {
                    cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2];
                }
                GenerateCylinderSubMesh(subMeshes[subMeshIndex], outerDiameter + nosingWidth, origin.y, origin.y + height, 0, outerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions);
                subMeshes[subMeshIndex].Operation = CSGOperationType.Intersecting;
            }

            if (haveInnerCyl)
            {
                var subMeshIndex          = subMeshCount - 1;
                var cylinderSurfaceAssets = new CSGSurfaceAsset[3] {
                    surfaceAssets[0], surfaceAssets[1], surfaceAssets[2]
                };
                var cylinderSurfaceDescriptions = new SurfaceDescription[innerSides + 2]; //surfaceDescriptions[0]
                cylinderSurfaceDescriptions[0] = surfaceDescriptions[0];
                cylinderSurfaceDescriptions[1] = surfaceDescriptions[1];
                for (int i = 0; i < innerSides; i++)
                {
                    cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2];
                }
                GenerateCylinderSubMesh(subMeshes[subMeshIndex], innerDiameter - nosingWidth, origin.y, origin.y + height, 0, innerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions);
                subMeshes[subMeshIndex].Operation = CSGOperationType.Subtractive;
            }


            brushMeshAsset.SubMeshes = subMeshes;
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
示例#17
0
        // TODO: clean up
        public static bool GenerateSegmentedSubMesh(CSGBrushSubMesh subMesh, int horzSegments, int vertSegments, Vector3[] segmentVertices, bool topCap, bool bottomCap, int topVertex, int bottomVertex, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
        {
            // FIXME: hack, to fix math below ..
            vertSegments++;

            //if (bottomCap || topCap)
            //	vertSegments++;

            int triangleCount, quadCount, capCount, extraVertices;

            capCount      = 0;
            triangleCount = 0;
            extraVertices = 0;
            if (topCap)
            {
                capCount += 1;
            }
            else
            {
                extraVertices += 1;
                triangleCount += horzSegments;
            }

            if (bottomCap)
            {
                capCount += 1;
            }
            else
            {
                extraVertices += 1;
                triangleCount += horzSegments;
            }

            quadCount = horzSegments * (vertSegments - 2);

            var vertexCount       = (horzSegments * (vertSegments - 1)) + extraVertices;
            var assetPolygonCount = triangleCount + quadCount + capCount;
            var halfEdgeCount     = (triangleCount * 3) + (quadCount * 4) + (capCount * horzSegments);

            if (segmentVertices.Length != vertexCount)
            {
                Debug.LogError("segmentVertices.Length (" + segmentVertices.Length + ") != expectedVertexCount (" + vertexCount + ")");
                subMesh.Clear();
                return(false);
            }

            var vertices  = segmentVertices;
            var polygons  = new CSGBrushSubMesh.Polygon[assetPolygonCount];
            var halfEdges = new BrushMesh.HalfEdge[halfEdgeCount];

            var twins = new int[horzSegments];

            var edgeIndex       = 0;
            var polygonIndex    = 0;
            var startVertex     = extraVertices;
            var startSegment    = topCap ? 1 : 0;
            var lastVertSegment = vertSegments - 1;
            var endSegment      = bottomCap ? lastVertSegment : vertSegments;

            if (topCap)
            {
                var polygonEdgeCount = horzSegments;
                for (int h = 0, p = horzSegments - 1; h < horzSegments; p = h, h++)
                {
                    var currEdgeIndex = edgeIndex + (horzSegments - 1) - h;
                    halfEdges[currEdgeIndex] = new BrushMesh.HalfEdge {
                        twinIndex = -1, vertexIndex = startVertex + (horzSegments - 1) - p
                    };
                    twins[h] = currEdgeIndex;
                }
                polygons[polygonIndex] = new CSGBrushSubMesh.Polygon {
                    surfaceID = polygonIndex, firstEdge = edgeIndex, edgeCount = polygonEdgeCount, description = surfaceDescriptions[0], surfaceAsset = surfaceAssets[0]
                };
                edgeIndex += polygonEdgeCount;
                polygonIndex++;
            }

            for (int v = startSegment; v < endSegment; v++)
            {
                var startEdge = edgeIndex;
                for (int h = 0, p = horzSegments - 1; h < horzSegments; p = h, h++)
                {
                    var n = (h + 1) % horzSegments;
                    int polygonEdgeCount;
                    if (v == 0) // top
                    {
                        //          0
                        //          *
                        //         ^ \
                        //     p1 /0 1\ n0
                        //       /  2  v
                        //		*<------*
                        //     2    t    1
                        polygonEdgeCount = 3;
                        var p1 = (p * 3) + 1;
                        var n0 = (n * 3) + 0;
                        halfEdges[edgeIndex + 0] = new BrushMesh.HalfEdge {
                            twinIndex = p1, vertexIndex = topVertex
                        };
                        halfEdges[edgeIndex + 1] = new BrushMesh.HalfEdge {
                            twinIndex = n0, vertexIndex = startVertex + (horzSegments - 1) - h
                        };
                        halfEdges[edgeIndex + 2] = new BrushMesh.HalfEdge {
                            twinIndex = -1, vertexIndex = startVertex + (horzSegments - 1) - p
                        };
                        twins[h] = edgeIndex + 2;
                    }
                    else
                    if (v == lastVertSegment) // bottom
                    {
                        //     0    t    1
                        //		*------>*
                        //       ^  1  /
                        //     p2 \0 2/ n0
                        //         \ v
                        //          *
                        //          2
                        polygonEdgeCount = 3;
                        var p2 = startEdge + (p * 3) + 2;
                        var n0 = startEdge + (n * 3) + 0;
                        var t  = twins[h];
                        halfEdges[twins[h]].twinIndex = edgeIndex + 1;
                        halfEdges[edgeIndex + 0]      = new BrushMesh.HalfEdge {
                            twinIndex = p2, vertexIndex = startVertex + (horzSegments - 1) - p
                        };
                        halfEdges[edgeIndex + 1] = new BrushMesh.HalfEdge {
                            twinIndex = t, vertexIndex = startVertex + (horzSegments - 1) - h
                        };
                        halfEdges[edgeIndex + 2] = new BrushMesh.HalfEdge {
                            twinIndex = n0, vertexIndex = bottomVertex
                        };
                    }
                    else
                    {
                        //     0    t3   1
                        //		*------>*
                        //      ^   1   |
                        //   p1 |0     2| n0
                        //      |   3   v
                        //		*<------*
                        //     3    t1   2
                        polygonEdgeCount = 4;
                        var p1 = startEdge + (p * 4) + 2;
                        var n0 = startEdge + (n * 4) + 0;
                        var t  = twins[h];
                        halfEdges[twins[h]].twinIndex = edgeIndex + 1;
                        halfEdges[edgeIndex + 0]      = new BrushMesh.HalfEdge {
                            twinIndex = p1, vertexIndex = startVertex + (horzSegments - 1) - p
                        };
                        halfEdges[edgeIndex + 1] = new BrushMesh.HalfEdge {
                            twinIndex = t, vertexIndex = startVertex + (horzSegments - 1) - h
                        };
                        halfEdges[edgeIndex + 2] = new BrushMesh.HalfEdge {
                            twinIndex = n0, vertexIndex = startVertex + (horzSegments - 1) - h + horzSegments
                        };
                        halfEdges[edgeIndex + 3] = new BrushMesh.HalfEdge {
                            twinIndex = -1, vertexIndex = startVertex + (horzSegments - 1) - p + horzSegments
                        };
                        twins[h] = edgeIndex + 3;
                    }
                    polygons[polygonIndex] = new CSGBrushSubMesh.Polygon {
                        surfaceID = polygonIndex, firstEdge = edgeIndex, edgeCount = polygonEdgeCount, description = surfaceDescriptions[0], surfaceAsset = surfaceAssets[0]
                    };
                    edgeIndex += polygonEdgeCount;
                    polygonIndex++;
                }
                if (v > 0)
                {
                    startVertex += horzSegments;
                }
            }
            if (bottomCap)
            {
                var polygonEdgeCount = horzSegments;
                for (int h = 0; h < horzSegments; h++)
                {
                    var currEdgeIndex = edgeIndex + h;
                    halfEdges[twins[h]].twinIndex = currEdgeIndex;
                    halfEdges[currEdgeIndex]      = new BrushMesh.HalfEdge {
                        twinIndex = twins[h], vertexIndex = startVertex + (horzSegments - 1) - h
                    };
                }
                polygons[polygonIndex] = new CSGBrushSubMesh.Polygon {
                    surfaceID = polygonIndex, firstEdge = edgeIndex, edgeCount = polygonEdgeCount, description = surfaceDescriptions[0], surfaceAsset = surfaceAssets[0]
                };
            }

            subMesh.Polygons  = polygons;
            subMesh.HalfEdges = halfEdges;
            subMesh.Vertices  = vertices;
            return(true);
        }
示例#18
0
        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);
        }
示例#19
0
        public static bool GenerateExtrudedShapeAsset(CSGBrushMeshAsset brushMeshAsset, Curve2D shape, Path path, int curveSegments, CSGSurfaceAsset[] surfaceAssets, ref SurfaceDescription[] surfaceDescriptions)
        {
            var shapeVertices       = new List <Vector2>();
            var shapeSegmentIndices = new List <int>();

            GetPathVertices(shape, curveSegments, shapeVertices, shapeSegmentIndices);

            Vector2[][] polygonVerticesArray;
            int[][]     polygonIndicesArray;

            if (!Decomposition.ConvexPartition(shapeVertices, shapeSegmentIndices,
                                               out polygonVerticesArray,
                                               out polygonIndicesArray))
            {
                return(false);
            }

            // TODO: make each extruded quad split into two triangles when it's not a perfect plane,
            //			split it to make sure it's convex

            // TODO: make it possible to smooth (parts) of the shape

            // TODO: make materials work well
            // TODO: make it possible to 'draw' shapes on any surface

            // TODO: make path work as a spline, with subdivisions
            // TODO:	make this work well with twisted rotations
            // TODO: make shape/path subdivisions be configurable / automatic



            var subMeshes = new List <CSGBrushSubMesh>();

            for (int p = 0; p < polygonVerticesArray.Length; p++)
            {
                var polygonVertices = polygonVerticesArray[p];
                var segmentIndices  = polygonIndicesArray[p];
                var shapeSegments   = polygonVertices.Length;

                for (int s = 0; s < path.segments.Length - 1; s++)
                {
                    var pathPointA       = path.segments[s];
                    var pathPointB       = path.segments[s + 1];
                    int subSegments      = 1;
                    var offsetQuaternion = pathPointB.rotation * Quaternion.Inverse(pathPointA.rotation);
                    var offsetEuler      = offsetQuaternion.eulerAngles;
                    if (offsetEuler.x > 180)
                    {
                        offsetEuler.x = 360 - offsetEuler.x;
                    }
                    if (offsetEuler.y > 180)
                    {
                        offsetEuler.y = 360 - offsetEuler.y;
                    }
                    if (offsetEuler.z > 180)
                    {
                        offsetEuler.z = 360 - offsetEuler.z;
                    }
                    var maxAngle = Mathf.Max(offsetEuler.x, offsetEuler.y, offsetEuler.z);
                    if (maxAngle != 0)
                    {
                        subSegments = Mathf.Max(1, (int)Mathf.Ceil(maxAngle / 5));
                    }

                    if ((pathPointA.scale.x / pathPointA.scale.y) != (pathPointB.scale.x / pathPointB.scale.y) &&
                        (subSegments & 1) == 1)
                    {
                        subSegments += 1;
                    }

                    for (int n = 0; n < subSegments; n++)
                    {
                        var matrix0 = PathPoint.Lerp(ref path.segments[s], ref path.segments[s + 1], n / (float)subSegments);
                        var matrix1 = PathPoint.Lerp(ref path.segments[s], ref path.segments[s + 1], (n + 1) / (float)subSegments);

                        // TODO: this doesn't work if top and bottom polygons intersect
                        //			=> need to split into two brushes then, invert one of the two brushes
                        var invertDot = Vector3.Dot(matrix0.MultiplyVector(Vector3.forward).normalized, (matrix1.MultiplyPoint(shapeVertices[0]) - matrix0.MultiplyPoint(shapeVertices[0])).normalized);

                        if (invertDot == 0.0f)
                        {
                            continue;
                        }

                        Vector3[] vertices;
                        if (invertDot < 0)
                        {
                            var m = matrix0; matrix0 = matrix1; matrix1 = m;
                        }
                        if (!GetExtrudedVertices(polygonVertices, matrix0, matrix1, out vertices))
                        {
                            continue;
                        }

                        var subMesh = new CSGBrushSubMesh();
                        CreateExtrudedSubMesh(subMesh, shapeSegments, segmentIndices, 0, 1, vertices, surfaceAssets, surfaceDescriptions);
                        subMeshes.Add(subMesh);
                    }
                }
            }

            brushMeshAsset.SubMeshes = subMeshes.ToArray();
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.OnValidate();
            brushMeshAsset.SetDirty();
            return(true);
        }
示例#20
0
        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();
        }