TangentSpaceHelper.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "TangentSpaceHelper.h"
  9. #include <AzCore/std/containers/array.h>
  10. #include <CryCommon/Cry_Math.h>
  11. namespace WhiteBox
  12. {
  13. void AZTangentSpaceCalculation::Calculate(
  14. const AZStd::vector<AZ::Vector3>& vertices, const AZStd::vector<uint32_t>& indices,
  15. const AZStd::vector<AZ::Vector2>& uvs)
  16. {
  17. AZ_Error(
  18. "AZTangentSpaceCalculation", (indices.size() % 3) == 0,
  19. "Size of list of indices (%d) is not a multiple of 3.", indices.size());
  20. const size_t triangleCount = indices.size() / 3;
  21. const size_t vertexCount = vertices.size();
  22. // Reset results with the right number of elements
  23. m_baseVectors = AZStd::vector<Base33>(vertexCount);
  24. using TriangleIndices = AZStd::array<uint32_t, 3>;
  25. using TrianglePositions = AZStd::array<AZ::Vector3, 3>;
  26. using TriangleUVs = AZStd::array<AZ::Vector3, 3>;
  27. using TriangleEdges = AZStd::array<AZ::Vector3, 2>;
  28. AZStd::vector<TriangleIndices> trianglesIndices;
  29. AZStd::vector<TrianglePositions> trianglesPositions;
  30. AZStd::vector<TriangleUVs> trianglesUVs;
  31. AZStd::vector<TriangleEdges> trianglesEdges;
  32. trianglesIndices.reserve(triangleCount);
  33. trianglesPositions.reserve(triangleCount);
  34. trianglesUVs.reserve(triangleCount);
  35. trianglesEdges.reserve(triangleCount);
  36. // Precalculate triangles' indices, positions, UVs and edges.
  37. for (AZ::u32 i = 0; i < triangleCount; ++i)
  38. {
  39. const TriangleIndices triangleIndices = {{indices[i * 3 + 0], indices[i * 3 + 1], indices[i * 3 + 2]}};
  40. const TrianglePositions trianglePositions = {
  41. {(vertices[triangleIndices[0]]), (vertices[triangleIndices[1]]), (vertices[triangleIndices[2]])}};
  42. const TriangleUVs triangleUVs = {
  43. {AZ::Vector3(uvs[triangleIndices[0]].GetX(), uvs[triangleIndices[0]].GetY(), 0.0f),
  44. AZ::Vector3(uvs[triangleIndices[1]].GetX(), uvs[triangleIndices[1]].GetY(), 0.0f),
  45. AZ::Vector3(uvs[triangleIndices[2]].GetX(), uvs[triangleIndices[2]].GetY(), 0.0f)}};
  46. const TriangleEdges triangleEdges = {
  47. {trianglePositions[1] - trianglePositions[0], trianglePositions[2] - trianglePositions[0]}};
  48. trianglesIndices.push_back(AZStd::move(triangleIndices));
  49. trianglesPositions.push_back(AZStd::move(trianglePositions));
  50. trianglesUVs.push_back(AZStd::move(triangleUVs));
  51. trianglesEdges.push_back(AZStd::move(triangleEdges));
  52. }
  53. // base vectors per triangle
  54. AZStd::vector<Base33> triangleBases;
  55. triangleBases.reserve(triangleCount);
  56. // calculate the base vectors per triangle
  57. {
  58. const float identityInfluence = 0.01f;
  59. const Base33 identityBase(
  60. AZ::Vector3(identityInfluence, 0.0f, 0.0f), AZ::Vector3(0.0f, identityInfluence, 0.0f),
  61. AZ::Vector3(0.0f, 0.0f, identityInfluence));
  62. for (AZ::u32 i = 0; i < triangleCount; ++i)
  63. {
  64. #if defined(AZ_ENABLE_TRACING)
  65. const auto& trianglePositions = trianglesPositions[i];
  66. #endif
  67. const auto& triangleUVs = trianglesUVs[i];
  68. const auto& triangleEdges = trianglesEdges[i];
  69. // calculate tangent vectors
  70. AZ::Vector3 normal = triangleEdges[0].Cross(triangleEdges[1]);
  71. // Avoid situations where the edges are parallel resulting in an invalid normal.
  72. // This can happen if the simulation moves particles of triangle to the same spot or very far away.
  73. if (normal.IsZero(0.0001f))
  74. {
  75. // Use the identity base with low influence to leave other valid triangles to
  76. // affect these vertices. In case no other triangle affects the vertices the base
  77. // will still be valid with identity values as it gets normalized later.
  78. triangleBases.push_back(identityBase);
  79. continue;
  80. }
  81. normal.Normalize();
  82. const float deltaU1 = triangleUVs[1].GetX() - triangleUVs[0].GetX();
  83. const float deltaU2 = triangleUVs[2].GetX() - triangleUVs[0].GetX();
  84. const float deltaV1 = triangleUVs[1].GetY() - triangleUVs[0].GetY();
  85. const float deltaV2 = triangleUVs[2].GetY() - triangleUVs[0].GetY();
  86. const float div = (deltaU1 * deltaV2 - deltaU2 * deltaV1);
  87. if (_isnan(div))
  88. {
  89. AZ_Error(
  90. "AZTangentSpaceCalculation", false,
  91. "Vertices 0,1,2 have broken texture coordinates v0:(%f : %f : %f) v1:(%f : %f : %f) v2:(%f : "
  92. "%f : %f)",
  93. float(trianglePositions[0].GetX()), float(trianglePositions[0].GetY()),
  94. float(trianglePositions[0].GetZ()), float(trianglePositions[1].GetX()),
  95. float(trianglePositions[1].GetY()), float(trianglePositions[1].GetZ()),
  96. float(trianglePositions[2].GetX()), float(trianglePositions[2].GetY()),
  97. float(trianglePositions[2].GetZ()));
  98. return;
  99. }
  100. AZ::Vector3 tangent, bitangent;
  101. if (div != 0.0f)
  102. {
  103. // 2D triangle area = (u1*v2-u2*v1)/2
  104. const float a = deltaV2; // /div was removed - no required because of normalize()
  105. const float b = -deltaV1;
  106. const float c = -deltaU2;
  107. const float d = deltaU1;
  108. // /fAreaMul2*fAreaMul2 was optimized away -> small triangles in UV should contribute less and
  109. // less artifacts (no divide and multiply)
  110. tangent = (triangleEdges[0] * a + triangleEdges[1] * b) * fsgnf(div);
  111. bitangent = (triangleEdges[0] * c + triangleEdges[1] * d) * fsgnf(div);
  112. }
  113. else
  114. {
  115. tangent = AZ::Vector3(1.0f, 0.0f, 0.0f);
  116. bitangent = AZ::Vector3(0.0f, 1.0f, 0.0f);
  117. }
  118. triangleBases.push_back(Base33(tangent, bitangent, normal));
  119. }
  120. }
  121. // distribute the normals and uv vectors to the vertices
  122. {
  123. // we create a new tangent base for every vertex index that has a different normal (later we split further
  124. // for mirrored use) and sum the base vectors (weighted by angle and mirrored if necessary)
  125. for (AZ::u32 i = 0; i < triangleCount; ++i)
  126. {
  127. const auto& triangleIndices = trianglesIndices[i];
  128. const auto& trianglePositions = trianglesPositions[i];
  129. Base33& triBase = triangleBases[i];
  130. // for each triangle vertex
  131. for (AZ::u32 e = 0; e < 3; ++e)
  132. {
  133. // weight by angle to fix the L-Shape problem
  134. const float weight = CalcAngleBetween(
  135. trianglePositions[(e + 2) % 3] - trianglePositions[e],
  136. trianglePositions[(e + 1) % 3] - trianglePositions[e]);
  137. triBase.m_normal *= AZStd::max(weight, 0.0001f);
  138. triBase.m_tangent *= weight;
  139. triBase.m_bitangent *= weight;
  140. AddNormalToBase(triangleIndices[e], triBase.m_normal);
  141. AddUVToBase(triangleIndices[e], triBase.m_tangent, triBase.m_bitangent);
  142. }
  143. }
  144. }
  145. // adjust the base vectors per vertex
  146. {
  147. for (auto& ref : m_baseVectors)
  148. {
  149. // rotate u and v in n plane
  150. AZ::Vector3 nOut = ref.m_normal;
  151. nOut.Normalize();
  152. // project u in n plane
  153. // project v in n plane
  154. AZ::Vector3 uOut = ref.m_tangent - nOut * (nOut.Dot(ref.m_tangent));
  155. AZ::Vector3 vOut = ref.m_bitangent - nOut * (nOut.Dot(ref.m_bitangent));
  156. ref.m_tangent = uOut;
  157. ref.m_tangent.Normalize();
  158. ref.m_bitangent = vOut;
  159. ref.m_bitangent.Normalize();
  160. ref.m_normal = nOut;
  161. }
  162. }
  163. AZ_Error(
  164. "AZTangentSpaceCalculation", GetBaseCount() == vertices.size(),
  165. "Number of tangent spaces (%d) doesn't match with the number of input vertices (%d).", GetBaseCount(),
  166. vertices.size());
  167. }
  168. size_t AZTangentSpaceCalculation::GetBaseCount() const
  169. {
  170. return m_baseVectors.size();
  171. }
  172. void AZTangentSpaceCalculation::GetBase(
  173. AZ::u32 index, AZ::Vector3& tangent, AZ::Vector3& bitangent, AZ::Vector3& normal) const
  174. {
  175. tangent = GetTangent(index);
  176. bitangent = GetBitangent(index);
  177. normal = GetNormal(index);
  178. }
  179. AZ::Vector3 AZTangentSpaceCalculation::GetTangent(AZ::u32 index) const
  180. {
  181. return m_baseVectors[index].m_tangent;
  182. }
  183. AZ::Vector3 AZTangentSpaceCalculation::GetBitangent(AZ::u32 index) const
  184. {
  185. return m_baseVectors[index].m_bitangent;
  186. }
  187. AZ::Vector3 AZTangentSpaceCalculation::GetNormal(AZ::u32 index) const
  188. {
  189. return m_baseVectors[index].m_normal;
  190. }
  191. void AZTangentSpaceCalculation::AddNormalToBase(AZ::u32 index, const AZ::Vector3& normal)
  192. {
  193. m_baseVectors[index].m_normal += normal;
  194. }
  195. void AZTangentSpaceCalculation::AddUVToBase(AZ::u32 index, const AZ::Vector3& u, const AZ::Vector3& v)
  196. {
  197. m_baseVectors[index].m_tangent += u;
  198. m_baseVectors[index].m_bitangent += v;
  199. }
  200. float AZTangentSpaceCalculation::CalcAngleBetween(const AZ::Vector3& a, const AZ::Vector3& b)
  201. {
  202. double lengthQ = sqrt(a.GetLengthSq() * b.GetLengthSq());
  203. // to prevent division by zero
  204. lengthQ = AZStd::max(lengthQ, 1e-8);
  205. double cosAngle = a.Dot(b) / lengthQ;
  206. // acosf is not available on every platform. acos_tpl clamps cosAngle to [-1,1].
  207. return static_cast<float>(acos_tpl(cosAngle));
  208. }
  209. AZTangentSpaceCalculation::Base33::Base33(
  210. const AZ::Vector3& tangent, const AZ::Vector3& bitangent, const AZ::Vector3& normal)
  211. : m_tangent(tangent)
  212. , m_bitangent(bitangent)
  213. , m_normal(normal)
  214. {
  215. }
  216. } // namespace WhiteBox