/// Builds an array of edges that connect to only one triangle.
        /// In other words, the outline of the mesh
        public static Edge[] BuildManifoldEdges(GOMesh mesh)
        {
            // Build a edge list for all unique edges in the mesh
            Edge[] edges = BuildEdges(mesh.vertices.Length, mesh.triangles);

            // We only want edges that connect to a single triangle
            ArrayList culledEdges = new ArrayList();

            foreach (Edge edge in edges)
            {
                if (edge.faceIndex[0] == edge.faceIndex[1])
                {
                    culledEdges.Add(edge);
                }
            }

            return(culledEdges.ToArray(typeof(Edge)) as Edge[]);
        }
Example #2
0
 public GOMesh(GOMesh premesh)
 {
     vertices  = premesh.vertices;
     triangles = premesh.triangles;
     uv        = premesh.uv;
 }
        public static void ExtrudeMesh(GOMesh srcMesh, GOMesh extrudedMesh, Matrix4x4[] extrusion, Edge[] edges, bool invertFaces)
        {
            int extrudedVertexCount   = edges.Length * 2 * extrusion.Length;
            int triIndicesPerStep     = edges.Length * 6;
            int extrudedTriIndexCount = triIndicesPerStep * (extrusion.Length - 1);

            Vector3[] inputVertices  = srcMesh.vertices;
            Vector2[] inputUV        = srcMesh.uv;
            int[]     inputTriangles = srcMesh.triangles;

            Vector3[] vertices  = new Vector3[extrudedVertexCount + srcMesh.vertices.Length * 2];
            Vector2[] uvs       = new Vector2[vertices.Length];
            int[]     triangles = new int[extrudedTriIndexCount + inputTriangles.Length * 2];


            // Build extruded vertices
            int v = 0;

            for (int i = 0; i < extrusion.Length; i++)
            {
                Matrix4x4 matrix = extrusion[i];
                float     vcoord = (float)i / (extrusion.Length - 1);
                float     factor = (extrusion.Length - 1) - ((float)i / (extrusion.Length - 1)) * srcMesh.sliceHeight;
                //foreach (Edge e in edges)
                for (int k = 0; k < edges.Length; k++)
                {
                    Edge e = edges[k];

                    vertices[v + 0] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[0]]);
                    vertices[v + 1] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[1]]);

                    switch (srcMesh.uvMappingStyle)
                    {
                    case GOUVMappingStyle.TopAndSidesRepeated:

                        Matrix4x4 mn;
                        if (i == extrusion.Length - 1)
                        {
                            mn = extrusion[0];
                        }
                        else
                        {
                            mn = extrusion[i + 1];
                        }
                        Vector3 p1 = mn.MultiplyPoint(inputVertices[e.vertexIndex[0]]);
                        Vector3 p2 = mn.MultiplyPoint(inputVertices[e.vertexIndex[1]]);
                        uvs[v + 0] = new Vector2(p1.x, -p1.y) / 100;
                        uvs[v + 1] = new Vector2(p2.x, -p2.y) / 100;

                        break;

                    case GOUVMappingStyle.TopRepeatedSidesStretched:
                        uvs[v + 0] = inputUV[e.vertexIndex[0]];
                        uvs[v + 1] = inputUV[e.vertexIndex[1]];
                        break;

                    case GOUVMappingStyle.TopFitSidesFit:
                    case GOUVMappingStyle.TopRepeatedSidesFit:
                    case GOUVMappingStyle.TopCenteredSidesFit:
                        uvs[v + 0] = new Vector2(1, extrusion.Length - 1 - i);
                        uvs[v + 1] = new Vector2(0, extrusion.Length - 1 - i);
                        break;

                    case GOUVMappingStyle.TopFitSidesRatio:

                        float hdist = Vector3.Distance(vertices[v + 0], vertices[v + 1]);
                        float vdist = srcMesh.sliceHeight;
                        int   ratio = Mathf.CeilToInt(hdist / vdist);

                        uvs[v + 0] = new Vector2(1 * ratio, extrusion.Length - 1 - i);
                        uvs[v + 1] = new Vector2(0, extrusion.Length - 1 - i);
                        break;

                    case GOUVMappingStyle.TopFitSidesSliced:

                        hdist = Vector3.Distance(vertices[v + 0], vertices[v + 1]);
                        vdist = srcMesh.sliceHeight;
                        ratio = Mathf.CeilToInt(hdist / vdist);

                        float textureFactor = 1 / 3f;
                        int   f             = (extrusion.Length - 1 - i);

                        float offset = 0f;
                        if (srcMesh.Y > srcMesh.sliceHeight)
                        {
                            offset = textureFactor;
                        }

                        if (extrusion.Length == 2)
                        {
                            uvs[v + 0] = new Vector2(1 * ratio, textureFactor * f + offset);
                            uvs[v + 1] = new Vector2(0, textureFactor * f + offset);
                            break;
                        }

                        float x = f % 2 == 0 ? textureFactor : 1 - textureFactor;          // (f/2f) - 0.25f;

                        if (i == 0)
                        {
                            x = extrusion.Length % 2 == 0 ? 1 : textureFactor;
                        }
                        else if (f == 0)
                        {
                            x = offset;
                        }

                        uvs[v + 0] = new Vector2(1 * ratio, x);
                        uvs[v + 1] = new Vector2(0, x);
                        break;


                    default:
                        break;
                    }

                    v += 2;
                }
            }

            // Build cap vertices
            // * The bottom mesh we scale along it's negative extrusion direction. This way extruding a half sphere results in a capsule.
            for (int c = 0; c < 2; c++)
            {
                Matrix4x4 matrix         = extrusion[c == 0 ? 0 : extrusion.Length - 1];
                int       firstCapVertex = c == 0 ? extrudedVertexCount : extrudedVertexCount + inputVertices.Length;
                for (int i = 0; i < inputVertices.Length; i++)
                {
                    vertices[firstCapVertex + i] = matrix.MultiplyPoint(inputVertices[i]);
                    uvs[firstCapVertex + i]      = inputUV[i];
                }
            }

            // Build extruded triangles
            for (int i = 0; i < extrusion.Length - 1; i++)
            {
                int baseVertexIndex = (edges.Length * 2) * i;
                int nextVertexIndex = (edges.Length * 2) * (i + 1);
                for (int e = 0; e < edges.Length; e++)
                {
                    int triIndex = i * triIndicesPerStep + e * 6;

                    triangles[triIndex + 0] = baseVertexIndex + e * 2;
                    triangles[triIndex + 1] = nextVertexIndex + e * 2;
                    triangles[triIndex + 2] = baseVertexIndex + e * 2 + 1;
                    triangles[triIndex + 3] = nextVertexIndex + e * 2;
                    triangles[triIndex + 4] = nextVertexIndex + e * 2 + 1;
                    triangles[triIndex + 5] = baseVertexIndex + e * 2 + 1;
                }
            }

            // build cap triangles
            int triCount = inputTriangles.Length / 3;

            // Top
            if (!srcMesh.separateTop)
            {
                int firstCapVertex   = extrudedVertexCount;
                int firstCapTriIndex = extrudedTriIndexCount;
                for (int i = 0; i < triCount; i++)
                {
                    triangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex;
                    triangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
                    triangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex;
                }
            }
            else
            {
                int firstCapVertex   = extrudedVertexCount;
                int firstCapTriIndex = extrudedTriIndexCount;
                extrudedMesh.topTriangles = new int[triangles.Length];
                for (int i = 0; i < triCount; i++)
                {
                    extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex;
                    extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
                    extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex;
                }
            }

            // Bottom
            if (!srcMesh.separateBottom)
            {
                int firstCapVertex   = extrudedVertexCount + inputVertices.Length;
                int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length;
                for (int i = 0; i < triCount; i++)
                {
                    triangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex;
                    triangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
                    triangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex;
                }
            }
            else
            {
                int firstCapVertex   = extrudedVertexCount + inputVertices.Length;
                int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length;
                extrudedMesh.bottomTriangles = new int[triangles.Length];
                for (int i = 0; i < triCount; i++)
                {
                    extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex;
                    extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
                    extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex;
                }
            }

            if (invertFaces)
            {
                for (int i = 0; i < triangles.Length / 3; i++)
                {
                    int temp = triangles[i * 3 + 0];
                    triangles[i * 3 + 0] = triangles[i * 3 + 1];
                    triangles[i * 3 + 1] = temp;
                }
            }

            extrudedMesh.vertices  = vertices;
            extrudedMesh.uv        = uvs;
            extrudedMesh.triangles = triangles;
        }
 public static void ExtrudeMesh(GOMesh srcMesh, GOMesh extrudedMesh, Matrix4x4[] extrusion, bool invertFaces)
 {
     Edge[] edges = BuildManifoldEdges(srcMesh);
     ExtrudeMesh(srcMesh, extrudedMesh, extrusion, edges, invertFaces);
 }
        /// <summary>
        ///     Recalculate the normals of a mesh based on an angle threshold. This takes
        ///     into account distinct vertices that have the same position.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="angle">
        ///     The smoothing angle. Note that triangles that already share
        ///     the same vertex will be smooth regardless of the angle!
        /// </param>
        public static void RecalculateNormals(this GOMesh mesh, float angle)
        {
            var cosineThreshold = Mathf.Cos(angle * Mathf.Deg2Rad);

            var vertices  = mesh.vertices;
            var normals   = new Vector3[vertices.Length];
            var triangles = mesh.triangles;

            int submeshCount = 0;

            // Holds the normal of each triangle in each sub mesh.
            var triNormals = new Vector3[submeshCount][];

            var dictionary = new Dictionary <VertexKey, List <VertexEntry> >(vertices.Length);

            for (var subMeshIndex = 0; subMeshIndex < submeshCount; ++subMeshIndex)
            {
                triNormals[subMeshIndex] = new Vector3[triangles.Length / 3];

                for (var i = 0; i < triangles.Length; i += 3)
                {
                    int i1 = triangles[i];
                    int i2 = triangles[i + 1];
                    int i3 = triangles[i + 2];

                    // Calculate the normal of the triangle
                    Vector3 p1       = vertices[i2] - vertices[i1];
                    Vector3 p2       = vertices[i3] - vertices[i1];
                    Vector3 normal   = Vector3.Cross(p1, p2).normalized;
                    int     triIndex = i / 3;
                    triNormals[subMeshIndex][triIndex] = normal;

                    List <VertexEntry> entry;
                    VertexKey          key;

                    if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry))
                    {
                        entry = new List <VertexEntry>(4);
                        dictionary.Add(key, entry);
                    }
                    entry.Add(new VertexEntry(subMeshIndex, triIndex, i1));

                    if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry))
                    {
                        entry = new List <VertexEntry>();
                        dictionary.Add(key, entry);
                    }
                    entry.Add(new VertexEntry(subMeshIndex, triIndex, i2));

                    if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry))
                    {
                        entry = new List <VertexEntry>();
                        dictionary.Add(key, entry);
                    }
                    entry.Add(new VertexEntry(subMeshIndex, triIndex, i3));
                }
            }

            // Each entry in the dictionary represents a unique vertex position.

            foreach (var vertList in dictionary.Values)
            {
                for (var i = 0; i < vertList.Count; ++i)
                {
                    var sum      = new Vector3();
                    var lhsEntry = vertList[i];

                    for (var j = 0; j < vertList.Count; ++j)
                    {
                        var rhsEntry = vertList[j];

                        if (lhsEntry.VertexIndex == rhsEntry.VertexIndex)
                        {
                            sum += triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex];
                        }
                        else
                        {
                            // The dot product is the cosine of the angle between the two triangles.
                            // A larger cosine means a smaller angle.
                            var dot = Vector3.Dot(
                                triNormals[lhsEntry.MeshIndex][lhsEntry.TriangleIndex],
                                triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]);
                            if (dot >= cosineThreshold)
                            {
                                sum += triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex];
                            }
                        }
                    }

                    normals[lhsEntry.VertexIndex] = sum.normalized;
                }
            }

            mesh.normals = normals;
        }
        public static void ExtrudeMesh(GOMesh srcMesh, GOMesh extrudedMesh, Matrix4x4[] extrusion, Edge[] edges, bool invertFaces)
        {
            int extrudedVertexCount   = edges.Length * 2 * extrusion.Length;
            int triIndicesPerStep     = edges.Length * 6;
            int extrudedTriIndexCount = triIndicesPerStep * (extrusion.Length - 1);

            Vector3[] inputVertices  = srcMesh.vertices;
            Vector2[] inputUV        = srcMesh.uv;
            int[]     inputTriangles = srcMesh.triangles;

            Vector3[] vertices  = new Vector3[extrudedVertexCount + srcMesh.vertices.Length * 2];
            Vector2[] uvs       = new Vector2[vertices.Length];
            int[]     triangles = new int[extrudedTriIndexCount + inputTriangles.Length * 2];

            // Build extruded vertices
            int v = 0;

            for (int i = 0; i < extrusion.Length; i++)
            {
                Matrix4x4 matrix = extrusion[i];
                float     vcoord = (float)i / (extrusion.Length - 1);
                foreach (Edge e in edges)
                {
                    vertices[v + 0] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[0]]);
                    vertices[v + 1] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[1]]);

                    uvs[v + 0] = new Vector2(inputUV[e.vertexIndex[0]].x, vcoord);
                    uvs[v + 1] = new Vector2(inputUV[e.vertexIndex[1]].x, vcoord);

                    v += 2;
                }
            }

            // Build cap vertices
            // * The bottom mesh we scale along it's negative extrusion direction. This way extruding a half sphere results in a capsule.
            for (int c = 0; c < 2; c++)
            {
                Matrix4x4 matrix         = extrusion[c == 0 ? 0 : extrusion.Length - 1];
                int       firstCapVertex = c == 0 ? extrudedVertexCount : extrudedVertexCount + inputVertices.Length;
                for (int i = 0; i < inputVertices.Length; i++)
                {
                    vertices[firstCapVertex + i] = matrix.MultiplyPoint(inputVertices[i]);
                    uvs[firstCapVertex + i]      = inputUV[i];
                }
            }

            // Build extruded triangles
            for (int i = 0; i < extrusion.Length - 1; i++)
            {
                int baseVertexIndex = (edges.Length * 2) * i;
                int nextVertexIndex = (edges.Length * 2) * (i + 1);
                for (int e = 0; e < edges.Length; e++)
                {
                    int triIndex = i * triIndicesPerStep + e * 6;

                    triangles[triIndex + 0] = baseVertexIndex + e * 2;
                    triangles[triIndex + 1] = nextVertexIndex + e * 2;
                    triangles[triIndex + 2] = baseVertexIndex + e * 2 + 1;
                    triangles[triIndex + 3] = nextVertexIndex + e * 2;
                    triangles[triIndex + 4] = nextVertexIndex + e * 2 + 1;
                    triangles[triIndex + 5] = baseVertexIndex + e * 2 + 1;
                }
            }

            // build cap triangles
            int triCount = inputTriangles.Length / 3;

            // Top
            if (!srcMesh.separateTop)
            {
                int firstCapVertex   = extrudedVertexCount;
                int firstCapTriIndex = extrudedTriIndexCount;
                for (int i = 0; i < triCount; i++)
                {
                    triangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex;
                    triangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
                    triangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex;
                }
            }
            else
            {
                int firstCapVertex   = extrudedVertexCount;
                int firstCapTriIndex = extrudedTriIndexCount;
                extrudedMesh.topTriangles = new int[triangles.Length];
                for (int i = 0; i < triCount; i++)
                {
                    extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex;
                    extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
                    extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex;
                }
            }

            // Bottom
            if (!srcMesh.separateBottom)
            {
                int firstCapVertex   = extrudedVertexCount + inputVertices.Length;
                int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length;
                for (int i = 0; i < triCount; i++)
                {
                    triangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex;
                    triangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
                    triangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex;
                }
            }
            else
            {
                int firstCapVertex   = extrudedVertexCount + inputVertices.Length;
                int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length;
                extrudedMesh.bottomTriangles = new int[triangles.Length];
                for (int i = 0; i < triCount; i++)
                {
                    extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex;
                    extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
                    extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex;
                }
            }

            if (invertFaces)
            {
                for (int i = 0; i < triangles.Length / 3; i++)
                {
                    int temp = triangles[i * 3 + 0];
                    triangles[i * 3 + 0] = triangles[i * 3 + 1];
                    triangles[i * 3 + 1] = temp;
                }
            }

            extrudedMesh.vertices  = vertices;
            extrudedMesh.uv        = uvs;
            extrudedMesh.triangles = triangles;
        }