Beispiel #1
0
        /**
         * Generates new valid rest positions for the entire rope.
         */
        public override void RegenerateRestPositions()
        {
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            // Iterate trough all distance constraints in order:
            int   particle            = -1;
            int   lastParticle        = -1;
            float accumulatedDistance = 0;

            for (int i = 0; i < distanceBatch.ConstraintCount; ++i)
            {
                if (i == 0)
                {
                    lastParticle            = particle = distanceBatch.springIndices[i * 2];
                    restPositions[particle] = new Vector4(0, 0, 0, 1);
                }

                accumulatedDistance += Mathf.Min(interParticleDistance, principalRadii[particle][0], principalRadii[lastParticle][0]);

                particle = distanceBatch.springIndices[i * 2 + 1];
                restPositions[particle]    = Vector3.right * accumulatedDistance;
                restPositions[particle][3] = 1;                 // activate rest position
            }

            PushDataToSolver(ParticleData.REST_POSITIONS);
        }
Beispiel #2
0
        /**
         * Counts the amount of continuous sections in each chunk of rope.
         */
        protected void CountContinuousSegments()
        {
            rawCurves.Clear();
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            int segmentCount = 0;
            int lastParticle = -1;

            // Iterate trough all distance constraints in order. If we find a discontinuity, reset segment count:
            for (int i = 0; i < distanceBatch.ConstraintCount; ++i)
            {
                int particle1 = distanceBatch.springIndices[i * 2];
                int particle2 = distanceBatch.springIndices[i * 2 + 1];

                // start new curve at discontinuities:
                if (particle1 != lastParticle && segmentCount > 0)
                {
                    // add a new curve with the correct amount of sections:
                    AddCurve(segmentCount + 1);
                    segmentCount = 0;
                }

                lastParticle = particle2;
                segmentCount++;
            }

            // add the last curve:
            AddCurve(segmentCount + 1);
        }
Beispiel #3
0
        /**
         * Returns the rest length of a structural constraint
         */
        public override float GetStructuralConstraintRestLength(int constraintIndex)
        {
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            if (distanceBatch != null && constraintIndex >= 0 && constraintIndex < distanceBatch.ConstraintCount)
            {
                return(distanceBatch.restLengths[constraintIndex]);
            }
            return(0);
        }
Beispiel #4
0
        /**
         * Returns the index of the structural constraint at a given normalized rope coordinate.
         */
        public override int GetConstraintIndexAtNormalizedCoordinate(float coord)
        {
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            if (distanceBatch != null)
            {
                float mu = coord * distanceBatch.ConstraintCount;
                return(Mathf.Clamp(Mathf.FloorToInt(mu), 0, distanceBatch.ConstraintCount - 1));
            }
            return(-1);
        }
Beispiel #5
0
        /**
         * Returns the index of the distance constraint at a given normalized rope coordinate.
         */
        public int GetConstraintIndexAtNormalizedCoordinate(float coord)
        {
            // Nothing guarantees particle index order is the same as particle ordering in the rope.
            // However distance constraints must be ordered, so we'll use that:

            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            float mu = coord * distanceBatch.ConstraintCount;

            return(Mathf.Clamp(Mathf.FloorToInt(mu), 0, distanceBatch.ConstraintCount - 1));
        }
Beispiel #6
0
        /**
         * Returns the particle indices affected by a structural constraint
         */
        public override bool GetStructuralConstraintParticles(int constraintIndex, ref int particleIndex1, ref int particleIndex2)
        {
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            if (distanceBatch != null && constraintIndex >= 0 && constraintIndex < distanceBatch.ConstraintCount)
            {
                particleIndex1 = distanceBatch.springIndices[constraintIndex * 2];
                particleIndex2 = distanceBatch.springIndices[constraintIndex * 2 + 1];
                return(true);
            }
            return(false);
        }
Beispiel #7
0
        /**
         * Recalculates rest rope length.
         */
        public void RecalculateLenght()
        {
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            restLength = 0;

            // Iterate trough all distance constraints in order:
            for (int i = 0; i < distanceBatch.ConstraintCount; ++i)
            {
                restLength += distanceBatch.restLengths[i];
            }
        }
