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,97 @@
#
# 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.
#
if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_add_target(
NAME ResourceCompilerScene.Static STATIC
NAMESPACE Legacy
FILES_CMAKE
resourcecompilerscene_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
../..
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
Legacy::CryCommon
Legacy::ResourceCompiler.Static
Legacy::ResourceCompilerPC.Static
AZ::AssetBuilderSDK
PUBLIC
AZ::AzToolsFramework
AZ::SceneCore
AZ::FbxSceneBuilder
AZ::GFxFramework
)
if(TARGET AssetBuilder)
# The SceneBuilder uses the AssetBuilder gem dependency list to determine which gems it should load in order to process
# Therefore the AssetBuilder target is set as the LY_CMAKE_TARGET for the SceneCompiler.cpp in order
# to allow it to use the Settings Regisry specialization the AssetBuilder in order
# to load the cmake_dependencies.<project>.AssetBuilder.setreg file to gather the list of gems to load
set_source_files_properties(
SceneCompiler.cpp
PROPERTIES
COMPILE_DEFINITIONS
LY_CMAKE_TARGET="AssetBuilder"
)
else()
message(FATAL_ERROR "Cannot set LY_CMAKE_TARGET define to AssetBuilder as the AssetBuilder TARGET doesn't exist.")
endif()
ly_add_target(
NAME ResourceCompilerScene MODULE
NAMESPACE Legacy
OUTPUT_SUBDIRECTORY rc_plugins
FILES_CMAKE
resourcecompilerscene_shared_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
../..
BUILD_DEPENDENCIES
PRIVATE
Legacy::ResourceCompilerScene.Static
Legacy::ResourceCompiler.Static
Legacy::CryCommon
Gem::EditorPythonBindings.Static
)
ly_add_dependencies(RC Legacy::ResourceCompilerScene)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME ResourceCompilerScene.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Legacy
FILES_CMAKE
resourcecompilerscene_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Tests
.
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
Legacy::ResourceCompilerScene.Static
Legacy::CryCommon
Legacy::ResourceCompiler.Static
RUNTIME_DEPENDENCIES
Legacy::CrySystem
)
ly_add_googletest(
NAME Legacy::ResourceCompilerScene.Tests
)
endif()
@@ -0,0 +1,48 @@
/*
* 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 <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
namespace AZ
{
namespace RC
{
CgfGroupExportContext::CgfGroupExportContext(SceneAPI::Events::ExportEventContext& parent,
const SceneAPI::DataTypes::IMeshGroup& group, Phase phase)
: m_products(parent.GetProductList())
, m_scene(parent.GetScene())
, m_outputDirectory(parent.GetOutputDirectory())
, m_group(group)
, m_phase(phase)
{
}
CgfGroupExportContext::CgfGroupExportContext(SceneAPI::Events::ExportProductList& products, const SceneAPI::Containers::Scene& scene,
const AZStd::string& outputDirectory, const SceneAPI::DataTypes::IMeshGroup& group, Phase phase)
: m_products(products)
, m_scene(scene)
, m_outputDirectory(outputDirectory)
, m_group(group)
, m_phase(phase)
{
}
CgfGroupExportContext::CgfGroupExportContext(const CgfGroupExportContext& copyContext, Phase phase)
: m_products(copyContext.m_products)
, m_scene(copyContext.m_scene)
, m_outputDirectory(copyContext.m_outputDirectory)
, m_group(copyContext.m_group)
, m_phase(phase)
{
}
} // RC
} // AZ
@@ -0,0 +1,65 @@
#pragma once
/*
* 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/RTTI/RTTI.h>
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <RC/ResourceCompilerScene/Common/ExportContextGlobal.h>
#include <CryHeaders.h>
class CContentCGF;
struct CNodeCGF;
class CMesh;
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class IGroup;
class IMeshGroup;
}
namespace Events
{
class ExportProductList;
}
}
namespace RC
{
// Called to export a specific Mesh (Cgf) Group.
struct CgfGroupExportContext
: public SceneAPI::Events::ICallContext
{
AZ_RTTI(CgfGroupExportContext, "{974FF54E-9724-4E8B-A2A1-D360C3B3DA80}", SceneAPI::Events::ICallContext);
CgfGroupExportContext(SceneAPI::Events::ExportEventContext& parent,
const SceneAPI::DataTypes::IMeshGroup& group, Phase phase);
CgfGroupExportContext(SceneAPI::Events::ExportProductList& products, const SceneAPI::Containers::Scene& scene, const AZStd::string& outputDirectory,
const SceneAPI::DataTypes::IMeshGroup& group, Phase phase);
CgfGroupExportContext(const CgfGroupExportContext& copyContext, Phase phase);
CgfGroupExportContext(const CgfGroupExportContext& copyContext) = delete;
~CgfGroupExportContext() override = default;
CgfGroupExportContext& operator=(const CgfGroupExportContext& other) = delete;
SceneAPI::Events::ExportProductList& m_products;
const SceneAPI::Containers::Scene& m_scene;
const AZStd::string& m_outputDirectory;
const SceneAPI::DataTypes::IMeshGroup& m_group;
const Phase m_phase;
};
} // RC
} // AZ
@@ -0,0 +1,63 @@
/*
* 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 <RC/ResourceCompilerScene/Cgf/CgfExporter.h>
#include <Cry_Geo.h>
#include <ConvertContext.h>
#include <CGFContent.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 <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
namespace AZ
{
namespace RC
{
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
CgfExporter::CgfExporter(IConvertContext* convertContext)
: m_convertContext(convertContext)
{
BindToCall(&CgfExporter::ProcessContext);
ActivateBindings();
}
SceneEvents::ProcessingResult CgfExporter::ProcessContext(SceneEvents::ExportEventContext& context)
{
AZ_TraceContext("Scene name", context.GetScene().GetName());
AZ_TraceContext("Source file", context.GetScene().GetSourceFilename());
AZ_TraceContext("Output path", context.GetOutputDirectory());
const SceneContainers::SceneManifest& manifest = context.GetScene().GetManifest();
auto valueStorage = manifest.GetValueStorage();
auto view = SceneContainers::MakeDerivedFilterView<SceneDataTypes::IMeshGroup>(valueStorage);
SceneEvents::ProcessingResultCombiner result;
for (const SceneDataTypes::IMeshGroup& meshGroup : view)
{
AZ_TraceContext("Mesh group", meshGroup.GetName());
result += SceneEvents::Process<CgfGroupExportContext>(context, meshGroup, Phase::Construction);
result += SceneEvents::Process<CgfGroupExportContext>(context, meshGroup, Phase::Filling);
result += SceneEvents::Process<CgfGroupExportContext>(context, meshGroup, Phase::Finalizing);
}
return result.GetResult();
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,46 @@
/*
* 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 <SceneAPI/SceneCore/Events/CallProcessorBinder.h>
struct IConvertContext;
namespace AZ
{
namespace SceneAPI
{
namespace Events
{
class ExportEventContext;
}
}
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
class CgfExporter
: public SceneEvents::CallProcessorBinder
{
public:
CgfExporter(IConvertContext* convertContext);
~CgfExporter() override = default;
SceneEvents::ProcessingResult ProcessContext(SceneEvents::ExportEventContext& context);
private:
IConvertContext* m_convertContext;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,117 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzCore/IO/SystemFile.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfGroupExporter.h>
#include <RC/ResourceCompilerScene/Cgf/CgfUtils.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/DataTypes/ManifestBase/ISceneNodeSelectionList.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IMaterialRule.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ
{
namespace RC
{
namespace SceneUtil = AZ::SceneAPI::Utilities;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
namespace SceneContainers = AZ::SceneAPI::Containers;
const AZStd::string CgfGroupExporter::s_fileExtension = "cgf";
CgfGroupExporter::CgfGroupExporter(IAssetWriter* writer)
: m_assetWriter(writer)
{
BindToCall(&CgfGroupExporter::ProcessContext);
ActivateBindings();
}
SceneEvents::ProcessingResult CgfGroupExporter::ProcessContext(CgfGroupExportContext& context) const
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZStd::string filename = SceneUtil::FileUtilities::CreateOutputFileName(context.m_group.GetName(), context.m_outputDirectory, s_fileExtension);
AZ_TraceContext("CGF File Name", filename);
if (filename.empty() || !SceneUtil::FileUtilities::EnsureTargetFolderExists(filename))
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Unable to write CGF file. Filename is empty or target folder does not exist.");
return SceneEvents::ProcessingResult::Failure;
}
SceneEvents::ProcessingResultCombiner result;
CContentCGF cgfContent(filename.c_str());
ConfigureCgfContent(cgfContent);
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
AZStd::vector<AZStd::string> targetNodes = SceneUtil::SceneGraphSelector::GenerateTargetNodes(graph,
context.m_group.GetSceneNodeSelectionList(), SceneUtil::SceneGraphSelector::IsMesh);
result += ProcessMeshes(context, cgfContent, targetNodes);
if (m_assetWriter && cgfContent.GetNodeCount() > 0)
{
if (m_assetWriter->WriteCGF(&cgfContent))
{
static const Data::AssetType staticMeshAssetType("{C2869E3B-DDA0-4E01-8FE3-6770D788866B}"); // from MeshAsset.h
AZ::SceneAPI::Events::ExportProduct& exportProduct = context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), staticMeshAssetType, 0, AZStd::nullopt);
// If the mesh group has a material rule, then add the material path dependency
if (context.m_group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::IMaterialRule>())
{
// All CGFs are assumed to have a single material with their same name in their same folder.
// Add just the material file name as a path dependency for now.
// Note that at this point, the .mtl file may or may not exist, which is fine.
AZStd::string materialName;
AzFramework::StringFunc::Path::GetFullFileName(context.m_scene.GetSourceFilename().c_str(), materialName);
AzFramework::StringFunc::Path::ReplaceExtension(materialName, ".mtl");
exportProduct.m_legacyPathDependencies.push_back(materialName);
}
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Unable to write CGF file.");
result += SceneEvents::ProcessingResult::Failure;
}
}
else
{
if (!m_assetWriter)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "No asset writer found. Unable to write cgf to disk");
}
if (cgfContent.GetNodeCount() == 0 )
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Empty Cgf file. Cgf not written to disk." );
}
result += SceneEvents::ProcessingResult::Failure;
}
return result.GetResult();
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,44 @@
/*
* 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/std/string/string.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBinder.h>
class CContentCGF;
struct IAssetWriter;
namespace AZ
{
namespace RC
{
struct CgfGroupExportContext;
namespace SceneEvents = AZ::SceneAPI::Events;
class CgfGroupExporter
: public SceneEvents::CallProcessorBinder
{
public:
CgfGroupExporter(IAssetWriter* writer);
~CgfGroupExporter() override = default;
static const AZStd::string s_fileExtension;
SceneEvents::ProcessingResult ProcessContext(CgfGroupExportContext& context) const;
protected:
IAssetWriter* m_assetWriter;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,126 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/string/conversions.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfLodExporter.h>
#include <RC/ResourceCompilerScene/Cgf/CgfUtils.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/DataTypes/ManifestBase/ISceneNodeSelectionList.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/ILodRule.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ
{
namespace RC
{
namespace SceneUtil = AZ::SceneAPI::Utilities;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
namespace SceneContainers = AZ::SceneAPI::Containers;
const AZStd::string CgfLodExporter::s_fileExtension = "cgf";
CgfLodExporter::CgfLodExporter(IAssetWriter* writer)
: m_assetWriter(writer)
{
BindToCall(&CgfLodExporter::ProcessContext);
ActivateBindings();
}
SceneEvents::ProcessingResult CgfLodExporter::ProcessContext(CgfGroupExportContext& context) const
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZStd::shared_ptr<const SceneDataTypes::ILodRule> lodRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::ILodRule>();
if (!lodRule)
{
return SceneEvents::ProcessingResult::Ignored;
}
// Create the base CGF name
AZStd::string baseCGFfilename = SceneUtil::FileUtilities::CreateOutputFileName(context.m_group.GetName(), context.m_outputDirectory, s_fileExtension);
SceneEvents::ProcessingResultCombiner result;
for (size_t index = 0; index < lodRule->GetLodCount(); ++index)
{
AZStd::string filename = SceneUtil::FileUtilities::CreateOutputFileName(
context.m_group.GetName() + "_LOD" + AZStd::to_string(static_cast<int>(index + 1)), context.m_outputDirectory, s_fileExtension);
AZ_TraceContext("CGF Lod File Name", filename);
if (filename.empty() || !SceneUtil::FileUtilities::EnsureTargetFolderExists(filename))
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Unable to write CGF Lod file. Filename is empty or target folder does not exist.");
result += SceneEvents::ProcessingResult::Failure;
break;
}
CContentCGF cgfContent(filename.c_str());
ConfigureCgfContent(cgfContent);
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
AZStd::vector<AZStd::string> targetNodes = SceneUtil::SceneGraphSelector::GenerateTargetNodes(graph,
lodRule->GetSceneNodeSelectionList(index), SceneUtil::SceneGraphSelector::IsMesh);
result += ProcessMeshes(context, cgfContent, targetNodes);
if (cgfContent.GetNodeCount() == 0)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Empty LoD Detected at level %d.", index);
result += SceneEvents::ProcessingResult::Failure;
break;
}
if (m_assetWriter)
{
if (m_assetWriter->WriteCGF(&cgfContent))
{
static const AZ::Data::AssetType staticMeshLodsAssetType("{9AAE4926-CB6A-4C60-9948-A1A22F51DB23}");
// Using the same guid as the parent group/cgf as this needs to be a lod of that cgf.
// Setting the lod to index+1 as 0 means the base mesh and 1-6 are lod levels 0-5.
AZ::SceneAPI::Events::ExportProduct& lodProduct = context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), staticMeshLodsAssetType, index + 1, AZStd::nullopt);
// Add this LOD as a dependency to the base CGF
context.m_products.AddDependencyToProduct(baseCGFfilename, lodProduct);
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Unable to write CGF LoD file at level %d.", index);
result += SceneEvents::ProcessingResult::Failure;
break;
}
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "No asset writer found. Unable to write cgf to disk");
result += SceneEvents::ProcessingResult::Failure;
break;
}
}
return result.GetResult();
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,44 @@
/*
* 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/std/string/string.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBinder.h>
class CContentCGF;
struct IAssetWriter;
namespace AZ
{
namespace RC
{
struct CgfGroupExportContext;
namespace SceneEvents = AZ::SceneAPI::Events;
class CgfLodExporter
: public SceneEvents::CallProcessorBinder
{
public:
CgfLodExporter(IAssetWriter* writer);
~CgfLodExporter() override = default;
static const AZStd::string s_fileExtension;
SceneEvents::ProcessingResult ProcessContext(CgfGroupExportContext& context) const;
protected:
IAssetWriter* m_assetWriter;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,93 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfUtils.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
namespace AZ
{
namespace RC
{
void ConfigureCgfContent(CContentCGF& content)
{
CExportInfoCGF* exportInfo = content.GetExportInfo();
bool useCustomNormalDefault = true;
AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast(&AZ::SceneAPI::Events::AssetImportRequestBus::Events::AreCustomNormalsUsed, useCustomNormalDefault);
exportInfo->bMergeAllNodes = true;
exportInfo->bUseCustomNormals = useCustomNormalDefault; // This will be overritten by StaticMeshAdvancedRule (if teh rule exists) when calling ContainerSettingsExporter::ProcessContext
exportInfo->bCompiledCGF = false;
exportInfo->bHavePhysicsProxy = false;
exportInfo->bHaveAutoLods = false;
exportInfo->bNoMesh = true;
exportInfo->b8WeightsPerVertex = false;
exportInfo->bWantF32Vertices = false;
exportInfo->authorToolVersion = 1;
}
void ProcessMeshType(ContainerExportContext& context, CContentCGF& content, const AZStd::vector<AZStd::string>& targetNodes, EPhysicsGeomType physicalizeType)
{
const SceneAPI::Containers::SceneGraph& graph = context.m_scene.GetGraph();
for (const AZStd::string& nodeName : targetNodes)
{
AZ_TraceContext("Mesh node", nodeName);
SceneAPI::Containers::SceneGraph::NodeIndex index = graph.Find(nodeName);
if (index.IsValid())
{
CNodeCGF* node = new CNodeCGF(); // will be auto deleted by CContentCGF cgf
SetNodeName(nodeName, *node);
AZStd::string rootBoneName;
SceneAPI::Events::Process<NodeExportContext>(context, *node, nodeName, index, physicalizeType, rootBoneName, Phase::Construction);
SceneAPI::Events::Process<NodeExportContext>(context, *node, nodeName, index, physicalizeType, rootBoneName, Phase::Filling);
content.AddNode(node);
SceneAPI::Events::Process<NodeExportContext>(context, *node, nodeName, index, physicalizeType, rootBoneName, Phase::Finalizing);
}
}
}
void SetNodeName(const AZStd::string& name, CNodeCGF& node)
{
static const size_t nodeNameCount = sizeof(node.name) / sizeof(node.name[0]);
size_t offset = name.length() < nodeNameCount ? 0 : name.length() - nodeNameCount + 1;
azstrcpy(node.name, nodeNameCount, name.c_str() + offset);
}
SceneAPI::Events::ProcessingResult ProcessMeshes(CgfGroupExportContext& context, CContentCGF& content, const AZStd::vector<AZStd::string>& targetNodes)
{
SceneAPI::Events::ProcessingResultCombiner result;
ContainerExportContext containerContext(context.m_scene, context.m_outputDirectory, context.m_group, content, Phase::Construction);
result += SceneAPI::Events::Process(containerContext);
result += SceneAPI::Events::Process<ContainerExportContext>(containerContext, Phase::Filling);
const SceneAPI::Containers::SceneGraph& graph = context.m_scene.GetGraph();
ProcessMeshType(containerContext, content, targetNodes, PHYS_GEOM_TYPE_NONE);
result += SceneAPI::Events::Process<ContainerExportContext>(containerContext, Phase::Finalizing);
return result.GetResult();
}
} // RC
} // AZ
@@ -0,0 +1,32 @@
/*
* 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/std/string/string.h>
#include <SceneAPI/SceneCore/Events/ProcessingResult.h>
class CContentCGF;
namespace AZ
{
namespace RC
{
struct CgfContainerExportContext;
struct CgfGroupExportContext;
void ConfigureCgfContent(CContentCGF& content);
AZ::SceneAPI::Events::ProcessingResult ProcessMeshes(CgfGroupExportContext& context, CContentCGF& content, const AZStd::vector<AZStd::string>& targetNodes);
void ProcessMeshType(ContainerExportContext& context, CContentCGF& content, const AZStd::vector<AZStd::string>& targetNodes, EPhysicsGeomType physicalizeType);
void SetNodeName(const AZStd::string& name, CNodeCGF& node);
} // namespace RC
} // namespace AZ
@@ -0,0 +1,48 @@
/*
* 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 <RC/ResourceCompilerScene/Chr/ChrExportContexts.h>
namespace AZ
{
namespace RC
{
ChrGroupExportContext::ChrGroupExportContext(SceneAPI::Events::ExportEventContext& parent,
const SceneAPI::DataTypes::ISkeletonGroup& group, Phase phase)
: m_products(parent.GetProductList())
, m_scene(parent.GetScene())
, m_outputDirectory(parent.GetOutputDirectory())
, m_group(group)
, m_phase(phase)
{
}
ChrGroupExportContext::ChrGroupExportContext(SceneAPI::Events::ExportProductList& products, const SceneAPI::Containers::Scene& scene,
const AZStd::string& outputDirectory, const SceneAPI::DataTypes::ISkeletonGroup& group, Phase phase)
: m_products(products)
, m_scene(scene)
, m_outputDirectory(outputDirectory)
, m_group(group)
, m_phase(phase)
{
}
ChrGroupExportContext::ChrGroupExportContext(const ChrGroupExportContext& copyContext, Phase phase)
: m_products(copyContext.m_products)
, m_scene(copyContext.m_scene)
, m_outputDirectory(copyContext.m_outputDirectory)
, m_group(copyContext.m_group)
, m_phase(phase)
{
}
}
}
@@ -0,0 +1,59 @@
#pragma once
/*
* 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/RTTI/RTTI.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <RC/ResourceCompilerScene/Common/ExportContextGlobal.h>
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class ISkeletonGroup;
}
namespace Events
{
class ExportProductList;
}
}
namespace RC
{
// Called to export a specific Skeleton (Chr) Group
struct ChrGroupExportContext
: public SceneAPI::Events::ICallContext
{
AZ_RTTI(ChrGroupExportContext, "{294BB98B-DE9D-4B03-B219-A8A94657E81E}", SceneAPI::Events::ICallContext);
ChrGroupExportContext(SceneAPI::Events::ExportEventContext& parent,
const SceneAPI::DataTypes::ISkeletonGroup& group, Phase phase);
ChrGroupExportContext(SceneAPI::Events::ExportProductList& products, const SceneAPI::Containers::Scene& scene,
const AZStd::string& outputDirectory, const SceneAPI::DataTypes::ISkeletonGroup& group, Phase phase);
ChrGroupExportContext(const ChrGroupExportContext& copyContent, Phase phase);
ChrGroupExportContext(const ChrGroupExportContext& copyContent) = delete;
~ChrGroupExportContext() override = default;
ChrGroupExportContext& operator=(const ChrGroupExportContext& other) = delete;
SceneAPI::Events::ExportProductList& m_products;
const SceneAPI::Containers::Scene& m_scene;
const AZStd::string& m_outputDirectory;
const SceneAPI::DataTypes::ISkeletonGroup& m_group;
const Phase m_phase;
};
}
}
@@ -0,0 +1,58 @@
/*
* 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 <RC/ResourceCompilerScene/Chr/ChrExporter.h>
#include <Cry_Geo.h>
#include <ConvertContext.h>
#include <CGFContent.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/ISkeletonGroup.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <RC/ResourceCompilerScene/Chr/ChrExportContexts.h>
namespace AZ
{
namespace RC
{
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
ChrExporter::ChrExporter(IConvertContext* convertContext)
: m_convertContext(convertContext)
{
BindToCall(&ChrExporter::ProcessContext);
ActivateBindings();
}
SceneEvents::ProcessingResult ChrExporter::ProcessContext(SceneEvents::ExportEventContext& context)
{
const SceneContainers::SceneManifest& manifest = context.GetScene().GetManifest();
auto valueStorage = manifest.GetValueStorage();
auto view = SceneContainers::MakeDerivedFilterView<SceneDataTypes::ISkeletonGroup>(valueStorage);
SceneEvents::ProcessingResultCombiner result;
for (const SceneDataTypes::ISkeletonGroup& skeletonGroup : view)
{
AZ_TraceContext("Skeleton Group", skeletonGroup.GetName());
result += SceneEvents::Process<ChrGroupExportContext>(context, skeletonGroup, Phase::Construction);
result += SceneEvents::Process<ChrGroupExportContext>(context, skeletonGroup, Phase::Filling);
result += SceneEvents::Process<ChrGroupExportContext>(context, skeletonGroup, Phase::Finalizing);
}
return result.GetResult();
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,46 @@
/*
* 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 <SceneAPI/SceneCore/Events/CallProcessorBinder.h>
struct IConvertContext;
namespace AZ
{
namespace SceneAPI
{
namespace Events
{
class ExportEventContext;
}
}
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
class ChrExporter
: public SceneEvents::CallProcessorBinder
{
public:
ChrExporter(IConvertContext* convertContext);
~ChrExporter() override = default;
SceneEvents::ProcessingResult ProcessContext(SceneEvents::ExportEventContext& context);
private:
IConvertContext* m_convertContext;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,131 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Chr/ChrExportContexts.h>
#include <RC/ResourceCompilerScene/Chr/ChrGroupExporter.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkeletonGroup.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ
{
namespace RC
{
namespace SceneUtil = AZ::SceneAPI::Utilities;
namespace SceneContainer = AZ::SceneAPI::Containers;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
const AZStd::string ChrGroupExporter::fileExtension = "chr";
ChrGroupExporter::ChrGroupExporter(IAssetWriter* writer, IConvertContext* convertContext)
: m_assetWriter(writer)
, m_convertContext(convertContext)
{
BindToCall(&ChrGroupExporter::ProcessContext);
ActivateBindings();
}
SceneEvents::ProcessingResult ChrGroupExporter::ProcessContext(ChrGroupExportContext& context) const
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZStd::string filename = SceneUtil::FileUtilities::CreateOutputFileName(context.m_group.GetName(), context.m_outputDirectory, fileExtension);
if (filename.empty())
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Invalid filename, can not be an empty value.\n");
return SceneEvents::ProcessingResult::Failure;
}
if (!SceneUtil::FileUtilities::EnsureTargetFolderExists(filename))
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Invalid filename, target folder does not exist. ('%s')\n", filename.c_str());
return SceneEvents::ProcessingResult::Failure;
}
SceneEvents::ProcessingResultCombiner result;
CContentCGF cgfContent(filename.c_str());
AZStd::unordered_map<AZStd::string, int> boneNameIdMap;
SkeletonExportContext skeletonContextConstruction(context.m_scene, context.m_group.GetSelectedRootBone(), *cgfContent.GetSkinningInfo(), boneNameIdMap, Phase::Construction);
ConfigureChrContent(cgfContent);
result += SceneEvents::Process(skeletonContextConstruction);
result += SceneEvents::Process<SkeletonExportContext>(context.m_scene, context.m_group.GetSelectedRootBone(), *cgfContent.GetSkinningInfo(), boneNameIdMap, Phase::Filling);
result += SceneEvents::Process<SkeletonExportContext>(context.m_scene, context.m_group.GetSelectedRootBone(), *cgfContent.GetSkinningInfo(), boneNameIdMap, Phase::Finalizing);
AZ_Assert(m_assetWriter != nullptr, "Unable to write CHR due to invalid asset writer.");
if (m_assetWriter)
{
if (m_assetWriter->WriteCHR(&cgfContent, m_convertContext))
{
static const AZ::Data::AssetType skeletonAssetType("{60161B46-21F0-4396-A4F0-F2CCF0664CDE}");
auto& list = context.m_products.GetProducts();
bool isFirst = AZStd::find_if(list.begin(), list.end(),
[](const SceneAPI::Events::ExportProduct& it) -> bool
{
return it.m_assetType == skeletonAssetType;
}) == list.end();
SceneAPI::Events::ExportProduct& product = context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), skeletonAssetType,
AZStd::nullopt, AZStd::nullopt);
// Previously only a single skeleton would be exported that was named after the source file. This was changed to exporting all
// skeletons now named after the root node. This means that the first skeleton would previously have been known under
// another name.
if (isFirst)
{
AZStd::string legacyName = product.m_filename;
AzFramework::StringFunc::Path::ReplaceFullName(legacyName, context.m_scene.GetName().c_str(), fileExtension.c_str());
product.m_legacyFileNames.emplace_back(AZStd::move(legacyName));
}
}
else
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Failed writing CHR file ('%s')\n", filename.c_str());
result += SceneEvents::ProcessingResult::Failure;
}
}
else
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Failed writing CHR file ('%s')\n", filename.c_str());
result += SceneEvents::ProcessingResult::Failure;
}
return result.GetResult();
}
void ChrGroupExporter::ConfigureChrContent(CContentCGF& content) const
{
CExportInfoCGF* exportInfo = content.GetExportInfo();
AZ_Assert(exportInfo != nullptr, "Invalid export info from %s.", content.GetFilename());
exportInfo->bMergeAllNodes = true;
exportInfo->bUseCustomNormals = false;
exportInfo->bCompiledCGF = false;
exportInfo->bHavePhysicsProxy = false;
exportInfo->bHaveAutoLods = false;
exportInfo->bNoMesh = true;
exportInfo->b8WeightsPerVertex = false;
exportInfo->bWantF32Vertices = false;
exportInfo->authorToolVersion = 1;
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,47 @@
/*
* 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/std/string/string.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBinder.h>
struct IConvertContext;
class CContentCGF;
namespace AZ
{
namespace RC
{
struct ChrGroupExportContext;
namespace SceneEvents = AZ::SceneAPI::Events;
class ChrGroupExporter
: public SceneEvents::CallProcessorBinder
{
public:
ChrGroupExporter(IAssetWriter* writer, IConvertContext* convertContext);
~ChrGroupExporter() override = default;
SceneEvents::ProcessingResult ProcessContext(ChrGroupExportContext& context) const;
static const AZStd::string fileExtension;
protected:
void ConfigureChrContent(CContentCGF& content) const;
IAssetWriter* m_assetWriter;
IConvertContext* m_convertContext;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,47 @@
/*
* 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 <RC/ResourceCompilerScene/Common/AssetExportUtilities.h>
namespace AZ
{
namespace RC
{
Matrix34 AssetExportUtilities::ConvertToCryMatrix34(const SceneAPI::DataTypes::MatrixType& transform)
{
Matrix34 matrix;
matrix.m00 = transform.GetColumn(0).GetX();
matrix.m10 = transform.GetColumn(0).GetY();
matrix.m20 = transform.GetColumn(0).GetZ();
matrix.m01 = transform.GetColumn(1).GetX();
matrix.m11 = transform.GetColumn(1).GetY();
matrix.m21 = transform.GetColumn(1).GetZ();
matrix.m02 = transform.GetColumn(2).GetX();
matrix.m12 = transform.GetColumn(2).GetY();
matrix.m22 = transform.GetColumn(2).GetZ();
matrix.m03 = transform.GetColumn(3).GetX();
matrix.m13 = transform.GetColumn(3).GetY();
matrix.m23 = transform.GetColumn(3).GetZ();
return matrix;
}
float AssetExportUtilities::CryQuatDotProd(const CryQuat& q, const CryQuat& p)
{
return q.w*p.w + q.v*p.v;
}
}
}
@@ -0,0 +1,29 @@
#pragma once
/*
* 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 <Cry_Math.h>
#include <SceneAPI/SceneCore/DataTypes/MatrixType.h>
namespace AZ
{
namespace RC
{
class AssetExportUtilities
{
public:
static Matrix34 ConvertToCryMatrix34(const SceneAPI::DataTypes::MatrixType& transform);
static float CryQuatDotProd(const CryQuat& q, const CryQuat& p);
};
}
}
@@ -0,0 +1,136 @@
/*
* 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 <Cry_Geo.h>
#include <CGFContent.h>
#include <IIndexedMesh.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/algorithm.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphUpwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IBlendShapeData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/ITransform.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkinGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IBlendShapeRule.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/BlendShapeExporter.h>
namespace AZ
{
namespace RC
{
BlendShapeExporter::BlendShapeExporter()
{
BindToCall(&BlendShapeExporter::ProcessBlendShapes);
}
void BlendShapeExporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<BlendShapeExporter, SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneAPI::Events::ProcessingResult BlendShapeExporter::ProcessBlendShapes(MeshNodeExportContext& context)
{
if (context.m_phase != Phase::Filling)
{
return SceneAPI::Events::ProcessingResult::Ignored;
}
if (!context.m_group.RTTI_IsTypeOf(AZ::SceneAPI::DataTypes::ISkinGroup::TYPEINFO_Uuid()))
{
return SceneAPI::Events::ProcessingResult::Ignored;
}
AZStd::shared_ptr<const AZ::SceneAPI::DataTypes::IBlendShapeRule> blendShapeRule = context.m_group.GetRuleContainerConst().FindFirstByType<AZ::SceneAPI::DataTypes::IBlendShapeRule>();
if (!blendShapeRule)
{
return SceneAPI::Events::ProcessingResult::Ignored;
}
const SceneAPI::Containers::SceneGraph& graph = context.m_scene.GetGraph();
CSkinningInfo* skinInfo = context.m_container.GetSkinningInfo();
for (size_t index = 0; index < blendShapeRule->GetSceneNodeSelectionList().GetSelectedNodeCount(); ++index)
{
SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex = graph.Find(blendShapeRule->GetSceneNodeSelectionList().GetSelectedNode(index));
if (!nodeIndex.IsValid())
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Invalid name %s for blend shape.", blendShapeRule->GetSceneNodeSelectionList().GetSelectedNode(index).c_str());
return SceneAPI::Events::ProcessingResult::Failure;
}
AZStd::shared_ptr<const SceneAPI::DataTypes::IBlendShapeData> blendShape =
azrtti_cast<const SceneAPI::DataTypes::IBlendShapeData*>(graph.GetNodeContent(nodeIndex));
if (!blendShape)
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Unable to find blend shape.");
return SceneAPI::Events::ProcessingResult::Failure;
}
SceneAPI::DataTypes::MatrixType skinTransform = SceneAPI::DataTypes::MatrixType::Identity();
//Check to see if the blend shape parent skin has a transform an propagate that transform onto the vertices.
auto view = MakeSceneGraphChildView(graph, graph.GetNodeParent(nodeIndex), graph.GetContentStorage().begin(), true);
auto transform = AZStd::find_if(view.begin(), view.end(), SceneAPI::Containers::DerivedTypeFilter<SceneAPI::DataTypes::ITransform>());
if (transform != view.end())
{
skinTransform = azrtti_cast<const SceneAPI::DataTypes::ITransform*>(*transform)->GetMatrix();
}
MorphTargets* target = new MorphTargets();
target->MeshID = -1; //Based on the collada importer there's not a great way to set this.
target->m_strName = string(graph.GetNodeName(nodeIndex).GetName());
const size_t controlPointCount = blendShape->GetUsedControlPointCount();
for (size_t controlPointIndex = 0; controlPointIndex < controlPointCount; ++controlPointIndex)
{
SMeshMorphTargetVertex vert;
vert.nVertexId = controlPointIndex;
AZ::Vector3 vtx = blendShape->GetPosition(blendShape->GetUsedPointIndexForControlPoint(controlPointIndex));
//Apply base skin transform if one exists.
vtx = skinTransform * vtx;
vert.ptVertex = Vec3(vtx.GetX(), vtx.GetY(), vtx.GetZ());
target->m_arrIntMorph.push_back(vert);
}
skinInfo->m_arrMorphTargets.push_back(target);
}
return SceneAPI::Events::ProcessingResult::Success;
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,37 @@
/*
* 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 <SceneAPI/SceneCore/Components/RCExportingComponent.h>
namespace AZ
{
namespace RC
{
struct MeshNodeExportContext;
class BlendShapeExporter
: public SceneAPI::SceneCore::RCExportingComponent
{
public:
AZ_COMPONENT(BlendShapeExporter, "{1A27BF62-F684-4F9E-B2C6-B15E728659EA}", SceneAPI::SceneCore::RCExportingComponent);
BlendShapeExporter();
~BlendShapeExporter() override = default;
static void Reflect(ReflectContext* context);
SceneAPI::Events::ProcessingResult ProcessBlendShapes(MeshNodeExportContext& context);
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,111 @@
/*
* 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 <Cry_Geo.h> // Needed for IIndexedMesh.h
#include <IIndexedMesh.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IMeshAdvancedRule.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexColorData.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/ColorStreamExporter.h>
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneUtilities = AZ::SceneAPI::Utilities;
ColorStreamExporter::ColorStreamExporter()
{
BindToCall(&ColorStreamExporter::CopyVertexColorStream);
}
void ColorStreamExporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ColorStreamExporter, SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneEvents::ProcessingResult ColorStreamExporter::CopyVertexColorStream(MeshNodeExportContext& context) const
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
const SceneDataTypes::IGroup& group = context.m_group;
AZStd::shared_ptr<const SceneDataTypes::IMeshVertexColorData> colors = nullptr;
AZStd::string streamName;
AZStd::shared_ptr<const SceneDataTypes::IMeshAdvancedRule> rule = group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::IMeshAdvancedRule>();
if (!rule || rule->IsVertexColorStreamDisabled() || rule->GetVertexColorStreamName().empty())
{
return SceneEvents::ProcessingResult::Ignored;
}
AZ_TraceContext("Vertex color stream", rule->GetVertexColorStreamName());
SceneContainers::SceneGraph::NodeIndex index = graph.Find(context.m_nodeIndex, rule->GetVertexColorStreamName());
colors = azrtti_cast<const SceneDataTypes::IMeshVertexColorData*>(graph.GetNodeContent(index));
if (colors)
{
bool countMatch = context.m_mesh.GetVertexCount() == colors->GetCount();
if (!countMatch)
{
AZ_TracePrintf(SceneUtilities::ErrorWindow,
"Number of vertices in the mesh (%i) don't match with the number of stored vertex color stream (%i).",
context.m_mesh.GetVertexCount(), colors->GetCount());
return SceneEvents::ProcessingResult::Failure;
}
// Vertex coloring always uses the first vertex color stream.
context.m_mesh.ReallocStream(CMesh::COLORS, 0, context.m_mesh.GetVertexCount());
for (int i = 0; i < context.m_mesh.GetVertexCount(); ++i)
{
const SceneDataTypes::Color& color = colors->GetColor(i);
context.m_mesh.m_pColor0[i] = SMeshColor(
static_cast<uint8_t>(GetClamp<float>(color.red, 0.0f, 1.0f) * 255.0f),
static_cast<uint8_t>(GetClamp<float>(color.green, 0.0f, 1.0f) * 255.0f),
static_cast<uint8_t>(GetClamp<float>(color.blue, 0.0f, 1.0f) * 255.0f),
static_cast<uint8_t>(GetClamp<float>(color.alpha, 0.0f, 1.0f) * 255.0f));
}
}
else
{
AZ_TracePrintf(SceneUtilities::WarningWindow, "Vertex color stream not found or name doesn't refer to a vertex color stream.");
context.m_mesh.ReallocStream(CMesh::COLORS, 0, context.m_mesh.GetVertexCount());
for (int i = 0; i < context.m_mesh.GetVertexCount(); ++i)
{
context.m_mesh.m_pColor0[i] = SMeshColor(255, 255, 255, 255);
}
}
return SceneEvents::ProcessingResult::Success;
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,37 @@
/*
* 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 <SceneAPI/SceneCore/Components/RCExportingComponent.h>
namespace AZ
{
namespace RC
{
struct MeshNodeExportContext;
class ColorStreamExporter
: public SceneAPI::SceneCore::RCExportingComponent
{
public:
AZ_COMPONENT(ColorStreamExporter, "{912F9D7B-55C1-4871-A3BE-6C63B27E6B49}", SceneAPI::SceneCore::RCExportingComponent);
ColorStreamExporter();
~ColorStreamExporter() override = default;
static void Reflect(ReflectContext* context);
SceneAPI::Events::ProcessingResult CopyVertexColorStream(MeshNodeExportContext& context) const;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,160 @@
/*
* 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 <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISceneNodeGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IAnimationGroup.h>
namespace AZ
{
namespace RC
{
ContainerExportContext::ContainerExportContext(SceneAPI::Events::ExportEventContext& parent,
const SceneAPI::DataTypes::IGroup& group, CContentCGF& container, Phase phase)
: m_scene(parent.GetScene())
, m_outputDirectory(parent.GetOutputDirectory())
, m_group(group)
, m_container(container)
, m_phase(phase)
{
}
ContainerExportContext::ContainerExportContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& outputDirectory,
const SceneAPI::DataTypes::IGroup& group, CContentCGF& container, Phase phase)
: m_scene(scene)
, m_outputDirectory(outputDirectory)
, m_group(group)
, m_container(container)
, m_phase(phase)
{
}
ContainerExportContext::ContainerExportContext(const ContainerExportContext& copyContext, Phase phase)
: m_scene(copyContext.m_scene)
, m_outputDirectory(copyContext.m_outputDirectory)
, m_group(copyContext.m_group)
, m_container(copyContext.m_container)
, m_phase(phase)
{
}
NodeExportContext::NodeExportContext(ContainerExportContext& parent, CNodeCGF& node, const AZStd::string& nodeName,
SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex, EPhysicsGeomType physicalizeType, AZStd::string& rootBoneName, Phase phase)
: ContainerExportContext(parent, phase)
, m_node(node)
, m_nodeName(nodeName)
, m_nodeIndex(nodeIndex)
, m_physicalizeType(physicalizeType)
, m_rootBoneName(rootBoneName)
{
}
NodeExportContext::NodeExportContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& outputDirectory,
const SceneAPI::DataTypes::IGroup& group, CContentCGF& container, CNodeCGF& node,
const AZStd::string& nodeName, SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex, EPhysicsGeomType physicalizeType,
AZStd::string& rootBoneName, Phase phase)
: ContainerExportContext(scene, outputDirectory, group, container, phase)
, m_node(node)
, m_nodeName(nodeName)
, m_nodeIndex(nodeIndex)
, m_physicalizeType(physicalizeType)
, m_rootBoneName(rootBoneName)
{
}
NodeExportContext::NodeExportContext(const NodeExportContext& copyContext, Phase phase)
: ContainerExportContext(copyContext, phase)
, m_node(copyContext.m_node)
, m_nodeName(copyContext.m_nodeName)
, m_nodeIndex(copyContext.m_nodeIndex)
, m_physicalizeType(copyContext.m_physicalizeType)
, m_rootBoneName(copyContext.m_rootBoneName)
{
}
MeshNodeExportContext::MeshNodeExportContext(NodeExportContext& parent, CMesh& mesh, Phase phase)
: NodeExportContext(parent, phase)
, m_mesh(mesh)
{
}
MeshNodeExportContext::MeshNodeExportContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& outputDirectory,
const SceneAPI::DataTypes::IGroup& group, CContentCGF& container, CNodeCGF& node,
const AZStd::string& nodeName, SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex, EPhysicsGeomType physicalizeType,
AZStd::string& rootBoneName, CMesh& mesh, Phase phase)
: NodeExportContext(scene, outputDirectory, group, container, node, nodeName, nodeIndex, physicalizeType, rootBoneName, phase)
, m_mesh(mesh)
{
}
MeshNodeExportContext::MeshNodeExportContext(const MeshNodeExportContext& copyContext, Phase phase)
: NodeExportContext(copyContext, phase)
, m_mesh(copyContext.m_mesh)
{
}
TouchBendableMeshNodeExportContext::TouchBendableMeshNodeExportContext(const MeshNodeExportContext& copyContext, AZStd::string& rootBoneName, Phase phase)
: MeshNodeExportContext(copyContext, phase)
{
m_rootBoneName = rootBoneName;
}
ResolveRootBoneFromNodeContext::ResolveRootBoneFromNodeContext(
AZStd::string& result, const SceneAPI::Containers::Scene& scene, SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex)
: m_scene(scene)
, m_rootBoneName(result)
, m_nodeIndex(nodeIndex)
{
}
ResolveRootBoneFromBoneContext::ResolveRootBoneFromBoneContext(
AZStd::string& result, const SceneAPI::Containers::Scene& scene, const AZStd::string& boneName)
: m_scene(scene)
, m_boneName(boneName)
, m_rootBoneName(result)
{
}
AddBonesToSkinningInfoContext::AddBonesToSkinningInfoContext(
CSkinningInfo& skinningInfo, const SceneAPI::Containers::Scene& scene, const AZStd::string& rootBoneName)
: m_scene(scene)
, m_rootBoneName(rootBoneName)
, m_skinningInfo(skinningInfo)
{
}
BuildBoneMapContext::BuildBoneMapContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& rootBoneName,
AZStd::unordered_map<AZStd::string, int>& boneNameIdMap)
: m_scene(scene)
, m_rootBoneName(rootBoneName)
, m_boneNameIdMap(boneNameIdMap)
{
}
SkeletonExportContext::SkeletonExportContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& rootBoneName, CSkinningInfo& skinningInfo,
[[maybe_unused]] AZStd::unordered_map<AZStd::string, int>& boneNameIdMap, Phase phase)
: m_scene(scene)
, m_rootBoneName(rootBoneName)
, m_skinningInfo(skinningInfo)
, m_phase(phase)
{
}
SkeletonExportContext::SkeletonExportContext(const SkeletonExportContext& copyContext, Phase phase)
: m_scene(copyContext.m_scene)
, m_rootBoneName(copyContext.m_rootBoneName)
, m_skinningInfo(copyContext.m_skinningInfo)
, m_phase(phase)
{
}
} // RC
} // AZ
@@ -0,0 +1,195 @@
#pragma once
/*
* 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/RTTI/RTTI.h>
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IAnimationGroup.h>
#include <RC/ResourceCompilerScene/Common/ExportContextGlobal.h>
#include <CryHeaders.h>
class CContentCGF;
struct CNodeCGF;
class CMesh;
struct CSkinningInfo;
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class IGroup;
class ISceneNodeSelectionList;
}
}
namespace RC
{
// Called while creating, filling and finalizing a CContentCGF container.
struct ContainerExportContext
: public SceneAPI::Events::ICallContext
{
AZ_RTTI(ContainerExportContext, "{667A9E60-F3AA-45E1-8E66-05B0C971A094}", SceneAPI::Events::ICallContext);
ContainerExportContext(SceneAPI::Events::ExportEventContext& parent,
const SceneAPI::DataTypes::IGroup& group, CContentCGF& container, Phase phase);
ContainerExportContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& outputDirectory,
const SceneAPI::DataTypes::IGroup& group, CContentCGF& container, Phase phase);
ContainerExportContext(const ContainerExportContext& copyContext, Phase phase);
ContainerExportContext(const ContainerExportContext& copyContext) = delete;
~ContainerExportContext() override = default;
ContainerExportContext& operator=(const ContainerExportContext& other) = delete;
const SceneAPI::Containers::Scene& m_scene;
const AZStd::string& m_outputDirectory;
const SceneAPI::DataTypes::IGroup& m_group;
CContentCGF& m_container;
const Phase m_phase;
};
// Called when a new CNode is added to a CContentCGF container.
struct NodeExportContext
: public ContainerExportContext
{
AZ_RTTI(NodeExportContext, "{A7D130C6-2CB2-47AC-9D9C-969FA473DFDA}", ContainerExportContext);
NodeExportContext(ContainerExportContext& parent, CNodeCGF& node, const AZStd::string& nodeName,
SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex, EPhysicsGeomType physicalizeType, AZStd::string& rootBoneName, Phase phase);
NodeExportContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& outputDirectory,
const SceneAPI::DataTypes::IGroup& group,
CContentCGF& container, CNodeCGF& node, const AZStd::string& nodeName, SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex,
EPhysicsGeomType physicalizeType, AZStd::string& rootBoneName, Phase phase);
NodeExportContext(const NodeExportContext& copyContext, Phase phase);
NodeExportContext(const NodeExportContext& copyContext) = delete;
~NodeExportContext() override = default;
NodeExportContext& operator=(const NodeExportContext& other) = delete;
CNodeCGF& m_node;
const AZStd::string& m_nodeName;
SceneAPI::Containers::SceneGraph::NodeIndex m_nodeIndex;
EPhysicsGeomType m_physicalizeType;
AZStd::string& m_rootBoneName;
};
// Called when new mesh data was added to a CNode in a CContentCGF container.
struct MeshNodeExportContext
: public NodeExportContext
{
AZ_RTTI(MeshNodeExportContext, "{D39D08D6-8EB5-4058-B9D7-BED4EB460555}", NodeExportContext);
MeshNodeExportContext(NodeExportContext& parent, CMesh& mesh, Phase phase);
MeshNodeExportContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& outputDirectory,
const SceneAPI::DataTypes::IGroup& group, CContentCGF& container, CNodeCGF& node,
const AZStd::string& nodeName, SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex, EPhysicsGeomType physicalizeType,
AZStd::string& rootBoneName, CMesh& mesh, Phase phase);
MeshNodeExportContext(const MeshNodeExportContext& copyContext, Phase phase);
MeshNodeExportContext(const MeshNodeExportContext& copyContext) = delete;
~MeshNodeExportContext() override = default;
MeshNodeExportContext& operator=(const MeshNodeExportContext& other) = delete;
CMesh& m_mesh;
};
struct TouchBendableMeshNodeExportContext
: public MeshNodeExportContext
{
AZ_RTTI(TouchBendableMeshNodeExportContext, "{A3370E01-EF04-4F5A-95F3-5B9ADFEFD2F0}", MeshNodeExportContext);
TouchBendableMeshNodeExportContext(const MeshNodeExportContext& copyContext, AZStd::string& rootBoneName, Phase phase);
TouchBendableMeshNodeExportContext(const TouchBendableMeshNodeExportContext& copyContext) = delete;
~TouchBendableMeshNodeExportContext() override = default;
TouchBendableMeshNodeExportContext& operator=(const TouchBendableMeshNodeExportContext& other) = delete;
};
// Finds a root bone of the skeleton that is referenced by the given node.
struct ResolveRootBoneFromNodeContext
: public SceneAPI::Events::ICallContext
{
AZ_RTTI(ResolveRootBoneFromNodeContext, "{7BA28E30-E313-4B55-8200-C3BDD4EEE240}", SceneAPI::Events::ICallContext);
ResolveRootBoneFromNodeContext(AZStd::string& result, const SceneAPI::Containers::Scene& scene, SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex);
~ResolveRootBoneFromNodeContext() override = default;
const SceneAPI::Containers::Scene& m_scene;
AZStd::string& m_rootBoneName;
SceneAPI::Containers::SceneGraph::NodeIndex m_nodeIndex;
};
// Finds a root bone of the skeleton that contains m_boneName. If the given bone name is not a fully specified
// path the graph will be searched for the node that's closest to the root that matches the name.
struct ResolveRootBoneFromBoneContext
: public SceneAPI::Events::ICallContext
{
AZ_RTTI(ResolveRootBoneFromBoneContext, "{DCA7DE80-28D8-42B1-845D-2FD596E7B8D5}", SceneAPI::Events::ICallContext);
ResolveRootBoneFromBoneContext(AZStd::string& result, const SceneAPI::Containers::Scene& scene, const AZStd::string& boneName);
~ResolveRootBoneFromBoneContext() override = default;
const SceneAPI::Containers::Scene& m_scene;
const AZStd::string& m_boneName;
AZStd::string& m_rootBoneName;
};
struct AddBonesToSkinningInfoContext
: public SceneAPI::Events::ICallContext
{
AZ_RTTI(AddBonesToSkinningInfoContext, "{18BFBCA3-DE2D-45BF-A776-E93A991C467E}", SceneAPI::Events::ICallContext);
AddBonesToSkinningInfoContext(CSkinningInfo& skinningInfo, const SceneAPI::Containers::Scene& scene, const AZStd::string& rootBoneName);
~AddBonesToSkinningInfoContext() override = default;
const SceneAPI::Containers::Scene& m_scene;
const AZStd::string& m_rootBoneName;
CSkinningInfo& m_skinningInfo;
};
struct BuildBoneMapContext
: public SceneAPI::Events::ICallContext
{
AZ_RTTI(BuildBoneMapContext, "{9D9EE333-EC8C-4811-AB82-CC3B414E334C}", SceneAPI::Events::ICallContext);
BuildBoneMapContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& rootBoneName,
AZStd::unordered_map<AZStd::string, int>& boneNameIdMap);
~BuildBoneMapContext() override = default;
const SceneAPI::Containers::Scene& m_scene;
const AZStd::string& m_rootBoneName;
AZStd::unordered_map<AZStd::string, int>& m_boneNameIdMap;
};
struct SkeletonExportContext
: public SceneAPI::Events::ICallContext
{
AZ_RTTI(SkeletonExportContext, "{40512752-150F-4BAF-BC4E-01016DAE5088}", SceneAPI::Events::ICallContext);
SkeletonExportContext(const SceneAPI::Containers::Scene& scene, const AZStd::string& rootBoneName, CSkinningInfo& skinningInfo,
AZStd::unordered_map<AZStd::string, int>& boneNameIdMap, Phase phase);
SkeletonExportContext(const SkeletonExportContext& copyContext, Phase phase);
SkeletonExportContext(const SkeletonExportContext& copyContext) = delete;
~SkeletonExportContext() override = default;
SkeletonExportContext& operator=(const SkeletonExportContext& other) = delete;
const SceneAPI::Containers::Scene& m_scene;
const AZStd::string& m_rootBoneName;
CSkinningInfo& m_skinningInfo;
const Phase m_phase;
};
} // RC
} // AZ
@@ -0,0 +1,67 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IMeshAdvancedRule.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/ContainerSettingsExporter.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
ContainerSettingsExporter::ContainerSettingsExporter()
{
BindToCall(&ContainerSettingsExporter::ProcessContext);
}
void ContainerSettingsExporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<ContainerSettingsExporter, SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneEvents::ProcessingResult ContainerSettingsExporter::ProcessContext(ContainerExportContext& context) const
{
if (context.m_phase != Phase::Construction)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZStd::shared_ptr<const SceneDataTypes::IMeshAdvancedRule> advancedRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::IMeshAdvancedRule>();
if (advancedRule)
{
context.m_container.GetExportInfo()->bWantF32Vertices = advancedRule->Use32bitVertices();
context.m_container.GetExportInfo()->bMergeAllNodes = advancedRule->MergeMeshes();
context.m_container.GetExportInfo()->bUseCustomNormals = advancedRule->UseCustomNormals();
return SceneEvents::ProcessingResult::Success;
}
else
{
return SceneEvents::ProcessingResult::Ignored;
}
}
} // RC
} // AZ
@@ -0,0 +1,37 @@
/*
* 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 <SceneAPI/SceneCore/Components/RCExportingComponent.h>
namespace AZ
{
namespace RC
{
struct ContainerExportContext;
class ContainerSettingsExporter
: public SceneAPI::SceneCore::RCExportingComponent
{
public:
AZ_COMPONENT(ContainerSettingsExporter, "{878C641C-6614-413A-A174-EDFF84D8B119}", SceneAPI::SceneCore::RCExportingComponent);
ContainerSettingsExporter();
~ContainerSettingsExporter() override = default;
static void Reflect(ReflectContext* context);
SceneAPI::Events::ProcessingResult ProcessContext(ContainerExportContext& context) const;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,26 @@
#pragma once
/*
* 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.
*
*/
namespace AZ
{
namespace RC
{
enum class Phase
{
Construction, // The target is created.
Filling, // Data is added to the target.
Finalizing // Work on the target has completed.
};
}
}
@@ -0,0 +1,370 @@
/*
* 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 <Cry_Geo.h>
#include <IIndexedMesh.h>
#include <CGFContent.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <GFxFramework/MaterialIO/Material.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IMaterialRule.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMaterialData.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/Export/MtlMaterialExporter.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/MaterialExporter.h>
#include <SceneAPI/SceneCore/Containers/RuleContainer.h>
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneViews = AZ::SceneAPI::Containers::Views;
MaterialExporter::MaterialExporter()
: SceneAPI::SceneCore::RCExportingComponent()
, m_cachedGroup(nullptr)
, m_exportMaterial(true)
{
m_physMaterialNames[PHYS_GEOM_TYPE_DEFAULT_PROXY] = GFxFramework::MaterialExport::g_stringPhysicsNoDraw;
BindToCall(&MaterialExporter::ConfigureContainer);
BindToCall(&MaterialExporter::ProcessNode);
BindToCall(&MaterialExporter::PatchMesh);
}
void MaterialExporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MaterialExporter, SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneEvents::ProcessingResult MaterialExporter::ConfigureContainer(ContainerExportContext& context)
{
switch (context.m_phase)
{
case Phase::Construction:
{
if (!context.m_group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::IMaterialRule>())
{
m_exportMaterial = false;
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Skipping material processing due to material rule not being present.");
return SceneEvents::ProcessingResult::Ignored;
}
if (!LoadMaterialFile(context))
{
m_exportMaterial = false;
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Unable to read MTL file for processing meshes.");
return SceneEvents::ProcessingResult::Failure;
}
m_cachedGroup = &(context.m_group);
SetupGlobalMaterial(context);
return SceneEvents::ProcessingResult::Success;
}
case Phase::Finalizing:
if (!m_exportMaterial)
{
Reset();
return SceneEvents::ProcessingResult::Ignored;
}
PatchSubmeshes(context);
CreateSubMaterials(context);
Reset();
return SceneEvents::ProcessingResult::Success;
default:
return SceneEvents::ProcessingResult::Ignored;
}
}
SceneEvents::ProcessingResult MaterialExporter::ProcessNode(NodeExportContext& context)
{
if (context.m_phase == Phase::Filling && m_exportMaterial)
{
AssignCommonMaterial(context);
return SceneEvents::ProcessingResult::Success;
}
else
{
return SceneEvents::ProcessingResult::Ignored;
}
}
SceneEvents::ProcessingResult MaterialExporter::PatchMesh(MeshNodeExportContext& context)
{
if (context.m_phase == Phase::Filling && m_exportMaterial)
{
return PatchMaterials(context);
}
else
{
return SceneEvents::ProcessingResult::Ignored;
}
}
bool MaterialExporter::LoadMaterialFile(ContainerExportContext& context)
{
// Load the material from the source first. If there's no source material a temporary material should have been
// created in the cache by the MaterialExporterComponent in SceneCore.
m_materialGroup = AZStd::make_shared<GFxFramework::MaterialGroup>();
bool fileRead = false;
AZStd::string materialPath = context.m_scene.GetSourceFilename();
AzFramework::StringFunc::Path::ReplaceExtension(materialPath, GFxFramework::MaterialExport::g_mtlExtension);
AZ_TraceContext("Material source file path", materialPath);
//get if we need to upate materials in source folder
const AZ::SceneAPI::Containers::RuleContainer& rules = context.m_group.GetRuleContainerConst();
AZStd::shared_ptr<const SceneDataTypes::IMaterialRule> materialRule = rules.FindFirstByType<SceneDataTypes::IMaterialRule>();
bool updateMaterials = materialRule->UpdateMaterials();
//if the source material exist and we won't need to update material later then we load the material from source folder
if (AZ::IO::SystemFile::Exists(materialPath.c_str()) && !updateMaterials)
{
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Using source material file for linking to meshes.");
fileRead = m_materialGroup->ReadMtlFile(materialPath.c_str());
}
else
{
materialPath = SceneAPI::Utilities::FileUtilities::CreateOutputFileName(
context.m_scene.GetName(), context.m_outputDirectory, GFxFramework::MaterialExport::g_dccMaterialExtension);
AZ_TraceContext("Material cache file path", materialPath);
if (AZ::IO::SystemFile::Exists(materialPath.c_str()))
{
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Using cached material file for linking to meshes.");
fileRead = m_materialGroup->ReadMtlFile(materialPath.c_str());
}
}
if (!fileRead)
{
m_materialGroup.reset();
}
return fileRead;
}
void MaterialExporter::SetupGlobalMaterial(ContainerExportContext& context)
{
AZ_Assert(m_cachedGroup == &context.m_group, "ContainerExportContext doesn't belong to chain of previously called MeshGroupExportContext.");
CMaterialCGF* rootMaterial = context.m_container.GetCommonMaterial();
if (!rootMaterial)
{
rootMaterial = new CMaterialCGF();
rootMaterial->nPhysicalizeType = PHYS_GEOM_TYPE_NONE;
azstrcpy(rootMaterial->name, sizeof(rootMaterial->name), context.m_scene.GetName().c_str());
context.m_container.SetCommonMaterial(rootMaterial);
}
}
void MaterialExporter::AssignCommonMaterial(NodeExportContext& context)
{
AZ_Assert(m_cachedGroup == &context.m_group, "MeshNodeExportContext doesn't belong to chain of previously called MeshGroupExportContext.");
CMaterialCGF* rootMaterial = context.m_container.GetCommonMaterial();
AZ_Assert(rootMaterial, "Previously assigned root material has been deleted.");
context.m_node.pMaterial = rootMaterial;
}
SceneAPI::Events::ProcessingResult MaterialExporter::PatchMaterials(MeshNodeExportContext& context)
{
AZ_Assert(m_cachedGroup == &context.m_group, "MeshNodeExportContext doesn't belong to chain of previously\
called MeshGroupExportContext.");
AZStd::vector<size_t> relocationTable;
SceneEvents::ProcessingResult result = BuildRelocationTable(relocationTable, context);
if (result == SceneEvents::ProcessingResult::Failure)
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Material mapping error, mesh generation failed. \
Change FBX Setting's \"Update Materials\" to true or modify the associated material file(.mtl) to fix the issue.");
return result;
}
if (relocationTable.empty())
{
// If the relocationTable is empty no materials were assigned to any of the
// selected meshes. In this case simply leave the subsets as assigned
// so users can later manually add materials if needed.
return SceneEvents::ProcessingResult::Ignored;
}
if (context.m_container.GetExportInfo()->bMergeAllNodes)
{
// Due to a bug which cases subsets to not merge correctly (see PatchSubmeshes for more details) use the global
// table so far to patch the subset index in the face info instead. This way they will be assigned to the
// eventual global subset stored in the first mesh.
int faceCount = context.m_mesh.GetFaceCount();
for (int i = 0; i < faceCount; ++i)
{
context.m_mesh.m_pFaces[i].nSubset = relocationTable[context.m_mesh.m_pFaces[i].nSubset];
}
}
else
{
for (SMeshSubset& subset : context.m_mesh.m_subsets)
{
subset.nMatID = relocationTable[subset.nMatID];
}
}
return SceneEvents::ProcessingResult::Success;
}
void MaterialExporter::PatchSubmeshes(ContainerExportContext& context)
{
// Due to a bug in the merging process of the Compiler it will always take the number of subsets of the first mesh
// it finds. This causes files with more materials than the first model to not merge properly and ultimately cause
// the entire export to fail. (See CGFNodeMerger::MergeNodes for more details.) The work-around for now is to fill
// the first mesh up with placeholder subsets and adjust the subset indices in the face info.
AZ_Assert(m_cachedGroup == &context.m_group, "ContainerExportContext doesn't belong to chain of previously called MeshGroupExportContext.");
if (context.m_container.GetExportInfo()->bMergeAllNodes)
{
CMesh* firstMesh = nullptr;
int nodeCount = context.m_container.GetNodeCount();
for (int i = 0; i < nodeCount; ++i)
{
CNodeCGF* node = context.m_container.GetNode(i);
if (node->pMesh && !node->bPhysicsProxy && node->type == CNodeCGF::NODE_MESH)
{
firstMesh = node->pMesh;
break;
}
}
if (firstMesh)
{
int subsetCount = firstMesh->GetSubSetCount();
size_t materialCount = m_materialGroup->GetMaterialCount();
for (int i = 0; i < subsetCount; ++i)
{
AZ_Assert(firstMesh->m_subsets[i].nMatID == i, "Materials addition order broken. (%i vs. %i)", firstMesh->m_subsets[i].nMatID, i);
}
for (size_t i = subsetCount; i < materialCount; ++i)
{
SMeshSubset meshSubset;
meshSubset.nMatID = i;
firstMesh->m_subsets.push_back(meshSubset);
}
}
}
}
SceneAPI::Events::ProcessingResult MaterialExporter::BuildRelocationTable(AZStd::vector<size_t>& table, MeshNodeExportContext& context)
{
SceneEvents::ProcessingResultCombiner result;
auto physicalizeType = context.m_physicalizeType;
if ((physicalizeType == PHYS_GEOM_TYPE_DEFAULT_PROXY) || (physicalizeType == PHYS_GEOM_TYPE_NO_COLLIDE))
{
table.push_back(m_materialGroup->FindMaterialIndex(GFxFramework::MaterialExport::g_stringPhysicsNoDraw));
}
else
{
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
auto view = SceneViews::MakeSceneGraphChildView<SceneViews::AcceptEndPointsOnly>(
graph, context.m_nodeIndex, graph.GetContentStorage().begin(), true);
for (auto it = view.begin(); it != view.end(); ++it)
{
if ((*it) && (*it)->RTTI_IsTypeOf(SceneDataTypes::IMaterialData::TYPEINFO_Uuid()))
{
AZStd::string nodeName = graph.GetNodeName(graph.ConvertToNodeIndex(it.GetHierarchyIterator())).GetName();
size_t index = m_materialGroup->FindMaterialIndex(nodeName);
if (index == GFxFramework::MaterialExport::g_materialNotFound)
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Unable to find material named %s in mtl file while building FBX to Lumberyard material index table.", nodeName.c_str());
result += SceneEvents::ProcessingResult::Failure;
}
table.push_back(index);
}
}
}
return result.GetResult();
}
void MaterialExporter::CreateSubMaterials(ContainerExportContext& context)
{
AZ_Assert(m_cachedGroup == &context.m_group, "MeshNodeExportContext doesn't belong to chain of previously called MeshGroupExportContext.");
CMaterialCGF* rootMaterial = context.m_container.GetCommonMaterial();
if (!rootMaterial)
{
AZ_Assert(rootMaterial, "Previously assigned root material has been deleted.");
return;
}
// Create sub-materials stored in root material. Sub-materials will be used to assign physical types
// to subsets stored in meshes when mesh gets compiled later on.
rootMaterial->subMaterials.resize(m_materialGroup->GetMaterialCount(), nullptr);
for (size_t i = 0; i < m_materialGroup->GetMaterialCount(); ++i)
{
CMaterialCGF* materialCGF = new CMaterialCGF();
AZStd::shared_ptr<const GFxFramework::IMaterial> material = m_materialGroup->GetMaterial(i);
if (material)
{
azstrncpy(materialCGF->name, sizeof(materialCGF->name), material->GetName().c_str(), sizeof(materialCGF->name));
int materialFlags = material->GetMaterialFlags();
//MTL_FLAG_NODRAW_TOUCHBENDING and MTL_FLAG_NODRAW are mutually exclusive.
const int errorMask = AZ::GFxFramework::EMaterialFlags::MTL_FLAG_NODRAW_TOUCHBENDING |
AZ::GFxFramework::EMaterialFlags::MTL_FLAG_NODRAW;
AZ_Assert((materialFlags & errorMask) != errorMask, "A physics material can not be NODRAW and NODRAW_TOUCHBENDING at the the same time.");
if (materialFlags & AZ::GFxFramework::EMaterialFlags::MTL_FLAG_NODRAW_TOUCHBENDING)
{
materialCGF->nPhysicalizeType = PHYS_GEOM_TYPE_NO_COLLIDE;
}
else if (materialFlags & AZ::GFxFramework::EMaterialFlags::MTL_FLAG_NODRAW)
{
materialCGF->nPhysicalizeType = PHYS_GEOM_TYPE_DEFAULT_PROXY;
}
else
{
materialCGF->nPhysicalizeType = PHYS_GEOM_TYPE_NONE;
}
rootMaterial->subMaterials[i] = materialCGF;
}
}
}
void MaterialExporter::Reset()
{
m_materialGroup = nullptr;
m_exportMaterial = true;
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,68 @@
#pragma once
/*
* 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/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <GFxFramework/MaterialIO/IMaterial.h>
#include <SceneAPI/SceneCore/Components/RCExportingComponent.h>
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class IGroup;
}
}
namespace RC
{
struct ContainerExportContext;
struct NodeExportContext;
struct MeshNodeExportContext;
class MaterialExporter
: public SceneAPI::SceneCore::RCExportingComponent
{
public:
AZ_COMPONENT(MaterialExporter, "{F82300E0-ABE7-49F2-8BFF-1BFBD8BF3288}", SceneAPI::SceneCore::RCExportingComponent);
MaterialExporter();
~MaterialExporter() override = default;
static void Reflect(ReflectContext* context);
SceneAPI::Events::ProcessingResult ConfigureContainer(ContainerExportContext& context);
SceneAPI::Events::ProcessingResult ProcessNode(NodeExportContext& context);
SceneAPI::Events::ProcessingResult PatchMesh(MeshNodeExportContext& context);
protected:
bool LoadMaterialFile(ContainerExportContext& context);
void SetupGlobalMaterial(ContainerExportContext& context);
void CreateSubMaterials(ContainerExportContext& context);
void PatchSubmeshes(ContainerExportContext& context);
void AssignCommonMaterial(NodeExportContext& context);
SceneAPI::Events::ProcessingResult PatchMaterials(MeshNodeExportContext& context);
SceneAPI::Events::ProcessingResult BuildRelocationTable(AZStd::vector<size_t>& table, MeshNodeExportContext& context);
void Reset();
AZStd::shared_ptr<AZ::GFxFramework::IMaterialGroup> m_materialGroup;
AZStd::unordered_map<int, AZStd::string> m_physMaterialNames;
const SceneAPI::DataTypes::IGroup* m_cachedGroup;
bool m_exportMaterial;
};
} // RC
} // AZ
@@ -0,0 +1,191 @@
/*
* 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 <Cry_Geo.h>
#include <IIndexedMesh.h>
#include <CGFContent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <RC/ResourceCompilerScene/Common/MeshExporter.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkinGroup.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
namespace SceneContainers = AZ::SceneAPI::Containers;
MeshExporter::MeshExporter()
{
BindToCall(&MeshExporter::ProcessMesh);
}
void MeshExporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MeshExporter, SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneEvents::ProcessingResult MeshExporter::ProcessMesh(NodeExportContext& context) const
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
AZStd::shared_ptr<const SceneDataTypes::IMeshData> meshData =
azrtti_cast<const SceneDataTypes::IMeshData*>(graph.GetNodeContent(context.m_nodeIndex));
if (meshData)
{
SceneEvents::ProcessingResultCombiner result;
CMesh* mesh = new CMesh();
result += SceneEvents::Process<MeshNodeExportContext>(context, *mesh, Phase::Construction);
MeshNodeExportContext meshNodeContextFilling(context, *mesh, Phase::Filling);
SetMeshFaces(*meshData, *mesh, context.m_physicalizeType);
if (!SetMeshVertices(*meshData, *mesh))
{
return SceneEvents::ProcessingResult::Failure;
}
if (!SetMeshNormals(*meshData, *mesh))
{
return SceneEvents::ProcessingResult::Failure;
}
SetMeshTopologyIds(*meshData, *mesh, context);
context.m_node.type = CNodeCGF::NODE_MESH;
context.m_node.pMesh = mesh;
result += SceneEvents::Process(meshNodeContextFilling);
MeshNodeExportContext meshNodeContextFinalizing(context, *mesh, Phase::Finalizing);
context.m_container.GetExportInfo()->bNoMesh = false;
result += SceneEvents::Process(meshNodeContextFinalizing);
return result.GetResult();
}
else
{
return SceneEvents::ProcessingResult::Ignored;
}
}
void MeshExporter::SetMeshFaces(const SceneDataTypes::IMeshData& meshData, CMesh& mesh, EPhysicsGeomType physicalizeType) const
{
if (meshData.GetFaceCount() == 0)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "No mesh faces specified.");
return;
}
mesh.ReallocStream(CMesh::FACES, 0, meshData.GetFaceCount());
for (uint32_t i = 0; i < meshData.GetFaceCount(); ++i)
{
const SceneDataTypes::IMeshData::Face& face = meshData.GetFaceInfo(i);
mesh.m_pFaces[i].v[0] = face.vertexIndex[0];
mesh.m_pFaces[i].v[1] = face.vertexIndex[1];
mesh.m_pFaces[i].v[2] = face.vertexIndex[2];
// Create and use a unified subset if the mesh is chosen to be physicalized
if (physicalizeType == PHYS_GEOM_TYPE_DEFAULT_PROXY ||
physicalizeType == PHYS_GEOM_TYPE_OBSTRUCT ||
physicalizeType == PHYS_GEOM_TYPE_NO_COLLIDE)
{
mesh.m_pFaces[i].nSubset = 0;
if (mesh.m_subsets.empty())
{
SMeshSubset meshSubset;
meshSubset.nMatID = 0;
mesh.m_subsets.push_back(meshSubset);
}
}
else
{
int materialIndex = meshData.GetFaceMaterialId(i);
mesh.m_pFaces[i].nSubset = materialIndex;
while (mesh.m_subsets.size() <= materialIndex)
{
SMeshSubset meshSubset;
meshSubset.nMatID = mesh.m_subsets.size();
mesh.m_subsets.push_back(meshSubset);
}
}
}
}
bool MeshExporter::SetMeshVertices(const SceneDataTypes::IMeshData& meshData, CMesh& mesh) const
{
mesh.ReallocStream(CMesh::POSITIONS, 0, meshData.GetVertexCount());
for (uint32_t i = 0; i < meshData.GetVertexCount(); ++i)
{
const Vector3& position = meshData.GetPosition(i);
if (!position.IsFinite())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Invalid vertex data detected at index %d", i);
return false;
}
mesh.m_pPositions[i] = Vec3(position.GetX(), position.GetY(), position.GetZ());
}
return true;
}
bool MeshExporter::SetMeshNormals(const SceneDataTypes::IMeshData& meshData, CMesh& mesh) const
{
// Mesh requires normals. If they're missing add a stream of default normals.
mesh.ReallocStream(CMesh::NORMALS, 0, meshData.GetVertexCount());
if (meshData.HasNormalData())
{
for (int i = 0; i < meshData.GetVertexCount(); ++i)
{
const Vector3& normal = meshData.GetNormal(i);
if (!normal.IsFinite())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Invalid normal data detected at index %d", i);
return false;
}
mesh.m_pNorms[i] = SMeshNormal(Vec3(normal.GetX(), normal.GetY(), normal.GetZ()));
}
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "No mesh normals detected. Adding default normals.");
static const SMeshNormal defaultNormal(Vec3(1.0f, 0.0f, 0.0f));
for (int i = 0; i < meshData.GetVertexCount(); ++i)
{
mesh.m_pNorms[i] = defaultNormal;
}
}
return true;
}
void MeshExporter::SetMeshTopologyIds(const SceneAPI::DataTypes::IMeshData& meshData, CMesh& mesh, NodeExportContext& context) const
{
// Note, If this is a skin mesh create dummy topology id data even though it seems to be unnecessary data.
// Currently just provide it to prevent crash during skin mesh processing due to data misalignments.
if (context.m_group.RTTI_IsTypeOf(AZ::SceneAPI::DataTypes::ISkinGroup::TYPEINFO_Uuid()))
{
mesh.ReallocStream(CMesh::TOPOLOGY_IDS, 0, meshData.GetVertexCount());
}
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,53 @@
/*
* 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 <CryHeaders.h>
#include <SceneAPI/SceneCore/Components/RCExportingComponent.h>
class CMesh;
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class IMeshData;
}
}
namespace RC
{
struct NodeExportContext;
class MeshExporter
: public SceneAPI::SceneCore::RCExportingComponent
{
public:
AZ_COMPONENT(MeshExporter, "{1F826DB8-D6B0-4392-90C8-8F6E63F649CA}", SceneAPI::SceneCore::RCExportingComponent);
MeshExporter();
~MeshExporter() override = default;
static void Reflect(ReflectContext* context);
SceneAPI::Events::ProcessingResult ProcessMesh(NodeExportContext& context) const;
protected:
void SetMeshFaces(const SceneAPI::DataTypes::IMeshData& meshData, CMesh& mesh, EPhysicsGeomType physicalizeType) const;
bool SetMeshVertices(const SceneAPI::DataTypes::IMeshData& meshData, CMesh& mesh) const;
bool SetMeshNormals(const SceneAPI::DataTypes::IMeshData& meshData, CMesh& mesh) const;
void SetMeshTopologyIds(const SceneAPI::DataTypes::IMeshData& meshData, CMesh& mesh, NodeExportContext& context) const;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,312 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CryCrc32.h>
#include <CGFContent.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/SkeletonExporter.h>
#include <RC/ResourceCompilerScene/Common/AssetExportUtilities.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphUpwardsIterator.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IBoneData.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneUtil = AZ::SceneAPI::Utilities;
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
SkeletonExporter::SkeletonExporter()
{
BindToCall(&SkeletonExporter::ResolveRootBoneFromBone);
BindToCall(&SkeletonExporter::BuildBoneMap);
BindToCall(&SkeletonExporter::AddBonesToSkinningInfo);
BindToCall(&SkeletonExporter::ProcessSkeleton);
}
void SkeletonExporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<SkeletonExporter, SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneAPI::Events::ProcessingResult SkeletonExporter::ResolveRootBoneFromBone(ResolveRootBoneFromBoneContext& context)
{
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
const AZStd::string& boneName = context.m_boneName;
AZ_TraceContext("Bone name", boneName.c_str());
auto contentStorage = graph.GetContentStorage();
auto nameStorage = graph.GetNameStorage();
auto nameContentView = SceneContainers::Views::MakePairView(nameStorage, contentStorage);
// If the boneName is a full graph path, use that particular bone.
SceneContainers::SceneGraph::NodeIndex boneIndex = graph.Find(boneName);
if (!boneIndex.IsValid())
{
// If the the bone index is only the name, start looking for the first bone found with that name. The bone closest to
// the root of the graph is preferred.
auto graphDownwardsView = SceneContainers::Views::MakeSceneGraphDownwardsView<SceneContainers::Views::BreadthFirst>(
graph, graph.GetRoot(), nameContentView.begin(), true);
auto it = AZStd::find_if(graphDownwardsView.begin(), graphDownwardsView.end(),
[&boneName](const decltype(*nameContentView.begin())& entry) -> bool
{
if (!entry.second || !entry.second->RTTI_IsTypeOf(SceneDataTypes::IBoneData::TYPEINFO_Uuid()))
{
return false;
}
return azstrnicmp(entry.first.GetName(), boneName.c_str(), entry.first.GetNameLength()) == 0;
});
if (it == graphDownwardsView.end())
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Unable to find the skeleton root bone for bone");
return SceneEvents::ProcessingResult::Failure;
}
boneIndex = graph.ConvertToNodeIndex(it.GetHierarchyIterator());
}
AZ_Assert(boneIndex.IsValid(), "A bone was found but the index for it's node is still invalid.");
// Now that the bone has been found, search upwards to find the root bone of the skeleton the bone belongs to.
auto graphUpwardsView = SceneContainers::Views::MakeSceneGraphUpwardsView(graph, boneIndex, nameContentView.begin(), true);
const char* rootBoneName = nullptr;
for (const auto& it : graphUpwardsView)
{
if (it.second && it.second->RTTI_IsTypeOf(SceneDataTypes::IBoneData::TYPEINFO_Uuid()))
{
rootBoneName = it.first.GetPath();
}
else
{
break;
}
}
AZ_Assert(rootBoneName, "The name of the first bone should have been found.");
context.m_rootBoneName = rootBoneName;
return SceneEvents::ProcessingResult::Success;
}
SceneEvents::ProcessingResult SkeletonExporter::BuildBoneMap(BuildBoneMapContext& context)
{
return BuildBoneMap(context.m_boneNameIdMap, context.m_scene.GetGraph(), context.m_rootBoneName) ?
SceneEvents::ProcessingResult::Success : SceneEvents::ProcessingResult::Failure;
}
SceneEvents::ProcessingResult SkeletonExporter::AddBonesToSkinningInfo(AddBonesToSkinningInfoContext& context)
{
return AddBonesToSkinningInfo(context.m_skinningInfo, context.m_scene.GetGraph(), context.m_rootBoneName) ?
SceneEvents::ProcessingResult::Success : SceneEvents::ProcessingResult::Failure;
}
SceneEvents::ProcessingResult SkeletonExporter::ProcessSkeleton(SkeletonExportContext& context)
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
return AddBonesToSkinningInfo(context.m_skinningInfo, context.m_scene.GetGraph(), context.m_rootBoneName) ?
SceneEvents::ProcessingResult::Success : SceneEvents::ProcessingResult::Failure;
}
bool SkeletonExporter::AddBonesToSkinningInfo(CSkinningInfo& skinningInfo, const SceneContainers::SceneGraph& graph, const AZStd::string& rootBoneName) const
{
if (rootBoneName.empty())
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Root bone name cannot be empty.");
return false;
}
AZ_TraceContext("Root bone", rootBoneName);
AZStd::unordered_map<AZStd::string, int> boneNameIdMap;
if (!BuildBoneMap(boneNameIdMap, graph, rootBoneName))
{
// Error already reported by BuildBoneMap.
return false;
}
SceneContainers::SceneGraph::NodeIndex nodeIndex = graph.Find(rootBoneName);
if (!nodeIndex.IsValid())
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Unable to find root bone in scene graph.");
return false;
}
auto contentStorage = graph.GetContentStorage();
auto nameStorage = graph.GetNameStorage();
auto pairView = SceneContainers::Views::MakePairView(contentStorage, nameStorage);
auto view = SceneContainers::Views::MakeSceneGraphDownwardsView<SceneContainers::Views::DepthFirst>(graph, nodeIndex, pairView.begin(), true);
for (auto it = view.begin(); it != view.end(); ++it)
{
if (it->first && it->first->RTTI_IsTypeOf(SceneDataTypes::IBoneData::TYPEINFO_Uuid()))
{
AZ_TraceContext("Bone", it->second.GetPath());
AZStd::shared_ptr<const SceneDataTypes::IBoneData> boneData = azrtti_cast<const SceneDataTypes::IBoneData*>(it->first);
AZ_Assert(boneData, "Graph object couldn't be converted to bone data even though it matched the type.");
// Example fbx file exported from Maya will have default unit in centimeter.
// E.g. A global transformation in meter unit:
// 0.01 0 0 | 0.05
// 0 0.01 0 | 0
// 0 0 0.01 | 0
// while a global transform in centimeter unit:
// 1 0 0 | 5
// 0 1 0 | 0
// 0 0 1 | 0
// We need to remove scale from transform matrix (so the root bone's rotation matrix is identity) to satisfy the
// input requirement of AssetWriter
SceneAPI::DataTypes::MatrixType transformNoScale = boneData->GetWorldTransform();
AZ_Assert(transformNoScale.RetrieveScale().GetLength() >= Constants::FloatEpsilon, "Transform on bone %s has 0 scale", it->second.GetName());
transformNoScale.ExtractScale();
AddBoneDescriptor(skinningInfo, it->second.GetName(), it->second.GetNameLength(), transformNoScale);
if (!AddBoneEntity(skinningInfo, graph, graph.ConvertToNodeIndex(it.GetHierarchyIterator()), boneNameIdMap,
it->second.GetName(), it->second.GetPath(), rootBoneName))
{
// Error already reported in AddBoneEntity.
return false;
}
}
else
{
// End of bone chain or interruption in the bone chain. In both cases stop looking into this part of hierarchy further.
it.IgnoreNodeDescendants();
}
}
return true;
}
bool SkeletonExporter::BuildBoneMap(AZStd::unordered_map<AZStd::string, int>& boneNameIdMap, const SceneContainers::SceneGraph& graph, const AZStd::string& rootBoneName) const
{
if (rootBoneName.empty())
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Root bone name cannot be empty.");
return false;
}
AZ_TraceContext("Root bone", rootBoneName);
SceneContainers::SceneGraph::NodeIndex nodeIndex = graph.Find(rootBoneName);
if (!nodeIndex.IsValid())
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Unable to find root bone in scene graph.");
return false;
}
auto contentStorage = graph.GetContentStorage();
auto nameStorage = graph.GetNameStorage();
auto pairView = SceneContainers::Views::MakePairView(contentStorage, nameStorage);
auto view = SceneContainers::Views::MakeSceneGraphDownwardsView<SceneContainers::Views::DepthFirst>(graph, nodeIndex, pairView.begin(), true);
int index = 0;
for (auto it = view.begin(); it != view.end(); ++it)
{
if (it->first && it->first->RTTI_IsTypeOf(SceneDataTypes::IBoneData::TYPEINFO_Uuid()))
{
boneNameIdMap[it->second.GetName()] = index;
index++;
}
else
{
// End of bone chain or interruption in the bone chain. In both cases stop looking into this part of hierarchy further.
it.IgnoreNodeDescendants();
}
}
return true;
}
void SkeletonExporter::AddBoneDescriptor(CSkinningInfo& skinningInfo, const char* boneName, size_t boneNameLength,
const SceneAPI::DataTypes::MatrixType& worldTransform) const
{
CryBoneDescData boneDesc;
auto convertedTransform{ AssetExportUtilities::ConvertToCryMatrix34(worldTransform) };
AZ_Assert(convertedTransform.IsValid(), "Bone %s has invalid world transform", boneName);
// Invalid transform will set off an assertion in the equals operator below - the check above is so
// in case of that assertion AP will give a hint of what to look at in the logs
boneDesc.m_DefaultB2W = convertedTransform;
boneDesc.m_DefaultW2B = boneDesc.m_DefaultB2W.GetInverted();
SetBoneName(boneName, boneNameLength, boneDesc);
boneDesc.m_nControllerID = CCrc32::ComputeLowercase(boneName);
skinningInfo.m_arrBonesDesc.push_back(boneDesc);
}
bool SkeletonExporter::AddBoneEntity(CSkinningInfo& skinningInfo, const SceneContainers::SceneGraph& graph, const SceneContainers::SceneGraph::NodeIndex index,
const AZStd::unordered_map<AZStd::string, int>& boneNameIdMap, const char* boneName, const char* bonePath, const AZStd::string& rootBoneName) const
{
BONE_ENTITY boneEntity;
memset(&boneEntity, 0, sizeof(boneEntity));
auto boneIndex = boneNameIdMap.find(boneName);
if (boneIndex != boneNameIdMap.end())
{
boneEntity.BoneID = boneIndex->second;
boneEntity.ParentID = -1;
if (rootBoneName.compare(bonePath) != 0)
{
SceneContainers::SceneGraph::NodeIndex parentIndex = graph.GetNodeParent(index);
auto parentIt = boneNameIdMap.find(graph.GetNodeName(parentIndex).GetName());
if (parentIt != boneNameIdMap.end())
{
boneEntity.ParentID = parentIt->second;
}
else
{
AZ_TracePrintf(SceneUtil::ErrorWindow, "Bone is not the root bone but doesn't have another bone as it's parent.");
return false;
}
}
}
boneEntity.ControllerID = CCrc32::ComputeLowercase(boneName);
boneEntity.phys.nPhysGeom = -1;
auto childBones = SceneContainers::Views::MakeSceneGraphChildView<SceneContainers::Views::AcceptNodesOnly>(
graph, index, graph.GetNameStorage().begin(), true);
boneEntity.nChildren = aznumeric_caster(AZStd::count_if(childBones.begin(), childBones.end(),
[&boneNameIdMap](const SceneContainers::SceneGraph::Name& name)
{
return boneNameIdMap.find(name.GetName()) != boneNameIdMap.end();
}));
skinningInfo.m_arrBoneEntities.push_back(boneEntity);
return true;
}
void SkeletonExporter::SetBoneName(const char* name, size_t nameLength, CryBoneDescData& boneDesc) const
{
static const size_t nodeNameCount = sizeof(boneDesc.m_arrBoneName) / sizeof(boneDesc.m_arrBoneName[0]);
size_t offset = (nameLength < nodeNameCount) ? 0 : (nameLength - nodeNameCount + 1);
azstrcpy(boneDesc.m_arrBoneName, nodeNameCount, name + offset);
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,61 @@
/*
* 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/std/string/string.h>
#include <SceneAPI/SceneCore/Components/RCExportingComponent.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/DataTypes/MatrixType.h>
struct CryBoneDescData;
struct CSkinningInfo;
namespace AZ
{
namespace RC
{
struct SkeletonExportContext;
struct ResolveRootBoneFromBoneContext;
struct BuildBoneMapContext;
struct AddBonesToSkinningInfoContext;
class SkeletonExporter
: public SceneAPI::SceneCore::RCExportingComponent
{
public:
AZ_COMPONENT(SkeletonExporter, "{FDEC2360-3D9C-4027-BCFB-E8C99CAADB43}", SceneAPI::SceneCore::RCExportingComponent);
SkeletonExporter();
~SkeletonExporter() override = default;
static void Reflect(ReflectContext* context);
SceneAPI::Events::ProcessingResult ResolveRootBoneFromBone(ResolveRootBoneFromBoneContext& context);
SceneAPI::Events::ProcessingResult BuildBoneMap(BuildBoneMapContext& context);
SceneAPI::Events::ProcessingResult AddBonesToSkinningInfo(AddBonesToSkinningInfoContext& context);
SceneAPI::Events::ProcessingResult ProcessSkeleton(SkeletonExportContext& context);
protected:
bool AddBonesToSkinningInfo(CSkinningInfo& skinningInfo,
const AZ::SceneAPI::Containers::SceneGraph& graph, const AZStd::string& rootBoneName) const;
bool BuildBoneMap(AZStd::unordered_map<AZStd::string, int>& boneNameIdMap,
const AZ::SceneAPI::Containers::SceneGraph& graph, const AZStd::string& rootBoneName) const;
void AddBoneDescriptor(CSkinningInfo& skinningInfo, const char* boneName, size_t boneNameLength,
const SceneAPI::DataTypes::MatrixType& worldTransform) const;
bool AddBoneEntity(CSkinningInfo& skinningInfo, const AZ::SceneAPI::Containers::SceneGraph& graph,
const AZ::SceneAPI::Containers::SceneGraph::NodeIndex index, const AZStd::unordered_map<AZStd::string, int>& boneNameIdMap,
const char* boneName, const char* bonePath, const AZStd::string& rootBoneName) const;
void SetBoneName(const char* name, size_t nameLength, CryBoneDescData& boneDesc) const;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,240 @@
/*
* 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 <Cry_Geo.h>
#include <IIndexedMesh.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphUpwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IBoneData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/ISkinWeightData.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkinGroup.h>
#include <SceneAPI/SceneData/GraphData/MeshData.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/SkinWeightExporter.h>
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneUtils = AZ::SceneAPI::Utilities;
namespace SceneViews = SceneContainers::Views;
SkinWeightExporter::SkinWeightExporter()
{
BindToCall(&SkinWeightExporter::ResolveRootBoneFromNode);
BindToCall(&SkinWeightExporter::ProcessSkinWeights);
BindToCall(&SkinWeightExporter::ProcessTouchBendableSkinWeights);
}
void SkinWeightExporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<SkinWeightExporter, SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneEvents::ProcessingResult SkinWeightExporter::ResolveRootBoneFromNode(ResolveRootBoneFromNodeContext& context)
{
using namespace SceneContainers;
using namespace SceneContainers::Views;
using namespace SceneDataTypes;
using namespace SceneEvents;
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
auto attributeView = MakeSceneGraphChildView<AcceptEndPointsOnly>(graph, context.m_nodeIndex, graph.GetContentStorage().begin(), true);
auto weights = AZStd::find_if(attributeView.begin(), attributeView.end(), DerivedTypeFilter<ISkinWeightData>());
if (weights == attributeView.end() || azrtti_cast<const SceneDataTypes::ISkinWeightData*>(*weights)->GetBoneCount() == 0)
{
AZ_TracePrintf(SceneUtils::WarningWindow, "No skin weight data, skin weight data ignored.");
return SceneEvents::ProcessingResult::Ignored;
}
const AZStd::string& boneName = azrtti_cast<const SceneDataTypes::ISkinWeightData*>(*weights)->GetBoneName(0);
AZ_TraceContext("Bone name", boneName);
ProcessingResult result = SceneEvents::Process<ResolveRootBoneFromBoneContext>(context.m_rootBoneName, context.m_scene, boneName);
if (result == ProcessingResult::Ignored)
{
AZ_TracePrintf(SceneUtils::WarningWindow, "No system registered that can resolve bone names.");
}
else if (result == ProcessingResult::Failure)
{
AZ_TracePrintf(SceneUtils::ErrorWindow, "Failed to resolve skeleton from bone.");
}
return result;
}
SceneEvents::ProcessingResult SkinWeightExporter::ProcessSkinWeights(MeshNodeExportContext& context)
{
if (context.m_phase != Phase::Filling || !context.m_group.RTTI_IsTypeOf(AZ::SceneAPI::DataTypes::ISkinGroup::TYPEINFO_Uuid()))
{
return SceneEvents::ProcessingResult::Ignored;
}
AZ_TraceContext("Root bone", context.m_rootBoneName);
BoneNameIdMap boneNameIdMap;
SceneEvents::ProcessingResult result = SceneAPI::Events::Process<BuildBoneMapContext>(context.m_scene, context.m_rootBoneName, boneNameIdMap);
if (result == SceneEvents::ProcessingResult::Ignored)
{
AZ_TracePrintf(SceneUtils::WarningWindow, "No system registered that can handle skeletons for skins.");
return SceneEvents::ProcessingResult::Ignored;
}
else if (result == SceneEvents::ProcessingResult::Failure)
{
AZ_TracePrintf(SceneUtils::ErrorWindow, "Failed to load bone mapping for skin.");
return SceneEvents::ProcessingResult::Failure;
}
SetSkinWeights(context, boneNameIdMap);
return SceneEvents::ProcessingResult::Success;
}
SceneEvents::ProcessingResult SkinWeightExporter::ProcessTouchBendableSkinWeights(TouchBendableMeshNodeExportContext& context)
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZ_TraceContext("Root bone", context.m_rootBoneName);
BoneNameIdMap boneNameIdMap;
SceneEvents::ProcessingResult result = SceneAPI::Events::Process<BuildBoneMapContext>(context.m_scene, context.m_rootBoneName, boneNameIdMap);
if (result == SceneEvents::ProcessingResult::Ignored)
{
AZ_TracePrintf(SceneUtils::WarningWindow, "No system registered that can handle skeletons for skins.");
return SceneEvents::ProcessingResult::Ignored;
}
else if (result == SceneEvents::ProcessingResult::Failure)
{
AZ_TracePrintf(SceneUtils::ErrorWindow, "Failed to load bone mapping for skin.");
return SceneEvents::ProcessingResult::Failure;
}
SetSkinWeights(context, boneNameIdMap);
return SceneEvents::ProcessingResult::Success;
}
void SkinWeightExporter::SetSkinWeights(MeshNodeExportContext& context, BoneNameIdMap boneNameIdMap)
{
AZStd::shared_ptr<const SceneDataTypes::ISkinWeightData> skinWeights = nullptr;
AZStd::shared_ptr<const SceneData::GraphData::MeshData> meshData = nullptr;
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
SceneContainers::SceneGraph::NodeIndex index = graph.GetNodeChild(context.m_nodeIndex);
while (index.IsValid())
{
skinWeights = azrtti_cast<const SceneDataTypes::ISkinWeightData*>(graph.GetNodeContent(index));
if (skinWeights)
{
// Support first set of skin weights for now
auto parentIndex = graph.GetNodeParent(index);
if (parentIndex.IsValid())
{
meshData = azrtti_cast<const SceneData::GraphData::MeshData*>(graph.GetNodeContent(parentIndex));
}
else
{
AZ_TracePrintf(SceneUtils::WarningWindow, "Invalid mesh parent data for skin weights data");
}
break;
}
index = graph.GetNodeSibling(index);
}
if (skinWeights)
{
if (skinWeights->GetVertexCount() == 0)
{
AZ_TracePrintf(SceneUtils::WarningWindow, "Empty skin weight data, skin weight data ignored.");
return;
}
bool hasExtraWeights = false;
for (size_t vertexIndex = 0; vertexIndex < skinWeights->GetVertexCount(); ++vertexIndex)
{
if (skinWeights->GetLinkCount(vertexIndex) > 4)
{
hasExtraWeights = true;
break;
}
}
context.m_mesh.ReallocStream(CMesh::BONEMAPPING, 0, context.m_mesh.GetVertexCount());
if (hasExtraWeights)
{
context.m_mesh.ReallocStream(CMesh::EXTRABONEMAPPING, 0, context.m_mesh.GetVertexCount());
}
for (size_t vertexIndex = 0; vertexIndex < context.m_mesh.GetVertexCount(); ++vertexIndex)
{
int controlPointIndex = meshData->GetControlPointIndex(vertexIndex);
size_t linkCount = skinWeights->GetLinkCount(controlPointIndex);
for (size_t linkIndex = 0; linkIndex < 4 && linkIndex < linkCount; ++linkIndex)
{
const SceneDataTypes::ISkinWeightData::Link& link = skinWeights->GetLink(controlPointIndex, linkIndex);
context.m_mesh.m_pBoneMapping[vertexIndex].weights[linkIndex] = aznumeric_caster(GetClamp<float>(255.0f*link.weight, 0.0f, 255.0f));
context.m_mesh.m_pBoneMapping[vertexIndex].boneIds[linkIndex] =
aznumeric_caster(GetGlobalBoneId(skinWeights, boneNameIdMap, link.boneId));
}
if (hasExtraWeights)
{
for (size_t linkIndex = 4; linkIndex < 8 && linkIndex < linkCount; ++linkIndex)
{
const SceneDataTypes::ISkinWeightData::Link& link = skinWeights->GetLink(controlPointIndex, linkIndex);
context.m_mesh.m_pExtraBoneMapping[vertexIndex].weights[linkIndex - 4] = aznumeric_caster(GetClamp<float>(255.0f*link.weight, 0.0f, 255.0f));
context.m_mesh.m_pExtraBoneMapping[vertexIndex].boneIds[linkIndex - 4] =
aznumeric_caster(GetGlobalBoneId(skinWeights, boneNameIdMap, link.boneId));
}
}
}
}
}
int SkinWeightExporter::GetGlobalBoneId(
const AZStd::shared_ptr<const SceneDataTypes::ISkinWeightData>& skinWeights, BoneNameIdMap boneNameIdMap, int boneId)
{
AZ_TraceContext("Bone id", boneId);
const AZStd::string& boneName = skinWeights->GetBoneName(boneId);
AZ_TraceContext("Bone name", boneName);
if (boneName.empty())
{
AZ_TracePrintf(SceneUtils::WarningWindow, "Invalid local bone id referenced in skin weight data");
return -1;
}
auto it = boneNameIdMap.find(boneName);
if (it == boneNameIdMap.end())
{
AZ_TracePrintf(SceneUtils::WarningWindow, "Local bone name referenced in skin weight data doesn't exist in global bone map");
return -1;
}
return it->second;
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,57 @@
/*
* 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/std/containers/unordered_map.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Components/RCExportingComponent.h>
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class ISkinWeightData;
}
}
namespace RC
{
struct ResolveRootBoneFromNodeContext;
struct MeshNodeExportContext;
class SkinWeightExporter
: public SceneAPI::SceneCore::RCExportingComponent
{
public:
using BoneNameIdMap = AZStd::unordered_map<AZStd::string, int>;
AZ_COMPONENT(SkinWeightExporter, "{97C7D185-14F5-4BB1-AAE0-120A722882D1}", SceneAPI::SceneCore::RCExportingComponent);
SkinWeightExporter();
~SkinWeightExporter() override = default;
static void Reflect(ReflectContext* context);
SceneAPI::Events::ProcessingResult ResolveRootBoneFromNode(ResolveRootBoneFromNodeContext& context);
SceneAPI::Events::ProcessingResult ProcessSkinWeights(MeshNodeExportContext& context);
SceneAPI::Events::ProcessingResult ProcessTouchBendableSkinWeights(TouchBendableMeshNodeExportContext& context);
protected:
void SetSkinWeights(MeshNodeExportContext& context, BoneNameIdMap boneNameIdMap);
int GetGlobalBoneId(const AZStd::shared_ptr<const SceneAPI::DataTypes::ISkinWeightData>& skinWeights, BoneNameIdMap boneNameIdMap, int boneId);
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,223 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <PropertyHelpers.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/ITouchBendingRule.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IBoneData.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <RC/ResourceCompilerScene/Common/ExportContextGlobal.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/TouchBendingExporter.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h> //Needed by CgfExportContexts.h
#include <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfUtils.h>
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneUtil = AZ::SceneAPI::Utilities;
namespace SceneViews = AZ::SceneAPI::Containers::Views;
namespace AzStringFunc = AzFramework::StringFunc;
TouchBendingExporter::TouchBendingExporter()
: AZ::SceneAPI::SceneCore::RCExportingComponent()
{
BindToCall(&TouchBendingExporter::ConfigureContainer);
BindToCall(&TouchBendingExporter::ProcessSkinnedMesh);
}
void TouchBendingExporter::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<TouchBendingExporter, AZ::SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneEvents::ProcessingResult TouchBendingExporter::ConfigureContainer(ContainerExportContext& context)
{
switch (context.m_phase)
{
case Phase::Filling:
{
AZStd::shared_ptr<const SceneDataTypes::ITouchBendingRule> touchBendingRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::ITouchBendingRule>();
if (!touchBendingRule)
{
return SceneEvents::ProcessingResult::Ignored;
}
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
AZStd::vector<AZStd::string> noCollideTargetNodes = SceneUtil::SceneGraphSelector::GenerateTargetNodes(graph, touchBendingRule->GetSceneNodeSelectionList(), SceneUtil::SceneGraphSelector::IsMesh);
ProcessMeshType(context, context.m_container, noCollideTargetNodes, PHYS_GEOM_TYPE_NO_COLLIDE);
}
return SceneEvents::ProcessingResult::Success;
case Phase::Finalizing:
{
AZStd::shared_ptr<const SceneDataTypes::ITouchBendingRule> touchBendingRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::ITouchBendingRule>();
if (!touchBendingRule)
{
return SceneEvents::ProcessingResult::Ignored;
}
//Let's make sure we have valid CSkinningInfo, otherwise,
//there's no point in adding the Bone Tree helper nodes.
CSkinningInfo* skinningInfo = context.m_container.GetSkinningInfo();
if (skinningInfo->m_arrBonesDesc.size() < 1)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZStd::string rootBoneName = touchBendingRule->GetRootBoneName();
AddHelperBoneNodes(context, context.m_container, rootBoneName,
touchBendingRule->ShouldOverrideDamping(), touchBendingRule->GetOverrideDamping(),
touchBendingRule->ShouldOverrideStiffness(), touchBendingRule->GetOverrideStiffness(),
touchBendingRule->ShouldOverrideThickness(), touchBendingRule->GetOverrideThickness());
}
return SceneEvents::ProcessingResult::Success;
default:
return SceneEvents::ProcessingResult::Ignored;
}
}
SceneEvents::ProcessingResult TouchBendingExporter::ProcessSkinnedMesh(AZ::RC::MeshNodeExportContext& context)
{
if (context.m_physicalizeType != PHYS_GEOM_TYPE_NONE)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZStd::shared_ptr<const SceneDataTypes::ITouchBendingRule> touchBendingRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::ITouchBendingRule>();
if (!touchBendingRule)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZStd::string rootBoneName = touchBendingRule->GetRootBoneName();
SceneEvents::ProcessingResult result = SceneEvents::ProcessingResult::Ignored;
switch (context.m_phase)
{
case Phase::Filling:
result = SceneEvents::Process<TouchBendableMeshNodeExportContext>(
context, rootBoneName, Phase::Filling);
break;
case Phase::Construction:
{
//Add the Bones to CSkinningInfo only if they have not been added already.
CSkinningInfo* skinningInfo = context.m_container.GetSkinningInfo();
if (skinningInfo->m_arrBonesDesc.size() == 0)
{
result = SceneEvents::Process<AddBonesToSkinningInfoContext>(
*skinningInfo, context.m_scene, rootBoneName);
}
}
break;
default:
break;
}
return result;
}
/*!
The format is based on Cry's PropertyHelpers::SetPropertyValue.
This version doesn't do any nullptr checking or white space trimming,
because those errors are guaranteed not to happen.
*/
static void AddPropertyValue(AZStd::string& inoutPropertiesString, const char* propertyName, float value, const char * propertySeparator)
{
char valueStr[16];
snprintf(valueStr, sizeof(valueStr), "%f", value);
AzStringFunc::Append(inoutPropertiesString, propertyName);
AzStringFunc::Append(inoutPropertiesString, '=');
AzStringFunc::Append(inoutPropertiesString, valueStr);
if (propertySeparator)
{
AzStringFunc::Append(inoutPropertiesString, propertySeparator);
}
}
bool TouchBendingExporter::AddHelperBoneNodes(AZ::RC::ContainerExportContext& context, CContentCGF& content, AZStd::string& rootBoneName,
[[maybe_unused]] bool shouldOverrideDamping, float damping,
[[maybe_unused]] bool shouldOverrideStiffness, float stiffness,
[[maybe_unused]] bool shouldOverrideThickness, float thickness)
{
AZ_TraceContext("AddHelperBoneNodes() rootBoneName:", rootBoneName);
if (rootBoneName.empty())
{
AZ_TracePrintf(TraceWindowName, "Root bone name cannot be empty.");
return false;
}
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
SceneContainers::SceneGraph::NodeIndex nodeIndex = graph.Find(rootBoneName);
if (!nodeIndex.IsValid())
{
AZ_TracePrintf(TraceWindowName, "Unable to find root bone in scene graph.");
return false;
}
auto contentStorage = graph.GetContentStorage();
auto nameStorage = graph.GetNameStorage();
auto pairView = SceneViews::MakePairView(contentStorage, nameStorage);
auto view = SceneViews::MakeSceneGraphDownwardsView<SceneViews::DepthFirst>(graph, nodeIndex, pairView.begin(), true);
int index = 0;
//Once SceneAPI supports Per Node Attributes, the string with properties
//should be built per Node. In the meantime, because all the properties are
//the same for all nodes, the string can be built once.
AZStd::string nodeProperties;
AddPropertyValue(nodeProperties, NODE_PROPERTY_DAMPING, damping, "\r\n");
AddPropertyValue(nodeProperties, NODE_PROPERTY_STIFFNESS, stiffness, "\r\n");
AddPropertyValue(nodeProperties, NODE_PROPERTY_THICKNESS, thickness, nullptr);
for (auto it = view.begin(); it != view.end(); ++it)
{
if (it->first && it->first->RTTI_IsTypeOf(SceneDataTypes::IBoneData::TYPEINFO_Uuid()))
{
//These very dummy nodes, are only used to define the name
//of the spines. It is not necessary to set transform matrices,
//nor it is relevant to set parent pointers, etc.
CNodeCGF* nodeCgf = new CNodeCGF();
SetNodeName(it->second.GetName(), *nodeCgf);
nodeCgf->type = CNodeCGF::NODE_HELPER;
nodeCgf->helperType = HP_POINT;
nodeCgf->properties = nodeProperties.c_str();
content.AddNode(nodeCgf);
}
else
{
// End of bone chain or interruption in the bone chain. In both cases stop looking into this part of hierarchy further.
it.IgnoreNodeDescendants();
}
}
return true;
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,60 @@
#pragma once
/*
* 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/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <SceneAPI/SceneCore/Components/RCExportingComponent.h>
struct CNodeCGF;
class CContentCGF;
namespace AZ
{
class ReflectContext;
namespace RC
{
struct CgfGroupExportContext;
struct ContainerExportContext;
struct MeshNodeExportContext;
class TouchBendingExporter
: public AZ::SceneAPI::SceneCore::RCExportingComponent
{
public:
AZ_COMPONENT(TouchBendingExporter, "{4C6694B3-F7A8-48D8-A10A-46D57F8CC75E}", AZ::SceneAPI::SceneCore::RCExportingComponent);
TouchBendingExporter();
~TouchBendingExporter() override = default;
static void Reflect(AZ::ReflectContext* context);
SceneAPI::Events::ProcessingResult ConfigureContainer(AZ::RC::ContainerExportContext& context);
SceneAPI::Events::ProcessingResult ProcessSkinnedMesh(AZ::RC::MeshNodeExportContext& context);
static constexpr const char * const TraceWindowName = "TouchBending";
protected:
/*!
StaticObjectCompiler, when building SFoliageInfoCGF, uses the "branch%d_%d" named bones to build the spines.
This methods adds the tree of CNodeCGF helper nodes from Bones with such names.
*/
bool AddHelperBoneNodes(AZ::RC::ContainerExportContext& context, CContentCGF& content, AZStd::string& rootBoneName,
bool shouldOverrideDamping, float damping,
bool shouldOverrideStiffness, float stiffness,
bool shouldOverrideThickness, float thickness);
}; //class TouchBendingCgfExporter
} // namespace RC
} //namespace AZ
@@ -0,0 +1,149 @@
/*
* 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 <Cry_Geo.h> // Needed for IIndexedMesh.h
#include <IIndexedMesh.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IMeshAdvancedRule.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexUVData.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkinGroup.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/UVStreamExporter.h>
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneUtilities = AZ::SceneAPI::Utilities;
UVStreamExporter::UVStreamExporter()
{
BindToCall(&UVStreamExporter::CopyUVStream);
}
void UVStreamExporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<UVStreamExporter, SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneEvents::ProcessingResult UVStreamExporter::CopyUVStream(MeshNodeExportContext& context) const
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
const SceneDataTypes::IGroup& group = context.m_group;
SceneEvents::ProcessingResultCombiner result;
AZStd::vector<AZStd::shared_ptr<const SceneDataTypes::IMeshVertexUVData>> uvStreams;
// Find all uv streams and save them into uvStreams to be used later
SceneContainers::SceneGraph::NodeIndex index = graph.GetNodeChild(context.m_nodeIndex);
while (index.IsValid())
{
AZStd::string streamName;
AZStd::shared_ptr<const SceneDataTypes::IMeshVertexUVData> uvStream = azrtti_cast<const SceneDataTypes::IMeshVertexUVData*>(graph.GetNodeContent(index));
if (uvStream)
{
uvStreams.push_back(uvStream);
streamName = graph.GetNodeName(index).GetName();
AZ_TraceContext("UV set", streamName);
if (context.m_mesh.GetVertexCount() != uvStream->GetCount())
{
AZ_TracePrintf(SceneUtilities::ErrorWindow,
"Number of vertices in the mesh (%i) doesn't match with the number of stored UVs (%i).",
context.m_mesh.GetVertexCount(), uvStream->GetCount(), streamName.c_str());
result += SceneEvents::ProcessingResult::Failure;
}
}
index = graph.GetNodeSibling(index);
}
// Populate a default uv if there is no existing uv stream.
if (uvStreams.size() == 0)
{
AZ_TraceContext("UV set", "UVs not used");
uvStreams.emplace_back(nullptr);
}
for (size_t uvIndex = 0; uvIndex < AZStd::min((size_t)s_uvMaxStreamCount, uvStreams.size()); ++uvIndex)
{
AZStd::shared_ptr<const SceneDataTypes::IMeshVertexUVData> uvs = uvStreams[uvIndex];
result += PopulateUVStream(context, uvIndex, uvs);
}
return result.GetResult();
}
SceneEvents::ProcessingResult UVStreamExporter::PopulateUVStream(MeshNodeExportContext& context, int index, AZStd::shared_ptr<const SceneDataTypes::IMeshVertexUVData> uvs) const
{
context.m_mesh.ReallocStream(CMesh::TEXCOORDS, index, context.m_mesh.GetVertexCount());
SMeshTexCoord* uvStream = context.m_mesh.template GetStreamPtr<SMeshTexCoord>(CMesh::TEXCOORDS, index);
if (uvs)
{
for (int i = 0; i < context.m_mesh.GetVertexCount(); ++i)
{
const AZ::Vector2& uv = uvs->GetUV(i);
if (!uv.IsFinite())
{
AZ_TracePrintf(SceneUtilities::ErrorWindow, "Invalid UV data detected at index %d.", i);
return SceneEvents::ProcessingResult::Failure;
}
// Note: If this is a skin mesh the y value of texture coordinate needs to be inverted, because as it processes
// through CharacterCompiler::ProcessWork, it will get inverted again. This pre-corrects things to ensure the
// finally generated skin's uv texture coordinates are correct.
if (context.m_group.RTTI_IsTypeOf(AZ::SceneAPI::DataTypes::ISkinGroup::TYPEINFO_Uuid()))
{
uvStream[i] = SMeshTexCoord(uv.GetX(), 1.0f - uv.GetY());
}
else
{
uvStream[i] = SMeshTexCoord(uv.GetX(), uv.GetY());
}
}
}
//Default to a dummy stream of data.
else
{
static const SMeshTexCoord defaultTextureCoordinate(0.0f, 0.0f);
for (int i = 0; i < context.m_mesh.GetVertexCount(); ++i)
{
uvStream[i] = defaultTextureCoordinate;
}
}
return SceneEvents::ProcessingResult::Success;
}
} // namespace RC
} // 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.
*
*/
#pragma once
#include <SceneAPI/SceneCore/Components/RCExportingComponent.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexUVData.h>
namespace AZ
{
namespace RC
{
struct MeshNodeExportContext;
class UVStreamExporter
: public SceneAPI::SceneCore::RCExportingComponent
{
public:
AZ_COMPONENT(UVStreamExporter, "{3840C94B-C131-4C34-B35B-C8E8CFC5AFD1}", SceneAPI::SceneCore::RCExportingComponent);
UVStreamExporter();
~UVStreamExporter() override = default;
static void Reflect(ReflectContext* context);
SceneAPI::Events::ProcessingResult CopyUVStream(MeshNodeExportContext& context) const;
protected:
SceneAPI::Events::ProcessingResult PopulateUVStream(MeshNodeExportContext& context, int index, AZStd::shared_ptr<const SceneAPI::DataTypes::IMeshVertexUVData> uvs) const;
static const size_t s_uvMaxStreamCount = 2;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,199 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphUpwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IOriginRule.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/ITransform.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/WorldMatrixExporter.h>
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneViews = AZ::SceneAPI::Containers::Views;
WorldMatrixExporter::WorldMatrixExporter()
: m_cachedRootMatrix(MatrixType::CreateIdentity())
, m_cachedGroup(nullptr)
, m_cachedRootMatrixIsSet(false)
{
BindToCall(&WorldMatrixExporter::ProcessMeshGroup);
BindToCall(&WorldMatrixExporter::ProcessNode);
}
void WorldMatrixExporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<WorldMatrixExporter, SceneAPI::SceneCore::RCExportingComponent>()->Version(1);
}
}
SceneEvents::ProcessingResult WorldMatrixExporter::ProcessMeshGroup(ContainerExportContext& context)
{
if (context.m_phase != Phase::Construction)
{
return SceneEvents::ProcessingResult::Ignored;
}
m_cachedGroup = &context.m_group;
m_cachedRootMatrix = MatrixType::CreateIdentity();
m_cachedRootMatrixIsSet = false;
AZStd::shared_ptr<const SceneDataTypes::IOriginRule> rule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::IOriginRule>();
if (rule)
{
if (rule->GetTranslation() != Vector3(0.0f, 0.0f, 0.0f) || !rule->GetRotation().IsIdentity())
{
m_cachedRootMatrix = MatrixType::CreateFromQuaternionAndTranslation(rule->GetRotation(), rule->GetTranslation());
m_cachedRootMatrixIsSet = true;
}
if (rule->GetScale() != 1.0f)
{
float scale = rule->GetScale();
m_cachedRootMatrix.MultiplyByScale(Vector3(scale, scale, scale));
m_cachedRootMatrixIsSet = true;
}
if (!rule->GetOriginNodeName().empty() && !rule->UseRootAsOrigin())
{
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
SceneContainers::SceneGraph::NodeIndex index = graph.Find(rule->GetOriginNodeName());
if (index.IsValid())
{
MatrixType worldMatrix = MatrixType::CreateIdentity();
if (ConcatenateMatricesUpwards(worldMatrix, graph.ConvertToHierarchyIterator(index), graph))
{
worldMatrix.InvertFull();
m_cachedRootMatrix *= worldMatrix;
m_cachedRootMatrixIsSet = true;
}
}
}
return m_cachedRootMatrixIsSet ? SceneEvents::ProcessingResult::Success : SceneEvents::ProcessingResult::Ignored;
}
else
{
return SceneEvents::ProcessingResult::Ignored;
}
}
SceneEvents::ProcessingResult WorldMatrixExporter::ProcessNode(NodeExportContext& context)
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
MatrixType worldMatrix = MatrixType::CreateIdentity();
const SceneContainers::SceneGraph& graph = context.m_scene.GetGraph();
HierarchyStorageIterator nodeIterator = graph.ConvertToHierarchyIterator(context.m_nodeIndex);
bool translated = ConcatenateMatricesUpwards(worldMatrix, nodeIterator, graph);
AZ_Assert(m_cachedGroup == &context.m_group, "NodeExportContext doesn't belong to chain of previously called MeshGroupExportContext.");
if (m_cachedRootMatrixIsSet)
{
worldMatrix = m_cachedRootMatrix * worldMatrix;
translated = true;
}
//If we aren't merging nodes we need to put the transforms into the localTM
//due to how the CGFSaver works inside the ResourceCompilerPC code.
if (!context.m_container.GetExportInfo()->bMergeAllNodes)
{
SceneAPIMatrixTypeToMatrix34(context.m_node.localTM, worldMatrix);
}
else
{
SceneAPIMatrixTypeToMatrix34(context.m_node.worldTM, worldMatrix);
}
context.m_node.bIdentityMatrix = !translated;
return SceneEvents::ProcessingResult::Success;
}
bool WorldMatrixExporter::ConcatenateMatricesUpwards(MatrixType& transform, const HierarchyStorageIterator& nodeIterator, const SceneContainers::SceneGraph& graph) const
{
bool translated = false;
auto view = SceneViews::MakeSceneGraphUpwardsView(graph, nodeIterator, graph.GetContentStorage().cbegin(), true);
for (auto it = view.begin(); it != view.end(); ++it)
{
if (!(*it))
{
continue;
}
const SceneDataTypes::ITransform* nodeTransform = azrtti_cast<const SceneDataTypes::ITransform*>(it->get());
if (nodeTransform)
{
transform = nodeTransform->GetMatrix() * transform;
translated = true;
}
else
{
bool endPointTransform = MultiplyEndPointTransforms(transform, it.GetHierarchyIterator(), graph);
translated = translated || endPointTransform;
}
}
return translated;
}
bool WorldMatrixExporter::MultiplyEndPointTransforms(MatrixType& transform, const HierarchyStorageIterator& nodeIterator, const SceneContainers::SceneGraph& graph) const
{
// If the translation is not an end point it means it's its own group as opposed to being
// a component of the parent, so only list end point children.
auto view = SceneViews::MakeSceneGraphChildView<SceneViews::AcceptEndPointsOnly>(graph, nodeIterator,
graph.GetContentStorage().begin(), true);
auto result = AZStd::find_if(view.begin(), view.end(), SceneContainers::DerivedTypeFilter<SceneDataTypes::ITransform>());
if (result != view.end())
{
transform = azrtti_cast<const SceneDataTypes::ITransform*>(result->get())->GetMatrix() * transform;
return true;
}
else
{
return false;
}
}
void WorldMatrixExporter::SceneAPIMatrixTypeToMatrix34(Matrix34& out, const MatrixType& in) const
{
// Setting column instead of row because as of writing Matrix34 doesn't support adding
// full rows, as the translation has to be done separately.
for (int column = 0; column < 4; ++column)
{
Vector3 data = in.GetColumn(column);
out.SetColumn(column, Vec3(data.GetX(), data.GetY(), data.GetZ()));
}
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,65 @@
/*
* 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 <Cry_Matrix34.h>
#include <SceneAPI/SceneCore/Components/RCExportingComponent.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/DataTypes/MatrixType.h>
namespace AZ
{
class Transform;
namespace SceneAPI
{
namespace DataTypes
{
class IOriginRule;
class IGroup;
}
}
namespace RC
{
struct ContainerExportContext;
struct NodeExportContext;
class WorldMatrixExporter
: public SceneAPI::SceneCore::RCExportingComponent
{
public:
AZ_COMPONENT(WorldMatrixExporter, "{65A0914C-5953-405F-819B-0E6EB96938F1}", SceneAPI::SceneCore::RCExportingComponent);
WorldMatrixExporter();
~WorldMatrixExporter() override = default;
static void Reflect(ReflectContext* context);
SceneAPI::Events::ProcessingResult ProcessMeshGroup(ContainerExportContext& context);
SceneAPI::Events::ProcessingResult ProcessNode(NodeExportContext& context);
protected:
using HierarchyStorageIterator = SceneAPI::Containers::SceneGraph::HierarchyStorageConstIterator;
using MatrixType = SceneAPI::DataTypes::MatrixType;
bool ConcatenateMatricesUpwards(MatrixType& transform, const HierarchyStorageIterator& nodeIterator, const SceneAPI::Containers::SceneGraph& graph) const;
bool MultiplyEndPointTransforms(MatrixType& transform, const HierarchyStorageIterator& nodeIterator, const SceneAPI::Containers::SceneGraph& graph) const;
void SceneAPIMatrixTypeToMatrix34(Matrix34& out, const MatrixType& in) const;
MatrixType m_cachedRootMatrix;
const SceneAPI::DataTypes::IGroup* m_cachedGroup;
bool m_cachedRootMatrixIsSet;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,33 @@
#pragma once
/*
* 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.
*
*/
class IConfig;
namespace AZ
{
class SerializeContext;
namespace RC
{
class ISceneConfig
{
public:
virtual ~ISceneConfig() = 0;
virtual size_t GetErrorCount() const = 0;
};
inline ISceneConfig::~ISceneConfig() = default;
} // RC
} // AZ
@@ -0,0 +1,35 @@
/*
* 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 "ResourceCompilerScene_precompiled.h"
#include <platform.h>
#include <IResCompiler.h>
#include <IRCLog.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <SceneConfig.h>
#include <SceneConverter.h>
extern "C"
{
DLL_EXPORT void __stdcall RegisterConvertors(IResourceCompiler* pRC)
{
PREVENT_MODULE_AND_ENVIRONMENT_SYMBOL_STRIPPING
SetRCLog(pRC->GetIRCLog());
AZStd::shared_ptr<AZ::RC::SceneConfig> config = AZStd::make_shared<AZ::RC::SceneConfig>();
pRC->RegisterConvertor("SceneConverter", new AZ::RC::SceneConverter(config));
}
}
@@ -0,0 +1,45 @@
#pragma once
/*
* 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.
*
*/
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#include <assert.h>
#define CRY_ASSERT(condition) assert(condition)
#define CRY_ASSERT_TRACE(condition, message) assert(condition)
#define CRY_ASSERT_MESSAGE(condition, message) assert(condition)
// Define this to prevent including CryAssert (there is no proper hook for turning this off, like the above).
#define CRYINCLUDE_CRYCOMMON_CRYASSERT_H
#include <platform.h>
#include <vector>
#include <map>
#include <stdio.h>
#include <Cry_Math.h>
#include <Cry_Geo.h>
#include <CryHeaders.h>
#include <primitives.h>
#include <smartptr.h>
#include <physinterface.h>
#include <CrySizer.h>
#include <RC/ResourceCompiler/IRCLog.h>
@@ -0,0 +1,597 @@
/*
* 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 "ResourceCompilerScene_precompiled.h"
#include <IRCLog.h>
#include <ISceneConfig.h>
#include <ISystem.h>
#include <SceneCompiler.h>
#include <AzCore/Casting/lossy_cast.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Memory/AllocatorManager.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzFramework/Asset/AssetSystemComponent.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <AzToolsFramework/SourceControl/PerforceComponent.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <RC/ResourceCompilerScene/Cgf/CgfExporter.h>
#include <RC/ResourceCompilerScene/Cgf/CgfGroupExporter.h>
#include <RC/ResourceCompilerScene/Cgf/CgfLodExporter.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Common/ContainerSettingsExporter.h>
#include <RC/ResourceCompilerScene/Common/MeshExporter.h>
#include <RC/ResourceCompilerScene/Common/MaterialExporter.h>
#include <RC/ResourceCompilerScene/Common/WorldMatrixExporter.h>
#include <RC/ResourceCompilerScene/Common/ColorStreamExporter.h>
#include <RC/ResourceCompilerScene/Common/UVStreamExporter.h>
#include <RC/ResourceCompilerScene/Common/SkeletonExporter.h>
#include <RC/ResourceCompilerScene/Common/SkinWeightExporter.h>
#include <RC/ResourceCompilerScene/Common/BlendShapeExporter.h>
#include <RC/ResourceCompilerScene/Common/TouchBendingExporter.h>
#include <RC/ResourceCompilerScene/SceneSerializationHandler.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Components/GenerationComponent.h>
#include <SceneAPI/SceneCore/Components/RCExportingComponent.h>
#include <SceneAPI/SceneCore/Components/SceneSystemComponent.h>
#include <SceneAPI/SceneCore/Components/Utilities/EntityConstructor.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Events/GenerateEventContext.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ
{
namespace RC
{
//! This function returns the build system target name
AZStd::string_view GetAssetBuilderTargetName()
{
#if !defined (LY_CMAKE_TARGET)
#error "LY_CMAKE_TARGET must be defined to the build target of the AssetBuilder application which is currently \"AssetBuilder\" via the source file properties for the SceneCompiler.cpp"
#endif
return AZStd::string_view{ LY_CMAKE_TARGET };
}
static const u32 s_maxLegacyCrcClashRetries = 255;
namespace SceneEvents = AZ::SceneAPI::Events;
namespace SceneContainers = AZ::SceneAPI::Containers;
RCToolApplication::RCToolApplication()
{
}
void RCToolApplication::RegisterDescriptors()
{
RegisterComponentDescriptor(SceneSerializationHandler::CreateDescriptor());
RegisterComponentDescriptor(BlendShapeExporter::CreateDescriptor());
RegisterComponentDescriptor(ColorStreamExporter::CreateDescriptor());
RegisterComponentDescriptor(ContainerSettingsExporter::CreateDescriptor());
RegisterComponentDescriptor(MaterialExporter::CreateDescriptor());
RegisterComponentDescriptor(MeshExporter::CreateDescriptor());
RegisterComponentDescriptor(SkeletonExporter::CreateDescriptor());
RegisterComponentDescriptor(SkinWeightExporter::CreateDescriptor());
RegisterComponentDescriptor(UVStreamExporter::CreateDescriptor());
RegisterComponentDescriptor(WorldMatrixExporter::CreateDescriptor());
RegisterComponentDescriptor(TouchBendingExporter::CreateDescriptor());
}
AZ::ComponentTypeList RCToolApplication::GetRequiredSystemComponents() const
{
AZ::ComponentTypeList components = AzToolsFramework::ToolsApplication::GetRequiredSystemComponents();
auto removed = AZStd::remove_if(components.begin(), components.end(),
[](const Uuid& id) -> bool
{
return id == azrtti_typeid<AzFramework::TargetManagementComponent>()
|| id == azrtti_typeid<AzToolsFramework::PerforceComponent>()
|| id == azrtti_typeid<AZ::UserSettingsComponent>();
});
components.erase(removed, components.end());
return components;
}
void RCToolApplication::SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations)
{
AzToolsFramework::ToolsApplication::SetSettingsRegistrySpecializations(specializations);
specializations.Append("scenecompiler");
}
SceneCompiler::SceneCompiler(const AZStd::shared_ptr<ISceneConfig>& config, const char* appRoot)
: m_config(config)
, m_appRoot(appRoot)
{
}
void SceneCompiler::Release()
{
delete this;
}
void SceneCompiler::BeginProcessing(const IConfig* /*config*/)
{
}
bool SceneCompiler::Process()
{
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Starting scene processing.\n");
AssetBuilderSDK::ProcessJobResponse response;
RCToolApplication application;
// Add the Build Target name as a specialization to the settings registry
AZ::SettingsRegistryInterface& registry = *AZ::SettingsRegistry::Get();
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
registry, AZ::RC::GetAssetBuilderTargetName());
//the project name can be overridden, check it
AZStd::string overrideProjectName;
overrideProjectName = m_context.m_config->GetAsString("gamesubdirectory", "", "");
if (!overrideProjectName.empty())
{
// Copy the gamesubdirectory argument into --regset command line parameter for the sys_game_folder
auto gameNameOverride = AZStd::string::format("--regset=%s/sys_game_folder=%s", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey,
overrideProjectName.c_str());
AZ::CommandLine* commandLine = application.GetAzCommandLine();
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ::CommandLine::ParamContainer commandLineArgs;
commandLine->Dump(commandLineArgs);
commandLineArgs.emplace_back(gameNameOverride.c_str(), gameNameOverride.size());
commandLine->Parse(commandLineArgs);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*settingsRegistry, *commandLine, false);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
}
bool connectedToAP;
if (!PrepareForExporting(application, m_appRoot, connectedToAP))
{
bool result = WriteResponse(m_context.GetOutputFolder().c_str(), response, connectedToAP ? AssetBuilderSDK::ProcessJobResult_Failed : AssetBuilderSDK::ProcessJobResult_NetworkIssue);
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystemRequestBus::Events::StartDisconnectingAssetProcessor);
return result;
}
// Do this after PrepareForExporting is called so the types are registered for reading the request and writing a response.
AZStd::unique_ptr<AssetBuilderSDK::ProcessJobRequest> request = ReadJobRequest(m_context.GetOutputFolder().c_str());
if (!request)
{
bool result = WriteResponse(m_context.GetOutputFolder().c_str(), response, AssetBuilderSDK::ProcessJobResult_Failed);
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystemRequestBus::Events::StartDisconnectingAssetProcessor);
return result;
}
bool result = false;
// Active components, load the scene then process and export it.
{
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Creating scene system modules.\n");
AZ::Entity* systemEntity = AZ::SceneAPI::SceneCore::EntityConstructor::BuildSceneSystemEntity();
if (systemEntity)
{
constexpr const char PythonMarshalComponentTypeId[] = "{C733E1AD-9FDD-484E-A8D9-3EAB944B7841}";
constexpr const char PythonReflectionComponentTypeId[] = "{CBF32BE1-292C-4988-9E64-25127A8525A7}";
constexpr const char PythonSystemComponentTypeId[] = "{97F88B0F-CF68-4623-9541-549E59EE5F0C}";
systemEntity->CreateComponentIfReady({PythonSystemComponentTypeId});
systemEntity->CreateComponentIfReady({PythonMarshalComponentTypeId});
systemEntity->CreateComponentIfReady({PythonReflectionComponentTypeId});
systemEntity->Init();
systemEntity->Activate();
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Processing scene file.\n");
result = LoadAndExportScene(*request, response);
systemEntity->Deactivate();
delete systemEntity;
}
else
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Unable to create a system component for the SceneAPI.\n");
result = false;
}
}
if (!result || m_config->GetErrorCount() > 0)
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "During processing one or more problems were found.\n");
result = false;
}
// Manually disconnect from the Asset Processor before the application goes out of scope to avoid
// a potential serialization issue due to deficiencies in the order of teardown operations.
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystemRequestBus::Events::StartDisconnectingAssetProcessor);
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Finished scene processing.\n");
return WriteResponse(m_context.GetOutputFolder().c_str(), response, result ? AssetBuilderSDK::ProcessJobResult_Success : AssetBuilderSDK::ProcessJobResult_Failed);
}
void SceneCompiler::EndProcessing()
{
}
IConvertContext* SceneCompiler::GetConvertContext()
{
return &m_context;
}
bool SceneCompiler::PrepareForExporting(RCToolApplication& application, const AZStd::string& appRoot, bool& connectedToAssetProcessor)
{
// Not all Gems shutdown properly and leak memory, but this shouldn't
// prevent this builder from completing.
AZ::AllocatorManager::Instance().SetAllocatorLeaking(true);
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Initializing tools application environment.\n");
AZ::ComponentApplication::Descriptor descriptor;
descriptor.m_useExistingAllocator = true;
descriptor.m_enableScriptReflection = false;
AZ::ComponentApplication::StartupParameters startupParam;
startupParam.m_appRootOverride = appRoot.c_str();
startupParam.m_loadDynamicModules = false;
application.Start(descriptor, startupParam);
// Load Dynamic Modules after the Application::Start has been called to avoid creating
// creating SystemComponents automatically
application.LoadDynamicModules();
application.RegisterDescriptors();
// Register the AssetBuilderSDK structures needed later on.
AssetBuilderSDK::InitializeSerializationContext();
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Connecting to asset processor.\n");
// Retrieve the asset processor connection params from the settings registry
AzFramework::AssetSystem::ConnectionSettings connectionSettings;
bool succeeded = AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
if(!succeeded)
{
AZ_Error("RC Scene Compiler", false, "Getting bootstrap params failed");
return false;
}
//override bootstrap params
//the branch token can be overridden, check it
AZStd::string overrideBranchToken;
overrideBranchToken = m_context.m_config->GetAsString("branchtoken", "", "");
if (!overrideBranchToken.empty())
{
connectionSettings.m_branchToken = overrideBranchToken;
}
//the port can be overridden, check it
AZ::u16 overridePort = 0;
overridePort = aznumeric_cast<AZ::u16>(m_context.m_config->GetAsInt("port", 0, 0));
if (overridePort)
{
connectionSettings.m_assetProcessorPort = overridePort;
}
//the project name can be overridden, check it
AZStd::string overrideProjectName;
overrideProjectName = m_context.m_config->GetAsString("gamesubdirectory", "", "");
if (!overrideProjectName.empty())
{
connectionSettings.m_projectName = overrideProjectName;
}
connectionSettings.m_connectionIdentifier = "RC Scene Compiler";
connectionSettings.m_connectionDirection = AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
connectionSettings.m_launchAssetProcessorOnFailedConnection = false; // builders shouldn't launch the AssetProcessor
connectionSettings.m_waitUntilAssetProcessorIsReady = false; // builders are what make the AssetProcessor ready, so the cannot wait until the AssetProcessor is ready
connectionSettings.m_waitForConnect = true; // application is a builder so it needs to wait for a connection
// connect to Asset Processor.
AzFramework::AssetSystemRequestBus::BroadcastResult(connectedToAssetProcessor,
&AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings);
return connectedToAssetProcessor;
}
bool SceneCompiler::LoadAndExportScene(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
const string platformName = m_context.m_config->GetAsString("p", "<unknown>", "<invalid>");
AZ_TraceContext("Platform", platformName.c_str());
if (platformName == "<unknown>")
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "No target platform provided - this compiler requires the /p=platformIdentifier option\n");
return false;
}
if (platformName == "<invalid>")
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Invalid target platform provided (Parse error reading command line)\n");
return false;
}
AZStd::string sourcePath = m_context.GetSourcePath().c_str();
AZ_TraceContext("Source", sourcePath);
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Loading source files.\n");
AZStd::shared_ptr<SceneContainers::Scene> scene;
SceneEvents::SceneSerializationBus::BroadcastResult(scene, &SceneEvents::SceneSerializationBus::Events::LoadScene, sourcePath, request.m_sourceFileUUID);
if (!scene)
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Failed to load scene file.\n");
return false;
}
AZ_TraceContext("Manifest", scene->GetManifestFilename());
if (scene->GetManifest().IsEmpty())
{
AZ_TracePrintf(SceneAPI::Utilities::WarningWindow, "No manifest loaded and not enough information to create a default manifest.\n");
return true;
}
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Generating data into scene.\n");
if (!GenerateScene(*scene, platformName.c_str()))
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Failed to run data generation for scene.\n");
return false;
}
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Exporting loaded data to engine specific formats.\n");
if (!ExportScene(request, response, *scene, platformName.c_str()))
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Failed to convert and export scene\n");
return false;
}
return true;
}
bool SceneCompiler::GenerateScene(AZ::SceneAPI::Containers::Scene& scene, const char* platformIdentifier)
{
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Creating generation entities.\n");
AZ::SceneAPI::SceneCore::EntityConstructor::EntityPointer rcExporter = AZ::SceneAPI::SceneCore::EntityConstructor::BuildEntity(
"Scene Generators", AZ::SceneAPI::SceneCore::GenerationComponent::TYPEINFO_Uuid());
SceneEvents::ProcessingResultCombiner result;
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Preparing for generation.\n");
result += SceneEvents::Process<SceneEvents::PreGenerateEventContext>(scene, platformIdentifier);
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Generating...\n");
result += SceneEvents::Process<SceneEvents::GenerateEventContext>(scene, platformIdentifier);
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Finalizing generation process.\n");
result += SceneEvents::Process<SceneEvents::PostGenerateEventContext>(scene, platformIdentifier);
switch (result.GetResult())
{
case SceneEvents::ProcessingResult::Success:
case SceneEvents::ProcessingResult::Ignored:
return true;
case SceneEvents::ProcessingResult::Failure:
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Failure during conversion and exporting.\n");
return false;
}
return false;
}
bool SceneCompiler::ExportScene(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response, const AZ::SceneAPI::Containers::Scene& scene, const char* platformIdentifier)
{
AZ_TraceContext("Output folder", m_context.GetOutputFolder().c_str());
AZ_Assert(m_context.m_pRC->GetAssetWriter() != nullptr, "Invalid IAssetWriter initialization.");
if (!m_context.m_pRC->GetAssetWriter())
{
return false;
}
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Creating export entities.\n");
AZ::SceneAPI::SceneCore::EntityConstructor::EntityPointer rcExporter = AZ::SceneAPI::SceneCore::EntityConstructor::BuildEntity(
"Scene RC Exporters", AZ::SceneAPI::SceneCore::RCExportingComponent::TYPEINFO_Uuid());
// Register additional processors. Will be automatically unregistered when leaving scope.
// These have not yet been converted to components as they need special attention due
// to the arguments they currently need.
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Registering export processors.\n");
CgfExporter cgfProcessor(&m_context);
CgfGroupExporter meshGroupExporter(m_context.m_pRC->GetAssetWriter());
CgfLodExporter meshLodExporter(m_context.m_pRC->GetAssetWriter());
SceneAPI::Events::ExportProductList productList;
SceneEvents::ProcessingResultCombiner result;
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Preparing for export.\n");
result += SceneEvents::Process<SceneEvents::PreExportEventContext>(productList, m_context.GetOutputFolder().c_str(), scene, platformIdentifier);
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Exporting...\n");
result += SceneEvents::Process<SceneEvents::ExportEventContext>(productList, m_context.GetOutputFolder().c_str(), scene, platformIdentifier);
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Finalizing export process.\n");
result += SceneEvents::Process<SceneEvents::PostExportEventContext>(productList, m_context.GetOutputFolder().c_str(), platformIdentifier);
AZStd::map<AZStd::string, size_t> preSubIdFiles;
for (const auto& it : productList.GetProducts())
{
size_t index = response.m_outputProducts.size();
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Listed product: %s+0x%08x - %s (type %s)\n", it.m_id.ToString<AZStd::string>().c_str(),
BuildSubId(it), it.m_filename.c_str(), it.m_assetType.ToString<AZStd::string>().c_str());
response.m_outputProducts.emplace_back(AZStd::move(it.m_filename), it.m_assetType, BuildSubId(it));
if (IsPreSubIdFile(it.m_filename))
{
preSubIdFiles[it.m_filename] = index;
}
for (const AZStd::string& legacyIt : it.m_legacyFileNames)
{
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, " -> Legacy name: %s\n", legacyIt.c_str());
preSubIdFiles[legacyIt] = index;
}
// Add our relative path dependencies the exporters may have generated
AssetBuilderSDK::JobProduct& currentProduct = response.m_outputProducts.back();
for (const AZStd::string& pathDep : it.m_legacyPathDependencies)
{
// For now, we assume the relative path dependencies are simply file names.
// Append the source product path to these dependencies to generate the proper path dependency
AZStd::string relativePath;
AzFramework::StringFunc::Path::GetFolderPath(request.m_sourceFile.c_str(), relativePath);
AzFramework::StringFunc::AssetDatabasePath::Join(relativePath.c_str(), pathDep.c_str(), relativePath);
currentProduct.m_pathDependencies.emplace(relativePath, AssetBuilderSDK::ProductPathDependencyType::SourceFile);
}
// If we have any output products that are a dependency of this product, add them here.
// This will include adding LODs as dependencies of the base CGFs
for (auto& exportProduct : it.m_productDependencies)
{
AZ::Data::AssetId productAssetId(request.m_sourceFileUUID, BuildSubId(exportProduct));
currentProduct.m_dependencies.push_back(AssetBuilderSDK::ProductDependency(productAssetId, exportProduct.m_dependencyFlags));
}
currentProduct.m_dependenciesHandled = true; // We've populated the dependencies immediately above so it's OK to tell the AP we've handled dependencies
}
ResolvePreSubIds(response, preSubIdFiles);
switch (result.GetResult())
{
case SceneEvents::ProcessingResult::Success:
return true;
case SceneEvents::ProcessingResult::Ignored:
return true;
case SceneEvents::ProcessingResult::Failure:
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Failure during conversion and exporting.\n");
return false;
default:
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow,
"Unexpected result from conversion and exporting (%i).\n", result.GetResult());
return false;
}
}
bool SceneCompiler::IsPreSubIdFile(const AZStd::string& file) const
{
AZStd::string extension;
if (!AzFramework::StringFunc::Path::GetExtension(file.c_str(), extension))
{
return false;
}
return extension == ".caf" || extension == ".cgf" || extension == ".chr" || extension == ".mtl" || extension == ".skin";
}
// BuildSubId has an equivalent counterpart in SceneBuilder. Both need to remain the same to avoid problems with sub ids.
u32 SceneCompiler::BuildSubId(const SceneAPI::Events::ExportProduct& product) const
{
// Instead of the just the lower 16-bits, use the full 32-bits that are available. There are production examples of
// uber-fbx files that contain hundreds of meshes that need to be split into individual mesh objects as an example.
u32 id = static_cast<u32>(product.m_id.GetHash());
if (product.m_lod.has_value())
{
AZ::u8 lod = product.m_lod.value();
if (lod > 0xF)
{
AZ_TracePrintf(SceneAPI::Utilities::WarningWindow, "%i is too large to fit in the allotted bits for LOD.\n", static_cast<u32>(lod));
lod = 0xF;
}
// The product uses lods so mask out the lod bits and set them appropriately.
id &= ~AssetBuilderSDK::SUBID_MASK_LOD_LEVEL;
id |= lod << AssetBuilderSDK::SUBID_LOD_LEVEL_SHIFT;
}
return id;
}
void SceneCompiler::ResolvePreSubIds(AssetBuilderSDK::ProcessJobResponse& response, const AZStd::map<AZStd::string, size_t>& preSubIdFiles) const
{
if (!preSubIdFiles.empty())
{
// Start by compiling a list of known sub ids. Include sub ids from non-legacy files as well because sub ids created
// here are not allowed to clash with any sub id not matter if it's legacy or not.
AZStd::unordered_set<u32> assignedSubIds;
for (const auto& it : response.m_outputProducts)
{
if (assignedSubIds.find(it.m_productSubID) == assignedSubIds.end())
{
assignedSubIds.insert(it.m_productSubID);
}
else
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Sub id collision found (0x%04x).\n", it.m_productSubID);
}
}
// First legacy product always had sub id 0. Also add the hashed version in the loop though as there might be a file in
// front of it that the RCScene doesn't know about.
response.m_outputProducts[preSubIdFiles.begin()->second].m_legacySubIDs.push_back(0);
AZStd::string filename;
for (const auto& it : preSubIdFiles)
{
AZ_TraceContext("Legacy file name", it.first);
if (!AzFramework::StringFunc::Path::GetFullFileName(it.first.c_str(), filename))
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Unable to extract filename for legacy sub id.\n");
continue;
}
// Modified version of the algorithm in RCBuilder.
for (u32 seedValue = 0; seedValue < s_maxLegacyCrcClashRetries; ++seedValue)
{
u32 fullCrc = AZ::Crc32(filename.c_str());
u32 maskedCrc = (fullCrc + seedValue) & AssetBuilderSDK::SUBID_MASK_ID;
if (assignedSubIds.find(maskedCrc) == assignedSubIds.end())
{
response.m_outputProducts[it.second].m_legacySubIDs.push_back(maskedCrc);
assignedSubIds.insert(maskedCrc);
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "Added legacy sub id 0x%04x - %s\n", maskedCrc, filename.c_str());
break;
}
}
filename.clear();
}
}
}
AZStd::unique_ptr<AssetBuilderSDK::ProcessJobRequest> SceneCompiler::ReadJobRequest(const char* cacheFolder) const
{
AZStd::string requestFilePath;
AzFramework::StringFunc::Path::ConstructFull(cacheFolder, AssetBuilderSDK::s_processJobRequestFileName, requestFilePath);
AssetBuilderSDK::ProcessJobRequest* result = AZ::Utils::LoadObjectFromFile<AssetBuilderSDK::ProcessJobRequest>(requestFilePath);
if (!result)
{
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Unable to load ProcessJobRequest. Not enough information to process this file %s.\n", requestFilePath.c_str());
}
return AZStd::unique_ptr<AssetBuilderSDK::ProcessJobRequest>(result);
}
bool SceneCompiler::WriteResponse(const char* cacheFolder, AssetBuilderSDK::ProcessJobResponse& response, AssetBuilderSDK::ProcessJobResultCode jobResult) const
{
AZStd::string responseFilePath;
AzFramework::StringFunc::Path::ConstructFull(cacheFolder, AssetBuilderSDK::s_processJobResponseFileName, responseFilePath);
response.m_requiresSubIdGeneration = false;
response.m_resultCode = jobResult;
bool result = AZ::Utils::SaveObjectToFile(responseFilePath, AZ::DataStream::StreamType::ST_XML, &response);
return result && jobResult == AssetBuilderSDK::ProcessJobResult_Success;
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,110 @@
/*
* 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 <IConvertor.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
namespace AssetBuilderSDK
{
struct ProcessJobRequest;
struct ProcessJobResponse;
}
namespace AZ
{
class Entity;
namespace SceneAPI
{
namespace Containers
{
class Scene;
class SceneManifest;
}
namespace Events
{
struct ExportProduct;
}
namespace Import
{
class IImportersList;
}
}
namespace RC
{
// Used, among other things, to make sure a valid SerializeContext is available.
class RCToolApplication : public AzToolsFramework::ToolsApplication
{
public:
RCToolApplication();
void RegisterDescriptors();
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
void SetSettingsRegistrySpecializations(AZ::SettingsRegistryInterface::Specializations& specializations) override;
};
class ISceneConfig;
class SceneCompiler
: public ICompiler
{
public:
SceneCompiler(const AZStd::shared_ptr<ISceneConfig>& config, const char* appRoot);
void Release() override;
void BeginProcessing(const IConfig* config) override;
bool Process() override;
void EndProcessing() override;
IConvertContext* GetConvertContext() override;
protected:
bool PrepareForExporting(RCToolApplication& application, const AZStd::string& appRoot, bool& connectedToAssetProcessor);
bool LoadAndExportScene(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
// @brief Execute runtime modifications to the Scene graph
//
// This step is run after the scene is loaded, but before the scene
// is exported. It emits events with the GenerateEventContext.
// Event handlers bound to that event can apply arbitrary
// transformations to the Scene, adding new nodes, replacing nodes,
// or removing nodes.
bool GenerateScene(AZ::SceneAPI::Containers::Scene& scene, const char* platformIdentifier);
bool ExportScene(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response, const AZ::SceneAPI::Containers::Scene& scene, const char* platformIdentifier);
// Several file produced by this compiler used to have their sub id automatically assigned by the AP. This was causing problems with keeping
// the sub id stable and the sub id was changed to be provided by this compiler. However these new sub ids differ from the original sub id
// so to be compatible with legacy sub ids, the previously automatically created sub id is calculated for all files that used to
// have them. This has to be limited to only the products that would have previously had an automated sub id assigned as some of
// the automatically generated sub ids were file order depended.
virtual bool IsPreSubIdFile(const AZStd::string& file) const;
virtual void ResolvePreSubIds(AssetBuilderSDK::ProcessJobResponse& response, const AZStd::map<AZStd::string, size_t>& preSubIdFiles) const;
virtual u32 BuildSubId(const SceneAPI::Events::ExportProduct& product) const;
virtual AZStd::unique_ptr<AssetBuilderSDK::ProcessJobRequest> ReadJobRequest(const char* cacheFolder) const;
virtual bool WriteResponse(const char* cacheFolder, AssetBuilderSDK::ProcessJobResponse& response, AssetBuilderSDK::ProcessJobResultCode jobResult) const;
ConvertContext m_context;
AZStd::shared_ptr<ISceneConfig> m_config;
AZStd::string m_appRoot;
private:
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,67 @@
/*
* 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 "ResourceCompilerScene_precompiled.h"
#include <IConfig.h>
#include <SceneConfig.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/FbxSceneBuilder/FbxSceneSystem.h>
namespace AZ
{
namespace RC
{
SceneConfig::SceneConfig()
{
LoadSceneLibrary("SceneCore");
LoadSceneLibrary("FbxSceneBuilder"); // Still needs to be explicitly loaded in order to be able to get the supported file extensions.
}
SceneConfig::~SceneConfig()
{
// Explicitly uninitialize all modules. Because we loaded them twice, we need to explicitly uninit them.
for (AZStd::unique_ptr<AZ::DynamicModuleHandle>& module : m_modules)
{
auto uninit = module->GetFunction<UninitializeDynamicModuleFunction>(UninitializeDynamicModuleFunctionName);
if (uninit)
{
(*uninit)();
}
}
}
void SceneConfig::LoadSceneLibrary(const char* name)
{
AZStd::unique_ptr<DynamicModuleHandle> module = AZ::DynamicModuleHandle::Create(name);
AZ_Assert(module, "Failed to initialize library '%s'", name);
if (!module)
{
return;
}
module->Load(false);
// Explicitly initialize all modules. Because we're loading them twice (link time, and now-time), we need to explicitly uninit them.
auto init = module->GetFunction<InitializeDynamicModuleFunction>(InitializeDynamicModuleFunctionName);
if (init)
{
(*init)(AZ::Environment::GetInstance());
}
m_modules.push_back(AZStd::move(module));
}
size_t SceneConfig::GetErrorCount() const
{
return traceHook.GetErrorCount();
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,42 @@
#pragma once
/*
* 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 <ISceneConfig.h>
#include <TraceDrillerHook.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Module/DynamicModuleHandle.h>
namespace AZ
{
namespace RC
{
class SceneConfig
: public ISceneConfig
{
public:
SceneConfig();
~SceneConfig() override;
size_t GetErrorCount() const override;
protected:
virtual void LoadSceneLibrary(const char* name);
AZStd::vector<AZStd::unique_ptr<AZ::DynamicModuleHandle>> m_modules;
TraceDrillerHook traceHook;
};
} // RC
} // AZ
@@ -0,0 +1,65 @@
/*
* 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 "ResourceCompilerScene_precompiled.h"
#include <SceneConverter.h>
#include <SceneCompiler.h>
#include <ISceneConfig.h>
#include <AzCore/std/iterator.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
namespace AZ
{
namespace RC
{
SceneConverter::SceneConverter(const AZStd::shared_ptr<ISceneConfig>& config)
: m_config(config)
{
AZStd::unordered_set<AZStd::string> extensions;
EBUS_EVENT(SceneAPI::Events::AssetImportRequestBus, GetSupportedFileExtensions, extensions);
m_extensions.reserve(extensions.size() + 1);
AZStd::move(extensions.begin(), extensions.end(), AZStd::back_inserter(m_extensions));
AZStd::string manifestExtension;
EBUS_EVENT(SceneAPI::Events::AssetImportRequestBus, GetManifestExtension, manifestExtension);
m_extensions.push_back(AZStd::move(manifestExtension));
}
void SceneConverter::Release()
{
delete this;
}
void SceneConverter::Init(const ConvertorInitContext& context)
{
m_appRoot = context.appRootPath;
}
ICompiler* SceneConverter::CreateCompiler()
{
return new SceneCompiler(m_config, m_appRoot.c_str());
}
const char* SceneConverter::GetExt(int index) const
{
if (index < m_extensions.size())
{
const char* result = m_extensions[index].c_str();
return result[0] == '.' ? result + 1 : result;
}
else
{
return nullptr;
}
}
}
}
@@ -0,0 +1,43 @@
#pragma once
/*
* 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/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <IConvertor.h>
namespace AZ
{
namespace RC
{
class ISceneConfig;
class SceneConverter
: public IConvertor
{
public:
explicit SceneConverter(const AZStd::shared_ptr<ISceneConfig>& config);
void Release() override;
void Init(const ConvertorInitContext& context) override;
ICompiler* CreateCompiler() override;
const char* GetExt(int index) const override;
private:
AZStd::shared_ptr<ISceneConfig> m_config;
AZStd::vector<AZStd::string> m_extensions;
AZStd::string m_appRoot;
};
}
}
@@ -0,0 +1,95 @@
/*
* 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 "ResourceCompilerScene_precompiled.h"
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/string/conversions.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneSerializationHandler.h>
namespace AZ
{
namespace RC
{
void SceneSerializationHandler::Activate()
{
BusConnect();
}
void SceneSerializationHandler::Deactivate()
{
BusDisconnect();
}
void SceneSerializationHandler::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<SceneSerializationHandler>()->Version(1);
}
}
AZStd::shared_ptr<SceneAPI::Containers::Scene> SceneSerializationHandler::LoadScene(
const AZStd::string& filePath, Uuid sceneSourceGuid)
{
namespace Utilities = AZ::SceneAPI::Utilities;
using AZ::SceneAPI::Events::AssetImportRequest;
AZ_TraceContext("File", filePath);
if (sceneSourceGuid.IsNull())
{
AZ_TracePrintf(Utilities::ErrorWindow, "Invalid source guid for the scene file.");
return nullptr;
}
if (AZ::SceneAPI::Events::AssetImportRequest::IsManifestExtension(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "Provided path contains the manifest path, not the path to the source file.");
return nullptr;
}
if (!AZ::SceneAPI::Events::AssetImportRequest::IsSceneFileExtension(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "Provided path doesn't contain an extension supported by the SceneAPI.");
return nullptr;
}
if (AzFramework::StringFunc::Path::IsRelative(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "Given file path is relative where an absolute path was expected.");
return nullptr;
}
if (!AZ::IO::SystemFile::Exists(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "No file exists at given source path.");
return nullptr;
}
AZStd::shared_ptr<SceneAPI::Containers::Scene> scene =
AssetImportRequest::LoadSceneFromVerifiedPath(filePath, sceneSourceGuid, AssetImportRequest::RequestingApplication::AssetProcessor);
if (!scene)
{
AZ_TracePrintf(Utilities::ErrorWindow, "Failed to load the requested scene.");
return nullptr;
}
return scene;
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,43 @@
/*
* 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/std/containers/unordered_set.h>
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Events/SceneSerializationBus.h>
#include <SceneAPI/SceneCore/Components/SceneSystemComponent.h>
namespace AZ
{
namespace RC
{
class SceneSerializationHandler
: public SceneAPI::SceneCore::SceneSystemComponent
, public SceneAPI::Events::SceneSerializationBus::Handler
{
public:
AZ_COMPONENT(SceneSerializationHandler, "{944BB08A-FECB-4029-8E7C-810C801357B2}", SceneAPI::SceneCore::SceneSystemComponent);
SceneSerializationHandler() = default;
~SceneSerializationHandler() override = default;
void Activate() override;
void Deactivate() override;
static void Reflect(ReflectContext* context);
AZStd::shared_ptr<SceneAPI::Containers::Scene> LoadScene(
const AZStd::string& sceneFilePath, Uuid sceneSourceGuid) override;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,48 @@
/*
* 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 <RC/ResourceCompilerScene/Skin/SkinExportContexts.h>
namespace AZ
{
namespace RC
{
SkinGroupExportContext::SkinGroupExportContext(SceneAPI::Events::ExportEventContext& parent,
const SceneAPI::DataTypes::ISkinGroup& group, Phase phase)
: m_products(parent.GetProductList())
, m_scene(parent.GetScene())
, m_outputDirectory(parent.GetOutputDirectory())
, m_group(group)
, m_phase(phase)
{
}
SkinGroupExportContext::SkinGroupExportContext(SceneAPI::Events::ExportProductList& products, const SceneAPI::Containers::Scene& scene,
const AZStd::string& outputDirectory, const SceneAPI::DataTypes::ISkinGroup& group, Phase phase)
: m_products(products)
, m_scene(scene)
, m_outputDirectory(outputDirectory)
, m_group(group)
, m_phase(phase)
{
}
SkinGroupExportContext::SkinGroupExportContext(const SkinGroupExportContext& copyContext, Phase phase)
: m_products(copyContext.m_products)
, m_scene(copyContext.m_scene)
, m_outputDirectory(copyContext.m_outputDirectory)
, m_group(copyContext.m_group)
, m_phase(phase)
{
}
}
}
@@ -0,0 +1,57 @@
#pragma once
/*
* 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/RTTI/RTTI.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <RC/ResourceCompilerScene/Common/ExportContextGlobal.h>
struct CSkinningInfo;
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class ISkinGroup;
}
}
namespace RC
{
// Called to export a specific Skin Group
struct SkinGroupExportContext
: public SceneAPI::Events::ICallContext
{
AZ_RTTI(SkinGroupExportContext, "{F2C0DF6D-84F7-4692-9626-C981FA599755}", SceneAPI::Events::ICallContext);
SkinGroupExportContext(SceneAPI::Events::ExportEventContext& parent,
const SceneAPI::DataTypes::ISkinGroup& group, Phase phase);
SkinGroupExportContext(SceneAPI::Events::ExportProductList& products, const SceneAPI::Containers::Scene& scene,
const AZStd::string& outputDirectory, const SceneAPI::DataTypes::ISkinGroup& group, Phase phase);
SkinGroupExportContext(const SkinGroupExportContext& copyContent, Phase phase);
SkinGroupExportContext(const SkinGroupExportContext& copyContent) = delete;
~SkinGroupExportContext() override = default;
SkinGroupExportContext& operator=(const SkinGroupExportContext& other) = delete;
SceneAPI::Events::ExportProductList& m_products;
const SceneAPI::Containers::Scene& m_scene;
const AZStd::string& m_outputDirectory;
const SceneAPI::DataTypes::ISkinGroup& m_group;
const Phase m_phase;
};
}
}
@@ -0,0 +1,59 @@
/*
* 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 <RC/ResourceCompilerScene/Skin/SkinExporter.h>
#include <Cry_Geo.h>
#include <ConvertContext.h>
#include <CGFContent.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/ISkinGroup.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <RC/ResourceCompilerScene/Skin/SkinExportContexts.h>
namespace AZ
{
namespace RC
{
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
SkinExporter::SkinExporter(IConvertContext* convertContext)
: m_convertContext(convertContext)
{
BindToCall(&SkinExporter::ProcessContext);
ActivateBindings();
}
SceneEvents::ProcessingResult SkinExporter::ProcessContext(SceneEvents::ExportEventContext& context)
{
const SceneContainers::SceneManifest& manifest = context.GetScene().GetManifest();
auto valueStorage = manifest.GetValueStorage();
auto view = SceneContainers::MakeDerivedFilterView<SceneDataTypes::ISkinGroup>(valueStorage);
SceneEvents::ProcessingResultCombiner result;
for (const SceneDataTypes::ISkinGroup& skinGroup : view)
{
AZ_TraceContext("Skin Group", skinGroup.GetName());
result += SceneEvents::Process<SkinGroupExportContext>(context, skinGroup, Phase::Construction);
result += SceneEvents::Process<SkinGroupExportContext>(context, skinGroup, Phase::Filling);
result += SceneEvents::Process<SkinGroupExportContext>(context, skinGroup, Phase::Finalizing);
}
return result.GetResult();
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,49 @@
/*
* 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 <SceneAPI/SceneCore/Events/CallProcessorBinder.h>
namespace AZ
{
namespace SceneAPI
{
namespace Events
{
class ExportEventContext;
}
}
}
struct IConvertContext;
namespace AZ
{
namespace RC
{
namespace SceneEvents = AZ::SceneAPI::Events;
class SkinExporter
: public SceneEvents::CallProcessorBinder
{
public:
SkinExporter(IConvertContext* convertContext);
~SkinExporter() override = default;
SceneEvents::ProcessingResult ProcessContext(SceneEvents::ExportEventContext& context);
private:
IConvertContext* m_convertContext;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,93 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzCore/std/containers/map.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Skin/SkinExportContexts.h>
#include <RC/ResourceCompilerScene/Skin/SkinGroupExporter.h>
#include <RC/ResourceCompilerScene/Skin/SkinUtils.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/DataTypes/ManifestBase/ISceneNodeSelectionList.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkinGroup.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ
{
namespace RC
{
namespace SceneUtil = AZ::SceneAPI::Utilities;
namespace SceneContainer = AZ::SceneAPI::Containers;
const AZStd::string SkinGroupExporter::s_fileExtension = "skin";
SkinGroupExporter::SkinGroupExporter(IAssetWriter* writer, IConvertContext* convertContext)
: m_assetWriter(writer)
, m_convertContext(convertContext)
{
BindToCall(&SkinGroupExporter::ProcessContext);
ActivateBindings();
}
SceneEvents::ProcessingResult SkinGroupExporter::ProcessContext(SkinGroupExportContext& context) const
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZStd::string filename = SceneUtil::FileUtilities::CreateOutputFileName(context.m_group.GetName(), context.m_outputDirectory, s_fileExtension);
AZ_TraceContext("Skin filename", filename);
if (filename.empty() || !SceneUtil::FileUtilities::EnsureTargetFolderExists(filename))
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Invalid file name for skin");
return SceneEvents::ProcessingResult::Failure;
}
SceneEvents::ProcessingResultCombiner result;
CContentCGF cgfContent(filename.c_str());
ConfigureSkinContent(cgfContent);
// Process mesh
// For each selected mesh, find its skinned skeleton's root bone. Make sure the root bone is consistent through all selected skin meshes.
const SceneContainer::SceneGraph& graph = context.m_scene.GetGraph();
AZStd::vector<AZStd::string> targetNodes = SceneUtil::SceneGraphSelector::GenerateTargetNodes(graph,
context.m_group.GetSceneNodeSelectionList(), SceneUtil::SceneGraphSelector::IsMesh);
result += ProcessSkins(context, cgfContent, targetNodes);
if (m_assetWriter)
{
if (m_assetWriter->WriteSKIN(&cgfContent, m_convertContext, true))
{
static const AZ::Data::AssetType skinnedMeshAssetType("{C5D443E1-41FF-4263-8654-9438BC888CB7}"); // from MeshAsset.h
context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), skinnedMeshAssetType, 0, AZStd::nullopt);
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Writing Skin has failed.");
result += SceneEvents::ProcessingResult::Failure;
}
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "No asset writer found. Unable to write skin to disk");
result += SceneEvents::ProcessingResult::Failure;
}
return result.GetResult();
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,44 @@
/*
* 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/std/string/string.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBinder.h>
struct IConvertContext;
namespace AZ
{
namespace RC
{
struct SkinGroupExportContext;
namespace SceneEvents = AZ::SceneAPI::Events;
class SkinGroupExporter
: public SceneEvents::CallProcessorBinder
{
public:
SkinGroupExporter(IAssetWriter* writer, IConvertContext* convertContext);
~SkinGroupExporter() override = default;
SceneEvents::ProcessingResult ProcessContext(SkinGroupExportContext& context) const;
static const AZStd::string s_fileExtension;
protected:
IAssetWriter* m_assetWriter;
IConvertContext* m_convertContext;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,117 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/string/conversions.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfUtils.h>
#include <RC/ResourceCompilerScene/Skin/SkinExportContexts.h>
#include <RC/ResourceCompilerScene/Skin/SkinLodExporter.h>
#include <RC/ResourceCompilerScene/Skin/SkinUtils.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/ManifestBase/ISceneNodeSelectionList.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkinGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/ILodRule.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ
{
namespace RC
{
namespace SceneUtil = AZ::SceneAPI::Utilities;
namespace SceneContainer = AZ::SceneAPI::Containers;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
const AZStd::string SkinLodExporter::s_fileExtension = "skin";
SkinLodExporter::SkinLodExporter(IAssetWriter* writer, IConvertContext* convertContext)
: m_assetWriter(writer)
, m_convertContext(convertContext)
{
BindToCall(&SkinLodExporter::ProcessContext);
ActivateBindings();
}
SceneEvents::ProcessingResult SkinLodExporter::ProcessContext(SkinGroupExportContext& context) const
{
if (context.m_phase != Phase::Filling)
{
return SceneEvents::ProcessingResult::Ignored;
}
AZStd::shared_ptr<const SceneDataTypes::ILodRule> lodRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneDataTypes::ILodRule>();
if (!lodRule)
{
return SceneEvents::ProcessingResult::Ignored;
}
SceneEvents::ProcessingResultCombiner result;
for (size_t index = 0; index < lodRule->GetLodCount(); ++index)
{
AZ_TraceContext("Skin lod level", static_cast<uint64_t>(index));
AZStd::string filename = SceneUtil::FileUtilities::CreateOutputFileName(
context.m_group.GetName() + "_LOD" + AZStd::to_string(static_cast<int>(index + 1)), context.m_outputDirectory, s_fileExtension);
AZ_TraceContext("Skin lod filename", filename);
if (filename.empty() || !SceneUtil::FileUtilities::EnsureTargetFolderExists(filename))
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Invalid file name for skin");
result += SceneEvents::ProcessingResult::Failure;
break;
}
CContentCGF cgfContent(filename.c_str());
ConfigureSkinContent(cgfContent);
// Process mesh
// For each selected mesh, find its skinned skeleton's root bone. Make sure the root bone is consistent through all selected skin meshes.
const SceneContainer::SceneGraph& graph = context.m_scene.GetGraph();
AZStd::vector<AZStd::string> targetNodes = SceneUtil::SceneGraphSelector::GenerateTargetNodes(graph,
lodRule->GetSceneNodeSelectionList(index), SceneUtil::SceneGraphSelector::IsMesh);
result += ProcessSkins(context, cgfContent, targetNodes);
if (m_assetWriter)
{
if (m_assetWriter->WriteSKIN(&cgfContent, m_convertContext, false))
{
static const AZ::Data::AssetType skinnedMeshLodsAssetType("{58E5824F-C27B-46FD-AD48-865BA41B7A51}");
// Using the same guid as the parent group/cgf as this needs to be a lod of that cgf.
// Setting the lod to index+1 as 0 means the base mesh and 1-6 are lod levels 0-5.
context.m_products.AddProduct(AZStd::move(filename), context.m_group.GetId(), skinnedMeshLodsAssetType, index + 1, AZStd::nullopt);
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Writing Skin has failed.");
result += SceneEvents::ProcessingResult::Failure;
break;
}
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "No asset writer found. Unable to write skin to disk");
result += SceneEvents::ProcessingResult::Failure;
break;
}
}
return result.GetResult();
}
} // namespace RC
} // namespace AZ
@@ -0,0 +1,45 @@
/*
* 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/std/string/string.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBinder.h>
struct IConvertContext;
class CContentCGF;
namespace AZ
{
namespace RC
{
struct SkinGroupExportContext;
namespace SceneEvents = AZ::SceneAPI::Events;
class SkinLodExporter
: public SceneEvents::CallProcessorBinder
{
public:
SkinLodExporter(IAssetWriter* writer, IConvertContext* convertContext);
~SkinLodExporter() override = default;
SceneEvents::ProcessingResult ProcessContext(SkinGroupExportContext& context) const;
static const AZStd::string s_fileExtension;
protected:
IAssetWriter* m_assetWriter;
IConvertContext* m_convertContext;
};
} // namespace RC
} // namespace AZ
@@ -0,0 +1,194 @@
/*
* 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 <Cry_Geo.h> // Needed for CGFContent.h
#include <CGFContent.h>
#include <AzCore/std/containers/map.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Skin/SkinExportContexts.h>
#include <RC/ResourceCompilerScene/Skin/SkinGroupExporter.h>
#include <RC/ResourceCompilerScene/Cgf/CgfUtils.h>
#include <RC/ResourceCompilerScene/Skin/SkinUtils.h>
#include <RC/ResourceCompilerScene/Skin/SkinExportContexts.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkinGroup.h>
#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ
{
namespace RC
{
void ConfigureSkinContent(CContentCGF& content)
{
CExportInfoCGF* exportInfo = content.GetExportInfo();
exportInfo->bMergeAllNodes = true;
exportInfo->bUseCustomNormals = false;
exportInfo->bCompiledCGF = false;
exportInfo->bHavePhysicsProxy = false;
exportInfo->bHaveAutoLods = false;
exportInfo->bNoMesh = true;
exportInfo->b8WeightsPerVertex = false;
exportInfo->bWantF32Vertices = false;
exportInfo->authorToolVersion = 1;
}
void MergeToFirstNodeMesh(CContentCGF& content)
{
AZ_Assert(content.GetNodeCount() > 0, "Skin Mesh has no node to merge");
if (content.GetNodeCount() == 0)
{
return;
}
CMesh* mergedMesh = content.GetNode(0)->pMesh;
AZ_Assert(mergedMesh, "Failed to retrieve merged mesh for content root node");
if (!mergedMesh)
{
return;
}
for (size_t nodeIndex = 0; nodeIndex < content.GetNodeCount(); ++nodeIndex)
{
CNodeCGF* node = content.GetNode(nodeIndex);
AZ_Assert(node, "Failed to retrieve node at index %i", nodeIndex);
if (!node)
{
continue;
}
if (!node->bIdentityMatrix)
{
AZ_Assert(node->pMesh, "No mesh node set on CGF node at index %i", nodeIndex);
if (!node->pMesh)
{
continue;
}
for (size_t vertexIndex = 0; vertexIndex < node->pMesh->GetVertexCount(); ++vertexIndex)
{
node->pMesh->m_pPositions[vertexIndex] = node->worldTM.TransformPoint(node->pMesh->m_pPositions[vertexIndex]);
node->pMesh->m_pNorms[vertexIndex].RotateSafelyBy(node->worldTM);
}
}
if (nodeIndex > 0)
{
mergedMesh->Append(*node->pMesh);
// Keep color stream in sync size with vertex/normals stream. Reference to CGFNodeMerger::MergeNodes
if (mergedMesh->m_streamSize[CMesh::COLORS][0] > 0 && mergedMesh->m_streamSize[CMesh::COLORS][0] < mergedMesh->GetVertexCount())
{
int colorCount = mergedMesh->m_streamSize[CMesh::COLORS][0];
mergedMesh->ReallocStream(CMesh::COLORS, 0, mergedMesh->GetVertexCount());
memset(mergedMesh->m_pColor0 + colorCount, 255, (mergedMesh->GetVertexCount() - colorCount) * sizeof(SMeshColor));
}
}
}
// We already used the transform during merge, so clear it out
content.GetNode(0)->worldTM.SetIdentity();
}
void RemoveRedundantNodes(CContentCGF& content)
{
while (content.GetNodeCount() > 1)
{
CNodeCGF* deleteNode = content.GetNode(content.GetNodeCount() - 1);
content.RemoveNode(deleteNode);
}
}
SceneAPI::Events::ProcessingResult ProcessSkins(SkinGroupExportContext& context, CContentCGF& content, AZStd::vector<AZStd::string>& targetNodes)
{
namespace SceneEvents = SceneAPI::Events;
if (targetNodes.empty())
{
AZ_TracePrintf(SceneAPI::Utilities::WarningWindow, "No nodes selected for mesh exporting.");
return SceneEvents::ProcessingResult::Ignored;
}
SceneEvents::ProcessingResultCombiner result;
ContainerExportContext containerContext(context.m_scene, context.m_outputDirectory, context.m_group, content, Phase::Construction);
result += SceneEvents::Process(containerContext);
result += SceneEvents::Process<ContainerExportContext>(containerContext, Phase::Filling);
const EPhysicsGeomType physicalizeType = PHYS_GEOM_TYPE_NONE;
AZStd::string rootBoneName;
const SceneAPI::Containers::SceneGraph& graph = context.m_scene.GetGraph();
AZStd::string currentRootBoneName;
for (const AZStd::string& nodeName : targetNodes)
{
AZ_TraceContext("Skin mesh", nodeName);
SceneAPI::Containers::SceneGraph::NodeIndex index = graph.Find(nodeName.c_str());
if (index.IsValid())
{
// Pick the target skeleton from the first node, then make sure all the remaining meshes are referencing the same skeleton
// as the skins need to merged to a single mesh at the end.
SceneEvents::ProcessingResult rootNameResult = SceneEvents::Process<ResolveRootBoneFromNodeContext>(currentRootBoneName, context.m_scene, index);
if (rootNameResult != SceneEvents::ProcessingResult::Success || currentRootBoneName.empty())
{
AZ_TracePrintf(SceneAPI::Utilities::WarningWindow, "Selected skin has no weight data.");
continue;
}
if (rootBoneName.empty())
{
rootBoneName = currentRootBoneName;
// The skeleton has been established so fill up the skinning information for it as there's still
// a strong link between skin and skeleton.
SceneEvents::ProcessingResult skinInfoResult = SceneEvents::Process<AddBonesToSkinningInfoContext>(
*content.GetSkinningInfo(), context.m_scene, rootBoneName);
if (skinInfoResult != SceneEvents::ProcessingResult::Success)
{
// Without the skinning info further processing will cause a crash so early out here.
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Unable to link bones to skin.");
return SceneEvents::ProcessingResult::Failure;
}
}
else if (rootBoneName != currentRootBoneName)
{
AZ_TracePrintf(SceneAPI::Utilities::WarningWindow, "Skin doesn't belong to the same skeleton as the rest of the meshes in the group.");
continue;
}
CNodeCGF* node = new CNodeCGF(); // will be auto deleted by CContentCGF cgf
SetNodeName(nodeName, *node);
result += SceneAPI::Events::Process<NodeExportContext>(containerContext, *node, nodeName, index, physicalizeType, rootBoneName, Phase::Construction);
result += SceneAPI::Events::Process<NodeExportContext>(containerContext, *node, nodeName, index, physicalizeType, rootBoneName, Phase::Filling);
content.AddNode(node);
result += SceneAPI::Events::Process<NodeExportContext>(containerContext, *node, nodeName, index, physicalizeType, rootBoneName, Phase::Finalizing);
currentRootBoneName.clear();
}
}
if (content.GetNodeCount() > 0)
{
// CharacterCompiler expects all skin sub-meshes to be merged and stored in a single CNodeCGF
MergeToFirstNodeMesh(content);
result += SceneAPI::Events::Process<ContainerExportContext>(containerContext, Phase::Finalizing);
RemoveRedundantNodes(content);
}
else
{
AZ_TracePrintf(SceneAPI::Utilities::WarningWindow, "No valid skin information found that could be added to this container.");
result += SceneAPI::Events::Process<ContainerExportContext>(containerContext, Phase::Finalizing);
}
return result.GetResult();
}
}
}
@@ -0,0 +1,31 @@
/*
* 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/std/string/string.h>
#include <SceneAPI/SceneCore/Events/ProcessingResult.h>
class CContentCGF;
struct CNodeCGF;
namespace AZ
{
namespace RC
{
struct SkinGroupExportContext;
void ConfigureSkinContent(CContentCGF& content);
void MergeToFirstNodeMesh(CContentCGF& content);
void RemoveRedundantNodes(CContentCGF& content);
AZ::SceneAPI::Events::ProcessingResult ProcessSkins(SkinGroupExportContext& context, CContentCGF& content, AZStd::vector<AZStd::string>& targetNodes);
} // namespace RC
} // namespace AZ
@@ -0,0 +1,132 @@
/*
* 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 <RC/ResourceCompilerScene/Tests/Cgf/CgfExportContextTestBase.h>
#include <RC/ResourceCompilerScene/Common/ColorStreamExporter.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/GraphData/MockIMeshData.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/GraphData/MockIMeshVertexColorData.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/ManifestBase/MockISceneNodeSelectionList.h>
namespace AZ
{
namespace RC
{
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::_;
class ColorStreamExporterContextTestBase
: public CgfExporterContextTestBase
{
public:
ColorStreamExporterContextTestBase()
: m_stubMeshData(new SceneAPI::DataTypes::MockIMeshData())
, m_stubMeshVertexColorData(new SceneAPI::DataTypes::MockIMeshVertexColorData())
, m_sampleColor({1.f, 0.f, 0.f, 1.f})
{
}
~ColorStreamExporterContextTestBase() override = default;
protected:
// Minimal data subset:
// - Graph contains a single MeshData node
// - MeshData node has a single MeshDataVertexColor child
void SetUp() override
{
CgfExporterContextTestBase::SetUp();
SceneAPI::Containers::SceneGraph& graph = m_stubScene.GetGraph();
SceneAPI::Containers::SceneGraph::NodeIndex rootIndex = graph.GetRoot();
SceneAPI::Containers::SceneGraph::NodeIndex meshIndex = graph.AddChild(rootIndex, "sampleMeshData", m_stubMeshData);
UpdateNodeIndex(meshIndex);
graph.AddChild(m_sampleNodeIndex, "sampleMeshVertexColorData", m_stubMeshVertexColorData);
m_outMesh.SetVertexCount(3);
EXPECT_CALL(*m_stubMeshData, GetVertexCount())
.WillRepeatedly(Return(3));
EXPECT_CALL(*m_stubMeshVertexColorData, GetCount())
.WillRepeatedly(Return(3));
EXPECT_CALL(*m_stubMeshVertexColorData, GetColor(_))
.WillRepeatedly(ReturnRef(m_sampleColor));
}
bool TestCausedNoChanges()
{
CMesh emptyMesh;
emptyMesh.SetVertexCount(3);
return emptyMesh.CompareStreams(m_outMesh);
}
AZStd::shared_ptr<SceneAPI::DataTypes::MockIMeshData> m_stubMeshData;
AZStd::shared_ptr<SceneAPI::DataTypes::MockIMeshVertexColorData> m_stubMeshVertexColorData;
ColorStreamExporter m_testExporter;
AZ::SceneAPI::DataTypes::Color m_sampleColor;
};
class ColorStreamExporterNoOpTests
: public ColorStreamExporterContextTestBase
{
public:
~ColorStreamExporterNoOpTests() override = default;
};
TEST_P(ColorStreamExporterNoOpTests, Process_UnsupportedContext_MeshRemainsEmpty)
{
m_testExporter.Process(m_stubContext);
EXPECT_TRUE(TestCausedNoChanges());
}
static const ContextPhaseTuple g_CgfColorStreamExporterTestsunsupportedContextPhaseTuples[] =
{
{ TestContextMeshGroup, Phase::Construction },
{ TestContextMeshGroup, Phase::Filling },
{ TestContextMeshGroup, Phase::Finalizing },
{ TestContextContainer, Phase::Construction },
{ TestContextContainer, Phase::Filling },
{ TestContextContainer, Phase::Finalizing },
{ TestContextNode, Phase::Construction },
{ TestContextNode, Phase::Filling },
{ TestContextNode, Phase::Finalizing },
{ TestContextMeshNode, Phase::Construction },
{ TestContextMeshNode, Phase::Finalizing }
};
INSTANTIATE_TEST_CASE_P(ColorStreamExporter,
ColorStreamExporterNoOpTests,
::testing::ValuesIn(g_CgfColorStreamExporterTestsunsupportedContextPhaseTuples));
class ColorStreamExporterSimpleTests
: public ColorStreamExporterContextTestBase
{
public:
~ColorStreamExporterSimpleTests() override = default;
};
// Need a new way to test as ColorStreamExporter is update to derive from CallProcessorBinder
//TEST_P(ColorStreamExporterSimpleTests, Process_SupportedContext_MeshIsNotEmpty)
//{
// m_testExporter.Process(m_stubContext);
// EXPECT_TRUE(!TestCausedNoChanges());
//}
static const ContextPhaseTuple g_CgfColorStreamExporterTestsSupportedContextPhaseTuples[] =
{
{ TestContextMeshNode, Phase::Filling }
};
INSTANTIATE_TEST_CASE_P(ColorStreamExporter,
ColorStreamExporterSimpleTests,
::testing::ValuesIn(g_CgfColorStreamExporterTestsSupportedContextPhaseTuples));
} // namespace RC
} // namespace AZ
@@ -0,0 +1,121 @@
/*
* 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 <RC/ResourceCompilerScene/Tests/Cgf/CgfExportContextTestBase.h>
#include <RC/ResourceCompilerScene/Common/ContainerSettingsExporter.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/ManifestBase/MockISceneNodeSelectionList.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/Rules/MockIMeshAdvancedRule.h>
namespace AZ
{
namespace RC
{
using ::testing::Const;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::_;
class ContainerSettingsExporterContextTestBase
: public CgfExporterContextTestBase
{
public:
ContainerSettingsExporterContextTestBase()
: m_stubMeshAdvancedRule(new SceneAPI::DataTypes::MockIMeshAdvancedRule())
{
m_ruleContainer.AddRule(m_stubMeshAdvancedRule);
}
~ContainerSettingsExporterContextTestBase() override = default;
protected:
// Minimal subset for check
// - Group has advanced rule
// - Advanced rule defines 32 bit vertex precision to be true
void SetUp() override
{
CgfExporterContextTestBase::SetUp();
EXPECT_CALL(*m_stubMeshAdvancedRule, Use32bitVertices())
.WillRepeatedly(Return(true));
EXPECT_CALL(*m_stubMeshAdvancedRule, MergeMeshes())
.WillRepeatedly(Return(false));
ON_CALL(m_stubMeshGroup, GetRuleContainer())
.WillByDefault(ReturnRef(m_ruleContainer));
ON_CALL(Const(m_stubMeshGroup), GetRuleContainerConst())
.WillByDefault(ReturnRef(m_ruleContainer));
}
bool TestDataChanged()
{
return m_outContent.GetExportInfo()->bWantF32Vertices;
}
AZStd::shared_ptr<SceneAPI::DataTypes::MockIMeshAdvancedRule> m_stubMeshAdvancedRule;
SceneAPI::Containers::RuleContainer m_ruleContainer;
ContainerSettingsExporter m_testExporter;
};
class ContainerSettingsExporterNoOpTests
: public ContainerSettingsExporterContextTestBase
{
public:
~ContainerSettingsExporterNoOpTests() override = default;
};
TEST_P(ContainerSettingsExporterNoOpTests, Process_UnsupportedContext_ExportInfoNotChanged)
{
m_testExporter.Process(m_stubContext);
EXPECT_FALSE(TestDataChanged());
}
static const ContextPhaseTuple g_CgfContainerSettingsExporterTestsUnsupportedContextPhaseTuples[] =
{
{ TestContextMeshGroup, Phase::Construction },
{ TestContextMeshGroup, Phase::Filling },
{ TestContextMeshGroup, Phase::Finalizing },
{ TestContextContainer, Phase::Filling },
{ TestContextContainer, Phase::Finalizing },
{ TestContextNode, Phase::Construction },
{ TestContextNode, Phase::Filling },
{ TestContextNode, Phase::Finalizing },
{ TestContextMeshNode, Phase::Construction },
{ TestContextMeshNode, Phase::Filling },
{ TestContextMeshNode, Phase::Finalizing }
};
INSTANTIATE_TEST_CASE_P(ContainerSettingsExporter,
ContainerSettingsExporterNoOpTests,
::testing::ValuesIn(g_CgfContainerSettingsExporterTestsUnsupportedContextPhaseTuples));
class ContainerSettingsExporterSimpleTests
: public ContainerSettingsExporterContextTestBase
{
public:
~ContainerSettingsExporterSimpleTests() override = default;
};
TEST_P(ContainerSettingsExporterSimpleTests, Process_SupportedContext_ExportInfoChanged)
{
m_testExporter.Process(m_stubContext);
EXPECT_TRUE(TestDataChanged());
}
static const ContextPhaseTuple g_CgfContainerSettingsExporterTestsSupportedContextPhaseTuples[] =
{
{TestContextContainer, Phase::Construction}
};
INSTANTIATE_TEST_CASE_P(ContainerSettingsExporter,
ContainerSettingsExporterSimpleTests,
::testing::ValuesIn(g_CgfContainerSettingsExporterTestsSupportedContextPhaseTuples));
} // namespace RC
} // namespace AZ
@@ -0,0 +1,120 @@
/*
* 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 <Cry_Geo.h>
#include <Mocks/MockCGFContent.h>
#include <RC/ResourceCompilerScene/Common/CommonExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/Groups/MockIMeshGroup.h>
#include <gmock/gmock.h>
#pragma once
namespace AZ
{
namespace RC
{
using ::testing::StrictMock;
namespace SceneEvents = SceneAPI::Events;
enum TestContextType
{
TestContextMeshGroup,
TestContextContainer,
TestContextNode,
TestContextMeshNode,
TestContextCount
};
typedef AZStd::pair<TestContextType, Phase> ContextPhaseTuple;
class CgfExporterContextTestBase
: public ::testing::TestWithParam<ContextPhaseTuple>
{
public:
CgfExporterContextTestBase()
: m_stubScene(m_sampleSceneName)
, m_outContent(m_sampleOutputDirectory.c_str())
, m_sampleNodeIndex(m_stubScene.GetGraph().Find("InvalidNodeName"))
, m_stubContext(nullptr)
, m_stubMeshGroupExportContext(m_productList, m_stubScene, m_sampleOutputDirectory, m_stubMeshGroup, GetParam().second)
, m_stubContainerExportContext(m_stubScene, m_sampleOutputDirectory, m_stubMeshGroup, m_outContent, GetParam().second)
, m_stubNodeExportContext(m_stubContainerExportContext, m_outNode, m_sampleNodeName, m_sampleNodeIndex, m_samplePhysGeomType, m_sampleRootBoneName, GetParam().second)
, m_stubMeshNodeExportContext(m_stubNodeExportContext, m_outMesh, GetParam().second)
{
m_outContent.GetExportInfo()->bWantF32Vertices = false;
}
~CgfExporterContextTestBase() override = default;
protected:
void SetUp() override
{
switch(GetParam().first)
{
case TestContextMeshGroup:
m_stubContext = &m_stubMeshGroupExportContext;
break;
case TestContextContainer:
m_stubContext = &m_stubContainerExportContext;
break;
case TestContextNode:
m_stubContext = &m_stubNodeExportContext;
break;
case TestContextMeshNode:
m_stubContext = &m_stubMeshNodeExportContext;
break;
default:
m_stubContext = nullptr;
break;
}
}
void TearDown() override
{
}
void UpdateNodeIndex(SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex)
{
m_sampleNodeIndex = nodeIndex;
m_stubNodeExportContext.m_nodeIndex = nodeIndex;
m_stubMeshNodeExportContext.m_nodeIndex = nodeIndex;
}
// Sample Context Data Payloads
AZ::SceneAPI::Events::ExportProductList m_productList;
AZStd::string m_sampleSceneName = "SampleScene";
SceneAPI::Containers::Scene m_stubScene;
AZStd::string m_sampleOutputDirectory = "TEST:\\Sample\\Output";
AZStd::string m_sampleGroupName = "SampleGroupName";
SceneAPI::DataTypes::MockIMeshGroup m_stubMeshGroup;
CContentCGF m_outContent;
CNodeCGF m_outNode;
AZStd::string m_sampleNodeName = "SampleNodeName";
// Note that m_sampleNodeIndex will always be invalid and fetched using a non existent node
// from the graph. This is not important for the tests, just needs to be present as a parameter
SceneAPI::Containers::SceneGraph::NodeIndex m_sampleNodeIndex;
EPhysicsGeomType m_samplePhysGeomType = PHYS_GEOM_TYPE_NONE;
AZStd::string m_sampleRootBoneName;
CMesh m_outMesh;
// Sample Context Types
SceneEvents::ICallContext* m_stubContext;
CgfGroupExportContext m_stubMeshGroupExportContext;
ContainerExportContext m_stubContainerExportContext;
NodeExportContext m_stubNodeExportContext;
MeshNodeExportContext m_stubMeshNodeExportContext;
};
}
}
@@ -0,0 +1,135 @@
/*
* 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 <Cry_Geo.h> // Needed for Cry_Matrix34.h
#include <IIndexedMesh.h>
#include <CGFContent.h>
#include <ConvertContext.h>
#include <RC/ResourceCompilerScene/Tests/Cgf/CgfExportContextTestBase.h>
#include <RC/ResourceCompilerScene/Common/MaterialExporter.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/ManifestBase/MockISceneNodeSelectionList.h>
namespace AZ
{
namespace RC
{
class MaterialExporterContextTestBase
: public CgfExporterContextTestBase
{
public:
MaterialExporterContextTestBase()
: m_cacheGenerationContext(m_productList, m_stubScene, m_sampleOutputDirectory, m_stubMeshGroup, Phase::Construction)
, m_testExporter()
{
}
~MaterialExporterContextTestBase() override = default;
protected:
static const size_t s_testFaceCount = 3;
static const unsigned char s_testDefaultSubset = 0;
void SetUp() override
{
CgfExporterContextTestBase::SetUp();
m_outContent.SetCommonMaterial(nullptr);
m_outNode.pMaterial = nullptr;
// We require a MeshGroupContext in the Construction phase for correct behavior of
// all other contexts
m_testExporter.Process(&m_cacheGenerationContext);
}
virtual bool TestChangedData()
{
if (m_outContent.GetCommonMaterial() != nullptr)
{
return true;
}
if (m_outNode.pMaterial != nullptr)
{
return true;
}
return false;
}
ConvertContext m_convertContext;
CgfGroupExportContext m_cacheGenerationContext;
MaterialExporter m_testExporter;
};
class MaterialExporterNoOpTests
: public MaterialExporterContextTestBase
{
public:
~MaterialExporterNoOpTests() override = default;
protected:
// To
void SetUp() override
{
MaterialExporterContextTestBase::SetUp();
}
};
TEST_P(MaterialExporterNoOpTests, Process_UnsupportedContext_DataNotChanged)
{
m_testExporter.Process(m_stubContext);
EXPECT_FALSE(TestChangedData());
}
static const ContextPhaseTuple g_CgfMaterialExporterTestsUnsupportedContextPhaseTuples[] =
{
{ TestContextMeshGroup, Phase::Filling },
{ TestContextMeshGroup, Phase::Finalizing }, // Technically changes, but only internal state
{ TestContextContainer, Phase::Filling },
{ TestContextNode, Phase::Construction },
{ TestContextNode, Phase::Finalizing },
{ TestContextMeshNode, Phase::Construction },
{ TestContextMeshNode, Phase::Finalizing }
};
INSTANTIATE_TEST_CASE_P(MaterialExporter,
MaterialExporterNoOpTests,
::testing::ValuesIn(g_CgfMaterialExporterTestsUnsupportedContextPhaseTuples));
class MaterialExporterContainerContextTests
: public MaterialExporterContextTestBase
{
public:
~MaterialExporterContainerContextTests() override = default;
protected:
// To
void SetUp() override
{
MaterialExporterContextTestBase::SetUp();
}
};
// Tests still required
// ContainerContext/Finalizing - Should be trivial
// NodeContext/Filling - Will require complex setup of internal cache
// MeshNodeContext/Filling - Will require complex setup of internal cache
static const ContextPhaseTuple g_CgfMaterialExporterTestsSupportedContextPhaseTuples[] =
{
{ TestContextContainer, Phase::Construction }
};
INSTANTIATE_TEST_CASE_P(MaterialExporter,
MaterialExporterContainerContextTests,
::testing::ValuesIn(g_CgfMaterialExporterTestsSupportedContextPhaseTuples));
} // namespace RC
} // namespace AZ
@@ -0,0 +1,121 @@
/*
* 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 <RC/ResourceCompilerScene/Tests/Cgf/CgfExportContextTestBase.h>
#include <RC/ResourceCompilerScene/Common/MeshExporter.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/ManifestBase/MockISceneNodeSelectionList.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/GraphData/MockIMeshData.h>
namespace AZ
{
namespace RC
{
using ::testing::Return;
class MeshExporterContextTestBase
: public CgfExporterContextTestBase
{
public:
MeshExporterContextTestBase()
: m_stubMeshData(new SceneAPI::DataTypes::MockIMeshData())
{
}
~MeshExporterContextTestBase() override = default;
protected:
// Minimal subset for a valid Processing pass
void SetUp() override
{
CgfExporterContextTestBase::SetUp();
SceneAPI::Containers::SceneGraph& graph = m_stubScene.GetGraph();
SceneAPI::Containers::SceneGraph::NodeIndex rootIndex = graph.GetRoot();
SceneAPI::Containers::SceneGraph::NodeIndex meshIndex = graph.AddChild(rootIndex, "sampleMeshData", m_stubMeshData);
UpdateNodeIndex(meshIndex);
m_outMesh.SetVertexCount(3);
m_outContent.GetExportInfo()->bNoMesh = true;
}
bool TestChangedData()
{
return !m_outContent.GetExportInfo()->bNoMesh;
}
AZStd::shared_ptr<SceneAPI::DataTypes::MockIMeshData> m_stubMeshData;
MeshExporter m_testExporter;
};
class MeshExporterNoOpTests
: public MeshExporterContextTestBase
{
public:
~MeshExporterNoOpTests() override = default;
};
TEST_P(MeshExporterNoOpTests, Process_UnsupportedContext_ContentNotChanged)
{
m_testExporter.Process(m_stubContext);
EXPECT_FALSE(TestChangedData());
}
static const ContextPhaseTuple g_CgfMeshExporterTestsUnsupportedContextPhaseTuples[] =
{
{ TestContextMeshGroup, Phase::Construction },
{ TestContextMeshGroup, Phase::Filling },
{ TestContextMeshGroup, Phase::Finalizing },
{ TestContextContainer, Phase::Construction },
{ TestContextContainer, Phase::Filling },
{ TestContextContainer, Phase::Finalizing },
{ TestContextNode, Phase::Construction },
{ TestContextNode, Phase::Finalizing },
{ TestContextMeshNode, Phase::Construction },
{ TestContextMeshNode, Phase::Filling },
{ TestContextMeshNode, Phase::Finalizing }
};
INSTANTIATE_TEST_CASE_P(MeshExporter,
MeshExporterNoOpTests,
::testing::ValuesIn(g_CgfMeshExporterTestsUnsupportedContextPhaseTuples));
class MeshExporterSimpleTests
: public MeshExporterContextTestBase
{
public:
~MeshExporterSimpleTests() override = default;
};
TEST_P(MeshExporterSimpleTests, Process_SupportedContext_ContentChanged)
{
EXPECT_CALL(*m_stubMeshData, GetVertexCount())
.WillRepeatedly(Return(0));
EXPECT_CALL(*m_stubMeshData, GetFaceCount())
.WillRepeatedly(Return(0));
EXPECT_CALL(*m_stubMeshData, HasNormalData())
.WillRepeatedly(Return(false));
m_testExporter.Process(&m_stubNodeExportContext);
EXPECT_TRUE(TestChangedData());
}
static const ContextPhaseTuple g_CgfMeshExporterTestsSupportedContextPhaseTuples[] =
{
{TestContextNode, Phase::Filling}
};
INSTANTIATE_TEST_CASE_P(MeshExporter,
MeshExporterSimpleTests,
::testing::ValuesIn(g_CgfMeshExporterTestsSupportedContextPhaseTuples));
} // namespace RC
} // namespace AZ
@@ -0,0 +1,113 @@
/*
* 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 <RC/ResourceCompilerScene/Tests/Cgf/CgfExportContextTestBase.h>
#include <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
#include <RC/ResourceCompilerScene/Cgf/CgfGroupExporter.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/ManifestBase/MockISceneNodeSelectionList.h>
namespace AZ
{
namespace RC
{
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::Const;
class CgfGroupExporterContextTestBase
: public CgfExporterContextTestBase
{
public:
CgfGroupExporterContextTestBase()
: m_testExporter(&m_mockAssetWriter)
{
}
~CgfGroupExporterContextTestBase() override = default;
protected:
StrictMock<MockIAssetWriter> m_mockAssetWriter;
CgfGroupExporter m_testExporter;
};
class CgfGroupExporterNoOpTests
: public CgfGroupExporterContextTestBase
{
public:
~CgfGroupExporterNoOpTests() override = default;
};
TEST_P(CgfGroupExporterNoOpTests, Process_UnsupportedContext_WriterNotUsed)
{
m_testExporter.Process(m_stubContext);
}
static const ContextPhaseTuple g_CgfMeshGroupExporterTestsUnsupportedContextPhaseTuples[] =
{
{ TestContextMeshGroup, Phase::Construction },
{ TestContextMeshGroup, Phase::Finalizing },
{ TestContextContainer, Phase::Construction },
{ TestContextContainer, Phase::Filling },
{ TestContextContainer, Phase::Finalizing },
{ TestContextNode, Phase::Construction },
{ TestContextNode, Phase::Filling },
{ TestContextNode, Phase::Finalizing },
{ TestContextMeshNode, Phase::Construction },
{ TestContextMeshNode, Phase::Filling },
{ TestContextMeshNode, Phase::Finalizing }
};
INSTANTIATE_TEST_CASE_P(MeshGroupExporter,
CgfGroupExporterNoOpTests,
::testing::ValuesIn(g_CgfMeshGroupExporterTestsUnsupportedContextPhaseTuples));
class CgfGroupExporterSimpleTestFramework
: public CgfGroupExporterContextTestBase
{
public:
~CgfGroupExporterSimpleTestFramework() override = default;
protected:
void SetUp() override
{
}
void TearDown() override
{
}
SceneAPI::DataTypes::MockISceneNodeSelectionList m_stubSceneNodeSelectionList;
};
TEST_P(CgfGroupExporterSimpleTestFramework, Process_SupportedContextNoNodesSelected_WriterNotUsed)
{
AZStd::string testGroupName = "testName";
EXPECT_CALL(m_stubSceneNodeSelectionList, GetSelectedNodeCount())
.WillRepeatedly(Return(0));
EXPECT_CALL(m_stubSceneNodeSelectionList, GetUnselectedNodeCount())
.WillRepeatedly(Return(0));
EXPECT_CALL(Const(m_stubMeshGroup), GetSceneNodeSelectionList())
.WillRepeatedly(ReturnRef(m_stubSceneNodeSelectionList));
EXPECT_CALL(m_stubMeshGroup, GetName())
.WillRepeatedly(ReturnRef(testGroupName));
m_testExporter.Process(&m_stubMeshGroupExportContext);
}
static const ContextPhaseTuple g_CgfMeshGroupExporterTestsSupportedContextPhaseTuples[] =
{
{ TestContextMeshGroup, Phase::Filling }
};
INSTANTIATE_TEST_CASE_P(MeshGroupExporter,
CgfGroupExporterSimpleTestFramework,
::testing::ValuesIn(g_CgfMeshGroupExporterTestsSupportedContextPhaseTuples));
} // namespace RC
} // namespace AZ
@@ -0,0 +1,137 @@
/*
* 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 <RC/ResourceCompilerScene/Tests/Cgf/CgfExportContextTestBase.h>
#include <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
#include <RC/ResourceCompilerScene/Common/UVStreamExporter.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/GraphData/MockIMeshData.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/GraphData/MockIMeshVertexUVData.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/ManifestBase/MockISceneNodeSelectionList.h>
namespace AZ
{
namespace RC
{
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::_;
class UVStreamExporterContextTestBase
: public CgfExporterContextTestBase
{
public:
UVStreamExporterContextTestBase()
: m_stubMeshData(new SceneAPI::DataTypes::MockIMeshData())
, m_stubMeshVertexUVData(new SceneAPI::DataTypes::MockIMeshVertexUVData())
{
}
~UVStreamExporterContextTestBase() override = default;
protected:
// Minimal data subset:
// - Graph contains a single MeshData node
// - MeshData node has a single MeshDataVertexColor child
void SetUp() override
{
CgfExporterContextTestBase::SetUp();
SceneAPI::Containers::SceneGraph& graph = m_stubScene.GetGraph();
SceneAPI::Containers::SceneGraph::NodeIndex rootIndex = graph.GetRoot();
SceneAPI::Containers::SceneGraph::NodeIndex meshIndex = graph.AddChild(rootIndex, "sampleMeshData", m_stubMeshData);
UpdateNodeIndex(meshIndex);
graph.AddChild(m_sampleNodeIndex, "sampleMeshVertexColorData", m_stubMeshVertexUVData);
m_outMesh.SetVertexCount(3);
m_uvs.push_back(AZ::Vector2(0.f, 0.f));
m_uvs.push_back(AZ::Vector2(1.f, 0.f));
m_uvs.push_back(AZ::Vector2(1.f, 1.f));
EXPECT_CALL(*m_stubMeshData, GetVertexCount())
.WillRepeatedly(Return(3));
EXPECT_CALL(*m_stubMeshVertexUVData, GetCount())
.WillRepeatedly(Return(3));
EXPECT_CALL(*m_stubMeshVertexUVData, GetUV(0))
.WillRepeatedly(ReturnRef(m_uvs[0]));
EXPECT_CALL(*m_stubMeshVertexUVData, GetUV(1))
.WillRepeatedly(ReturnRef(m_uvs[1]));
EXPECT_CALL(*m_stubMeshVertexUVData, GetUV(2))
.WillRepeatedly(ReturnRef(m_uvs[2]));
}
bool TestCausedNoChanges()
{
CMesh emptyMesh;
emptyMesh.SetVertexCount(3);
return emptyMesh.CompareStreams(m_outMesh);
}
AZStd::shared_ptr<SceneAPI::DataTypes::MockIMeshData> m_stubMeshData;
AZStd::shared_ptr<SceneAPI::DataTypes::MockIMeshVertexUVData> m_stubMeshVertexUVData;
UVStreamExporter m_testExporter;
AZStd::vector<AZ::Vector2> m_uvs;
};
class UVStreamExporterNoOpTests
: public UVStreamExporterContextTestBase
{
public:
~UVStreamExporterNoOpTests() override = default;
};
TEST_P(UVStreamExporterNoOpTests, Process_UnsupportedContext_OutDataNotChanged)
{
m_testExporter.Process(m_stubContext);
EXPECT_TRUE(TestCausedNoChanges());
}
static const ContextPhaseTuple g_CgfUVStreamExporterTestsUnsupportedContextPhaseTuples[] =
{
{ TestContextMeshGroup, Phase::Construction },
{ TestContextMeshGroup, Phase::Filling },
{ TestContextMeshGroup, Phase::Finalizing },
{ TestContextContainer, Phase::Construction },
{ TestContextContainer, Phase::Filling },
{ TestContextContainer, Phase::Finalizing },
{ TestContextNode, Phase::Construction },
{ TestContextNode, Phase::Filling },
{ TestContextNode, Phase::Finalizing },
{ TestContextMeshNode, Phase::Construction },
{ TestContextMeshNode, Phase::Finalizing }
};
INSTANTIATE_TEST_CASE_P(UVStreamExporter,
UVStreamExporterNoOpTests,
::testing::ValuesIn(g_CgfUVStreamExporterTestsUnsupportedContextPhaseTuples));
class UVStreamExporterSimpleTests
: public UVStreamExporterContextTestBase
{
public:
~UVStreamExporterSimpleTests() override = default;
};
TEST_P(UVStreamExporterSimpleTests, Process_SupportedContext_OutDataChanged)
{
m_testExporter.Process(&m_stubMeshNodeExportContext);
EXPECT_FALSE(TestCausedNoChanges());
}
static const ContextPhaseTuple g_CgfUVStreamExporterTestsSupportedContextPhaseTuples[] = {
{TestContextMeshNode, Phase::Filling}
};
INSTANTIATE_TEST_CASE_P(UVStreamExporter,
UVStreamExporterSimpleTests,
::testing::ValuesIn(g_CgfUVStreamExporterTestsSupportedContextPhaseTuples));
} // namespace RC
} // namespace AZ
@@ -0,0 +1,143 @@
/*
* 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 <Cry_Geo.h> // Needed for Cry_Matrix34.h
#include <Cry_Matrix34.h>
#include <RC/ResourceCompilerScene/Tests/Cgf/CgfExportContextTestBase.h>
#include <RC/ResourceCompilerScene/Cgf/CgfExportContexts.h>
#include <RC/ResourceCompilerScene/Common/WorldMatrixExporter.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/GraphData/MockITransform.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/GraphData/MockIMeshData.h>
#include <SceneAPI/SceneCore/Mocks/DataTypes/ManifestBase/MockISceneNodeSelectionList.h>
namespace AZ
{
namespace RC
{
using ::testing::Const;
using ::testing::Return;
using ::testing::ReturnRef;
class WorldMatrixExporterContextTestBase
: public CgfExporterContextTestBase
{
public:
WorldMatrixExporterContextTestBase()
: m_stubTransformData(new SceneAPI::DataTypes::MockITransform())
, m_stubMeshData(new SceneAPI::DataTypes::MockIMeshData())
, m_stubTransform(AZ::SceneAPI::DataTypes::MatrixType::CreateTranslation(AZ::Vector3(0.f, 0.f, 1.f)))
{
}
~WorldMatrixExporterContextTestBase() override = default;
protected:
void SetUp()
{
CgfExporterContextTestBase::SetUp();
m_outNode.bIdentityMatrix = true;
SceneAPI::Containers::SceneGraph& sceneGraph = m_stubScene.GetGraph();
SceneAPI::Containers::SceneGraph::NodeIndex rootIndex = sceneGraph.GetRoot();
SceneAPI::Containers::SceneGraph::NodeIndex transformIndex = sceneGraph.AddChild(rootIndex, "SampleTransformData", m_stubTransformData);
SceneAPI::Containers::SceneGraph::NodeIndex meshIndex = sceneGraph.AddChild(transformIndex, "SampleMeshData", m_stubMeshData);
UpdateNodeIndex(meshIndex);
EXPECT_CALL(Const(*m_stubTransformData), GetMatrix())
.WillRepeatedly(ReturnRef(m_stubTransform));
ON_CALL(m_stubMeshGroup, GetRuleContainer())
.WillByDefault(ReturnRef(m_ruleContainer));
ON_CALL(Const(m_stubMeshGroup), GetRuleContainerConst())
.WillByDefault(ReturnRef(m_ruleContainer));
}
bool TestChangedData()
{
return !m_outNode.bIdentityMatrix;
}
AZStd::shared_ptr<SceneAPI::DataTypes::MockITransform> m_stubTransformData;
AZStd::shared_ptr<SceneAPI::DataTypes::MockIMeshData> m_stubMeshData;
AZ::SceneAPI::DataTypes::MatrixType m_stubTransform;
WorldMatrixExporter m_testExporter;
SceneAPI::Containers::RuleContainer m_ruleContainer;
};
class WorldMatrixExporterNoOpTestFramework
: public WorldMatrixExporterContextTestBase
{
public:
~WorldMatrixExporterNoOpTestFramework() override = default;
};
TEST_P(WorldMatrixExporterNoOpTestFramework, Process_UnsupportedContext_OutNodeAtIndentity)
{
m_testExporter.Process(m_stubContext);
EXPECT_FALSE(TestChangedData());
}
ContextPhaseTuple g_CgfWorldMatrixExporterTestsUnsupportedContextPhaseTuples[] =
{
{ TestContextMeshGroup, Phase::Filling },
{ TestContextMeshGroup, Phase::Finalizing },
{ TestContextContainer, Phase::Construction },
{ TestContextContainer, Phase::Filling },
{ TestContextContainer, Phase::Finalizing },
{ TestContextNode, Phase::Construction },
{ TestContextNode, Phase::Finalizing },
{ TestContextMeshNode, Phase::Construction },
{ TestContextMeshNode, Phase::Filling },
{ TestContextMeshNode, Phase::Finalizing }
};
INSTANTIATE_TEST_CASE_P(WorldMatrixExporter,
WorldMatrixExporterNoOpTestFramework,
::testing::ValuesIn(g_CgfWorldMatrixExporterTestsUnsupportedContextPhaseTuples));
class WorldMatrixExporterSimpleTests
: public WorldMatrixExporterContextTestBase
{
public:
WorldMatrixExporterSimpleTests()
: m_cacheGenerationContext(m_productList, m_stubScene, m_sampleOutputDirectory, m_stubMeshGroup, Phase::Construction)
{
}
~WorldMatrixExporterSimpleTests() override = default;
protected:
void SetUp() override
{
WorldMatrixExporterContextTestBase::SetUp();
m_testExporter.Process(&m_cacheGenerationContext);
}
CgfGroupExportContext m_cacheGenerationContext;
};
// Disable the test due to an assert that checking consistency of cached mesh group which lacks a way to support currently
//TEST_P(WorldMatrixExporterSimpleTests, Process_SupportedContext_OutNodeNotAtIndentity)
//{
// m_testExporter.Process(m_stubContext);
// EXPECT_TRUE(TestChangedData());
//}
ContextPhaseTuple g_CgfWorldMatrixExporterTestsSupportedContextPhaseTuples[] =
{
{ TestContextNode, Phase::Filling }
};
INSTANTIATE_TEST_CASE_P(WorldMatrixExporter,
WorldMatrixExporterSimpleTests,
::testing::ValuesIn(g_CgfWorldMatrixExporterTestsSupportedContextPhaseTuples));
} // namespace RC
} // namespace AZ
@@ -0,0 +1,88 @@
/*
* 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 <AzTest/AzTest.h>
#include <AzCore/Module/Environment.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Module/DynamicModuleHandle.h>
class ResourceCompilerSceneTestEnvironment
: public AZ::Test::ITestEnvironment
{
public:
virtual ~ResourceCompilerSceneTestEnvironment()
{}
protected:
void SetupEnvironment() override
{
if (!AZ::AllocatorInstance<AZ::SystemAllocator>().IsReady())
{
AZ::AllocatorInstance<AZ::SystemAllocator>().Create();
m_hasLocalMemoryAllocator = true;
}
{
sceneCoreModule = AZ::DynamicModuleHandle::Create("SceneCore");
AZ_Assert(sceneCoreModule, "ResourceCompilerScene unit tests failed to create SceneCore module.");
bool loaded = sceneCoreModule->Load(false);
AZ_Assert(loaded, "ResourceCompilerScene unit tests failed to load SceneCore module.");
auto init = sceneCoreModule->GetFunction<AZ::InitializeDynamicModuleFunction>(AZ::InitializeDynamicModuleFunctionName);
AZ_Assert(init, "ResourceCompilerScene unit tests failed to find the initialization function the SceneCore module.");
(*init)(AZ::Environment::GetInstance());
}
{
sceneDataModule = AZ::DynamicModuleHandle::Create("SceneData");
AZ_Assert(sceneDataModule, "ResourceCompilerScene unit tests failed to create SceneData module.");
bool loaded = sceneDataModule->Load(false);
AZ_Assert(loaded, "ResourceCompilerScene unit tests failed to load SceneData module.");
auto init = sceneDataModule->GetFunction<AZ::InitializeDynamicModuleFunction>(AZ::InitializeDynamicModuleFunctionName);
AZ_Assert(init, "ResourceCompilerScene unit tests failed to find the initialization function the SceneData module.");
(*init)(AZ::Environment::GetInstance());
}
{
fbxSceneBuilderModule = AZ::DynamicModuleHandle::Create("FbxSceneBuilder");
AZ_Assert(fbxSceneBuilderModule, "ResourceCompilerScene unit tests failed to create FbxSceneBuilder module.");
bool loaded = fbxSceneBuilderModule->Load(false);
AZ_Assert(loaded, "ResourceCompilerScene unit tests failed to load FbxSceneBuilder module.");
}
}
void TeardownEnvironment() override
{
fbxSceneBuilderModule.reset();
auto uninit = sceneDataModule->GetFunction<AZ::UninitializeDynamicModuleFunction>(AZ::UninitializeDynamicModuleFunctionName);
AZ_Assert(uninit, "FbxSceneBuilder unit tests failed to find the uninitialization function the SceneData module.");
(*uninit)();
sceneDataModule.reset();
uninit = sceneCoreModule->GetFunction<AZ::UninitializeDynamicModuleFunction>(AZ::UninitializeDynamicModuleFunctionName);
AZ_Assert(uninit, "FbxSceneBuilder unit tests failed to find the uninitialization function the SceneCore module.");
(*uninit)();
sceneCoreModule.reset();
if (m_hasLocalMemoryAllocator)
{
AZ::AllocatorInstance<AZ::SystemAllocator>().Destroy();
}
}
private:
bool m_hasLocalMemoryAllocator = false;
AZStd::unique_ptr<AZ::DynamicModuleHandle> sceneCoreModule;
AZStd::unique_ptr<AZ::DynamicModuleHandle> sceneDataModule;
AZStd::unique_ptr<AZ::DynamicModuleHandle> fbxSceneBuilderModule;
};
AZ_UNIT_TEST_HOOK(new ResourceCompilerSceneTestEnvironment);
@@ -0,0 +1,126 @@
/*
* 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 <AzToolsFramework/Debug/TraceContextLogFormatter.h>
#include <TraceDrillerHook.h>
namespace AZ
{
namespace RC
{
TraceDrillerHook::TraceDrillerHook()
: m_errorCount(0)
{
BusConnect();
}
TraceDrillerHook::~TraceDrillerHook()
{
BusDisconnect();
}
bool TraceDrillerHook::OnPreAssert(const char* fileName, int line, const char* func, const char* message)
{
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
DumpContextStack();
m_errorCount++;
RCLogError("%.*s", CalculateLineLength(message), message);
return true;
}
bool TraceDrillerHook::OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message)
{
AZ_UNUSED(window);
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
DumpContextStack();
m_errorCount++;
RCLogError("%.*s", CalculateLineLength(message), message);
return true;
}
bool TraceDrillerHook::OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message)
{
AZ_UNUSED(window);
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
DumpContextStack();
RCLogWarning("%.*s", CalculateLineLength(message), message);
return true;
}
bool TraceDrillerHook::OnPrintf(const char* window, const char* message)
{
DumpContextStack();
// "%.*s" specifier only supports int for its size
int messageLineLen = aznumeric_cast<int>(CalculateLineLength(message));
if (AzFramework::StringFunc::Equal(window, SceneAPI::Utilities::ErrorWindow))
{
m_errorCount++;
RCLogError("%.*s", messageLineLen, message);
}
else if (AzFramework::StringFunc::Equal(window, SceneAPI::Utilities::WarningWindow))
{
RCLogWarning("%.*s", messageLineLen, message);
}
else
{
RCLog("%.*s", messageLineLen, message);
}
return true;
}
size_t TraceDrillerHook::GetErrorCount() const
{
return m_errorCount;
}
void TraceDrillerHook::DumpContextStack() const
{
AZStd::shared_ptr<const AzToolsFramework::Debug::TraceContextStack> stack = m_stacks.GetCurrentStack();
if (stack)
{
AZStd::string line;
size_t stackSize = stack->GetStackCount();
for (size_t i = 0; i < stackSize; ++i)
{
line.clear();
if (stack->GetType(i) == AzToolsFramework::Debug::TraceContextStackInterface::ContentType::UuidType)
{
continue;
}
AzToolsFramework::Debug::TraceContextLogFormatter::PrintLine(line, *stack, i);
RCLogContext(line.c_str());
}
}
}
size_t TraceDrillerHook::CalculateLineLength(const char* message) const
{
size_t length = strlen(message);
while ((message[length - 1] == '\n' || message[length - 1] == '\r' ) && length > 1)
{
length--;
}
return length;
}
} // RC
} // AZ
@@ -0,0 +1,53 @@
#pragma once
/*
* 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 <cstdint>
#include <IRCLog.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Debug/TraceContextMultiStackHandler.h>
namespace AZ
{
namespace RC
{
class TraceDrillerHook
: public AZ::Debug::TraceMessageBus::Handler
{
public:
TraceDrillerHook();
~TraceDrillerHook() override;
// Support for legacy Cry system cause AzCoreLogSink to eat asserts and errors, so registering for these
// callbacks never fires them. To still receive messages the OnPre* calls are hooked into. This isn't needed
// for warning, but is still done for consistency.
bool OnPreAssert(const char* fileName, int line, const char* func, const char* message) override;
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override;
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override;
bool OnPrintf(const char* window, const char* message) override;
size_t GetErrorCount() const;
private:
void DumpContextStack() const;
size_t CalculateLineLength(const char* message) const;
AzToolsFramework::Debug::TraceContextMultiStackHandler m_stacks;
size_t m_errorCount;
};
} // RC
} // AZ
@@ -0,0 +1,76 @@
#
# 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.
#
set(FILES
ResourceCompilerScene_precompiled.h
TraceDrillerHook.h
TraceDrillerHook.cpp
ISceneConfig.h
SceneConfig.h
SceneConfig.cpp
SceneConverter.h
SceneConverter.cpp
SceneCompiler.h
SceneCompiler.cpp
SceneSerializationHandler.h
SceneSerializationHandler.cpp
Common/AssetExportUtilities.h
Common/AssetExportUtilities.cpp
Common/ExportContextGlobal.h
Common/CommonExportContexts.h
Common/CommonExportContexts.cpp
Common/ContainerSettingsExporter.h
Common/ContainerSettingsExporter.cpp
Common/MeshExporter.h
Common/MeshExporter.cpp
Common/MaterialExporter.h
Common/MaterialExporter.cpp
Common/WorldMatrixExporter.h
Common/WorldMatrixExporter.cpp
Common/ColorStreamExporter.h
Common/ColorStreamExporter.cpp
Common/UVStreamExporter.h
Common/UVStreamExporter.cpp
Common/SkeletonExporter.h
Common/SkeletonExporter.cpp
Common/SkinWeightExporter.h
Common/SkinWeightExporter.cpp
Common/BlendShapeExporter.h
Common/BlendShapeExporter.cpp
Common/TouchBendingExporter.h
Common/TouchBendingExporter.cpp
Cgf/CgfExportContexts.h
Cgf/CgfExportContexts.cpp
Cgf/CgfGroupExporter.h
Cgf/CgfGroupExporter.cpp
Cgf/CgfLodExporter.h
Cgf/CgfLodExporter.cpp
Cgf/CgfExporter.h
Cgf/CgfExporter.cpp
Cgf/CgfUtils.h
Cgf/CgfUtils.cpp
Chr/ChrExportContexts.h
Chr/ChrExportContexts.cpp
Chr/ChrGroupExporter.h
Chr/ChrGroupExporter.cpp
Chr/ChrExporter.h
Chr/ChrExporter.cpp
Skin/SkinExportContexts.h
Skin/SkinExportContexts.cpp
Skin/SkinGroupExporter.h
Skin/SkinGroupExporter.cpp
Skin/SkinLodExporter.h
Skin/SkinLodExporter.cpp
Skin/SkinExporter.h
Skin/SkinExporter.cpp
Skin/SkinUtils.h
Skin/SkinUtils.cpp
)
@@ -0,0 +1,14 @@
#
# 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.
#
set(FILES
ResourceCompilerScene.cpp
)
@@ -0,0 +1,23 @@
#
# 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.
#
set(FILES
Tests/TestsMain.cpp
Tests/Cgf/CgfExportContextTestBase.h
Tests/Cgf/CgfColorStreamExporterTests.cpp
Tests/Cgf/CgfContainerSettingsExporterTests.cpp
Tests/Cgf/CgfMaterialExporterTests.cpp
Tests/Cgf/CgfMeshExporterTests.cpp
Tests/Cgf/CgfMeshGroupExporterTests.cpp
Tests/Cgf/CgfUVStreamExporterTests.cpp
Tests/Cgf/CgfWorldMatrixExporterTests.cpp
ResourceCompilerScene.cpp
)