Пример #1
0
        public void OnSceneGUI()
        {
            if (Event.current.type == EventType.Repaint)
            {
                ObiRope rope = cursor.GetComponent <ObiRope>();

                ObiDistanceConstraintBatch distanceBatch = rope.DistanceConstraints.GetFirstBatch();

                Handles.color = Color.yellow;
                int     constraint = rope.GetConstraintIndexAtNormalizedCoordinate(cursor.normalizedCoord);
                Vector3 pos1       = rope.GetParticlePosition(distanceBatch.springIndices[constraint * 2]);
                Vector3 pos2       = rope.GetParticlePosition(distanceBatch.springIndices[constraint * 2 + 1]);

                if (cursor.direction)
                {
                    Handles.DrawWireDisc(pos1, pos2 - pos1, rope.thickness * 2);
                    Vector3 direction = pos2 - pos1;
                    if (direction != Vector3.zero)
                    {
                        Handles.ArrowHandleCap(0, pos1, Quaternion.LookRotation(direction), 0.2f, EventType.Repaint);
                    }
                }
                else
                {
                    Handles.DrawWireDisc(pos2, pos1 - pos2, rope.thickness * 2);
                    Vector3 direction = pos1 - pos2;
                    if (direction != Vector3.zero)
                    {
                        Handles.ArrowHandleCap(0, pos2, Quaternion.LookRotation(direction), 0.2f, EventType.Repaint);
                    }
                }
            }
        }
Пример #2
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);
        }
Пример #3
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);
        }
 public void AddBatch(ObiDistanceConstraintBatch batch)
 {
     if (batch != null && batch.GetConstraintType() == GetConstraintType())
     {
         batches.Add(batch);
     }
 }
Пример #5
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);
        }
Пример #6
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);
        }
Пример #7
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));
        }
Пример #8
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);
        }
Пример #9
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];
            }
        }
Пример #10
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]);
            }
        }
Пример #11
0
        /**
         * Returns whether a bend constraint affects the two particles referenced by a given distance constraint:
         */
        public bool DoesBendConstraintSpanDistanceConstraint(ObiDistanceConstraintBatch dbatch, ObiBendConstraintBatch bbatch, int d, int b)
        {
            return((bbatch.bendingIndices[b * 3 + 2] == dbatch.springIndices[d * 2] &&
                    bbatch.bendingIndices[b * 3 + 1] == dbatch.springIndices[d * 2 + 1]) ||

                   (bbatch.bendingIndices[b * 3 + 1] == dbatch.springIndices[d * 2] &&
                    bbatch.bendingIndices[b * 3 + 2] == dbatch.springIndices[d * 2 + 1]) ||

                   (bbatch.bendingIndices[b * 3 + 2] == dbatch.springIndices[d * 2] &&
                    bbatch.bendingIndices[b * 3] == dbatch.springIndices[d * 2 + 1]) ||

                   (bbatch.bendingIndices[b * 3] == dbatch.springIndices[d * 2] &&
                    bbatch.bendingIndices[b * 3 + 2] == dbatch.springIndices[d * 2 + 1]));
        }
Пример #12
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);
        }
Пример #13
0
        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();
            }
        }
Пример #14
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();
            }
        }
Пример #15
0
        /**
         * From a given constraint index and direction, finds the constraint where rope extension/shortening will happen.
         */
        private int FindHotConstraint(ObiDistanceConstraintBatch distanceBatch, int constraint, int maxAmount)
        {
            // from the current particle, iterate distance constraints in the adequate direction until we find a discontinuity:
            if (direction)
            {
                int lastParticle = distanceBatch.springIndices[constraint * 2 + 1];

                for (int i = 1; i <= maxAmount; ++i)
                {
                    if (constraint + i == distanceBatch.ConstraintCount ||
                        distanceBatch.springIndices[(constraint + i) * 2] != lastParticle)
                    {
                        return(constraint + i - 1);
                    }
                    lastParticle = distanceBatch.springIndices[(constraint + i) * 2 + 1];
                }

                return(constraint + maxAmount);
            }
            else
            {
                int lastParticle = distanceBatch.springIndices[constraint * 2];

                for (int i = 1; i <= maxAmount; ++i)
                {
                    if (constraint - i < 0 ||
                        distanceBatch.springIndices[(constraint - i) * 2 + 1] != lastParticle)
                    {
                        return(constraint - i + 1);
                    }
                    lastParticle = distanceBatch.springIndices[(constraint - i) * 2];
                }

                return(constraint - maxAmount);
            }
        }
Пример #16
0
 public void RemoveBatch(ObiDistanceConstraintBatch batch)
 {
     batches.Remove(batch);
 }
