Files
o3de/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp
T
yuriy0 a9c55c1070 Update actor render bounding box (#991)
* Extend MeshFeatureProcessor to allow changing the mesh bbox, which requires re-compute the culling data for that mesh

* Update actor mesh bbox when EMFX actor instance bbox changes.

Also use the actor instance global bbox to compute the local bbox for the skinned render mesh, instead of the using the static bounds based bbox, as not every actor instance is going to be using the static bounds bbox.

* Store per-instance mesh AABB in the right place.

In the MeshInstanceData, which is unique per instance, instead of in the Model, which is shared between all instances.

For greater clarity, also remove Model::m_aabb and the corresponding getter and setter, as it isn't immediately obvious whether this gets the model asset bbox or the mesh instance bbox. Callers should instead be explicit about which bbox they want.

* Bug fix: model asset is not necessarily ready in AcquireMesh

* Remove now-unused forward declaration

* Update MockMeshFeatureProcessor with SetLocalAabb/GetLocalAabb
2021-06-15 13:09:30 -05:00

199 lines
7.2 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Atom/RPI.Public/Model/Model.h>
#include <Atom/RHI/Factory.h>
#include <AzCore/Debug/EventTrace.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);
}
size_t Model::GetLodCount() const
{
return m_lods.size();
}
AZStd::array_view<Data::Instance<ModelLod>> Model::GetLods() const
{
return m_lods;
}
Data::Instance<Model> Model::CreateInternal(ModelAsset& modelAsset)
{
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
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(ModelAsset& modelAsset)
{
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
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);
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, AZ::Data::AssetLoadBehavior::PreLoad };
m_isUploadPending = true;
return RHI::ResultCode::Success;
}
void Model::WaitForUpload()
{
if (m_isUploadPending)
{
AZ_PROFILE_SCOPE_STALL_DYNAMIC(Debug::ProfileCategory::AzRender, "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_FUNCTION(Debug::ProfileCategory::AzRender);
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
const bool hit = modelAssetPtr->LocalRayIntersectionAgainstModel(rayStart, rayDir, 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_FUNCTION(Debug::ProfileCategory::AzRender);
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