TangentGenerateComponent.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  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 <Generation/Components/TangentGenerator/TangentGenerateComponent.h>
  9. #include <Generation/Components/TangentGenerator/TangentGenerators/MikkTGenerator.h>
  10. #include <Generation/Components/TangentGenerator/TangentGenerators/BlendShapeMikkTGenerator.h>
  11. #include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
  12. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexUVData.h>
  13. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h>
  14. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h>
  15. #include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
  16. #include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
  17. #include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
  18. #include <SceneAPI/SceneCore/Containers/Views/ConvertIterator.h>
  19. #include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
  20. #include <SceneAPI/SceneCore/Utilities/Reporting.h>
  21. #include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
  22. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
  23. #include <SceneAPI/SceneData/GraphData/BlendShapeData.h>
  24. #include <SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h>
  25. #include <SceneAPI/SceneData/GraphData/MeshVertexTangentData.h>
  26. #include <AzCore/Math/Vector4.h>
  27. #include <AzCore/Settings/SettingsRegistry.h>
  28. #include <AzCore/std/smart_ptr/make_shared.h>
  29. namespace AZ::SceneGenerationComponents
  30. {
  31. static constexpr AZStd::string_view DefaultTangentGenerationKey{ "/O3DE/SceneAPI/TangentGenerateComponent/DefaultGenerationMethod" };
  32. static constexpr AZStd::string_view DebugBitangentFlipKey{ "/O3DE/SceneAPI/TangentGenerateComponent/DebugBitangentFlip" };
  33. TangentGenerateComponent::TangentGenerateComponent()
  34. {
  35. BindToCall(&TangentGenerateComponent::GenerateTangentData);
  36. }
  37. void TangentGenerateComponent::Reflect(AZ::ReflectContext* context)
  38. {
  39. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  40. if (serializeContext)
  41. {
  42. serializeContext->Class<TangentGenerateComponent, AZ::SceneAPI::SceneCore::GenerationComponent>()->Version(3);
  43. }
  44. }
  45. const AZ::SceneAPI::SceneData::TangentsRule* TangentGenerateComponent::GetTangentRule(const AZ::SceneAPI::Containers::Scene& scene) const
  46. {
  47. for (const auto& object : scene.GetManifest().GetValueStorage())
  48. {
  49. if (object->RTTI_IsTypeOf(AZ::SceneAPI::DataTypes::IGroup::TYPEINFO_Uuid()))
  50. {
  51. const AZ::SceneAPI::DataTypes::IGroup* group = azrtti_cast<const AZ::SceneAPI::DataTypes::IGroup*>(object.get());
  52. const AZ::SceneAPI::SceneData::TangentsRule* rule = group->GetRuleContainerConst().FindFirstByType<AZ::SceneAPI::SceneData::TangentsRule>().get();
  53. if (rule)
  54. {
  55. return rule;
  56. }
  57. }
  58. }
  59. return nullptr;
  60. }
  61. void TangentGenerateComponent::GetRegistrySettings(
  62. AZ::SceneAPI::DataTypes::TangentGenerationMethod& defaultGenerationMethod, bool& debugBitangentFlip) const
  63. {
  64. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  65. {
  66. AZStd::string defaultTangentGenerationMethodString;
  67. if (settingsRegistry->Get(defaultTangentGenerationMethodString, DefaultTangentGenerationKey))
  68. {
  69. const bool isCaseSensitive = false;
  70. if (AZ::StringFunc::Equal(defaultTangentGenerationMethodString, "MikkT", isCaseSensitive))
  71. {
  72. defaultGenerationMethod = AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT;
  73. }
  74. else
  75. {
  76. AZ_Warning(
  77. AZ::SceneAPI::Utilities::WarningWindow,
  78. AZ::StringFunc::Equal(defaultTangentGenerationMethodString, "FromSourceScene", isCaseSensitive),
  79. "'" AZ_STRING_FORMAT "' is not a valid default tangent generation method. Check the value of %s in your settings registry, and change "
  80. "it to 'FromSourceScene' or 'MikkT'",
  81. defaultTangentGenerationMethodString.c_str(), AZ_STRING_ARG(DefaultTangentGenerationKey));
  82. }
  83. }
  84. settingsRegistry->Get(debugBitangentFlip, DebugBitangentFlipKey);
  85. }
  86. }
  87. AZ::SceneAPI::Events::ProcessingResult TangentGenerateComponent::GenerateTangentData(TangentGenerateContext& context)
  88. {
  89. // Get any tangent related settings from the settings registry
  90. AZ::SceneAPI::DataTypes::TangentGenerationMethod defaultGenerationMethod =
  91. AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene;
  92. bool debugBitangentFlip = false;
  93. GetRegistrySettings(defaultGenerationMethod, debugBitangentFlip);
  94. // Get the generation setting for this scene
  95. const AZ::SceneAPI::SceneData::TangentsRule* tangentsRule = GetTangentRule(context.GetScene());
  96. const AZ::SceneAPI::DataTypes::TangentGenerationMethod generationMethod =
  97. tangentsRule ? tangentsRule->GetGenerationMethod() : defaultGenerationMethod;
  98. // Iterate over all graph content and filter out all meshes.
  99. AZ::SceneAPI::Containers::SceneGraph& graph = context.GetScene().GetGraph();
  100. AZ::SceneAPI::Containers::SceneGraph::ContentStorageData graphContent = graph.GetContentStorage();
  101. // Build a list of mesh data nodes.
  102. AZStd::vector<AZStd::pair<AZ::SceneAPI::DataTypes::IMeshData*, AZ::SceneAPI::Containers::SceneGraph::NodeIndex> > meshes;
  103. for (auto item = graphContent.begin(); item != graphContent.end(); ++item)
  104. {
  105. // Skip anything that isn't a mesh.
  106. if (!(*item) || !(*item)->RTTI_IsTypeOf(AZ::SceneAPI::DataTypes::IMeshData::TYPEINFO_Uuid()))
  107. {
  108. continue;
  109. }
  110. // Get the mesh data and node index and store them in the vector as a pair, so we can iterate over them later.
  111. auto* mesh = static_cast<AZ::SceneAPI::DataTypes::IMeshData*>(item->get());
  112. AZ::SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex = graph.ConvertToNodeIndex(item);
  113. meshes.emplace_back(mesh, nodeIndex);
  114. }
  115. // Iterate over them. We had to build the array before as this method can insert new nodes, so using the iterator directly would fail.
  116. for (auto& [mesh, nodeIndex] : meshes)
  117. {
  118. // Generate tangents for the mesh (if this is desired or needed).
  119. if (!GenerateTangentsForMesh(context.GetScene(), nodeIndex, mesh, generationMethod))
  120. {
  121. return AZ::SceneAPI::Events::ProcessingResult::Failure;
  122. }
  123. // Now that we have the tangents and bitangents, calculate the tangent w values for the ones that we imported from the scene file, as they only have xyz.
  124. // But only do this if we are getting tangents from the source scene, because MikkT will provide us with a correct tangent.w already
  125. if (generationMethod == SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene)
  126. {
  127. if (!UpdateFbxTangentWValues(graph, nodeIndex, mesh, debugBitangentFlip))
  128. {
  129. return AZ::SceneAPI::Events::ProcessingResult::Failure;
  130. }
  131. }
  132. }
  133. return AZ::SceneAPI::Events::ProcessingResult::Success;
  134. }
  135. bool TangentGenerateComponent::UpdateFbxTangentWValues(
  136. AZ::SceneAPI::Containers::SceneGraph& graph,
  137. const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex,
  138. const AZ::SceneAPI::DataTypes::IMeshData* meshData,
  139. bool debugBitangentFlip)
  140. {
  141. // Iterate over all UV sets.
  142. AZ::SceneAPI::DataTypes::IMeshVertexUVData* uvData = FindUvData(graph, nodeIndex, 0);
  143. size_t uvSetIndex = 0;
  144. while (uvData)
  145. {
  146. AZ::SceneAPI::DataTypes::IMeshVertexTangentData* fbxTangentData = FindTangentData(graph, nodeIndex, uvSetIndex);
  147. AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* fbxBitangentData = FindBitangentData(graph, nodeIndex, uvSetIndex);
  148. if (fbxTangentData && fbxBitangentData)
  149. {
  150. const size_t numVerts = uvData->GetCount();
  151. AZ_Assert((numVerts == fbxTangentData->GetCount()) && (numVerts == fbxBitangentData->GetCount()), "Number of vertices inside UV set is not the same as number of tangents and bitangents.");
  152. for (size_t i = 0; i < numVerts; ++i)
  153. {
  154. // This code calculates the best tangent.w value, which is either -1 or +1, depending on the bitangent being mirrored or not.
  155. // We determine this by checking the angle between the generated tangent by doing a cross product between the tangent and normal, and the actual real bitangent.
  156. // It is no guarantee that using "cross(normal, tangent.xyz)* tangent.w" will result in the right bitangent, as the basis might not be orthogonal.
  157. // But we still go for the best guess.
  158. AZ::Vector4 tangent = fbxTangentData->GetTangent(i);
  159. AZ::Vector3 tangentDir = tangent.GetAsVector3();
  160. tangentDir.NormalizeSafe();
  161. AZ::Vector3 normal = meshData->GetNormal(static_cast<AZ::u32>(i));
  162. normal.NormalizeSafe();
  163. AZ::Vector3 generatedBitangent = normal.Cross(tangentDir);
  164. float dot = fbxBitangentData->GetBitangent(i).Dot(generatedBitangent);
  165. dot = AZ::GetMax(dot, -1.0f);
  166. dot = AZ::GetMin(dot, 1.0f);
  167. const float angle = acosf(dot);
  168. if (angle > AZ::Constants::HalfPi)
  169. {
  170. tangent = fbxTangentData->GetTangent(i);
  171. tangent.SetW(-1.0f);
  172. }
  173. else
  174. {
  175. tangent = fbxTangentData->GetTangent(i);
  176. tangent.SetW(1.0f);
  177. }
  178. if (debugBitangentFlip)
  179. {
  180. AZ::Vector4 originalTangent = fbxTangentData->GetTangent(i);
  181. if (originalTangent.GetW() > 0.0f)
  182. {
  183. // If the tangent has a positive w value, the fix for GHI-7125 is going to flip the bitangent
  184. // compared to the original behavior. Report an error and fail to process as an indication
  185. // that this asset will be impacted by GHI-7125
  186. AZ_Error(
  187. AZ::SceneAPI::Utilities::ErrorWindow, false,
  188. "Tangent w is positive for at least one vertex in the mesh. This model will be impacted by GHI-7125. "
  189. "See https://github.com/o3de/o3de/issues/7125 for details.");
  190. return false;
  191. }
  192. }
  193. // Update the tangent.w in the scene
  194. fbxTangentData->SetTangent(i, tangent);
  195. }
  196. }
  197. // Find the next UV set.
  198. uvData = FindUvData(graph, nodeIndex, ++uvSetIndex);
  199. }
  200. return true;
  201. }
  202. void TangentGenerateComponent::FindBlendShapes(
  203. AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex,
  204. AZStd::vector<AZ::SceneData::GraphData::BlendShapeData*>& outBlendShapes) const
  205. {
  206. const auto nameContentView = AZ::SceneAPI::Containers::Views::MakePairView(graph.GetNameStorage(), graph.GetContentStorage());
  207. auto meshChildView = AZ::SceneAPI::Containers::Views::MakeSceneGraphChildView<AZ::SceneAPI::Containers::Views::AcceptEndPointsOnly>(
  208. graph, nodeIndex, nameContentView.begin(), true);
  209. for (auto child = meshChildView.begin(); child != meshChildView.end(); ++child)
  210. {
  211. AZ::SceneData::GraphData::BlendShapeData* blendShape =
  212. azrtti_cast<AZ::SceneData::GraphData::BlendShapeData*>(child->second.get());
  213. if (blendShape)
  214. {
  215. outBlendShapes.emplace_back(blendShape);
  216. }
  217. }
  218. }
  219. bool TangentGenerateComponent::GenerateTangentsForMesh(
  220. AZ::SceneAPI::Containers::Scene& scene,
  221. const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex,
  222. AZ::SceneAPI::DataTypes::IMeshData* meshData,
  223. AZ::SceneAPI::DataTypes::TangentGenerationMethod ruleGenerationMethod)
  224. {
  225. AZ::SceneAPI::Containers::SceneGraph& graph = scene.GetGraph();
  226. // Check if we have any UV data, if not, we cannot possibly generate the tangents.
  227. const size_t uvSetCount = CalcUvSetCount(graph, nodeIndex);
  228. if (uvSetCount == 0)
  229. {
  230. AZ_Warning(AZ::SceneAPI::Utilities::WarningWindow, false, "Cannot generate tangents for this mesh, as it has no UV coordinates.\n");
  231. return true; // No fatal error
  232. }
  233. const AZ::SceneAPI::SceneData::TangentsRule* tangentsRule = GetTangentRule(scene);
  234. // Find all blend shape data under the mesh. We need to generate the tangent and bitangent for blend shape as well.
  235. AZStd::vector<AZ::SceneData::GraphData::BlendShapeData*> blendShapes;
  236. FindBlendShapes(graph, nodeIndex, blendShapes);
  237. // Generate tangents/bitangents for all uv sets.
  238. bool allSuccess = true;
  239. for (size_t uvSetIndex = 0; uvSetIndex < uvSetCount; ++uvSetIndex)
  240. {
  241. AZ::SceneAPI::DataTypes::IMeshVertexUVData* uvData = FindUvData(graph, nodeIndex, uvSetIndex);
  242. if (!uvData)
  243. {
  244. AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, "Cannot generate tangents for uv set %zu as it cannot be retrieved.\n", uvSetIndex);
  245. continue;
  246. }
  247. // Check if we had tangents inside the source scene file.
  248. AZ::SceneAPI::DataTypes::TangentGenerationMethod generationMethod = ruleGenerationMethod;
  249. AZ::SceneAPI::DataTypes::IMeshVertexTangentData* tangentData = FindTangentData(graph, nodeIndex, uvSetIndex);
  250. AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* bitangentData = FindBitangentData(graph, nodeIndex, uvSetIndex);
  251. // If all we need is import from the source scene, and we have tangent data from the source scene already, then skip generating.
  252. if ((generationMethod == AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene))
  253. {
  254. if (tangentData && bitangentData)
  255. {
  256. AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Using source scene tangents and bitangents for uv set %zu for mesh '%s'.\n",
  257. uvSetIndex, scene.GetGraph().GetNodeName(nodeIndex).GetName());
  258. continue;
  259. }
  260. else
  261. {
  262. // In case there are no tangents/bitangents while the user selected to use the source ones, default to MikkT.
  263. AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Cannot use source scene tangents as there are none in the asset for mesh '%s' for uv set %zu. Defaulting to generating tangents using MikkT.\n",
  264. scene.GetGraph().GetNodeName(nodeIndex).GetName(), uvSetIndex);
  265. generationMethod = AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT;
  266. }
  267. }
  268. if (!tangentData)
  269. {
  270. if (!AZ::SceneGenerationComponents::TangentGenerateComponent::CreateTangentLayer(scene.GetManifest(), nodeIndex, meshData->GetVertexCount(), uvSetIndex,
  271. generationMethod, graph, &tangentData))
  272. {
  273. AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, "Failed to create tangents data set for mesh %s for uv set %zu.\n",
  274. scene.GetGraph().GetNodeName(nodeIndex).GetName(), uvSetIndex);
  275. continue;
  276. }
  277. }
  278. AZ_Assert(tangentData == FindTangentData(graph, nodeIndex, uvSetIndex), "Used tangent data is not the same as the graph returns.");
  279. if (!bitangentData)
  280. {
  281. if (!AZ::SceneGenerationComponents::TangentGenerateComponent::CreateBitangentLayer(scene.GetManifest(), nodeIndex, meshData->GetVertexCount(), uvSetIndex,
  282. generationMethod, graph, &bitangentData))
  283. {
  284. AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, "Failed to create bitangents data set for mesh %s for uv set %zu.\n",
  285. scene.GetGraph().GetNodeName(nodeIndex).GetName(), uvSetIndex);
  286. continue;
  287. }
  288. }
  289. AZ_Assert(bitangentData == FindBitangentData(graph, nodeIndex, uvSetIndex), "Used bitangent data is not the same as the graph returns.");
  290. tangentData->SetGenerationMethod(generationMethod);
  291. bitangentData->SetGenerationMethod(generationMethod);
  292. switch (generationMethod)
  293. {
  294. // Generate using MikkT space.
  295. case AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT:
  296. {
  297. const AZ::SceneAPI::DataTypes::MikkTSpaceMethod tSpaceMethod = tangentsRule ? tangentsRule->GetMikkTSpaceMethod() : AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpace;
  298. allSuccess &= AZ::TangentGeneration::Mesh::MikkT::GenerateTangents(meshData, uvData, tangentData, bitangentData, tSpaceMethod);
  299. for (AZ::SceneData::GraphData::BlendShapeData* blendShape : blendShapes)
  300. {
  301. allSuccess &= AZ::TangentGeneration::BlendShape::MikkT::GenerateTangents(blendShape, uvSetIndex, tSpaceMethod);
  302. }
  303. }
  304. break;
  305. default:
  306. {
  307. AZ_Assert(false, "Unknown tangent generation method selected (%d) for UV set %d, cannot generate tangents.\n", static_cast<AZ::u32>(generationMethod), uvSetIndex);
  308. allSuccess = false;
  309. }
  310. }
  311. }
  312. return allSuccess;
  313. }
  314. size_t TangentGenerateComponent::CalcUvSetCount(AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex) const
  315. {
  316. const auto nameContentView = AZ::SceneAPI::Containers::Views::MakePairView(graph.GetNameStorage(), graph.GetContentStorage());
  317. size_t result = 0;
  318. auto meshChildView = AZ::SceneAPI::Containers::Views::MakeSceneGraphChildView<AZ::SceneAPI::Containers::Views::AcceptEndPointsOnly>(graph, nodeIndex, nameContentView.begin(), true);
  319. for (auto child = meshChildView.begin(); child != meshChildView.end(); ++child)
  320. {
  321. AZ::SceneAPI::DataTypes::IMeshVertexUVData* data = azrtti_cast<AZ::SceneAPI::DataTypes::IMeshVertexUVData*>(child->second.get());
  322. if (data)
  323. {
  324. result++;
  325. }
  326. }
  327. return result;
  328. }
  329. AZ::SceneAPI::DataTypes::IMeshVertexUVData* TangentGenerateComponent::FindUvData(AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, AZ::u64 uvSet) const
  330. {
  331. const auto nameContentView = AZ::SceneAPI::Containers::Views::MakePairView(graph.GetNameStorage(), graph.GetContentStorage());
  332. AZ::u64 uvSetIndex = 0;
  333. auto meshChildView = AZ::SceneAPI::Containers::Views::MakeSceneGraphChildView<AZ::SceneAPI::Containers::Views::AcceptEndPointsOnly>(graph, nodeIndex, nameContentView.begin(), true);
  334. for (auto child = meshChildView.begin(); child != meshChildView.end(); ++child)
  335. {
  336. AZ::SceneAPI::DataTypes::IMeshVertexUVData* data = azrtti_cast<AZ::SceneAPI::DataTypes::IMeshVertexUVData*>(child->second.get());
  337. if (data)
  338. {
  339. if (uvSetIndex == uvSet)
  340. {
  341. return data;
  342. }
  343. uvSetIndex++;
  344. }
  345. }
  346. return nullptr;
  347. }
  348. AZ::SceneAPI::DataTypes::IMeshVertexTangentData* TangentGenerateComponent::FindTangentData(AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, AZ::u64 setIndex) const
  349. {
  350. const auto nameContentView = AZ::SceneAPI::Containers::Views::MakePairView(graph.GetNameStorage(), graph.GetContentStorage());
  351. auto meshChildView = AZ::SceneAPI::Containers::Views::MakeSceneGraphChildView<AZ::SceneAPI::Containers::Views::AcceptEndPointsOnly>(graph, nodeIndex, nameContentView.begin(), true);
  352. for (auto child = meshChildView.begin(); child != meshChildView.end(); ++child)
  353. {
  354. AZ::SceneAPI::DataTypes::IMeshVertexTangentData* data = azrtti_cast<AZ::SceneAPI::DataTypes::IMeshVertexTangentData*>(child->second.get());
  355. if (data && setIndex == data->GetTangentSetIndex())
  356. {
  357. return data;
  358. }
  359. }
  360. return nullptr;
  361. }
  362. bool TangentGenerateComponent::CreateTangentLayer(AZ::SceneAPI::Containers::SceneManifest& manifest,
  363. const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex,
  364. size_t numVerts,
  365. size_t uvSetIndex,
  366. AZ::SceneAPI::DataTypes::TangentGenerationMethod generationMethod,
  367. AZ::SceneAPI::Containers::SceneGraph& graph,
  368. AZ::SceneAPI::DataTypes::IMeshVertexTangentData** outTangentData)
  369. {
  370. *outTangentData = nullptr;
  371. AZStd::shared_ptr<SceneData::GraphData::MeshVertexTangentData> tangentData = AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexTangentData>();
  372. tangentData->Resize(numVerts);
  373. AZ_Assert(tangentData, "Failed to allocate tangent data for scene graph.");
  374. if (!tangentData)
  375. {
  376. AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, "Failed to allocate tangent data.\n");
  377. return false;
  378. }
  379. tangentData->SetTangentSetIndex(uvSetIndex);
  380. tangentData->SetGenerationMethod(generationMethod);
  381. const AZStd::string tangentGeneratedName = AZStd::string::format("TangentSet_%zu", uvSetIndex);
  382. const AZStd::string tangentSetName = AZ::SceneAPI::DataTypes::Utilities::CreateUniqueName<SceneData::GraphData::MeshVertexBitangentData>(tangentGeneratedName, manifest);
  383. AZ::SceneAPI::Containers::SceneGraph::NodeIndex newIndex = graph.AddChild(nodeIndex, tangentSetName.c_str(), tangentData);
  384. AZ_Assert(newIndex.IsValid(), "Failed to create SceneGraph node for tangent attribute.");
  385. if (!newIndex.IsValid())
  386. {
  387. AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, "Failed to create node in scene graph that stores tangent data.\n");
  388. return false;
  389. }
  390. graph.MakeEndPoint(newIndex);
  391. *outTangentData = tangentData.get();
  392. return true;
  393. }
  394. AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* TangentGenerateComponent::FindBitangentData(AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, AZ::u64 setIndex) const
  395. {
  396. const auto nameContentView = AZ::SceneAPI::Containers::Views::MakePairView(graph.GetNameStorage(), graph.GetContentStorage());
  397. auto meshChildView = AZ::SceneAPI::Containers::Views::MakeSceneGraphChildView<AZ::SceneAPI::Containers::Views::AcceptEndPointsOnly>(graph, nodeIndex, nameContentView.begin(), true);
  398. for (auto child = meshChildView.begin(); child != meshChildView.end(); ++child)
  399. {
  400. AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* data = azrtti_cast<AZ::SceneAPI::DataTypes::IMeshVertexBitangentData*>(child->second.get());
  401. if (data && setIndex == data->GetBitangentSetIndex())
  402. {
  403. return data;
  404. }
  405. }
  406. return nullptr;
  407. }
  408. bool TangentGenerateComponent::CreateBitangentLayer(AZ::SceneAPI::Containers::SceneManifest& manifest,
  409. const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex,
  410. size_t numVerts,
  411. size_t uvSetIndex,
  412. AZ::SceneAPI::DataTypes::TangentGenerationMethod generationMethod,
  413. AZ::SceneAPI::Containers::SceneGraph& graph,
  414. AZ::SceneAPI::DataTypes::IMeshVertexBitangentData** outBitangentData)
  415. {
  416. *outBitangentData = nullptr;
  417. AZStd::shared_ptr<AZ::SceneData::GraphData::MeshVertexBitangentData> bitangentData = AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexBitangentData>();
  418. bitangentData->Resize(numVerts);
  419. AZ_Assert(bitangentData, "Failed to allocate bitangent data for scene graph.");
  420. if (!bitangentData)
  421. {
  422. AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, "Failed to allocate bitangent data.\n");
  423. return false;
  424. }
  425. bitangentData->SetBitangentSetIndex(uvSetIndex);
  426. bitangentData->SetGenerationMethod(generationMethod);
  427. const AZStd::string bitangentGeneratedName = AZStd::string::format("BitangentSet_%zu", uvSetIndex);
  428. const AZStd::string bitangentSetName = AZ::SceneAPI::DataTypes::Utilities::CreateUniqueName<SceneData::GraphData::MeshVertexBitangentData>(bitangentGeneratedName, manifest);
  429. AZ::SceneAPI::Containers::SceneGraph::NodeIndex newIndex = graph.AddChild(nodeIndex, bitangentSetName.c_str(), bitangentData);
  430. AZ_Assert(newIndex.IsValid(), "Failed to create SceneGraph node for bitangent attribute.");
  431. if (!newIndex.IsValid())
  432. {
  433. AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, "Failed to create node in scene graph that stores bitangent data.\n");
  434. return false;
  435. }
  436. graph.MakeEndPoint(newIndex);
  437. *outBitangentData = bitangentData.get();
  438. return true;
  439. }
  440. } // namespace AZ::SceneGenerationComponents