/** * 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; } } }
public void AddBatch(ObiShapeMatchingConstraintBatch batch) { if (batch != null && batch.GetConstraintType() == GetConstraintType()) { batches.Add(batch); } }
/** * 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 CopyBoneTransforms(ObiShapeMatchingConstraintBatch batch) { IList <int> activeShapes = batch.ActiveConstraints; bones = new Transform[activeShapes.Count]; for (int i = target.bones.Length - bones.Length, j = 0; i < target.bones.Length && j < bones.Length; ++i, ++j) { bones[j] = target.bones[i]; } }
public override void OnSolverStepEnd(float deltaTime) { ObiShapeMatchingConstraintBatch batch = ShapeMatchingConstraints.GetFirstBatch(); IList <int> shapes = batch.ActiveConstraints; if (Application.isPlaying && isActiveAndEnabled && centerShape > -1 && centerShape < shapes.Count) { int shape = shapes[centerShape]; transform.position = (Vector3)batch.coms[shape] - batch.orientations[shape] * batch.restComs[shape]; transform.rotation = batch.orientations[shape]; } }
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(); }
public void RemoveBatch(ObiShapeMatchingConstraintBatch batch) { batches.Remove(batch); }
protected override IEnumerator Initialize() { initialized = false; initializing = false; RemoveFromSolver(null); if (inputMesh == null) { Debug.LogError("No input mesh provided. Cannot initialize physical representation."); yield break; } initializing = true; initialScaleMatrix.SetTRS(Vector3.zero, Quaternion.identity, transform.lossyScale); Vector3[] vertices = inputMesh.vertices; Vector3[] normals = inputMesh.normals; List <Vector3> particles = new List <Vector3>(); // Add particles to every vertex, as long as they are not too close to the already added ones: for (int i = 0; i < vertices.Length; ++i) { bool intersects = false; Vector3 vertexScaled = initialScaleMatrix * vertices[i]; for (int j = 0; j < particles.Count; ++j) { if (Vector3.Distance(vertexScaled, particles[j]) < particleRadius * 2 * (1 - particleOverlap)) { intersects = true; break; } } if (intersects) { continue; } particles.Add(vertexScaled); } active = new bool[particles.Count]; positions = new Vector3[particles.Count]; orientations = new Quaternion[particles.Count]; restPositions = new Vector4[particles.Count]; restOrientations = new Quaternion[particles.Count]; velocities = new Vector3[particles.Count]; angularVelocities = new Vector3[particles.Count]; invMasses = new float[particles.Count]; invRotationalMasses = new float[particles.Count]; principalRadii = new Vector3[particles.Count]; phases = new int[particles.Count]; for (int i = 0; i < particles.Count; ++i) { // Perform ellipsoid fitting: Vector3 avgNormal = Vector3.zero; List <Vector3> neighbourVertices = new List <Vector3>(); for (int j = 0; j < vertices.Length; ++j) { Vector3 vertexScaled = initialScaleMatrix * vertices[j]; if (Vector3.Distance(vertexScaled, particles[i]) < anisotropyNeighborhood) { neighbourVertices.Add(vertexScaled); avgNormal += normals[j]; } } if (neighbourVertices.Count > 0) { avgNormal /= neighbourVertices.Count; } Vector3 centroid = particles[i]; Quaternion orientation = Quaternion.identity; Vector3 principalValues = Vector3.one; Oni.GetPointCloudAnisotropy(neighbourVertices.ToArray(), neighbourVertices.Count, maxAnisotropy, particleRadius, ref avgNormal, ref centroid, ref orientation, ref principalValues); active[i] = true; invRotationalMasses[i] = invMasses[i] = 1.0f; positions[i] = Vector3.Lerp(particles[i], centroid, shapeSmoothing); restPositions[i] = positions[i]; orientations[i] = orientation; restOrientations[i] = orientation; restPositions[i][3] = 1; // activate rest position. principalRadii[i] = principalValues; phases[i] = Oni.MakePhase(1, (selfCollisions?Oni.ParticlePhase.SelfCollide:0) | (oneSided?Oni.ParticlePhase.OneSided:0)); } //Create shape matching clusters: ShapeMatchingConstraints.Clear(); ObiShapeMatchingConstraintBatch shapeBatch = new ObiShapeMatchingConstraintBatch(false, false); ShapeMatchingConstraints.AddBatch(shapeBatch); List <int> indices = new List <int>(); // Generate soft clusters: //if (makeSoftClusters){ for (int i = 0; i < particles.Count; ++i) { indices.Clear(); indices.Add(i); for (int j = 0; j < particles.Count; ++j) { if (i != j && Vector3.Distance(particles[j], particles[i]) < softClusterRadius) { indices.Add(j); } } shapeBatch.AddConstraint(indices.ToArray(), 1, 0, 0, false); if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiSoftbody: generating shape matching constraints...", i / (float)particles.Count)); } } //} //if (makeSolidCluster){ /*indices.Clear(); * for (int i = 0; i < particles.Count; ++i) * indices.Add(i); * shapeBatch.AddConstraint(indices.ToArray(),1,0,0,true);*/ //} // Initialize pin constraints: PinConstraints.Clear(); ObiPinConstraintBatch pinBatch = new ObiPinConstraintBatch(false, false); PinConstraints.AddBatch(pinBatch); initializing = false; initialized = true; }