/** * Tears a cloth distance constraint, affecting both the physical representation of the cloth and its mesh. */ public void Tear(int constraintIndex) { if (topology == null) { return; } // don't allow splitting if there are no free particles left in the pool. if (usedParticles >= sharedMesh.vertexCount + pooledVertices) { return; } // get involved constraint batches: ObiDistanceConstraintBatch distanceBatch = (ObiDistanceConstraintBatch)DistanceConstraints.GetBatches()[0]; ObiBendConstraintBatch bendingBatch = (ObiBendConstraintBatch)BendingConstraints.GetBatches()[0]; // get particle indices at both ends of the constraint: int splitIndex = distanceBatch.springIndices[constraintIndex * 2]; int intactIndex = distanceBatch.springIndices[constraintIndex * 2 + 1]; // we will split the particle with higher mass, so swap them if needed. if (invMasses[splitIndex] > invMasses[intactIndex]) { int aux = splitIndex; splitIndex = intactIndex; intactIndex = aux; } // Calculate the splitting plane in local space: Vector3 v1 = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[splitIndex]]); Vector3 v2 = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[intactIndex]]); Vector3 normal = (v2 - v1).normalized; int numUpdatedHalfEdges = maxVertexValency; int[] updatedHalfEdges = new int[numUpdatedHalfEdges]; // Try to split the vertex at that particle. // If we cannot not split the higher mass particle, try the other one. If that fails too, we cannot tear this edge. if (invMasses[splitIndex] == 0 || !Oni.TearDeformableMeshAtVertex(deformableMesh, splitIndex, ref v1, ref normal, updatedHalfEdges, ref numUpdatedHalfEdges)) { // Try to split the other particle: int aux = splitIndex; splitIndex = intactIndex; intactIndex = aux; v1 = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[splitIndex]]); v2 = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[intactIndex]]); normal = (v2 - v1).normalized; if (invMasses[splitIndex] == 0 || !Oni.TearDeformableMeshAtVertex(deformableMesh, splitIndex, ref v1, ref normal, updatedHalfEdges, ref numUpdatedHalfEdges)) { return; } } // identify weak points around the cut: int weakPt1 = -1; int weakPt2 = -1; float weakestValue = float.MaxValue; float secondWeakestValue = float.MaxValue; foreach (Oni.Vertex v in topology.GetNeighbourVerticesEnumerator(topology.heVertices[splitIndex])) { Vector3 neighbour = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[v.index]]); float weakness = Mathf.Abs(Vector3.Dot(normal, (neighbour - v1).normalized)); if (weakness < weakestValue) { secondWeakestValue = weakestValue; weakestValue = weakness; weakPt2 = weakPt1; weakPt1 = v.index; } else if (weakness < secondWeakestValue) { secondWeakestValue = weakness; weakPt2 = v.index; } } // reduce tear resistance at the weak spots of the cut, to encourage coherent tear formation. if (weakPt1 >= 0) { tearResistance[weakPt1] *= 1 - tearDebilitation; } if (weakPt2 >= 0) { tearResistance[weakPt2] *= 1 - tearDebilitation; } topology.UpdateVertexCount(); // halve the mass and radius of the original particle: invMasses[splitIndex] *= 2; solidRadii[splitIndex] *= 0.5f; // copy the new particle data in the actor and solver arrays: positions[usedParticles] = positions[splitIndex]; velocities[usedParticles] = velocities[splitIndex]; active[usedParticles] = active[splitIndex]; invMasses[usedParticles] = invMasses[splitIndex]; solidRadii[usedParticles] = solidRadii[splitIndex]; phases[usedParticles] = phases[splitIndex]; areaContribution[usedParticles] = areaContribution[splitIndex]; tearResistance[usedParticles] = tearResistance[splitIndex]; restPositions[usedParticles] = positions[splitIndex]; restPositions[usedParticles][3] = 0; // activate rest position. solver.activeParticles.Add(particleIndices[usedParticles]); // update solver particle data: Vector4[] velocity = { Vector4.zero }; Oni.GetParticleVelocities(solver.OniSolver, velocity, 1, particleIndices[splitIndex]); Oni.SetParticleVelocities(solver.OniSolver, velocity, 1, particleIndices[usedParticles]); Vector4[] position = { Vector4.zero }; Oni.GetParticlePositions(solver.OniSolver, position, 1, particleIndices[splitIndex]); Oni.SetParticlePositions(solver.OniSolver, position, 1, particleIndices[usedParticles]); Oni.SetParticleInverseMasses(solver.OniSolver, new float[] { invMasses[splitIndex] }, 1, particleIndices[usedParticles]); Oni.SetParticleSolidRadii(solver.OniSolver, new float[] { solidRadii[splitIndex] }, 1, particleIndices[usedParticles]); Oni.SetParticlePhases(solver.OniSolver, new int[] { phases[splitIndex] }, 1, particleIndices[usedParticles]); usedParticles++; // update distance constraints: for (int i = 0; i < numUpdatedHalfEdges; ++i) { int halfEdgeIndex = updatedHalfEdges[i]; Oni.HalfEdge e = topology.heHalfEdges[halfEdgeIndex]; // find start and end vertex indices for this edge: int startVertex = topology.GetHalfEdgeStartVertex(topology.heHalfEdges[halfEdgeIndex]); int endVertex = topology.heHalfEdges[halfEdgeIndex].endVertex; if (distanceConstraintMap[halfEdgeIndex] > -1) // update existing edge { distanceBatch.springIndices[distanceConstraintMap[halfEdgeIndex] * 2] = startVertex; distanceBatch.springIndices[distanceConstraintMap[halfEdgeIndex] * 2 + 1] = endVertex; } else if (topology.IsSplit(halfEdgeIndex)) // new edge { int pairConstraintIndex = distanceConstraintMap[topology.heHalfEdges[halfEdgeIndex].pair]; // update constraint-edge map: distanceConstraintMap[halfEdgeIndex] = distanceBatch.restLengths.Count; // add the new constraint: distanceBatch.AddConstraint(startVertex, endVertex, distanceBatch.restLengths[pairConstraintIndex], distanceBatch.stiffnesses[pairConstraintIndex].x, distanceBatch.stiffnesses[pairConstraintIndex].y); } // update deformable triangles: if (e.indexInFace > -1) { deformableTriangles[e.face * 3 + e.indexInFace] = e.endVertex; } } if (splitIndex < bendConstraintOffsets.Length - 1) { // deactivate bend constraints that contain the split vertex... // ...at the center: for (int i = bendConstraintOffsets[splitIndex]; i < bendConstraintOffsets[splitIndex + 1]; ++i) { bendingBatch.DeactivateConstraint(i); } // ...at one end: foreach (Oni.Vertex v in topology.GetNeighbourVerticesEnumerator(topology.heVertices[splitIndex])) { if (v.index < bendConstraintOffsets.Length - 1) { for (int i = bendConstraintOffsets[v.index]; i < bendConstraintOffsets[v.index + 1]; ++i) { if (bendingBatch.bendingIndices[i * 3] == splitIndex || bendingBatch.bendingIndices[i * 3 + 1] == splitIndex) { bendingBatch.DeactivateConstraint(i); } } } } } // Create new aerodynamic constraint: /*aerodynamicConstraints.AddConstraint(true,newParticle[0], * Vector3.up, * aerodynamicConstraints.windVector, * areaContribution[newParticle[0]], * aerodynamicConstraints.dragCoefficient, * aerodynamicConstraints.liftCoefficient);*/ }
public void Tear(int constraintIndex) { // don't allow splitting if there are no free particles left in the pool. if (usedParticles >= totalParticles) { return; } // get involved constraint batches: ObiDistanceConstraintBatch distanceBatch = (ObiDistanceConstraintBatch)DistanceConstraints.GetFirstBatch(); ObiBendConstraintBatch bendingBatch = (ObiBendConstraintBatch)BendingConstraints.GetFirstBatch(); // get particle indices at both ends of the constraint: int splitIndex = distanceBatch.springIndices[constraintIndex * 2]; int intactIndex = distanceBatch.springIndices[constraintIndex * 2 + 1]; // see if the rope is continuous at the split index and the intact index: bool continuousAtSplit = (constraintIndex < distanceBatch.ConstraintCount - 1 && distanceBatch.springIndices[(constraintIndex + 1) * 2] == splitIndex) || (constraintIndex > 0 && distanceBatch.springIndices[(constraintIndex - 1) * 2 + 1] == splitIndex); bool continuousAtIntact = (constraintIndex < distanceBatch.ConstraintCount - 1 && distanceBatch.springIndices[(constraintIndex + 1) * 2] == intactIndex) || (constraintIndex > 0 && distanceBatch.springIndices[(constraintIndex - 1) * 2 + 1] == intactIndex); // we will split the particle with higher mass, so swap them if needed (and possible). Also make sure that the rope hasnt been cut there yet: if ((invMasses[splitIndex] > invMasses[intactIndex] || invMasses[splitIndex] == 0) && continuousAtIntact) { int aux = splitIndex; splitIndex = intactIndex; intactIndex = aux; } // see if we are able to proceed with the cut: if (invMasses[splitIndex] == 0 || !continuousAtSplit) { return; } // halve the mass of the teared particle: invMasses[splitIndex] *= 2; // copy the new particle data in the actor and solver arrays: positions[usedParticles] = positions[splitIndex]; velocities[usedParticles] = velocities[splitIndex]; active[usedParticles] = active[splitIndex]; invMasses[usedParticles] = invMasses[splitIndex]; principalRadii[usedParticles] = principalRadii[splitIndex]; phases[usedParticles] = phases[splitIndex]; if (colors != null && colors.Length > 0) { colors[usedParticles] = colors[splitIndex]; } tearResistance[usedParticles] = tearResistance[splitIndex]; restPositions[usedParticles] = positions[splitIndex]; restPositions[usedParticles][3] = 1; // activate rest position. // update solver particle data: solver.velocities[particleIndices[usedParticles]] = solver.velocities[particleIndices[splitIndex]]; solver.startPositions[particleIndices[usedParticles]] = solver.positions [particleIndices[usedParticles]] = solver.positions [particleIndices[splitIndex]]; solver.invMasses [particleIndices[usedParticles]] = solver.invMasses [particleIndices[splitIndex]] = invMasses[splitIndex]; solver.principalRadii[particleIndices[usedParticles]] = solver.principalRadii[particleIndices[splitIndex]] = principalRadii[splitIndex]; solver.phases [particleIndices[usedParticles]] = solver.phases [particleIndices[splitIndex]]; // Update bending constraints: for (int i = 0; i < bendingBatch.ConstraintCount; ++i) { // disable the bending constraint centered at the split particle: if (bendingBatch.bendingIndices[i * 3 + 2] == splitIndex) { bendingBatch.DeactivateConstraint(i); } // update the one that bridges the cut: else if (!DoesBendConstraintSpanDistanceConstraint(distanceBatch, bendingBatch, constraintIndex, i)) { // if the bend constraint does not involve the split distance constraint, // update the end that references the split vertex: if (bendingBatch.bendingIndices[i * 3] == splitIndex) { bendingBatch.bendingIndices[i * 3] = usedParticles; } else if (bendingBatch.bendingIndices[i * 3 + 1] == splitIndex) { bendingBatch.bendingIndices[i * 3 + 1] = usedParticles; } } } // Update distance constraints at both ends of the cut: if (constraintIndex < distanceBatch.ConstraintCount - 1) { if (distanceBatch.springIndices[(constraintIndex + 1) * 2] == splitIndex) { distanceBatch.springIndices[(constraintIndex + 1) * 2] = usedParticles; } if (distanceBatch.springIndices[(constraintIndex + 1) * 2 + 1] == splitIndex) { distanceBatch.springIndices[(constraintIndex + 1) * 2 + 1] = usedParticles; } } if (constraintIndex > 0) { if (distanceBatch.springIndices[(constraintIndex - 1) * 2] == splitIndex) { distanceBatch.springIndices[(constraintIndex - 1) * 2] = usedParticles; } if (distanceBatch.springIndices[(constraintIndex - 1) * 2 + 1] == splitIndex) { distanceBatch.springIndices[(constraintIndex - 1) * 2 + 1] = usedParticles; } } usedParticles++; pooledParticles--; }