| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <Atom/RPI.Public/Model/Model.h>
- #include <Atom/RPI.Reflect/Model/ModelAsset.h>
- #include <Atom/RHI/Factory.h>
- #include <AtomCore/Instance/InstanceDatabase.h>
- #include <AzCore/Debug/Timer.h>
- #include <AzCore/Jobs/JobFunction.h>
- #include <AzCore/Math/IntersectSegment.h>
- // Enable to show profile logs of how long it takes to raycast against models in the Editor
- //#define AZ_RPI_PROFILE_RAYCASTING_AGAINST_MODELS
- namespace AZ
- {
- namespace RPI
- {
- Data::Instance<Model> Model::FindOrCreate(const Data::Asset<ModelAsset>& modelAsset)
- {
- return Data::InstanceDatabase<Model>::Instance().FindOrCreate(
- Data::InstanceId::CreateFromAsset(modelAsset),
- modelAsset);
- }
- void Model::TEMPOrphanFromDatabase(const Data::Asset<ModelAsset>& modelAsset)
- {
- for (auto& modelLodAsset : modelAsset->GetLodAssets())
- {
- for (auto& mesh : modelLodAsset->GetMeshes())
- {
- for (auto& streamBufferInfo : mesh.GetStreamBufferInfoList())
- {
- Data::InstanceDatabase<Buffer>::Instance().TEMPOrphan(
- Data::InstanceId::CreateFromAsset(streamBufferInfo.m_bufferAssetView.GetBufferAsset()));
- }
- Data::InstanceDatabase<Buffer>::Instance().TEMPOrphan(
- Data::InstanceId::CreateFromAsset(mesh.GetIndexBufferAssetView().GetBufferAsset()));
- }
- Data::InstanceDatabase<ModelLod>::Instance().TEMPOrphan(Data::InstanceId::CreateFromAsset(modelLodAsset));
- }
- Data::InstanceDatabase<Model>::Instance().TEMPOrphan(Data::InstanceId::CreateFromAsset(modelAsset));
- }
- size_t Model::GetLodCount() const
- {
- return m_lods.size();
- }
- AZStd::span<const Data::Instance<ModelLod>> Model::GetLods() const
- {
- return m_lods;
- }
- Data::Instance<Model> Model::CreateInternal(const Data::Asset<ModelAsset>& modelAsset)
- {
- AZ_PROFILE_SCOPE(RPI, "Model: CreateInternal");
- Data::Instance<Model> model = aznew Model();
- const RHI::ResultCode resultCode = model->Init(modelAsset);
- if (resultCode == RHI::ResultCode::Success)
- {
- return model;
- }
- return nullptr;
- }
- RHI::ResultCode Model::Init(const Data::Asset<ModelAsset>& modelAsset)
- {
- AZ_PROFILE_SCOPE(RPI, "Model: Init");
- m_lods.resize(modelAsset->GetLodAssets().size());
- for (size_t lodIndex = 0; lodIndex < m_lods.size(); ++lodIndex)
- {
- const auto lodAssets = modelAsset->GetLodAssets();
- const Data::Asset<ModelLodAsset> lodAsset = lodAssets[lodIndex];
- if (!lodAsset)
- {
- AZ_Error("Model", false, "Invalid Operation: Last ModelLod is not loaded.");
- return RHI::ResultCode::Fail;
- }
- Data::Instance<ModelLod> lodInstance = ModelLod::FindOrCreate(lodAsset, modelAsset);
- if (lodInstance == nullptr)
- {
- return RHI::ResultCode::Fail;
- }
- for (const AZ::RPI::ModelLod::Mesh& mesh : lodInstance->GetMeshes())
- {
- for (const AZ::RPI::ModelLod::StreamBufferInfo& stream : mesh.m_streamInfo)
- {
- if (stream.m_semantic.m_name.GetStringView().starts_with(RHI::ShaderSemantic::UvStreamSemantic))
- {
- // For unnamed UVs, use the semantic instead.
- if (stream.m_customName.IsEmpty())
- {
- m_uvNames.insert(AZ::Name(stream.m_semantic.ToString()));
- }
- else
- {
- m_uvNames.insert(stream.m_customName);
- }
- }
- }
- }
- m_lods[lodIndex] = AZStd::move(lodInstance);
- }
- m_modelAsset = modelAsset;
- m_isUploadPending = true;
- return RHI::ResultCode::Success;
- }
- void Model::WaitForUpload()
- {
- if (m_isUploadPending)
- {
- AZ_PROFILE_SCOPE(RPI, "Model::WaitForUpload - %s", GetDatabaseName());
- for (const Data::Instance<ModelLod>& lod : m_lods)
- {
- lod->WaitForUpload();
- }
- m_isUploadPending = false;
- }
- }
- bool Model::IsUploadPending() const
- {
- return m_isUploadPending;
- }
- const Data::Asset<ModelAsset>& Model::GetModelAsset() const
- {
- return m_modelAsset;
- }
- bool Model::LocalRayIntersection(const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const
- {
- AZ_PROFILE_SCOPE(RPI, "Model: LocalRayIntersection");
- if (!GetModelAsset())
- {
- AZ_Assert(false, "Invalid Model - not created from a ModelAsset?");
- return false;
- }
- float start;
- float end;
- const int result = Intersect::IntersectRayAABB2(rayStart, rayDir.GetReciprocal(), GetModelAsset()->GetAabb(), start, end);
- if (Intersect::ISECT_RAY_AABB_NONE != result)
- {
- ModelAsset* modelAssetPtr = m_modelAsset.Get();
- if (modelAssetPtr && modelAssetPtr->SupportLocalRayIntersection())
- {
- #if defined(AZ_RPI_PROFILE_RAYCASTING_AGAINST_MODELS)
- AZ::Debug::Timer timer;
- timer.Stamp();
- #endif
- constexpr bool AllowBruteForce = false;
- const bool hit = modelAssetPtr->LocalRayIntersectionAgainstModel(
- rayStart, rayDir, AllowBruteForce, distanceNormalized, normal);
- #if defined(AZ_RPI_PROFILE_RAYCASTING_AGAINST_MODELS)
- if (hit)
- {
- AZ_Printf("Model", "Model::LocalRayIntersection took %.2f ms", timer.StampAndGetDeltaTimeInSeconds() * 1000.f);
- }
- #endif
- return hit;
- }
- }
- return false;
- }
- bool Model::RayIntersection(
- const AZ::Transform& modelTransform,
- const AZ::Vector3& nonUniformScale,
- const AZ::Vector3& rayStart,
- const AZ::Vector3& rayDir,
- float& distanceNormalized,
- AZ::Vector3& normal) const
- {
- AZ_PROFILE_SCOPE(RPI, "Model: RayIntersection");
- const AZ::Vector3 clampedScale = nonUniformScale.GetMax(AZ::Vector3(AZ::MinTransformScale));
- const AZ::Transform inverseTM = modelTransform.GetInverse();
- const AZ::Vector3 raySrcLocal = inverseTM.TransformPoint(rayStart) / clampedScale;
- // Instead of just rotating 'rayDir' we need it to be scaled too, so that 'distanceNormalized' will be in the target units rather
- // than object local units.
- const AZ::Vector3 rayDest = rayStart + rayDir;
- const AZ::Vector3 rayDestLocal = inverseTM.TransformPoint(rayDest) / clampedScale;
- const AZ::Vector3 rayDirLocal = rayDestLocal - raySrcLocal;
- const bool result = LocalRayIntersection(raySrcLocal, rayDirLocal, distanceNormalized, normal);
- normal = (normal * clampedScale).GetNormalized();
- return result;
- }
- const AZStd::unordered_set<AZ::Name>& Model::GetUvNames() const
- {
- return m_uvNames;
- }
- } // namespace RPI
- } // namespace AZ
|