// THIS IS ONLY A PORTION OF THE FILE // WILL NOT COMPILE OUT OF THE BOX! void OrthogonalizeTangent (TangentInfo& tangentInfo, Vector3f normalf, Vector4f& outputTangent) { TangentInfo::Vector3d normal = { normalf.x, normalf.y, normalf.z }; TangentInfo::Vector3d tangent = tangentInfo.tangent; TangentInfo::Vector3d binormal = tangentInfo.binormal; // Try Gram-Schmidt orthonormalize. // This might fail in degenerate cases which we all handle seperately. double NdotT = TangentInfo::Vector3d::Dot (normal, tangent); TangentInfo::Vector3d newTangent = { tangent.x - NdotT * normal.x, tangent.y - NdotT * normal.y, tangent.z - NdotT * normal.z }; double magT = TangentInfo::Vector3d::Magnitude (newTangent); newTangent = TangentInfo::Vector3d::Normalize (newTangent, magT); double NdotB = TangentInfo::Vector3d::Dot (normal, binormal); double TdotB = TangentInfo::Vector3d::Dot (newTangent, binormal) * magT; TangentInfo::Vector3d newBinormal = { binormal.x - NdotB * normal.x - TdotB * newTangent.x, binormal.y - NdotB * normal.y - TdotB * newTangent.y, binormal.z - NdotB * normal.z - TdotB * newTangent.z }; double magB = TangentInfo::Vector3d::Magnitude (newBinormal); newBinormal = TangentInfo::Vector3d::Normalize (newBinormal, magB); Vector3f tangentf = Vector3f( (float)newTangent.x, (float)newTangent.y, (float)newTangent.z ); Vector3f binormalf = Vector3f( (float)newBinormal.x, (float)newBinormal.y, (float)newBinormal.z ); const double kNormalizeEpsilon = 1e-6; if (magT <= kNormalizeEpsilon || magB <= kNormalizeEpsilon) { // Create tangent basis from scratch - we can safely use Vector3f here - no computations ;-) Vector3f axis1, axis2; float dpXN = Abs( Dot( Vector3f::xAxis, normalf ) ); float dpYN = Abs( Dot( Vector3f::yAxis, normalf ) ); float dpZN = Abs( Dot( Vector3f::zAxis, normalf ) ); if ( dpXN <= dpYN && dpXN <= dpZN ) { axis1 = Vector3f::xAxis; if( dpYN <= dpZN ) axis2 = Vector3f::yAxis; else axis2 = Vector3f::zAxis; } else if ( dpYN <= dpXN && dpYN <= dpZN ) { axis1 = Vector3f::yAxis; if( dpXN <= dpZN ) axis2 = Vector3f::xAxis; else axis2 = Vector3f::zAxis; } else { axis1 = Vector3f::zAxis; if( dpXN <= dpYN ) axis2 = Vector3f::xAxis; else axis2 = Vector3f::yAxis; } tangentf = axis1 - Dot (normalf, axis1) * normalf; binormalf = axis2 - Dot (normalf, axis2) * normalf - Dot(tangentf,axis2)*NormalizeSafe(tangentf); tangentf = NormalizeSafe(tangentf); binormalf = NormalizeSafe(binormalf); } outputTangent = Vector4f( tangentf.x, tangentf.y, tangentf.z, 0.0F); float dp = Dot (Cross (normalf, tangentf), binormalf); if ( dp > 0.0F) outputTangent.w = 1.0F; else outputTangent.w = -1.0F; } void CreateTangentSpaceTangentsUnsplit (const ImportMesh& mesh, Vector4f* outTangents, float normalSmoothingAngle) { float smoothingAngle = max(89.0F, normalSmoothingAngle); const int vertexCount = mesh.vertices.size(); const int faceCount = mesh.polygonSizes.size(); const int indexCount = mesh.polygons.size(); const Vector3f* vertices = &mesh.vertices[0]; const Vector3f* normals = &mesh.normals[0]; const Vector2f* tex = &mesh.uvs[0][0]; const UInt32* indices = &mesh.polygons[0]; // Calculate the tangent and binormal vectors of each face, // also compute face start offsets in the index buffer vector<TangentInfo> faceTangents(indexCount); vector<UInt32> faceOffsets(faceCount); for (int i = 0, idx = 0; i < faceCount; ++i) { const int fs = mesh.polygonSizes[i]; ComputeTriangleTangentBasis (vertices, tex + idx, indices + idx, &faceTangents[idx]); ///@TODO: is this good enough? if (fs == 4) ComputeTriangleTangentBasis (vertices, tex + idx+1, indices + idx+1, &faceTangents[idx+1]); faceOffsets[i] = idx; idx += fs; } // Average the tangents/binormals but only if they are within the smoothing angle vector<TangentInfo> avgTangents(indexCount); ConnectedMesh meshConnection (vertexCount, indices, mesh.polygons.size(), &mesh.polygonSizes[0], faceCount); float hardDot = cos (Deg2Rad (smoothingAngle)) - 0.001F; // NOTE: subtract is for more consistent results across platforms // Go through all faces, average with the connected faces for (int fi = 0, idx = 0; fi < faceCount; ++fi) { const int fs = mesh.polygonSizes[fi]; for (int e = 0; e < fs; ++e) { TangentInfo::Vector3d faceTangent = TangentInfo::Vector3d::Normalize (faceTangents[idx+e].tangent); TangentInfo::Vector3d faceBinormal = TangentInfo::Vector3d::Normalize (faceTangents[idx+e].binormal); TangentInfo::Vector3d averagedTangent = {0,0,0}; TangentInfo::Vector3d averagedBinormal = {0,0,0}; ConnectedMesh::Vertex connected = meshConnection.vertices[indices[idx + e]]; for (int i=0;i<connected.faceCount;i++) { int faceI = -1; const UInt32 connectedFaceOffset = faceOffsets[connected.faces[i]]; const int connectedFaceSize = mesh.polygonSizes[connected.faces[i]]; for (int k = 0; k < connectedFaceSize; ++k) { if (indices[connectedFaceOffset+k] == indices[idx+e]) { faceI = connectedFaceOffset+k; break; } } Assert(faceI != -1); TangentInfo::Vector3d connectedTangent = faceTangents[faceI].tangent; TangentInfo::Vector3d connectedBinormal = faceTangents[faceI].binormal; if ( TangentInfo::Vector3d::Dot(TangentInfo::Vector3d::Normalize(connectedTangent), faceTangent) > (double)hardDot ) { averagedTangent.x += connectedTangent.x; averagedTangent.y += connectedTangent.y; averagedTangent.z += connectedTangent.z; } if ( TangentInfo::Vector3d::Dot(TangentInfo::Vector3d::Normalize(connectedBinormal), faceBinormal) > (double)hardDot ) { averagedBinormal.x += connectedBinormal.x; averagedBinormal.y += connectedBinormal.y; averagedBinormal.z += connectedBinormal.z; } } avgTangents[idx+e].tangent = TangentInfo::Vector3d::Normalize (averagedTangent); avgTangents[idx+e].binormal = TangentInfo::Vector3d::Normalize (averagedBinormal); } idx += fs; } // Orthogonalize the tangents/binormals and output them into the tangents array for (int i=0;i<avgTangents.size();i++) { OrthogonalizeTangent (avgTangents[i], normals[i], outTangents[i]); } } // THIS IS ONLY A PORTION OF THE FILE // WILL NOT COMPILE OUT OF THE BOX!