Пример #17
0
        public override void Update(Camera camera)
        {
            if (linkInstances.Count == 0)
            {
                return;
            }

            ObiDistanceConstraintBatch distanceBatch = rope.DistanceConstraints.GetFirstBatch();

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

            int lastParticle = -1;
            int tearCount    = 0;

            for (int i = 0; i < distanceBatch.ConstraintCount; ++i)
            {
                int particle1 = distanceBatch.springIndices[i * 2];
                int particle2 = distanceBatch.springIndices[i * 2 + 1];

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

                if (i > 0 && particle1 != lastParticle)
                {
                    // update tear prefab at the first side of tear:
                    if (rope.tearPrefabPool != null && tearCount < rope.tearPrefabPool.Length)
                    {
                        if (!rope.tearPrefabPool[tearCount].activeSelf)
                        {
                            rope.tearPrefabPool[tearCount].SetActive(true);
                        }

                        rope.PlaceObjectAtCurveFrame(frame, rope.tearPrefabPool[tearCount], Space.World, true);
                        tearCount++;
                    }

                    // reset frame at discontinuities:
                    frame.Reset();
                }

                // update frame:
                frame.Transport(nextPos, tangent, rope.sectionTwist);

                // update tear prefab at the other side of the tear:
                if (i > 0 && particle1 != lastParticle && rope.tearPrefabPool != null && tearCount < rope.tearPrefabPool.Length)
                {
                    if (!rope.tearPrefabPool[tearCount].activeSelf)
                    {
                        rope.tearPrefabPool[tearCount].SetActive(true);
                    }

                    frame.position = pos;
                    rope.PlaceObjectAtCurveFrame(frame, rope.tearPrefabPool[tearCount], Space.World, false);
                    tearCount++;
                }

                // update start/end prefabs:
                if (!rope.Closed)
                {
                    if (i == 0 && rope.startPrefabInstance != null)
                    {
                        rope.PlaceObjectAtCurveFrame(frame, rope.startPrefabInstance, Space.World, false);
                    }
                    else if (i == distanceBatch.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.solidRadii[particle1] / rope.thickness) * linkScale : linkScale;
                    linkTransform.rotation   = Quaternion.LookRotation(tangent, frame.normal);
                }

                lastParticle = particle2;
            }

            for (int i = distanceBatch.ConstraintCount; i < linkInstances.Count; ++i)
            {
                if (linkInstances[i] != null)
                {
                    linkInstances[i].SetActive(false);
                }
            }
        }
