Example #1
0
 public override void TransportFrame(ObiCurveFrame frame, ObiCurveSection section, float sectionTwist)
 {
     if (frame != null)
     {
         frame.Set(section);
     }
 }
Example #2
0
        public override void UpdateTearPrefab(ObiCurveFrame frame, ref int tearCount, bool reverseLookDirection)
        {
            if (tearPrefabPool != null && tearCount < tearPrefabPool.Length)
            {
                if (!tearPrefabPool[tearCount].activeSelf)
                {
                    tearPrefabPool[tearCount].SetActive(true);
                }

                PlaceObjectAtCurveFrame(frame, tearPrefabPool[tearCount], Space.Self, reverseLookDirection);
                tearCount++;
            }
        }
Example #3
0
 public void PlaceObjectAtCurveFrame(ObiCurveFrame frame, GameObject obj, Space space, bool reverseLookDirection)
 {
     if (space == Space.Self)
     {
         Matrix4x4 l2w = transform.localToWorldMatrix;
         obj.transform.position = l2w.MultiplyPoint3x4(frame.position);
         if (frame.tangent != Vector3.zero)
         {
             obj.transform.rotation = Quaternion.LookRotation(l2w.MultiplyVector(reverseLookDirection ? frame.tangent:-frame.tangent),
                                                              l2w.MultiplyVector(frame.normal));
         }
     }
     else
     {
         obj.transform.position = frame.position;
         if (frame.tangent != Vector3.zero)
         {
             obj.transform.rotation = Quaternion.LookRotation(reverseLookDirection ? frame.tangent:-frame.tangent, frame.normal);
         }
     }
 }
Example #4
0
        /**
         * Generate a list of smooth curves using particles as control points. Will take into account cuts in the rope,
         * generating one curve for each continuous piece of rope.
         */
        public void SmoothCurvesFromParticles()
        {
            curves.Clear();

            curveSections = 0;
            curveLength   = 0;

            // count amount of segments in each rope chunk:
            CountContinuousSegments();

            Matrix4x4  w2l            = transform.worldToLocalMatrix;
            Quaternion matrixRotation = Quaternion.LookRotation(
                w2l.GetColumn(2),
                w2l.GetColumn(1));
            int firstSegment = 0;

            // generate curve for each rope chunk:
            for (int i = 0; i < rawCurves.Count; ++i)
            {
                int segments = rawCurves[i].Count - 1;

                // allocate memory for the curve:
                ObiList <ObiCurveSection> controlPoints = rawCurves[i];

                // get control points position:
                int lastParticle = -1;
                int particle1 = -1, particle2 = -1;
                for (int m = 0; m < segments; ++m)
                {
                    if (GetStructuralConstraintParticles(firstSegment + m, ref particle1, ref particle2))
                    {
                        if (m == 0)
                        {
                            lastParticle = particle1;
                        }

                        // Find next and previous vectors:
                        Vector3 nextV = GetParticlePosition(particle2) - GetParticlePosition(particle1);
                        Vector3 prevV = GetParticlePosition(particle1) - GetParticlePosition(lastParticle);

                        Vector3    pos     = w2l.MultiplyPoint3x4(GetParticlePosition(particle1));
                        Quaternion orient  = matrixRotation * Quaternion.SlerpUnclamped(GetParticleOrientation(lastParticle), GetParticleOrientation(particle1), 0.5f);
                        Vector3    tangent = w2l.MultiplyVector(prevV + nextV).normalized;
                        Color      color   = (this.colors != null && particle1 < this.colors.Length) ? this.colors[particle1] : Color.white;

                        controlPoints[m] = new ObiCurveSection(new Vector4(pos.x, pos.y, pos.z, principalRadii[particle1][0]), tangent, orient * Vector3.up, color);

                        lastParticle = particle1;
                    }
                }

                // last segment adds its second particle too:
                if (segments > 0)
                {
                    Vector3    pos     = w2l.MultiplyPoint3x4(GetParticlePosition(particle2));
                    Quaternion orient  = matrixRotation * GetParticleOrientation(particle1);
                    Vector3    tangent = w2l.MultiplyVector(GetParticlePosition(particle2) - GetParticlePosition(particle1)).normalized;
                    Color      color   = (this.colors != null && particle2 < this.colors.Length) ? this.colors[particle2] : Color.white;

                    controlPoints[segments] = new ObiCurveSection(new Vector4(pos.x, pos.y, pos.z, principalRadii[particle2][0]), tangent, orient * Vector3.up, color);
                }

                firstSegment += segments;

                // get smooth curve points:
                ObiCurveFrame.Chaikin(controlPoints, curves[i], smoothing);

                // count total curve sections and total curve length:
                curveSections += curves[i].Count - 1;
                curveLength   += CalculateCurveLength(curves[i]);
            }
        }
