Exemple #1
0
 private SlaveVertex BindToFace(int slaveIndex,
                                MasterFace triangle,
                                Vector3 position,
                                Vector3 normalPoint,
                                Vector3 tangentPoint)
 {
     return(new SlaveVertex(slaveIndex,
                            triangle.index,
                            FindSkinBarycentricCoords(triangle, position, 24, 0.001f),
                            FindSkinBarycentricCoords(triangle, normalPoint, 16, 0.02f),
                            FindSkinBarycentricCoords(triangle, tangentPoint, 16, 0.02f)));
 }
Exemple #2
0
        private float GetFaceMappingError(MasterFace triangle,
                                          SlaveVertex vertex,
                                          Vector3 normal)
        {
            // initialize the error with barycentric coords error (larger the further away we are from the triangle's limits):
            float bary_error = GetBarycentricError(vertex.position.barycentricCoords) * barycentricWeight;
            float error      = bary_error;

            // calculate deviation of point normal from face normal, use it to weight normal barycentric error:
            float normal_deviation = Mathf.Clamp01(0.5f * (1.0f - Mathf.Abs(Vector3.Dot(triangle.faceNormal, normal))));

            error += normal_deviation * GetBarycentricError(vertex.normal.barycentricCoords) * normalAlignmentWeight;

            // make height relative to triangle size, and calculate error weight based on barycentric error:
            float height_val = vertex.position.height / triangle.size;
            float height_w   = 0.3f + 2.5f * bary_error;

            error += height_w * Mathf.Abs(height_val) * elevationWeight;

            return(error);
        }
Exemple #3
0
        private bool BindToFace(int slaveIndex,
                                MasterFace triangle,
                                Vector3 position,
                                Vector3 normalPoint,
                                Vector3 tangentPoint,
                                out SlaveVertex skinning)
        {
            skinning = SlaveVertex.empty;

            BarycentricPoint posBary;

            if (FindSkinBarycentricCoords(triangle, position, 24, 0.001f, out posBary))
            {
                BarycentricPoint normBary;
                BarycentricPoint tangentBary;
                FindSkinBarycentricCoords(triangle, normalPoint, 16, 0.005f, out normBary);
                FindSkinBarycentricCoords(triangle, tangentPoint, 16, 0.005f, out tangentBary);

                skinning = new SlaveVertex(slaveIndex, triangle.index, posBary, normBary, tangentBary);

                return(true);
            }
            return(false);
        }
Exemple #4
0
        public IEnumerator Bind()
        {
            Clear();

            if (master == null || slave == null)
            {
                yield break;
            }

            Vector3[] slavePositions = slave.vertices;
            Vector3[] slaveNormals   = slave.normals;
            Vector4[] slaveTangents  = slave.tangents;

            Matrix4x4 s2world       = m_SlaveTransform.GetMatrix4X4();
            Matrix4x4 s2worldNormal = s2world.inverse.transpose;

            // count active triangles normals:
            int activeDeformableTriangles = 0;

            for (int i = 0; i < master.deformableTriangles.Length; i += 3)
            {
                int t1 = master.deformableTriangles[i];
                int t2 = master.deformableTriangles[i + 1];
                int t3 = master.deformableTriangles[i + 2];

                if (t1 >= master.activeParticleCount || t2 >= master.activeParticleCount || t3 >= master.activeParticleCount)
                {
                    continue;
                }

                activeDeformableTriangles++;
            }


            // generate master triangle info:
            MasterFace[] masterFaces = new MasterFace[activeDeformableTriangles];
            int          count       = 0;

            for (int i = 0; i < master.deformableTriangles.Length; i += 3)
            {
                MasterFace face = new MasterFace();

                int t1 = master.deformableTriangles[i];
                int t2 = master.deformableTriangles[i + 1];
                int t3 = master.deformableTriangles[i + 2];

                if (t1 >= master.activeParticleCount || t2 >= master.activeParticleCount || t3 >= master.activeParticleCount)
                {
                    continue;
                }

                face.p1 = master.positions[t1];
                face.p2 = master.positions[t2];
                face.p3 = master.positions[t3];

                face.n1 = master.restNormals[t1];
                face.n2 = master.restNormals[t2];
                face.n3 = master.restNormals[t3];

                face.master = m_MasterChannels[t1] |
                              m_MasterChannels[t2] |
                              m_MasterChannels[t3];

                face.size = ((face.p1 - face.p2).magnitude +
                             (face.p1 - face.p3).magnitude +
                             (face.p2 - face.p3).magnitude) / 3.0f;

                face.faceNormal = Vector3.Cross(face.p2 - face.p1, face.p3 - face.p1).normalized;

                face.index = i;
                face.CacheBarycentricData();

                masterFaces[count++] = face;

                if (i % 10 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("Generating master faces...", count / (float)masterFaces.Length));
                }
            }

            // for each slave vertex, find the best fitting master triangle:
            for (int i = 0; i < slavePositions.Length; ++i)
            {
                // if vertex slave channel is deactivated, don´t skin it.
                if (m_SlaveChannels[i] == 0)
                {
                    continue;
                }

                // initialize best triangle error and index:
                float       bestError    = float.MaxValue;
                SlaveVertex bestSkinning = SlaveVertex.empty;

                Vector3 worldPos          = s2world.MultiplyPoint3x4(slavePositions[i]);
                Vector3 worldNormalDir    = s2worldNormal.MultiplyVector(slaveNormals[i]).normalized;
                Vector3 worldNormalPoint  = worldPos + worldNormalDir * 0.05f;
                Vector3 worldTangentPoint = worldPos + s2worldNormal.MultiplyVector(new Vector3(slaveTangents[i].x, slaveTangents[i].y, slaveTangents[i].z).normalized) * 0.05f;

                // find the best fitting master face:
                for (int j = 0; j < masterFaces.Length; ++j)
                {
                    MasterFace face = masterFaces[j];

                    // if the triangle master channel and the target slave channel do not overlap, skip it.
                    if ((face.master & m_SlaveChannels[i]) == 0)
                    {
                        continue;
                    }

                    // calculate skinning data for this face:
                    SlaveVertex skinning = BindToFace(i, face, worldPos, worldNormalPoint, worldTangentPoint);

                    // calculate mapping error for this triangle:
                    float error = GetFaceMappingError(face, skinning, worldNormalDir);

                    // if the error is less than the best, update it.
                    if (error < bestError)
                    {
                        bestError    = error;
                        bestSkinning = skinning;
                    }
                }

                // skin target vertex to the best source triangle found, if any:
                if (!bestSkinning.isEmpty)
                {
                    skinnedVertices.Add(bestSkinning);
                }

                if (i % 5 == 0)
                {
                    yield return(new CoroutineJob.ProgressInfo("Skinning slave vertices (" + i + "/" + slavePositions.Length + ")...", i / (float)slavePositions.Length));
                }
            }

            bound = true;

