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