Example #5
0
 public abstract void TransportFrame(ObiCurveFrame frame, ObiCurveSection section, float sectionTwist);
Example #6
0
 public virtual void UpdateTearPrefab(ObiCurveFrame frame, ref int tearCount, bool reverseLookDirection)
 {
     return;
 }
Example #7
0
        /**
         * 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();
        }
Example #8
0
        public override void UpdateRenderer(object sender, EventArgs e)
        {
            // In case there are no link prefabs to instantiate:
            if (linkPrefabs.Count == 0)
            {
                return;
            }

            // Regenerate instances if needed:
            if (linkInstances == null || linkInstances.Count < rope.TotalParticles)
            {
                CreateChainLinkInstances();
            }

            int constraintCount = rope.GetStructuralConstraintCount();

            // we will define and transport a reference frame along the curve using parallel transport method:
            if (frame == null)
            {
                frame = new ObiCurveFrame();
            }
            frame.Reset();
            frame.SetTwist(-sectionTwist * constraintCount * twistAnchor);

            int lastParticle = -1;
            int tearCount    = 0;

            for (int i = 0; i < constraintCount; ++i)
            {
                int particle1 = -1, particle2 = -1;
                rope.GetStructuralConstraintParticles(i, ref particle1, ref particle2);

                Vector3 pos        = rope.GetParticlePosition(particle1);
                Vector3 nextPos    = rope.GetParticlePosition(particle2);
                Vector3 linkVector = nextPos - pos;
                Vector3 tangent    = linkVector.normalized;

                // update tear prefab at the first side of tear:
                if (i > 0 && particle1 != lastParticle)
                {
                    rope.UpdateTearPrefab(frame, ref tearCount, false);
                    // reset frame at discontinuities:
                    frame.Reset();
                }

                // update frame: TODO: allow mesh rendering offset from torsion (twist)
                rope.TransportFrame(frame, new ObiCurveSection(nextPos, tangent, rope.GetParticleOrientation(particle1) * Vector3.up, Color.white), sectionTwist);

                // update tear prefab at the other side of the tear:
                if (i > 0 && particle1 != lastParticle)
                {
                    frame.position = pos;
                    rope.UpdateTearPrefab(frame, ref tearCount, false);
                }

                // update start/end prefabs:
                if (!rope.Closed)
                {
                    if (i == 0 && rope.startPrefabInstance != null)
                    {
                        rope.PlaceObjectAtCurveFrame(frame, rope.startPrefabInstance, Space.World, false);
                    }
                    else if (i == constraintCount - 1 && rope.endPrefabInstance != null)
                    {
                        frame.position = nextPos;
                        rope.PlaceObjectAtCurveFrame(frame, rope.endPrefabInstance, Space.World, true);
                    }
                }

                if (linkInstances[i] != null)
                {
                    linkInstances[i].SetActive(true);
                    Transform linkTransform = linkInstances[i].transform;
                    linkTransform.position   = pos + linkVector * 0.5f;
                    linkTransform.localScale = rope.thicknessFromParticles ? (rope.principalRadii[particle1][0] / rope.thickness) * linkScale : linkScale;
                    linkTransform.rotation   = Quaternion.LookRotation(tangent, frame.normal);
                }

                lastParticle = particle2;
            }

            for (int i = constraintCount; i < linkInstances.Count; ++i)
            {
                if (linkInstances[i] != null)
                {
                    linkInstances[i].SetActive(false);
                }
            }
        }
Example #9
0
        public override void UpdateRenderer(object sender, EventArgs e)
        {
            if (section == null || extrudedMesh == null)
            {
                return;
            }

            rope.SmoothCurvesFromParticles();

            CreateMeshIfNeeded();
            ClearMeshData();

            float actualToRestLengthRatio = rope.SmoothLength / rope.RestLength;

            int sectionSegments    = section.Segments;
            int verticesPerSection = sectionSegments + 1;                       // the last vertex in each section must be duplicated, due to uv wraparound.

            float vCoord       = -uvScale.y * rope.RestLength * uvAnchor;       // v texture coordinate.
            int   sectionIndex = 0;
            int   tearCount    = 0;

            // we will define and transport a reference frame along the curve using parallel transport method:
            if (frame == null)
            {
                frame = new ObiCurveFrame();
            }
            frame.Reset();
            frame.SetTwist(-sectionTwist * rope.SmoothSections * uvAnchor);

            // for closed curves, last frame of the last curve must be equal to first frame of first curve.
            Vector3 firstTangent = Vector3.forward;

            Vector4 texTangent = Vector4.zero;
            Vector2 uv         = Vector2.zero;

            for (int c = 0; c < rope.curves.Count; ++c)
            {
                ObiList <ObiCurveSection> curve = rope.curves[c];

                // Reinitialize frame for each curve.
                frame.Reset();

                for (int i = 0; i < curve.Count; ++i)
                {
                    // Calculate previous and next curve indices:
                    //int nextIndex = Mathf.Min(i+1,curve.Count-1);
                    int prevIndex = Mathf.Max(i - 1, 0);

                    // Calculate current tangent as the vector between previous and next curve points:
                    //Vector3 nextV;

                    // The next tangent of the last segment of the last curve in a closed rope, is the first tangent again:

                    /*if (rope.Closed && c == rope.curves.Count-1 && i == curve.Count-1 )
                     *      nextV = firstTangent;
                     * else
                     *      nextV = curve[nextIndex].positionAndRadius - curve[i].positionAndRadius;*/

                    //Vector3 prevV = curve[i].positionAndRadius - curve[prevIndex].positionAndRadius;
                    //Vector3 tangent = nextV + prevV;

                    // update frame:
                    rope.TransportFrame(frame, curve[i], sectionTwist);                  //curve[i].positionAndRadius,tangent,rope.sectionTwist);

                    // update tear prefabs (first segment of not first curve, last segment of not last curve)
                    if (c > 0 && i == 0)
                    {
                        rope.UpdateTearPrefab(frame, ref tearCount, false);
                    }
                    if (c < rope.curves.Count - 1 && i == curve.Count - 1)
                    {
                        rope.UpdateTearPrefab(frame, ref tearCount, true);
                    }

                    // update start/end prefabs:
                    if (c == 0 && i == 0)
                    {
                        // store first tangent of the first curve (for closed ropes):
                        firstTangent = frame.tangent;

                        if (rope.startPrefabInstance != null && !rope.Closed)
                        {
                            rope.PlaceObjectAtCurveFrame(frame, rope.startPrefabInstance, Space.Self, false);
                        }
                    }
                    else if (c == rope.curves.Count - 1 && i == curve.Count - 1 && rope.endPrefabInstance != null && !rope.Closed)
                    {
                        rope.PlaceObjectAtCurveFrame(frame, rope.endPrefabInstance, Space.Self, true);
                    }

                    // advance v texcoord:
                    vCoord += uvScale.y * (Vector3.Distance(curve[i].positionAndRadius, curve[prevIndex].positionAndRadius) /
                                           (normalizeV ? rope.SmoothLength : actualToRestLengthRatio));

                    // calculate section thickness (either constant, or particle radius based):
                    float sectionThickness = (rope.thicknessFromParticles ? curve[i].positionAndRadius.w : rope.thickness) * sectionThicknessScale;

                    // Loop around each segment:
                    for (int j = 0; j <= sectionSegments; ++j)
                    {
                        vertices.Add(frame.position + (section.vertices[j].x * frame.normal + section.vertices[j].y * frame.binormal) * sectionThickness);
                        normals.Add(vertices[vertices.Count - 1] - frame.position);
                        texTangent   = Vector3.Cross(normals[normals.Count - 1], frame.tangent);
                        texTangent.w = -1;
                        tangents.Add(texTangent);

                        vertColors.Add(curve[i].color);
                        uv.Set((j / (float)sectionSegments) * uvScale.x, vCoord);
                        uvs.Add(uv);

                        if (j < sectionSegments && i < curve.Count - 1)
                        {
                            tris.Add(sectionIndex * verticesPerSection + j);
                            tris.Add((sectionIndex + 1) * verticesPerSection + j);
                            tris.Add(sectionIndex * verticesPerSection + (j + 1));

                            tris.Add(sectionIndex * verticesPerSection + (j + 1));
                            tris.Add((sectionIndex + 1) * verticesPerSection + j);
                            tris.Add((sectionIndex + 1) * verticesPerSection + (j + 1));
                        }
                    }

                    sectionIndex++;
                }
            }

            CommitMeshData();
        }
