TangentSpaceHelper.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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 <System/TangentSpaceHelper.h>
  9. #include <AzCore/Debug/Profiler.h>
  10. namespace NvCloth
  11. {
  12. namespace
  13. {
  14. const float Tolerance = 1e-7f;
  15. }
  16. bool TangentSpaceHelper::CalculateNormals(
  17. const AZStd::vector<SimParticleFormat>& vertices,
  18. const AZStd::vector<SimIndexType>& indices,
  19. AZStd::vector<AZ::Vector3>& outNormals)
  20. {
  21. AZ_PROFILE_FUNCTION(Cloth);
  22. if ((indices.size() % 3) != 0)
  23. {
  24. AZ_Error("TangentSpaceHelper", false,
  25. "Size of list of indices (%zu) is not a multiple of 3.",
  26. indices.size());
  27. return false;
  28. }
  29. const size_t triangleCount = indices.size() / 3;
  30. const size_t vertexCount = vertices.size();
  31. // Reset results
  32. outNormals.resize(vertexCount);
  33. AZStd::fill(outNormals.begin(), outNormals.end(), AZ::Vector3::CreateZero());
  34. // calculate the normals per triangle
  35. for (size_t i = 0; i < triangleCount; ++i)
  36. {
  37. TriangleIndices triangleIndices;
  38. TrianglePositions trianglePositions;
  39. TriangleEdges triangleEdges;
  40. GetTriangleData(
  41. i, indices, vertices,
  42. triangleIndices, trianglePositions, triangleEdges);
  43. AZ::Vector3 normal;
  44. ComputeNormal(triangleEdges, normal);
  45. // distribute the normals to the vertices.
  46. for (AZ::u32 vertexIndexInTriangle = 0; vertexIndexInTriangle < 3; ++vertexIndexInTriangle)
  47. {
  48. const float weight = GetVertexWeightInTriangle(vertexIndexInTriangle, trianglePositions);
  49. const SimIndexType vertexIndex = triangleIndices[vertexIndexInTriangle];
  50. outNormals[vertexIndex] += normal * AZStd::max(weight, Tolerance);
  51. }
  52. }
  53. // adjust the normals per vertex
  54. for (auto& outNormal : outNormals)
  55. {
  56. outNormal.NormalizeSafe(Tolerance);
  57. // Safety check for situations where simulation gets out of control.
  58. // Particles' positions can have huge floating point values that
  59. // could lead to non-finite numbers when calculating tangent spaces.
  60. if (!outNormal.IsFinite())
  61. {
  62. outNormal = AZ::Vector3::CreateAxisZ();
  63. }
  64. }
  65. return true;
  66. }
  67. bool TangentSpaceHelper::CalculateTangentsAndBitagents(
  68. const AZStd::vector<SimParticleFormat>& vertices,
  69. const AZStd::vector<SimIndexType>& indices,
  70. const AZStd::vector<SimUVType>& uvs,
  71. const AZStd::vector<AZ::Vector3>& normals,
  72. AZStd::vector<AZ::Vector3>& outTangents,
  73. AZStd::vector<AZ::Vector3>& outBitangents)
  74. {
  75. AZ_PROFILE_FUNCTION(Cloth);
  76. if ((indices.size() % 3) != 0)
  77. {
  78. AZ_Error("TangentSpaceHelper", false,
  79. "Size of list of indices (%zu) is not a multiple of 3.",
  80. indices.size());
  81. return false;
  82. }
  83. if (vertices.size() != uvs.size())
  84. {
  85. AZ_Error("TangentSpaceHelper", false,
  86. "Number of vertices (%zu) does not match the number of uvs (%zu).",
  87. vertices.size(), uvs.size());
  88. return false;
  89. }
  90. if (vertices.size() != normals.size())
  91. {
  92. AZ_Error("TangentSpaceHelper", false,
  93. "Number of vertices (%zu) does not match the number of normals (%zu).",
  94. vertices.size(), normals.size());
  95. return false;
  96. }
  97. const size_t triangleCount = indices.size() / 3;
  98. const size_t vertexCount = vertices.size();
  99. // Reset results
  100. outTangents.resize(vertexCount);
  101. outBitangents.resize(vertexCount);
  102. AZStd::fill(outTangents.begin(), outTangents.end(), AZ::Vector3::CreateZero());
  103. AZStd::fill(outBitangents.begin(), outBitangents.end(), AZ::Vector3::CreateZero());
  104. // calculate the base vectors per triangle
  105. for (size_t i = 0; i < triangleCount; ++i)
  106. {
  107. TriangleIndices triangleIndices;
  108. TrianglePositions trianglePositions;
  109. TriangleEdges triangleEdges;
  110. TriangleUVs triangleUVs;
  111. GetTriangleData(
  112. i, indices, vertices, uvs,
  113. triangleIndices, trianglePositions, triangleEdges, triangleUVs);
  114. AZ::Vector3 tangent, bitangent;
  115. ComputeTangentAndBitangent(triangleUVs, triangleEdges, tangent, bitangent);
  116. // distribute the uv vectors to the vertices.
  117. for (AZ::u32 vertexIndexInTriangle = 0; vertexIndexInTriangle < 3; ++vertexIndexInTriangle)
  118. {
  119. const float weight = GetVertexWeightInTriangle(vertexIndexInTriangle, trianglePositions);
  120. const SimIndexType vertexIndex = triangleIndices[vertexIndexInTriangle];
  121. outTangents[vertexIndex] += tangent * weight;
  122. outBitangents[vertexIndex] += bitangent * weight;
  123. }
  124. }
  125. // adjust the base vectors per vertex
  126. for (size_t i = 0; i < vertexCount; ++i)
  127. {
  128. AdjustTangentAndBitangent(normals[i], outTangents[i], outBitangents[i]);
  129. // Safety check for situations where simulation gets out of control.
  130. // Particles' positions can have huge floating point values that
  131. // could lead to non-finite numbers when calculating tangent spaces.
  132. if (!outTangents[i].IsFinite() ||
  133. !outBitangents[i].IsFinite())
  134. {
  135. outTangents[i] = AZ::Vector3::CreateAxisX();
  136. outBitangents[i] = AZ::Vector3::CreateAxisY();
  137. }
  138. }
  139. return true;
  140. }
  141. bool TangentSpaceHelper::CalculateTangentSpace(
  142. const AZStd::vector<SimParticleFormat>& vertices,
  143. const AZStd::vector<SimIndexType>& indices,
  144. const AZStd::vector<SimUVType>& uvs,
  145. AZStd::vector<AZ::Vector3>& outTangents,
  146. AZStd::vector<AZ::Vector3>& outBitangents,
  147. AZStd::vector<AZ::Vector3>& outNormals)
  148. {
  149. AZ_PROFILE_FUNCTION(Cloth);
  150. if ((indices.size() % 3) != 0)
  151. {
  152. AZ_Error("TangentSpaceHelper", false,
  153. "Size of list of indices (%zu) is not a multiple of 3.",
  154. indices.size());
  155. return false;
  156. }
  157. if (vertices.size() != uvs.size())
  158. {
  159. AZ_Error("TangentSpaceHelper", false,
  160. "Number of vertices (%zu) does not match the number of uvs (%zu).",
  161. vertices.size(), uvs.size());
  162. return false;
  163. }
  164. const size_t triangleCount = indices.size() / 3;
  165. const size_t vertexCount = vertices.size();
  166. // Reset results
  167. outTangents.resize(vertexCount);
  168. outBitangents.resize(vertexCount);
  169. outNormals.resize(vertexCount);
  170. AZStd::fill(outTangents.begin(), outTangents.end(), AZ::Vector3::CreateZero());
  171. AZStd::fill(outBitangents.begin(), outBitangents.end(), AZ::Vector3::CreateZero());
  172. AZStd::fill(outNormals.begin(), outNormals.end(), AZ::Vector3::CreateZero());
  173. // calculate the base vectors per triangle
  174. for (size_t i = 0; i < triangleCount; ++i)
  175. {
  176. TriangleIndices triangleIndices;
  177. TrianglePositions trianglePositions;
  178. TriangleEdges triangleEdges;
  179. TriangleUVs triangleUVs;
  180. GetTriangleData(
  181. i, indices, vertices, uvs,
  182. triangleIndices, trianglePositions, triangleEdges, triangleUVs);
  183. AZ::Vector3 tangent, bitangent, normal;
  184. if (ComputeNormal(triangleEdges, normal))
  185. {
  186. ComputeTangentAndBitangent(triangleUVs, triangleEdges, tangent, bitangent);
  187. }
  188. else
  189. {
  190. // Use the identity base with low influence to leave other valid triangles to
  191. // affect these vertices. In case no other triangle affects the vertices the base
  192. // will still be valid with identity values as it gets normalized later.
  193. const float identityInfluence = 0.01f;
  194. tangent = AZ::Vector3::CreateAxisX(identityInfluence);
  195. bitangent = AZ::Vector3::CreateAxisY(identityInfluence);
  196. }
  197. // distribute the normals and uv vectors to the vertices.
  198. for (AZ::u32 vertexIndexInTriangle = 0; vertexIndexInTriangle < 3; ++vertexIndexInTriangle)
  199. {
  200. const float weight = GetVertexWeightInTriangle(vertexIndexInTriangle, trianglePositions);
  201. const SimIndexType vertexIndex = triangleIndices[vertexIndexInTriangle];
  202. outNormals[vertexIndex] += normal * AZStd::max(weight, Tolerance);
  203. outTangents[vertexIndex] += tangent * weight;
  204. outBitangents[vertexIndex] += bitangent * weight;
  205. }
  206. }
  207. // adjust the base vectors per vertex
  208. for (size_t i = 0; i < vertexCount; ++i)
  209. {
  210. outNormals[i].NormalizeSafe(Tolerance);
  211. AdjustTangentAndBitangent(outNormals[i], outTangents[i], outBitangents[i]);
  212. // Safety check for situations where simulation gets out of control.
  213. // Particles' positions can have huge floating point values that
  214. // could lead to non-finite numbers when calculating tangent spaces.
  215. if (!outNormals[i].IsFinite() ||
  216. !outTangents[i].IsFinite() ||
  217. !outBitangents[i].IsFinite())
  218. {
  219. outTangents[i] = AZ::Vector3::CreateAxisX();
  220. outBitangents[i] = AZ::Vector3::CreateAxisY();
  221. outNormals[i] = AZ::Vector3::CreateAxisZ();
  222. }
  223. }
  224. return true;
  225. }
  226. void TangentSpaceHelper::GetTriangleData(
  227. size_t triangleIndex,
  228. const AZStd::vector<SimIndexType>& indices,
  229. const AZStd::vector<SimParticleFormat>& vertices,
  230. TriangleIndices& triangleIndices,
  231. TrianglePositions& trianglePositions,
  232. TriangleEdges& triangleEdges)
  233. {
  234. triangleIndices =
  235. {{
  236. indices[triangleIndex * 3 + 0],
  237. indices[triangleIndex * 3 + 1],
  238. indices[triangleIndex * 3 + 2]
  239. }};
  240. trianglePositions =
  241. {{
  242. vertices[triangleIndices[0]].GetAsVector3(),
  243. vertices[triangleIndices[1]].GetAsVector3(),
  244. vertices[triangleIndices[2]].GetAsVector3()
  245. }};
  246. triangleEdges =
  247. {{
  248. trianglePositions[1] - trianglePositions[0],
  249. trianglePositions[2] - trianglePositions[0]
  250. }};
  251. }
  252. void TangentSpaceHelper::GetTriangleData(
  253. size_t triangleIndex,
  254. const AZStd::vector<SimIndexType>& indices,
  255. const AZStd::vector<SimParticleFormat>& vertices,
  256. const AZStd::vector<SimUVType>& uvs,
  257. TriangleIndices& triangleIndices,
  258. TrianglePositions& trianglePositions,
  259. TriangleEdges& triangleEdges,
  260. TriangleUVs& triangleUVs)
  261. {
  262. GetTriangleData(
  263. triangleIndex, indices, vertices,
  264. triangleIndices, trianglePositions, triangleEdges);
  265. triangleUVs =
  266. {{
  267. uvs[triangleIndices[0]],
  268. uvs[triangleIndices[1]],
  269. uvs[triangleIndices[2]]
  270. }};
  271. }
  272. bool TangentSpaceHelper::ComputeNormal(const TriangleEdges& triangleEdges, AZ::Vector3& normal)
  273. {
  274. normal = triangleEdges[0].Cross(triangleEdges[1]);
  275. // Avoid situations where the edges are parallel resulting in an invalid normal.
  276. // This can happen if the simulation moves particles of triangle to the same spot or very far away.
  277. if (normal.IsZero(Tolerance))
  278. {
  279. // Use the identity base with low influence to leave other valid triangles to
  280. // affect these vertices. In case no other triangle affects the vertices the base
  281. // will still be valid with identity values as it gets normalized later.
  282. const float identityInfluence = 0.01f;
  283. normal = AZ::Vector3::CreateAxisZ(identityInfluence);
  284. return false;
  285. }
  286. normal.Normalize();
  287. return true;
  288. }
  289. bool TangentSpaceHelper::ComputeTangentAndBitangent(
  290. const TriangleUVs& triangleUVs, const TriangleEdges& triangleEdges,
  291. AZ::Vector3& tangent, AZ::Vector3& bitangent)
  292. {
  293. const float deltaU1 = triangleUVs[1].GetX() - triangleUVs[0].GetX();
  294. const float deltaU2 = triangleUVs[2].GetX() - triangleUVs[0].GetX();
  295. const float deltaV1 = triangleUVs[1].GetY() - triangleUVs[0].GetY();
  296. const float deltaV2 = triangleUVs[2].GetY() - triangleUVs[0].GetY();
  297. const float div = (deltaU1 * deltaV2 - deltaU2 * deltaV1);
  298. if (AZ::IsClose(div, 0.0f, Tolerance))
  299. {
  300. tangent = AZ::Vector3::CreateAxisX();
  301. bitangent = AZ::Vector3::CreateAxisY();
  302. return false;
  303. }
  304. // 2D triangle area = (u1*v2-u2*v1)/2
  305. const float a = deltaV2; // /div was removed - no required because of normalize()
  306. const float b = -deltaV1;
  307. const float c = -deltaU2;
  308. const float d = deltaU1;
  309. const float signDiv = AZ::GetSign(div);
  310. // /fAreaMul2*fAreaMul2 was optimized away -> small triangles in UV should contribute less and
  311. // less artifacts (no divide and multiply)
  312. tangent = (triangleEdges[0] * a + triangleEdges[1] * b) * signDiv;
  313. bitangent = (triangleEdges[0] * c + triangleEdges[1] * d) * signDiv;
  314. return true;
  315. }
  316. void TangentSpaceHelper::AdjustTangentAndBitangent(
  317. const AZ::Vector3& normal, AZ::Vector3& tangent, AZ::Vector3& bitangent)
  318. {
  319. // Calculate handedness of the bitangent
  320. AZ::Vector3 bitangentReference = normal.Cross(tangent);
  321. const float handedness = (bitangentReference.Dot(bitangent) < 0.0f) ? -1.0f : 1.0f;
  322. // Apply Gram-Schmidt method to make tangent perpendicular to normal.
  323. tangent -= normal * normal.Dot(tangent);
  324. tangent.NormalizeSafe(Tolerance);
  325. bitangent = normal.Cross(tangent) * handedness;
  326. }
  327. float TangentSpaceHelper::GetVertexWeightInTriangle(AZ::u32 vertexIndexInTriangle, const TrianglePositions& trianglePositions)
  328. {
  329. // weight by angle to fix the L-Shape problem
  330. const AZ::Vector3 edgeA = trianglePositions[(vertexIndexInTriangle + 2) % 3] - trianglePositions[vertexIndexInTriangle];
  331. const AZ::Vector3 edgeB = trianglePositions[(vertexIndexInTriangle + 1) % 3] - trianglePositions[vertexIndexInTriangle];
  332. return edgeA.AngleSafe(edgeB);
  333. }
  334. } // namespace NvCloth