Files
o3de/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelLodAssetCreator.cpp
T
Benjamin Jillich 0fa00a117c [ATOM-14477] Add clone function to model and/or model asset (#135)
* Added clone methods for the buffer, model lod and model assets.
* Cloning works via the creators.
* Mesh handle/instance now knows about the model asset it originated from as well as the cloned model asset that we need for instancing until instancing works without the dependencies to the asset ids.
* MeshComponentRequestBus returns the original asset id and the model asset. If you need access to the clone, go by the model.
* Cloth component mesh now gettings its model asset from the model as it needs to work on the clone.
* Moved the requires cloning function from the mesh loader in the mesh feature processor to the mesh component controller.
* As we need the model asset to be loaded before we can check if it requires cloning or not, we couldn't pass in a bool from the controller but had to use a callback.
* Using asset hint instead of asset id for the model lod asset creator error.
* Storing a map of source buffer asset ids and the actual cloned buffer assets rather than just their ids.
* Using the buffer assets from the new map directly and removed the search process in the mesh loop where the asset views are created.
* Fixed the vegetation mocks.
* Using the actor's mesh asset when emitting the on model ready event for the mesh component notification bus.
* Mesh components notifications connection policy changed to adapt to cloned model asset.
* Handling empty meshes in model lod asset creator.
* Removed the requires cloning callback from the mesh feature processor and made it a parameter to the acquire mesh function
* Fixing mocks and unit tests
2021-04-23 18:33:00 +02:00

330 lines
12 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 <AzCore/std/containers/set.h>
#include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
#include <Atom/RPI.Reflect/Model/ModelLodAssetCreator.h>
#include <AzCore/Asset/AssetManager.h>
namespace AZ
{
namespace RPI
{
void ModelLodAssetCreator::Begin(const Data::AssetId& assetId)
{
BeginCommon(assetId);
}
void ModelLodAssetCreator::SetLodIndexBuffer(const Data::Asset<BufferAsset>& bufferAsset)
{
if (ValidateIsReady())
{
m_asset->m_indexBuffer = AZStd::move(bufferAsset);
}
}
void ModelLodAssetCreator::AddLodStreamBuffer(const Data::Asset<BufferAsset>& bufferAsset)
{
if (ValidateIsReady())
{
m_asset->m_streamBuffers.push_back(AZStd::move(bufferAsset));
}
}
void ModelLodAssetCreator::BeginMesh()
{
if (ValidateIsReady())
{
m_currentMesh = ModelLodAsset::Mesh();
m_meshBegan = true;
}
}
void ModelLodAssetCreator::SetMeshName(const AZ::Name& name)
{
if (ValidateIsMeshReady())
{
m_currentMesh.m_name = name;
}
}
void ModelLodAssetCreator::SetMeshAabb(AZ::Aabb&& aabb)
{
if (ValidateIsMeshReady())
{
m_currentMesh.m_aabb = AZStd::move(aabb);
}
}
void ModelLodAssetCreator::SetMeshMaterialAsset(const Data::Asset<MaterialAsset>& materialAsset)
{
if (ValidateIsMeshReady())
{
m_currentMesh.m_materialAsset = materialAsset;
}
}
void ModelLodAssetCreator::SetMeshIndexBuffer(const BufferAssetView& bufferAssetView)
{
if (!ValidateIsMeshReady())
{
return;
}
if (m_currentMesh.m_indexBufferAssetView.GetBufferAsset().Get() != nullptr)
{
ReportError("The current mesh has already had an index buffer set.");
return;
}
m_currentMesh.m_indexBufferAssetView = AZStd::move(bufferAssetView);
}
bool ModelLodAssetCreator::AddMeshStreamBuffer(
const RHI::ShaderSemantic& streamSemantic,
const AZ::Name& customName,
const BufferAssetView& bufferAssetView)
{
if (!ValidateIsMeshReady())
{
return false;
}
if (m_currentMesh.m_streamBufferInfo.size() >= RHI::Limits::Pipeline::StreamCountMax)
{
ReportError("Cannot add another stream buffer info. Maximum of %d already reached.", RHI::Limits::Pipeline::StreamCountMax);
return false;
}
// If this streamId already exists throw an error
for (const ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo : m_currentMesh.m_streamBufferInfo)
{
if (streamBufferInfo.m_semantic == streamSemantic || (!streamBufferInfo.m_customName.IsEmpty() && streamBufferInfo.m_customName == customName))
{
ReportError("Failed to add Stream Buffer. Buffer with this streamId or name already exists.");
return false;
}
}
ModelLodAsset::Mesh::StreamBufferInfo streamBufferInfo;
streamBufferInfo.m_semantic = streamSemantic;
streamBufferInfo.m_customName = customName;
streamBufferInfo.m_bufferAssetView = AZStd::move(bufferAssetView);
m_currentMesh.m_streamBufferInfo.push_back(AZStd::move(streamBufferInfo));
return true;
}
void ModelLodAssetCreator::AddMeshStreamBuffer(
const ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo)
{
if (!ValidateIsMeshReady())
{
return;
}
// If this semantic already exists throw an error
for (const ModelLodAsset::Mesh::StreamBufferInfo& existingInfo : m_currentMesh.m_streamBufferInfo)
{
if (existingInfo.m_semantic == streamBufferInfo.m_semantic || (!existingInfo.m_customName.IsEmpty() && existingInfo.m_customName == streamBufferInfo.m_customName))
{
ReportError("Failed to add Stream Buffer. Buffer with this semantic or name already exists.");
return;
}
}
m_currentMesh.m_streamBufferInfo.push_back(AZStd::move(streamBufferInfo));
}
void ModelLodAssetCreator::EndMesh()
{
if (ValidateIsMeshReady() && ValidateMesh(m_currentMesh))
{
m_asset->AddMesh(AZStd::move(m_currentMesh));
m_meshBegan = false;
}
}
bool ModelLodAssetCreator::End(Data::Asset<ModelLodAsset>& result)
{
if (ValidateIsReady() && ValidateIsMeshEnded() && ValidateLod())
{
m_asset->SetReady();
return EndCommon(result);
}
return false;
}
bool ModelLodAssetCreator::ValidateIsMeshReady()
{
if (!ValidateIsReady())
{
return false;
}
if (!m_meshBegan)
{
AZ_Assert(false, "BeginMesh() was not called");
return false;
}
return true;
}
bool ModelLodAssetCreator::ValidateIsMeshEnded()
{
if (m_meshBegan)
{
AZ_Assert(false, "MeshEnd() was not called");
return false;
}
return true;
}
bool ModelLodAssetCreator::ValidateLod()
{
if (m_asset->GetMeshes().empty())
{
ReportError("No meshes have been provided for this LOD");
return false;
}
return true;
}
bool ModelLodAssetCreator::ValidateMesh(const ModelLodAsset::Mesh& mesh)
{
if (mesh.GetVertexCount() == 0)
{
ReportError("Mesh has a vertex count of 0");
return false;
}
if (mesh.GetIndexCount() == 0)
{
ReportError("Mesh has an index count of 0");
return false;
}
if (!mesh.GetAabb().IsValid())
{
ReportError("Mesh does not have a valid Aabb");
return false;
}
if (mesh.m_indexBufferAssetView.GetBufferAsset().Get() == nullptr)
{
ReportError("Mesh does not have a valid index buffer");
return false;
}
for (const ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo : mesh.m_streamBufferInfo)
{
if (streamBufferInfo.m_bufferAssetView.GetBufferAsset().Get() == nullptr)
{
ReportError("Mesh has an invalid stream buffer");
return false;
}
}
return true;
}
bool ModelLodAssetCreator::Clone(const Data::Asset<ModelLodAsset>& sourceAsset, Data::Asset<ModelLodAsset>& clonedResult, Data::AssetId& inOutLastCreatedAssetId)
{
AZStd::array_view<ModelLodAsset::Mesh> sourceMeshes = sourceAsset->GetMeshes();
if (sourceMeshes.empty())
{
return true;
}
ModelLodAssetCreator creator;
inOutLastCreatedAssetId.m_subId = inOutLastCreatedAssetId.m_subId + 1;
creator.Begin(inOutLastCreatedAssetId);
// Add the index buffer
const Data::Asset<BufferAsset> sourceIndexBufferAsset = sourceMeshes[0].GetIndexBufferAssetView().GetBufferAsset();
Data::Asset<BufferAsset> clonedIndexBufferAsset;
BufferAssetCreator::Clone(sourceIndexBufferAsset, clonedIndexBufferAsset, inOutLastCreatedAssetId);
creator.SetLodIndexBuffer(clonedIndexBufferAsset);
// Add meshes
AZStd::unordered_map<AZ::Data::AssetId, Data::Asset<BufferAsset>> oldToNewBufferAssets;
for (const ModelLodAsset::Mesh& sourceMesh : sourceMeshes)
{
// Add stream buffers
for (const AZ::RPI::ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo : sourceMesh.GetStreamBufferInfoList())
{
const Data::Asset<BufferAsset>& sourceStreamBuffer = streamBufferInfo.m_bufferAssetView.GetBufferAsset();
const AZ::Data::AssetId sourceBufferAssetId = sourceStreamBuffer.GetId();
// In case the buffer asset id is not part of our old to new asset id mapping, we did not convert and add it yet.
if (oldToNewBufferAssets.find(sourceBufferAssetId) == oldToNewBufferAssets.end())
{
Data::Asset<BufferAsset> streamBufferAsset;
if (!BufferAssetCreator::Clone(sourceStreamBuffer, streamBufferAsset, inOutLastCreatedAssetId))
{
AZ_Error("ModelLodAssetCreator", false,
"Cannot clone buffer asset for '%s'.", sourceBufferAssetId.ToString<AZStd::string>().c_str());
return false;
}
oldToNewBufferAssets[sourceBufferAssetId] = streamBufferAsset;
creator.AddLodStreamBuffer(streamBufferAsset);
}
}
// Add mesh
creator.BeginMesh();
creator.SetMeshName(sourceMesh.GetName());
AZ::Aabb aabb = sourceMesh.GetAabb();
creator.SetMeshAabb(AZStd::move(aabb));
creator.SetMeshMaterialAsset(sourceMesh.GetMaterialAsset());
// Mesh index buffer view
const BufferAssetView& sourceIndexBufferView = sourceMesh.GetIndexBufferAssetView();
BufferAssetView indexBufferAssetView(clonedIndexBufferAsset, sourceIndexBufferView.GetBufferViewDescriptor());
creator.SetMeshIndexBuffer(indexBufferAssetView);
// Mesh stream buffer views
for (const AZ::RPI::ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo : sourceMesh.GetStreamBufferInfoList())
{
// Get the corresponding new buffer asset id from the source buffer.
const AZ::Data::AssetId sourceBufferAssetId = streamBufferInfo.m_bufferAssetView.GetBufferAsset().GetId();
const auto assetIdIterator = oldToNewBufferAssets.find(sourceBufferAssetId);
if (assetIdIterator != oldToNewBufferAssets.end())
{
const Data::Asset<BufferAsset>& clonedBufferAsset = assetIdIterator->second;
BufferAssetView bufferAssetView(clonedBufferAsset, streamBufferInfo.m_bufferAssetView.GetBufferViewDescriptor());
creator.AddMeshStreamBuffer(streamBufferInfo.m_semantic, streamBufferInfo.m_customName, bufferAssetView);
}
else
{
AZ_Error("ModelLodAssetCreator", false,
"Cannot find cloned buffer asset for source buffer asset '%s'.",
sourceBufferAssetId.ToString<AZStd::string>().c_str());
return false;
}
}
creator.EndMesh();
}
return creator.End(clonedResult);
}
} // namespace RPI
} // namespace AZ