Example #10
0
        public override void UpdateRenderer(object sender, EventArgs e)
        {
            if (mesh == null)
            {
                return;
            }

            rope.SmoothCurvesFromParticles();

            if (rope.curves.Count == 0)
            {
                return;
            }

            ObiList <ObiCurveSection> curve = rope.curves[0];

            if (curve.Count < 2)
            {
                return;
            }

            float actualToRestLengthRatio = stretchWithRope ? rope.SmoothLength / rope.RestLength : 1;

            // squashing factor, makes mesh thinner when stretched and thicker when compresssed.
            float squashing = Mathf.Clamp(1 + volumeScaling * (1 / Mathf.Max(actualToRestLengthRatio, 0.01f) - 1), 0.01f, 2);

            // Calculate scale along swept axis so that the mesh spans the entire lenght of the rope if required.
            Vector3 actualScale = scale;

            if (spanEntireLength)
            {
                actualScale[(int)axis] = rope.RestLength / meshSizeAlongAxis;
            }

            float   previousVertexValue = 0;
            float   meshLength          = 0;
            int     index            = 0;
            int     nextIndex        = 1;
            int     prevIndex        = 0;
            Vector3 nextV            = curve[nextIndex].positionAndRadius - curve[index].positionAndRadius;
            Vector3 prevV            = curve[index].positionAndRadius - curve[prevIndex].positionAndRadius;
            Vector3 tangent          = (nextV + prevV).normalized;
            float   sectionMagnitude = nextV.magnitude;

            // we will define and transport a reference frame along the curve using parallel transport method:
            if (frame == null)
            {
                frame = new ObiCurveFrame();
            }
            frame.Reset();
            frame.SetTwistAndTangent(-sectionTwist * rope.SmoothSections * twistAnchor, tangent);

            // set frame's initial position:
            frame.position = curve[index].positionAndRadius;

            // basis matrix for deforming the mesh, also calculate column offsets based on swept axis:
            Matrix4x4 basis = new Matrix4x4();
            int       xo    = ((int)axis) % 3 * 4;
            int       yo    = ((int)axis + 1) % 3 * 4;
            int       zo    = ((int)axis + 2) % 3 * 4;

            basis[xo]     = frame.tangent[0];
            basis[xo + 1] = frame.tangent[1];
            basis[xo + 2] = frame.tangent[2];

            basis[yo]     = frame.binormal[0];
            basis[yo + 1] = frame.binormal[1];
            basis[yo + 2] = frame.binormal[2];

            basis[zo]     = frame.normal[0];
            basis[zo + 1] = frame.normal[1];
            basis[zo + 2] = frame.normal[2];

            for (int i = 0; i < orderedVertices.Length; ++i)
            {
                int   vIndex      = orderedVertices[i];
                float vertexValue = inputVertices[vIndex][(int)axis] * actualScale[(int)axis] + offset;

                // Calculate how much we've advanced in the sort axis since the last vertex:
                meshLength         += (vertexValue - previousVertexValue) * actualToRestLengthRatio;
                previousVertexValue = vertexValue;

                // If we have advanced to the next section of the curve:
                while (meshLength > sectionMagnitude && sectionMagnitude > Mathf.Epsilon)
                {
                    meshLength -= sectionMagnitude;
                    index       = Mathf.Min(index + 1, curve.Count - 1);

                    // Calculate previous and next curve indices:
                    nextIndex = Mathf.Min(index + 1, curve.Count - 1);
                    prevIndex = Mathf.Max(index - 1, 0);

                    // Calculate current tangent as the vector between previous and next curve points:
                    nextV = curve[index].positionAndRadius - curve[nextIndex].positionAndRadius;

                    /*prevV = curve[index].positionAndRadius - curve[prevIndex].positionAndRadius;
                     * tangent = (nextV + prevV).normalized;*/
                    sectionMagnitude = nextV.magnitude;                     // TODO: revisar, ya que la w debería ser cero, no debe influir el radio.

                    // Transport frame:
                    frame.Transport(curve[index], sectionTwist);

                    // Update basis matrix:
                    basis[xo]     = frame.tangent[0];
                    basis[xo + 1] = frame.tangent[1];
                    basis[xo + 2] = frame.tangent[2];

                    basis[yo]     = frame.binormal[0];
                    basis[yo + 1] = frame.binormal[1];
                    basis[yo + 2] = frame.binormal[2];

                    basis[zo]     = frame.normal[0];
                    basis[zo + 1] = frame.normal[1];
                    basis[zo + 2] = frame.normal[2];
                }

                float sectionThickness = rope.thicknessFromParticles ? curve[index].positionAndRadius.w : rope.thickness;

                // calculate deformed vertex position:
                Vector3 offsetFromCurve = Vector3.Scale(inputVertices[vIndex], actualScale * sectionThickness * squashing);
                offsetFromCurve[(int)axis] = meshLength;

                vertices[vIndex]   = frame.position + basis.MultiplyVector(offsetFromCurve);
                normals[vIndex]    = basis.MultiplyVector(inputNormals[vIndex]);
                tangents[vIndex]   = basis * inputTangents[vIndex];               // avoids expensive implicit conversion from Vector4 to Vector3.
                tangents[vIndex].w = inputTangents[vIndex].w;
            }

            CommitMeshData();
        }
