protected override IEnumerator Initialize()
        {
            if (inputMesh == null || !inputMesh.isReadable)
            {
                // TODO: return an error in the coroutine.
                Debug.LogError("The input mesh is null, or not readable.");
                yield break;
            }

            ClearParticleGroups();

            topology           = new HalfEdgeMesh();
            topology.inputMesh = inputMesh;
            topology.Generate();

            positions      = new Vector3[topology.vertices.Count];
            restPositions  = new Vector4[topology.vertices.Count];
            velocities     = new Vector3[topology.vertices.Count];
            invMasses      = new float[topology.vertices.Count];
            principalRadii = new Vector3[topology.vertices.Count];
            phases         = new int[topology.vertices.Count];
            colors         = new Color[topology.vertices.Count];

            areaContribution = new float[topology.vertices.Count];

            // Create a particle for each vertex:
            m_ActiveParticleCount = topology.vertices.Count;
            for (int i = 0; i < topology.vertices.Count; i++)
            {
                HalfEdgeMesh.Vertex vertex = topology.vertices[i];

                // Get the particle's area contribution.
                areaContribution[i] = 0;
                foreach (HalfEdgeMesh.Face face in topology.GetNeighbourFacesEnumerator(vertex))
                {
                    areaContribution[i] += topology.GetFaceArea(face) / 3;
                }

                // Get the shortest neighbour edge, particle radius will be half of its length.
                float minEdgeLength = Single.MaxValue;
                foreach (HalfEdgeMesh.HalfEdge edge in topology.GetNeighbourEdgesEnumerator(vertex))
                {
                    // vertices at each end of the edge:
                    Vector3 v1 = Vector3.Scale(scale, topology.vertices[topology.GetHalfEdgeStartVertex(edge)].position);
                    Vector3 v2 = Vector3.Scale(scale, topology.vertices[edge.endVertex].position);

                    minEdgeLength = Mathf.Min(minEdgeLength, Vector3.Distance(v1, v2));
                }

                invMasses[i]        = (/*skinnedMeshRenderer == null &&*/ areaContribution[i] > 0) ? (1.0f / (DEFAULT_PARTICLE_MASS * areaContribution[i])) : 0;
                positions[i]        = Vector3.Scale(scale, vertex.position);
                restPositions[i]    = positions[i];
                restPositions[i][3] = 1; // activate rest position.
                principalRadii[i]   = Vector3.one * minEdgeLength * 0.5f;
                phases[i]           = ObiUtils.MakePhase(1, /*selfCollisions ? Oni.ParticlePhase.SelfCollide : 0*/ 0);
                colors[i]           = Color.white;

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating particles...", i / (float)topology.vertices.Count));
                }
            }

            // Deformable triangles:
            IEnumerator dt = GenerateDeformableTriangles();

            while (dt.MoveNext())
            {
                yield return(dt.Current);
            }

            // Create distance constraints:
            IEnumerator dc = CreateDistanceConstraints();

            while (dc.MoveNext())
            {
                yield return(dc.Current);
            }

            // Create aerodynamic constraints:
            IEnumerator ac = CreateAerodynamicConstraints();

            while (ac.MoveNext())
            {
                yield return(ac.Current);
            }

            // Create bending constraints:
            IEnumerator bc = CreateBendingConstraints();

            while (bc.MoveNext())
            {
                yield return(bc.Current);
            }

            // Create volume constraints:
            IEnumerator vc = CreateVolumeConstraints();

            while (vc.MoveNext())
            {
                yield return(vc.Current);
            }
        }
Example #2
0
        private bool SplitTopologyAtVertex(int vertexIndex,
                                           Plane plane,
                                           List <HalfEdgeMesh.Face> updatedFaces,
                                           HashSet <int> updatedEdgeIndices)
        {
            if (vertexIndex < 0 || vertexIndex >= topology.vertices.Count)
            {
                return(false);
            }

            updatedFaces.Clear();
            updatedEdgeIndices.Clear();
            HalfEdgeMesh.Vertex vertex = topology.vertices[vertexIndex];

            // classify adjacent faces depending on which side of the plane they're at:
            var otherSide = new List <HalfEdgeMesh.Face>();

            ClassifyFaces(vertex, plane, updatedFaces, otherSide);

            // guard against pathological case in which all particles are in one side of the plane:
            if (otherSide.Count == 0 || updatedFaces.Count == 0)
            {
                return(false);
            }

            // create a new vertex:
            var newVertex = new HalfEdgeMesh.Vertex();

            newVertex.position = vertex.position;
            newVertex.index    = topology.vertices.Count;
            newVertex.halfEdge = vertex.halfEdge;

            // rearrange edges at the updated side:
            foreach (HalfEdgeMesh.Face face in updatedFaces)
            {
                // find half edges that start and end at the split vertex:
                HalfEdgeMesh.HalfEdge e1 = topology.halfEdges[face.halfEdge];
                HalfEdgeMesh.HalfEdge e2 = topology.halfEdges[e1.nextHalfEdge];
                HalfEdgeMesh.HalfEdge e3 = topology.halfEdges[e2.nextHalfEdge];

                var in_  = e1;
                var out_ = e2;

                if (e1.endVertex == vertex.index)
                {
                    in_ = e1;
                }
                else if (topology.GetHalfEdgeStartVertex(e1) == vertex.index)
                {
                    out_ = e1;
                }

                if (e2.endVertex == vertex.index)
                {
                    in_ = e2;
                }
                else if (topology.GetHalfEdgeStartVertex(e2) == vertex.index)
                {
                    out_ = e2;
                }

                if (e3.endVertex == vertex.index)
                {
                    in_ = e3;
                }
                else if (topology.GetHalfEdgeStartVertex(e3) == vertex.index)
                {
                    out_ = e3;
                }

                // stitch edges to new vertex:
                in_.endVertex = newVertex.index;
                topology.halfEdges[in_.index] = in_;
                newVertex.halfEdge            = out_.index;

                // store edges to be updated:
                updatedEdgeIndices.UnionWith(new int[]
                {
                    in_.index, in_.pair, out_.index, out_.pair
                });
            }

            // add new vertex:
            topology.vertices.Add(newVertex);
            topology.restNormals.Add(topology.restNormals[vertexIndex]);
            topology.restOrientations.Add(topology.restOrientations[vertexIndex]);

            //TODO: update mesh info. (mesh cannot be closed now)

            return(true);
        }