public void AddBatch(ObiSkinConstraintBatch batch)
 {
     if (batch != null && batch.GetConstraintType() == GetConstraintType())
     {
         batches.Add(batch);
     }
 }
Пример #2
0
        public override void OnSolverStepBegin()
        {
            // apparently checking whether the actor is enabled or not doesn't take a despreciable amount of time.
            bool actorEnabled = this.enabled;

            // manually update animator (before particle physics):

            // TODO: update root bone here if animator is deactiavted.
            if (animatorController != null)
            {
                animatorController.UpdateAnimation();
            }

            Vector4[] simulationPosition = { Vector4.zero };

            // build local to simulation space transform:
            Matrix4x4 l2sTransform;

            if (Solver.simulateInLocalSpace)
            {
                l2sTransform = Solver.transform.worldToLocalMatrix * ActorLocalToWorldMatrix;
            }
            else
            {
                l2sTransform = ActorLocalToWorldMatrix;
            }

            //Matrix4x4 delta = Solver.transform.worldToLocalMatrix * Solver.LastTransform;

            ObiSkinConstraintBatch skinConstraints = SkinConstraints.GetFirstBatch();

            // transform fixed particles:
            for (int i = 0; i < particleIndices.Length; i++)
            {
                Vector3 solverSpaceBone = l2sTransform.MultiplyPoint3x4(transform.InverseTransformPoint(bones[i].position));

                if (!actorEnabled || invMasses[i] == 0)
                {
                    simulationPosition[0] = solverSpaceBone;
                    Oni.SetParticlePositions(solver.OniSolver, simulationPosition, 1, particleIndices[i]);
                }                /*else if (Solver.simulateInLocalSpace){
                                  *
                                  *     Oni.GetParticlePositions(solver.OniSolver,simulationPosition,1,particleIndices[i]);
                                  *     simulationPosition[0] = Vector3.Lerp(simulationPosition[0],delta.MultiplyPoint3x4(simulationPosition[0]),worldVelocityScale);
                                  *     Oni.SetParticlePositions(solver.OniSolver,simulationPosition,1,particleIndices[i]);
                                  *
                                  * }*/

                skinConstraints.skinPoints[i] = solverSpaceBone;
            }

            skinConstraints.PushDataToSolver(SkinConstraints);
        }
Пример #3
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;
        }
Пример #4
0
        private void DrawSkinProperty(int property)
        {
            Material mat = Resources.Load <Material>("EditorLines");

            if (mat.SetPass(0) && constraints.GetBatches().Count > 0)
            {
                GL.PushMatrix();
                GL.Begin(GL.LINES);

                ObiSkinConstraintBatch batch = ((ObiSkinConstraintBatch)constraints.GetBatches()[0]);

                Matrix4x4 s2wTransform = Matrix4x4.identity;
                if (constraints.InSolver)
                {
                    s2wTransform = constraints.Actor.Solver.transform.localToWorldMatrix;
                }

                // get up to date constraint data:
                batch.PullDataFromSolver(constraints);

                foreach (int i in batch.ActiveConstraints)
                {
                    int particleIndex = batch.skinIndices[i];

                    if (particleIndex >= 0 && particleIndex < ObiParticleActorEditor.selectionStatus.Length &&
                        ObiParticleActorEditor.selectionStatus[particleIndex] &&
                        ObiParticleActorEditor.IsParticleVisible(particleIndex))
                    {
                        float   radius          = batch.skinRadiiBackstop[i * 3];
                        float   collisionRadius = batch.skinRadiiBackstop[i * 3 + 1];
                        float   backstop        = batch.skinRadiiBackstop[i * 3 + 2];
                        Vector3 point           = batch.GetSkinPosition(i);
                        Vector3 normal          = batch.GetSkinNormal(i);

                        if (!constraints.InSolver)
                        {
                            point  = constraints.Actor.ActorLocalToWorldMatrix.MultiplyPoint3x4(point);
                            normal = constraints.Actor.ActorLocalToWorldMatrix.MultiplyVector(normal);
                        }
                        else if (constraints.Actor.Solver.simulateInLocalSpace)
                        {
                            point  = s2wTransform.MultiplyPoint3x4(point);
                            normal = s2wTransform.MultiplyVector(normal);
                        }

                        switch (property)
                        {
                        case ObiClothEditor.ClothParticleProperty.SkinRadius:
                            GL.Color(Color.blue);
                            GL.Vertex(point);
                            GL.Color(Color.blue);
                            GL.Vertex(point + normal * radius);
                            break;

                        case ObiClothEditor.ClothParticleProperty.SkinBackstop:
                            GL.Color(Color.yellow);
                            GL.Vertex(point);
                            GL.Color(Color.yellow);
                            GL.Vertex(point - normal * backstop);
                            break;

                        case ObiClothEditor.ClothParticleProperty.SkinBackstopRadius:
                            GL.Color(Color.red);
                            GL.Vertex(point - normal * backstop);
                            GL.Color(Color.red);
                            GL.Vertex(point - normal * (collisionRadius + backstop));
                            break;
                        }
                    }
                }

                GL.End();
                GL.PopMatrix();
            }
        }
 public void RemoveBatch(ObiSkinConstraintBatch batch)
 {
     batches.Remove(batch);
 }