Example #11
0
        public void UpdateRenderer(Camera camera)
        {
            if (camera == null || !rope.gameObject.activeInHierarchy)
            {
                return;
            }

            rope.SmoothCurvesFromParticles();

            CreateMeshIfNeeded();
            ClearMeshData();

            float actualToRestLengthRatio = rope.SmoothLength / rope.RestLength;

            float vCoord       = -uvScale.y * rope.RestLength * uvAnchor;       // v texture coordinate.
            int   sectionIndex = 0;
            int   tearCount    = 0;

            Vector3 localSpaceCamera = rope.transform.InverseTransformPoint(camera.transform.position);

            // we will define and transport a reference frame along the curve using parallel transport method:
            if (frame == null)
            {
                frame = new ObiCurveFrame();
            }
            frame.Reset();

            // for closed curves, last frame of the last curve must be equal to first frame of first curve.
            Vector3 firstTangent = Vector3.forward;

            Vector4 texTangent = Vector4.zero;
            Vector2 uv         = Vector2.zero;

            for (int c = 0; c < rope.curves.Count; ++c)
            {
                ObiList <ObiCurveSection> curve = rope.curves[c];

                // Reinitialize frame for each curve.
                frame.Reset();

                for (int i = 0; i < curve.Count; ++i)
                {
                    // Calculate previous and next curve indices:
                    //int nextIndex = Mathf.Min(i+1,curve.Count-1);
                    int prevIndex = Mathf.Max(i - 1, 0);

                    // Calculate current tangent as the vector between previous and next curve points:

                    /*Vector3 nextV;
                     *
                     * // The next tangent of the last segment of the last curve in a closed rope, is the first tangent again:
                     * if (rope.Closed && c == rope.curves.Count-1 && i == curve.Count-1 )
                     *      nextV = firstTangent;
                     * else
                     *      nextV = curve[nextIndex].positionAndRadius - curve[i].positionAndRadius;
                     *
                     * Vector3 prevV = curve[i].positionAndRadius - curve[prevIndex].positionAndRadius;
                     * Vector3 tangent = nextV + prevV;*/

                    // update frame:
                    frame.Transport(curve[i], 0);

                    // update tear prefabs:
                    if (c > 0 && i == 0)
                    {
                        rope.UpdateTearPrefab(frame, ref tearCount, false);
                    }
                    if (c < rope.curves.Count - 1 && i == curve.Count - 1)
                    {
                        rope.UpdateTearPrefab(frame, ref tearCount, true);
                    }

                    // update start/end prefabs:
                    if (c == 0 && i == 0)
                    {
                        // store first tangent of the first curve (for closed ropes):
                        firstTangent = frame.tangent;

                        if (rope.startPrefabInstance != null && !rope.Closed)
                        {
                            rope.PlaceObjectAtCurveFrame(frame, rope.startPrefabInstance, Space.Self, false);
                        }
                    }
                    else if (c == rope.curves.Count - 1 && i == curve.Count - 1 && rope.endPrefabInstance != null && !rope.Closed)
                    {
                        rope.PlaceObjectAtCurveFrame(frame, rope.endPrefabInstance, Space.Self, true);
                    }

                    // advance v texcoord:
                    vCoord += uvScale.y * (Vector3.Distance(curve[i].positionAndRadius, curve[prevIndex].positionAndRadius) /
                                           (normalizeV?rope.SmoothLength:actualToRestLengthRatio));

                    // calculate section thickness (either constant, or particle radius based):
                    float sectionThickness = (rope.thicknessFromParticles ? curve[i].positionAndRadius.w : rope.thickness) * sectionThicknessScale;

                    Vector3 normal = frame.position - localSpaceCamera;
                    normal.Normalize();

                    Vector3 bitangent = Vector3.Cross(normal, frame.tangent);
                    bitangent.Normalize();

                    vertices.Add(frame.position + bitangent * sectionThickness);
                    vertices.Add(frame.position - bitangent * sectionThickness);

                    normals.Add(-normal);
                    normals.Add(-normal);

                    texTangent   = -bitangent;
                    texTangent.w = 1;
                    tangents.Add(texTangent);
                    tangents.Add(texTangent);

                    vertColors.Add(curve[i].color);
                    vertColors.Add(curve[i].color);

                    uv.Set(0, vCoord);
                    uvs.Add(uv);
                    uv.Set(1, vCoord);
                    uvs.Add(uv);

                    if (i < curve.Count - 1)
                    {
                        tris.Add(sectionIndex * 2);
                        tris.Add((sectionIndex + 1) * 2);
                        tris.Add(sectionIndex * 2 + 1);

                        tris.Add(sectionIndex * 2 + 1);
                        tris.Add((sectionIndex + 1) * 2);
                        tris.Add((sectionIndex + 1) * 2 + 1);
                    }

                    sectionIndex++;
                }
            }

            CommitMeshData();
        }