Пример #18
0
        /**
         * Generates the particle based physical representation of the cloth mesh. This is the initialization method for the cloth object
         * and should not be called directly once the object has been created.
         */
        protected override IEnumerator Initialize()
        {
            initialized  = false;
            initializing = false;

            if (sharedTopology == null)
            {
                Debug.LogError("No ObiMeshTopology provided. Cannot initialize physical representation.");
                yield break;
            }
            else if (!sharedTopology.Initialized)
            {
                Debug.LogError("The provided ObiMeshTopology contains no data. Cannot initialize physical representation.");
                yield break;
            }

            initializing = true;

            RemoveFromSolver(null);

            GameObject.DestroyImmediate(topology);
            topology = GameObject.Instantiate(sharedTopology);

            active              = new bool[topology.heVertices.Length];
            positions           = new Vector3[topology.heVertices.Length];
            restPositions       = new Vector4[topology.heVertices.Length];
            velocities          = new Vector3[topology.heVertices.Length];
            invMasses           = new float[topology.heVertices.Length];
            principalRadii      = new Vector3[topology.heVertices.Length];
            phases              = new int[topology.heVertices.Length];
            areaContribution    = new float[topology.heVertices.Length];
            deformableTriangles = new int[topology.heFaces.Length * 3];

            initialScaleMatrix.SetTRS(Vector3.zero, Quaternion.identity, transform.lossyScale);

            // Create a particle for each vertex:
            for (int i = 0; i < topology.heVertices.Length; i++)
            {
                Oni.Vertex vertex = topology.heVertices[i];

                // Get the particle's area contribution.
                areaContribution[i] = 0;
                foreach (Oni.Face face in topology.GetNeighbourFacesEnumerator(vertex))
                {
                    areaContribution[i] += topology.GetFaceArea(face) / 3;
                }

                // Get the shortest neighbour edge, particle radius will be half of its length.
                float minEdgeLength = Single.MaxValue;
                foreach (Oni.HalfEdge edge in topology.GetNeighbourEdgesEnumerator(vertex))
                {
                    // vertices at each end of the edge:
                    Vector3 v1 = initialScaleMatrix * topology.heVertices[topology.GetHalfEdgeStartVertex(edge)].position;
                    Vector3 v2 = initialScaleMatrix * topology.heVertices[edge.endVertex].position;

                    minEdgeLength = Mathf.Min(minEdgeLength, Vector3.Distance(v1, v2));
                }

                active[i]           = true;
                invMasses[i]        = (skinnedMeshRenderer == null && areaContribution[i] > 0) ? (1.0f / (DEFAULT_PARTICLE_MASS * areaContribution[i])) : 0;
                positions[i]        = initialScaleMatrix * vertex.position;
                restPositions[i]    = positions[i];
                restPositions[i][3] = 1;         // activate rest position.
                principalRadii[i]   = Vector3.one * minEdgeLength * 0.5f;
                phases[i]           = Oni.MakePhase(1, selfCollisions?Oni.ParticlePhase.SelfCollide:0);

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating particles...", i / (float)topology.heVertices.Length));
                }
            }

            // Generate deformable triangles:
            for (int i = 0; i < topology.heFaces.Length; i++)
            {
                Oni.Face face = topology.heFaces[i];

                Oni.HalfEdge e1 = topology.heHalfEdges[face.halfEdge];
                Oni.HalfEdge e2 = topology.heHalfEdges[e1.nextHalfEdge];
                Oni.HalfEdge e3 = topology.heHalfEdges[e2.nextHalfEdge];

                deformableTriangles[i * 3]     = e1.endVertex;
                deformableTriangles[i * 3 + 1] = e2.endVertex;
                deformableTriangles[i * 3 + 2] = e3.endVertex;

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating deformable geometry...", i / (float)topology.heFaces.Length));
                }
            }

            List <ObiMeshTopology.HEEdge> edges = topology.GetEdgeList();

            DistanceConstraints.Clear();
            ObiDistanceConstraintBatch distanceBatch = new ObiDistanceConstraintBatch(true, false);

            DistanceConstraints.AddBatch(distanceBatch);

            // Create distance springs:
            for (int i = 0; i < edges.Count; i++)
            {
                Oni.HalfEdge hedge       = topology.heHalfEdges[edges[i].halfEdgeIndex];
                Oni.Vertex   startVertex = topology.heVertices[topology.GetHalfEdgeStartVertex(hedge)];
                Oni.Vertex   endVertex   = topology.heVertices[hedge.endVertex];

                distanceBatch.AddConstraint(topology.GetHalfEdgeStartVertex(hedge), hedge.endVertex, Vector3.Distance(initialScaleMatrix * startVertex.position, initialScaleMatrix * endVertex.position), 1, 1);

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating structural constraints...", i / (float)topology.heHalfEdges.Length));
                }
            }

            // Cook distance constraints, for better cache and SIMD use:
            distanceBatch.Cook();

            // Create aerodynamic constraints:
            AerodynamicConstraints.Clear();
            ObiAerodynamicConstraintBatch aeroBatch = new ObiAerodynamicConstraintBatch(false, false);

            AerodynamicConstraints.AddBatch(aeroBatch);

            for (int i = 0; i < topology.heVertices.Length; i++)
            {
                aeroBatch.AddConstraint(i,
                                        areaContribution[i],
                                        AerodynamicConstraints.dragCoefficient,
                                        AerodynamicConstraints.liftCoefficient);

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating aerodynamic constraints...", i / (float)topology.heFaces.Length));
                }
            }

            //Create skin constraints (if needed)
            if (skinnedMeshRenderer != null)
            {
                SkinConstraints.Clear();
                ObiSkinConstraintBatch skinBatch = new ObiSkinConstraintBatch(true, false);
                SkinConstraints.AddBatch(skinBatch);

                for (int i = 0; i < topology.heVertices.Length; ++i)
                {
                    skinBatch.AddConstraint(i, initialScaleMatrix * topology.heVertices[i].position, Vector3.up, 0.05f, 0.1f, 0, 1);

                    if (i % 500 == 0)
                    {
                        yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating skin constraints...", i / (float)topology.heVertices.Length));
                    }
                }

                for (int i = 0; i < topology.normals.Length; ++i)
                {
                    skinBatch.skinNormals[topology.visualMap[i]] = topology.normals[i];
                }

                skinBatch.Cook();
            }

            //Create pressure constraints if the mesh is closed:
            VolumeConstraints.Clear();

            if (topology.IsClosed)
            {
                ObiVolumeConstraintBatch volumeBatch = new ObiVolumeConstraintBatch(false, false);
                VolumeConstraints.AddBatch(volumeBatch);

                float avgInitialScale = (initialScaleMatrix.m00 + initialScaleMatrix.m11 + initialScaleMatrix.m22) * 0.33f;

                int[] triangleIndices = new int[topology.heFaces.Length * 3];
                for (int i = 0; i < topology.heFaces.Length; i++)
                {
                    Oni.Face face = topology.heFaces[i];

                    Oni.HalfEdge e1 = topology.heHalfEdges[face.halfEdge];
                    Oni.HalfEdge e2 = topology.heHalfEdges[e1.nextHalfEdge];
                    Oni.HalfEdge e3 = topology.heHalfEdges[e2.nextHalfEdge];

                    triangleIndices[i * 3]     = e1.endVertex;
                    triangleIndices[i * 3 + 1] = e2.endVertex;
                    triangleIndices[i * 3 + 2] = e3.endVertex;

                    if (i % 500 == 0)
                    {
                        yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating volume constraints...", i / (float)topology.heFaces.Length));
                    }
                }

                volumeBatch.AddConstraint(triangleIndices, topology.MeshVolume * avgInitialScale, 1, 1);
            }

            //Create bending constraints:
            BendingConstraints.Clear();
            ObiBendConstraintBatch bendBatch = new ObiBendConstraintBatch(true, false);

            BendingConstraints.AddBatch(bendBatch);

            Dictionary <int, int> cons = new Dictionary <int, int>();

            for (int i = 0; i < topology.heVertices.Length; i++)
            {
                Oni.Vertex vertex = topology.heVertices[i];

                foreach (Oni.Vertex n1 in topology.GetNeighbourVerticesEnumerator(vertex))
                {
                    float      cosBest = 0;
                    Oni.Vertex vBest   = n1;

                    foreach (Oni.Vertex n2 in topology.GetNeighbourVerticesEnumerator(vertex))
                    {
                        float cos = Vector3.Dot((n1.position - vertex.position).normalized,
                                                (n2.position - vertex.position).normalized);
                        if (cos < cosBest)
                        {
                            cosBest = cos;
                            vBest   = n2;
                        }
                    }

                    if (!cons.ContainsKey(vBest.index) || cons[vBest.index] != n1.index)
                    {
                        cons[n1.index] = vBest.index;

                        Vector3 n1Pos     = initialScaleMatrix * n1.position;
                        Vector3 bestPos   = initialScaleMatrix * vBest.position;
                        Vector3 vertexPos = initialScaleMatrix * vertex.position;

                        float[] bendRestPositions = new float[] { n1Pos[0], n1Pos[1], n1Pos[2],
                                                                  bestPos[0], bestPos[1], bestPos[2],
                                                                  vertexPos[0], vertexPos[1], vertexPos[2] };

                        float restBend = Oni.BendingConstraintRest(bendRestPositions);
                        bendBatch.AddConstraint(n1.index, vBest.index, vertex.index, restBend, 0, 1);
                    }
                }

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: adding bend constraints...", i / (float)sharedTopology.heVertices.Length));
                }
            }

            bendBatch.Cook();

            // Initialize tether constraints:
            TetherConstraints.Clear();

            // Initialize pin constraints:
            PinConstraints.Clear();
            ObiPinConstraintBatch pinBatch = new ObiPinConstraintBatch(false, false);

            PinConstraints.AddBatch(pinBatch);

            initializing = false;
            initialized  = true;

            if (skinnedMeshRenderer == null)
            {
                InitializeWithRegularMesh();
            }
            else
            {
                InitializeWithSkinnedMesh();
            }
        }
