/** * This function generates tethers for a given set of particles, all belonging a connected graph. * This is use ful when the cloth mesh is composed of several * disjoint islands, and we dont want tethers in one island to anchor particles to fixed particles in a different island. * * Inside each island, fixed particles are partitioned again into "islands", then generates up to maxTethers constraints for each * particle linking it to the closest point in each fixed island. */ private void GenerateTethersForIsland(HashSet <int> particles, int maxTethers) { if (maxTethers > 0) { ObiTetherConstraintBatch tetherBatch = new ObiTetherConstraintBatch(true, false); TetherConstraints.AddBatch(tetherBatch); List <HashSet <int> > fixedIslands = GenerateIslands(particles, true); // Generate tether constraints: foreach (int i in particles) { if (invMasses[i] == 0 || !active[i]) { continue; } List <KeyValuePair <float, int> > tethers = new List <KeyValuePair <float, int> >(fixedIslands.Count * maxTethers); // Find the closest particle in each island, and add it to tethers. foreach (HashSet <int> island in fixedIslands) { int closest = -1; float minDistance = Mathf.Infinity; foreach (int j in island) { float distance = (topology.heVertices[i].position - topology.heVertices[j].position).sqrMagnitude; if (distance < minDistance) { minDistance = distance; closest = j; } } if (closest >= 0) { tethers.Add(new KeyValuePair <float, int>(minDistance, closest)); } } // Sort tether indices by distance: tethers.Sort( delegate(KeyValuePair <float, int> x, KeyValuePair <float, int> y) { return(x.Key.CompareTo(y.Key)); } ); // Create constraints for "maxTethers" closest anchor particles: for (int k = 0; k < Mathf.Min(maxTethers, tethers.Count); ++k) { tetherBatch.AddConstraint(i, tethers[k].Value, Mathf.Sqrt(tethers[k].Key), TetherConstraints.tetherScale, TetherConstraints.stiffness); } } tetherBatch.Cook(); } }
private void GenerateHierarchicalTethers(int maxLevels) { ObiTetherConstraintBatch tetherBatch = new ObiTetherConstraintBatch(true, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS); TetherConstraints.AddBatch(tetherBatch); // for each level: for (int i = 1; i <= maxLevels; ++i) { int stride = i * 2; // for each particle: for (int j = 0; j < usedParticles - stride; ++j) { int nextParticle = j + stride; tetherBatch.AddConstraint(j, nextParticle % usedParticles, interParticleDistance * stride, 1, 1); } } tetherBatch.Cook(); }
private void GenerateFixedTethers(int maxTethers) { ObiTetherConstraintBatch tetherBatch = new ObiTetherConstraintBatch(true, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS); TetherConstraints.AddBatch(tetherBatch); List <HashSet <int> > islands = new List <HashSet <int> >(); // Partition fixed particles into islands: for (int i = 0; i < usedParticles; i++) { if (invMasses[i] > 0 || !active[i]) { continue; } int assignedIsland = -1; // keep a list of islands to merge with ours: List <int> mergeableIslands = new List <int>(); // See if any of our neighbors is part of an island: int prev = Mathf.Max(i - 1, 0); int next = Mathf.Min(i + 1, usedParticles - 1); for (int k = 0; k < islands.Count; ++k) { if ((active[prev] && islands[k].Contains(prev)) || (active[next] && islands[k].Contains(next))) { // if we are not in an island yet, pick this one: if (assignedIsland < 0) { assignedIsland = k; islands[k].Add(i); } // if we already are in an island, we will merge this newfound island with ours: else if (assignedIsland != k && !mergeableIslands.Contains(k)) { mergeableIslands.Add(k); } } } // merge islands with the assigned one: foreach (int merge in mergeableIslands) { islands[assignedIsland].UnionWith(islands[merge]); } // remove merged islands: mergeableIslands.Sort(); mergeableIslands.Reverse(); foreach (int merge in mergeableIslands) { islands.RemoveAt(merge); } // If no adjacent particle is in an island, create a new one: if (assignedIsland < 0) { islands.Add(new HashSet <int>() { i }); } } // Generate tether constraints: for (int i = 0; i < usedParticles; ++i) { if (invMasses[i] == 0) { continue; } List <KeyValuePair <float, int> > tethers = new List <KeyValuePair <float, int> >(islands.Count); // Find the closest particle in each island, and add it to tethers. foreach (HashSet <int> island in islands) { int closest = -1; float minDistance = Mathf.Infinity; foreach (int j in island) { // TODO: Use linear distance along the rope in a more efficient way. precalculate it on generation! int min = Mathf.Min(i, j); int max = Mathf.Max(i, j); float distance = 0; for (int k = min; k < max; ++k) { distance += Vector3.Distance(positions[k], positions[k + 1]); } if (distance < minDistance) { minDistance = distance; closest = j; } } if (closest >= 0) { tethers.Add(new KeyValuePair <float, int>(minDistance, closest)); } } // Sort tether indices by distance: tethers.Sort( delegate(KeyValuePair <float, int> x, KeyValuePair <float, int> y) { return(x.Key.CompareTo(y.Key)); } ); // Create constraints for "maxTethers" closest anchor particles: for (int k = 0; k < Mathf.Min(maxTethers, tethers.Count); ++k) { tetherBatch.AddConstraint(i, tethers[k].Value, tethers[k].Key, 1, 1); } } tetherBatch.Cook(); }
/** * Automatically generates tether constraints for the cloth. * Partitions fixed particles into "islands", then generates up to maxTethers constraints for each * particle, linking it to the closest point in each island. */ public override bool GenerateTethers(int maxTethers) { if (!Initialized) { return(false); } TetherConstraints.Clear(); if (maxTethers > 0) { ObiTetherConstraintBatch tetherBatch = new ObiTetherConstraintBatch(true, false); TetherConstraints.AddBatch(tetherBatch); List <HashSet <int> > islands = new List <HashSet <int> >(); // Partition fixed particles into islands: for (int i = 0; i < topology.heVertices.Length; i++) { Oni.Vertex vertex = topology.heVertices[i]; if (invMasses[i] > 0 || !active[i]) { continue; } int assignedIsland = -1; // keep a list of islands to merge with ours: List <int> mergeableIslands = new List <int>(); // See if any of our neighbors is part of an island: foreach (Oni.Vertex n in topology.GetNeighbourVerticesEnumerator(vertex)) { if (!active[n.index]) { continue; } for (int k = 0; k < islands.Count; ++k) { if (islands[k].Contains(n.index)) { // if we are not in an island yet, pick this one: if (assignedIsland < 0) { assignedIsland = k; islands[k].Add(i); } // if we already are in an island, we will merge this newfound island with ours: else if (assignedIsland != k && !mergeableIslands.Contains(k)) { mergeableIslands.Add(k); } } } } // merge islands with the assigned one: foreach (int merge in mergeableIslands) { islands[assignedIsland].UnionWith(islands[merge]); } // remove merged islands: mergeableIslands.Sort(); mergeableIslands.Reverse(); foreach (int merge in mergeableIslands) { islands.RemoveAt(merge); } // If no adjacent particle is in an island, create a new one: if (assignedIsland < 0) { islands.Add(new HashSet <int>() { i }); } } // Generate tether constraints: for (int i = 0; i < invMasses.Length; ++i) { if (invMasses[i] == 0 || !active[i]) { continue; } List <KeyValuePair <float, int> > tethers = new List <KeyValuePair <float, int> >(islands.Count * maxTethers); // Find the closest particle in each island, and add it to tethers. foreach (HashSet <int> island in islands) { int closest = -1; float minDistance = Mathf.Infinity; foreach (int j in island) { float distance = (topology.heVertices[i].position - topology.heVertices[j].position).sqrMagnitude; if (distance < minDistance) { minDistance = distance; closest = j; } } if (closest >= 0) { tethers.Add(new KeyValuePair <float, int>(minDistance, closest)); } } // Sort tether indices by distance: tethers.Sort( delegate(KeyValuePair <float, int> x, KeyValuePair <float, int> y) { return(x.Key.CompareTo(y.Key)); } ); // Create constraints for "maxTethers" closest anchor particles: for (int k = 0; k < Mathf.Min(maxTethers, tethers.Count); ++k) { tetherBatch.AddConstraint(i, tethers[k].Value, Mathf.Sqrt(tethers[k].Key), TetherConstraints.tetherScale, TetherConstraints.stiffness); } } tetherBatch.Cook(); } return(true); }