#if UNITY_EDITOR
            EditorUtility.SetDirty(this);
#endif
        }
Exemple #5
0
        /**
         * We need to find the barycentric coordinates of point such that the interpolated normal at that point passes trough our target position.
         *
         *            X
         *  \        /  /
         *   \------/--/
         *
         * This is necessary to ensure curvature changes in the surface affect skinned points away from the face plane.
         * To do so, we use an iterative method similar to Newton´s method for root finding:
         *
         * - Project the point on the triangle using an initial normal.
         * - Get interpolated normal at projection.
         * - Intersect line from point and interpolated normal with triangle, to find a new projection.
         * - Repeat.
         */
        BarycentricPoint FindSkinBarycentricCoords(MasterFace triangle,
                                                   Vector3 position,
                                                   int max_iterations,
                                                   float min_convergence)
        {
            BarycentricPoint barycentricPoint = BarycentricPoint.zero;

            // start at center of triangle:
            Vector3 trusted_bary = Vector3.one / 3.0f;
            Vector3 temp_normal  = ObiUtils.BarycentricInterpolation(triangle.n1,
                                                                     triangle.n2,
                                                                     triangle.n3,
                                                                     trusted_bary);

            int   it          = 0;
            float trust       = 1.0f;
            float convergence = float.MaxValue;

            while (it++ < max_iterations)
            {
                Vector3 point;
                if (!Obi.ObiUtils.LinePlaneIntersection(triangle.p1, triangle.faceNormal, position, temp_normal, out point))
                {
                    break;
                }

                // get bary coords at intersection:
                Vector3 bary = Vector3.zero;
                if (!triangle.BarycentricCoords(point, ref bary))
                {
                    break;
                }

                // calculate error:
                Vector3 error = bary - trusted_bary;     // distance from current estimation to last trusted estimation.
                convergence = Vector3.Dot(error, error); // get a single convergence value.

                // weighted sum of bary coords:
                trusted_bary = (1.0f - trust) * trusted_bary + trust * bary;

                // update normal
                temp_normal = ObiUtils.BarycentricInterpolation(triangle.n1,
                                                                triangle.n2,
                                                                triangle.n3,
                                                                trusted_bary);

                if (convergence < min_convergence)
                {
                    break;
                }

                trust *= 0.8f;
            }

            Vector3 pos_on_tri = trusted_bary[0] * triangle.p1 +
                                 trusted_bary[1] * triangle.p2 +
                                 trusted_bary[2] * triangle.p3;

            float height = Vector3.Dot(position - pos_on_tri, temp_normal);

            barycentricPoint.barycentricCoords = trusted_bary;
            barycentricPoint.height            = height;

            return(barycentricPoint);
        }