Пример #19
0
        /**
         * Generates the particle based physical representation of the bone hierarcht. This is the initialization method for the actor
         * and should not be called directly once the object has been created.
         */
        protected override IEnumerator Initialize()
        {
            initialized  = false;
            initializing = true;

            RemoveFromSolver(null);

            // get a list of bones in preorder:
            bones = new List <Transform>();
            foreach (Transform bone in EnumerateBonesBreadthFirst())
            {
                bones.Add(bone);
            }

            parentIndices = new int[bones.Count];

            active           = new bool[bones.Count];
            positions        = new Vector3[bones.Count];
            velocities       = new Vector3[bones.Count];
            invMasses        = new float[bones.Count];
            principalRadii   = new Vector3[bones.Count];
            phases           = new int[bones.Count];
            restPositions    = new Vector4[bones.Count];
            restOrientations = new Quaternion[bones.Count];
            frozen           = new bool[bones.Count];

            DistanceConstraints.Clear();
            ObiDistanceConstraintBatch distanceBatch = new ObiDistanceConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS);

            DistanceConstraints.AddBatch(distanceBatch);

            BendingConstraints.Clear();
            ObiBendConstraintBatch bendingBatch = new ObiBendConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS);

            BendingConstraints.AddBatch(bendingBatch);

            SkinConstraints.Clear();
            ObiSkinConstraintBatch skinBatch = new ObiSkinConstraintBatch(true, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS);

            SkinConstraints.AddBatch(skinBatch);

            for (int i = 0; i < bones.Count; ++i)
            {
                active[i]           = true;
                invMasses[i]        = 1.0f / DEFAULT_PARTICLE_MASS;
                positions[i]        = transform.InverseTransformPoint(bones[i].position);
                restPositions[i]    = positions[i];
                restPositions[i][3] = 1;
                restOrientations[i] = Quaternion.identity;
                principalRadii[i]   = new Vector3(particleRadius, particleRadius, particleRadius);
                frozen[i]           = false;
                phases[i]           = Oni.MakePhase(1, selfCollisions?Oni.ParticlePhase.SelfCollide:0);

                parentIndices[i] = -1;
                if (bones[i].parent != null)
                {
                    parentIndices[i] = bones.IndexOf(bones[i].parent);
                }

                skinBatch.AddConstraint(i, positions[i], Vector3.up, 0.05f, 0, 0, 1);

                foreach (Transform child in bones[i])
                {
                    int childIndex = bones.IndexOf(child);
                    if (childIndex >= 0)
                    {
                        // add distance constraint between the bone and its child.
                        distanceBatch.AddConstraint(i, childIndex, Vector3.Distance(bones[i].position, child.position), 1, 1);

                        if (parentIndices[i] >= 0)
                        {
                            Transform parent = bones[parentIndices[i]];

                            float[] bendRestPositions = new float[] { parent.position[0], parent.position[1], parent.position[2],
                                                                      child.position[0], child.position[1], child.position[2],
                                                                      bones[i].position[0], bones[i].position[1], bones[i].position[2] };
                            float restBend = Oni.BendingConstraintRest(bendRestPositions);

                            // add bend constraint between the bone, its parent and its child.
                            bendingBatch.AddConstraint(parentIndices[i], childIndex, i, restBend, 0, 0);
                        }
                    }
                }

                if (i % 10 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiBone: generating particles...", i / (float)bones.Count));
                }
            }

            skinBatch.Cook();

            initializing = false;
            initialized  = true;
        }
Пример #20
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 = (ObiDistanceConstraintBatch)DistanceConstraints.GetBatches()[0];
            ObiBendConstraintBatch     bendingBatch  = (ObiBendConstraintBatch)BendingConstraints.GetBatches()[0];

            // 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.

            solver.activeParticles.Add(particleIndices[usedParticles]);

            // 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);
                            }
                        }
                    }
                }
            }

            // Create new aerodynamic constraint:

            /*aerodynamicConstraints.AddConstraint(true,newParticle[0],
             *                                                                                Vector3.up,
             *                                                                                aerodynamicConstraints.windVector,
             *                                                                                areaContribution[newParticle[0]],
             *                                            aerodynamicConstraints.dragCoefficient,
             *                                            aerodynamicConstraints.liftCoefficient);*/
        }
