void ParametricSurface<Real>::ComputePrincipalCurvatureInfo (Real u, Real v, Real& curv0, Real& curv1, Vector3<Real>& dir0, Vector3<Real>& dir1) { // Tangents: T0 = (x_u,y_u,z_u), T1 = (x_v,y_v,z_v) // Normal: N = Cross(T0,T1)/Length(Cross(T0,T1)) // Metric Tensor: G = +- -+ // | Dot(T0,T0) Dot(T0,T1) | // | Dot(T1,T0) Dot(T1,T1) | // +- -+ // // Curvature Tensor: B = +- -+ // | -Dot(N,T0_u) -Dot(N,T0_v) | // | -Dot(N,T1_u) -Dot(N,T1_v) | // +- -+ // // Principal curvatures k are the generalized eigenvalues of // // Bw = kGw // // If k is a curvature and w=(a,b) is the corresponding solution to // Bw = kGw, then the principal direction as a 3D vector is d = a*U+b*V. // // Let k1 and k2 be the principal curvatures. The mean curvature // is (k1+k2)/2 and the Gaussian curvature is k1*k2. // Compute derivatives. Vector3<Real> derU = PU(u,v); Vector3<Real> derV = PV(u,v); Vector3<Real> derUU = PUU(u,v); Vector3<Real> derUV = PUV(u,v); Vector3<Real> derVV = PVV(u,v); // Compute the metric tensor. Matrix2<Real> metricTensor; metricTensor[0][0] = derU.Dot(derU); metricTensor[0][1] = derU.Dot(derV); metricTensor[1][0] = metricTensor[0][1]; metricTensor[1][1] = derV.Dot(derV); // Compute the curvature tensor. Vector3<Real> normal = derU.UnitCross(derV); Matrix2<Real> curvatureTensor; curvatureTensor[0][0] = -normal.Dot(derUU); curvatureTensor[0][1] = -normal.Dot(derUV); curvatureTensor[1][0] = curvatureTensor[0][1]; curvatureTensor[1][1] = -normal.Dot(derVV); // Characteristic polynomial is 0 = det(B-kG) = c2*k^2+c1*k+c0. Real c0 = curvatureTensor.Determinant(); Real c1 = ((Real)2)*curvatureTensor[0][1]* metricTensor[0][1] - curvatureTensor[0][0]*metricTensor[1][1] - curvatureTensor[1][1]*metricTensor[0][0]; Real c2 = metricTensor.Determinant(); // Principal curvatures are roots of characteristic polynomial. Real temp = Math<Real>::Sqrt(Math<Real>::FAbs(c1*c1 - ((Real)4)*c0*c2)); Real mult = ((Real)0.5)/c2; curv0 = -mult*(c1+temp); curv1 = mult*(-c1+temp); // Principal directions are solutions to (B-kG)w = 0, // w1 = (b12-k1*g12,-(b11-k1*g11)) OR (b22-k1*g22,-(b12-k1*g12)). Real a0 = curvatureTensor[0][1] - curv0*metricTensor[0][1]; Real a1 = curv0*metricTensor[0][0] - curvatureTensor[0][0]; Real length = Math<Real>::Sqrt(a0*a0 + a1*a1); if (length >= Math<Real>::ZERO_TOLERANCE) { dir0 = a0*derU + a1*derV; } else { a0 = curvatureTensor[1][1] - curv0*metricTensor[1][1]; a1 = curv0*metricTensor[0][1] - curvatureTensor[0][1]; length = Math<Real>::Sqrt(a0*a0 + a1*a1); if (length >= Math<Real>::ZERO_TOLERANCE) { dir0 = a0*derU + a1*derV; } else { // Umbilic (surface is locally sphere, any direction principal). dir0 = derU; } } dir0.Normalize(); // Second tangent is cross product of first tangent and normal. dir1 = dir0.Cross(normal); }
Void TriangleMesh::UpdateTangentsFromGeometry( GPUDeferredContext * pContext ) { Assert( m_pIL != NULL && m_pVB != NULL ); Assert( m_pIL->IsBound() && m_pVB->IsBound() ); Bool bHasNormals = m_pIL->HasField( GPUINPUTFIELD_SEMANTIC_NORMAL, 0 ); if ( !bHasNormals ) return; Bool bHasTangents = m_pIL->HasField( GPUINPUTFIELD_SEMANTIC_TANGENT, 0 ); Bool bHasBiNormals = m_pIL->HasField( GPUINPUTFIELD_SEMANTIC_BINORMAL, 0 ); if ( !bHasTangents && !bHasBiNormals ) return; // Begin update Byte * pFirstVertex = NULL; if ( m_pVB->CanUpdate() ) { Assert( m_pVB->HasCPUData() ); pFirstVertex = m_pVB->GetData( m_iVertexOffset ); } else { Assert( m_pVB->CanLock() ); UInt iByteSize = 0; pFirstVertex = (Byte*)( m_pVB->Lock( GPURESOURCE_LOCK_WRITE, 0, &iByteSize, pContext ) ); Assert( iByteSize == m_pVB->GetSize() ); } // Update UInt iVertexSize = m_pVB->GetElementSize(); UInt iOffset, iSize; const Byte * arrPositions = NULL; const Byte * arrNormals = NULL; Byte * arrTangents = NULL; Byte * arrBiNormals = NULL; m_pIL->GetFieldRange( &iOffset, &iSize, GPUINPUTFIELD_SEMANTIC_POSITION, 0 ); arrPositions = ( pFirstVertex + iOffset ); m_pIL->GetFieldRange( &iOffset, &iSize, GPUINPUTFIELD_SEMANTIC_NORMAL, 0 ); arrNormals = ( pFirstVertex + iOffset ); if ( bHasTangents ) { m_pIL->GetFieldRange( &iOffset, &iSize, GPUINPUTFIELD_SEMANTIC_TANGENT, 0 ); arrTangents = ( pFirstVertex + iOffset ); } if ( bHasBiNormals ) { m_pIL->GetFieldRange( &iOffset, &iSize, GPUINPUTFIELD_SEMANTIC_BINORMAL, 0 ); arrBiNormals = ( pFirstVertex + iOffset ); } UInt i, iTriangleCount = GetTriangleCount(); // Start using temp storage RenderingFn->SelectMemory( TEXT("Scratch") ); Matrix4 * arrDerivateNormals = New Matrix4[m_iVertexCount]; // Compute derivate normals (dN/dX) _ComputeDerivateNormals( arrDerivateNormals, m_iVertexCount, iTriangleCount, arrPositions, arrNormals, iVertexSize ); // Perform update const Vector4 * pNormal; Vector4 *pTangent, *pBiNormal; Vector4 vU, vV; Scalar fS01, fS10, fSAvg; Matrix2 matS; Scalar fTrace, fDet, fDiscr, fRootDiscr; Scalar fMinCurvature; //, fMaxCurvature; Vector2 vEigen0, vEigen1; Vector4 vTangent, vBinormal; for( i = 0; i < m_iVertexCount; ++i ) { iOffset = ( i * iVertexSize ); pNormal = (const Vector4 *)( arrNormals + iOffset ); // Compute J = [U V] such that (U,V,N) is orthonormal Vector4::MakeComplementBasis( vU, vV, *pNormal ); // Compute shape matrix S = tJ * dN/dX * J, enforce symmetry fS01 = ( vU * (arrDerivateNormals[i] * vV) ); fS10 = ( vV * (arrDerivateNormals[i] * vU) ); fSAvg = ( fS01 + fS10 ) * 0.5f; matS.m00 = ( vU * (arrDerivateNormals[i] * vU) ); matS.m01 = fSAvg; matS.m10 = fSAvg; matS.m11 = ( vV * (arrDerivateNormals[i] * vV) ); // Eigenvalues of S are principal curvatures (k0 and k1) fTrace = ( matS.m00 + matS.m11 ); fDet = matS.Determinant(); fDiscr = ( fTrace * fTrace - 4.0f * fDet ); fRootDiscr = MathFn->Sqrt( MathFn->Abs(fDiscr) ); fMinCurvature = (fTrace - fRootDiscr) * 0.5f; //fMaxCurvature = (fTrace + fRootDiscr) * 0.5f; // Eigenvectors of S (W0 and W1) vEigen0.X = matS.m01; vEigen0.Y = fMinCurvature - matS.m00; vEigen1.X = fMinCurvature - matS.m11; vEigen1.Y = matS.m10; // Deduce principal directions (S*W=k*W => k=J*W) and assign them as tangent & binormal if ( vEigen0.NormSqr() >= vEigen1.NormSqr() ) { vEigen0.Normalize(); vTangent = ( (vU * vEigen0.X) + (vV * vEigen0.Y) ); vBinormal = ( (*pNormal) ^ vTangent ); } else { vEigen1.Normalize(); vTangent = ( (vU * vEigen1.X) + (vV * vEigen1.Y) ); vBinormal = ( (*pNormal) ^ vTangent ); } // Update if ( bHasTangents ) { pTangent = (Vector4*)( arrTangents + iOffset ); *pTangent = vTangent; } if ( bHasBiNormals ) { pBiNormal = (Vector4*)( arrBiNormals + iOffset ); *pBiNormal = vBinormal; } } // End using temp storage DeleteA( arrDerivateNormals ); RenderingFn->UnSelectMemory(); // End update if ( m_pVB->CanUpdate() ) m_pVB->Update( 0, INVALID_OFFSET, pContext ); else m_pVB->UnLock( pContext ); }