public void AddBatch(ObiSkinConstraintBatch batch) { if (batch != null && batch.GetConstraintType() == GetConstraintType()) { batches.Add(batch); } }
public override void OnSolverStepBegin() { // apparently checking whether the actor is enabled or not doesn't take a despreciable amount of time. bool actorEnabled = this.enabled; // manually update animator (before particle physics): // TODO: update root bone here if animator is deactiavted. if (animatorController != null) { animatorController.UpdateAnimation(); } Vector4[] simulationPosition = { Vector4.zero }; // build local to simulation space transform: Matrix4x4 l2sTransform; if (Solver.simulateInLocalSpace) { l2sTransform = Solver.transform.worldToLocalMatrix * ActorLocalToWorldMatrix; } else { l2sTransform = ActorLocalToWorldMatrix; } //Matrix4x4 delta = Solver.transform.worldToLocalMatrix * Solver.LastTransform; ObiSkinConstraintBatch skinConstraints = SkinConstraints.GetFirstBatch(); // transform fixed particles: for (int i = 0; i < particleIndices.Length; i++) { Vector3 solverSpaceBone = l2sTransform.MultiplyPoint3x4(transform.InverseTransformPoint(bones[i].position)); if (!actorEnabled || invMasses[i] == 0) { simulationPosition[0] = solverSpaceBone; Oni.SetParticlePositions(solver.OniSolver, simulationPosition, 1, particleIndices[i]); } /*else if (Solver.simulateInLocalSpace){ * * Oni.GetParticlePositions(solver.OniSolver,simulationPosition,1,particleIndices[i]); * simulationPosition[0] = Vector3.Lerp(simulationPosition[0],delta.MultiplyPoint3x4(simulationPosition[0]),worldVelocityScale); * Oni.SetParticlePositions(solver.OniSolver,simulationPosition,1,particleIndices[i]); * * }*/ skinConstraints.skinPoints[i] = solverSpaceBone; } skinConstraints.PushDataToSolver(SkinConstraints); }
/** * Generates the particle based physical representation of the bone hierarcht. This is the initialization method for the actor * and should not be called directly once the object has been created. */ protected override IEnumerator Initialize() { initialized = false; initializing = true; RemoveFromSolver(null); // get a list of bones in preorder: bones = new List <Transform>(); foreach (Transform bone in EnumerateBonesBreadthFirst()) { bones.Add(bone); } parentIndices = new int[bones.Count]; active = new bool[bones.Count]; positions = new Vector3[bones.Count]; velocities = new Vector3[bones.Count]; invMasses = new float[bones.Count]; principalRadii = new Vector3[bones.Count]; phases = new int[bones.Count]; restPositions = new Vector4[bones.Count]; restOrientations = new Quaternion[bones.Count]; frozen = new bool[bones.Count]; DistanceConstraints.Clear(); ObiDistanceConstraintBatch distanceBatch = new ObiDistanceConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS); DistanceConstraints.AddBatch(distanceBatch); BendingConstraints.Clear(); ObiBendConstraintBatch bendingBatch = new ObiBendConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS); BendingConstraints.AddBatch(bendingBatch); SkinConstraints.Clear(); ObiSkinConstraintBatch skinBatch = new ObiSkinConstraintBatch(true, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS); SkinConstraints.AddBatch(skinBatch); for (int i = 0; i < bones.Count; ++i) { active[i] = true; invMasses[i] = 1.0f / DEFAULT_PARTICLE_MASS; positions[i] = transform.InverseTransformPoint(bones[i].position); restPositions[i] = positions[i]; restPositions[i][3] = 1; restOrientations[i] = Quaternion.identity; principalRadii[i] = new Vector3(particleRadius, particleRadius, particleRadius); frozen[i] = false; phases[i] = Oni.MakePhase(1, selfCollisions?Oni.ParticlePhase.SelfCollide:0); parentIndices[i] = -1; if (bones[i].parent != null) { parentIndices[i] = bones.IndexOf(bones[i].parent); } skinBatch.AddConstraint(i, positions[i], Vector3.up, 0.05f, 0, 0, 1); foreach (Transform child in bones[i]) { int childIndex = bones.IndexOf(child); if (childIndex >= 0) { // add distance constraint between the bone and its child. distanceBatch.AddConstraint(i, childIndex, Vector3.Distance(bones[i].position, child.position), 1, 1); if (parentIndices[i] >= 0) { Transform parent = bones[parentIndices[i]]; float[] bendRestPositions = new float[] { parent.position[0], parent.position[1], parent.position[2], child.position[0], child.position[1], child.position[2], bones[i].position[0], bones[i].position[1], bones[i].position[2] }; float restBend = Oni.BendingConstraintRest(bendRestPositions); // add bend constraint between the bone, its parent and its child. bendingBatch.AddConstraint(parentIndices[i], childIndex, i, restBend, 0, 0); } } } if (i % 10 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiBone: generating particles...", i / (float)bones.Count)); } } skinBatch.Cook(); initializing = false; initialized = true; }
private void DrawSkinProperty(int property) { Material mat = Resources.Load <Material>("EditorLines"); if (mat.SetPass(0) && constraints.GetBatches().Count > 0) { GL.PushMatrix(); GL.Begin(GL.LINES); ObiSkinConstraintBatch batch = ((ObiSkinConstraintBatch)constraints.GetBatches()[0]); Matrix4x4 s2wTransform = Matrix4x4.identity; if (constraints.InSolver) { s2wTransform = constraints.Actor.Solver.transform.localToWorldMatrix; } // get up to date constraint data: batch.PullDataFromSolver(constraints); foreach (int i in batch.ActiveConstraints) { int particleIndex = batch.skinIndices[i]; if (particleIndex >= 0 && particleIndex < ObiParticleActorEditor.selectionStatus.Length && ObiParticleActorEditor.selectionStatus[particleIndex] && ObiParticleActorEditor.IsParticleVisible(particleIndex)) { float radius = batch.skinRadiiBackstop[i * 3]; float collisionRadius = batch.skinRadiiBackstop[i * 3 + 1]; float backstop = batch.skinRadiiBackstop[i * 3 + 2]; Vector3 point = batch.GetSkinPosition(i); Vector3 normal = batch.GetSkinNormal(i); if (!constraints.InSolver) { point = constraints.Actor.ActorLocalToWorldMatrix.MultiplyPoint3x4(point); normal = constraints.Actor.ActorLocalToWorldMatrix.MultiplyVector(normal); } else if (constraints.Actor.Solver.simulateInLocalSpace) { point = s2wTransform.MultiplyPoint3x4(point); normal = s2wTransform.MultiplyVector(normal); } switch (property) { case ObiClothEditor.ClothParticleProperty.SkinRadius: GL.Color(Color.blue); GL.Vertex(point); GL.Color(Color.blue); GL.Vertex(point + normal * radius); break; case ObiClothEditor.ClothParticleProperty.SkinBackstop: GL.Color(Color.yellow); GL.Vertex(point); GL.Color(Color.yellow); GL.Vertex(point - normal * backstop); break; case ObiClothEditor.ClothParticleProperty.SkinBackstopRadius: GL.Color(Color.red); GL.Vertex(point - normal * backstop); GL.Color(Color.red); GL.Vertex(point - normal * (collisionRadius + backstop)); break; } } } GL.End(); GL.PopMatrix(); } }
public void RemoveBatch(ObiSkinConstraintBatch batch) { batches.Remove(batch); }
/** * Generates the particle based physical representation of the cloth mesh. This is the initialization method for the cloth object * and should not be called directly once the object has been created. */ protected override IEnumerator Initialize() { initialized = false; initializing = false; if (sharedTopology == null) { Debug.LogError("No ObiMeshTopology provided. Cannot initialize physical representation."); yield break; } else if (!sharedTopology.Initialized) { Debug.LogError("The provided ObiMeshTopology contains no data. Cannot initialize physical representation."); yield break; } initializing = true; RemoveFromSolver(null); GameObject.DestroyImmediate(topology); topology = GameObject.Instantiate(sharedTopology); active = new bool[topology.heVertices.Length]; positions = new Vector3[topology.heVertices.Length]; restPositions = new Vector4[topology.heVertices.Length]; velocities = new Vector3[topology.heVertices.Length]; invMasses = new float[topology.heVertices.Length]; principalRadii = new Vector3[topology.heVertices.Length]; phases = new int[topology.heVertices.Length]; areaContribution = new float[topology.heVertices.Length]; deformableTriangles = new int[topology.heFaces.Length * 3]; initialScaleMatrix.SetTRS(Vector3.zero, Quaternion.identity, transform.lossyScale); // Create a particle for each vertex: for (int i = 0; i < topology.heVertices.Length; i++) { Oni.Vertex vertex = topology.heVertices[i]; // Get the particle's area contribution. areaContribution[i] = 0; foreach (Oni.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 (Oni.HalfEdge edge in topology.GetNeighbourEdgesEnumerator(vertex)) { // vertices at each end of the edge: Vector3 v1 = initialScaleMatrix * topology.heVertices[topology.GetHalfEdgeStartVertex(edge)].position; Vector3 v2 = initialScaleMatrix * topology.heVertices[edge.endVertex].position; minEdgeLength = Mathf.Min(minEdgeLength, Vector3.Distance(v1, v2)); } active[i] = true; invMasses[i] = (skinnedMeshRenderer == null && areaContribution[i] > 0) ? (1.0f / (DEFAULT_PARTICLE_MASS * areaContribution[i])) : 0; positions[i] = initialScaleMatrix * vertex.position; restPositions[i] = positions[i]; restPositions[i][3] = 1; // activate rest position. principalRadii[i] = Vector3.one * minEdgeLength * 0.5f; phases[i] = Oni.MakePhase(1, selfCollisions?Oni.ParticlePhase.SelfCollide:0); if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating particles...", i / (float)topology.heVertices.Length)); } } // Generate deformable triangles: for (int i = 0; i < topology.heFaces.Length; i++) { Oni.Face face = topology.heFaces[i]; Oni.HalfEdge e1 = topology.heHalfEdges[face.halfEdge]; Oni.HalfEdge e2 = topology.heHalfEdges[e1.nextHalfEdge]; Oni.HalfEdge e3 = topology.heHalfEdges[e2.nextHalfEdge]; deformableTriangles[i * 3] = e1.endVertex; deformableTriangles[i * 3 + 1] = e2.endVertex; deformableTriangles[i * 3 + 2] = e3.endVertex; if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating deformable geometry...", i / (float)topology.heFaces.Length)); } } List <ObiMeshTopology.HEEdge> edges = topology.GetEdgeList(); DistanceConstraints.Clear(); ObiDistanceConstraintBatch distanceBatch = new ObiDistanceConstraintBatch(true, false); DistanceConstraints.AddBatch(distanceBatch); // Create distance springs: for (int i = 0; i < edges.Count; i++) { Oni.HalfEdge hedge = topology.heHalfEdges[edges[i].halfEdgeIndex]; Oni.Vertex startVertex = topology.heVertices[topology.GetHalfEdgeStartVertex(hedge)]; Oni.Vertex endVertex = topology.heVertices[hedge.endVertex]; distanceBatch.AddConstraint(topology.GetHalfEdgeStartVertex(hedge), hedge.endVertex, Vector3.Distance(initialScaleMatrix * startVertex.position, initialScaleMatrix * endVertex.position), 1, 1); if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating structural constraints...", i / (float)topology.heHalfEdges.Length)); } } // Cook distance constraints, for better cache and SIMD use: distanceBatch.Cook(); // Create aerodynamic constraints: AerodynamicConstraints.Clear(); ObiAerodynamicConstraintBatch aeroBatch = new ObiAerodynamicConstraintBatch(false, false); AerodynamicConstraints.AddBatch(aeroBatch); for (int i = 0; i < topology.heVertices.Length; i++) { aeroBatch.AddConstraint(i, areaContribution[i], AerodynamicConstraints.dragCoefficient, AerodynamicConstraints.liftCoefficient); if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating aerodynamic constraints...", i / (float)topology.heFaces.Length)); } } //Create skin constraints (if needed) if (skinnedMeshRenderer != null) { SkinConstraints.Clear(); ObiSkinConstraintBatch skinBatch = new ObiSkinConstraintBatch(true, false); SkinConstraints.AddBatch(skinBatch); for (int i = 0; i < topology.heVertices.Length; ++i) { skinBatch.AddConstraint(i, initialScaleMatrix * topology.heVertices[i].position, Vector3.up, 0.05f, 0.1f, 0, 1); if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating skin constraints...", i / (float)topology.heVertices.Length)); } } for (int i = 0; i < topology.normals.Length; ++i) { skinBatch.skinNormals[topology.visualMap[i]] = topology.normals[i]; } skinBatch.Cook(); } //Create pressure constraints if the mesh is closed: VolumeConstraints.Clear(); if (topology.IsClosed) { ObiVolumeConstraintBatch volumeBatch = new ObiVolumeConstraintBatch(false, false); VolumeConstraints.AddBatch(volumeBatch); float avgInitialScale = (initialScaleMatrix.m00 + initialScaleMatrix.m11 + initialScaleMatrix.m22) * 0.33f; int[] triangleIndices = new int[topology.heFaces.Length * 3]; for (int i = 0; i < topology.heFaces.Length; i++) { Oni.Face face = topology.heFaces[i]; Oni.HalfEdge e1 = topology.heHalfEdges[face.halfEdge]; Oni.HalfEdge e2 = topology.heHalfEdges[e1.nextHalfEdge]; Oni.HalfEdge e3 = topology.heHalfEdges[e2.nextHalfEdge]; triangleIndices[i * 3] = e1.endVertex; triangleIndices[i * 3 + 1] = e2.endVertex; triangleIndices[i * 3 + 2] = e3.endVertex; if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating volume constraints...", i / (float)topology.heFaces.Length)); } } volumeBatch.AddConstraint(triangleIndices, topology.MeshVolume * avgInitialScale, 1, 1); } //Create bending constraints: BendingConstraints.Clear(); ObiBendConstraintBatch bendBatch = new ObiBendConstraintBatch(true, false); BendingConstraints.AddBatch(bendBatch); Dictionary <int, int> cons = new Dictionary <int, int>(); for (int i = 0; i < topology.heVertices.Length; i++) { Oni.Vertex vertex = topology.heVertices[i]; foreach (Oni.Vertex n1 in topology.GetNeighbourVerticesEnumerator(vertex)) { float cosBest = 0; Oni.Vertex vBest = n1; foreach (Oni.Vertex n2 in topology.GetNeighbourVerticesEnumerator(vertex)) { float cos = Vector3.Dot((n1.position - vertex.position).normalized, (n2.position - vertex.position).normalized); if (cos < cosBest) { cosBest = cos; vBest = n2; } } if (!cons.ContainsKey(vBest.index) || cons[vBest.index] != n1.index) { cons[n1.index] = vBest.index; Vector3 n1Pos = initialScaleMatrix * n1.position; Vector3 bestPos = initialScaleMatrix * vBest.position; Vector3 vertexPos = initialScaleMatrix * vertex.position; float[] bendRestPositions = new float[] { n1Pos[0], n1Pos[1], n1Pos[2], bestPos[0], bestPos[1], bestPos[2], vertexPos[0], vertexPos[1], vertexPos[2] }; float restBend = Oni.BendingConstraintRest(bendRestPositions); bendBatch.AddConstraint(n1.index, vBest.index, vertex.index, restBend, 0, 1); } } if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiCloth: adding bend constraints...", i / (float)sharedTopology.heVertices.Length)); } } bendBatch.Cook(); // Initialize tether constraints: TetherConstraints.Clear(); // Initialize pin constraints: PinConstraints.Clear(); ObiPinConstraintBatch pinBatch = new ObiPinConstraintBatch(false, false); PinConstraints.AddBatch(pinBatch); initializing = false; initialized = true; if (skinnedMeshRenderer == null) { InitializeWithRegularMesh(); } else { InitializeWithSkinnedMesh(); } }
public void OnSceneGUI() { if (Event.current.type != EventType.Repaint || !ObiParticleActorEditor.editMode) { return; } // Get the particle actor editor to retrieve selected particles: ObiParticleActorEditor[] editors = (ObiParticleActorEditor[])Resources.FindObjectsOfTypeAll(typeof(ObiParticleActorEditor)); // If there's any particle actor editor active, we can show pin constraints: if (editors.Length > 0 && (editors[0].currentProperty == ObiClothEditor.ClothParticleProperty.SkinRadius || editors[0].currentProperty == ObiClothEditor.ClothParticleProperty.SkinBackstopRadius || editors[0].currentProperty == ObiClothEditor.ClothParticleProperty.SkinBackstop)) { Material mat = ObiEditorUtils.GetRequiredEditorResource("Obi/EditorLines.mat") as Material; if (mat.SetPass(0) && constraints.GetBatches().Count > 0) { GL.PushMatrix(); GL.Begin(GL.LINES); ObiSkinConstraintBatch batch = ((ObiSkinConstraintBatch)constraints.GetBatches()[0]); Matrix4x4 s2wTransform = constraints.Actor.Solver.transform.localToWorldMatrix; // get up to date constraint data: batch.PullDataFromSolver(constraints); foreach (int i in batch.ActiveConstraints) { int particleIndex = batch.skinIndices[i]; bool[] stat = ObiParticleActorEditor.selectionStatus; if (particleIndex >= 0 && particleIndex < ObiParticleActorEditor.selectionStatus.Length && ObiParticleActorEditor.selectionStatus[particleIndex]) { float radius = batch.skinRadiiBackstop[i * 3]; float collisionRadius = batch.skinRadiiBackstop[i * 3 + 1]; float backstop = batch.skinRadiiBackstop[i * 3 + 2]; Vector3 point = batch.GetSkinPosition(i); Vector3 normal = batch.GetSkinNormal(i); if (!constraints.InSolver) { point = constraints.transform.TransformPoint(point); normal = constraints.transform.TransformDirection(normal); } else if (constraints.Actor.Solver.simulateInLocalSpace) { point = s2wTransform.MultiplyPoint3x4(point); normal = s2wTransform.MultiplyVector(normal); } // Detailed visual feedback only if few particles are selected, to avoid clutter. if (ObiParticleActorEditor.SelectedParticleCount < 5) { Handles.color = new Color(0, 0, 1, 0.2f); Handles.SphereCap(0, point, Quaternion.identity, radius * 2); Handles.color = new Color(1, 0, 0, 0.2f); Handles.SphereCap(0, point - normal * (collisionRadius + backstop), Quaternion.identity, collisionRadius * 2); // If more than 4 particles are selected, use lines. } else { GL.Color(Color.red); GL.Vertex(point); GL.Color(Color.red); GL.Vertex(point - normal * backstop); GL.Color(Color.blue); GL.Vertex(point - normal * backstop); GL.Color(Color.blue); GL.Vertex(point + normal * radius); } } } GL.End(); GL.PopMatrix(); } } }