//-------------------------------------------------------------------------------------------------- // Compute barycentric coordinates (area coordinates) (u, v, w) for // point p with respect to triangle (t0, t1, t2) // These can be used as weights for interpolating scalar values across the triangle // Based on section 3.4 in "Real Time collision detection" by Christer Ericson //-------------------------------------------------------------------------------------------------- cvf::Vec3d GeometryTools::barycentricCoords(const cvf::Vec3d& t0, const cvf::Vec3d& t1, const cvf::Vec3d& t2, const cvf::Vec3d& p) { // Unnormalized triangle normal cvf::Vec3d m = (t1 - t0 ^ t2 - t0); // Absolute components for determining projection plane int X = 0, Y = 1; int Z = findClosestAxis(m); switch (Z) { case 0: X = 1; Y = 2; break; // x is largest, project to the yz plane case 1: X = 0; Y = 2; break; // y is largest, project to the xz plane case 2: X = 0; Y = 1; break; // z is largest, project to the xy plane } // Compute areas in plane of largest projection // Nominators and one-over-denominator for u and v ratios double nu, nv, ood; nu = TriArea2D(p[X], p[Y], t1[X], t1[Y], t2[X], t2[Y]); // Area of PBC in yz plane nv = TriArea2D(p[X], p[Y], t2[X], t2[Y], t0[X], t0[Y]); // Area of PCA in yz plane ood = 1.0f / m[Z]; // 1/(2*area of ABC in yz plane) if (Z == 1) ood = -ood; // For some reason not explained // Normalize m[0] = nu * ood; m[1] = nv * ood; m[2] = 1.0f - m[0] - m[1]; return m; }
float3 Triangle::BarycentricUVW(const float3 &point) const { // Implementation from Christer Ericson's Real-Time Collision Detection, pp. 51-52. // Unnormalized triangle normal. float3 m = Cross(b-a, c-a); // Nominators and one-over-denominator for u and v ratios. float nu, nv, ood; // Absolute components for determining projection plane. float x = Abs(m.x); float y = Abs(m.y); float z = Abs(m.z); if (x >= y && x >= z) { // Project to the yz plane. nu = TriArea2D(point.y, point.z, b.y, b.z, c.y, c.z); // Area of PBC in yz-plane. nv = TriArea2D(point.y, point.z, c.y, c.z, a.y, a.z); // Area OF PCA in yz-plane. ood = 1.f / m.x; // 1 / (2*area of ABC in yz plane) } else if (y >= z) // Note: The book has a redundant 'if (y >= x)' comparison { // y is largest, project to the xz-plane. nu = TriArea2D(point.x, point.z, b.x, b.z, c.x, c.z); nv = TriArea2D(point.x, point.z, c.x, c.z, a.x, a.z); ood = 1.f / -m.y; } else // z is largest, project to the xy-plane. { nu = TriArea2D(point.x, point.y, b.x, b.y, c.x, c.y); nv = TriArea2D(point.x, point.y, c.x, c.y, a.x, a.y); ood = 1.f / m.z; } float u = nu * ood; float v = nv * ood; float w = 1.f - u - v; return float3(u,v,w); #if 0 // TODO: This version should be more SIMD-friendly, but for some reason, it doesn't return good values for all points inside the triangle. float3 v0 = b - a; float3 v1 = c - a; float3 v2 = point - a; float d00 = Dot(v0, v0); float d01 = Dot(v0, v1); float d02 = Dot(v0, v2); float d11 = Dot(v1, v1); float d12 = Dot(v1, v2); float denom = 1.f / (d00 * d11 - d01 * d01); float v = (d11 * d02 - d01 * d12) * denom; float w = (d00 * d12 - d01 * d02) * denom; float u = 1.0f - v - w; return float3(u, v, w); #endif }