Beispiel #8
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();

            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();
            Matrix4x4 w2l = transform.worldToLocalMatrix;

            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 <CurveSection> controlPoints = rawCurves[i];

                // get control points position:
                for (int m = 0; m < segments; ++m)
                {
                    int particleIndex = distanceBatch.springIndices[(firstSegment + m) * 2];
                    controlPoints[m] = new CurveSection(w2l.MultiplyPoint3x4(GetParticlePosition(particleIndex)),
                                                        solidRadii[particleIndex],
                                                        (this.colors != null && particleIndex < this.colors.Length) ? this.colors[particleIndex] : Color.white);

                    // last segment adds its second particle too:
                    if (m == segments - 1)
                    {
                        particleIndex        = distanceBatch.springIndices[(firstSegment + m) * 2 + 1];
                        controlPoints[m + 1] = new CurveSection(w2l.MultiplyPoint3x4(GetParticlePosition(particleIndex)),
                                                                solidRadii[particleIndex],
                                                                (this.colors != null && particleIndex < this.colors.Length) ? this.colors[particleIndex] : Color.white);
                    }
                }

                firstSegment += segments;

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

                // count total curve sections and total curve length:
                curveSections += curves[i].Count - 1;
                curveLength   += CalculateCurveLength(curves[i]);
            }
        }
Beispiel #9
0
        /**
         * Returns actual rope length, including stretch.
         */
        public float CalculateLength()
        {
            ObiDistanceConstraintBatch batch = DistanceConstraints.GetFirstBatch();

            // iterate over all distance constraints and accumulate their length:
            float   actualLength = 0;
            Vector3 a, b;

            for (int i = 0; i < batch.ConstraintCount; ++i)
            {
                a             = GetParticlePosition(batch.springIndices[i * 2]);
                b             = GetParticlePosition(batch.springIndices[i * 2 + 1]);
                actualLength += Vector3.Distance(a, b);
            }
            return(actualLength);
        }
        private void ApplyTearing()
        {
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            float[] forces = new float[distanceBatch.ConstraintCount];
            Oni.GetBatchConstraintForces(distanceBatch.OniBatch, forces, distanceBatch.ConstraintCount, 0);

            List <TornEdge> tornEdges = new List <TornEdge>();

            for (int i = 0; i < forces.Length; i++)
            {
                float p1Resistance = tearResistance[distanceBatch.springIndices[i * 2]];
                float p2Resistance = tearResistance[distanceBatch.springIndices[i * 2 + 1]];

                // average particle resistances:
                float resistance = (p1Resistance + p2Resistance) * 0.5f * tearResistanceMultiplier;

                if (-forces[i] * 1000 > resistance)          // units are kilonewtons.
                {
                    tornEdges.Add(new TornEdge(i, forces[i]));
                }
            }

            if (tornEdges.Count > 0)
            {
                // sort edges by tear force:
                tornEdges.Sort(delegate(TornEdge x, TornEdge y) {
                    return(x.force.CompareTo(y.force));
                });

                DistanceConstraints.RemoveFromSolver(null);
                for (int i = 0; i < Mathf.Min(tearRate, tornEdges.Count); i++)
                {
                    Tear(tornEdges[i].index);
                }
                DistanceConstraints.AddToSolver(this);

                // update active bending constraints:
                BendingConstraints.SetActiveConstraints();

                // update solver deformable triangle indices:
                UpdateDeformableTriangles();

                // upload active particle list to solver:
                solver.UpdateActiveParticles();
            }
        }
Beispiel #11
0
        protected void ApplyTearing()
        {
            if (!tearable)
            {
                return;
            }

            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            float[] forces = new float[distanceBatch.ConstraintCount];
            Oni.GetBatchConstraintForces(distanceBatch.OniBatch, forces, distanceBatch.ConstraintCount, 0);

            List <int> tearedEdges = new List <int>();

            for (int i = 0; i < forces.Length; i++)
            {
                float p1Resistance = tearResistance[distanceBatch.springIndices[i * 2]];
                float p2Resistance = tearResistance[distanceBatch.springIndices[i * 2 + 1]];

                // average particle resistances:
                float resistance = (p1Resistance + p2Resistance) * 0.5f * tearResistanceMultiplier;

                if (-forces[i] * 1000 > resistance)                  // units are kilonewtons.
                {
                    tearedEdges.Add(i);
                }
            }

            if (tearedEdges.Count > 0)
            {
                DistanceConstraints.RemoveFromSolver(null);
                BendingConstraints.RemoveFromSolver(null);
                for (int i = 0; i < tearedEdges.Count; i++)
                {
                    Tear(tearedEdges[i]);
                }
                BendingConstraints.AddToSolver(this);
                DistanceConstraints.AddToSolver(this);

                // update active bending constraints:
                BendingConstraints.SetActiveConstraints();

                // upload active particle list to solver:
                solver.UpdateActiveParticles();
            }
        }
