3
0

Model.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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 <Atom/RPI.Public/Model/Model.h>
  9. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  10. #include <Atom/RHI/Factory.h>
  11. #include <AtomCore/Instance/InstanceDatabase.h>
  12. #include <AzCore/Debug/Timer.h>
  13. #include <AzCore/Jobs/JobFunction.h>
  14. #include <AzCore/Math/IntersectSegment.h>
  15. // Enable to show profile logs of how long it takes to raycast against models in the Editor
  16. //#define AZ_RPI_PROFILE_RAYCASTING_AGAINST_MODELS
  17. namespace AZ
  18. {
  19. namespace RPI
  20. {
  21. Data::Instance<Model> Model::FindOrCreate(const Data::Asset<ModelAsset>& modelAsset)
  22. {
  23. return Data::InstanceDatabase<Model>::Instance().FindOrCreate(
  24. Data::InstanceId::CreateFromAsset(modelAsset),
  25. modelAsset);
  26. }
  27. void Model::TEMPOrphanFromDatabase(const Data::Asset<ModelAsset>& modelAsset)
  28. {
  29. for (auto& modelLodAsset : modelAsset->GetLodAssets())
  30. {
  31. for (auto& mesh : modelLodAsset->GetMeshes())
  32. {
  33. for (auto& streamBufferInfo : mesh.GetStreamBufferInfoList())
  34. {
  35. Data::InstanceDatabase<Buffer>::Instance().TEMPOrphan(
  36. Data::InstanceId::CreateFromAsset(streamBufferInfo.m_bufferAssetView.GetBufferAsset()));
  37. }
  38. Data::InstanceDatabase<Buffer>::Instance().TEMPOrphan(
  39. Data::InstanceId::CreateFromAsset(mesh.GetIndexBufferAssetView().GetBufferAsset()));
  40. }
  41. Data::InstanceDatabase<ModelLod>::Instance().TEMPOrphan(Data::InstanceId::CreateFromAsset(modelLodAsset));
  42. }
  43. Data::InstanceDatabase<Model>::Instance().TEMPOrphan(Data::InstanceId::CreateFromAsset(modelAsset));
  44. }
  45. size_t Model::GetLodCount() const
  46. {
  47. return m_lods.size();
  48. }
  49. AZStd::span<const Data::Instance<ModelLod>> Model::GetLods() const
  50. {
  51. return m_lods;
  52. }
  53. Data::Instance<Model> Model::CreateInternal(const Data::Asset<ModelAsset>& modelAsset)
  54. {
  55. AZ_PROFILE_SCOPE(RPI, "Model: CreateInternal");
  56. Data::Instance<Model> model = aznew Model();
  57. const RHI::ResultCode resultCode = model->Init(modelAsset);
  58. if (resultCode == RHI::ResultCode::Success)
  59. {
  60. return model;
  61. }
  62. return nullptr;
  63. }
  64. RHI::ResultCode Model::Init(const Data::Asset<ModelAsset>& modelAsset)
  65. {
  66. AZ_PROFILE_SCOPE(RPI, "Model: Init");
  67. m_lods.resize(modelAsset->GetLodAssets().size());
  68. for (size_t lodIndex = 0; lodIndex < m_lods.size(); ++lodIndex)
  69. {
  70. const auto lodAssets = modelAsset->GetLodAssets();
  71. const Data::Asset<ModelLodAsset> lodAsset = lodAssets[lodIndex];
  72. if (!lodAsset)
  73. {
  74. AZ_Error("Model", false, "Invalid Operation: Last ModelLod is not loaded.");
  75. return RHI::ResultCode::Fail;
  76. }
  77. Data::Instance<ModelLod> lodInstance = ModelLod::FindOrCreate(lodAsset, modelAsset);
  78. if (lodInstance == nullptr)
  79. {
  80. return RHI::ResultCode::Fail;
  81. }
  82. for (const AZ::RPI::ModelLod::Mesh& mesh : lodInstance->GetMeshes())
  83. {
  84. for (const AZ::RPI::ModelLod::StreamBufferInfo& stream : mesh.m_streamInfo)
  85. {
  86. if (stream.m_semantic.m_name.GetStringView().starts_with(RHI::ShaderSemantic::UvStreamSemantic))
  87. {
  88. // For unnamed UVs, use the semantic instead.
  89. if (stream.m_customName.IsEmpty())
  90. {
  91. m_uvNames.insert(AZ::Name(stream.m_semantic.ToString()));
  92. }
  93. else
  94. {
  95. m_uvNames.insert(stream.m_customName);
  96. }
  97. }
  98. }
  99. }
  100. m_lods[lodIndex] = AZStd::move(lodInstance);
  101. }
  102. m_modelAsset = modelAsset;
  103. m_isUploadPending = true;
  104. return RHI::ResultCode::Success;
  105. }
  106. void Model::WaitForUpload()
  107. {
  108. if (m_isUploadPending)
  109. {
  110. AZ_PROFILE_SCOPE(RPI, "Model::WaitForUpload - %s", GetDatabaseName());
  111. for (const Data::Instance<ModelLod>& lod : m_lods)
  112. {
  113. lod->WaitForUpload();
  114. }
  115. m_isUploadPending = false;
  116. }
  117. }
  118. bool Model::IsUploadPending() const
  119. {
  120. return m_isUploadPending;
  121. }
  122. const Data::Asset<ModelAsset>& Model::GetModelAsset() const
  123. {
  124. return m_modelAsset;
  125. }
  126. bool Model::LocalRayIntersection(const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const
  127. {
  128. AZ_PROFILE_SCOPE(RPI, "Model: LocalRayIntersection");
  129. if (!GetModelAsset())
  130. {
  131. AZ_Assert(false, "Invalid Model - not created from a ModelAsset?");
  132. return false;
  133. }
  134. float start;
  135. float end;
  136. const int result = Intersect::IntersectRayAABB2(rayStart, rayDir.GetReciprocal(), GetModelAsset()->GetAabb(), start, end);
  137. if (Intersect::ISECT_RAY_AABB_NONE != result)
  138. {
  139. ModelAsset* modelAssetPtr = m_modelAsset.Get();
  140. if (modelAssetPtr && modelAssetPtr->SupportLocalRayIntersection())
  141. {
  142. #if defined(AZ_RPI_PROFILE_RAYCASTING_AGAINST_MODELS)
  143. AZ::Debug::Timer timer;
  144. timer.Stamp();
  145. #endif
  146. constexpr bool AllowBruteForce = false;
  147. const bool hit = modelAssetPtr->LocalRayIntersectionAgainstModel(
  148. rayStart, rayDir, AllowBruteForce, distanceNormalized, normal);
  149. #if defined(AZ_RPI_PROFILE_RAYCASTING_AGAINST_MODELS)
  150. if (hit)
  151. {
  152. AZ_Printf("Model", "Model::LocalRayIntersection took %.2f ms", timer.StampAndGetDeltaTimeInSeconds() * 1000.f);
  153. }
  154. #endif
  155. return hit;
  156. }
  157. }
  158. return false;
  159. }
  160. bool Model::RayIntersection(
  161. const AZ::Transform& modelTransform,
  162. const AZ::Vector3& nonUniformScale,
  163. const AZ::Vector3& rayStart,
  164. const AZ::Vector3& rayDir,
  165. float& distanceNormalized,
  166. AZ::Vector3& normal) const
  167. {
  168. AZ_PROFILE_SCOPE(RPI, "Model: RayIntersection");
  169. const AZ::Vector3 clampedScale = nonUniformScale.GetMax(AZ::Vector3(AZ::MinTransformScale));
  170. const AZ::Transform inverseTM = modelTransform.GetInverse();
  171. const AZ::Vector3 raySrcLocal = inverseTM.TransformPoint(rayStart) / clampedScale;
  172. // Instead of just rotating 'rayDir' we need it to be scaled too, so that 'distanceNormalized' will be in the target units rather
  173. // than object local units.
  174. const AZ::Vector3 rayDest = rayStart + rayDir;
  175. const AZ::Vector3 rayDestLocal = inverseTM.TransformPoint(rayDest) / clampedScale;
  176. const AZ::Vector3 rayDirLocal = rayDestLocal - raySrcLocal;
  177. const bool result = LocalRayIntersection(raySrcLocal, rayDirLocal, distanceNormalized, normal);
  178. normal = (normal * clampedScale).GetNormalized();
  179. return result;
  180. }
  181. const AZStd::unordered_set<AZ::Name>& Model::GetUvNames() const
  182. {
  183. return m_uvNames;
  184. }
  185. } // namespace RPI
  186. } // namespace AZ