/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include namespace NvCloth { namespace { const float Tolerance = 0.0001f; } bool TangentSpaceHelper::CalculateNormals( const AZStd::vector& vertices, const AZStd::vector& indices, AZStd::vector& outNormals) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth); if ((indices.size() % 3) != 0) { AZ_Error("TangentSpaceHelper", false, "Size of list of indices (%zu) is not a multiple of 3.", indices.size()); return false; } const size_t triangleCount = indices.size() / 3; const size_t vertexCount = vertices.size(); // Reset results outNormals.resize(vertexCount, AZ::Vector3::CreateZero()); // calculate the normals per triangle for (size_t i = 0; i < triangleCount; ++i) { TriangleIndices triangleIndices; TrianglePositions trianglePositions; TriangleEdges triangleEdges; GetTriangleData( i, indices, vertices, triangleIndices, trianglePositions, triangleEdges); AZ::Vector3 normal; ComputeNormal(triangleEdges, normal); // distribute the normals to the vertices. for (AZ::u32 vertexIndexInTriangle = 0; vertexIndexInTriangle < 3; ++vertexIndexInTriangle) { const float weight = GetVertexWeightInTriangle(vertexIndexInTriangle, trianglePositions); const SimIndexType vertexIndex = triangleIndices[vertexIndexInTriangle]; outNormals[vertexIndex] += normal * AZStd::max(weight, Tolerance); } } // adjust the normals per vertex for (auto& outNormal : outNormals) { outNormal.NormalizeSafe(Tolerance); // Safety check for situations where simulation gets out of control. // Particles' positions can have huge floating point values that // could lead to non-finite numbers when calculating tangent spaces. if (!outNormal.IsFinite()) { outNormal = AZ::Vector3::CreateAxisZ(); } } return true; } bool TangentSpaceHelper::CalculateTangentsAndBitagents( const AZStd::vector& vertices, const AZStd::vector& indices, const AZStd::vector& uvs, const AZStd::vector& normals, AZStd::vector& outTangents, AZStd::vector& outBitangents) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth); if ((indices.size() % 3) != 0) { AZ_Error("TangentSpaceHelper", false, "Size of list of indices (%zu) is not a multiple of 3.", indices.size()); return false; } if (vertices.size() != uvs.size()) { AZ_Error("TangentSpaceHelper", false, "Number of vertices (%zu) does not match the number of uvs (%zu).", vertices.size(), uvs.size()); return false; } if (vertices.size() != normals.size()) { AZ_Error("TangentSpaceHelper", false, "Number of vertices (%zu) does not match the number of normals (%zu).", vertices.size(), normals.size()); return false; } const size_t triangleCount = indices.size() / 3; const size_t vertexCount = vertices.size(); // Reset results outTangents.resize(vertexCount, AZ::Vector3::CreateZero()); outBitangents.resize(vertexCount, AZ::Vector3::CreateZero()); // calculate the base vectors per triangle for (size_t i = 0; i < triangleCount; ++i) { TriangleIndices triangleIndices; TrianglePositions trianglePositions; TriangleEdges triangleEdges; TriangleUVs triangleUVs; GetTriangleData( i, indices, vertices, uvs, triangleIndices, trianglePositions, triangleEdges, triangleUVs); AZ::Vector3 tangent, bitangent; ComputeTangentAndBitangent(triangleUVs, triangleEdges, tangent, bitangent); // distribute the uv vectors to the vertices. for (AZ::u32 vertexIndexInTriangle = 0; vertexIndexInTriangle < 3; ++vertexIndexInTriangle) { const float weight = GetVertexWeightInTriangle(vertexIndexInTriangle, trianglePositions); const SimIndexType vertexIndex = triangleIndices[vertexIndexInTriangle]; outTangents[vertexIndex] += tangent * weight; outBitangents[vertexIndex] += bitangent * weight; } } // adjust the base vectors per vertex for (size_t i = 0; i < vertexCount; ++i) { AdjustTangentAndBitangent(normals[i], outTangents[i], outBitangents[i]); // Safety check for situations where simulation gets out of control. // Particles' positions can have huge floating point values that // could lead to non-finite numbers when calculating tangent spaces. if (!outTangents[i].IsFinite() || !outBitangents[i].IsFinite()) { outTangents[i] = AZ::Vector3::CreateAxisX(); outBitangents[i] = AZ::Vector3::CreateAxisY(); } } return true; } bool TangentSpaceHelper::CalculateTangentSpace( const AZStd::vector& vertices, const AZStd::vector& indices, const AZStd::vector& uvs, AZStd::vector& outTangents, AZStd::vector& outBitangents, AZStd::vector& outNormals) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth); if ((indices.size() % 3) != 0) { AZ_Error("TangentSpaceHelper", false, "Size of list of indices (%zu) is not a multiple of 3.", indices.size()); return false; } if (vertices.size() != uvs.size()) { AZ_Error("TangentSpaceHelper", false, "Number of vertices (%zu) does not match the number of uvs (%zu).", vertices.size(), uvs.size()); return false; } const size_t triangleCount = indices.size() / 3; const size_t vertexCount = vertices.size(); // Reset results outTangents.resize(vertexCount, AZ::Vector3::CreateZero()); outBitangents.resize(vertexCount, AZ::Vector3::CreateZero()); outNormals.resize(vertexCount, AZ::Vector3::CreateZero()); // calculate the base vectors per triangle for (size_t i = 0; i < triangleCount; ++i) { TriangleIndices triangleIndices; TrianglePositions trianglePositions; TriangleEdges triangleEdges; TriangleUVs triangleUVs; GetTriangleData( i, indices, vertices, uvs, triangleIndices, trianglePositions, triangleEdges, triangleUVs); AZ::Vector3 tangent, bitangent, normal; if (ComputeNormal(triangleEdges, normal)) { ComputeTangentAndBitangent(triangleUVs, triangleEdges, tangent, bitangent); } else { // Use the identity base with low influence to leave other valid triangles to // affect these vertices. In case no other triangle affects the vertices the base // will still be valid with identity values as it gets normalized later. const float identityInfluence = 0.01f; tangent = AZ::Vector3::CreateAxisX(identityInfluence); bitangent = AZ::Vector3::CreateAxisY(identityInfluence); } // distribute the normals and uv vectors to the vertices. for (AZ::u32 vertexIndexInTriangle = 0; vertexIndexInTriangle < 3; ++vertexIndexInTriangle) { const float weight = GetVertexWeightInTriangle(vertexIndexInTriangle, trianglePositions); const SimIndexType vertexIndex = triangleIndices[vertexIndexInTriangle]; outNormals[vertexIndex] += normal * AZStd::max(weight, Tolerance); outTangents[vertexIndex] += tangent * weight; outBitangents[vertexIndex] += bitangent * weight; } } // adjust the base vectors per vertex for (size_t i = 0; i < vertexCount; ++i) { outNormals[i].NormalizeSafe(Tolerance); AdjustTangentAndBitangent(outNormals[i], outTangents[i], outBitangents[i]); // Safety check for situations where simulation gets out of control. // Particles' positions can have huge floating point values that // could lead to non-finite numbers when calculating tangent spaces. if (!outNormals[i].IsFinite() || !outTangents[i].IsFinite() || !outBitangents[i].IsFinite()) { outTangents[i] = AZ::Vector3::CreateAxisX(); outBitangents[i] = AZ::Vector3::CreateAxisY(); outNormals[i] = AZ::Vector3::CreateAxisZ(); } } return true; } void TangentSpaceHelper::GetTriangleData( size_t triangleIndex, const AZStd::vector& indices, const AZStd::vector& vertices, TriangleIndices& triangleIndices, TrianglePositions& trianglePositions, TriangleEdges& triangleEdges) { triangleIndices = {{ indices[triangleIndex * 3 + 0], indices[triangleIndex * 3 + 1], indices[triangleIndex * 3 + 2] }}; trianglePositions = {{ vertices[triangleIndices[0]].GetAsVector3(), vertices[triangleIndices[1]].GetAsVector3(), vertices[triangleIndices[2]].GetAsVector3() }}; triangleEdges = {{ trianglePositions[1] - trianglePositions[0], trianglePositions[2] - trianglePositions[0] }}; } void TangentSpaceHelper::GetTriangleData( size_t triangleIndex, const AZStd::vector& indices, const AZStd::vector& vertices, const AZStd::vector& uvs, TriangleIndices& triangleIndices, TrianglePositions& trianglePositions, TriangleEdges& triangleEdges, TriangleUVs& triangleUVs) { GetTriangleData( triangleIndex, indices, vertices, triangleIndices, trianglePositions, triangleEdges); triangleUVs = {{ uvs[triangleIndices[0]], uvs[triangleIndices[1]], uvs[triangleIndices[2]] }}; } bool TangentSpaceHelper::ComputeNormal(const TriangleEdges& triangleEdges, AZ::Vector3& normal) { normal = triangleEdges[0].Cross(triangleEdges[1]); // Avoid situations where the edges are parallel resulting in an invalid normal. // This can happen if the simulation moves particles of triangle to the same spot or very far away. if (normal.IsZero(Tolerance)) { // Use the identity base with low influence to leave other valid triangles to // affect these vertices. In case no other triangle affects the vertices the base // will still be valid with identity values as it gets normalized later. const float identityInfluence = 0.01f; normal = AZ::Vector3::CreateAxisZ(identityInfluence); return false; } normal.Normalize(); return true; } bool TangentSpaceHelper::ComputeTangentAndBitangent( const TriangleUVs& triangleUVs, const TriangleEdges& triangleEdges, AZ::Vector3& tangent, AZ::Vector3& bitangent) { const float deltaU1 = triangleUVs[1].GetX() - triangleUVs[0].GetX(); const float deltaU2 = triangleUVs[2].GetX() - triangleUVs[0].GetX(); const float deltaV1 = triangleUVs[1].GetY() - triangleUVs[0].GetY(); const float deltaV2 = triangleUVs[2].GetY() - triangleUVs[0].GetY(); const float div = (deltaU1 * deltaV2 - deltaU2 * deltaV1); if (AZ::IsClose(div, 0.0f, Tolerance)) { tangent = AZ::Vector3::CreateAxisX(); bitangent = AZ::Vector3::CreateAxisY(); return false; } // 2D triangle area = (u1*v2-u2*v1)/2 const float a = deltaV2; // /div was removed - no required because of normalize() const float b = -deltaV1; const float c = -deltaU2; const float d = deltaU1; const float signDiv = AZ::GetSign(div); // /fAreaMul2*fAreaMul2 was optimized away -> small triangles in UV should contribute less and // less artifacts (no divide and multiply) tangent = (triangleEdges[0] * a + triangleEdges[1] * b) * signDiv; bitangent = (triangleEdges[0] * c + triangleEdges[1] * d) * signDiv; return true; } void TangentSpaceHelper::AdjustTangentAndBitangent( const AZ::Vector3& normal, AZ::Vector3& tangent, AZ::Vector3& bitangent) { // Calculate handedness of the bitangent AZ::Vector3 bitangentReference = normal.Cross(tangent); const float handedness = (bitangentReference.Dot(bitangent) < 0.0f) ? -1.0f : 1.0f; // Apply Gram-Schmidt method to make tangent perpendicular to normal. tangent -= normal * normal.Dot(tangent); tangent.NormalizeSafe(Tolerance); bitangent = normal.Cross(tangent) * handedness; } float TangentSpaceHelper::GetVertexWeightInTriangle(AZ::u32 vertexIndexInTriangle, const TrianglePositions& trianglePositions) { // weight by angle to fix the L-Shape problem const AZ::Vector3 edgeA = trianglePositions[(vertexIndexInTriangle + 2) % 3] - trianglePositions[vertexIndexInTriangle]; const AZ::Vector3 edgeB = trianglePositions[(vertexIndexInTriangle + 1) % 3] - trianglePositions[vertexIndexInTriangle]; return edgeA.AngleSafe(edgeB); } } // namespace NvCloth