b9824ed172
* Updated all array_view uses with the C++20 span. The updates were done in the following order 1. `AZStd::array_view<([^>].+)\* ?>` -> `AZStd::span<\1 const>` 2. `AZStd::array_view<(?:const )(.+)>` -> `AZStd::span<const \1>` 3. `AZStd::array_view` -> `AZStd::span` Removed the implementation of array_view. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added missing whitespace between `const` and the typename for spans. Updated the ShaderTest comparison of the ShaderResourceGroupLayout span to compare the sizes as well Updated comments on some of the methods that stated that they return "an array" to mention they return "a span". Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
219 lines
8.0 KiB
C++
219 lines
8.0 KiB
C++
/*
|
|
* 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::CreateFromAssetId(modelAsset.GetId()),
|
|
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::CreateFromAssetId(streamBufferInfo.m_bufferAssetView.GetBufferAsset().GetId()));
|
|
}
|
|
Data::InstanceDatabase<Buffer>::Instance().TEMPOrphan(
|
|
Data::InstanceId::CreateFromAssetId(mesh.GetIndexBufferAssetView().GetBufferAsset().GetId()));
|
|
}
|
|
Data::InstanceDatabase<ModelLod>::Instance().TEMPOrphan(Data::InstanceId::CreateFromAssetId(modelLodAsset.GetId()));
|
|
}
|
|
|
|
Data::InstanceDatabase<Model>::Instance().TEMPOrphan(
|
|
Data::InstanceId::CreateFromAssetId(modelAsset.GetId()));
|
|
}
|
|
|
|
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 Data::Asset<ModelLodAsset>& lodAsset = modelAsset->GetLodAssets()[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)
|
|
{
|
|
if (ModelAsset* modelAssetPtr = m_modelAsset.Get())
|
|
{
|
|
#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
|