Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,189 @@
/*
* 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 <Model/MaterialAssetBuilderComponent.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Math/Color.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMaterialData.h>
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialConverterBus.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
namespace AZ
{
namespace RPI
{
static const char* MaterialExporterName = "Scene Material Builder";
void MaterialAssetDependenciesComponent::Reflect(ReflectContext* context)
{
if (auto* serialize = azrtti_cast<SerializeContext*>(context))
{
serialize->Class<MaterialAssetDependenciesComponent, Component>()
->Version(4)
->Attribute(Edit::Attributes::SystemComponentTags, AZStd::vector<Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }));
}
}
void MaterialAssetDependenciesComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("MaterialAssetDependenciesService", 0x28bbd0f3));
}
void MaterialAssetDependenciesComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("MaterialAssetDependenciesService", 0x28bbd0f3));
}
void MaterialAssetDependenciesComponent::Activate()
{
SceneAPI::SceneBuilderDependencyBus::Handler::BusConnect();
}
void MaterialAssetDependenciesComponent::Deactivate()
{
SceneAPI::SceneBuilderDependencyBus::Handler::BusDisconnect();
}
void MaterialAssetDependenciesComponent::ReportJobDependencies(SceneAPI::JobDependencyList& jobDependencyList, const char* platformIdentifier)
{
AssetBuilderSDK::SourceFileDependency materialTypeSource;
// Right now, FBX importing only supports a single material type, once that changes, this will have to be re-designed, see ATOM-3554
RPI::MaterialConverterBus::BroadcastResult(materialTypeSource.m_sourceFileDependencyPath, &RPI::MaterialConverterBus::Events::GetMaterialTypePath);
AssetBuilderSDK::JobDependency jobDependency;
jobDependency.m_jobKey = "Atom Material Builder";
jobDependency.m_sourceFile = materialTypeSource;
jobDependency.m_platformIdentifier = platformIdentifier;
jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order;
jobDependencyList.push_back(jobDependency);
}
void MaterialAssetBuilderComponent::Reflect(ReflectContext* context)
{
if (auto* serialize = azrtti_cast<SerializeContext*>(context))
{
serialize->Class<MaterialAssetBuilderComponent, SceneAPI::SceneCore::ExportingComponent>()
->Version(13); // [ATOM-13410]
}
}
uint32_t MaterialAssetBuilderComponent::GetMaterialAssetSubId(uint64_t materialUid)
{
// [GFX TODO] I am suggesting we use the first two 16bits for different kind of assets generated from a Scene
// For example, 0x10000 for mesh, 0x20000 for material, 0x30000 for animation, 0x40000 for scene graph and etc.
// so the subid can be evaluated for reference across different assets generate within this fbx.
/*const uint32_t materialPrefix = 0x20000;
AZ_Assert(materialPrefix > materialId, "materialId should be smaller than materialPrefix");
return materialPrefix + materialId;*/
return static_cast<uint32_t>(materialUid);
}
MaterialAssetBuilderComponent::MaterialAssetBuilderComponent()
{
BindToCall(&MaterialAssetBuilderComponent::BuildMaterials);
}
SceneAPI::Events::ProcessingResult MaterialAssetBuilderComponent::BuildMaterials(MaterialAssetBuilderContext& context) const
{
const auto& scene = context.m_scene;
const Uuid sourceSceneUuid = scene.GetSourceGuid();
const auto& sceneGraph = scene.GetGraph();
auto names = sceneGraph.GetNameStorage();
auto content = sceneGraph.GetContentStorage();
auto pairView = SceneAPI::Containers::Views::MakePairView(names, content);
auto view = SceneAPI::Containers::Views::MakeSceneGraphDownwardsView<
SceneAPI::Containers::Views::BreadthFirst>(
sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true);
struct NamedMaterialSourceData
{
MaterialSourceData m_data;
AZStd::string m_name;
};
AZStd::unordered_map<uint64_t, NamedMaterialSourceData> materialSourceDataByUid;
for (const auto& viewIt : view)
{
if (viewIt.second == nullptr)
{
continue;
}
if (azrtti_istypeof<SceneAPI::DataTypes::IMaterialData>(viewIt.second.get()))
{
// Convert MaterialData to MaterialSourceData and add it to materialSourceDataByUid
auto materialData = AZStd::static_pointer_cast<const SceneAPI::DataTypes::IMaterialData>(viewIt.second);
uint64_t materialUid = materialData->GetUniqueId();
if (materialSourceDataByUid.find(materialUid) != materialSourceDataByUid.end())
{
continue;
}
// The source data for generating material asset
MaterialSourceData sourceData;
// User hook to create their materials based on the data from Fbx pipeline
bool result = false;
RPI::MaterialConverterBus::BroadcastResult(result, &RPI::MaterialConverterBus::Events::ConvertMaterial, *materialData, sourceData);
if (result)
{
materialSourceDataByUid[materialUid] = { AZStd::move(sourceData), materialData->GetMaterialName() };
}
}
}
// Build material assets.
for (auto& itr : materialSourceDataByUid)
{
const uint64_t materialUid = itr.first;
Data::AssetId assetId(sourceSceneUuid, GetMaterialAssetSubId(materialUid));
auto materialSourceData = itr.second;
Outcome<Data::Asset<MaterialAsset>> result = materialSourceData.m_data.CreateMaterialAsset(assetId, "", false);
if (result.IsSuccess())
{
context.m_outputMaterialsByUid[materialUid] = { result.GetValue(), materialSourceData.m_name };
}
else
{
AZ_Error(MaterialExporterName, false, "Create material failed");
return SceneAPI::Events::ProcessingResult::Failure;
}
}
return SceneAPI::Events::ProcessingResult::Success;
}
} // namespace RPI
} // namespace AZ
@@ -0,0 +1,74 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Serialization/Utils.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/SceneBuilderDependencyBus.h>
#include <Model/ModelExporterContexts.h>
namespace AZ
{
namespace RPI
{
/**
* Export materials from Scene
*/
class MaterialAssetBuilderComponent : public SceneAPI::SceneCore::ExportingComponent
{
public:
AZ_COMPONENT(
MaterialAssetBuilderComponent,
"{777BB521-FCFE-4382-B8FD-E1EAF846F5C9}",
SceneAPI::SceneCore::ExportingComponent);
MaterialAssetBuilderComponent();
~MaterialAssetBuilderComponent() override = default;
SceneAPI::Events::ProcessingResult BuildMaterials(MaterialAssetBuilderContext& context) const;
// Return the material assetid for material id
static uint32_t GetMaterialAssetSubId(uint64_t materialUid);
// Required for ExportingComponent
static void Reflect(AZ::ReflectContext* context);
};
/**
* Report dependencies for the MaterialAssetBuilderComponent
*/
class MaterialAssetDependenciesComponent
: public Component
, public SceneAPI::SceneBuilderDependencyBus::Handler
{
public:
AZ_COMPONENT(MaterialAssetDependenciesComponent, "{02163945-D7B4-45D4-9729-4376E1195505}");
static void Reflect(ReflectContext* context);
MaterialAssetDependenciesComponent() = default;
~MaterialAssetDependenciesComponent() override = default;
static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible);
// AZ::Component overrides...
void Activate() override;
void Deactivate() override;
// SceneAPI::SceneBuilderDependencyBus::Handler overrides...
void ReportJobDependencies(SceneAPI::JobDependencyList& jobDependencyList, const char* platformIdentifier) override;
};
} // namespace RPI
} // namespace AZ
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,278 @@
/*
* 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.
*
*/
#pragma once
#include <Atom/RPI.Reflect/Base.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMaterialData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexColorData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexUVData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/ITransform.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <Model/ModelExporterContexts.h>
#include <AzCore/std/containers/map.h>
namespace AZ
{
namespace RPI
{
using MeshData = AZ::SceneAPI::DataTypes::IMeshData;
using TransformData = AZ::SceneAPI::DataTypes::ITransform;
using UVData = AZ::SceneAPI::DataTypes::IMeshVertexUVData;
using ColorData = AZ::SceneAPI::DataTypes::IMeshVertexColorData;
using MaterialData = AZ::SceneAPI::DataTypes::IMaterialData;
using TangentData = AZ::SceneAPI::DataTypes::IMeshVertexTangentData;
using BitangentData = AZ::SceneAPI::DataTypes::IMeshVertexBitangentData;
class Stream;
class ModelLodAssetCreator;
class BufferAssetCreator;
//! Component responsible for building Atom's AzModel from SceneAPI input.
//!
//! The current strategy in this builder is to merge data as much as possible
//! while keeping streams separate.
//! This may change for different platforms in the future.
//!
//! Currently Meshes are merged as much as possible at the ModelLod level.
//! An input SceneAPI Model with 3 meshes in an LOD are going to get merged
//! into stream buffers owned by the ModelLod. The Meshes in the ModelLod
//! are then just going to act as views into the ModelLod's buffers with
//! an associated Material.
class ModelAssetBuilderComponent
: public AZ::SceneAPI::SceneCore::ExportingComponent
{
public:
AZ_COMPONENT(
ModelAssetBuilderComponent,
"{FE21DEEB-F9E6-487E-B9F0-88478FC2F52F}",
AZ::SceneAPI::SceneCore::ExportingComponent);
static void Reflect(AZ::ReflectContext* context);
ModelAssetBuilderComponent();
~ModelAssetBuilderComponent() override = default;
AZ::SceneAPI::Events::ProcessingResult BuildModel(ModelAssetBuilderContext& context);
private:
using MaterialUid = uint64_t;
//! Describes the source SceneAPI data that makes up a "Mesh" as understood by Atom.
struct SourceMeshContent
{
AZ::Name m_name;
AZStd::shared_ptr<const MeshData> m_meshData;
SceneAPI::DataTypes::MatrixType m_worldTransform = SceneAPI::DataTypes::MatrixType::CreateIdentity();
AZStd::shared_ptr<const TangentData> m_meshTangents;
AZStd::shared_ptr<const BitangentData> m_meshBitangents;
AZStd::vector<AZStd::shared_ptr<const UVData>> m_meshUVData;
AZStd::vector<AZStd::shared_ptr<const ColorData>> m_meshColorData;
AZStd::vector<MaterialUid> m_materials;
MaterialUid GetMaterialUniqueId(uint32_t index) const;
};
using SourceMeshContentList = AZStd::vector<SourceMeshContent>;
//! Describes the data needed to produce the buffers that make up a Mesh.
struct ProductMeshContent
{
AZ::Name m_name;
AZStd::vector<uint32_t> m_indices;
AZStd::vector<float> m_positions;
AZStd::vector<float> m_normals;
AZStd::vector<float> m_tangents;
AZStd::vector<float> m_bitangents;
AZStd::vector<AZStd::vector<float>> m_uvSets;
AZStd::vector<AZ::Name> m_uvCustomNames;
AZStd::vector<AZStd::vector<float>> m_colorSets;
AZStd::vector<AZ::Name> m_colorCustomNames;
MaterialUid m_materialUid;
};
using ProductMeshContentList = AZStd::vector<ProductMeshContent>;
//! Used to accumulate info about how much space to allocate when creating a ProductMeshContent structure.
//!
//! This is needed to batch memory allocations together. Otherwise
//! hundreds of thousands of small allocations when building a ProductMeshContent
//! structure can cause the allocators to fragment heavily and waste large
//! amounts of memory. 2GB of model memory can become 30+GB of fragmented memory in
//! the allocator.
struct ProductMeshContentAllocInfo
{
size_t m_indexCount = 0;
size_t m_positionsFloatCount = 0;
size_t m_normalsFloatCount = 0;
size_t m_tangentsFloatCount = 0;
size_t m_bitangentsFloatCount = 0;
AZStd::vector<size_t> m_uvSetFloatCounts;
AZStd::vector<size_t> m_colorSetFloatCounts;
};
//! Describes a view into data described in a ProductMeshContent structure.
//!
//! As described by the strategy in the class comment, Meshes are going to
//! just be views into the ModelLod-wide buffers.
//! If a ProductMeshContent structure describes all the buffers in a ModelLod,
//! a ProductMeshView describes each Mesh's view into those ModelLod buffers.
struct ProductMeshView
{
AZ::Name m_name;
RHI::BufferViewDescriptor m_indexView;
RHI::BufferViewDescriptor m_positionView;
RHI::BufferViewDescriptor m_normalView;
AZStd::vector<RHI::BufferViewDescriptor> m_uvSetViews;
AZStd::vector<AZ::Name> m_uvCustomNames;
AZStd::vector<RHI::BufferViewDescriptor> m_colorSetViews;
AZStd::vector<AZ::Name> m_colorCustomNames;
RHI::BufferViewDescriptor m_tangentView;
RHI::BufferViewDescriptor m_bitangentView;
MaterialUid m_materialUid;
};
using ProductMeshViewList = AZStd::vector<ProductMeshView>;
//! Takes an abstract graph object and tries to add it to the given SourceMeshContent object.
void AddToMeshContent(
const AZStd::shared_ptr<const AZ::SceneAPI::DataTypes::IGraphObject>& data,
SourceMeshContent& content);
//! Takes in a list of SourceMeshContent and outputs list of ProductMeshContent.
//! This product list is not 1:1 but it is as close as we can get with minimal processing.
//! Since a SouceMeshContent object may have faces that have multiple materials we have to break
//! that into a ProductMeshContent object for each material. No further processing is done
ProductMeshContentList SourceMeshListToProductMeshList(
const SourceMeshContentList& sourceMeshContentList);
//! Takes in a ProductMeshContentList and merges all elements that share the same MaterialUid.
ProductMeshContentList MergeMeshesByMaterialUid(
const ProductMeshContentList& productMeshList);
//! Simple helper to create a MeshView that views an entire given ProductMeshContent object as one mesh.
ProductMeshView CreateViewToEntireMesh(const ProductMeshContent& mesh);
//! Takes a ProductMeshContentList and merges all elements into a single ProductMeshContent object.
//! This also produces a ProductMeshViewList that contains views to all
//! the original meshes described in the lodMeshList collection.
void MergeMeshesToCommonBuffers(
const ProductMeshContentList& lodMeshList,
ProductMeshContent& lodMeshContent,
ProductMeshViewList& meshViewsPerLodBuffer);
enum IndicesOperation
{
PreserveIndices,
RemapIndices
};
//! Helper method for MergeMeshesByMaterialUid and MergeAllMeshes.
//! Takes a given ProductMeshContentList and outputs a single
//! ProductMeshContent object.
//!
//! If preserveIndices is true the values of the indices will not be rescaled
//! to treat the merged mesh as one continuous mesh. Instead each mesh will retain
//! its indices as they were.
ProductMeshContent MergeMeshList(
const ProductMeshContentList& productMeshList,
IndicesOperation indicesOp);
//! Takes a ProductMeshContent object, produces BufferAsset objects for each
//! stream and then applies those to the given ModelLodAssetCreator as the
//! lod-wide buffers. It also returns the index and stream buffer data so that it
//! can be referenced later.
//!
//! Returns false if an error occurs
bool CreateModelLodBuffers(
const ProductMeshContent& lodBufferContent,
BufferAssetView& outIndexBuffer,
AZStd::vector<ModelLodAsset::Mesh::StreamBufferInfo>& outStreamBuffers,
ModelLodAssetCreator& lodAssetCreator);
//! Takes a ProductMeshView and the buffers that it is supposed to be a view
//! into and adds that data as a Mesh onto the given ModelLodAssetCreator.
//!
//! Returns false if an error occurs
bool CreateMesh(
const ProductMeshView& meshView,
const BufferAssetView& lodIndexBuffer,
const AZStd::vector<ModelLodAsset::Mesh::StreamBufferInfo>& lodStreamBuffers,
ModelLodAssetCreator& lodAssetCreator,
const MaterialAssetsByUid& materialAssetsByUid);
//! Takes in a pointer to data with a given element count and format and creates a BufferAsset.
Outcome<Data::Asset<BufferAsset>> CreateBufferAsset(
const void* data, const size_t elementCount, RHI::Format format, const AZStd::string& bufferName);
//! Helper method for CreateMesh.
//! Searches lodStreamBuffers for the given semantic and if found takes
//! the BufferAsset in that StreamBufferInfo and pairs it with the given
//! bufferViewDescriptor to add a Mesh stream buffer to the given lodAssetCreator.
bool SetMeshStreamBufferById(
const RHI::ShaderSemantic& semantic,
const AZ::Name& customName,
const RHI::BufferViewDescriptor& bufferViewDescriptor,
const AZStd::vector<ModelLodAsset::Mesh::StreamBufferInfo>& lodStreamBuffers,
ModelLodAssetCreator& lodAssetCreator);
// Create stable asset id from an unique name
Data::AssetId CreateAssetId(const AZStd::string& assetName);
// Get asset full name for different product assets based on their type
// For buffer asset, it needs to provide a buffer name
AZStd::string GetAssetFullName(const TypeId& assetType, const AZStd::string& bufferName = {});
//! Calculates the AABB of the SubMesh.
//! This should be called when a position stream is added
//!
//! @param[in] bufferViewDesc The buffer view descriptor used to examine the given bufferAsset
//! and determine the range of the buffer to use to calculate the AABB from.
//! @param[in] bufferAsset The BufferAsset to calculate the AABB from. Expected to
//! be a Position stream
//! @param[out] aabb The AABB that encompasses the given stream
//! @return True if the AABB was successfully calculated
static bool CalculateAABB(const RHI::BufferViewDescriptor& bufferViewDesc, const BufferAsset& bufferAsset, AZ::Aabb& aabb);
//! Helper method for CreateMesh.
//! Finds a buffer in the given streamBufferInfoList that matches the given stream id
//! and returns it as part of outStreamBufferInfo.
//! Returns false if no StreamBufferInfo is found.
bool FindStreamBufferById(
const AZStd::vector<ModelLodAsset::Mesh::StreamBufferInfo>& streamBufferInfoList,
const RHI::ShaderSemantic& streamSemantic,
ModelLodAsset::Mesh::StreamBufferInfo& outStreamBufferInfo);
Uuid m_sourceUuid;
// cached names for asset id generation
AZStd::string m_modelName;
AZStd::string m_lodName;
AZStd::string m_meshName;
AZStd::set<uint32_t> m_createdSubId;
// NOTE: This is explicitly fetched from a filename. In the future, this should be fetched from the RPI system
// configuration data.
Data::AssetId m_systemInputAssemblyBufferPoolId;
//! Calculates the world transform of the node given all of its parent nodes
SceneAPI::DataTypes::MatrixType GetWorldTransform(const SceneAPI::Containers::SceneGraph& sceneGraph, SceneAPI::Containers::SceneGraph::NodeIndex node);
};
} // namespace RPI
} // namespace AZ
@@ -0,0 +1,299 @@
/*
* 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 <Model/ModelExporterComponent.h>
#include <Model/ModelExporterContexts.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AssetBuilderSDK/SerializationDependencies.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <cinttypes>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
namespace AZ
{
namespace RPI
{
static const char* s_exporterName = "Atom Model Builder";
ModelExporterComponent::ModelExporterComponent()
{
BindToCall(&ModelExporterComponent::ExportModel);
}
SceneAPI::Events::ProcessingResult
ModelExporterComponent::ExportModel(SceneAPI::Events::ExportEventContext& exportEventContext) const
{
const SceneAPI::Containers::SceneManifest& manifest =
exportEventContext.GetScene().GetManifest();
const Uuid sourceSceneUuid = exportEventContext.GetScene().GetSourceGuid();
auto valueStorage = manifest.GetValueStorage();
auto view = SceneAPI::Containers::MakeDerivedFilterView<
SceneAPI::DataTypes::IMeshGroup>(valueStorage);
SceneAPI::Events::ProcessingResultCombiner combinerResult;
MaterialAssetsByUid materialsByUid;
MaterialAssetBuilderContext materialContext(exportEventContext.GetScene(), materialsByUid);
combinerResult = SceneAPI::Events::Process<MaterialAssetBuilderContext>(materialContext);
if (combinerResult.GetResult() == SceneAPI::Events::ProcessingResult::Failure)
{
return SceneAPI::Events::ProcessingResult::Failure;
}
//Export MaterialAssets
for (auto& materialPair : materialsByUid)
{
uint64_t materialUid = materialPair.first;
const AZStd::string& sceneName = exportEventContext.GetScene().GetName();
const Data::Asset<MaterialAsset>& asset = materialPair.second.m_asset;
// escape the material name acceptable for a filename
AZStd::string materialName = materialPair.second.m_name;
for (char& item : materialName)
{
if (!isalpha(item) && !isdigit(item))
{
item = '_';
}
}
const AZStd::string relativeMaterialFileName = AZStd::string::format("%s_%s_%" PRIu64, sceneName.c_str(), materialName.data(), materialUid);
AssetExportContext materialExportContext =
{
relativeMaterialFileName,
MaterialAsset::Extension,
sourceSceneUuid,
DataStream::ST_XML
};
if (!ExportAsset(asset, materialExportContext, exportEventContext, "Material"))
{
return SceneAPI::Events::ProcessingResult::Failure;
}
}
AZStd::set<AZStd::string> groupNames;
for (const SceneAPI::DataTypes::IMeshGroup& meshGroup : view)
{
const AZStd::string& meshGroupName = meshGroup.GetName();
//Check for duplicate group names
if(groupNames.find(meshGroupName) != groupNames.end())
{
AZ_Warning(s_exporterName, false, "Multiple mesh groups with duplicate name: \"%s\". Skipping export...", meshGroupName.c_str());
continue;
}
else
{
groupNames.insert(meshGroupName);
}
AZ_TraceContext("Mesh group", meshGroupName.c_str());
Data::Asset<ModelAsset> modelAsset;
ModelAssetBuilderContext modelContext(exportEventContext.GetScene(), meshGroup, materialsByUid, modelAsset);
combinerResult = SceneAPI::Events::Process<ModelAssetBuilderContext>(modelContext);
if (combinerResult.GetResult() != SceneAPI::Events::ProcessingResult::Success)
{
return combinerResult.GetResult();
}
//Retrieve source asset info so we can get a string with the relative path to the asset
bool assetInfoResult;
Data::AssetInfo info;
AZStd::string watchFolder;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(assetInfoResult, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, exportEventContext.GetScene().GetSourceFilename().c_str(), info, watchFolder);
AZ_Assert(assetInfoResult, "Failed to retrieve source asset info. Can't reason about product asset paths");
uint32_t lodIndex = 0;
for (const Data::Asset<ModelLodAsset>& lodAsset : modelAsset->GetLodAssets())
{
AZStd::set<uint32_t> exportedSubAssets;
uint32_t bufferIndex = 0;
for (const ModelLodAsset::Mesh& mesh : lodAsset->GetMeshes())
{
//Export all BufferAssets for this Lod
//Export Index Buffer
{
const Data::Asset<BufferAsset>& indexBufferAsset = mesh.GetIndexBufferAssetView().GetBufferAsset();
if (exportedSubAssets.find(indexBufferAsset.GetId().m_subId) == exportedSubAssets.end())
{
AssetExportContext bufferExportContext =
{
indexBufferAsset.GetHint(),
BufferAsset::Extension,
sourceSceneUuid,
DataStream::ST_BINARY
};
if (!ExportAsset(indexBufferAsset, bufferExportContext, exportEventContext, "Buffer"))
{
return SceneAPI::Events::ProcessingResult::Failure;
}
exportedSubAssets.insert(indexBufferAsset.GetId().m_subId);
bufferIndex++;
}
}
//Export Stream Buffers
for (const ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo : mesh.GetStreamBufferInfoList())
{
const Data::Asset<BufferAsset>& bufferAsset = streamBufferInfo.m_bufferAssetView.GetBufferAsset();
if (exportedSubAssets.find(bufferAsset.GetId().m_subId) == exportedSubAssets.end())
{
AssetExportContext bufferExportContext =
{
bufferAsset.GetHint(),
BufferAsset::Extension,
sourceSceneUuid,
DataStream::ST_BINARY
};
if (!ExportAsset(bufferAsset, bufferExportContext, exportEventContext, "Buffer"))
{
return SceneAPI::Events::ProcessingResult::Failure;
}
exportedSubAssets.insert(bufferAsset.GetId().m_subId);
bufferIndex++;
}
}
}
//Export ModelLodAsset
AssetExportContext lodExportContext =
{
lodAsset.GetHint(),
ModelLodAsset::Extension,
sourceSceneUuid,
DataStream::ST_BINARY
};
if (!ExportAsset(lodAsset, lodExportContext, exportEventContext, "Model LOD"))
{
return SceneAPI::Events::ProcessingResult::Failure;
}
lodIndex++;
} //foreach lodAsset
//Export ModelAsset
AssetExportContext modelExportContext =
{
modelAsset.GetHint(),
ModelAsset::Extension,
sourceSceneUuid,
DataStream::ST_BINARY
};
if (!ExportAsset(modelAsset, modelExportContext, exportEventContext, "Model"))
{
return SceneAPI::Events::ProcessingResult::Failure;
}
} //foreach meshGroup
return SceneAPI::Events::ProcessingResult::Success;
}
template<class T>
bool ModelExporterComponent::ExportAsset(
const Data::Asset<T>& asset,
const AssetExportContext& assetContext,
SceneAPI::Events::ExportEventContext& context,
[[maybe_unused]] const char* assetTypeDebugName) const
{
const AZStd::string assetFileName = SceneAPI::Utilities::FileUtilities::CreateOutputFileName(
assetContext.m_relativeFileName, context.GetOutputDirectory(), assetContext.m_extension);
if (!Utils::SaveObjectToFile(assetFileName, assetContext.m_dataStreamType, asset.Get()))
{
AZ_Error(s_exporterName, false, "Failed to save %s to file %s", assetTypeDebugName, assetFileName.c_str());
return false;
}
const Uuid assetUuid = asset.GetId().m_guid;
if (assetUuid != assetContext.m_sourceUuid)
{
AZ_Assert(false, "All product UUIDs should be the same as the scene source UUID");
return false;
}
const uint32_t assetSubId = asset.GetId().m_subId;
// Add product to output list
// Otherwise the asset won't be copied from temp folders to cache
SceneAPI::Events::ExportProduct& product = context.GetProductList().AddProduct(
assetFileName, assetUuid, asset->GetType(), AZStd::nullopt, assetSubId);
AssetBuilderSDK::JobProduct jobProduct;
if (!AssetBuilderSDK::OutputObject(asset.Get(), assetFileName, asset->GetType(), assetSubId, jobProduct))
{
AZ_Assert(false, "Failed to output product dependencies.");
return false;
}
for (auto& dependency : jobProduct.m_dependencies)
{
product.m_productDependencies.push_back(SceneAPI::Events::ExportProduct{
{},
dependency.m_dependencyId.m_guid,
{},
AZStd::nullopt,
dependency.m_dependencyId.m_subId,
dependency.m_flags
});
}
return true;
}
void ModelExporterComponent::Reflect(ReflectContext* context)
{
if (auto* serialize = azrtti_cast<SerializeContext*>(context))
{
serialize->Class<ModelExporterComponent, SceneAPI::SceneCore::ExportingComponent>()
->Version(3);
}
}
ModelExporterComponent::AssetExportContext::AssetExportContext(
AZStd::string_view relativeFileName,
AZStd::string_view extension,
Uuid sourceUuid,
DataStream::StreamType dataStreamType)
: m_relativeFileName{relativeFileName}
, m_extension{extension}
, m_sourceUuid{sourceUuid}
, m_dataStreamType{dataStreamType}
{}
} // namespace RPI
} // namespace AZ
@@ -0,0 +1,74 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Serialization/Utils.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
namespace AZ
{
namespace SceneAPI
{
namespace Events
{
class ExportEventContext;
}
}
namespace RPI
{
/**
* This is the central component that drive the process of exporting a scene to Model
* and Material assets. It delegates asset-build duties to other components like
* ModelAssetBuilderComponent and MaterialAssetBuilderComponent via export events.
*/
class ModelExporterComponent : public SceneAPI::SceneCore::ExportingComponent
{
public:
AZ_COMPONENT(
ModelExporterComponent,
"{AE42AB62-A4D6-4147-88A0-692549EE5427}",
SceneAPI::SceneCore::ExportingComponent);
static void Reflect(ReflectContext* context);
ModelExporterComponent();
~ModelExporterComponent() override = default;
SceneAPI::Events::ProcessingResult ExportModel(SceneAPI::Events::ExportEventContext& context) const;
private:
struct AssetExportContext
{
AssetExportContext() = default;
AssetExportContext(
AZStd::string_view relativeFileName,
AZStd::string_view extension,
Uuid sourceUuid,
DataStream::StreamType dataStreamType);
AZStd::string_view m_relativeFileName;
AZStd::string_view m_extension;
const Uuid m_sourceUuid;
const DataStream::StreamType m_dataStreamType = DataStream::ST_BINARY;
};
template<class T>
bool ExportAsset(
const Data::Asset<T>& asset,
const AssetExportContext& assetContext,
SceneAPI::Events::ExportEventContext& eventContext,
const char* assetTypeDebugName) const;
};
} // namespace RPI
} // namespace AZ
@@ -0,0 +1,41 @@
/*
* 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 <Model/ModelExporterContexts.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
namespace AZ
{
namespace RPI
{
ModelAssetBuilderContext::ModelAssetBuilderContext(
const AZ::SceneAPI::Containers::Scene& scene,
const AZ::SceneAPI::DataTypes::IMeshGroup& group,
const MaterialAssetsByUid& materialsByUid,
Data::Asset<ModelAsset>& outputModelAsset)
: m_scene(scene)
, m_group(group)
, m_materialsByUid(materialsByUid)
, m_outputModelAsset(outputModelAsset)
{}
MaterialAssetBuilderContext::MaterialAssetBuilderContext(
const AZ::SceneAPI::Containers::Scene& scene,
MaterialAssetsByUid& outputMaterialsByUid)
: m_scene(scene)
, m_outputMaterialsByUid(outputMaterialsByUid)
{}
} // namespace RPI
} // namespace AZ
@@ -0,0 +1,85 @@
/*
* 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.
*
*/
#pragma once
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/Model/ModelLodAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Buffer/BufferAsset.h>
#include <AzCore/Asset/AssetCommon.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class IMeshGroup;
}
namespace Containers
{
class Scene;
}
}
namespace RPI
{
struct NamedMaterialAsset
{
Data::Asset<MaterialAsset> m_asset;
AZStd::string m_name;
};
using MaterialAssetsByUid = AZStd::unordered_map<uint64_t, NamedMaterialAsset>;
struct ModelAssetBuilderContext : public AZ::SceneAPI::Events::ICallContext
{
AZ_RTTI(
ModelAssetBuilderContext,
"{63FEFB4B-25DC-48DD-AC72-D27DA9A6D94A}",
AZ::SceneAPI::Events::ICallContext);
ModelAssetBuilderContext(
const AZ::SceneAPI::Containers::Scene& scene,
const AZ::SceneAPI::DataTypes::IMeshGroup& group,
const MaterialAssetsByUid& materialsByUid,
Data::Asset<ModelAsset>& outputModelAsset);
~ModelAssetBuilderContext() override = default;
ModelAssetBuilderContext& operator=(const ModelAssetBuilderContext& other) = delete;
const AZ::SceneAPI::Containers::Scene& m_scene;
const AZ::SceneAPI::DataTypes::IMeshGroup& m_group;
const MaterialAssetsByUid& m_materialsByUid;
Data::Asset<ModelAsset>& m_outputModelAsset;
};
struct MaterialAssetBuilderContext : public AZ::SceneAPI::Events::ICallContext
{
AZ_RTTI(
MaterialAssetBuilderContext,
"{6451418A-453B-4646-A5B2-A5687FA2E97F}",
AZ::SceneAPI::Events::ICallContext);
MaterialAssetBuilderContext(const AZ::SceneAPI::Containers::Scene& scene, MaterialAssetsByUid& outputMaterialsByUid);
~MaterialAssetBuilderContext() override = default;
MaterialAssetBuilderContext& operator=(const MaterialAssetBuilderContext& other) = delete;
const AZ::SceneAPI::Containers::Scene& m_scene;
MaterialAssetsByUid& m_outputMaterialsByUid;
};
} // namespace RPI
} // namespace AZ