public void UpdateRenderer(ObiActor actor) { using (m_UpdateChainRopeRendererChunksPerfMarker.Auto()) { var rope = actor as ObiRopeBase; // In case there are no link prefabs to instantiate: if (linkPrefabs.Count == 0) { return; } // Regenerate instances if needed: if (linkInstances == null || linkInstances.Count < rope.particleCount) { CreateChainLinkInstances(rope); } var blueprint = rope.blueprint; int elementCount = rope.elements.Count; float twist = -sectionTwist * elementCount * twistAnchor; //we will define and transport a reference frame along the curve using parallel transport method: frame.Reset(); frame.SetTwist(twist); int lastParticle = -1; for (int i = 0; i < elementCount; ++i) { ObiStructuralElement elm = rope.elements[i]; Vector3 pos = rope.GetParticlePosition(elm.particle1); Vector3 nextPos = rope.GetParticlePosition(elm.particle2); Vector3 linkVector = nextPos - pos; Vector3 tangent = linkVector.normalized; if (rope.blueprint.usesOrientedParticles) { frame.Transport(nextPos, tangent, rope.GetParticleOrientation(elm.particle1) * Vector3.up, twist); twist += sectionTwist; } else { frame.Transport(nextPos, tangent, sectionTwist); } if (linkInstances[i] != null) { linkInstances[i].SetActive(true); Transform linkTransform = linkInstances[i].transform; linkTransform.position = pos + linkVector * 0.5f; linkTransform.localScale = rope.GetParticleMaxRadius(elm.particle1) * 2 * linkScale; linkTransform.rotation = Quaternion.LookRotation(tangent, frame.normal); } lastParticle = elm.particle2; } for (int i = elementCount; i < linkInstances.Count; ++i) { if (linkInstances[i] != null) { linkInstances[i].SetActive(false); } } } }
/** * Generates smooth curve chunks. */ public void GenerateSmoothChunks(ObiRopeBase actor, uint smoothingLevels) { using (m_GenerateSmoothChunksPerfMarker.Auto()) { smoothChunks.Clear(); smoothSections = 0; smoothLength = 0; if (!Application.isPlaying) { actor.RebuildElementsFromConstraints(); } AllocateRawChunks(actor); w2l = actor.transform.worldToLocalMatrix; w2lRotation = w2l.rotation; // keep track of the first element of each chunk int chunkStart = 0; ObiPathFrame frame_0 = new ObiPathFrame(); // "next" frame ObiPathFrame frame_1 = new ObiPathFrame(); // current frame ObiPathFrame frame_2 = new ObiPathFrame(); // previous frame // generate curve for each rope chunk: for (int i = 0; i < rawChunks.Count; ++i) { int elementCount = rawChunks[i].Count - 1; // Initialize frames: frame_0.Reset(); frame_1.Reset(); frame_2.Reset(); PathFrameFromParticle(actor, ref frame_1, actor.elements[chunkStart].particle1, false); frame_2 = frame_1; for (int m = 1; m <= rawChunks[i].Count; ++m) { int index; if (m >= elementCount) { // second particle of last element in the chunk. index = actor.elements[chunkStart + elementCount - 1].particle2; } else { //first particle of current element. index = actor.elements[chunkStart + m].particle1; } // generate curve frame from particle: PathFrameFromParticle(actor, ref frame_0, index); if (actor.usesOrientedParticles) { // copy frame directly. frame_2 = frame_1; } else { // perform parallel transport, using forward / backward average to calculate tangent. frame_1.tangent = ((frame_1.position - frame_2.position) + (frame_0.position - frame_1.position)).normalized; frame_2.Transport(frame_1, twist); } // in case we wrapped around the rope, average first and last frames: if (chunkStart + m > actor.activeParticleCount) { frame_2 = rawChunks[0][0] = 0.5f * frame_2 + 0.5f * rawChunks[0][0]; } frame_1 = frame_0; rawChunks[i][m - 1] = frame_2; } // increment chunkStart by the amount of elements in this chunk: chunkStart += elementCount; // adaptive curvature-based decimation: if (Decimate(rawChunks[i], smoothChunks[i], decimation)) { // if any decimation took place, swap raw and smooth chunks: var aux = rawChunks[i]; rawChunks[i] = smoothChunks[i]; smoothChunks[i] = aux; } // get smooth curve points: Chaikin(rawChunks[i], smoothChunks[i], smoothingLevels); // count total curve sections and total curve length: smoothSections += smoothChunks[i].Count - 1; smoothLength += CalculateChunkLength(smoothChunks[i]); } } }
protected virtual IEnumerator CreateStretchShearConstraints(List <Vector3> particleNormals) { stretchShearConstraintsData = new ObiStretchShearConstraintsData(); stretchShearConstraintsData.AddBatch(new ObiStretchShearConstraintsBatch()); stretchShearConstraintsData.AddBatch(new ObiStretchShearConstraintsBatch()); // rotation minimizing frame: ObiPathFrame frame = new ObiPathFrame(); frame.Reset(); for (int i = 0; i < totalParticles - 1; i++) { var batch = stretchShearConstraintsData.batches[i % 2] as ObiStretchShearConstraintsBatch; Vector2Int indices = new Vector2Int(i, i + 1); Vector3 d = positions[indices.y] - positions[indices.x]; restLengths[i] = d.magnitude; frame.Transport(positions[indices.x], d.normalized, 0); orientations[i] = Quaternion.LookRotation(frame.tangent, particleNormals[indices.x]); 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[indices.y] = orientations[i]; restOrientations[indices.y] = orientations[i]; batch.AddConstraint(indices, indices.x, restLengths[i], Quaternion.identity); batch.activeConstraintCount++; if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiRod: generating structural constraints...", i / (float)(totalParticles - 1))); } } // if the path is closed, add the last, loop closing constraint to a new batch to avoid sharing particles. if (path.Closed) { var loopClosingBatch = new ObiStretchShearConstraintsBatch(); stretchShearConstraintsData.AddBatch(loopClosingBatch); Vector2Int indices = new Vector2Int(m_ActiveParticleCount - 1, 0); Vector3 d = positions[indices.y] - positions[indices.x]; restLengths[m_ActiveParticleCount - 2] = d.magnitude; frame.Transport(positions[indices.x], d.normalized, 0); orientations[m_ActiveParticleCount - 1] = Quaternion.LookRotation(frame.tangent, particleNormals[indices.x]); restOrientations[m_ActiveParticleCount - 1] = orientations[m_ActiveParticleCount - 1]; loopClosingBatch.AddConstraint(indices, indices.x, restLengths[m_ActiveParticleCount - 2], Quaternion.identity); loopClosingBatch.activeConstraintCount++; } // Recalculate rest length: m_RestLength = 0; foreach (float length in restLengths) { m_RestLength += length; } }