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); } }
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); }