Пример #21
0
        /**
         * Generates the particle based physical representation of the cloth mesh. This is the initialization method for the cloth object
         * and should not be called directly once the object has been created.
         */
        public override IEnumerator GeneratePhysicRepresentationForMesh()
        {
            initialized  = false;
            initializing = false;

            if (sharedTopology == null)
            {
                Debug.LogError("No ObiMeshTopology provided. Cannot initialize physical representation.");
                yield break;
            }
            else if (!sharedTopology.Initialized)
            {
                Debug.LogError("The provided ObiMeshTopology contains no data. Cannot initialize physical representation.");
                yield break;
            }

            initializing = true;

            RemoveFromSolver(null);

            ResetTopology();

            maxVertexValency = 0;

            pooledParticles = (int)((topology.heFaces.Length * 3 - topology.heVertices.Length) * tearCapacity);
            usedParticles   = topology.heVertices.Length;

            int totalParticles = usedParticles + pooledParticles;

            active              = new bool[totalParticles];
            positions           = new Vector3[totalParticles];
            restPositions       = new Vector4[totalParticles];
            velocities          = new Vector3[totalParticles];
            vorticities         = new Vector3[totalParticles];
            invMasses           = new float[totalParticles];
            solidRadii          = new float[totalParticles];
            phases              = new int[totalParticles];
            areaContribution    = new float[totalParticles];
            tearResistance      = new float[totalParticles];
            deformableTriangles = new int[topology.heFaces.Length * 3];

            // Create a particle for each vertex, and gather per-vertex data (area, valency)
            for (int i = 0; i < topology.heVertices.Length; i++)
            {
                Oni.Vertex vertex = topology.heVertices[i];

                // Get the particle's area contribution.
                areaContribution[i] = 0;
                foreach (Oni.Face face in topology.GetNeighbourFacesEnumerator(vertex))
                {
                    areaContribution[i] += topology.GetFaceArea(face) / 3;
                }

                // Calculate particle's valency:
                int valency = 0;
                foreach (Oni.HalfEdge edge in topology.GetNeighbourEdgesEnumerator(vertex))
                {
                    valency++;
                }
                maxVertexValency = Mathf.Max(maxVertexValency, valency);

                // Get the shortest neighbour edge, particle radius will be half of its length.
                float minEdgeLength = Single.MaxValue;
                foreach (Oni.HalfEdge edge in topology.GetNeighbourEdgesEnumerator(vertex))
                {
                    minEdgeLength = Mathf.Min(minEdgeLength, Vector3.Distance(topology.heVertices[topology.GetHalfEdgeStartVertex(edge)].position,
                                                                              topology.heVertices[edge.endVertex].position));
                }
                active[i]           = true;
                tearResistance[i]   = 1;
                invMasses[i]        = (areaContribution[i] > 0) ? (1.0f / (0.05f * areaContribution[i])) : 0;
                positions[i]        = vertex.position;
                restPositions[i]    = positions[i];
                restPositions[i][3] = 0;         // activate rest position.
                solidRadii[i]       = minEdgeLength * 0.5f;
                phases[i]           = Oni.MakePhase(gameObject.layer, selfCollisions?Oni.ParticlePhase.SelfCollide:0);

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating particles...", i / (float)topology.heVertices.Length));
                }
            }

            // Initialize basic data for pooled particles:
            for (int i = topology.heVertices.Length; i < pooledParticles; i++)
            {
                active[i]         = false;
                tearResistance[i] = 1;
                invMasses[i]      = 1.0f / 0.05f;
                solidRadii[i]     = 0.1f;
                phases[i]         = Oni.MakePhase(gameObject.layer, selfCollisions?Oni.ParticlePhase.SelfCollide:0);

                if (i % 100 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiRope: generating pooled particles...", i / (float)pooledParticles));
                }
            }

            // Generate deformable triangles:
            for (int i = 0; i < topology.heFaces.Length; i++)
            {
                Oni.Face face = topology.heFaces[i];

                Oni.HalfEdge e1 = topology.heHalfEdges[face.halfEdge];
                Oni.HalfEdge e2 = topology.heHalfEdges[e1.nextHalfEdge];
                Oni.HalfEdge e3 = topology.heHalfEdges[e2.nextHalfEdge];

                deformableTriangles[i * 3]     = e1.endVertex;
                deformableTriangles[i * 3 + 1] = e2.endVertex;
                deformableTriangles[i * 3 + 2] = e3.endVertex;

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating deformable geometry...", i / (float)topology.heFaces.Length));
                }
            }

            List <ObiMeshTopology.HEEdge> edges = topology.GetEdgeList();

            DistanceConstraints.Clear();
            ObiDistanceConstraintBatch distanceBatch = new ObiDistanceConstraintBatch(false, false);

            DistanceConstraints.AddBatch(distanceBatch);

            // Initialize constraint-halfedge map for cloth tearing purposes: TODO: reset on awake!!!
            distanceConstraintMap = new int[topology.heHalfEdges.Length];
            for (int i = 0; i < distanceConstraintMap.Length; i++)
            {
                distanceConstraintMap[i] = -1;
            }

            // Create distance springs:
            for (int i = 0; i < edges.Count; i++)
            {
                distanceConstraintMap[edges[i].halfEdgeIndex] = i;
                Oni.HalfEdge hedge       = topology.heHalfEdges[edges[i].halfEdgeIndex];
                Oni.Vertex   startVertex = topology.heVertices[topology.GetHalfEdgeStartVertex(hedge)];
                Oni.Vertex   endVertex   = topology.heVertices[hedge.endVertex];

                distanceBatch.AddConstraint(topology.GetHalfEdgeStartVertex(hedge), hedge.endVertex, Vector3.Distance(startVertex.position, endVertex.position), 1, 1);

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating structural constraints...", i / (float)topology.heHalfEdges.Length));
                }
            }

            // Create aerodynamic constraints:
            AerodynamicConstraints.Clear();
            ObiAerodynamicConstraintBatch aeroBatch = new ObiAerodynamicConstraintBatch(false, false);

            AerodynamicConstraints.AddBatch(aeroBatch);

            for (int i = 0; i < topology.heVertices.Length; i++)
            {
                aeroBatch.AddConstraint(i,
                                        areaContribution[i],
                                        AerodynamicConstraints.dragCoefficient,
                                        AerodynamicConstraints.liftCoefficient);

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating aerodynamic constraints...", i / (float)topology.heFaces.Length));
                }
            }

            BendingConstraints.Clear();
            ObiBendConstraintBatch bendBatch = new ObiBendConstraintBatch(false, false);

            BendingConstraints.AddBatch(bendBatch);

            bendConstraintOffsets = new int[topology.heVertices.Length + 1];

            Dictionary <int, int> cons = new Dictionary <int, int>();

            for (int i = 0; i < topology.heVertices.Length; i++)
            {
                Oni.Vertex vertex = topology.heVertices[i];

                bendConstraintOffsets[i] = bendBatch.ConstraintCount;

                foreach (Oni.Vertex n1 in topology.GetNeighbourVerticesEnumerator(vertex))
                {
                    float      cosBest = 0;
                    Oni.Vertex vBest   = n1;

                    foreach (Oni.Vertex n2 in topology.GetNeighbourVerticesEnumerator(vertex))
                    {
                        float cos = Vector3.Dot((n1.position - vertex.position).normalized,
                                                (n2.position - vertex.position).normalized);
                        if (cos < cosBest)
                        {
                            cosBest = cos;
                            vBest   = n2;
                        }
                    }

                    if (!cons.ContainsKey(vBest.index) || cons[vBest.index] != n1.index)
                    {
                        cons[n1.index] = vBest.index;

                        float[] restPos = new float[] { n1.position[0], n1.position[1], n1.position[2],
                                                        vBest.position[0], vBest.position[1], vBest.position[2],
                                                        vertex.position[0], vertex.position[1], vertex.position[2] };
                        float restBend = Oni.BendingConstraintRest(restPos);
                        bendBatch.AddConstraint(n1.index, vBest.index, vertex.index, restBend, 0, 1);
                    }
                }

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiCloth: adding bend constraints...", i / (float)sharedTopology.heVertices.Length));
                }
            }
            bendConstraintOffsets[topology.heVertices.Length] = bendBatch.ConstraintCount;

            //Initialize pin constraints:
            PinConstraints.Clear();
            ObiPinConstraintBatch pinBatch = new ObiPinConstraintBatch(false, false);

            PinConstraints.AddBatch(pinBatch);

            AddToSolver(null);

            initializing = false;
            initialized  = true;

            InitializeWithRegularMesh();

            pooledVertices = (int)((topology.heFaces.Length * 3 - sharedMesh.vertexCount) * tearCapacity);
        }
