/** * Recalculates the shape used as reference for transform position/orientation when there are no fixed particles. * Should be called manually when changing the amount of fixed particles and/or active particles. */ public void RecalculateCenterShape() { centerShape = -1; for (int i = 0; i < invMasses.Length; ++i) { if (invMasses[i] <= 0) { return; } } ObiShapeMatchingConstraintBatch batch = ShapeMatchingConstraints.GetFirstBatch(); IList <int> activeShapes = batch.ActiveConstraints; float minDistance = float.MaxValue; for (int i = 0; i < activeShapes.Count; ++i) { float dist = positions[batch.GetParticleIndex(activeShapes[i])].sqrMagnitude; if (dist < minDistance) { minDistance = dist; centerShape = i; } } }
/** * Deactivates all fixed particles that are attached to fixed particles only, and all the constraints * affecting them. */ public IEnumerator Optimize() { ObiShapeMatchingConstraintBatch shapeBatch = ShapeMatchingConstraints.GetFirstBatch(); // Iterate over all particles and get those fixed ones that are only linked to fixed particles. for (int i = 0; i < shapeBatch.ConstraintCount; ++i) { if (invMasses[shapeBatch.GetParticleIndex(i)] > 0) { continue; } active[i] = false; for (int j = shapeBatch.firstIndex[i]; j < shapeBatch.firstIndex[i] + shapeBatch.numIndices[i]; ++j) { // If at least one neighbour particle is not fixed, then the particle we are considering for optimization should not be removed. if (invMasses[shapeBatch.shapeIndices[j]] > 0) { active[i] = true; break; } } // Deactivate all constraints involving this inactive particle: if (!active[i]) { // for each constraint type: foreach (ObiBatchedConstraints constraint in constraints) { // for each constraint batch (usually only one) if (constraint != null) { foreach (ObiConstraintBatch batch in constraint.GetBatches()) { // deactivate constraints that affect the particle: List <int> affectedConstraints = batch.GetConstraintsInvolvingParticle(i); foreach (int j in affectedConstraints) { batch.DeactivateConstraint(j); } batch.SetActiveConstraints(); } } } } yield return(new CoroutineJob.ProgressInfo("ObiSoftbody: optimizing constraints...", i / (float)shapeBatch.ConstraintCount)); } PushDataToSolver(ParticleData.ACTIVE_STATUS); // Perform skinning: /*IEnumerator bind = BindSkin(); * while(bind.MoveNext()){ * yield return bind.Current; * }*/ }
private void UpdateBones(object sender, EventArgs e) { if (bones.Length > 0 && source.InSolver) { ObiShapeMatchingConstraintBatch batch = source.ShapeMatchingConstraints.GetFirstBatch(); IList <int> activeShapes = batch.ActiveConstraints; int particleIndex; for (int i = 0; i < activeShapes.Count; ++i) { particleIndex = source.particleIndices[batch.GetParticleIndex(activeShapes[i])]; bones[i].position = source.Solver.renderablePositions [particleIndex]; bones[i].rotation = source.Solver.renderableOrientations[particleIndex]; } } }
public IEnumerator BindSkin() { bound = false; if (source == null || !source.Initialized || target.sharedMesh == null) { yield break; } ObiShapeMatchingConstraintBatch batch = source.ShapeMatchingConstraints.GetFirstBatch(); IList <int> activeShapes = batch.ActiveConstraints; Vector3[] clusterCenters = new Vector3[activeShapes.Count]; Quaternion[] clusterOrientations = new Quaternion[activeShapes.Count]; Vector3[] vertices = target.sharedMesh.vertices; m_bindposes = new Matrix4x4[activeShapes.Count]; m_boneWeights = new BoneWeight[vertices.Length]; // Calculate softbody local to world matrix, and target to world matrix. Matrix4x4 source2w = source.transform.localToWorldMatrix * source.InitialScaleMatrix.inverse; Matrix4x4 target2w = transform.localToWorldMatrix; // Create bind pose matrices, one per shape matching cluster: for (int i = 0; i < clusterCenters.Length; ++i) { // world space bone center/orientation: clusterCenters[i] = source2w.MultiplyPoint3x4(source.restPositions[batch.GetParticleIndex(activeShapes[i])]); clusterOrientations[i] = source2w.rotation * source.restOrientations[batch.GetParticleIndex(activeShapes[i])]; // world space bone transform * object local to world. m_bindposes[i] = Matrix4x4.TRS(clusterCenters[i], clusterOrientations[i], Vector3.one).inverse *target2w; yield return(new CoroutineJob.ProgressInfo("ObiSoftbody: calculating bind poses...", i / (float)clusterCenters.Length)); } // Calculate skin weights and bone indices: for (int j = 0; j < m_boneWeights.Length; ++j) { // transform each vertex to world space: m_boneWeights[j] = CalculateBoneWeights(target2w.MultiplyPoint3x4(vertices[j]), clusterCenters); yield return(new CoroutineJob.ProgressInfo("ObiSoftbody: calculating bone weights...", j / (float)m_boneWeights.Length)); } bound = true; }
private void AppendBoneTransforms(ObiShapeMatchingConstraintBatch batch) { IList <int> activeShapes = batch.ActiveConstraints; // Calculate softbody local to world matrix, and target to world matrix. Matrix4x4 source2w = source.transform.localToWorldMatrix * source.InitialScaleMatrix.inverse; Quaternion source2wRot = source2w.rotation; bones = new Transform[activeShapes.Count]; for (int i = 0; i < bones.Length; ++i) { GameObject bone = new GameObject("Cluster" + i); bone.transform.parent = transform; bone.transform.position = source2w.MultiplyPoint3x4(source.restPositions[batch.GetParticleIndex(activeShapes[i])]); bone.transform.rotation = source2wRot * source.restOrientations[batch.GetParticleIndex(activeShapes[i])]; bone.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy; bones[i] = bone.transform; } List <Transform> originalBones = new List <Transform>(target.bones); originalBones.AddRange(bones); target.bones = originalBones.ToArray(); }