Beispiel #12
0
        public void Tear(int constraintIndex)
        {
            // don't allow splitting if there are no free particles left in the pool.
            if (usedParticles >= totalParticles)
            {
                return;
            }

            // get involved constraint batches:
            ObiDistanceConstraintBatch distanceBatch = (ObiDistanceConstraintBatch)DistanceConstraints.GetFirstBatch();
            ObiBendConstraintBatch     bendingBatch  = (ObiBendConstraintBatch)BendingConstraints.GetFirstBatch();

            // get particle indices at both ends of the constraint:
            int splitIndex  = distanceBatch.springIndices[constraintIndex * 2];
            int intactIndex = distanceBatch.springIndices[constraintIndex * 2 + 1];

            // see if the rope is continuous at the split index and the intact index:
            bool continuousAtSplit = (constraintIndex < distanceBatch.ConstraintCount - 1 && distanceBatch.springIndices[(constraintIndex + 1) * 2] == splitIndex) ||
                                     (constraintIndex > 0 && distanceBatch.springIndices[(constraintIndex - 1) * 2 + 1] == splitIndex);

            bool continuousAtIntact = (constraintIndex < distanceBatch.ConstraintCount - 1 && distanceBatch.springIndices[(constraintIndex + 1) * 2] == intactIndex) ||
                                      (constraintIndex > 0 && distanceBatch.springIndices[(constraintIndex - 1) * 2 + 1] == intactIndex);

            // we will split the particle with higher mass, so swap them if needed (and possible). Also make sure that the rope hasnt been cut there yet:
            if ((invMasses[splitIndex] > invMasses[intactIndex] || invMasses[splitIndex] == 0) &&
                continuousAtIntact)
            {
                int aux = splitIndex;
                splitIndex  = intactIndex;
                intactIndex = aux;
            }

            // see if we are able to proceed with the cut:
            if (invMasses[splitIndex] == 0 || !continuousAtSplit)
            {
                return;
            }

            // halve the mass of the teared particle:
            invMasses[splitIndex] *= 2;

            // copy the new particle data in the actor and solver arrays:
            positions[usedParticles]      = positions[splitIndex];
            velocities[usedParticles]     = velocities[splitIndex];
            active[usedParticles]         = active[splitIndex];
            invMasses[usedParticles]      = invMasses[splitIndex];
            principalRadii[usedParticles] = principalRadii[splitIndex];
            phases[usedParticles]         = phases[splitIndex];

            if (colors != null && colors.Length > 0)
            {
                colors[usedParticles] = colors[splitIndex];
            }
            tearResistance[usedParticles]   = tearResistance[splitIndex];
            restPositions[usedParticles]    = positions[splitIndex];
            restPositions[usedParticles][3] = 1;             // activate rest position.

            // update solver particle data:
            solver.velocities[particleIndices[usedParticles]]     = solver.velocities[particleIndices[splitIndex]];
            solver.startPositions[particleIndices[usedParticles]] = solver.positions [particleIndices[usedParticles]] = solver.positions [particleIndices[splitIndex]];

            solver.invMasses [particleIndices[usedParticles]]     = solver.invMasses [particleIndices[splitIndex]] = invMasses[splitIndex];
            solver.principalRadii[particleIndices[usedParticles]] = solver.principalRadii[particleIndices[splitIndex]] = principalRadii[splitIndex];
            solver.phases    [particleIndices[usedParticles]]     = solver.phases        [particleIndices[splitIndex]];

            // Update bending constraints:
            for (int i = 0; i < bendingBatch.ConstraintCount; ++i)
            {
                // disable the bending constraint centered at the split particle:
                if (bendingBatch.bendingIndices[i * 3 + 2] == splitIndex)
                {
                    bendingBatch.DeactivateConstraint(i);
                }

                // update the one that bridges the cut:
                else if (!DoesBendConstraintSpanDistanceConstraint(distanceBatch, bendingBatch, constraintIndex, i))
                {
                    // if the bend constraint does not involve the split distance constraint,
                    // update the end that references the split vertex:
                    if (bendingBatch.bendingIndices[i * 3] == splitIndex)
                    {
                        bendingBatch.bendingIndices[i * 3] = usedParticles;
                    }
                    else if (bendingBatch.bendingIndices[i * 3 + 1] == splitIndex)
                    {
                        bendingBatch.bendingIndices[i * 3 + 1] = usedParticles;
                    }
                }
            }

            // Update distance constraints at both ends of the cut:
            if (constraintIndex < distanceBatch.ConstraintCount - 1)
            {
                if (distanceBatch.springIndices[(constraintIndex + 1) * 2] == splitIndex)
                {
                    distanceBatch.springIndices[(constraintIndex + 1) * 2] = usedParticles;
                }
                if (distanceBatch.springIndices[(constraintIndex + 1) * 2 + 1] == splitIndex)
                {
                    distanceBatch.springIndices[(constraintIndex + 1) * 2 + 1] = usedParticles;
                }
            }

            if (constraintIndex > 0)
            {
                if (distanceBatch.springIndices[(constraintIndex - 1) * 2] == splitIndex)
                {
                    distanceBatch.springIndices[(constraintIndex - 1) * 2] = usedParticles;
                }
                if (distanceBatch.springIndices[(constraintIndex - 1) * 2 + 1] == splitIndex)
                {
                    distanceBatch.springIndices[(constraintIndex - 1) * 2 + 1] = usedParticles;
                }
            }

            usedParticles++;
            pooledParticles--;
        }