Пример #22
0
        private int AddParticles(int amount)
        {
            // get constraint batches:
            ObiDistanceConstraintBatch distanceBatch = rope.DistanceConstraints.GetFirstBatch();
            ObiBendConstraintBatch     bendingBatch  = rope.BendingConstraints.GetFirstBatch();

            amount = Mathf.Min(amount, rope.PooledParticles);

            // if no particles can be added, just return.
            if (amount == 0)
            {
                return(0);
            }

            // find current constraint and hot constraint:
            int constraint = rope.GetConstraintIndexAtNormalizedCoordinate(normalizedCoord);

            rope.DistanceConstraints.RemoveFromSolver(null);
            rope.BendingConstraints.RemoveFromSolver(null);

            // find indices of first N inactive particles. we'll need them to create new rope.
            // the first and last particle indices in this array will be the ones in the current constraint.
            int[] newParticleIndices = new int[amount + 2];
            for (int i = 0, j = 0; i < amount && j < rope.TotalParticles; ++j)
            {
                if (!rope.active[j])
                {
                    newParticleIndices[i + 1] = j;
                    rope.active[j]            = true;
                    rope.invMasses[j]         = 1.0f / ObiRope.DEFAULT_PARTICLE_MASS;
                    ++i;
                }
            }

            // TODO: closed curves have a different amount of bend constraints!

            Vector4[] zeroVelocity = new Vector4[] { Vector4.zero };
            Vector4[] refPosition1 = new Vector4[1];
            Vector4[] refPosition2 = new Vector4[1];

            if (direction)
            {
                // fill first and last indices of the new particles array with the ones in the current constraint:
                newParticleIndices[0] = distanceBatch.springIndices[constraint * 2];
                newParticleIndices[newParticleIndices.Length - 1] = distanceBatch.springIndices[constraint * 2 + 1];

                // update normalized coord:
                normalizedCoord = constraint / (float)(distanceBatch.ConstraintCount + amount);

                Oni.GetParticlePositions(rope.Solver.OniSolver, refPosition1, 1, rope.particleIndices[newParticleIndices[0]]);
                Oni.GetParticlePositions(rope.Solver.OniSolver, refPosition2, 1, rope.particleIndices[newParticleIndices[newParticleIndices.Length - 1]]);

                // update constraints:
                distanceBatch.SetParticleIndex(constraint, newParticleIndices[newParticleIndices.Length - 2], ObiDistanceConstraintBatch.DistanceIndexType.First, rope.Closed);
                bendingBatch.SetParticleIndex(constraint, newParticleIndices[newParticleIndices.Length - 2], ObiBendConstraintBatch.BendIndexType.First, rope.Closed);
                bendingBatch.SetParticleIndex(constraint - 1, newParticleIndices[1], ObiBendConstraintBatch.BendIndexType.Second, rope.Closed);

                // add constraints and particles:
                for (int i = 1; i < newParticleIndices.Length - 1; ++i)
                {
                    Vector4[] pos = new Vector4[] { refPosition1[0] + (refPosition2[0] - refPosition1[0]) * i / (float)(newParticleIndices.Length - 1) * 0.5f };
                    Oni.SetParticlePositions(rope.Solver.OniSolver, pos, 1, rope.particleIndices[newParticleIndices[i]]);
                    Oni.SetParticleVelocities(rope.Solver.OniSolver, zeroVelocity, 1, rope.particleIndices[newParticleIndices[i]]);

                    int newConstraintIndex = constraint + i - 1;
                    distanceBatch.InsertConstraint(newConstraintIndex, newParticleIndices[i - 1], newParticleIndices[i], rope.InterparticleDistance, 0, 0);
                    bendingBatch.InsertConstraint(newConstraintIndex, newParticleIndices[i - 1], newParticleIndices[i + 1], newParticleIndices[i], 0, 0, 0);
                }
            }
            else
            {
                // fill first and last indices of the new particles array with the ones in the current constraint:
                newParticleIndices[0] = distanceBatch.springIndices[constraint * 2 + 1];
                newParticleIndices[newParticleIndices.Length - 1] = distanceBatch.springIndices[constraint * 2];

                // update normalized coord:
                normalizedCoord = (constraint + amount) / (float)(distanceBatch.ConstraintCount + amount);

                Oni.GetParticlePositions(rope.Solver.OniSolver, refPosition1, 1, rope.particleIndices[newParticleIndices[0]]);
                Oni.GetParticlePositions(rope.Solver.OniSolver, refPosition2, 1, rope.particleIndices[newParticleIndices[newParticleIndices.Length - 1]]);

                // update constraints:
                distanceBatch.SetParticleIndex(constraint, newParticleIndices[newParticleIndices.Length - 2], ObiDistanceConstraintBatch.DistanceIndexType.Second, rope.Closed);
                bendingBatch.SetParticleIndex(constraint, newParticleIndices[1], ObiBendConstraintBatch.BendIndexType.First, rope.Closed);
                bendingBatch.SetParticleIndex(constraint - 1, newParticleIndices[newParticleIndices.Length - 2], ObiBendConstraintBatch.BendIndexType.Second, rope.Closed);

                // add constraints and particles:
                for (int i = 1; i < newParticleIndices.Length - 1; ++i)
                {
                    Vector4[] pos = new Vector4[] { refPosition1[0] + (refPosition2[0] - refPosition1[0]) * i / (float)(newParticleIndices.Length - 1) * 0.5f };
                    Oni.SetParticlePositions(rope.Solver.OniSolver, pos, 1, rope.particleIndices[newParticleIndices[i]]);
                    Oni.SetParticleVelocities(rope.Solver.OniSolver, zeroVelocity, 1, rope.particleIndices[newParticleIndices[i]]);

                    distanceBatch.InsertConstraint(constraint + 1, newParticleIndices[i], newParticleIndices[i - 1], rope.InterparticleDistance, 0, 0);
                    bendingBatch.InsertConstraint(constraint, newParticleIndices[i + 1], newParticleIndices[i - 1], newParticleIndices[i], 0, 0, 0);
                }
            }

            rope.DistanceConstraints.AddToSolver(null);
            rope.BendingConstraints.AddToSolver(null);
            rope.PushDataToSolver(ParticleData.ACTIVE_STATUS);

            rope.UsedParticles += amount;

            rope.RegenerateRestPositions();

            return(amount);
        }
