/** * Generates the particle based physical representation of the rope. This is the initialization method for the rope object * and should not be called directly once the object has been created. */ protected override IEnumerator Initialize() { initialized = false; initializing = true; interParticleDistance = -1; RemoveFromSolver(null); if (ropePath == null) { Debug.LogError("Cannot initialize rope. There's no ropePath present. Please provide a spline to define the shape of the rope"); yield break; } ropePath.RecalculateSplineLenght(0.00001f, 7); closed = ropePath.closed; restLength = ropePath.Length; usedParticles = Mathf.CeilToInt(restLength / thickness * resolution) + (closed ? 0:1); totalParticles = usedParticles; active = new bool[totalParticles]; positions = new Vector3[totalParticles]; orientations = new Quaternion[totalParticles]; velocities = new Vector3[totalParticles]; angularVelocities = new Vector3[totalParticles]; invMasses = new float[totalParticles]; invRotationalMasses = new float[totalParticles]; principalRadii = new Vector3[totalParticles]; phases = new int[totalParticles]; restPositions = new Vector4[totalParticles]; restOrientations = new Quaternion[totalParticles]; colors = new Color[totalParticles]; int numSegments = usedParticles - (closed ? 0:1); if (numSegments > 0) { interParticleDistance = restLength / (float)numSegments; } else { interParticleDistance = 0; } float radius = interParticleDistance * resolution; for (int i = 0; i < usedParticles; i++) { active[i] = true; invMasses[i] = 1.0f / DEFAULT_PARTICLE_MASS; invRotationalMasses[i] = 1.0f / DEFAULT_PARTICLE_ROTATIONAL_MASS; float mu = ropePath.GetMuAtLenght(interParticleDistance * i); positions[i] = transform.InverseTransformPoint(ropePath.transform.TransformPoint(ropePath.GetPositionAt(mu))); principalRadii[i] = Vector3.one * radius; phases[i] = Oni.MakePhase(1, selfCollisions?Oni.ParticlePhase.SelfCollide:0); colors[i] = Color.white; if (i % 100 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiRod: generating particles...", i / (float)usedParticles)); } } StretchShearConstraints.Clear(); ObiStretchShearConstraintBatch stretchBatch = new ObiStretchShearConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS); StretchShearConstraints.AddBatch(stretchBatch); // rotation minimizing frame: ObiCurveFrame frame = new ObiCurveFrame(); frame.Reset(); for (int i = 0; i < numSegments; i++) { int next = (i + 1) % (ropePath.closed ? usedParticles:usedParticles + 1); float mu = ropePath.GetMuAtLenght(interParticleDistance * i); Vector3 normal = transform.InverseTransformVector(ropePath.transform.TransformVector(ropePath.GetNormalAt(mu))); frame.Transport(positions[i], (positions[next] - positions[i]).normalized, 0); orientations[i] = Quaternion.LookRotation(frame.tangent, normal); restOrientations[i] = orientations[i]; // Also set the orientation of the next particle. If it is not the last one, we will overwrite it. // This makes sure that open rods provide an orientation for their last particle (or rather, a phantom segment past the last particle). orientations[next] = orientations[i]; restOrientations[next] = orientations[i]; stretchBatch.AddConstraint(i, next, interParticleDistance, Quaternion.identity, Vector3.one); if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiRod: generating structural constraints...", i / (float)numSegments)); } } BendTwistConstraints.Clear(); ObiBendTwistConstraintBatch twistBatch = new ObiBendTwistConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS); BendTwistConstraints.AddBatch(twistBatch); // the last bend constraint couples the last segment and a phantom segment past the last particle. for (int i = 0; i < numSegments; i++) { int next = (i + 1) % (ropePath.closed ? usedParticles:usedParticles + 1); Quaternion darboux = keepInitialShape ? ObiUtils.RestDarboux(orientations[i], orientations[next]) : Quaternion.identity; twistBatch.AddConstraint(i, next, darboux, Vector3.one); if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiRod: generating structural constraints...", i / (float)numSegments)); } } ChainConstraints.Clear(); ObiChainConstraintBatch chainBatch = new ObiChainConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS); ChainConstraints.AddBatch(chainBatch); int[] indices = new int[usedParticles + (closed ? 1:0)]; for (int i = 0; i < usedParticles; ++i) { indices[i] = i; } // Add the first particle as the last index of the chain, if closed. if (closed) { indices[usedParticles] = 0; } chainBatch.AddConstraint(indices, interParticleDistance, 1, 1); // Initialize tether constraints: TetherConstraints.Clear(); // Initialize pin constraints: PinConstraints.Clear(); ObiPinConstraintBatch pinBatch = new ObiPinConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS); PinConstraints.AddBatch(pinBatch); initializing = false; initialized = true; RegenerateRestPositions(); }