Beispiel #13
0
        /**
         * Returns the amount of structural constraints in the rope.
         */
        public override int GetStructuralConstraintCount()
        {
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            return(distanceBatch != null ? distanceBatch.ConstraintCount : 0);
        }
Beispiel #14
0
        /**
         * Tears a cloth distance constraint, affecting both the physical representation of the cloth and its mesh.
         */
        public void Tear(int constraintIndex)
        {
            if (topology == null)
            {
                return;
            }

            // don't allow splitting if there are no free particles left in the pool.
            if (usedParticles >= sharedMesh.vertexCount + pooledVertices)
            {
                return;
            }

            // get involved constraint batches:
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();
            ObiBendConstraintBatch     bendingBatch  = BendingConstraints.GetFirstBatch();

            // get particle indices at both ends of the constraint:
            int splitIndex  = distanceBatch.springIndices[constraintIndex * 2];
            int intactIndex = distanceBatch.springIndices[constraintIndex * 2 + 1];

            // we will split the particle with higher mass, so swap them if needed.
            if (invMasses[splitIndex] > invMasses[intactIndex])
            {
                int aux = splitIndex;
                splitIndex  = intactIndex;
                intactIndex = aux;
            }

            // Calculate the splitting plane in local space:
            Vector3 v1     = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[splitIndex]]);
            Vector3 v2     = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[intactIndex]]);
            Vector3 normal = (v2 - v1).normalized;

            int numUpdatedHalfEdges = maxVertexValency;

            int[] updatedHalfEdges = new int[numUpdatedHalfEdges];

            // Try to split the vertex at that particle.
            // If we cannot not split the higher mass particle, try the other one. If that fails too, we cannot tear this edge.
            if (invMasses[splitIndex] == 0 ||
                !Oni.TearDeformableMeshAtVertex(deformableMesh, splitIndex, ref v1, ref normal, updatedHalfEdges, ref numUpdatedHalfEdges))
            {
                // Try to split the other particle:
                int aux = splitIndex;
                splitIndex  = intactIndex;
                intactIndex = aux;

                v1     = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[splitIndex]]);
                v2     = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[intactIndex]]);
                normal = (v2 - v1).normalized;

                if (invMasses[splitIndex] == 0 ||
                    !Oni.TearDeformableMeshAtVertex(deformableMesh, splitIndex, ref v1, ref normal, updatedHalfEdges, ref numUpdatedHalfEdges))
                {
                    return;
                }
            }

            // identify weak points around the cut:
            int   weakPt1            = -1;
            int   weakPt2            = -1;
            float weakestValue       = float.MaxValue;
            float secondWeakestValue = float.MaxValue;

            foreach (Oni.Vertex v in topology.GetNeighbourVerticesEnumerator(topology.heVertices[splitIndex]))
            {
                Vector3 neighbour = transform.worldToLocalMatrix.MultiplyPoint3x4(solver.renderablePositions[particleIndices[v.index]]);
                float   weakness  = Mathf.Abs(Vector3.Dot(normal, (neighbour - v1).normalized));

                if (weakness < weakestValue)
                {
                    secondWeakestValue = weakestValue;
                    weakestValue       = weakness;
                    weakPt2            = weakPt1;
                    weakPt1            = v.index;
                }
                else if (weakness < secondWeakestValue)
                {
                    secondWeakestValue = weakness;
                    weakPt2            = v.index;
                }
            }

            // reduce tear resistance at the weak spots of the cut, to encourage coherent tear formation.
            if (weakPt1 >= 0)
            {
                tearResistance[weakPt1] *= 1 - tearDebilitation;
            }
            if (weakPt2 >= 0)
            {
                tearResistance[weakPt2] *= 1 - tearDebilitation;
            }

            topology.UpdateVertexCount();

            // halve the mass and radius of the original particle:
            invMasses[splitIndex]  *= 2;
            solidRadii[splitIndex] *= 0.5f;

            // copy the new particle data in the actor and solver arrays:
            positions[usedParticles]        = positions[splitIndex];
            velocities[usedParticles]       = velocities[splitIndex];
            active[usedParticles]           = active[splitIndex];
            invMasses[usedParticles]        = invMasses[splitIndex];
            solidRadii[usedParticles]       = solidRadii[splitIndex];
            phases[usedParticles]           = phases[splitIndex];
            areaContribution[usedParticles] = areaContribution[splitIndex];
            tearResistance[usedParticles]   = tearResistance[splitIndex];
            restPositions[usedParticles]    = positions[splitIndex];
            restPositions[usedParticles][3] = 0;     // activate rest position.

            // update solver particle data:
            Vector4[] velocity = { Vector4.zero };
            Oni.GetParticleVelocities(solver.OniSolver, velocity, 1, particleIndices[splitIndex]);
            Oni.SetParticleVelocities(solver.OniSolver, velocity, 1, particleIndices[usedParticles]);

            Vector4[] position = { Vector4.zero };
            Oni.GetParticlePositions(solver.OniSolver, position, 1, particleIndices[splitIndex]);
            Oni.SetParticlePositions(solver.OniSolver, position, 1, particleIndices[usedParticles]);

            Oni.SetParticleInverseMasses(solver.OniSolver, new float[] { invMasses[splitIndex] }, 1, particleIndices[usedParticles]);
            Oni.SetParticleSolidRadii(solver.OniSolver, new float[] { solidRadii[splitIndex] }, 1, particleIndices[usedParticles]);
            Oni.SetParticlePhases(solver.OniSolver, new int[] { phases[splitIndex] }, 1, particleIndices[usedParticles]);

            usedParticles++;

            // update distance constraints:
            for (int i = 0; i < numUpdatedHalfEdges; ++i)
            {
                int          halfEdgeIndex = updatedHalfEdges[i];
                Oni.HalfEdge e             = topology.heHalfEdges[halfEdgeIndex];

                // find start and end vertex indices for this edge:
                int startVertex = topology.GetHalfEdgeStartVertex(topology.heHalfEdges[halfEdgeIndex]);
                int endVertex   = topology.heHalfEdges[halfEdgeIndex].endVertex;

                if (distanceConstraintMap[halfEdgeIndex] > -1)          // update existing edge

                {
                    distanceBatch.springIndices[distanceConstraintMap[halfEdgeIndex] * 2]     = startVertex;
                    distanceBatch.springIndices[distanceConstraintMap[halfEdgeIndex] * 2 + 1] = endVertex;
                }
                else if (topology.IsSplit(halfEdgeIndex))           // new edge

                {
                    int pairConstraintIndex = distanceConstraintMap[topology.heHalfEdges[halfEdgeIndex].pair];

                    // update constraint-edge map:
                    distanceConstraintMap[halfEdgeIndex] = distanceBatch.restLengths.Count;

                    // add the new constraint:
                    distanceBatch.AddConstraint(startVertex,
                                                endVertex,
                                                distanceBatch.restLengths[pairConstraintIndex],
                                                distanceBatch.stiffnesses[pairConstraintIndex].x,
                                                distanceBatch.stiffnesses[pairConstraintIndex].y);
                }

                // update deformable triangles:
                if (e.indexInFace > -1)
                {
                    deformableTriangles[e.face * 3 + e.indexInFace] = e.endVertex;
                }
            }

            if (splitIndex < bendConstraintOffsets.Length - 1)
            {
                // deactivate bend constraints that contain the split vertex...

                // ...at the center:
                for (int i = bendConstraintOffsets[splitIndex]; i < bendConstraintOffsets[splitIndex + 1]; ++i)
                {
                    bendingBatch.DeactivateConstraint(i);
                }

                // ...at one end:
                foreach (Oni.Vertex v in topology.GetNeighbourVerticesEnumerator(topology.heVertices[splitIndex]))
                {
                    if (v.index < bendConstraintOffsets.Length - 1)
                    {
                        for (int i = bendConstraintOffsets[v.index]; i < bendConstraintOffsets[v.index + 1]; ++i)
                        {
                            if (bendingBatch.bendingIndices[i * 3] == splitIndex || bendingBatch.bendingIndices[i * 3 + 1] == splitIndex)
                            {
                                bendingBatch.DeactivateConstraint(i);
                            }
                        }
                    }
                }
            }
        }