Пример #23
0
        /**
         * Changes the length of the rope, adding or removing particles from its end as needed (as long as there are enough pooled particles
         * left). Since particles are added/removed to/from the end only, any existing particle data (masses, editor selection data) will
         * be preserved for existing particles when adding new ones.
         */
        public void ChangeLength(float newLength)
        {
            if (rope == null)
            {
                return;
            }

            // Clamp new length to sane limits:
            newLength = Mathf.Clamp(newLength, 0, (rope.TotalParticles - 1) * rope.InterparticleDistance);

            // find current extrusion constraint:
            int constraint = rope.GetConstraintIndexAtNormalizedCoordinate(normalizedCoord);

            // get constraint batch:
            ObiDistanceConstraintBatch distanceBatch = rope.DistanceConstraints.GetFirstBatch();

            // calculate the change in rope length:
            float lengthChange = newLength - rope.RestLength;

            // calculate length change of current constraint:
            float constraintLengthChange = Mathf.Clamp(lengthChange, -distanceBatch.restLengths[constraint], rope.InterparticleDistance - distanceBatch.restLengths[constraint]);

            distanceBatch.restLengths[constraint] += constraintLengthChange;
            lengthChange -= constraintLengthChange;

            // figure out how many particles we need to add (or remove) and what length will remain after that:
            int   particleChange  = lengthChange > 0 ? Mathf.CeilToInt(lengthChange / rope.InterparticleDistance): Mathf.FloorToInt(lengthChange / rope.InterparticleDistance);
            float remainingLenght = ObiUtils.Mod(lengthChange, rope.InterparticleDistance);

            if (particleChange > 0)
            {
                particleChange = AddParticles(particleChange);

                // we cannot add any particles, so extend the segment as much as possible.
                if (particleChange == 0)
                {
                    remainingLenght = rope.InterparticleDistance;
                }

                // Add remaining lenght to the new constraint:
                constraint = rope.GetConstraintIndexAtNormalizedCoordinate(normalizedCoord);
                distanceBatch.restLengths[constraint] = remainingLenght;
            }
            else if (particleChange < 0)
            {
                particleChange = RemoveParticles(-particleChange);

                // we cannot remove any particles, so reduce the segment as much as possible:
                if (particleChange == 0)
                {
                    remainingLenght = 0;
                }

                // Add remaining lenght to the new constraint:
                constraint = rope.GetConstraintIndexAtNormalizedCoordinate(normalizedCoord);
                distanceBatch.restLengths[constraint] = remainingLenght;
            }

            distanceBatch.PushDataToSolver(rope.DistanceConstraints);

            // Update rest length:
            rope.RecalculateLenght();
        }