Пример #6
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();
            }
        }
Пример #7
0
        public void OnSceneGUI()
        {
            if (Event.current.type != EventType.Repaint || !ObiParticleActorEditor.editMode)
            {
                return;
            }

            // Get the particle actor editor to retrieve selected particles:
            ObiParticleActorEditor[] editors = (ObiParticleActorEditor[])Resources.FindObjectsOfTypeAll(typeof(ObiParticleActorEditor));

            // If there's any particle actor editor active, we can show pin constraints:
            if (editors.Length > 0 && (editors[0].currentProperty == ObiClothEditor.ClothParticleProperty.SkinRadius ||
                                       editors[0].currentProperty == ObiClothEditor.ClothParticleProperty.SkinBackstopRadius ||
                                       editors[0].currentProperty == ObiClothEditor.ClothParticleProperty.SkinBackstop))
            {
                Material mat = ObiEditorUtils.GetRequiredEditorResource("Obi/EditorLines.mat") as Material;

                if (mat.SetPass(0) && constraints.GetBatches().Count > 0)
                {
                    GL.PushMatrix();
                    GL.Begin(GL.LINES);

                    ObiSkinConstraintBatch batch = ((ObiSkinConstraintBatch)constraints.GetBatches()[0]);

                    Matrix4x4 s2wTransform = constraints.Actor.Solver.transform.localToWorldMatrix;

                    // get up to date constraint data:
                    batch.PullDataFromSolver(constraints);

                    foreach (int i in batch.ActiveConstraints)
                    {
                        int particleIndex = batch.skinIndices[i];

                        bool[] stat = ObiParticleActorEditor.selectionStatus;

                        if (particleIndex >= 0 && particleIndex < ObiParticleActorEditor.selectionStatus.Length &&
                            ObiParticleActorEditor.selectionStatus[particleIndex])
                        {
                            float   radius          = batch.skinRadiiBackstop[i * 3];
                            float   collisionRadius = batch.skinRadiiBackstop[i * 3 + 1];
                            float   backstop        = batch.skinRadiiBackstop[i * 3 + 2];
                            Vector3 point           = batch.GetSkinPosition(i);
                            Vector3 normal          = batch.GetSkinNormal(i);

                            if (!constraints.InSolver)
                            {
                                point  = constraints.transform.TransformPoint(point);
                                normal = constraints.transform.TransformDirection(normal);
                            }
                            else if (constraints.Actor.Solver.simulateInLocalSpace)
                            {
                                point  = s2wTransform.MultiplyPoint3x4(point);
                                normal = s2wTransform.MultiplyVector(normal);
                            }

                            // Detailed visual feedback only if few particles are selected, to avoid clutter.
                            if (ObiParticleActorEditor.SelectedParticleCount < 5)
                            {
                                Handles.color = new Color(0, 0, 1, 0.2f);
                                Handles.SphereCap(0, point, Quaternion.identity, radius * 2);
                                Handles.color = new Color(1, 0, 0, 0.2f);
                                Handles.SphereCap(0, point - normal * (collisionRadius + backstop), Quaternion.identity, collisionRadius * 2);

                                // If more than 4 particles are selected, use lines.
                            }
                            else
                            {
                                GL.Color(Color.red);
                                GL.Vertex(point);
                                GL.Color(Color.red);
                                GL.Vertex(point - normal * backstop);

                                GL.Color(Color.blue);
                                GL.Vertex(point - normal * backstop);
                                GL.Color(Color.blue);
                                GL.Vertex(point + normal * radius);
                            }
                        }
                    }

                    GL.End();
                    GL.PopMatrix();
                }
            }
        }