You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
7.1 KiB
C++
199 lines
7.1 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_aabb = modelAsset.GetAabb();
|
|
|
|
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 AZ::Aabb& Model::GetAabb() const
|
|
{
|
|
return m_aabb;
|
|
}
|
|
|
|
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);
|
|
float start;
|
|
float end;
|
|
const int result = Intersect::IntersectRayAABB2(rayStart, rayDir.GetReciprocal(), m_aabb, 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
|