Пример #24
0
        /**
         * Removes a certain amount of particles and constraints from the rope, at the point and direction specified:
         */
        private int RemoveParticles(int amount)
        {
            // get constraint batches:
            ObiDistanceConstraintBatch distanceBatch = rope.DistanceConstraints.GetFirstBatch();
            ObiBendConstraintBatch     bendingBatch  = rope.BendingConstraints.GetFirstBatch();

            amount = Mathf.Min(amount, rope.UsedParticles - 2);

            // find current constraint and hot constraint:
            int constraint    = Mathf.Max(rope.GetConstraintIndexAtNormalizedCoordinate(normalizedCoord), firstConstraintId);
            int hotConstraint = FindHotConstraint(distanceBatch, constraint, amount);

            amount = Mathf.Min(amount, Mathf.Abs(hotConstraint - constraint));

            // if no particles can be removed, just return.
            if (amount == 0)
            {
                return(0);
            }

            rope.DistanceConstraints.RemoveFromSolver(null);
            rope.BendingConstraints.RemoveFromSolver(null);

            if (direction)
            {
                // update normalized coord:
                normalizedCoord = constraint / (float)(distanceBatch.ConstraintCount - amount);

                // update constraints:
                distanceBatch.SetParticleIndex(constraint, distanceBatch.springIndices[hotConstraint * 2 + 1], ObiDistanceConstraintBatch.DistanceIndexType.Second, rope.Closed);
                bendingBatch.SetParticleIndex(constraint - 1, distanceBatch.springIndices[hotConstraint * 2 + 1], ObiBendConstraintBatch.BendIndexType.Second, rope.Closed);
                bendingBatch.SetParticleIndex(hotConstraint, distanceBatch.springIndices[constraint * 2], ObiBendConstraintBatch.BendIndexType.First, rope.Closed);

                // remove constraints and particles:
                for (int i = constraint + amount; i > constraint; --i)
                {
                    rope.active[distanceBatch.springIndices[i * 2]] = false;
                    distanceBatch.RemoveConstraint(i);
                    bendingBatch.RemoveConstraint(i - 1);
                }
            }
            else
            {
                // update normalized coord:
                normalizedCoord = (constraint - amount) / (float)(distanceBatch.ConstraintCount - amount);

                // update constraint:
                distanceBatch.SetParticleIndex(constraint, distanceBatch.springIndices[hotConstraint * 2], ObiDistanceConstraintBatch.DistanceIndexType.First, rope.Closed);
                bendingBatch.SetParticleIndex(constraint, distanceBatch.springIndices[hotConstraint * 2], ObiBendConstraintBatch.BendIndexType.First, rope.Closed);
                bendingBatch.SetParticleIndex(hotConstraint - 1, distanceBatch.springIndices[constraint * 2 + 1], ObiBendConstraintBatch.BendIndexType.Second, rope.Closed);

                // remove constraints and particles:
                for (int i = constraint - 1; i >= constraint - amount; --i)
                {
                    rope.active[distanceBatch.springIndices[i * 2 + 1]] = false;
                    distanceBatch.RemoveConstraint(i);
                    bendingBatch.RemoveConstraint(i);
                }
            }

            rope.DistanceConstraints.AddToSolver(null);
            rope.BendingConstraints.AddToSolver(null);
            rope.PushDataToSolver(ParticleData.ACTIVE_STATUS);

            rope.UsedParticles -= amount;

            rope.RegenerateRestPositions();

            return(amount);
        }
Пример #25
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 + pooledParticles;             //allocate extra particles to allow for lenght change and tearing.

            active         = new bool[totalParticles];
            positions      = new Vector3[totalParticles];
            velocities     = new Vector3[totalParticles];
            invMasses      = new float[totalParticles];
            principalRadii = new Vector3[totalParticles];
            phases         = new int[totalParticles];
            restPositions  = new Vector4[totalParticles];
            tearResistance = new float[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;
                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);
                tearResistance[i] = 1;
                colors[i]         = Color.white;

                if (i % 100 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiRope: generating particles...", i / (float)usedParticles));
                }
            }

            // Initialize basic data for pooled particles:
            for (int i = usedParticles; i < totalParticles; i++)
            {
                active[i]         = false;
                invMasses[i]      = 1.0f / DEFAULT_PARTICLE_MASS;
                principalRadii[i] = Vector3.one * radius;
                phases[i]         = Oni.MakePhase(1, selfCollisions?Oni.ParticlePhase.SelfCollide:0);
                tearResistance[i] = 1;
                colors[i]         = Color.white;

                if (i % 100 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiRope: generating particles...", i / (float)usedParticles));
                }
            }

            DistanceConstraints.Clear();
            ObiDistanceConstraintBatch distanceBatch = new ObiDistanceConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS);

            DistanceConstraints.AddBatch(distanceBatch);

            for (int i = 0; i < numSegments; i++)
            {
                distanceBatch.AddConstraint(i, (i + 1) % (ropePath.closed ? usedParticles:usedParticles + 1), interParticleDistance, 1, 1);

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiRope: generating structural constraints...", i / (float)numSegments));
                }
            }

            BendingConstraints.Clear();
            ObiBendConstraintBatch bendingBatch = new ObiBendConstraintBatch(false, false, MIN_YOUNG_MODULUS, MAX_YOUNG_MODULUS);

            BendingConstraints.AddBatch(bendingBatch);
            for (int i = 0; i < usedParticles - (closed?0:2); i++)
            {
                // rope bending constraints always try to keep it completely straight:
                bendingBatch.AddConstraint(i, (i + 2) % usedParticles, (i + 1) % usedParticles, 0, 0, 1);

                if (i % 500 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("ObiRope: adding bend constraints...", i / (float)usedParticles));
                }
            }

            // Initialize tether constraints:
            TetherConstraints.Clear();

            // Initialize pin constraints:
            PinConstraints.Clear();
            ObiPinConstraintBatch pinBatch = new ObiPinConstraintBatch(false, false, 0, MAX_YOUNG_MODULUS);

            PinConstraints.AddBatch(pinBatch);

            initializing = false;
            initialized  = true;

            RegenerateRestPositions();
        }
Пример #26
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--;
        }
Пример #27
0
        /**
         * Returns the amount of structural constraints in the rope.
         */
        public override int GetStructuralConstraintCount()
        {
            ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch();

            return(distanceBatch != null ? distanceBatch.ConstraintCount : 0);
        }