FBX -> Scene, part 2. Code changes, file renames (#1704)
* First pass FBX -> Scene File conversion. This is the simple pass, minimizing code changes and focused on comments. Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Step 1 of part 2 of the FBX -> Scene rename Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Renaming FbxSceneBuilder folder to SceneBuilder Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Renamed files Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * More FBX -> Scene Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
if (NOT PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
ly_add_target(
|
||||
NAME SceneBuilder.Static STATIC
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
scenebuilder_files.cmake
|
||||
Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
SCENE_BUILDER_EXPORTS
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
../..
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
PUBLIC
|
||||
AZ::AzToolsFramework
|
||||
AZ::SceneCore
|
||||
AZ::SceneData
|
||||
AZ::SDKWrapper
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME SceneBuilder MODULE
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
scenebuilder_shared_files.cmake
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
SCENE_BUILDER_EXPORTS
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
../..
|
||||
BUILD_DEPENDENCIES
|
||||
PUBLIC
|
||||
AZ::SceneBuilder.Static
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
)
|
||||
|
||||
ly_add_dependencies(AssetBuilder AZ::SceneBuilder)
|
||||
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_target(
|
||||
NAME SceneBuilder.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
scenebuilder_testing_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
AZ::SceneBuilder
|
||||
)
|
||||
ly_add_googletest(
|
||||
NAME AZ::SceneBuilder.Tests
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(AZ_MONOLITHIC_BUILD)
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneImportRequestHandler.h>
|
||||
|
||||
#include <SceneAPI/SceneBuilder/SceneImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpBitangentStreamImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpColorStreamImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpMaterialImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpMeshImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpTangentStreamImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpTransformImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpUvMapImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpSkinImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpSkinWeightsImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpBoneImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpAnimationImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpBlendShapeImporter.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
static AZStd::vector<AZ::ComponentDescriptor*> g_componentDescriptors;
|
||||
|
||||
void Reflect(AZ::SerializeContext* /*context*/)
|
||||
{
|
||||
// Descriptor registration is done in Reflect instead of Initialize because the ResourceCompilerScene initializes the libraries before
|
||||
// there's an application.
|
||||
using namespace AZ::SceneAPI;
|
||||
using namespace AZ::SceneAPI::SceneBuilder;
|
||||
|
||||
if (g_componentDescriptors.empty())
|
||||
{
|
||||
// Global importer and behavior
|
||||
g_componentDescriptors.push_back(SceneBuilder::SceneImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(SceneImportRequestHandler::CreateDescriptor());
|
||||
|
||||
// Node and attribute importers
|
||||
g_componentDescriptors.push_back(AssImpBitangentStreamImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpColorStreamImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpMaterialImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpMeshImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpTangentStreamImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpTransformImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpUvMapImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpSkinImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpSkinWeightsImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpBoneImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpAnimationImporter::CreateDescriptor());
|
||||
g_componentDescriptors.push_back(AssImpBlendShapeImporter::CreateDescriptor());
|
||||
|
||||
for (AZ::ComponentDescriptor* descriptor : g_componentDescriptors)
|
||||
{
|
||||
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Handler::RegisterComponentDescriptor, descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectBehavior([[maybe_unused]] AZ::BehaviorContext* context)
|
||||
{
|
||||
// stub in until LYN-1284 is done
|
||||
}
|
||||
|
||||
void Activate()
|
||||
{
|
||||
}
|
||||
|
||||
void Deactivate()
|
||||
{
|
||||
}
|
||||
|
||||
void Uninitialize()
|
||||
{
|
||||
if (!g_componentDescriptors.empty())
|
||||
{
|
||||
for (AZ::ComponentDescriptor* descriptor : g_componentDescriptors)
|
||||
{
|
||||
descriptor->ReleaseDescriptor();
|
||||
}
|
||||
g_componentDescriptors.clear();
|
||||
g_componentDescriptors.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
|
||||
extern "C" AZ_DLL_EXPORT void InitializeDynamicModule(void* env)
|
||||
{
|
||||
AZ::Environment::Attach(static_cast<AZ::EnvironmentInstance>(env));
|
||||
}
|
||||
extern "C" AZ_DLL_EXPORT void Reflect(AZ::SerializeContext* context)
|
||||
{
|
||||
AZ::SceneAPI::SceneBuilder::Reflect(context);
|
||||
}
|
||||
extern "C" AZ_DLL_EXPORT void ReflectBehavior(AZ::BehaviorContext* context)
|
||||
{
|
||||
AZ::SceneAPI::SceneBuilder::ReflectBehavior(context);
|
||||
}
|
||||
extern "C" AZ_DLL_EXPORT void UninitializeDynamicModule()
|
||||
{
|
||||
AZ::SceneAPI::SceneBuilder::Uninitialize();
|
||||
AZ::Environment::Detach();
|
||||
}
|
||||
|
||||
#endif // !defined(AZ_MONOLITHIC_BUILD)
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SceneCore/Events/ImportEventContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
AssImpImportContext::AssImpImportContext(const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& sourceNode)
|
||||
: m_sourceScene(sourceScene)
|
||||
, m_sourceSceneSystem(sourceSceneSystem)
|
||||
, m_sourceNode(sourceNode)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpNodeEncounteredContext::AssImpNodeEncounteredContext(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap,
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& sourceNode)
|
||||
: AssImpImportContext(sourceScene, sourceSceneSystem, sourceNode)
|
||||
, NodeEncounteredContext(scene, currentGraphPosition, nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpNodeEncounteredContext::AssImpNodeEncounteredContext(
|
||||
Events::ImportEventContext& parent,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap,
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& sourceNode)
|
||||
: AssImpImportContext(sourceScene, sourceSceneSystem, sourceNode)
|
||||
, NodeEncounteredContext(parent.GetScene(), currentGraphPosition, nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpSceneDataPopulatedContext::AssImpSceneDataPopulatedContext(AssImpNodeEncounteredContext& parent,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& graphData, const AZStd::string& dataName)
|
||||
: AssImpImportContext(parent.m_sourceScene, parent.m_sourceSceneSystem, parent.m_sourceNode)
|
||||
, SceneDataPopulatedContextBase(parent, graphData, dataName)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpSceneDataPopulatedContext::AssImpSceneDataPopulatedContext(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap,
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& sourceNode,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData, const AZStd::string& dataName)
|
||||
: AssImpImportContext(sourceScene, sourceSceneSystem, sourceNode)
|
||||
, SceneDataPopulatedContextBase(scene, currentGraphPosition, nodeNameMap, nodeData, dataName)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpSceneNodeAppendedContext::AssImpSceneNodeAppendedContext(AssImpSceneDataPopulatedContext& parent,
|
||||
Containers::SceneGraph::NodeIndex newIndex)
|
||||
: AssImpImportContext(parent.m_sourceScene, parent.m_sourceSceneSystem, parent.m_sourceNode)
|
||||
, SceneNodeAppendedContextBase(parent.m_scene, newIndex, parent.m_nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpSceneNodeAppendedContext::AssImpSceneNodeAppendedContext(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap, AssImpSDKWrapper::AssImpNodeWrapper& sourceNode)
|
||||
: AssImpImportContext(sourceScene, sourceSceneSystem, sourceNode)
|
||||
, SceneNodeAppendedContextBase(scene, currentGraphPosition, nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpSceneAttributeDataPopulatedContext::AssImpSceneAttributeDataPopulatedContext(AssImpSceneNodeAppendedContext& parent, const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData, const Containers::SceneGraph::NodeIndex attributeNodeIndex, const AZStd::string& dataName)
|
||||
: AssImpImportContext(parent.m_sourceScene, parent.m_sourceSceneSystem, parent.m_sourceNode)
|
||||
, SceneAttributeDataPopulatedContextBase(parent, nodeData, attributeNodeIndex, dataName)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpSceneAttributeNodeAppendedContext::AssImpSceneAttributeNodeAppendedContext(AssImpSceneAttributeDataPopulatedContext& parent, Containers::SceneGraph::NodeIndex newIndex)
|
||||
: AssImpImportContext(parent.m_sourceScene, parent.m_sourceSceneSystem, parent.m_sourceNode)
|
||||
, SceneAttributeNodeAppendedContextBase(parent, newIndex)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpSceneNodeAddedAttributesContext::AssImpSceneNodeAddedAttributesContext(AssImpSceneNodeAppendedContext& parent)
|
||||
: AssImpImportContext(parent.m_sourceScene, parent.m_sourceSceneSystem, parent.m_sourceNode)
|
||||
, SceneNodeAddedAttributesContextBase(parent)
|
||||
{
|
||||
}
|
||||
|
||||
AssImpSceneNodeFinalizeContext::AssImpSceneNodeFinalizeContext(AssImpSceneNodeAddedAttributesContext& parent)
|
||||
: AssImpImportContext(parent.m_sourceScene, parent.m_sourceSceneSystem, parent.m_sourceNode)
|
||||
, SceneNodeFinalizeContextBase(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
AssImpFinalizeSceneContext::AssImpFinalizeSceneContext(Containers::Scene& scene,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap)
|
||||
: FinalizeSceneContextBase(scene, nodeNameMap)
|
||||
, m_sourceScene(sourceScene)
|
||||
, m_sourceSceneSystem(sourceSceneSystem)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace SceneAPI
|
||||
} // namespace SceneBuilder
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/ImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace AssImpSDKWrapper
|
||||
{
|
||||
class AssImpNodeWrapper;
|
||||
class AssImpSceneWrapper;
|
||||
}
|
||||
|
||||
namespace SceneAPI
|
||||
{
|
||||
class SceneSystem;
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class RenamedNodesMap;
|
||||
|
||||
// AssImpImportContext
|
||||
// Base structure containing common data needed for all import contexts
|
||||
// Member Variables:
|
||||
// m_sourceNode - AssImp node being used for data processing.
|
||||
struct AssImpImportContext
|
||||
{
|
||||
AZ_RTTI(AssImpImportContext, "{B1076AFF-991B-423C-8D3E-D5C9230434AB}");
|
||||
|
||||
AssImpImportContext(const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& sourceNode);
|
||||
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& m_sourceScene;
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& m_sourceNode;
|
||||
const SceneSystem& m_sourceSceneSystem; // Needed for unit and axis conversion
|
||||
};
|
||||
|
||||
// AssImpNodeEncounteredContext
|
||||
// Context pushed to indicate that a new AssImp Node has been found and any
|
||||
// importers that have means to process the contained data should do so
|
||||
struct AssImpNodeEncounteredContext
|
||||
: public AssImpImportContext
|
||||
, public NodeEncounteredContext
|
||||
{
|
||||
AZ_RTTI(AssImpNodeEncounteredContext, "{C2305BC5-EAEC-4515-BAD6-45E63C3FBD3D}", AssImpImportContext, NodeEncounteredContext);
|
||||
|
||||
AssImpNodeEncounteredContext(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap,
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& sourceNode);
|
||||
|
||||
AssImpNodeEncounteredContext(Events::ImportEventContext& parent,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap,
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& sourceNode);
|
||||
};
|
||||
|
||||
// AssImpSceneDataPopulatedContext
|
||||
// Context pushed to indicate that a piece of scene data has been fully
|
||||
// processed and any importers that wish to place it within the scene graph
|
||||
// may now do so.
|
||||
struct AssImpSceneDataPopulatedContext
|
||||
: public AssImpImportContext
|
||||
, public SceneDataPopulatedContextBase
|
||||
{
|
||||
AZ_RTTI(AssImpSceneDataPopulatedContext, "{888DA37E-4234-4990-AD50-E6E54AFA9C35}", AssImpImportContext, SceneDataPopulatedContextBase);
|
||||
|
||||
AssImpSceneDataPopulatedContext(AssImpNodeEncounteredContext& parent,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData,
|
||||
const AZStd::string& dataName);
|
||||
|
||||
AssImpSceneDataPopulatedContext(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap,
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& sourceNode,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData,
|
||||
const AZStd::string& dataName);
|
||||
};
|
||||
|
||||
// AssImpSceneNodeAppendedContext
|
||||
// Context pushed to indicate that data has been added to the scene graph.
|
||||
// Generally created due to the insertion of a node during SceneDataPopulatedContext
|
||||
// processing.
|
||||
struct AssImpSceneNodeAppendedContext
|
||||
: public AssImpImportContext
|
||||
, public SceneNodeAppendedContextBase
|
||||
{
|
||||
AZ_RTTI(AssImpSceneNodeAppendedContext, "{9C8B688E-8ECD-4EF0-9AC6-21BBCFE8F5A3}", AssImpImportContext, SceneNodeAppendedContextBase);
|
||||
|
||||
AssImpSceneNodeAppendedContext(AssImpSceneDataPopulatedContext& parent, Containers::SceneGraph::NodeIndex newIndex);
|
||||
AssImpSceneNodeAppendedContext(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap,
|
||||
AssImpSDKWrapper::AssImpNodeWrapper& sourceNode);
|
||||
};
|
||||
|
||||
// AssImpSceneAttributeDataPopulatedContext
|
||||
// Context pushed to indicate that attribute data has been found and processed
|
||||
struct AssImpSceneAttributeDataPopulatedContext
|
||||
: public AssImpImportContext
|
||||
, public SceneAttributeDataPopulatedContextBase
|
||||
{
|
||||
AZ_RTTI(AssImpSceneAttributeDataPopulatedContext, "{A5EFB485-2F36-4214-972B-0EFF4EFBF33D}", AssImpImportContext, SceneAttributeDataPopulatedContextBase);
|
||||
|
||||
AssImpSceneAttributeDataPopulatedContext(AssImpSceneNodeAppendedContext& parent,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData,
|
||||
const Containers::SceneGraph::NodeIndex attributeNodeIndex,const AZStd::string& dataName);
|
||||
};
|
||||
|
||||
// AssImpSceneAttributeNodeAppendedContext
|
||||
// Context pushed to indicate that an attribute node has been added to the scene graph
|
||||
struct AssImpSceneAttributeNodeAppendedContext
|
||||
: public AssImpImportContext
|
||||
, public SceneAttributeNodeAppendedContextBase
|
||||
{
|
||||
AZ_RTTI(AssImpSceneAttributeNodeAppendedContext, "{96FDC405-2D3B-4030-A301-B3A2B5432498}", AssImpImportContext, SceneAttributeNodeAppendedContextBase);
|
||||
|
||||
AssImpSceneAttributeNodeAppendedContext(AssImpSceneAttributeDataPopulatedContext& parent, Containers::SceneGraph::NodeIndex newIndex);
|
||||
};
|
||||
|
||||
// AssImpSceneNodeAddedAttributesContext
|
||||
// Context pushed to indicate that all attribute processors have completed their
|
||||
// work for a specific data node.
|
||||
struct AssImpSceneNodeAddedAttributesContext
|
||||
: public AssImpImportContext
|
||||
, public SceneNodeAddedAttributesContextBase
|
||||
{
|
||||
AZ_RTTI(AssImpSceneNodeAddedAttributesContext, "{D305EAA5-5F16-4AAD-805D-DF07A1B355B9}", AssImpImportContext, SceneNodeAddedAttributesContextBase);
|
||||
|
||||
AssImpSceneNodeAddedAttributesContext(AssImpSceneNodeAppendedContext& parent);
|
||||
};
|
||||
|
||||
// AssImpSceneNodeFinalizeContext
|
||||
// Context pushed last after all other contexts for a scene node to allow any
|
||||
// post-processing needed for an importer.
|
||||
struct AssImpSceneNodeFinalizeContext
|
||||
: public AssImpImportContext
|
||||
, public SceneNodeFinalizeContextBase
|
||||
{
|
||||
AZ_RTTI(AssImpSceneNodeFinalizeContext, "{FD8B4AD5-3735-4D55-9455-504AB1DCA655}", AssImpImportContext, SceneNodeFinalizeContextBase);
|
||||
|
||||
AssImpSceneNodeFinalizeContext(AssImpSceneNodeAddedAttributesContext& parent);
|
||||
};
|
||||
|
||||
// AssImpFinalizeSceneContext
|
||||
// Context pushed after the scene has been fully created. This can be used to finalize pending work
|
||||
// such as resolving named links.
|
||||
struct AssImpFinalizeSceneContext
|
||||
: public FinalizeSceneContextBase
|
||||
{
|
||||
AZ_RTTI(AssImpFinalizeSceneContext, "{6B23A54A-44BF-4661-A130-6B4D06A57B9F}", FinalizeSceneContextBase);
|
||||
|
||||
AssImpFinalizeSceneContext(
|
||||
Containers::Scene& scene,
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& sourceScene,
|
||||
const SceneSystem& sourceSceneSystem,
|
||||
RenamedNodesMap& nodeNameMap);
|
||||
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper& m_sourceScene;
|
||||
const SceneSystem& m_sourceSceneSystem; // Needed for unit and axis conversion
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/ImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Events/ImportEventContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
ImportContext::ImportContext(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
RenamedNodesMap& nodeNameMap)
|
||||
: m_scene(scene)
|
||||
, m_currentGraphPosition(currentGraphPosition)
|
||||
, m_nodeNameMap(nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
ImportContext::ImportContext(Containers::Scene& scene, RenamedNodesMap& nodeNameMap)
|
||||
: m_scene(scene)
|
||||
, m_nodeNameMap(nodeNameMap)
|
||||
{
|
||||
m_currentGraphPosition = Containers::SceneGraph::NodeIndex();
|
||||
}
|
||||
|
||||
NodeEncounteredContext::NodeEncounteredContext(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
RenamedNodesMap& nodeNameMap)
|
||||
: ImportContext(scene, currentGraphPosition, nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
NodeEncounteredContext::NodeEncounteredContext(
|
||||
Events::ImportEventContext& parent, Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
RenamedNodesMap& nodeNameMap)
|
||||
: ImportContext(parent.GetScene(), currentGraphPosition, nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
SceneDataPopulatedContextBase::SceneDataPopulatedContextBase(NodeEncounteredContext& parent,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& graphData, const AZStd::string& dataName)
|
||||
: ImportContext(parent.m_scene, parent.m_currentGraphPosition, parent.m_nodeNameMap)
|
||||
, m_graphData(graphData)
|
||||
, m_dataName(dataName)
|
||||
{
|
||||
}
|
||||
|
||||
SceneDataPopulatedContextBase::SceneDataPopulatedContextBase(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
RenamedNodesMap& nodeNameMap,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData, const AZStd::string& dataName)
|
||||
: ImportContext(scene, currentGraphPosition, nodeNameMap)
|
||||
, m_graphData(nodeData)
|
||||
, m_dataName(dataName)
|
||||
{
|
||||
}
|
||||
|
||||
SceneNodeAppendedContextBase::SceneNodeAppendedContextBase(SceneDataPopulatedContextBase& parent,
|
||||
Containers::SceneGraph::NodeIndex newIndex)
|
||||
: ImportContext(parent.m_scene, newIndex, parent.m_nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
SceneNodeAppendedContextBase::SceneNodeAppendedContextBase(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition, RenamedNodesMap& nodeNameMap)
|
||||
: ImportContext(scene, currentGraphPosition, nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
SceneAttributeDataPopulatedContextBase::SceneAttributeDataPopulatedContextBase(SceneNodeAppendedContextBase& parent,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData,
|
||||
const Containers::SceneGraph::NodeIndex attributeNodeIndex, const AZStd::string& dataName)
|
||||
: ImportContext(parent.m_scene, attributeNodeIndex, parent.m_nodeNameMap)
|
||||
, m_graphData(nodeData)
|
||||
, m_dataName(dataName)
|
||||
{
|
||||
}
|
||||
|
||||
SceneAttributeNodeAppendedContextBase::SceneAttributeNodeAppendedContextBase(SceneAttributeDataPopulatedContextBase& parent, Containers::SceneGraph::NodeIndex newIndex)
|
||||
: ImportContext(parent.m_scene, newIndex, parent.m_nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
SceneNodeAddedAttributesContextBase::SceneNodeAddedAttributesContextBase(SceneNodeAppendedContextBase& parent)
|
||||
: ImportContext(parent.m_scene, parent.m_currentGraphPosition, parent.m_nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
SceneNodeFinalizeContextBase::SceneNodeFinalizeContextBase(SceneNodeAddedAttributesContextBase& parent)
|
||||
: ImportContext(parent.m_scene, parent.m_currentGraphPosition, parent.m_nodeNameMap)
|
||||
{
|
||||
}
|
||||
|
||||
FinalizeSceneContextBase::FinalizeSceneContextBase(Containers::Scene& scene, RenamedNodesMap& nodeNameMap)
|
||||
: ImportContext(scene, nodeNameMap)
|
||||
{
|
||||
}
|
||||
} // namespace SceneAPI
|
||||
} // namespace SceneBuilder
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
|
||||
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace Containers
|
||||
{
|
||||
class Scene;
|
||||
}
|
||||
|
||||
namespace DataTypes
|
||||
{
|
||||
class IGraphObject;
|
||||
}
|
||||
|
||||
namespace Events
|
||||
{
|
||||
class ImportEventContext;
|
||||
}
|
||||
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class RenamedNodesMap;
|
||||
|
||||
// ImportContext
|
||||
// Base structure containing common data needed for all import contexts
|
||||
struct ImportContext
|
||||
: public Events::ICallContext
|
||||
{
|
||||
AZ_RTTI(ImportContext, "{68E546D5-9B79-4293-AD37-4A4BA688892F}", Events::ICallContext);
|
||||
|
||||
ImportContext(Containers::Scene& scene, Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
RenamedNodesMap& nodeNameMap);
|
||||
ImportContext(Containers::Scene& scene, RenamedNodesMap& nodeNameMap);
|
||||
|
||||
Containers::Scene& m_scene;
|
||||
Containers::SceneGraph::NodeIndex m_currentGraphPosition;
|
||||
RenamedNodesMap& m_nodeNameMap; // Map of the nodes that have received a new name.
|
||||
};
|
||||
|
||||
// NodeEncounteredContext
|
||||
// Context pushed to indicate that a new Node has been found and any
|
||||
// importers that have means to process the contained data should do so
|
||||
// Member Variables:
|
||||
// m_createdData - out container that importers must add their created data
|
||||
// to.
|
||||
struct NodeEncounteredContext
|
||||
: public ImportContext
|
||||
{
|
||||
AZ_RTTI(NodeEncounteredContext, "{40C31D76-7101-4ACD-8849-0D6D0AF62855}", ImportContext);
|
||||
|
||||
NodeEncounteredContext(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
RenamedNodesMap& nodeNameMap);
|
||||
|
||||
NodeEncounteredContext(Events::ImportEventContext& parent,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
RenamedNodesMap& nodeNameMap);
|
||||
|
||||
AZStd::vector<AZStd::shared_ptr<DataTypes::IGraphObject>> m_createdData;
|
||||
};
|
||||
|
||||
// SceneDataPopulatedContextBase
|
||||
// Context pushed to indicate that a piece of scene data has been fully
|
||||
// processed and any importers that wish to place it within the scene graph
|
||||
// may now do so.
|
||||
// Member Variables:
|
||||
// m_graphData - the piece of data that should be inserted in the graph
|
||||
// m_dataName - the name that should be used as the basis for the scene node
|
||||
// name
|
||||
struct SceneDataPopulatedContextBase
|
||||
: public ImportContext
|
||||
{
|
||||
AZ_RTTI(SceneDataPopulatedContextBase, "{5F4CE8D2-EEAC-49F7-8065-0B6372162D6F}", ImportContext);
|
||||
|
||||
SceneDataPopulatedContextBase(NodeEncounteredContext& parent,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData,
|
||||
const AZStd::string& dataName);
|
||||
|
||||
SceneDataPopulatedContextBase(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition,
|
||||
RenamedNodesMap& nodeNameMap,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData, const AZStd::string& dataName);
|
||||
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& m_graphData;
|
||||
const AZStd::string m_dataName;
|
||||
};
|
||||
|
||||
// SceneNodeAppendedContextBase
|
||||
// Context pushed to indicate that data has been added to the scene graph.
|
||||
// Generally created due to the insertion of a node during SceneDataPopulatedContextBase
|
||||
// processing.
|
||||
struct SceneNodeAppendedContextBase
|
||||
: public ImportContext
|
||||
{
|
||||
AZ_RTTI(SceneNodeAppendedContextBase, "{0A69FB6C-2B1B-46E7-AEC3-C4B8ABBFDD69}", ImportContext);
|
||||
|
||||
SceneNodeAppendedContextBase(SceneDataPopulatedContextBase& parent, Containers::SceneGraph::NodeIndex newIndex);
|
||||
SceneNodeAppendedContextBase(Containers::Scene& scene,
|
||||
Containers::SceneGraph::NodeIndex currentGraphPosition, RenamedNodesMap& nodeNameMap);
|
||||
};
|
||||
|
||||
// SceneAttributeDataPopulatedContextBase
|
||||
// Context pushed to indicate that attribute data has been found and processed
|
||||
struct SceneAttributeDataPopulatedContextBase
|
||||
: public ImportContext
|
||||
{
|
||||
AZ_RTTI(SceneAttributeDataPopulatedContextBase, "{DA133E14-0770-435B-9A4E-38679367F56C}", ImportContext);
|
||||
|
||||
SceneAttributeDataPopulatedContextBase(SceneNodeAppendedContextBase& parent,
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& nodeData,
|
||||
const Containers::SceneGraph::NodeIndex attributeNodeIndex, const AZStd::string& dataName);
|
||||
|
||||
const AZStd::shared_ptr<DataTypes::IGraphObject>& m_graphData;
|
||||
const AZStd::string m_dataName;
|
||||
};
|
||||
|
||||
// SceneAttributeNodeAppendedContextBase
|
||||
// Context pushed to indicate that an attribute node has been added to the scene graph
|
||||
struct SceneAttributeNodeAppendedContextBase
|
||||
: public ImportContext
|
||||
{
|
||||
AZ_RTTI(SceneAttributeNodeAppendedContextBase, "{8A382A1E-CFE7-47D2-BA5B-CFDF1FB9F03D}", ImportContext);
|
||||
|
||||
SceneAttributeNodeAppendedContextBase(SceneAttributeDataPopulatedContextBase& parent,
|
||||
Containers::SceneGraph::NodeIndex newIndex);
|
||||
};
|
||||
|
||||
// SceneNodeAddedAttributesContextBase
|
||||
// Context pushed to indicate that all attribute processors have completed their
|
||||
// work for a specific data node.
|
||||
struct SceneNodeAddedAttributesContextBase
|
||||
: public ImportContext
|
||||
{
|
||||
AZ_RTTI(SceneNodeAddedAttributesContextBase, "{65B97E48-16A0-4BBD-B364-CFDA9E3600B6}", ImportContext);
|
||||
|
||||
SceneNodeAddedAttributesContextBase(SceneNodeAppendedContextBase& parent);
|
||||
};
|
||||
|
||||
// SceneNodeFinalizeContextBase
|
||||
// Context pushed last after all other contexts for a scene node to allow any
|
||||
// post-processing needed for an importer.
|
||||
struct SceneNodeFinalizeContextBase
|
||||
: public ImportContext
|
||||
{
|
||||
AZ_RTTI(SceneNodeFinalizeContextBase, "{F2C7D1BC-8065-423E-9212-241EB426A2BB}", ImportContext);
|
||||
|
||||
SceneNodeFinalizeContextBase(SceneNodeAddedAttributesContextBase& parent);
|
||||
};
|
||||
|
||||
// FinalizeSceneContextBase
|
||||
// Context pushed after the scene has been fully created. This can be used to finalize pending work
|
||||
// such as resolving named links.
|
||||
struct FinalizeSceneContextBase
|
||||
: public ImportContext
|
||||
{
|
||||
AZ_RTTI(FinalizeSceneContextBase, "{91C54F51-9B4D-4C61-956C-9D530725D737}", ImportContext);
|
||||
|
||||
FinalizeSceneContextBase(Containers::Scene& scene, RenamedNodesMap& nodeNameMap);
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <AssImpTypeConverter.h>
|
||||
#include <assimp/mesh.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneSystem.h>
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpAnimationImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneData/GraphData/AnimationData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
const char* AssImpAnimationImporter::s_animationNodeName = "animation";
|
||||
|
||||
// Downstream only supports 30 frames per second sample rate. Adjusting to 60 doubles the
|
||||
// length of the animations, they still play back at 30 frames per second.
|
||||
const double AssImpAnimationImporter::s_defaultTimeStepBetweenFrames = 1.0 / 30.0;
|
||||
|
||||
AZ::u32 GetNumKeyFrames(AZ::u32 keysSize, double duration, double ticksPerSecond)
|
||||
{
|
||||
if (AZ::IsClose(ticksPerSecond, 0))
|
||||
{
|
||||
AZ_Warning("AnimationImporter", false, "Animation ticks per second should not be zero, defaulting to %d keyframes for animation.", keysSize);
|
||||
return keysSize;
|
||||
}
|
||||
|
||||
const double totalTicks = duration / ticksPerSecond;
|
||||
AZ::u32 numKeys = keysSize;
|
||||
// +1 because the animation is from [0, duration] - we have a keyframe at the end of the duration which needs to be included
|
||||
double totalFramesAtDefaultTimeStep = totalTicks / AssImpAnimationImporter::s_defaultTimeStepBetweenFrames + 1;
|
||||
if (!AZ::IsClose(totalFramesAtDefaultTimeStep, numKeys, 1))
|
||||
{
|
||||
numKeys = AZStd::ceilf(totalFramesAtDefaultTimeStep);
|
||||
}
|
||||
return numKeys;
|
||||
}
|
||||
|
||||
double GetTimeForFrame(AZ::u32 frame, double ticksPerSecond)
|
||||
{
|
||||
return frame * AssImpAnimationImporter::s_defaultTimeStepBetweenFrames * ticksPerSecond;
|
||||
}
|
||||
|
||||
// Helper class to store key data, when translating from AssImp layout to the engine's scene format.
|
||||
struct KeyData
|
||||
{
|
||||
KeyData(float value, float time) :
|
||||
mValue(value),
|
||||
mTime(time)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool operator<(const KeyData& other) const
|
||||
{
|
||||
return mTime < other.mTime;
|
||||
}
|
||||
|
||||
float mValue = 0;
|
||||
float mTime = 0;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
void LerpTemplate(T& start, const T& end, float t)
|
||||
{
|
||||
start = start * (1.0f - t) + end * t;
|
||||
}
|
||||
|
||||
template<>
|
||||
void LerpTemplate(aiQuaternion& start, const aiQuaternion& end, float t)
|
||||
{
|
||||
aiQuaternion::Interpolate(start, start, end, t);
|
||||
}
|
||||
|
||||
template<>
|
||||
void LerpTemplate(float& start, const float& end, float t)
|
||||
{
|
||||
start = AZ::Lerp(start, end, t);
|
||||
}
|
||||
|
||||
template<class KeyContainerType, class FrameValueType>
|
||||
bool SampleKeyFrame(FrameValueType& result, const KeyContainerType& keys, AZ::u32 numKeys, double time, AZ::u32& lastIndex)
|
||||
{
|
||||
if (numKeys == 0)
|
||||
{
|
||||
AZ_Error("AnimationImporter", numKeys > 0, "Animation key set must have at least 1 key");
|
||||
return false;
|
||||
}
|
||||
if (numKeys == 1)
|
||||
{
|
||||
result = keys[0].mValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
while (lastIndex < numKeys - 1 && time >= keys[lastIndex + 1].mTime)
|
||||
{
|
||||
++lastIndex;
|
||||
}
|
||||
result = keys[lastIndex].mValue;
|
||||
if (lastIndex < numKeys - 1)
|
||||
{
|
||||
auto nextValue = keys[lastIndex + 1].mValue;
|
||||
float normalizedTimeBetweenFrames = 0;
|
||||
if (keys[lastIndex + 1].mTime != keys[lastIndex].mTime)
|
||||
{
|
||||
normalizedTimeBetweenFrames =
|
||||
(time - keys[lastIndex].mTime) / (keys[lastIndex + 1].mTime - keys[lastIndex].mTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("AnimationImporter", false,
|
||||
"Animation has keys with duplicate time %5.5f, at indices %d and %d. The second will be ignored.",
|
||||
keys[lastIndex].mTime,
|
||||
lastIndex,
|
||||
lastIndex + 1);
|
||||
}
|
||||
LerpTemplate(result, nextValue, normalizedTimeBetweenFrames);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
AssImpAnimationImporter::AssImpAnimationImporter()
|
||||
{
|
||||
BindToCall(&AssImpAnimationImporter::ImportAnimation);
|
||||
}
|
||||
|
||||
void AssImpAnimationImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpAnimationImporter, SceneCore::LoadingComponent>()->Version(5); // [LYN-4226] Invert PostRotation matrix in animation chains
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string GetName(const aiMeshAnim* anim)
|
||||
{
|
||||
return anim->mName.C_Str();
|
||||
}
|
||||
|
||||
AZStd::string GetName(const aiNodeAnim* anim)
|
||||
{
|
||||
return anim->mNodeName.C_Str();
|
||||
}
|
||||
|
||||
AZStd::string GetName(const aiMeshMorphAnim* anim)
|
||||
{
|
||||
return anim->mName.C_Str();
|
||||
}
|
||||
|
||||
struct ConsolidatedNodeAnim
|
||||
{
|
||||
ConsolidatedNodeAnim() = default;
|
||||
ConsolidatedNodeAnim(const ConsolidatedNodeAnim& other) = delete;
|
||||
|
||||
ConsolidatedNodeAnim(ConsolidatedNodeAnim&& other) noexcept
|
||||
: mNumPositionKeys{ other.mNumPositionKeys },
|
||||
mPositionKeys{ other.mPositionKeys },
|
||||
mNumRotationKeys{ other.mNumRotationKeys },
|
||||
mRotationKeys{ other.mRotationKeys },
|
||||
mNumScalingKeys{ other.mNumScalingKeys },
|
||||
mScalingKeys{ other.mScalingKeys },
|
||||
m_ownedPositionKeys{ std::move(other.m_ownedPositionKeys) },
|
||||
m_ownedScalingKeys{ std::move(other.m_ownedScalingKeys) },
|
||||
m_ownedRotationKeys{ std::move(other.m_ownedRotationKeys) },
|
||||
mPreState{ other.mPreState },
|
||||
mPostState{ other.mPostState }
|
||||
{
|
||||
}
|
||||
|
||||
ConsolidatedNodeAnim& operator=(const ConsolidatedNodeAnim& other) = delete;
|
||||
|
||||
ConsolidatedNodeAnim& operator=(ConsolidatedNodeAnim&& other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
mNumPositionKeys = other.mNumPositionKeys;
|
||||
mPositionKeys = other.mPositionKeys;
|
||||
mNumRotationKeys = other.mNumRotationKeys;
|
||||
mRotationKeys = other.mRotationKeys;
|
||||
mNumScalingKeys = other.mNumScalingKeys;
|
||||
mScalingKeys = other.mScalingKeys;
|
||||
m_ownedPositionKeys = std::move(other.m_ownedPositionKeys);
|
||||
m_ownedScalingKeys = std::move(other.m_ownedScalingKeys);
|
||||
m_ownedRotationKeys = std::move(other.m_ownedRotationKeys);
|
||||
mPreState = other.mPreState;
|
||||
mPostState = other.mPostState;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// These variables are named using AssImp naming convention for consistency
|
||||
AZ::u32 mNumPositionKeys{};
|
||||
aiVectorKey* mPositionKeys{};
|
||||
|
||||
AZ::u32 mNumRotationKeys{};
|
||||
aiQuatKey* mRotationKeys{};
|
||||
|
||||
AZ::u32 mNumScalingKeys{};
|
||||
aiVectorKey* mScalingKeys{};
|
||||
|
||||
AZStd::vector<aiVectorKey> m_ownedPositionKeys{};
|
||||
AZStd::vector<aiVectorKey> m_ownedScalingKeys{};
|
||||
AZStd::vector<aiQuatKey> m_ownedRotationKeys{};
|
||||
|
||||
aiAnimBehaviour mPreState{};
|
||||
aiAnimBehaviour mPostState{};
|
||||
};
|
||||
|
||||
AZStd::pair<const aiAnimation*, ConsolidatedNodeAnim> MakeMyPair(const aiAnimation* animation, const aiNodeAnim* anim)
|
||||
{
|
||||
ConsolidatedNodeAnim consolidatedNodeAnim{};
|
||||
|
||||
consolidatedNodeAnim.mNumRotationKeys = anim->mNumRotationKeys;
|
||||
consolidatedNodeAnim.mNumPositionKeys = anim->mNumPositionKeys;
|
||||
consolidatedNodeAnim.mNumScalingKeys = anim->mNumScalingKeys;
|
||||
consolidatedNodeAnim.mRotationKeys = anim->mRotationKeys;
|
||||
consolidatedNodeAnim.mPositionKeys = anim->mPositionKeys;
|
||||
consolidatedNodeAnim.mScalingKeys = anim->mScalingKeys;
|
||||
|
||||
return {animation, AZStd::move(consolidatedNodeAnim)};
|
||||
}
|
||||
|
||||
auto MakeMyPair(const aiAnimation* animation, const aiMeshAnim* anim)
|
||||
{
|
||||
return AZStd::make_pair(animation, anim);
|
||||
}
|
||||
|
||||
auto MakeMyPair(const aiAnimation* animation, const aiMeshMorphAnim* anim)
|
||||
{
|
||||
return AZStd::make_pair(animation, anim);
|
||||
}
|
||||
Events::ProcessingResult AssImpAnimationImporter::ImportAnimation(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Animation");
|
||||
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
// Add check for animation layers at the scene level.
|
||||
|
||||
if (!scene->HasAnimations() || IsPivotNode(currentNode->mName))
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
AZStd::unordered_multimap<AZStd::string, AZStd::pair<const aiAnimation*, ConsolidatedNodeAnim>> boneAnimations;
|
||||
typedef AZStd::pair<const aiAnimation*, const aiMeshMorphAnim*> AnimAndMorphAnim;
|
||||
typedef AZStd::unordered_map<AZStd::string, AnimAndMorphAnim> ChannelToMorphAnim;
|
||||
typedef AZStd::unordered_map<AZStd::string, ChannelToMorphAnim> NodeToChannelToMorphAnim;
|
||||
NodeToChannelToMorphAnim meshMorphAnimations;
|
||||
|
||||
// Goes through all the animation channels of a given type and adds them to a map so we can easily find
|
||||
// all the animations for a given node
|
||||
// In the case of bone animations, the data is copied into a ConsolidatedNodeAnim so we can
|
||||
// do fix-ups later without affecting the original data
|
||||
auto mapAnimationsFunc = [](AZ::u32 numChannels, auto** channels, const aiAnimation* animation, auto& map)
|
||||
{
|
||||
map.reserve(numChannels);
|
||||
|
||||
for (AZ::u32 channelIndex = 0; channelIndex < numChannels; ++channelIndex)
|
||||
{
|
||||
auto* nodeAnim = channels[channelIndex];
|
||||
AZStd::string name = GetName(nodeAnim);
|
||||
|
||||
map.emplace(name, MakeMyPair(animation, nodeAnim));
|
||||
}
|
||||
};
|
||||
|
||||
for (AZ::u32 animIndex = 0; animIndex < scene->mNumAnimations; ++animIndex)
|
||||
{
|
||||
const aiAnimation* animation = scene->mAnimations[animIndex];
|
||||
if (animation->mTicksPerSecond == 0)
|
||||
{
|
||||
AZ_Error(
|
||||
"AnimationImporter", false,
|
||||
"Animation name %s has a sample rate of 0 ticks per second and cannot be processed.",
|
||||
animation->mName.C_Str());
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
mapAnimationsFunc(animation->mNumChannels, animation->mChannels, animation, boneAnimations);
|
||||
|
||||
for (AZ::u32 channelIndex = 0; channelIndex < animation->mNumMorphMeshChannels; ++channelIndex)
|
||||
{
|
||||
auto* nodeAnim = animation->mMorphMeshChannels[channelIndex];
|
||||
AZStd::string name = GetName(nodeAnim);
|
||||
|
||||
AZStd::vector<AZStd::string> meshNodeNameAndChannel;
|
||||
|
||||
// Morph target animations include the channel in the name,
|
||||
// so if a mesh is named Mesh01, the morph target for the first channel
|
||||
// will be named Mesh01*0
|
||||
AZ::StringFunc::Tokenize(name, meshNodeNameAndChannel, '*');
|
||||
|
||||
if (meshNodeNameAndChannel.size() != 2)
|
||||
{
|
||||
AZ_Error(
|
||||
"AnimationImporter", false,
|
||||
"Morph animation name %s was not in the expected format of: node name, asterisk, node channel. "
|
||||
"Example: 'NodeName*0'",
|
||||
name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
AZStd::string meshNodeName(meshNodeNameAndChannel[0]);
|
||||
AZStd::string channel(meshNodeNameAndChannel[1]);
|
||||
meshMorphAnimations[meshNodeName][channel] = AnimAndMorphAnim(animation, nodeAnim);
|
||||
}
|
||||
}
|
||||
|
||||
// Go through all the bone animations and find any that reference a pivot node
|
||||
// We'll make a new node anim and store all the combined animation channels there
|
||||
// with the name set to the base bone name
|
||||
decltype(boneAnimations) combinedAnimations;
|
||||
|
||||
for (auto&& animation : boneAnimations)
|
||||
{
|
||||
size_t pos = AZ::StringFunc::Find(animation.first, PivotNodeMarker, 0);
|
||||
|
||||
if (pos != animation.first.npos)
|
||||
{
|
||||
AZStd::string_view name, part;
|
||||
SplitPivotNodeName(aiString(animation.first.c_str()), pos, name, part);
|
||||
|
||||
auto&& iterator = combinedAnimations.find(name);
|
||||
|
||||
if (iterator == combinedAnimations.end())
|
||||
{
|
||||
combinedAnimations.emplace(AZStd::string(name), AZStd::move(animation.second));
|
||||
iterator = combinedAnimations.find(name);
|
||||
}
|
||||
|
||||
if (iterator != combinedAnimations.end())
|
||||
{
|
||||
auto& existingNode = iterator->second.second;
|
||||
auto&& src = animation.second.second;
|
||||
|
||||
if (part == "Translation")
|
||||
{
|
||||
existingNode.mNumPositionKeys = src.mNumPositionKeys;
|
||||
existingNode.mPositionKeys = src.mPositionKeys;
|
||||
}
|
||||
else if (part == "Rotation")
|
||||
{
|
||||
existingNode.mNumRotationKeys = src.mNumRotationKeys;
|
||||
existingNode.mRotationKeys = src.mRotationKeys;
|
||||
}
|
||||
else if (part == "Scaling")
|
||||
{
|
||||
existingNode.mNumScalingKeys = src.mNumScalingKeys;
|
||||
existingNode.mScalingKeys = src.mScalingKeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!combinedAnimations.empty())
|
||||
{
|
||||
boneAnimations.swap(combinedAnimations);
|
||||
}
|
||||
|
||||
Events::ProcessingResultCombiner combinedAnimationResult;
|
||||
if (context.m_sourceNode.ContainsMesh())
|
||||
{
|
||||
const aiMesh* firstMesh = scene->mMeshes[currentNode->mMeshes[0]];
|
||||
if (NodeToChannelToMorphAnim::iterator channelsForMeshName = meshMorphAnimations.find(firstMesh->mName.C_Str());
|
||||
channelsForMeshName != meshMorphAnimations.end())
|
||||
{
|
||||
const auto [nodeIterName, channels] = *channelsForMeshName;
|
||||
for (const auto& [channel, animAndMorphAnim] : channels)
|
||||
{
|
||||
const auto& [animation, morphAnimation] = animAndMorphAnim;
|
||||
combinedAnimationResult += ImportBlendShapeAnimation(
|
||||
context, animation, morphAnimation, firstMesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string nodeName = s_animationNodeName;
|
||||
RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition);
|
||||
AZ_TraceContext("Animation node name", nodeName);
|
||||
|
||||
// If there are no bone animations, but there are mesh animations,
|
||||
// then a stub animation needs to be created so the exporter can create the exported morph target animation.
|
||||
if (boneAnimations.empty() && !meshMorphAnimations.empty())
|
||||
{
|
||||
const aiAnimation* animation = scene->mAnimations[0];
|
||||
for (AZ::u32 channelIndex = 0; channelIndex < animation->mNumMorphMeshChannels; ++channelIndex)
|
||||
{
|
||||
const aiMeshMorphAnim* nodeAnim = animation->mMorphMeshChannels[channelIndex];
|
||||
// Morph animations need a regular animation on the node, as well.
|
||||
// If there is no bone animation on the current node, then generate one here.
|
||||
AZStd::shared_ptr<SceneData::GraphData::AnimationData> createdAnimationData =
|
||||
AZStd::make_shared<SceneData::GraphData::AnimationData>();
|
||||
|
||||
const size_t numKeyframes = GetNumKeyFrames(
|
||||
nodeAnim->mNumKeys,
|
||||
animation->mDuration,
|
||||
animation->mTicksPerSecond);
|
||||
createdAnimationData->ReserveKeyFrames(numKeyframes);
|
||||
|
||||
const double timeStepBetweenFrames = 1.0 / animation->mTicksPerSecond;
|
||||
createdAnimationData->SetTimeStepBetweenFrames(timeStepBetweenFrames);
|
||||
|
||||
// Set every frame of the animation to the start location of the node.
|
||||
aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode);
|
||||
DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform);
|
||||
context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform);
|
||||
context.m_sourceSceneSystem.ConvertUnit(localTransform);
|
||||
for (AZ::u32 time = 0; time <= numKeyframes; ++time)
|
||||
{
|
||||
createdAnimationData->AddKeyFrame(localTransform);
|
||||
}
|
||||
|
||||
const AZStd::string stubBoneAnimForMorphName(AZStd::string::format("%s%s", nodeName.c_str(), nodeAnim->mName.C_Str()));
|
||||
Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
|
||||
context.m_currentGraphPosition, stubBoneAnimForMorphName.c_str(), AZStd::move(createdAnimationData));
|
||||
context.m_scene.GetGraph().MakeEndPoint(addNode);
|
||||
}
|
||||
|
||||
return combinedAnimationResult.GetResult();
|
||||
}
|
||||
|
||||
AZStd::unordered_set<AZStd::string> boneList;
|
||||
|
||||
for (int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
|
||||
{
|
||||
aiMesh* mesh = scene->mMeshes[meshIndex];
|
||||
|
||||
for (int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
|
||||
{
|
||||
aiBone* bone = mesh->mBones[boneIndex];
|
||||
|
||||
boneList.insert(bone->mName.C_Str());
|
||||
}
|
||||
}
|
||||
|
||||
decltype(boneAnimations) fillerAnimations;
|
||||
|
||||
// Go through all the animations and make sure we create placeholder animations for any bones missing them
|
||||
for (auto&& anim : boneAnimations)
|
||||
{
|
||||
for (auto boneName : boneList)
|
||||
{
|
||||
if (!IsPivotNode(aiString(boneName.c_str())))
|
||||
{
|
||||
if (!boneAnimations.contains(boneName) &&
|
||||
!fillerAnimations.contains(boneName))
|
||||
{
|
||||
// Create 1 key for each type that just copies the current transform
|
||||
ConsolidatedNodeAnim emptyAnimation;
|
||||
auto node = scene->mRootNode->FindNode(boneName.c_str());
|
||||
aiMatrix4x4 globalTransform = GetConcatenatedLocalTransform(node);
|
||||
|
||||
aiVector3D position, scale;
|
||||
aiQuaternion rotation;
|
||||
|
||||
globalTransform.Decompose(scale, rotation, position);
|
||||
|
||||
emptyAnimation.mNumRotationKeys = emptyAnimation.mNumPositionKeys = emptyAnimation.mNumScalingKeys = 1;
|
||||
|
||||
emptyAnimation.m_ownedPositionKeys.emplace_back(0, position);
|
||||
emptyAnimation.mPositionKeys = emptyAnimation.m_ownedPositionKeys.data();
|
||||
|
||||
emptyAnimation.m_ownedRotationKeys.emplace_back(0, rotation);
|
||||
emptyAnimation.mRotationKeys = emptyAnimation.m_ownedRotationKeys.data();
|
||||
|
||||
emptyAnimation.m_ownedScalingKeys.emplace_back(0, scale);
|
||||
emptyAnimation.mScalingKeys = emptyAnimation.m_ownedScalingKeys.data();
|
||||
|
||||
fillerAnimations.insert(
|
||||
AZStd::make_pair(boneName, AZStd::make_pair(anim.second.first, AZStd::move(emptyAnimation))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boneAnimations.insert(AZStd::make_move_iterator(fillerAnimations.begin()), AZStd::make_move_iterator(fillerAnimations.end()));
|
||||
|
||||
auto animItr = boneAnimations.equal_range(currentNode->mName.C_Str());
|
||||
|
||||
if (animItr.first == animItr.second)
|
||||
{
|
||||
return combinedAnimationResult.GetResult();
|
||||
}
|
||||
|
||||
bool onlyOne = false;
|
||||
|
||||
for (auto it = animItr.first; it != animItr.second; ++it)
|
||||
{
|
||||
if (onlyOne)
|
||||
{
|
||||
AZ_Error("AnimationImporter", false, "Bone %s has multiple animations. Only 1 animation per bone is supported", currentNode->mName.C_Str());
|
||||
break;
|
||||
}
|
||||
|
||||
const aiAnimation* animation = it->second.first;
|
||||
const ConsolidatedNodeAnim* anim = &it->second.second;
|
||||
|
||||
// We don't currently handle having a different number of keys, with 1 exception:
|
||||
// 1 key is essentially a constant so we do handle that case
|
||||
if ((anim->mNumPositionKeys != anim->mNumRotationKeys && anim->mNumPositionKeys > 1 && anim->mNumRotationKeys > 1) ||
|
||||
(anim->mNumPositionKeys != anim->mNumScalingKeys && anim->mNumPositionKeys > 1 && anim->mNumScalingKeys > 1) ||
|
||||
(anim->mNumRotationKeys != anim->mNumScalingKeys && anim->mNumRotationKeys > 1 && anim->mNumScalingKeys > 1))
|
||||
{
|
||||
AZ_Error("AnimationImporter", false, "Bone Animation with different number of position (%d)/rotation (%d)/scaling (%d) keys not supported",
|
||||
anim->mNumPositionKeys, anim->mNumRotationKeys, anim->mNumScalingKeys);
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
// Resample the animations at a fixed time step. This matches the behaviour of
|
||||
// the previous SDK used. Longer term, this could be data driven, or based on the
|
||||
// smallest time step between key frames.
|
||||
// AssImp has an animation->mTicksPerSecond and animation->mDuration, but those
|
||||
// are less predictable than just using a fixed time step.
|
||||
// AssImp documentation claims animation->mDuration is the duration of the animation in ticks, but
|
||||
// not all animations we've tested follow that pattern. Sometimes duration is in seconds.
|
||||
const size_t numKeyFrames = GetNumKeyFrames(
|
||||
AZStd::max(AZStd::max(anim->mNumScalingKeys, anim->mNumPositionKeys), anim->mNumRotationKeys),
|
||||
animation->mDuration,
|
||||
animation->mTicksPerSecond);
|
||||
|
||||
AZStd::shared_ptr<SceneData::GraphData::AnimationData> createdAnimationData =
|
||||
AZStd::make_shared<SceneData::GraphData::AnimationData>();
|
||||
createdAnimationData->ReserveKeyFrames(numKeyFrames);
|
||||
createdAnimationData->SetTimeStepBetweenFrames(s_defaultTimeStepBetweenFrames);
|
||||
|
||||
AZ::u32 lastScaleIndex = 0;
|
||||
AZ::u32 lastPositionIndex = 0;
|
||||
AZ::u32 lastRotationIndex = 0;
|
||||
for (AZ::u32 frame = 0; frame < numKeyFrames; ++frame)
|
||||
{
|
||||
const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
|
||||
|
||||
aiVector3D scale(1.0f, 1.0f, 1.0f);
|
||||
aiVector3D position(0.0f, 0.0f, 0.0f);
|
||||
aiQuaternion rotation(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
if (!SampleKeyFrame(scale, anim->mScalingKeys, anim->mNumScalingKeys, time, lastScaleIndex) ||
|
||||
!SampleKeyFrame(position, anim->mPositionKeys, anim->mNumPositionKeys, time, lastPositionIndex) ||
|
||||
!SampleKeyFrame(rotation, anim->mRotationKeys, anim->mNumRotationKeys, time, lastRotationIndex))
|
||||
{
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
aiMatrix4x4 transform(scale, rotation, position);
|
||||
DataTypes::MatrixType animTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(transform);
|
||||
|
||||
context.m_sourceSceneSystem.SwapTransformForUpAxis(animTransform);
|
||||
context.m_sourceSceneSystem.ConvertBoneUnit(animTransform);
|
||||
|
||||
createdAnimationData->AddKeyFrame(animTransform);
|
||||
}
|
||||
|
||||
Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
|
||||
context.m_currentGraphPosition, nodeName.c_str(), AZStd::move(createdAnimationData));
|
||||
context.m_scene.GetGraph().MakeEndPoint(addNode);
|
||||
|
||||
onlyOne = true;
|
||||
}
|
||||
|
||||
combinedAnimationResult += Events::ProcessingResult::Success;
|
||||
return combinedAnimationResult.GetResult();
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpAnimationImporter::ImportBlendShapeAnimation(
|
||||
AssImpSceneNodeAppendedContext& context,
|
||||
const aiAnimation* animation,
|
||||
const aiMeshMorphAnim* meshMorphAnim,
|
||||
const aiMesh* mesh)
|
||||
{
|
||||
if (meshMorphAnim->mNumKeys == 0)
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
Events::ProcessingResultCombiner combinedAnimationResult;
|
||||
|
||||
// In:
|
||||
// Key index
|
||||
// Time
|
||||
// Values in AssImp, Channel in FBX SDK
|
||||
// Weights in AssImp, Values in FBX SDK
|
||||
// Num values & weights
|
||||
|
||||
// Out:
|
||||
// One BlendShapeAnimationData per value (Channel in FBX SDK) index
|
||||
// SetTimeStepBetweenFrames set on the animation data
|
||||
// Keyframes. Weights (Values in FBX SDK) per key time.
|
||||
// Keyframes generated for every single frame of the animation.
|
||||
typedef AZStd::map<int, AZStd::vector<KeyData>> ValueToKeyDataMap;
|
||||
ValueToKeyDataMap valueToKeyDataMap;
|
||||
// Key time can be less than zero, normalize to have zero be the lowest time.
|
||||
double keyOffset = 0;
|
||||
for (int keyIdx = 0; keyIdx < meshMorphAnim->mNumKeys; keyIdx++)
|
||||
{
|
||||
aiMeshMorphKey& key = meshMorphAnim->mKeys[keyIdx];
|
||||
for (int valIdx = 0; valIdx < key.mNumValuesAndWeights; ++valIdx)
|
||||
{
|
||||
int currentValue = key.mValues[valIdx];
|
||||
KeyData thisKey(key.mWeights[valIdx], key.mTime);
|
||||
valueToKeyDataMap[currentValue].insert(
|
||||
AZStd::upper_bound(valueToKeyDataMap[currentValue].begin(), valueToKeyDataMap[currentValue].end(),thisKey),
|
||||
thisKey);
|
||||
if (key.mTime < keyOffset)
|
||||
{
|
||||
keyOffset = key.mTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [meshIdx, keys] : valueToKeyDataMap)
|
||||
{
|
||||
AZStd::shared_ptr<SceneData::GraphData::BlendShapeAnimationData> morphAnimNode =
|
||||
AZStd::make_shared<SceneData::GraphData::BlendShapeAnimationData>();
|
||||
|
||||
const size_t numKeyFrames = GetNumKeyFrames(keys.size(), animation->mDuration, animation->mTicksPerSecond);
|
||||
morphAnimNode->ReserveKeyFrames(numKeyFrames);
|
||||
morphAnimNode->SetTimeStepBetweenFrames(s_defaultTimeStepBetweenFrames);
|
||||
|
||||
aiAnimMesh* aiAnimMesh = mesh->mAnimMeshes[meshIdx];
|
||||
AZStd::string_view nodeName(aiAnimMesh->mName.C_Str());
|
||||
|
||||
const AZ::u32 maxKeys = keys.size();
|
||||
AZ::u32 keyIdx = 0;
|
||||
for (AZ::u32 frame = 0; frame < numKeyFrames; ++frame)
|
||||
{
|
||||
const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
|
||||
|
||||
float weight = 0;
|
||||
if (!SampleKeyFrame(weight, keys, keys.size(), time + keyOffset, keyIdx))
|
||||
{
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
morphAnimNode->AddKeyFrame(weight);
|
||||
}
|
||||
|
||||
const size_t dotIndex = nodeName.find_last_of('.');
|
||||
nodeName = nodeName.substr(dotIndex + 1);
|
||||
|
||||
morphAnimNode->SetBlendShapeName(nodeName.data());
|
||||
|
||||
AZStd::string animNodeName(AZStd::string::format("%s_%s", s_animationNodeName, nodeName.data()));
|
||||
Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
|
||||
context.m_currentGraphPosition, animNodeName.c_str(), AZStd::move(morphAnimNode));
|
||||
context.m_scene.GetGraph().MakeEndPoint(addNode);
|
||||
|
||||
}
|
||||
|
||||
return Events::ProcessingResult::Success;
|
||||
}
|
||||
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
|
||||
struct aiAnimation;
|
||||
struct aiMesh;
|
||||
struct aiMeshMorphAnim;
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpAnimationImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpAnimationImporter, "{93b3f4e3-6fcd-42b9-a74e-5923f76d25c7}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpAnimationImporter();
|
||||
~AssImpAnimationImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportAnimation(AssImpSceneNodeAppendedContext& context);
|
||||
Events::ProcessingResult ImportBlendShapeAnimation(
|
||||
AssImpSceneNodeAppendedContext& context,
|
||||
const aiAnimation* animation,
|
||||
const aiMeshMorphAnim* meshMorphAnim,
|
||||
const aiMesh* mesh);
|
||||
|
||||
static const double s_defaultTimeStepBetweenFrames;
|
||||
|
||||
protected:
|
||||
static const char* s_animationNodeName;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpBitangentStreamImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpTypeConverter.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/mesh.h>
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
const char* AssImpBitangentStreamImporter::m_defaultNodeName = "Bitangent";
|
||||
|
||||
AssImpBitangentStreamImporter::AssImpBitangentStreamImporter()
|
||||
{
|
||||
BindToCall(&AssImpBitangentStreamImporter::ImportBitangentStreams);
|
||||
}
|
||||
|
||||
void AssImpBitangentStreamImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpBitangentStreamImporter, SceneCore::LoadingComponent>()->Version(3); // LYN-3250
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpBitangentStreamImporter::ImportBitangentStreams(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", m_defaultNodeName);
|
||||
if (!context.m_sourceNode.ContainsMesh())
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
const auto meshHasTangentsAndBitangents = [&scene](const unsigned int meshIndex)
|
||||
{
|
||||
return scene->mMeshes[meshIndex]->HasTangentsAndBitangents();
|
||||
};
|
||||
|
||||
// If there are no bitangents on any meshes, there's nothing to import in this function.
|
||||
const bool anyMeshHasTangentsAndBitangents = AZStd::any_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents);
|
||||
if (!anyMeshHasTangentsAndBitangents)
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
// AssImp nodes with multiple meshes on them occur when AssImp split a mesh on material.
|
||||
// This logic recombines those meshes to minimize the changes needed to replace FBX SDK with AssImp, FBX SDK did not separate meshes,
|
||||
// and the engine has code to do this later.
|
||||
const bool allMeshesHaveTangentsAndBitangents = AZStd::all_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents);
|
||||
if (!allMeshesHaveTangentsAndBitangents)
|
||||
{
|
||||
AZ_Error(
|
||||
Utilities::ErrorWindow, false,
|
||||
"Node with name %s has meshes with and without bitangents. "
|
||||
"Placeholder incorrect bitangents will be generated to allow the data to process, "
|
||||
"but the source art needs to be fixed to correct this. Either apply bitangents to all meshes on this node, "
|
||||
"or remove all bitangents from all meshes on this node.",
|
||||
currentNode->mName.C_Str());
|
||||
}
|
||||
|
||||
const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
|
||||
|
||||
AZStd::shared_ptr<SceneData::GraphData::MeshVertexBitangentData> bitangentStream =
|
||||
AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexBitangentData>();
|
||||
// AssImp only has one bitangentStream per mesh.
|
||||
bitangentStream->SetBitangentSetIndex(0);
|
||||
|
||||
bitangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
|
||||
bitangentStream->ReserveContainerSpace(vertexCount);
|
||||
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
|
||||
|
||||
for (int v = 0; v < mesh->mNumVertices; ++v)
|
||||
{
|
||||
if (!mesh->HasTangentsAndBitangents())
|
||||
{
|
||||
// This node has mixed meshes with and without bitangents.
|
||||
// An error was already thrown above. Output stub bitangents so
|
||||
// the mesh can still be output in some form, even if the data isn't correct.
|
||||
// The bitangent count needs to match the vertex count on the associated mesh node.
|
||||
bitangentStream->AppendBitangent(Vector3::CreateAxisY());
|
||||
}
|
||||
else
|
||||
{
|
||||
const Vector3 bitangent(
|
||||
AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mBitangents[v]));
|
||||
bitangentStream->AppendBitangent(bitangent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Containers::SceneGraph::NodeIndex newIndex =
|
||||
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, m_defaultNodeName);
|
||||
|
||||
Events::ProcessingResult bitangentResults;
|
||||
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, bitangentStream, newIndex, m_defaultNodeName);
|
||||
bitangentResults = Events::Process(dataPopulated);
|
||||
|
||||
if (bitangentResults != Events::ProcessingResult::Failure)
|
||||
{
|
||||
bitangentResults = AddAttributeDataNodeWithContexts(dataPopulated);
|
||||
}
|
||||
return bitangentResults;
|
||||
}
|
||||
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpBitangentStreamImporter : public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpBitangentStreamImporter, "{49FC818A-956F-43DA-BBAC-73198E0C5A1F}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpBitangentStreamImporter();
|
||||
~AssImpBitangentStreamImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportBitangentStreams(AssImpSceneNodeAppendedContext& context);
|
||||
|
||||
protected:
|
||||
static const char* m_defaultNodeName;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpBlendShapeImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneSystem.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpTypeConverter.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/SkinMeshData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/BlendShapeData.h>
|
||||
#include <assimp/scene.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
AssImpBlendShapeImporter::AssImpBlendShapeImporter()
|
||||
{
|
||||
BindToCall(&AssImpBlendShapeImporter::ImportBlendShapes);
|
||||
}
|
||||
|
||||
void AssImpBlendShapeImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpBlendShapeImporter, SceneCore::LoadingComponent>()->Version(3); // LYN-2576
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpBlendShapeImporter::ImportBlendShapes(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Blend Shapes");
|
||||
int numMesh = context.m_sourceNode.GetAssImpNode()->mNumMeshes;
|
||||
bool animMeshExists = false;
|
||||
for (int idx = 0; idx < numMesh; idx++)
|
||||
{
|
||||
int meshId = context.m_sourceNode.GetAssImpNode()->mMeshes[idx];
|
||||
aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[meshId];
|
||||
if (aiMesh->mNumAnimMeshes)
|
||||
{
|
||||
animMeshExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!animMeshExists)
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context));
|
||||
if (!meshDataResult.IsSuccess())
|
||||
{
|
||||
return meshDataResult.GetError();
|
||||
}
|
||||
|
||||
Events::ProcessingResultCombiner combinedBlendShapeResult;
|
||||
|
||||
// 1. Loop through meshes & anims
|
||||
// Create storage: Anim to meshes
|
||||
// 2. Loop through anims & meshes
|
||||
// Create an anim mesh for each anim, with meshes re-combined.
|
||||
// AssImp separates meshes that have multiple materials.
|
||||
// This code re-combines them to match previous FBX SDK behavior,
|
||||
// so they can be separated by engine code instead.
|
||||
AZStd::map<AZStd::string_view, AZStd::vector<AZStd::pair<int, int>>> animToMeshToAnimMeshIndices;
|
||||
for (int nodeMeshIdx = 0; nodeMeshIdx < numMesh; nodeMeshIdx++)
|
||||
{
|
||||
int sceneMeshIdx = context.m_sourceNode.GetAssImpNode()->mMeshes[nodeMeshIdx];
|
||||
const aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[sceneMeshIdx];
|
||||
for (int animIdx = 0; animIdx < aiMesh->mNumAnimMeshes; animIdx++)
|
||||
{
|
||||
aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[animIdx];
|
||||
animToMeshToAnimMeshIndices[aiAnimMesh->mName.C_Str()].emplace_back(nodeMeshIdx, animIdx);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& animToMeshIndex : animToMeshToAnimMeshIndices)
|
||||
{
|
||||
AZStd::shared_ptr<SceneData::GraphData::BlendShapeData> blendShapeData =
|
||||
AZStd::make_shared<SceneData::GraphData::BlendShapeData>();
|
||||
|
||||
// Some DCC tools, like Maya, include a full path separated by '.' in the node names.
|
||||
// For example, "cone_skin_blendShapeNode.cone_squash"
|
||||
// Downstream processing doesn't want anything but the last part of that node name,
|
||||
// so find the last '.' and remove anything before it.
|
||||
AZStd::string nodeName(animToMeshIndex.first);
|
||||
size_t dotIndex = nodeName.rfind('.');
|
||||
if (dotIndex != AZStd::string::npos)
|
||||
{
|
||||
nodeName.erase(0, dotIndex + 1);
|
||||
}
|
||||
int vertexOffset = 0;
|
||||
RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition, "BlendShape");
|
||||
AZ_TraceContext("Blend shape name", nodeName);
|
||||
for (const auto& meshIndex : animToMeshIndex.second)
|
||||
{
|
||||
int sceneMeshIdx = context.m_sourceNode.GetAssImpNode()->mMeshes[meshIndex.first];
|
||||
const aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[sceneMeshIdx];
|
||||
const aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[meshIndex.second];
|
||||
|
||||
AZStd::bitset<SceneData::GraphData::BlendShapeData::MaxNumUVSets> uvSetUsedFlags;
|
||||
for (AZ::u8 uvSetIndex = 0; uvSetIndex < SceneData::GraphData::BlendShapeData::MaxNumUVSets; ++uvSetIndex)
|
||||
{
|
||||
uvSetUsedFlags.set(uvSetIndex, aiAnimMesh->HasTextureCoords(uvSetIndex));
|
||||
}
|
||||
AZStd::bitset<SceneData::GraphData::BlendShapeData::MaxNumColorSets> colorSetUsedFlags;
|
||||
for (AZ::u8 colorSetIndex = 0; colorSetIndex < SceneData::GraphData::BlendShapeData::MaxNumColorSets;
|
||||
++colorSetIndex)
|
||||
{
|
||||
colorSetUsedFlags.set(colorSetIndex, aiAnimMesh->HasVertexColors(colorSetIndex));
|
||||
}
|
||||
blendShapeData->ReserveData(
|
||||
aiAnimMesh->mNumVertices, aiAnimMesh->HasTangentsAndBitangents(), uvSetUsedFlags, colorSetUsedFlags);
|
||||
|
||||
for (int vertIdx = 0; vertIdx < aiAnimMesh->mNumVertices; ++vertIdx)
|
||||
{
|
||||
AZ::Vector3 vertex(AssImpSDKWrapper::AssImpTypeConverter::ToVector3(aiAnimMesh->mVertices[vertIdx]));
|
||||
|
||||
context.m_sourceSceneSystem.SwapVec3ForUpAxis(vertex);
|
||||
context.m_sourceSceneSystem.ConvertUnit(vertex);
|
||||
|
||||
blendShapeData->AddPosition(vertex);
|
||||
blendShapeData->SetVertexIndexToControlPointIndexMap(vertIdx + vertexOffset, vertIdx + vertexOffset);
|
||||
|
||||
// Add normals
|
||||
if (aiAnimMesh->HasNormals())
|
||||
{
|
||||
AZ::Vector3 normal(AssImpSDKWrapper::AssImpTypeConverter::ToVector3(aiAnimMesh->mNormals[vertIdx]));
|
||||
context.m_sourceSceneSystem.SwapVec3ForUpAxis(normal);
|
||||
normal.NormalizeSafe();
|
||||
blendShapeData->AddNormal(normal);
|
||||
}
|
||||
|
||||
// Add tangents and bitangents
|
||||
if (aiAnimMesh->HasTangentsAndBitangents())
|
||||
{
|
||||
// Vector4's constructor that takes in a vector3 sets w to 1.0f automatically.
|
||||
const AZ::Vector4 tangent(AssImpSDKWrapper::AssImpTypeConverter::ToVector3(aiAnimMesh->mTangents[vertIdx]));
|
||||
const AZ::Vector3 bitangent = AssImpSDKWrapper::AssImpTypeConverter::ToVector3(aiAnimMesh->mBitangents[vertIdx]);
|
||||
blendShapeData->AddTangentAndBitangent(tangent, bitangent);
|
||||
}
|
||||
|
||||
// Add UVs
|
||||
for (AZ::u8 uvSetIdx = 0; uvSetIdx < SceneData::GraphData::BlendShapeData::MaxNumUVSets; ++uvSetIdx)
|
||||
{
|
||||
if (aiAnimMesh->HasTextureCoords(uvSetIdx))
|
||||
{
|
||||
const AZ::Vector2 vertexUV(
|
||||
aiAnimMesh->mTextureCoords[uvSetIdx][vertIdx].x,
|
||||
// The engine's V coordinate is reverse of how it's stored in assImp.
|
||||
1.0f - aiAnimMesh->mTextureCoords[uvSetIdx][vertIdx].y);
|
||||
blendShapeData->AddUV(vertexUV, uvSetIdx);
|
||||
}
|
||||
}
|
||||
|
||||
// Add colors
|
||||
for (AZ::u8 colorSetIdx = 0; colorSetIdx < SceneData::GraphData::BlendShapeData::MaxNumColorSets; ++colorSetIdx)
|
||||
{
|
||||
if (aiAnimMesh->HasVertexColors(colorSetIdx))
|
||||
{
|
||||
SceneAPI::DataTypes::Color color =
|
||||
AssImpSDKWrapper::AssImpTypeConverter::ToColor(aiAnimMesh->mColors[colorSetIdx][vertIdx]);
|
||||
blendShapeData->AddColor(color, colorSetIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// aiAnimMesh just has a list of positions for vertices. The face indices are on the original mesh.
|
||||
for (int faceIdx = 0; faceIdx < aiMesh->mNumFaces; ++faceIdx)
|
||||
{
|
||||
aiFace face = aiMesh->mFaces[faceIdx];
|
||||
DataTypes::IBlendShapeData::Face blendFace;
|
||||
if (face.mNumIndices != 3)
|
||||
{
|
||||
// AssImp should have triangulated everything, so if this happens then someone has
|
||||
// probably changed AssImp's import settings. The engine only supports triangles.
|
||||
AZ_Error(
|
||||
Utilities::ErrorWindow, false,
|
||||
"Mesh for node %s has a face with %d vertices, only 3 vertices are supported per face.",
|
||||
nodeName.c_str(),
|
||||
face.mNumIndices);
|
||||
continue;
|
||||
}
|
||||
for (int idx = 0; idx < face.mNumIndices; ++idx)
|
||||
{
|
||||
blendFace.vertexIndex[idx] = face.mIndices[idx] + vertexOffset;
|
||||
}
|
||||
|
||||
blendShapeData->AddFace(blendFace);
|
||||
}
|
||||
vertexOffset += aiMesh->mNumVertices;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Report problem if no vertex or face converted to MeshData
|
||||
if (blendShapeData->GetVertexCount() <= 0 || blendShapeData->GetFaceCount() <= 0)
|
||||
{
|
||||
AZ_Error(Utilities::ErrorWindow, false, "Missing geometry data in blendshape node %s.", nodeName.c_str());
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
Containers::SceneGraph::NodeIndex newIndex =
|
||||
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
|
||||
|
||||
Events::ProcessingResult blendShapeResult;
|
||||
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, blendShapeData, newIndex, nodeName);
|
||||
blendShapeResult = Events::Process(dataPopulated);
|
||||
|
||||
if (blendShapeResult != Events::ProcessingResult::Failure)
|
||||
{
|
||||
blendShapeResult = AddAttributeDataNodeWithContexts(dataPopulated);
|
||||
}
|
||||
combinedBlendShapeResult += blendShapeResult;
|
||||
}
|
||||
|
||||
return combinedBlendShapeResult.GetResult();
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpBlendShapeImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpBlendShapeImporter, "{B0F7174B-9863-4C03-BFB2-83BF29B1A2DD}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpBlendShapeImporter();
|
||||
~AssImpBlendShapeImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportBlendShapes(AssImpSceneNodeAppendedContext& context);
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneSystem.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpBoneImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneData/GraphData/BoneData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/RootBoneData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpTypeConverter.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
AssImpBoneImporter::AssImpBoneImporter()
|
||||
{
|
||||
BindToCall(&AssImpBoneImporter::ImportBone);
|
||||
}
|
||||
|
||||
void AssImpBoneImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpBoneImporter, SceneCore::LoadingComponent>()->Version(2);
|
||||
}
|
||||
}
|
||||
|
||||
void MakeBoneMap(const aiScene* scene, AZStd::unordered_map<AZStd::string, const aiBone*>& boneLookup)
|
||||
{
|
||||
AZStd::queue<const aiNode*> queue;
|
||||
AZStd::unordered_set<AZStd::string> nodesWithNoMesh;
|
||||
|
||||
queue.push(scene->mRootNode);
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
const aiNode* currentNode = queue.front();
|
||||
queue.pop();
|
||||
|
||||
if (currentNode->mNumMeshes == 0)
|
||||
{
|
||||
nodesWithNoMesh.emplace(currentNode->mName.C_Str());
|
||||
}
|
||||
|
||||
for (int childIndex = 0; childIndex < currentNode->mNumChildren; ++childIndex)
|
||||
{
|
||||
queue.push(currentNode->mChildren[childIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[meshIndex];
|
||||
|
||||
for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
|
||||
{
|
||||
const aiBone* bone = mesh->mBones[boneIndex];
|
||||
|
||||
if (nodesWithNoMesh.contains(bone->mName.C_Str()))
|
||||
{
|
||||
boneLookup.emplace(bone->mName.C_Str(), bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aiMatrix4x4 CalculateWorldTransform(const aiNode* currentNode)
|
||||
{
|
||||
aiMatrix4x4 transform = {};
|
||||
const aiNode* iteratingNode = currentNode;
|
||||
|
||||
while (iteratingNode)
|
||||
{
|
||||
transform = iteratingNode->mTransformation * transform;
|
||||
iteratingNode = iteratingNode->mParent;
|
||||
}
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpBoneImporter::ImportBone(AssImpNodeEncounteredContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Bone");
|
||||
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
if (IsPivotNode(currentNode->mName))
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
bool isBone = false;
|
||||
|
||||
{
|
||||
AZStd::unordered_map<AZStd::string, const aiBone*> boneLookup;
|
||||
MakeBoneMap(scene, boneLookup);
|
||||
|
||||
isBone = boneLookup.contains(currentNode->mName.C_Str());
|
||||
|
||||
// If we have an animation, the bones will be listed in there
|
||||
if (!isBone)
|
||||
{
|
||||
for(unsigned animIndex = 0; animIndex < scene->mNumAnimations; ++animIndex)
|
||||
{
|
||||
aiAnimation* animation = scene->mAnimations[animIndex];
|
||||
|
||||
for (unsigned channelIndex = 0; channelIndex < animation->mNumChannels; ++channelIndex)
|
||||
{
|
||||
aiNodeAnim* nodeAnim = animation->mChannels[channelIndex];
|
||||
|
||||
if (nodeAnim->mNodeName == currentNode->mName)
|
||||
{
|
||||
isBone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isBone)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!isBone)
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
// If the current scene node (our eventual parent) contains bone data, we are not a root bone
|
||||
AZStd::shared_ptr<SceneData::GraphData::BoneData> createdBoneData;
|
||||
|
||||
if (NodeHasAncestorOfType(
|
||||
context.m_scene.GetGraph(), context.m_currentGraphPosition, DataTypes::IBoneData::TYPEINFO_Uuid()))
|
||||
{
|
||||
createdBoneData = AZStd::make_shared<SceneData::GraphData::BoneData>();
|
||||
}
|
||||
else
|
||||
{
|
||||
createdBoneData = AZStd::make_shared<SceneData::GraphData::RootBoneData>();
|
||||
}
|
||||
|
||||
aiMatrix4x4 transform = CalculateWorldTransform(currentNode);
|
||||
|
||||
SceneAPI::DataTypes::MatrixType globalTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(transform);
|
||||
|
||||
context.m_sourceSceneSystem.SwapTransformForUpAxis(globalTransform);
|
||||
context.m_sourceSceneSystem.ConvertBoneUnit(globalTransform);
|
||||
|
||||
createdBoneData->SetWorldTransform(globalTransform);
|
||||
|
||||
context.m_createdData.push_back(AZStd::move(createdBoneData));
|
||||
|
||||
return Events::ProcessingResult::Success;
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpBoneImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpBoneImporter, "{E7A62DE7-B660-4920-BF91-32738175D5A7}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpBoneImporter();
|
||||
~AssImpBoneImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportBone(AssImpNodeEncounteredContext& context);
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/numeric.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpColorStreamImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpTypeConverter.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshVertexColorData.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/mesh.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
const char* AssImpColorStreamImporter::m_defaultNodeName = "Col";
|
||||
|
||||
AssImpColorStreamImporter::AssImpColorStreamImporter()
|
||||
{
|
||||
BindToCall(&AssImpColorStreamImporter::ImportColorStreams);
|
||||
}
|
||||
|
||||
void AssImpColorStreamImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpColorStreamImporter, SceneCore::LoadingComponent>()->Version(3); // LYN-3250
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpColorStreamImporter::ImportColorStreams(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", m_defaultNodeName);
|
||||
if (!context.m_sourceNode.ContainsMesh())
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
// This node has at least one mesh, verify that the color channel counts are the same for all meshes.
|
||||
const int expectedColorChannels = scene->mMeshes[currentNode->mMeshes[0]]->GetNumColorChannels();
|
||||
const bool allMeshesHaveSameNumberOfColorChannels =
|
||||
AZStd::all_of(currentNode->mMeshes + 1, currentNode->mMeshes + currentNode->mNumMeshes, [scene, expectedColorChannels](const unsigned int meshIndex)
|
||||
{
|
||||
return scene->mMeshes[meshIndex]->GetNumColorChannels() == expectedColorChannels;
|
||||
});
|
||||
|
||||
AZ_Error(
|
||||
Utilities::ErrorWindow,
|
||||
allMeshesHaveSameNumberOfColorChannels,
|
||||
"Color channel counts for node %s has meshes with different color channel counts. "
|
||||
"The color channel count for the first mesh will be used, and placeholder incorrect color values "
|
||||
"will be generated to allow the data to process, but the source art needs to be fixed to correct this. "
|
||||
"All meshes on this node should have the same number of color channels.",
|
||||
currentNode->mName.C_Str());
|
||||
|
||||
if (expectedColorChannels == 0)
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
|
||||
|
||||
Events::ProcessingResultCombiner combinedVertexColorResults;
|
||||
for (int colorSetIndex = 0; colorSetIndex < expectedColorChannels; ++colorSetIndex)
|
||||
{
|
||||
|
||||
AZStd::shared_ptr<SceneData::GraphData::MeshVertexColorData> vertexColors =
|
||||
AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexColorData>();
|
||||
vertexColors->ReserveContainerSpace(vertexCount);
|
||||
|
||||
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
|
||||
for (int v = 0; v < mesh->mNumVertices; ++v)
|
||||
{
|
||||
if (colorSetIndex < mesh->GetNumColorChannels())
|
||||
{
|
||||
AZ::SceneAPI::DataTypes::Color vertexColor(
|
||||
AssImpSDKWrapper::AssImpTypeConverter::ToColor(mesh->mColors[colorSetIndex][v]));
|
||||
vertexColors->AppendColor(vertexColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// An error was already emitted if this mesh has less color channels
|
||||
// than other meshes on the parent node. Append an arbitrary color value, fully opaque black,
|
||||
// so the mesh can still be processed.
|
||||
// It's better to let the engine load a partially valid mesh than to completely fail.
|
||||
vertexColors->AppendColor(AZ::SceneAPI::DataTypes::Color(0.0f,0.0f,0.0f,1.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string nodeName(AZStd::string::format("%s%d", m_defaultNodeName, colorSetIndex));
|
||||
Containers::SceneGraph::NodeIndex newIndex =
|
||||
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
|
||||
|
||||
Events::ProcessingResult colorMapResults;
|
||||
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, vertexColors, newIndex, nodeName.c_str());
|
||||
colorMapResults = Events::Process(dataPopulated);
|
||||
|
||||
if (colorMapResults != Events::ProcessingResult::Failure)
|
||||
{
|
||||
colorMapResults = AddAttributeDataNodeWithContexts(dataPopulated);
|
||||
}
|
||||
|
||||
combinedVertexColorResults += colorMapResults;
|
||||
}
|
||||
return combinedVertexColorResults.GetResult();
|
||||
}
|
||||
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpColorStreamImporter : public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpColorStreamImporter, "{071F4764-F3B0-438A-9CB7-19A1248F3B54}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpColorStreamImporter();
|
||||
~AssImpColorStreamImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportColorStreams(AssImpSceneNodeAppendedContext& context);
|
||||
|
||||
protected:
|
||||
static const char* m_defaultNodeName;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpImporterUtilities.h>
|
||||
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
|
||||
#include <assimp/scene.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
bool IsSkinnedMesh(const aiNode& node, const aiScene& scene)
|
||||
{
|
||||
unsigned boneCount = 0;
|
||||
|
||||
for (unsigned mesh = 0; mesh < node.mNumMeshes; ++mesh)
|
||||
{
|
||||
if (scene.mMeshes[node.mMeshes[mesh]]->HasBones())
|
||||
{
|
||||
++boneCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (boneCount > 1 && boneCount != node.mNumMeshes)
|
||||
{
|
||||
AZ_Error("AssImpImporterUtilities", false, "Node has %d meshes but only %d are skinned. "
|
||||
"This is unexpected and may result in errors", node.mNumMeshes, boneCount);
|
||||
}
|
||||
|
||||
return boneCount > 0;
|
||||
}
|
||||
|
||||
bool IsPivotNode(const aiString& nodeName, size_t* pos)
|
||||
{
|
||||
AZStd::string_view name(nodeName.C_Str(), nodeName.length);
|
||||
|
||||
size_t myPos;
|
||||
|
||||
if (!pos)
|
||||
{
|
||||
pos = &myPos;
|
||||
}
|
||||
|
||||
*pos = AZ::StringFunc::Find(name, PivotNodeMarker);
|
||||
|
||||
return *pos != name.npos;
|
||||
}
|
||||
|
||||
void SplitPivotNodeName(const aiString& nodeName, size_t pivotPos, AZStd::string_view& baseNodeName, AZStd::string_view& pivotType)
|
||||
{
|
||||
AZStd::string_view nodeNameView(nodeName.data, nodeName.length);
|
||||
baseNodeName = AZStd::string_view(nodeNameView.substr(0, pivotPos));
|
||||
pivotType = AZStd::string_view(nodeNameView.substr(pivotPos + sizeof PivotNodeMarker - 1));
|
||||
}
|
||||
|
||||
aiMatrix4x4 GetConcatenatedLocalTransform(const aiNode* currentNode)
|
||||
{
|
||||
const aiNode* parent = currentNode->mParent;
|
||||
aiMatrix4x4 combinedTransform = currentNode->mTransformation;
|
||||
|
||||
while (parent)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
if (IsPivotNode(parent->mName, &pos))
|
||||
{
|
||||
combinedTransform = parent->mTransformation * combinedTransform;
|
||||
parent = parent->mParent;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return combinedTransform;
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <assimp/matrix4x4.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
struct aiNode;
|
||||
struct aiScene;
|
||||
struct aiString;
|
||||
|
||||
|
||||
namespace AZ::SceneAPI::SceneBuilder
|
||||
{
|
||||
inline constexpr char PivotNodeMarker[] = "_$AssimpFbx$_";
|
||||
|
||||
bool IsSkinnedMesh(const aiNode& node, const aiScene& scene);
|
||||
|
||||
// Checks if a node name is a pivot node and optionally returns the position in the name of the pivot marker (for splitting out the parts later)
|
||||
bool IsPivotNode(const aiString& nodeName, size_t* pos = nullptr);
|
||||
|
||||
// Returns the bone name and type of a pivot node
|
||||
void SplitPivotNodeName(const aiString& nodeName, size_t pivotPos, AZStd::string_view& baseNodeName, AZStd::string_view& pivotType);
|
||||
|
||||
// Gets the entire, combined local transform for a node taking pivot nodes into account. When pivot nodes are not used, this just returns the node's transform
|
||||
aiMatrix4x4 GetConcatenatedLocalTransform(const aiNode* currentNode);
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpMaterialImporter.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpMaterialWrapper.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MaterialData.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
#include <SceneAPI/SceneCore/Events/ProcessingResult.h>
|
||||
#include <assimp/scene.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
AssImpMaterialImporter::AssImpMaterialImporter()
|
||||
{
|
||||
BindToCall(&AssImpMaterialImporter::ImportMaterials);
|
||||
}
|
||||
|
||||
void AssImpMaterialImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpMaterialImporter, SceneCore::LoadingComponent>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpMaterialImporter::ImportMaterials(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Material");
|
||||
if (!context.m_sourceNode.ContainsMesh())
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
Events::ProcessingResultCombiner combinedMaterialImportResults;
|
||||
|
||||
AZStd::unordered_map<int, AZStd::shared_ptr<SceneData::GraphData::MaterialData>> materialMap;
|
||||
for (int idx = 0; idx < context.m_sourceNode.m_assImpNode->mNumMeshes; ++idx)
|
||||
{
|
||||
int meshIndex = context.m_sourceNode.m_assImpNode->mMeshes[idx];
|
||||
const aiMesh* assImpMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[meshIndex];
|
||||
AZ_Assert(assImpMesh, "Asset Importer Mesh should not be null.");
|
||||
int materialIndex = assImpMesh->mMaterialIndex;
|
||||
AZ_TraceContext("Material Index", materialIndex);
|
||||
|
||||
auto matFound = materialMap.find(materialIndex);
|
||||
|
||||
AZStd::shared_ptr<SceneData::GraphData::MaterialData> material;
|
||||
AZStd::string materialName;
|
||||
|
||||
if (matFound == materialMap.end())
|
||||
{
|
||||
std::shared_ptr<AssImpSDKWrapper::AssImpMaterialWrapper> assImpMaterial =
|
||||
std::shared_ptr<AssImpSDKWrapper::AssImpMaterialWrapper>(new AssImpSDKWrapper::AssImpMaterialWrapper(context.m_sourceScene.GetAssImpScene()->mMaterials[materialIndex]));
|
||||
|
||||
materialName = assImpMaterial->GetName().c_str();
|
||||
RenamedNodesMap::SanitizeNodeName(materialName, context.m_scene.GetGraph(), context.m_currentGraphPosition, "Material");
|
||||
AZ_TraceContext("Material Name", materialName);
|
||||
|
||||
material = AZStd::make_shared<SceneData::GraphData::MaterialData>();
|
||||
|
||||
material->SetMaterialName(assImpMaterial->GetName());
|
||||
material->SetTexture(DataTypes::IMaterialData::TextureMapType::Diffuse,
|
||||
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
|
||||
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Diffuse)).c_str());
|
||||
material->SetTexture(DataTypes::IMaterialData::TextureMapType::Specular,
|
||||
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
|
||||
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Specular)).c_str());
|
||||
material->SetTexture(DataTypes::IMaterialData::TextureMapType::Bump,
|
||||
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
|
||||
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Bump)).c_str());
|
||||
material->SetTexture(DataTypes::IMaterialData::TextureMapType::Normal,
|
||||
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
|
||||
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Normal)).c_str());
|
||||
material->SetUniqueId(assImpMaterial->GetUniqueId());
|
||||
material->SetDiffuseColor(assImpMaterial->GetDiffuseColor());
|
||||
material->SetSpecularColor(assImpMaterial->GetSpecularColor());
|
||||
material->SetEmissiveColor(assImpMaterial->GetEmissiveColor());
|
||||
material->SetShininess(assImpMaterial->GetShininess());
|
||||
|
||||
material->SetUseColorMap(assImpMaterial->GetUseColorMap());
|
||||
material->SetBaseColor(assImpMaterial->GetBaseColor());
|
||||
|
||||
material->SetUseMetallicMap(assImpMaterial->GetUseMetallicMap());
|
||||
material->SetMetallicFactor(assImpMaterial->GetMetallicFactor());
|
||||
material->SetTexture(
|
||||
DataTypes::IMaterialData::TextureMapType::Metallic,
|
||||
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
|
||||
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Metallic).c_str()));
|
||||
|
||||
material->SetUseRoughnessMap(assImpMaterial->GetUseRoughnessMap());
|
||||
material->SetRoughnessFactor(assImpMaterial->GetRoughnessFactor());
|
||||
material->SetTexture(
|
||||
DataTypes::IMaterialData::TextureMapType::Roughness,
|
||||
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
|
||||
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Roughness).c_str()));
|
||||
|
||||
material->SetUseEmissiveMap(assImpMaterial->GetUseEmissiveMap());
|
||||
material->SetEmissiveIntensity(assImpMaterial->GetEmissiveIntensity());
|
||||
|
||||
material->SetUseAOMap(assImpMaterial->GetUseAOMap());
|
||||
material->SetTexture(
|
||||
DataTypes::IMaterialData::TextureMapType::AmbientOcclusion,
|
||||
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
|
||||
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::AmbientOcclusion).c_str()));
|
||||
|
||||
material->SetTexture(
|
||||
DataTypes::IMaterialData::TextureMapType::Emissive,
|
||||
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
|
||||
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Emissive).c_str()));
|
||||
|
||||
material->SetTexture(
|
||||
DataTypes::IMaterialData::TextureMapType::BaseColor,
|
||||
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
|
||||
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::BaseColor).c_str()));
|
||||
|
||||
AZ_Assert(material, "Failed to allocate scene material data.");
|
||||
if (!material)
|
||||
{
|
||||
combinedMaterialImportResults += Events::ProcessingResult::Failure;
|
||||
continue;
|
||||
}
|
||||
|
||||
materialMap[materialIndex] = material;
|
||||
}
|
||||
else
|
||||
{
|
||||
material = matFound->second;
|
||||
materialName = material.get()->GetMaterialName();
|
||||
}
|
||||
|
||||
Events::ProcessingResult materialResult;
|
||||
Containers::SceneGraph::NodeIndex newIndex =
|
||||
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, materialName.c_str());
|
||||
|
||||
AZ_Assert(newIndex.IsValid(), "Failed to create SceneGraph node for attribute.");
|
||||
if (!newIndex.IsValid())
|
||||
{
|
||||
combinedMaterialImportResults += Events::ProcessingResult::Failure;
|
||||
continue;
|
||||
}
|
||||
|
||||
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, material, newIndex, materialName);
|
||||
materialResult = Events::Process(dataPopulated);
|
||||
|
||||
if (materialResult != Events::ProcessingResult::Failure)
|
||||
{
|
||||
materialResult = SceneAPI::SceneBuilder::AddAttributeDataNodeWithContexts(dataPopulated);
|
||||
}
|
||||
|
||||
combinedMaterialImportResults += materialResult;
|
||||
}
|
||||
|
||||
return combinedMaterialImportResults.GetResult();
|
||||
}
|
||||
|
||||
AZStd::string AssImpMaterialImporter::ResolveTexturePath(const AZStd::string& sceneFilePath, const AZStd::string& textureFilePath) const
|
||||
{
|
||||
if (textureFilePath.empty())
|
||||
{
|
||||
return textureFilePath;
|
||||
}
|
||||
|
||||
|
||||
AZStd::string texturePathRelativeToScene;
|
||||
AZStd::string cleanedUpSceneFilePath(sceneFilePath);
|
||||
AZ::StringFunc::Path::StripFullName(cleanedUpSceneFilePath);
|
||||
AZ::StringFunc::Path::Join(cleanedUpSceneFilePath.c_str(), textureFilePath.c_str(), texturePathRelativeToScene);
|
||||
|
||||
// If the texture did start with marker to change directories upward, then it's relative to the scene file,
|
||||
// and that path needs to be resolved. It can't be resolved later. This was handled internally by FBX SDK,
|
||||
// but is not handled by AssImp.
|
||||
if (textureFilePath.starts_with(".."))
|
||||
{
|
||||
// Not checking for the file existing because it may not be there yet.
|
||||
return texturePathRelativeToScene;
|
||||
}
|
||||
|
||||
// The engine only supports relative paths to scan folders.
|
||||
// Scene files may have paths to textures, relative to the scene file. Check for those, first.
|
||||
if (AZ::IO::FileIOBase::GetInstance()->Exists(texturePathRelativeToScene.c_str()))
|
||||
{
|
||||
return texturePathRelativeToScene;
|
||||
}
|
||||
|
||||
|
||||
return textureFilePath;
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpMaterialImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpMaterialImporter, "{CD936FA9-17B8-40B9-AA3C-5F593BEFFC94}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpMaterialImporter();
|
||||
~AssImpMaterialImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportMaterials(AssImpSceneNodeAppendedContext& context);
|
||||
|
||||
private:
|
||||
AZStd::string ResolveTexturePath(const AZStd::string& sceneFilePath, const AZStd::string& textureFilePath) const;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assimp/scene.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpMeshImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
AssImpMeshImporter::AssImpMeshImporter()
|
||||
{
|
||||
BindToCall(&AssImpMeshImporter::ImportMesh);
|
||||
}
|
||||
|
||||
void AssImpMeshImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpMeshImporter, SceneCore::LoadingComponent>()->Version(2);
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpMeshImporter::ImportMesh(AssImpNodeEncounteredContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Mesh");
|
||||
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
if (!context.m_sourceNode.ContainsMesh() || IsSkinnedMesh(*currentNode, *scene))
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
if (BuildSceneMeshFromAssImpMesh(currentNode, scene, context.m_sourceSceneSystem, context.m_createdData, [] {
|
||||
return AZStd::make_shared<SceneData::GraphData::MeshData>();
|
||||
}))
|
||||
{
|
||||
return Events::ProcessingResult::Success;
|
||||
}
|
||||
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpMeshImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpMeshImporter, "{41611339-1D32-474A-A6A4-25CE4430AAFB}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpMeshImporter();
|
||||
~AssImpMeshImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportMesh(AssImpNodeEncounteredContext& context);
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assimp/scene.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpSkinImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SceneData/GraphData/SkinMeshData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
AssImpSkinImporter::AssImpSkinImporter()
|
||||
{
|
||||
BindToCall(&AssImpSkinImporter::ImportSkin);
|
||||
}
|
||||
|
||||
void AssImpSkinImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpSkinImporter, SceneCore::LoadingComponent>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpSkinImporter::ImportSkin(AssImpNodeEncounteredContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Skin");
|
||||
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
if (!context.m_sourceNode.ContainsMesh() || !IsSkinnedMesh(*currentNode, *scene))
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
if (BuildSceneMeshFromAssImpMesh(currentNode, scene, context.m_sourceSceneSystem, context.m_createdData, [] {
|
||||
return AZStd::make_shared<SceneData::GraphData::SkinMeshData>();
|
||||
}))
|
||||
{
|
||||
return Events::ProcessingResult::Success;
|
||||
}
|
||||
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpSkinImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpSkinImporter, "{8FBCA725-C04E-42B7-9669-82DB3BB0901F}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpSkinImporter();
|
||||
~AssImpSkinImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportSkin(AssImpNodeEncounteredContext& context);
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpSkinWeightsImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
|
||||
#include <SceneAPI/SceneCore/Events/ImportEventContext.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/SkinWeightData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
const AZStd::string AssImpSkinWeightsImporter::s_skinWeightName = "SkinWeight_";
|
||||
|
||||
AssImpSkinWeightsImporter::AssImpSkinWeightsImporter()
|
||||
{
|
||||
BindToCall(&AssImpSkinWeightsImporter::ImportSkinWeights);
|
||||
BindToCall(&AssImpSkinWeightsImporter::SetupNamedBoneLinks);
|
||||
}
|
||||
|
||||
void AssImpSkinWeightsImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpSkinWeightsImporter, SceneCore::LoadingComponent>()->Version(3); // LYN-2576
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpSkinWeightsImporter::ImportSkinWeights(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Skin Weights");
|
||||
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
if(currentNode->mNumMeshes <= 0)
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
Events::ProcessingResultCombiner combinedSkinWeightsResult;
|
||||
|
||||
// Don't create this until a bone with weights is encountered
|
||||
Containers::SceneGraph::NodeIndex weightsIndexForMesh;
|
||||
AZStd::string skinWeightName;
|
||||
AZStd::shared_ptr<SceneData::GraphData::SkinWeightData> skinWeightData;
|
||||
|
||||
const uint64_t totalVertices = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
|
||||
|
||||
int vertexCount = 0;
|
||||
for(unsigned nodeMeshIndex = 0; nodeMeshIndex < currentNode->mNumMeshes; ++nodeMeshIndex)
|
||||
{
|
||||
int sceneMeshIndex = currentNode->mMeshes[nodeMeshIndex];
|
||||
const aiMesh* mesh = scene->mMeshes[sceneMeshIndex];
|
||||
|
||||
for(unsigned b = 0; b < mesh->mNumBones; ++b)
|
||||
{
|
||||
const aiBone* bone = mesh->mBones[b];
|
||||
|
||||
if(bone->mNumWeights <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!weightsIndexForMesh.IsValid())
|
||||
{
|
||||
skinWeightName = s_skinWeightName;
|
||||
RenamedNodesMap::SanitizeNodeName(skinWeightName, context.m_scene.GetGraph(), context.m_currentGraphPosition);
|
||||
|
||||
weightsIndexForMesh =
|
||||
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, skinWeightName.c_str());
|
||||
|
||||
AZ_Error("SkinWeightsImporter", weightsIndexForMesh.IsValid(), "Failed to create SceneGraph node for attribute.");
|
||||
if (!weightsIndexForMesh.IsValid())
|
||||
{
|
||||
combinedSkinWeightsResult += Events::ProcessingResult::Failure;
|
||||
continue;
|
||||
}
|
||||
skinWeightData = AZStd::make_shared<SceneData::GraphData::SkinWeightData>();
|
||||
}
|
||||
Pending pending;
|
||||
pending.m_bone = bone;
|
||||
pending.m_numVertices = totalVertices;
|
||||
pending.m_skinWeightData = skinWeightData;
|
||||
pending.m_vertOffset = vertexCount;
|
||||
m_pendingSkinWeights.push_back(pending);
|
||||
}
|
||||
vertexCount += mesh->mNumVertices;
|
||||
}
|
||||
|
||||
Events::ProcessingResult skinWeightsResult;
|
||||
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, skinWeightData, weightsIndexForMesh, skinWeightName);
|
||||
skinWeightsResult = Events::Process(dataPopulated);
|
||||
|
||||
if (skinWeightsResult != Events::ProcessingResult::Failure)
|
||||
{
|
||||
skinWeightsResult = AddAttributeDataNodeWithContexts(dataPopulated);
|
||||
}
|
||||
|
||||
combinedSkinWeightsResult += skinWeightsResult;
|
||||
|
||||
return combinedSkinWeightsResult.GetResult();
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpSkinWeightsImporter::SetupNamedBoneLinks(AssImpFinalizeSceneContext& /*context*/)
|
||||
{
|
||||
AZ_TraceContext("Importer", "Skin Weights");
|
||||
|
||||
for (auto& it : m_pendingSkinWeights)
|
||||
{
|
||||
it.m_skinWeightData->ResizeContainerSpace(it.m_numVertices);
|
||||
|
||||
AZStd::string boneName = it.m_bone->mName.C_Str();
|
||||
int boneId = it.m_skinWeightData->GetBoneId(boneName);
|
||||
|
||||
for(unsigned weight = 0; weight < it.m_bone->mNumWeights; ++weight)
|
||||
{
|
||||
DataTypes::ISkinWeightData::Link link;
|
||||
link.boneId = boneId;
|
||||
link.weight = it.m_bone->mWeights[weight].mWeight;
|
||||
|
||||
it.m_skinWeightData->AddAndSortLink(it.m_bone->mWeights[weight].mVertexId + it.m_vertOffset, link);
|
||||
}
|
||||
}
|
||||
const auto result = m_pendingSkinWeights.empty() ? Events::ProcessingResult::Ignored : Events::ProcessingResult::Success;
|
||||
m_pendingSkinWeights.clear();
|
||||
return result;
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <assimp/scene.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
#include <SceneAPI/SceneCore/DataTypes/GraphData/ISkinWeightData.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneData
|
||||
{
|
||||
namespace GraphData
|
||||
{
|
||||
class SkinWeightData;
|
||||
}
|
||||
}
|
||||
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace Events
|
||||
{
|
||||
class PostImportEventContext;
|
||||
}
|
||||
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpSkinWeightsImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpSkinWeightsImporter, "{79B5E863-C155-473A-BC0D-B85F8D8303EB}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpSkinWeightsImporter();
|
||||
~AssImpSkinWeightsImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportSkinWeights(AssImpSceneNodeAppendedContext& context);
|
||||
Events::ProcessingResult SetupNamedBoneLinks(AssImpFinalizeSceneContext& context);
|
||||
|
||||
protected:
|
||||
struct Pending
|
||||
{
|
||||
const aiBone* m_bone = nullptr;
|
||||
unsigned m_numVertices = 0;
|
||||
unsigned m_vertOffset = 0;
|
||||
AZStd::shared_ptr<SceneData::GraphData::SkinWeightData> m_skinWeightData;
|
||||
};
|
||||
|
||||
//! List of skin weights that still need to be filled in. Setting the data for skin weights is
|
||||
//! delayed until after the tree has been fully constructed as bones are linked by name, but until
|
||||
//! the graph has been fully filled in, those names can change which would break the names recorded
|
||||
//! for the skin.
|
||||
AZStd::vector<Pending> m_pendingSkinWeights;
|
||||
|
||||
static const AZStd::string s_skinWeightName;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/numeric.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpTangentStreamImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshVertexTangentData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpTypeConverter.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/mesh.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
const char* AssImpTangentStreamImporter::m_defaultNodeName = "Tangent";
|
||||
|
||||
AssImpTangentStreamImporter::AssImpTangentStreamImporter()
|
||||
{
|
||||
BindToCall(&AssImpTangentStreamImporter::ImportTangentStreams);
|
||||
}
|
||||
|
||||
void AssImpTangentStreamImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpTangentStreamImporter, SceneCore::LoadingComponent>()->Version(3); // LYN-3250
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpTangentStreamImporter::ImportTangentStreams(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", m_defaultNodeName);
|
||||
if (!context.m_sourceNode.ContainsMesh())
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
const auto meshHasTangentsAndBitangents = [&scene](const unsigned int meshIndex)
|
||||
{
|
||||
return scene->mMeshes[meshIndex]->HasTangentsAndBitangents();
|
||||
};
|
||||
|
||||
// If there are no tangents on any meshes, there's nothing to import in this function.
|
||||
const bool anyMeshHasTangentsAndBitangents = AZStd::any_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents);
|
||||
if (!anyMeshHasTangentsAndBitangents)
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
// AssImp nodes with multiple meshes on them occur when AssImp split a mesh on material.
|
||||
// This logic recombines those meshes to minimize the changes needed to replace FBX SDK with AssImp, FBX SDK did not separate meshes,
|
||||
// and the engine has code to do this later.
|
||||
const bool allMeshesHaveTangentsAndBitangents = AZStd::all_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents);
|
||||
if (!allMeshesHaveTangentsAndBitangents)
|
||||
{
|
||||
AZ_Error(
|
||||
Utilities::ErrorWindow, false,
|
||||
"Node with name %s has meshes with and without tangents. "
|
||||
"Placeholder incorrect tangents will be generated to allow the data to process, "
|
||||
"but the source art needs to be fixed to correct this. Either apply tangents to all meshes on this node, "
|
||||
"or remove all tangents from all meshes on this node.",
|
||||
currentNode->mName.C_Str());
|
||||
}
|
||||
|
||||
const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
|
||||
|
||||
AZStd::shared_ptr<SceneData::GraphData::MeshVertexTangentData> tangentStream =
|
||||
AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexTangentData>();
|
||||
// AssImp only has one tangentStream per mesh.
|
||||
tangentStream->SetTangentSetIndex(0);
|
||||
|
||||
tangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
|
||||
tangentStream->ReserveContainerSpace(vertexCount);
|
||||
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
|
||||
|
||||
for (int v = 0; v < mesh->mNumVertices; ++v)
|
||||
{
|
||||
if (!mesh->HasTangentsAndBitangents())
|
||||
{
|
||||
// This node has mixed meshes with and without tangents.
|
||||
// An error was already thrown above. Output stub tangents so
|
||||
// the mesh can still be output in some form, even if the data isn't correct.
|
||||
// The tangent count needs to match the vertex count on the associated mesh node.
|
||||
tangentStream->AppendTangent(Vector4(0.f, 1.f, 0.f, 1.f));
|
||||
}
|
||||
else
|
||||
{
|
||||
const Vector4 tangent(
|
||||
AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mTangents[v]));
|
||||
tangentStream->AppendTangent(tangent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Containers::SceneGraph::NodeIndex newIndex =
|
||||
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, m_defaultNodeName);
|
||||
|
||||
Events::ProcessingResult tangentResults;
|
||||
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, tangentStream, newIndex, m_defaultNodeName);
|
||||
tangentResults = Events::Process(dataPopulated);
|
||||
|
||||
if (tangentResults != Events::ProcessingResult::Failure)
|
||||
{
|
||||
tangentResults = AddAttributeDataNodeWithContexts(dataPopulated);
|
||||
}
|
||||
return tangentResults;
|
||||
}
|
||||
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpTangentStreamImporter : public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpTangentStreamImporter, "{AB2D1D1E-5A19-40AF-B4F4-C652DD578F43}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpTangentStreamImporter();
|
||||
~AssImpTangentStreamImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportTangentStreams(AssImpSceneNodeAppendedContext& context);
|
||||
|
||||
protected:
|
||||
static const char* m_defaultNodeName;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpTransformImporter.h>
|
||||
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneSystem.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
#include <SceneAPI/SceneData/GraphData/TransformData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpTypeConverter.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpImporterUtilities.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
const char* AssImpTransformImporter::s_transformNodeName = "transform";
|
||||
|
||||
AssImpTransformImporter::AssImpTransformImporter()
|
||||
{
|
||||
BindToCall(&AssImpTransformImporter::ImportTransform);
|
||||
}
|
||||
|
||||
void AssImpTransformImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpTransformImporter, SceneCore::LoadingComponent>()->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void GetAllBones(const aiScene* scene, AZStd::unordered_multimap<AZStd::string, const aiBone*>& boneLookup)
|
||||
{
|
||||
AZStd::queue<const aiNode*> queue;
|
||||
AZStd::unordered_set<AZStd::string> nodesWithNoMesh;
|
||||
|
||||
queue.push(scene->mRootNode);
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
const aiNode* currentNode = queue.front();
|
||||
queue.pop();
|
||||
|
||||
if (currentNode->mNumMeshes == 0)
|
||||
{
|
||||
nodesWithNoMesh.emplace(currentNode->mName.C_Str());
|
||||
}
|
||||
|
||||
for (int childIndex = 0; childIndex < currentNode->mNumChildren; ++childIndex)
|
||||
{
|
||||
queue.push(currentNode->mChildren[childIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[meshIndex];
|
||||
|
||||
for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
|
||||
{
|
||||
const aiBone* bone = mesh->mBones[boneIndex];
|
||||
|
||||
if (nodesWithNoMesh.contains(bone->mName.C_Str()))
|
||||
{
|
||||
boneLookup.emplace(bone->mName.C_Str(), bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpTransformImporter::ImportTransform(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", "transform");
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
if (currentNode == scene->mRootNode || IsPivotNode(currentNode->mName))
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
AZStd::unordered_multimap<AZStd::string, const aiBone*> boneLookup;
|
||||
GetAllBones(scene, boneLookup);
|
||||
|
||||
auto boneIterator = boneLookup.find(currentNode->mName.C_Str());
|
||||
const bool isBone = boneIterator != boneLookup.end();
|
||||
|
||||
DataTypes::MatrixType localTransform;
|
||||
|
||||
if (isBone)
|
||||
{
|
||||
AZStd::vector<DataTypes::MatrixType> offsets, inverseOffsets;
|
||||
auto iteratingNode = currentNode;
|
||||
|
||||
while (iteratingNode && boneLookup.count(iteratingNode->mName.C_Str()))
|
||||
{
|
||||
AZStd::string name = iteratingNode->mName.C_Str();
|
||||
|
||||
auto range = boneLookup.equal_range(name);
|
||||
|
||||
if (range.first != range.second)
|
||||
{
|
||||
// There can be multiple offsetMatrices for a given bone, we're only interested in grabbing the first one
|
||||
auto boneFirstOffsetMatrix = range.first->second->mOffsetMatrix;
|
||||
auto azMat = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(boneFirstOffsetMatrix);
|
||||
offsets.push_back(azMat);
|
||||
inverseOffsets.push_back(azMat.GetInverseFull());
|
||||
}
|
||||
|
||||
iteratingNode = iteratingNode->mParent;
|
||||
}
|
||||
|
||||
if (inverseOffsets.size() == 1)
|
||||
{
|
||||
// If this is the root bone, just use the inverseOffset, otherwise the equation below just results in the identity matrix
|
||||
localTransform = inverseOffsets[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
localTransform = offsets.at(1) // parent bone offset
|
||||
* inverseOffsets.at(inverseOffsets.size() - 1) // Inverse of root bone offset
|
||||
* offsets.at(offsets.size() - 1) // Root bone offset
|
||||
* inverseOffsets.at(0); // Inverse of current node offset
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(GetConcatenatedLocalTransform(currentNode));
|
||||
}
|
||||
|
||||
// Don't bother adding a node with the identity matrix
|
||||
if (localTransform == DataTypes::MatrixType::Identity())
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform);
|
||||
context.m_sourceSceneSystem.ConvertUnit(localTransform);
|
||||
|
||||
AZStd::shared_ptr<SceneData::GraphData::TransformData> transformData =
|
||||
AZStd::make_shared<SceneData::GraphData::TransformData>(localTransform);
|
||||
AZ_Error(SceneAPI::Utilities::ErrorWindow, transformData, "Failed to allocate transform data.");
|
||||
if (!transformData)
|
||||
{
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
// If it is non-endpoint data populated node, add a transform attribute
|
||||
if (context.m_scene.GetGraph().HasNodeContent(context.m_currentGraphPosition))
|
||||
{
|
||||
if (!context.m_scene.GetGraph().IsNodeEndPoint(context.m_currentGraphPosition))
|
||||
{
|
||||
AZStd::string nodeName = s_transformNodeName;
|
||||
RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition);
|
||||
AZ_TraceContext("Transform node name", nodeName);
|
||||
|
||||
Containers::SceneGraph::NodeIndex newIndex =
|
||||
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
|
||||
|
||||
AZ_Error(SceneAPI::Utilities::ErrorWindow, newIndex.IsValid(), "Failed to create SceneGraph node for attribute.");
|
||||
if (!newIndex.IsValid())
|
||||
{
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
Events::ProcessingResult transformAttributeResult;
|
||||
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, transformData, newIndex, nodeName);
|
||||
transformAttributeResult = Events::Process(dataPopulated);
|
||||
|
||||
if (transformAttributeResult != Events::ProcessingResult::Failure)
|
||||
{
|
||||
transformAttributeResult = AddAttributeDataNodeWithContexts(dataPopulated);
|
||||
}
|
||||
|
||||
return transformAttributeResult;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool addedData = context.m_scene.GetGraph().SetContent(context.m_currentGraphPosition, transformData);
|
||||
|
||||
AZ_Error(SceneAPI::Utilities::ErrorWindow, addedData, "Failed to add node data");
|
||||
return addedData ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpTransformImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpTransformImporter, "{A7494C53-5822-40EF-9B60-B1FF09FBFA59}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpTransformImporter();
|
||||
~AssImpTransformImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportTransform(AssImpSceneNodeAppendedContext& context);
|
||||
static const char* s_transformNodeName;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/numeric.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpUvMapImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshVertexUVData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/mesh.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
const char* AssImpUvMapImporter::m_defaultNodeName = "UV";
|
||||
|
||||
AssImpUvMapImporter::AssImpUvMapImporter()
|
||||
{
|
||||
BindToCall(&AssImpUvMapImporter::ImportUvMaps);
|
||||
}
|
||||
|
||||
void AssImpUvMapImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<AssImpUvMapImporter, SceneCore::LoadingComponent>()->Version(4); // LYN-3250
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult AssImpUvMapImporter::ImportUvMaps(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
AZ_TraceContext("Importer", m_defaultNodeName);
|
||||
if (!context.m_sourceNode.ContainsMesh())
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
|
||||
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
|
||||
|
||||
// AssImp separates meshes that have multiple materials.
|
||||
// This code re-combines them to match previous FBX SDK behavior,
|
||||
// so they can be separated by engine code instead.
|
||||
bool foundTextureCoordinates = false;
|
||||
AZStd::array<int, AI_MAX_NUMBER_OF_TEXTURECOORDS> meshesPerTextureCoordinateIndex = {};
|
||||
for (int localMeshIndex = 0; localMeshIndex < currentNode->mNumMeshes; ++localMeshIndex)
|
||||
{
|
||||
aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[localMeshIndex]];
|
||||
for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex)
|
||||
{
|
||||
if (!mesh->mTextureCoords[texCoordIndex])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
++meshesPerTextureCoordinateIndex[texCoordIndex];
|
||||
foundTextureCoordinates = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundTextureCoordinates)
|
||||
{
|
||||
return Events::ProcessingResult::Ignored;
|
||||
}
|
||||
|
||||
const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
|
||||
|
||||
for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex)
|
||||
{
|
||||
AZ_Error(
|
||||
Utilities::ErrorWindow,
|
||||
meshesPerTextureCoordinateIndex[texCoordIndex] == 0 ||
|
||||
meshesPerTextureCoordinateIndex[texCoordIndex] == currentNode->mNumMeshes,
|
||||
"Texture coordinate index %d for node %s is not on all meshes on this node. "
|
||||
"Placeholder arbitrary texture values will be generated to allow the data to process, but the source art "
|
||||
"needs to be fixed to correct this. All meshes on this node should have the same number of texture coordinate channels.",
|
||||
texCoordIndex,
|
||||
currentNode->mName.C_Str());
|
||||
}
|
||||
|
||||
Events::ProcessingResultCombiner combinedUvMapResults;
|
||||
for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex)
|
||||
{
|
||||
// No meshes have this texture coordinate index, skip it.
|
||||
if (meshesPerTextureCoordinateIndex[texCoordIndex] == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<SceneData::GraphData::MeshVertexUVData> uvMap =
|
||||
AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexUVData>();
|
||||
uvMap->ReserveContainerSpace(vertexCount);
|
||||
bool customNameFound = false;
|
||||
AZStd::string name(AZStd::string::format("%s%d", m_defaultNodeName, texCoordIndex));
|
||||
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
|
||||
if(mesh->mTextureCoords[texCoordIndex])
|
||||
{
|
||||
if (mesh->mTextureCoordsNames[texCoordIndex].length > 0)
|
||||
{
|
||||
if (!customNameFound)
|
||||
{
|
||||
name = mesh->mTextureCoordsNames[texCoordIndex].C_Str();
|
||||
customNameFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning(Utilities::WarningWindow,
|
||||
strcmp(name.c_str(), mesh->mTextureCoordsNames[texCoordIndex].C_Str()) == 0,
|
||||
"Node %s has conflicting mesh coordinate names at index %d, %s and %s. Using %s.",
|
||||
currentNode->mName.C_Str(),
|
||||
texCoordIndex,
|
||||
name.c_str(),
|
||||
mesh->mTextureCoordsNames[texCoordIndex].C_Str(),
|
||||
name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int v = 0; v < mesh->mNumVertices; ++v)
|
||||
{
|
||||
if (mesh->mTextureCoords[texCoordIndex])
|
||||
{
|
||||
AZ::Vector2 vertexUV(
|
||||
mesh->mTextureCoords[texCoordIndex][v].x,
|
||||
// The engine's V coordinate is reverse of how it's coming in from the SDK.
|
||||
1.0f - mesh->mTextureCoords[texCoordIndex][v].y);
|
||||
uvMap->AppendUV(vertexUV);
|
||||
}
|
||||
else
|
||||
{
|
||||
// An error was already emitted if the UV channels for all meshes on this node do not match.
|
||||
// Append an arbitrary UV value so that the mesh can still be processed.
|
||||
// It's better to let the engine load a partially valid mesh than to completely fail.
|
||||
uvMap->AppendUV(AZ::Vector2::CreateZero());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uvMap->SetCustomName(name.c_str());
|
||||
Containers::SceneGraph::NodeIndex newIndex =
|
||||
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, name.c_str());
|
||||
|
||||
Events::ProcessingResult uvMapResults;
|
||||
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, uvMap, newIndex, name);
|
||||
uvMapResults = Events::Process(dataPopulated);
|
||||
|
||||
if (uvMapResults != Events::ProcessingResult::Failure)
|
||||
{
|
||||
uvMapResults = AddAttributeDataNodeWithContexts(dataPopulated);
|
||||
}
|
||||
|
||||
combinedUvMapResults += uvMapResults;
|
||||
|
||||
}
|
||||
|
||||
return combinedUvMapResults.GetResult();
|
||||
}
|
||||
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class AssImpUvMapImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AssImpUvMapImporter, "{BF02F231-848B-4CDB-9B11-55EEE15CFAA6}", SceneCore::LoadingComponent);
|
||||
|
||||
AssImpUvMapImporter();
|
||||
~AssImpUvMapImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportUvMaps(AssImpSceneNodeAppendedContext& context);
|
||||
|
||||
protected:
|
||||
static const char* m_defaultNodeName;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
|
||||
#include <SceneAPI/SceneData/GraphData/AnimationData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/BoneData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MaterialData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshVertexColorData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshVertexUVData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/SkinWeightData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/TransformData.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
static const float g_sceneUtilityEqualityEpsilon = 0.001f;
|
||||
|
||||
CoreProcessingResult AddDataNodeWithContexts(SceneDataPopulatedContextBase& dataPopulated)
|
||||
{
|
||||
AZ_TraceContext("Node Name", dataPopulated.m_dataName);
|
||||
const char* nodeTypeName = dataPopulated.m_graphData ? dataPopulated.m_graphData->RTTI_GetTypeName() : "Null";
|
||||
AZ_TraceContext("Node Type", (!nodeTypeName || nodeTypeName[0] == '\0' ? "Null" : nodeTypeName));
|
||||
|
||||
Events::ProcessingResultCombiner nodeResults;
|
||||
nodeResults += Events::Process(dataPopulated);
|
||||
|
||||
dataPopulated.m_scene.GetGraph().SetContent(dataPopulated.m_currentGraphPosition,
|
||||
AZStd::move(dataPopulated.m_graphData));
|
||||
|
||||
if (azrtti_istypeof<AssImpSceneDataPopulatedContext>(dataPopulated))
|
||||
{
|
||||
AssImpSceneDataPopulatedContext* dataPopulatedContext = azrtti_cast<AssImpSceneDataPopulatedContext*>(&dataPopulated);
|
||||
AssImpSceneNodeAppendedContext nodeAppended(*dataPopulatedContext, dataPopulated.m_currentGraphPosition);
|
||||
nodeResults += Events::Process(nodeAppended);
|
||||
|
||||
AssImpSceneNodeAddedAttributesContext addedAttributes(nodeAppended);
|
||||
nodeResults += Events::Process(addedAttributes);
|
||||
|
||||
AssImpSceneNodeFinalizeContext finalizeNode(addedAttributes);
|
||||
nodeResults += Events::Process(finalizeNode);
|
||||
}
|
||||
|
||||
return nodeResults.GetResult();
|
||||
}
|
||||
|
||||
CoreProcessingResult AddAttributeDataNodeWithContexts(SceneAttributeDataPopulatedContextBase& dataPopulated)
|
||||
{
|
||||
AZ_TraceContext("Node Name", dataPopulated.m_dataName);
|
||||
const char* nodeTypeName = dataPopulated.m_graphData ? dataPopulated.m_graphData->RTTI_GetTypeName() : "Null";
|
||||
AZ_TraceContext("Node Type", (!nodeTypeName || nodeTypeName[0] == '\0' ? "Null" : nodeTypeName));
|
||||
|
||||
Events::ProcessingResultCombiner nodeResults;
|
||||
nodeResults += Events::Process(dataPopulated);
|
||||
|
||||
dataPopulated.m_scene.GetGraph().MakeEndPoint(dataPopulated.m_currentGraphPosition);
|
||||
|
||||
dataPopulated.m_scene.GetGraph().SetContent(dataPopulated.m_currentGraphPosition,
|
||||
AZStd::move(dataPopulated.m_graphData));
|
||||
if (azrtti_istypeof<AssImpSceneAttributeDataPopulatedContext>(dataPopulated))
|
||||
{
|
||||
AssImpSceneAttributeDataPopulatedContext* dataPopulatedContext = azrtti_cast<AssImpSceneAttributeDataPopulatedContext*>(&dataPopulated);
|
||||
AssImpSceneAttributeNodeAppendedContext nodeAppended(*dataPopulatedContext, dataPopulated.m_currentGraphPosition);
|
||||
nodeResults += Events::Process(nodeAppended);
|
||||
}
|
||||
|
||||
return nodeResults.GetResult();
|
||||
}
|
||||
|
||||
bool AreSceneGraphsEqual(const CoreSceneGraph& lhsGraph, const CoreSceneGraph& rhsGraph)
|
||||
{
|
||||
auto lhsContentStorage = lhsGraph.GetContentStorage();
|
||||
auto lhsNameStorage = lhsGraph.GetNameStorage();
|
||||
|
||||
auto lhsNameContentView = Containers::Views::MakePairView(lhsNameStorage, lhsContentStorage);
|
||||
Containers::SceneGraph::NodeIndex lhsRootIndex = lhsGraph.GetRoot();
|
||||
auto lhsDownwardView =
|
||||
Containers::Views::MakeSceneGraphDownwardsView<Containers::Views::BreadthFirst>(lhsGraph, lhsRootIndex,
|
||||
lhsNameContentView.begin(), true);
|
||||
|
||||
auto rhsContentStorage = rhsGraph.GetContentStorage();
|
||||
auto rhsNameStorage = rhsGraph.GetNameStorage();
|
||||
|
||||
auto rhsNameContentView = Containers::Views::MakePairView(rhsNameStorage, rhsContentStorage);
|
||||
Containers::SceneGraph::NodeIndex rhsRootIndex = rhsGraph.GetRoot();
|
||||
auto rhsDownwardView =
|
||||
Containers::Views::MakeSceneGraphDownwardsView<Containers::Views::BreadthFirst>(rhsGraph, rhsRootIndex,
|
||||
rhsNameContentView.begin(), true);
|
||||
|
||||
auto lhsIt = lhsDownwardView.begin();
|
||||
auto rhsIt = rhsDownwardView.begin();
|
||||
|
||||
while (lhsIt != lhsDownwardView.end() && rhsIt != rhsDownwardView.end())
|
||||
{
|
||||
if (!IsGraphDataEqual(lhsIt->second, rhsIt->second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (lhsIt->first != rhsIt->first)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
++lhsIt;
|
||||
++rhsIt;
|
||||
}
|
||||
|
||||
return (lhsIt == lhsDownwardView.end() && rhsIt == rhsDownwardView.end());
|
||||
}
|
||||
|
||||
bool operator==(const SceneData::GraphData::MeshData& lhs,
|
||||
const SceneData::GraphData::MeshData& rhs)
|
||||
{
|
||||
if (lhs.GetVertexCount() != rhs.GetVertexCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.HasNormalData() != rhs.HasNormalData())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.GetFaceCount() != rhs.GetFaceCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasNormals = lhs.HasNormalData();
|
||||
unsigned int vertexCount = lhs.GetVertexCount();
|
||||
for (unsigned int vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex)
|
||||
{
|
||||
if (lhs.GetPosition(vertexIndex) != rhs.GetPosition(vertexIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasNormals && (lhs.GetNormal(vertexIndex) != rhs.GetNormal(vertexIndex)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int faceCount = lhs.GetFaceCount();
|
||||
for (unsigned int faceIndex = 0; faceIndex < faceCount; ++faceIndex)
|
||||
{
|
||||
if (lhs.GetFaceMaterialId(faceIndex) != rhs.GetFaceMaterialId(faceIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.GetFaceInfo(faceIndex) != rhs.GetFaceInfo(faceIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const SceneData::GraphData::SkinWeightData& lhs,
|
||||
const SceneData::GraphData::SkinWeightData& rhs)
|
||||
{
|
||||
if (lhs.GetVertexCount() != rhs.GetVertexCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.GetBoneCount() != rhs.GetBoneCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t vertexCount = lhs.GetVertexCount();
|
||||
for (size_t vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex)
|
||||
{
|
||||
if (lhs.GetLinkCount(vertexIndex) != rhs.GetLinkCount(vertexIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t linkCount = lhs.GetLinkCount(vertexIndex);
|
||||
for (size_t linkIndex = 0; linkIndex < linkCount; ++linkIndex)
|
||||
{
|
||||
const DataTypes::ISkinWeightData::Link lhsLink = lhs.GetLink(vertexIndex, linkIndex);
|
||||
const DataTypes::ISkinWeightData::Link rhsLink = rhs.GetLink(vertexIndex, linkIndex);
|
||||
|
||||
if (lhsLink.boneId != rhsLink.boneId || !IsClose(lhsLink.weight, rhsLink.weight, g_sceneUtilityEqualityEpsilon))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.GetBoneName(lhsLink.boneId) != rhs.GetBoneName(rhsLink.boneId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const SceneData::GraphData::BoneData& lhs,
|
||||
const SceneData::GraphData::BoneData& rhs)
|
||||
{
|
||||
return (lhs.GetWorldTransform() == rhs.GetWorldTransform());
|
||||
}
|
||||
|
||||
bool operator==(const DataTypes::Color& lhs, const DataTypes::Color& rhs)
|
||||
{
|
||||
if (!IsClose(lhs.alpha, rhs.alpha, g_sceneUtilityEqualityEpsilon) ||
|
||||
!IsClose(lhs.blue, rhs.blue, g_sceneUtilityEqualityEpsilon) ||
|
||||
!IsClose(lhs.green, rhs.green, g_sceneUtilityEqualityEpsilon) ||
|
||||
!IsClose(lhs.red, rhs.red, g_sceneUtilityEqualityEpsilon))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const DataTypes::Color& lhs, const DataTypes::Color& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator==(const SceneData::GraphData::MeshVertexColorData& lhs,
|
||||
const SceneData::GraphData::MeshVertexColorData& rhs)
|
||||
{
|
||||
if (lhs.GetCount() != rhs.GetCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t colorCount = lhs.GetCount();
|
||||
for (size_t colorIndex = 0; colorIndex < colorCount; ++colorIndex)
|
||||
{
|
||||
if (lhs.GetColor(colorIndex) != rhs.GetColor(colorIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const SceneData::GraphData::MeshVertexUVData& lhs,
|
||||
const SceneData::GraphData::MeshVertexUVData& rhs)
|
||||
{
|
||||
if (lhs.GetCount() != rhs.GetCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
size_t uvCount = lhs.GetCount();
|
||||
for (size_t uvIndex = 0; uvIndex < uvCount; ++uvIndex)
|
||||
{
|
||||
if (lhs.GetUV(uvIndex) != rhs.GetUV(uvIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const SceneData::GraphData::MaterialData& lhs,
|
||||
const SceneData::GraphData::MaterialData& rhs)
|
||||
{
|
||||
if (lhs.IsNoDraw() != rhs.IsNoDraw())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.GetTexture(DataTypes::IMaterialData::TextureMapType::Diffuse) !=
|
||||
rhs.GetTexture(DataTypes::IMaterialData::TextureMapType::Diffuse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.GetTexture(DataTypes::IMaterialData::TextureMapType::Specular) !=
|
||||
rhs.GetTexture(DataTypes::IMaterialData::TextureMapType::Specular))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.GetTexture(DataTypes::IMaterialData::TextureMapType::Bump) !=
|
||||
rhs.GetTexture(DataTypes::IMaterialData::TextureMapType::Bump))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const SceneData::GraphData::TransformData& lhs,
|
||||
const SceneData::GraphData::TransformData& rhs)
|
||||
{
|
||||
return lhs.GetMatrix() == rhs.GetMatrix();
|
||||
}
|
||||
|
||||
bool operator==(const SceneData::GraphData::AnimationData& lhs,
|
||||
const SceneData::GraphData::AnimationData& rhs)
|
||||
{
|
||||
if (lhs.GetKeyFrameCount() != rhs.GetKeyFrameCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t keyFrameCount = lhs.GetKeyFrameCount();
|
||||
|
||||
for (size_t keyFrameIndex = 0; keyFrameIndex < keyFrameCount; ++keyFrameIndex)
|
||||
{
|
||||
if (lhs.GetKeyFrame(keyFrameIndex) != rhs.GetKeyFrame(keyFrameIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsGraphDataEqual(const AZStd::shared_ptr<const DataTypes::IGraphObject>& lhs,
|
||||
const AZStd::shared_ptr<const DataTypes::IGraphObject>& rhs)
|
||||
{
|
||||
// If both are null, they are considered equal
|
||||
if (!lhs && !rhs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If only one is null, they are considered not equal
|
||||
if (!lhs || !rhs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If they have disparate types they are considered not equal
|
||||
if (lhs->RTTI_GetType() != rhs->RTTI_GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs->RTTI_IsTypeOf(SceneData::GraphData::BoneData::TYPEINFO_Uuid()))
|
||||
{
|
||||
const SceneData::GraphData::BoneData* lhsBone =
|
||||
azrtti_cast<const SceneData::GraphData::BoneData*>(lhs.get());
|
||||
const SceneData::GraphData::BoneData* rhsBone =
|
||||
azrtti_cast<const SceneData::GraphData::BoneData*>(rhs.get());
|
||||
|
||||
return (*lhsBone == *rhsBone);
|
||||
}
|
||||
else if (lhs->RTTI_IsTypeOf(SceneData::GraphData::MeshData::TYPEINFO_Uuid()))
|
||||
{
|
||||
const SceneData::GraphData::MeshData* lhsMesh =
|
||||
azrtti_cast<const SceneData::GraphData::MeshData*>(lhs.get());
|
||||
const SceneData::GraphData::MeshData* rhsMesh =
|
||||
azrtti_cast<const SceneData::GraphData::MeshData*>(rhs.get());
|
||||
|
||||
return (*lhsMesh == *rhsMesh);
|
||||
}
|
||||
else if (lhs->RTTI_IsTypeOf(SceneData::GraphData::SkinWeightData::TYPEINFO_Uuid()))
|
||||
{
|
||||
const SceneData::GraphData::SkinWeightData* lhsSkinWeights =
|
||||
azrtti_cast<const SceneData::GraphData::SkinWeightData*>(lhs.get());
|
||||
const SceneData::GraphData::SkinWeightData* rhsSkinWeights =
|
||||
azrtti_cast<const SceneData::GraphData::SkinWeightData*>(rhs.get());
|
||||
|
||||
return (*lhsSkinWeights == *rhsSkinWeights);
|
||||
}
|
||||
else if (lhs->RTTI_IsTypeOf(SceneData::GraphData::MeshVertexColorData::TYPEINFO_Uuid()))
|
||||
{
|
||||
const SceneData::GraphData::MeshVertexColorData* lhsColorData =
|
||||
azrtti_cast<const SceneData::GraphData::MeshVertexColorData*>(lhs.get());
|
||||
const SceneData::GraphData::MeshVertexColorData* rhsColorData =
|
||||
azrtti_cast<const SceneData::GraphData::MeshVertexColorData*>(rhs.get());
|
||||
|
||||
return (*lhsColorData == *rhsColorData);
|
||||
}
|
||||
else if (lhs->RTTI_IsTypeOf(SceneData::GraphData::MeshVertexUVData::TYPEINFO_Uuid()))
|
||||
{
|
||||
const SceneData::GraphData::MeshVertexUVData* lhsUVData =
|
||||
azrtti_cast<const SceneData::GraphData::MeshVertexUVData*>(lhs.get());
|
||||
const SceneData::GraphData::MeshVertexUVData* rhsUVData =
|
||||
azrtti_cast<const SceneData::GraphData::MeshVertexUVData*>(rhs.get());
|
||||
|
||||
return (*lhsUVData == *rhsUVData);
|
||||
}
|
||||
else if (lhs->RTTI_IsTypeOf(SceneData::GraphData::MaterialData::TYPEINFO_Uuid()))
|
||||
{
|
||||
const SceneData::GraphData::MaterialData* lhsMaterialData =
|
||||
azrtti_cast<const SceneData::GraphData::MaterialData*>(lhs.get());
|
||||
const SceneData::GraphData::MaterialData* rhsMaterialData =
|
||||
azrtti_cast<const SceneData::GraphData::MaterialData*>(rhs.get());
|
||||
|
||||
return (*lhsMaterialData == *rhsMaterialData);
|
||||
}
|
||||
else if (lhs->RTTI_IsTypeOf(SceneData::GraphData::TransformData::TYPEINFO_Uuid()))
|
||||
{
|
||||
const SceneData::GraphData::TransformData* lhsTransform =
|
||||
azrtti_cast<const SceneData::GraphData::TransformData*>(lhs.get());
|
||||
const SceneData::GraphData::TransformData* rhsTransform =
|
||||
azrtti_cast<const SceneData::GraphData::TransformData*>(rhs.get());
|
||||
|
||||
return (*lhsTransform == *rhsTransform);
|
||||
}
|
||||
else if (lhs->RTTI_IsTypeOf(SceneData::GraphData::AnimationData::TYPEINFO_Uuid()))
|
||||
{
|
||||
const SceneData::GraphData::AnimationData* lhsAnimation =
|
||||
azrtti_cast<const SceneData::GraphData::AnimationData*>(lhs.get());
|
||||
const SceneData::GraphData::AnimationData* rhsAnimation =
|
||||
azrtti_cast<const SceneData::GraphData::AnimationData*>(rhs.get());
|
||||
|
||||
return (*lhsAnimation == *rhsAnimation);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/ImportContexts.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
|
||||
#include <SceneAPI/SceneCore/DataTypes/MatrixType.h>
|
||||
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
struct Uuid;
|
||||
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
struct SceneImportContext;
|
||||
|
||||
using CoreScene = Containers::Scene;
|
||||
using CoreSceneGraph = Containers::SceneGraph;
|
||||
using CoreGraphNodeIndex = Containers::SceneGraph::NodeIndex;
|
||||
using CoreProcessingResult = Events::ProcessingResult;
|
||||
|
||||
inline bool NodeIsOfType(const CoreSceneGraph& graph, CoreGraphNodeIndex nodeIndex, const AZ::Uuid& uuid);
|
||||
inline bool NodeParentIsOfType(const CoreSceneGraph& graph, CoreGraphNodeIndex nodeIndex,
|
||||
const AZ::Uuid& uuid);
|
||||
inline bool NodeHasAncestorOfType(const CoreSceneGraph& graph, CoreGraphNodeIndex nodeIndex,
|
||||
const AZ::Uuid& uuid);
|
||||
CoreProcessingResult AddDataNodeWithContexts(SceneDataPopulatedContextBase& dataContext);
|
||||
CoreProcessingResult AddAttributeDataNodeWithContexts(SceneAttributeDataPopulatedContextBase& dataContext);
|
||||
bool AreSceneGraphsEqual(const CoreSceneGraph& lhsGraph, const CoreSceneGraph& rhsGraph);
|
||||
inline bool AreScenesEqual(const CoreScene& lhs, const CoreScene& rhs);
|
||||
|
||||
bool IsGraphDataEqual(const AZStd::shared_ptr<const DataTypes::IGraphObject>& lhs,
|
||||
const AZStd::shared_ptr<const DataTypes::IGraphObject>& rhs);
|
||||
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.inl>
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
bool NodeIsOfType(const CoreSceneGraph& sceneGraph, CoreGraphNodeIndex nodeIndex, const AZ::Uuid& uuid)
|
||||
{
|
||||
if (nodeIndex.IsValid() && sceneGraph.HasNodeContent(nodeIndex) &&
|
||||
sceneGraph.GetNodeContent(nodeIndex)->RTTI_IsTypeOf(uuid))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeParentIsOfType(const CoreSceneGraph& sceneGraph, CoreGraphNodeIndex nodeIndex, const AZ::Uuid& uuid)
|
||||
{
|
||||
CoreGraphNodeIndex parentIndex = sceneGraph.GetNodeParent(nodeIndex);
|
||||
return NodeIsOfType(sceneGraph, parentIndex, uuid);
|
||||
}
|
||||
|
||||
bool NodeHasAncestorOfType(const CoreSceneGraph& sceneGraph, CoreGraphNodeIndex nodeIndex, const AZ::Uuid& uuid)
|
||||
{
|
||||
CoreGraphNodeIndex parentIndex = sceneGraph.GetNodeParent(nodeIndex);
|
||||
while (parentIndex.IsValid())
|
||||
{
|
||||
if (NodeIsOfType(sceneGraph, parentIndex, uuid))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
parentIndex = sceneGraph.GetNodeParent(parentIndex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AreScenesEqual(const CoreScene& lhs, const CoreScene& rhs)
|
||||
{
|
||||
if (lhs.GetGraph().GetNodeCount() != rhs.GetGraph().GetNodeCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AreSceneGraphsEqual(lhs.GetGraph(), rhs.GetGraph()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assimp/mesh.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/std/numeric.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneSystem.h>
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
#include <SceneAPI/SceneData/GraphData/BlendShapeData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/BoneData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
|
||||
namespace AZ::SceneAPI::SceneBuilder
|
||||
{
|
||||
bool BuildSceneMeshFromAssImpMesh(const aiNode* currentNode, const aiScene* scene, const SceneSystem& sceneSystem, AZStd::vector<AZStd::shared_ptr<DataTypes::IGraphObject>>& meshes,
|
||||
const AZStd::function<AZStd::shared_ptr<SceneData::GraphData::MeshData>()>& makeMeshFunc)
|
||||
{
|
||||
AZStd::unordered_map<int, int> assImpMatIndexToLYIndex;
|
||||
int lyMeshIndex = 0;
|
||||
|
||||
if(!currentNode || !scene)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto newMesh = makeMeshFunc();
|
||||
|
||||
newMesh->SetUnitSizeInMeters(sceneSystem.GetUnitSizeInMeters());
|
||||
newMesh->SetOriginalUnitSizeInMeters(sceneSystem.GetOriginalUnitSizeInMeters());
|
||||
|
||||
// AssImp separates meshes that have multiple materials.
|
||||
// This code re-combines them to match previous FBX SDK behavior,
|
||||
// so they can be separated by engine code instead.
|
||||
int vertOffset = 0;
|
||||
for (int m = 0; m < currentNode->mNumMeshes; ++m)
|
||||
{
|
||||
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[m]];
|
||||
|
||||
// Lumberyard materials are created in order based on mesh references in the scene
|
||||
if (assImpMatIndexToLYIndex.find(mesh->mMaterialIndex) == assImpMatIndexToLYIndex.end())
|
||||
{
|
||||
assImpMatIndexToLYIndex.insert(AZStd::pair<int, int>(mesh->mMaterialIndex, lyMeshIndex++));
|
||||
}
|
||||
|
||||
for (int vertIdx = 0; vertIdx < mesh->mNumVertices; ++vertIdx)
|
||||
{
|
||||
AZ::Vector3 vertex(mesh->mVertices[vertIdx].x, mesh->mVertices[vertIdx].y, mesh->mVertices[vertIdx].z);
|
||||
|
||||
sceneSystem.SwapVec3ForUpAxis(vertex);
|
||||
sceneSystem.ConvertUnit(vertex);
|
||||
newMesh->AddPosition(vertex);
|
||||
newMesh->SetVertexIndexToControlPointIndexMap(vertIdx + vertOffset, vertIdx + vertOffset);
|
||||
|
||||
if (mesh->HasNormals())
|
||||
{
|
||||
AZ::Vector3 normal(mesh->mNormals[vertIdx].x, mesh->mNormals[vertIdx].y, mesh->mNormals[vertIdx].z);
|
||||
sceneSystem.SwapVec3ForUpAxis(normal);
|
||||
normal.NormalizeSafe();
|
||||
newMesh->AddNormal(normal);
|
||||
}
|
||||
}
|
||||
|
||||
for (int faceIdx = 0; faceIdx < mesh->mNumFaces; ++faceIdx)
|
||||
{
|
||||
aiFace face = mesh->mFaces[faceIdx];
|
||||
AZ::SceneAPI::DataTypes::IMeshData::Face meshFace;
|
||||
if (face.mNumIndices != 3)
|
||||
{
|
||||
// AssImp should have triangulated everything, so if this happens then someone has
|
||||
// probably changed AssImp's import settings. The engine only supports triangles.
|
||||
AZ_Error(Utilities::ErrorWindow, false,
|
||||
"Mesh on node %s has a face with %d vertices, only 3 vertices are supported per face.",
|
||||
currentNode->mName.C_Str(),
|
||||
face.mNumIndices);
|
||||
continue;
|
||||
}
|
||||
for (int idx = 0; idx < face.mNumIndices; ++idx)
|
||||
{
|
||||
meshFace.vertexIndex[idx] = face.mIndices[idx] + vertOffset;
|
||||
}
|
||||
|
||||
newMesh->AddFace(meshFace, assImpMatIndexToLYIndex[mesh->mMaterialIndex]);
|
||||
}
|
||||
vertOffset += mesh->mNumVertices;
|
||||
|
||||
}
|
||||
meshes.push_back(newMesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GetMeshDataFromParentResult GetMeshDataFromParent(AssImpSceneNodeAppendedContext& context)
|
||||
{
|
||||
const DataTypes::IGraphObject* const parentData =
|
||||
context.m_scene.GetGraph().GetNodeContent(context.m_currentGraphPosition).get();
|
||||
|
||||
if (!parentData)
|
||||
{
|
||||
AZ_Error(Utilities::ErrorWindow, false,
|
||||
"GetMeshDataFromParent failed because the parent was null, it should only be called with a valid parent node");
|
||||
return AZ::Failure(Events::ProcessingResult::Failure);
|
||||
}
|
||||
|
||||
if (!parentData->RTTI_IsTypeOf(SceneData::GraphData::MeshData::TYPEINFO_Uuid()))
|
||||
{
|
||||
// The parent node may contain bone information and not mesh information, skip it.
|
||||
if (parentData->RTTI_IsTypeOf(SceneData::GraphData::BoneData::TYPEINFO_Uuid()))
|
||||
{
|
||||
// Return the ignore processing result in the failure.
|
||||
return AZ::Failure(Events::ProcessingResult::Ignored);
|
||||
}
|
||||
AZ_Error(Utilities::ErrorWindow, false,
|
||||
"Tried to get mesh data from parent for non-mesh parent data");
|
||||
return AZ::Failure(Events::ProcessingResult::Failure);
|
||||
}
|
||||
|
||||
const SceneData::GraphData::MeshData* const parentMeshData =
|
||||
azrtti_cast<const SceneData::GraphData::MeshData* const>(parentData);
|
||||
return AZ::Success(parentMeshData);
|
||||
}
|
||||
|
||||
uint64_t GetVertexCountForAllMeshesOnNode(const aiNode& node, const aiScene& scene)
|
||||
{
|
||||
return AZStd::accumulate(node.mMeshes, node.mMeshes + node.mNumMeshes, uint64_t{ 0u },
|
||||
[&scene](auto runningTotal, unsigned int meshIndex)
|
||||
{
|
||||
return runningTotal + scene.mMeshes[meshIndex]->mNumVertices;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneCore/Events/ProcessingResult.h>
|
||||
|
||||
struct aiNode;
|
||||
struct aiScene;
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneData
|
||||
{
|
||||
namespace GraphData
|
||||
{
|
||||
class MeshData;
|
||||
class BlendShapeData;
|
||||
}
|
||||
}
|
||||
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace DataTypes
|
||||
{
|
||||
class IGraphObject;
|
||||
}
|
||||
struct AssImpSceneNodeAppendedContext;
|
||||
class SceneSystem;
|
||||
|
||||
namespace SceneBuilder
|
||||
{
|
||||
bool BuildSceneMeshFromAssImpMesh(const aiNode* currentNode, const aiScene* scene, const SceneSystem& sceneSystem, AZStd::vector<AZStd::shared_ptr<DataTypes::IGraphObject>>& meshes,
|
||||
const AZStd::function<AZStd::shared_ptr<SceneData::GraphData::MeshData>()>& makeMeshFunc);
|
||||
|
||||
typedef AZ::Outcome<const SceneData::GraphData::MeshData* const, Events::ProcessingResult> GetMeshDataFromParentResult;
|
||||
GetMeshDataFromParentResult GetMeshDataFromParent(AssImpSceneNodeAppendedContext& context);
|
||||
|
||||
// If a node in the original scene file has a mesh with multiple materials on it, the associated AssImp
|
||||
// node will have multiple meshes on it, broken apart per material. This returns the total number
|
||||
// of vertices on all meshes on the given node.
|
||||
uint64_t GetVertexCountForAllMeshesOnNode(const aiNode& node, const aiScene& scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
bool RenamedNodesMap::SanitizeNodeName(AZStd::string& name, const Containers::SceneGraph& graph,
|
||||
Containers::SceneGraph::NodeIndex parentNode, const char* defaultName)
|
||||
{
|
||||
AZ_TraceContext("Node name", name);
|
||||
const AZStd::string originalNodeName(name);
|
||||
|
||||
bool isNameUpdated = false;
|
||||
// Nodes can't have an empty name, except of the root, otherwise nodes can't be referenced.
|
||||
if (name.empty())
|
||||
{
|
||||
name = defaultName;
|
||||
isNameUpdated = true;
|
||||
}
|
||||
|
||||
// The scene graph uses an arbitrary character (by default dot) to separate the names of the parents
|
||||
// therefore that character can't be used in the name.
|
||||
AZStd::replace_if(name.begin(), name.end(),
|
||||
[&isNameUpdated](char c) -> bool
|
||||
{
|
||||
if (c == Containers::SceneGraph::GetNodeSeperationCharacter())
|
||||
{
|
||||
isNameUpdated = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}, '_');
|
||||
|
||||
// Nodes under a particular parent have to be unique. Multiple nodes can share the same name, but they
|
||||
// can't reference the same parent in that case. This is to make sure the node can be quickly found as
|
||||
// the full path will be unique. To fix any issues, an index is appended.
|
||||
size_t index = 1;
|
||||
const size_t offset = name.length();
|
||||
while (graph.Find(parentNode, name).IsValid())
|
||||
{
|
||||
// Remove the previously tried extension.
|
||||
name.erase(offset, name.length() - offset);
|
||||
|
||||
name += ('_');
|
||||
name += AZStd::to_string(aznumeric_cast<u64>(index));
|
||||
index++;
|
||||
isNameUpdated = true;
|
||||
}
|
||||
|
||||
if (isNameUpdated)
|
||||
{
|
||||
AZ_TraceContext("New node name", name);
|
||||
AZ_TracePrintf(Utilities::WarningWindow, "The name of the node '%s' was invalid or conflicting and was updated to '%s'.",
|
||||
originalNodeName.c_str(), name.c_str());
|
||||
}
|
||||
|
||||
return isNameUpdated;
|
||||
}
|
||||
|
||||
bool RenamedNodesMap::RegisterNode(const std::shared_ptr<SDKNode::NodeWrapper>& node, const Containers::SceneGraph& graph,
|
||||
Containers::SceneGraph::NodeIndex parentNode, const char* defaultName)
|
||||
{
|
||||
return node ? RegisterNode(*node, graph, parentNode, defaultName) : false;
|
||||
}
|
||||
|
||||
bool RenamedNodesMap::RegisterNode(const std::shared_ptr<const SDKNode::NodeWrapper>& node, const Containers::SceneGraph& graph,
|
||||
Containers::SceneGraph::NodeIndex parentNode, const char* defaultName)
|
||||
{
|
||||
return node ? RegisterNode(*node, graph, parentNode, defaultName) : false;
|
||||
}
|
||||
|
||||
bool RenamedNodesMap::RegisterNode(const SDKNode::NodeWrapper& node, const Containers::SceneGraph& graph,
|
||||
Containers::SceneGraph::NodeIndex parentNode, const char* defaultName)
|
||||
{
|
||||
AZStd::string name = node.GetName();
|
||||
if (SanitizeNodeName(name, graph, parentNode, defaultName))
|
||||
{
|
||||
AZ_TraceContext("New node name", name);
|
||||
|
||||
// Only register if the name is updated, otherwise the name in the source scene's node can be returned.
|
||||
auto entry = m_idToName.find(node.GetUniqueId());
|
||||
if (entry == m_idToName.end())
|
||||
{
|
||||
m_idToName.insert(AZStd::make_pair(node.GetUniqueId(), AZStd::move(name)));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_TraceContext("Previous name", entry->second);
|
||||
if (entry->second == name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "Node has already been registered with a different name.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const char* RenamedNodesMap::GetNodeName(const std::shared_ptr<SDKNode::NodeWrapper>& node) const
|
||||
{
|
||||
return node ? GetNodeName(*node) : "<invalid>";
|
||||
}
|
||||
|
||||
const char* RenamedNodesMap::GetNodeName(const std::shared_ptr<const SDKNode::NodeWrapper>& node) const
|
||||
{
|
||||
return node ? GetNodeName(*node) : "<invalid>";
|
||||
}
|
||||
|
||||
const char* RenamedNodesMap::GetNodeName(const AZStd::shared_ptr<SDKNode::NodeWrapper>& node) const
|
||||
{
|
||||
return node ? GetNodeName(*node) : "<invalid>";
|
||||
}
|
||||
|
||||
const char* RenamedNodesMap::GetNodeName(const AZStd::shared_ptr<const SDKNode::NodeWrapper>& node) const
|
||||
{
|
||||
return node ? GetNodeName(*node) : "<invalid>";
|
||||
}
|
||||
|
||||
const char* RenamedNodesMap::GetNodeName(const SDKNode::NodeWrapper& node) const
|
||||
{
|
||||
auto entry = m_idToName.find(node.GetUniqueId());
|
||||
if (entry != m_idToName.end())
|
||||
{
|
||||
return entry->second.c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
return node.GetName();
|
||||
}
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
|
||||
#include <SceneAPI/SDKWrapper/NodeWrapper.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class RenamedNodesMap
|
||||
{
|
||||
public:
|
||||
//! Checks if the provided name is valid for the position in the graph and makes corrections if
|
||||
//! problems are found.
|
||||
//! @param name The name of the node in the scene graph.
|
||||
//! @param graph The scene graph the node will be added to.
|
||||
//! @param parentNode The node that will be the intended parent for the the node who's name is being checked.
|
||||
//! @param defaultName If the provided name is empty, the defaultName will be used.
|
||||
//! @return True if the name was updated otherwise false.
|
||||
static bool SanitizeNodeName(AZStd::string& name, const Containers::SceneGraph& graph,
|
||||
Containers::SceneGraph::NodeIndex parentNode, const char* defaultName = "unnamed");
|
||||
|
||||
//! Register the name for later reference. If the name needs to be sanitized, the sanitized name will be stored.
|
||||
//! @param node The node that's to be registered.
|
||||
//! @param graph The scene graph the node will be added to.
|
||||
//! @param parentNode The node that will be the intended parent for the the node who's name is being checked.
|
||||
//! @param defaultName If the provided name is empty, the defaultName will be used.
|
||||
//! @return True if the node was successfully registered.
|
||||
bool RegisterNode(const std::shared_ptr<SDKNode::NodeWrapper>& node, const Containers::SceneGraph& graph,
|
||||
Containers::SceneGraph::NodeIndex parentNode, const char* defaultName = "unnamed");
|
||||
bool RegisterNode(const std::shared_ptr<const SDKNode::NodeWrapper>& node, const Containers::SceneGraph& graph,
|
||||
Containers::SceneGraph::NodeIndex parentNode, const char* defaultName = "unnamed");
|
||||
bool RegisterNode(const SDKNode::NodeWrapper& node, const Containers::SceneGraph& graph,
|
||||
Containers::SceneGraph::NodeIndex parentNode, const char* defaultName = "unnamed");
|
||||
|
||||
//! Returns the name of the given node, which may be sanitized if this was needed.
|
||||
const char* GetNodeName(const std::shared_ptr<SDKNode::NodeWrapper>& node) const;
|
||||
const char* GetNodeName(const std::shared_ptr<const SDKNode::NodeWrapper>& node) const;
|
||||
const char* GetNodeName(const AZStd::shared_ptr<SDKNode::NodeWrapper>& node) const;
|
||||
const char* GetNodeName(const AZStd::shared_ptr<const SDKNode::NodeWrapper>& node) const;
|
||||
const char* GetNodeName(const SDKNode::NodeWrapper& node) const;
|
||||
private:
|
||||
|
||||
AZStd::unordered_map<u64, AZStd::string> m_idToName;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
../../../SceneCore/Containers/SceneManifest.h
|
||||
../../../SceneCore/Containers/SceneManifest.inl
|
||||
../../../SceneCore/Containers/SceneManifest.cpp
|
||||
../../../SceneCore/Events/AssetImportRequest.cpp
|
||||
../../../SceneCore/Events/AssetImportRequest.h
|
||||
../../../SceneCore/Events/CallProcessorBinder.cpp
|
||||
../../../SceneCore/Events/CallProcessorBinder.h
|
||||
../../../SceneData/GraphData/MeshVertexUVData.h
|
||||
../../../SceneData/GraphData/MeshVertexUVData.cpp
|
||||
../../../SceneData/GraphData/MeshVertexColorData.h
|
||||
../../../SceneData/GraphData/MeshVertexColorData.cpp
|
||||
../../../SceneData/GraphData/BoneData.h
|
||||
../../../SceneData/GraphData/BoneData.cpp
|
||||
../../../SceneData/GraphData/MeshData.h
|
||||
../../../SceneData/GraphData/MeshData.cpp
|
||||
../../../SceneData/GraphData/SkinWeightData.h
|
||||
../../../SceneData/GraphData/SkinWeightData.cpp
|
||||
../../../SceneData/GraphData/MaterialData.h
|
||||
../../../SceneData/GraphData/MaterialData.cpp
|
||||
../../../SceneData/GraphData/AnimationData.h
|
||||
../../../SceneData/GraphData/AnimationData.cpp
|
||||
../../../SceneData/GraphData/BlendShapeData.h
|
||||
../../../SceneData/GraphData/BlendShapeData.cpp
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
../../../SceneCore/Containers/SceneManifest.h
|
||||
../../../SceneCore/Containers/SceneManifest.inl
|
||||
../../../SceneCore/Containers/SceneManifest.cpp
|
||||
../../../SceneCore/Events/AssetImportRequest.cpp
|
||||
../../../SceneCore/Events/AssetImportRequest.h
|
||||
../../../SceneCore/Events/CallProcessorBinder.cpp
|
||||
../../../SceneCore/Events/CallProcessorBinder.h
|
||||
../../../SceneData/GraphData/MeshVertexUVData.h
|
||||
../../../SceneData/GraphData/MeshVertexUVData.cpp
|
||||
../../../SceneData/GraphData/MeshVertexColorData.h
|
||||
../../../SceneData/GraphData/MeshVertexColorData.cpp
|
||||
../../../SceneData/GraphData/BoneData.h
|
||||
../../../SceneData/GraphData/BoneData.cpp
|
||||
../../../SceneData/GraphData/MeshData.h
|
||||
../../../SceneData/GraphData/MeshData.cpp
|
||||
../../../SceneData/GraphData/SkinWeightData.h
|
||||
../../../SceneData/GraphData/SkinWeightData.cpp
|
||||
../../../SceneData/GraphData/MaterialData.h
|
||||
../../../SceneData/GraphData/MaterialData.cpp
|
||||
../../../SceneData/GraphData/AnimationData.h
|
||||
../../../SceneData/GraphData/AnimationData.cpp
|
||||
../../../SceneData/GraphData/BlendShapeData.h
|
||||
../../../SceneData/GraphData/BlendShapeData.cpp
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/PlatformDef.h>
|
||||
|
||||
#if defined(AZ_MONOLITHIC_BUILD)
|
||||
#define SCENE_BUILDER_API
|
||||
#else
|
||||
#ifdef SCENE_BUILDER_EXPORTS
|
||||
#define SCENE_BUILDER_API AZ_DLL_EXPORT
|
||||
#else
|
||||
#define SCENE_BUILDER_API AZ_DLL_IMPORT
|
||||
#endif
|
||||
#endif // AZ_MONOLITHIC_BUILD
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneImportRequestHandler.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
|
||||
#include <SceneAPI/SceneCore/Events/ImportEventContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
void SceneImporterSettings::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext)
|
||||
{
|
||||
serializeContext->Class<SceneImporterSettings>()
|
||||
->Version(2)
|
||||
->Field("SupportedFileTypeExtensions", &SceneImporterSettings::m_supportedFileTypeExtensions);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneImportRequestHandler::Activate()
|
||||
{
|
||||
if (auto* settingsRegistry = AZ::SettingsRegistry::Get())
|
||||
{
|
||||
settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter");
|
||||
}
|
||||
|
||||
BusConnect();
|
||||
}
|
||||
|
||||
void SceneImportRequestHandler::Deactivate()
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
|
||||
void SceneImportRequestHandler::Reflect(ReflectContext* context)
|
||||
{
|
||||
SceneImporterSettings::Reflect(context);
|
||||
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<SceneImportRequestHandler, AZ::Component>()->Version(1)->Attribute(
|
||||
AZ::Edit::Attributes::SystemComponentTags,
|
||||
AZStd::vector<AZ::Crc32>(
|
||||
{AssetBuilderSDK::ComponentTags::AssetBuilder,
|
||||
AssetImportRequest::GetAssetImportRequestComponentTag()}));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void SceneImportRequestHandler::GetSupportedFileExtensions(AZStd::unordered_set<AZStd::string>& extensions)
|
||||
{
|
||||
extensions.insert(m_settings.m_supportedFileTypeExtensions.begin(), m_settings.m_supportedFileTypeExtensions.end());
|
||||
}
|
||||
|
||||
Events::LoadingResult SceneImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, [[maybe_unused]] RequestingApplication requester)
|
||||
{
|
||||
AZStd::string extension;
|
||||
StringFunc::Path::GetExtension(path.c_str(), extension);
|
||||
AZStd::to_lower(extension.begin(), extension.end());
|
||||
|
||||
if (!m_settings.m_supportedFileTypeExtensions.contains(extension))
|
||||
{
|
||||
return Events::LoadingResult::Ignored;
|
||||
}
|
||||
|
||||
scene.SetSource(path, guid);
|
||||
|
||||
// Push contexts
|
||||
Events::ProcessingResultCombiner contextResult;
|
||||
contextResult += Events::Process<Events::PreImportEventContext>(path);
|
||||
contextResult += Events::Process<Events::ImportEventContext>(path, scene);
|
||||
contextResult += Events::Process<Events::PostImportEventContext>(scene);
|
||||
|
||||
if (contextResult.GetResult() == Events::ProcessingResult::Success)
|
||||
{
|
||||
return Events::LoadingResult::AssetLoaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Events::LoadingResult::AssetFailure;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneImportRequestHandler::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.emplace_back(AZ_CRC_CE("AssetImportRequestHandler"));
|
||||
}
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SceneAPI/SceneCore/Components/BehaviorComponent.h>
|
||||
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
struct SceneImporterSettings
|
||||
{
|
||||
AZ_TYPE_INFO(SceneImporterSettings, "{8BB6C7AD-BF99-44DC-9DA1-E7AD3F03DC10}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
AZStd::unordered_set<AZStd::string> m_supportedFileTypeExtensions;
|
||||
};
|
||||
|
||||
class SceneImportRequestHandler
|
||||
: public AZ::Component
|
||||
, public Events::AssetImportRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(SceneImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}");
|
||||
|
||||
~SceneImportRequestHandler() override = default;
|
||||
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
void GetSupportedFileExtensions(AZStd::unordered_set<AZStd::string>& extensions) override;
|
||||
Events::LoadingResult LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid,
|
||||
RequestingApplication requester) override;
|
||||
|
||||
static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided);
|
||||
|
||||
private:
|
||||
|
||||
SceneImporterSettings m_settings;
|
||||
|
||||
static constexpr const char* SettingsFilename = "AssetImporterSettings.json";
|
||||
};
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/containers/queue.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzToolsFramework/Debug/TraceContext.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/AssImpMaterialImporter.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
#include <SceneAPI/SceneData/GraphData/TransformData.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
struct QueueNode
|
||||
{
|
||||
std::shared_ptr<SDKNode::NodeWrapper> m_node;
|
||||
Containers::SceneGraph::NodeIndex m_parent;
|
||||
|
||||
QueueNode() = delete;
|
||||
QueueNode(std::shared_ptr<SDKNode::NodeWrapper>&& node, Containers::SceneGraph::NodeIndex parent)
|
||||
: m_node(std::move(node))
|
||||
, m_parent(parent)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
SceneImporter::SceneImporter()
|
||||
: m_sceneSystem(new SceneSystem())
|
||||
{
|
||||
m_sceneWrapper = AZStd::make_unique<AssImpSDKWrapper::AssImpSceneWrapper>();
|
||||
BindToCall(&SceneImporter::ImportProcessing);
|
||||
}
|
||||
|
||||
void SceneImporter::Reflect(ReflectContext* context)
|
||||
{
|
||||
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<SceneImporter, SceneCore::LoadingComponent>()->Version(2); // SPEC-5776
|
||||
}
|
||||
}
|
||||
|
||||
Events::ProcessingResult SceneImporter::ImportProcessing(Events::ImportEventContext& context)
|
||||
{
|
||||
m_sceneWrapper->Clear();
|
||||
|
||||
if (!m_sceneWrapper->LoadSceneFromFile(context.GetInputDirectory().c_str()))
|
||||
{
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
typedef AZStd::function<bool(Containers::Scene & scene)> ConvertFunc;
|
||||
ConvertFunc convertFunc;
|
||||
m_sceneSystem->Set(m_sceneWrapper.get());
|
||||
if (!azrtti_istypeof<AssImpSDKWrapper::AssImpSceneWrapper>(m_sceneWrapper.get()))
|
||||
{
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
|
||||
convertFunc = AZStd::bind(&SceneImporter::ConvertScene, this, AZStd::placeholders::_1);
|
||||
|
||||
if (convertFunc(context.GetScene()))
|
||||
{
|
||||
return Events::ProcessingResult::Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Events::ProcessingResult::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
bool SceneImporter::ConvertScene(Containers::Scene& scene) const
|
||||
{
|
||||
std::shared_ptr<SDKNode::NodeWrapper> sceneRoot = m_sceneWrapper->GetRootNode();
|
||||
if (!sceneRoot)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper* assImpSceneWrapper = azrtti_cast <AssImpSDKWrapper::AssImpSceneWrapper*>(m_sceneWrapper.get());
|
||||
|
||||
AZStd::pair<AssImpSDKWrapper::AssImpSceneWrapper::AxisVector, int32_t> upAxisAndSign = assImpSceneWrapper->GetUpVectorAndSign();
|
||||
|
||||
if (upAxisAndSign.second <= 0)
|
||||
{
|
||||
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Negative scene orientation is not a currently supported orientation.");
|
||||
return false;
|
||||
}
|
||||
switch (upAxisAndSign.first)
|
||||
{
|
||||
case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::X:
|
||||
scene.SetOriginalSceneOrientation(Containers::Scene::SceneOrientation::XUp);
|
||||
break;
|
||||
case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Y:
|
||||
scene.SetOriginalSceneOrientation(Containers::Scene::SceneOrientation::YUp);
|
||||
break;
|
||||
case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Z:
|
||||
scene.SetOriginalSceneOrientation(Containers::Scene::SceneOrientation::ZUp);
|
||||
break;
|
||||
default:
|
||||
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Unknown scene orientation, %d.", upAxisAndSign.first);
|
||||
AZ_Assert(false, "Unknown scene orientation, %d.", upAxisAndSign.first);
|
||||
break;
|
||||
}
|
||||
|
||||
AZStd::queue<SceneBuilder::QueueNode> nodes;
|
||||
nodes.emplace(AZStd::move(sceneRoot), scene.GetGraph().GetRoot());
|
||||
RenamedNodesMap nodeNameMap;
|
||||
|
||||
while (!nodes.empty())
|
||||
{
|
||||
SceneBuilder::QueueNode& node = nodes.front();
|
||||
AZ_Assert(node.m_node, "Empty asset importer node queued");
|
||||
if (!nodeNameMap.RegisterNode(node.m_node, scene.GetGraph(), node.m_parent))
|
||||
{
|
||||
AZ_TracePrintf(Utilities::ErrorWindow, "Failed to register asset importer node in name table.");
|
||||
// Skip this node since it could not be registered
|
||||
nodes.pop();
|
||||
continue;
|
||||
}
|
||||
AZStd::string nodeName = nodeNameMap.GetNodeName(node.m_node);
|
||||
SanitizeNodeName(nodeName);
|
||||
|
||||
AZ_TraceContext("SceneAPI Node Name", nodeName);
|
||||
Containers::SceneGraph::NodeIndex newNode = scene.GetGraph().AddChild(node.m_parent, nodeName.c_str());
|
||||
|
||||
AZ_Error(Utilities::ErrorWindow, newNode.IsValid(), "Failed to add Asset Importer node to scene graph");
|
||||
if (!newNode.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AssImpNodeEncounteredContext sourceNodeEncountered(scene, newNode, *assImpSceneWrapper, *m_sceneSystem, nodeNameMap, *azrtti_cast<AZ::AssImpSDKWrapper::AssImpNodeWrapper*>(node.m_node.get()));
|
||||
Events::ProcessingResultCombiner nodeResult;
|
||||
nodeResult += Events::Process(sourceNodeEncountered);
|
||||
|
||||
// If no importer created data, we still create an empty node that may eventually contain a transform
|
||||
if (sourceNodeEncountered.m_createdData.empty())
|
||||
{
|
||||
AZ_Assert(nodeResult.GetResult() != Events::ProcessingResult::Success,
|
||||
"Importers returned success but no data was created");
|
||||
AZStd::shared_ptr<DataTypes::IGraphObject> nullData(nullptr);
|
||||
sourceNodeEncountered.m_createdData.emplace_back(nullData);
|
||||
nodeResult += Events::ProcessingResult::Success;
|
||||
}
|
||||
|
||||
// Create single node since only one piece of graph data was created
|
||||
if (sourceNodeEncountered.m_createdData.size() == 1)
|
||||
{
|
||||
AZ_Assert(nodeResult.GetResult() != Events::ProcessingResult::Ignored,
|
||||
"An importer created data, but did not return success");
|
||||
if (nodeResult.GetResult() == Events::ProcessingResult::Failure)
|
||||
{
|
||||
AZ_TracePrintf(Utilities::ErrorWindow, "One or more importers failed to create data.");
|
||||
}
|
||||
|
||||
AssImpSceneDataPopulatedContext dataProcessed(sourceNodeEncountered,
|
||||
sourceNodeEncountered.m_createdData[0], nodeName.c_str());
|
||||
Events::ProcessingResult result = AddDataNodeWithContexts(dataProcessed);
|
||||
if (result != Events::ProcessingResult::Failure)
|
||||
{
|
||||
newNode = dataProcessed.m_currentGraphPosition;
|
||||
}
|
||||
}
|
||||
// Create an empty parent node and place all data under it. The remaining
|
||||
// tree will be built off of this as the logical parent
|
||||
else
|
||||
{
|
||||
AZ_Assert(nodeResult.GetResult() != Events::ProcessingResult::Ignored,
|
||||
"%i importers created data, but did not return success",
|
||||
sourceNodeEncountered.m_createdData.size());
|
||||
if (nodeResult.GetResult() == Events::ProcessingResult::Failure)
|
||||
{
|
||||
AZ_TracePrintf(Utilities::ErrorWindow, "One or more importers failed to create data.");
|
||||
}
|
||||
|
||||
size_t offset = nodeName.length();
|
||||
for (size_t i = 0; i < sourceNodeEncountered.m_createdData.size(); ++i)
|
||||
{
|
||||
nodeName += '_';
|
||||
nodeName += AZStd::to_string(aznumeric_cast<AZ::u64>(i + 1));
|
||||
|
||||
Containers::SceneGraph::NodeIndex subNode =
|
||||
scene.GetGraph().AddChild(newNode, nodeName.c_str());
|
||||
AZ_Assert(subNode.IsValid(), "Failed to create new scene sub node");
|
||||
AssImpSceneDataPopulatedContext dataProcessed(sourceNodeEncountered,
|
||||
sourceNodeEncountered.m_createdData[i], nodeName);
|
||||
dataProcessed.m_currentGraphPosition = subNode;
|
||||
AddDataNodeWithContexts(dataProcessed);
|
||||
|
||||
// Remove the temporary extension again.
|
||||
nodeName.erase(offset, nodeName.length() - offset);
|
||||
}
|
||||
}
|
||||
|
||||
AZ_Assert(nodeResult.GetResult() == Events::ProcessingResult::Success,
|
||||
"No importers successfully added processed scene data.");
|
||||
AZ_Assert(newNode != node.m_parent,
|
||||
"Failed to update current graph position during data processing.");
|
||||
|
||||
int childCount = node.m_node->GetChildCount();
|
||||
for (int i = 0; i < childCount; ++i)
|
||||
{
|
||||
std::shared_ptr<AssImpSDKWrapper::AssImpNodeWrapper> child = std::make_shared<AssImpSDKWrapper::AssImpNodeWrapper>(node.m_node->GetChild(i)->GetAssImpNode());
|
||||
if (child)
|
||||
{
|
||||
nodes.emplace(AZStd::move(child), newNode);
|
||||
}
|
||||
}
|
||||
|
||||
nodes.pop();
|
||||
};
|
||||
|
||||
Events::ProcessingResult result = Events::Process<AssImpFinalizeSceneContext>(scene, *assImpSceneWrapper, *m_sceneSystem, nodeNameMap);
|
||||
if (result == Events::ProcessingResult::Failure)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneImporter::SanitizeNodeName(AZStd::string& nodeName) const
|
||||
{
|
||||
// Replace % with something else so it is safe for use in printfs.
|
||||
AZStd::replace(nodeName.begin(), nodeName.end(), '%', '_');
|
||||
}
|
||||
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
|
||||
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
|
||||
#include <SceneAPI/SceneCore/Events/ImportEventContext.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneSystem.h>
|
||||
#include <SceneAPI/SDKWrapper/SceneWrapper.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace Containers
|
||||
{
|
||||
class Scene;
|
||||
}
|
||||
|
||||
namespace SceneBuilder
|
||||
{
|
||||
class SceneImporter
|
||||
: public SceneCore::LoadingComponent
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(SceneImporter, "{D5EE21B6-8B73-45BF-B711-31346E0BEDB3}", SceneCore::LoadingComponent);
|
||||
|
||||
SceneImporter();
|
||||
~SceneImporter() override = default;
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
Events::ProcessingResult ImportProcessing(Events::ImportEventContext& context);
|
||||
|
||||
protected:
|
||||
bool ConvertScene(Containers::Scene& scene) const;
|
||||
void SanitizeNodeName(AZStd::string& nodeName) const;
|
||||
|
||||
AZStd::unique_ptr<SDKScene::SceneWrapperBase> m_sceneWrapper;
|
||||
AZStd::shared_ptr<SceneSystem> m_sceneSystem;
|
||||
};
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneSystem.h>
|
||||
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
|
||||
#include <SceneAPI/SDKWrapper/AssImpTypeConverter.h>
|
||||
#include <assimp/scene.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
SceneSystem::SceneSystem() :
|
||||
m_unitSizeInMeters(1.0f),
|
||||
m_originalUnitSizeInMeters(1.0f),
|
||||
m_adjustTransform(nullptr),
|
||||
m_adjustTransformInverse(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void SceneSystem::Set(const SDKScene::SceneWrapperBase* scene)
|
||||
{
|
||||
// Get unit conversion factor to meter.
|
||||
if (!azrtti_istypeof<AssImpSDKWrapper::AssImpSceneWrapper>(scene))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const AssImpSDKWrapper::AssImpSceneWrapper* assImpScene = azrtti_cast<const AssImpSDKWrapper::AssImpSceneWrapper*>(scene);
|
||||
|
||||
// If either meta data piece is not available, the default of 1 will be used.
|
||||
assImpScene->GetAssImpScene()->mMetaData->Get("UnitScaleFactor", m_unitSizeInMeters);
|
||||
assImpScene->GetAssImpScene()->mMetaData->Get("OriginalUnitScaleFactor", m_originalUnitSizeInMeters);
|
||||
|
||||
/* Conversion factor for converting from centimeters to meters */
|
||||
m_unitSizeInMeters = m_unitSizeInMeters * .01f;
|
||||
|
||||
AZStd::pair<AssImpSDKWrapper::AssImpSceneWrapper::AxisVector, int32_t> upAxisAndSign = assImpScene->GetUpVectorAndSign();
|
||||
|
||||
if (upAxisAndSign.second <= 0)
|
||||
{
|
||||
AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Negative scene orientation is not a currently supported orientation.");
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::pair<AssImpSDKWrapper::AssImpSceneWrapper::AxisVector, int32_t> frontAxisAndSign = assImpScene->GetFrontVectorAndSign();
|
||||
|
||||
if (upAxisAndSign.first != AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Z &&
|
||||
upAxisAndSign.first != AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Unknown)
|
||||
{
|
||||
AZ::Matrix4x4 currentCoordMatrix = AZ::Matrix4x4::CreateIdentity();
|
||||
//(UpVector = +Z, FrontVector = +Y, CoordSystem = -X(RightHanded))
|
||||
AZ::Matrix4x4 targetCoordMatrix = AZ::Matrix4x4::CreateFromColumns(
|
||||
AZ::Vector4(-1, 0, 0, 0),
|
||||
AZ::Vector4(0, 0, 1, 0),
|
||||
AZ::Vector4(0, 1, 0, 0),
|
||||
AZ::Vector4(0, 0, 0, 1));
|
||||
|
||||
switch (upAxisAndSign.first)
|
||||
{
|
||||
case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::X: {
|
||||
if (frontAxisAndSign.second == 1)
|
||||
{
|
||||
currentCoordMatrix = AZ::Matrix4x4::CreateFromColumns(
|
||||
AZ::Vector4(0, -1, 0, 0),
|
||||
AZ::Vector4(1, 0, 0, 0),
|
||||
AZ::Vector4(0, 0, 1, 0),
|
||||
AZ::Vector4(0, 0, 0, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCoordMatrix = AZ::Matrix4x4::CreateFromColumns(
|
||||
AZ::Vector4(0, 1, 0, 0),
|
||||
AZ::Vector4(1, 0, 0, 0),
|
||||
AZ::Vector4(0, 0, -1, 0),
|
||||
AZ::Vector4(0, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Y: {
|
||||
if (frontAxisAndSign.second == 1)
|
||||
{
|
||||
currentCoordMatrix = AZ::Matrix4x4::CreateFromColumns(
|
||||
AZ::Vector4(1, 0, 0, 0),
|
||||
AZ::Vector4(0, 1, 0, 0),
|
||||
AZ::Vector4(0, 0, 1, 0),
|
||||
AZ::Vector4(0, 0, 0, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCoordMatrix = AZ::Matrix4x4::CreateFromColumns(
|
||||
AZ::Vector4(-1, 0, 0, 0),
|
||||
AZ::Vector4(0, 1, 0, 0),
|
||||
AZ::Vector4(0, 0, -1, 0),
|
||||
AZ::Vector4(0, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
AZ::Matrix4x4 inverse = currentCoordMatrix.GetInverseTransform();
|
||||
AZ::Matrix4x4 adjustmatrix = targetCoordMatrix * currentCoordMatrix.GetInverseTransform();
|
||||
m_adjustTransform.reset(new DataTypes::MatrixType(AssImpSDKWrapper::AssImpTypeConverter::ToTransform(adjustmatrix)));
|
||||
m_adjustTransformInverse.reset(new DataTypes::MatrixType(m_adjustTransform->GetInverseFull()));
|
||||
}
|
||||
}
|
||||
|
||||
void SceneSystem::SwapVec3ForUpAxis(Vector3& swapVector) const
|
||||
{
|
||||
if (m_adjustTransform)
|
||||
{
|
||||
swapVector = *m_adjustTransform * swapVector;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneSystem::SwapTransformForUpAxis(DataTypes::MatrixType& inOutTransform) const
|
||||
{
|
||||
if (m_adjustTransform)
|
||||
{
|
||||
inOutTransform = (*m_adjustTransform * inOutTransform) * *m_adjustTransformInverse;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneSystem::ConvertUnit(Vector3& scaleVector) const
|
||||
{
|
||||
scaleVector *= m_unitSizeInMeters;
|
||||
}
|
||||
|
||||
void SceneSystem::ConvertUnit(DataTypes::MatrixType& inOutTransform) const
|
||||
{
|
||||
Vector3 translation = inOutTransform.GetTranslation();
|
||||
translation *= m_unitSizeInMeters;
|
||||
inOutTransform.SetTranslation(translation);
|
||||
}
|
||||
|
||||
void SceneSystem::ConvertBoneUnit(DataTypes::MatrixType& inOutTransform) const
|
||||
{
|
||||
|
||||
|
||||
// Need to scale translation explicitly as MultiplyByScale won't change the translation component
|
||||
// and we need to convert to meter unit
|
||||
Vector3 translation = inOutTransform.GetTranslation();
|
||||
translation *= m_unitSizeInMeters;
|
||||
inOutTransform.SetTranslation(translation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <SceneAPI/SceneBuilder/SceneBuilderConfiguration.h>
|
||||
#include <SceneAPI/SceneCore/DataTypes/MatrixType.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class Vector3;
|
||||
|
||||
namespace SDKScene
|
||||
{
|
||||
class SceneWrapperBase;
|
||||
}
|
||||
|
||||
namespace SceneAPI
|
||||
{
|
||||
class SCENE_BUILDER_API SceneSystem
|
||||
{
|
||||
public:
|
||||
SceneSystem();
|
||||
|
||||
void Set(const SDKScene::SceneWrapperBase* sceneWrapper);
|
||||
void SwapVec3ForUpAxis(Vector3& swapVector) const;
|
||||
void SwapTransformForUpAxis(DataTypes::MatrixType& inOutTransform) const;
|
||||
void ConvertUnit(Vector3& scaleVector) const;
|
||||
void ConvertUnit(DataTypes::MatrixType& inOutTransform) const;
|
||||
void ConvertBoneUnit(DataTypes::MatrixType& inOutTransform) const;
|
||||
|
||||
//! Get effect unit size in meters of this scene, internally FBX saves it in the following manner
|
||||
//! GlobalSettings: {
|
||||
//! P : "UnitScaleFactor", "double", "Number", "", 2.54
|
||||
float GetUnitSizeInMeters() const { return m_unitSizeInMeters; }
|
||||
//! Get original unit size in meters of this scene, internally FBX saves it in the following manner
|
||||
//! GlobalSettings: {
|
||||
//! P : "OriginalUnitScaleFactor", "double", "Number", "", 2.54
|
||||
float GetOriginalUnitSizeInMeters() const { return m_originalUnitSizeInMeters; }
|
||||
|
||||
protected:
|
||||
float m_unitSizeInMeters = 1;
|
||||
float m_originalUnitSizeInMeters = 1;
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
AZStd::unique_ptr<DataTypes::MatrixType> m_adjustTransform;
|
||||
AZStd::unique_ptr<DataTypes::MatrixType> m_adjustTransformInverse;
|
||||
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
|
||||
#include <SceneAPI/SceneCore/Containers/Scene.h>
|
||||
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
#include <SceneAPI/SceneData/GraphData/BoneData.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
TEST(SceneImporterUtilityTests, AreSceneGraphsEqual_EmptySceneGraphs_ReturnsTrue)
|
||||
{
|
||||
Containers::SceneGraph lhsGraph;
|
||||
Containers::SceneGraph rhsGraph;
|
||||
|
||||
bool sceneGraphsEqual =
|
||||
(AreSceneGraphsEqual(lhsGraph, rhsGraph) && AreSceneGraphsEqual(rhsGraph, lhsGraph));
|
||||
|
||||
EXPECT_TRUE(sceneGraphsEqual);
|
||||
}
|
||||
|
||||
TEST(SceneImporterUtilityTests, AreSceneGraphsEqual_SameNameSingleNodeBothNull_ReturnsTrue)
|
||||
{
|
||||
Containers::SceneGraph lhsGraph;
|
||||
lhsGraph.AddChild(lhsGraph.GetRoot(), "testChild");
|
||||
Containers::SceneGraph rhsGraph;
|
||||
rhsGraph.AddChild(rhsGraph.GetRoot(), "testChild");
|
||||
|
||||
bool sceneGraphsEqual =
|
||||
(AreSceneGraphsEqual(lhsGraph, rhsGraph) && AreSceneGraphsEqual(rhsGraph, lhsGraph));
|
||||
|
||||
EXPECT_TRUE(sceneGraphsEqual);
|
||||
}
|
||||
|
||||
TEST(SceneImporterUtilityTests, AreSceneGraphsEqual_SameNameSingleNodeSameType_ReturnsTrue)
|
||||
{
|
||||
Containers::SceneGraph lhsGraph;
|
||||
AZStd::shared_ptr<DataTypes::IGraphObject> lhsData = AZStd::make_shared<SceneData::GraphData::MeshData>();
|
||||
lhsGraph.AddChild(lhsGraph.GetRoot(), "testChild", AZStd::move(lhsData));
|
||||
Containers::SceneGraph rhsGraph;
|
||||
AZStd::shared_ptr<DataTypes::IGraphObject> rhsData = AZStd::make_shared<SceneData::GraphData::MeshData>();
|
||||
rhsGraph.AddChild(rhsGraph.GetRoot(), "testChild", AZStd::move(rhsData));
|
||||
|
||||
bool sceneGraphsEqual =
|
||||
(AreSceneGraphsEqual(lhsGraph, rhsGraph) && AreSceneGraphsEqual(rhsGraph, lhsGraph));
|
||||
|
||||
EXPECT_TRUE(sceneGraphsEqual);
|
||||
}
|
||||
|
||||
TEST(SceneImporterUtilityTests, AreSceneGraphsEqual_SameNameSingleNodeOneNull_ReturnsFalse)
|
||||
{
|
||||
Containers::SceneGraph lhsGraph;
|
||||
AZStd::shared_ptr<DataTypes::IGraphObject> lhsData = AZStd::make_shared<SceneData::GraphData::MeshData>();
|
||||
lhsGraph.AddChild(lhsGraph.GetRoot(), "testChild", AZStd::move(lhsData));
|
||||
Containers::SceneGraph rhsGraph;
|
||||
rhsGraph.AddChild(rhsGraph.GetRoot(), "testChild");
|
||||
|
||||
bool sceneGraphsEqual =
|
||||
(AreSceneGraphsEqual(lhsGraph, rhsGraph) && AreSceneGraphsEqual(rhsGraph, lhsGraph));
|
||||
|
||||
EXPECT_FALSE(sceneGraphsEqual);
|
||||
}
|
||||
|
||||
TEST(SceneImporterUtilityTests, AreSceneGraphsEqual_SameNameSingleNodeDifferentTypes_ReturnsFalse)
|
||||
{
|
||||
Containers::SceneGraph lhsGraph;
|
||||
AZStd::shared_ptr<DataTypes::IGraphObject> lhsData = AZStd::make_shared<SceneData::GraphData::MeshData>();
|
||||
lhsGraph.AddChild(lhsGraph.GetRoot(), "testChild", AZStd::move(lhsData));
|
||||
Containers::SceneGraph rhsGraph;
|
||||
AZStd::shared_ptr<DataTypes::IGraphObject> rhsData = AZStd::make_shared<SceneData::GraphData::BoneData>();
|
||||
rhsGraph.AddChild(rhsGraph.GetRoot(), "testChild", AZStd::move(rhsData));
|
||||
|
||||
bool sceneGraphsEqual =
|
||||
(AreSceneGraphsEqual(lhsGraph, rhsGraph) && AreSceneGraphsEqual(rhsGraph, lhsGraph));
|
||||
|
||||
EXPECT_FALSE(sceneGraphsEqual);
|
||||
}
|
||||
|
||||
TEST(SceneImporterUtilityTests, AreSceneGraphsEqual_SameNameOneEmptyOneSingleNode_ReturnsFalse)
|
||||
{
|
||||
Containers::SceneGraph lhsGraph;
|
||||
AZStd::shared_ptr<DataTypes::IGraphObject> lhsData = AZStd::make_shared<SceneData::GraphData::MeshData>();
|
||||
lhsGraph.AddChild(lhsGraph.GetRoot(), "testChild", AZStd::move(lhsData));
|
||||
Containers::SceneGraph rhsGraph;
|
||||
|
||||
bool sceneGraphsEqual =
|
||||
(AreSceneGraphsEqual(lhsGraph, rhsGraph) && AreSceneGraphsEqual(rhsGraph, lhsGraph));
|
||||
|
||||
EXPECT_FALSE(sceneGraphsEqual);
|
||||
}
|
||||
|
||||
TEST(SceneImpoterUtilityTests, AreSceneGraphsEqual_DifferentNamesSingleNodeBothNull_ReturnsFalse)
|
||||
{
|
||||
Containers::SceneGraph lhsGraph;
|
||||
lhsGraph.AddChild(lhsGraph.GetRoot(), "testChild");
|
||||
Containers::SceneGraph rhsGraph;
|
||||
rhsGraph.AddChild(rhsGraph.GetRoot(), "differentName");
|
||||
|
||||
bool sceneGraphsEqual =
|
||||
(AreSceneGraphsEqual(lhsGraph, rhsGraph) && AreSceneGraphsEqual(rhsGraph, lhsGraph));
|
||||
|
||||
EXPECT_FALSE(sceneGraphsEqual);
|
||||
}
|
||||
|
||||
TEST(SceneImporterUtilityTests, AreSceneGraphsEqual_SecondGraphExtraChild_ReturnsFalse)
|
||||
{
|
||||
Containers::SceneGraph lhsGraph;
|
||||
lhsGraph.AddChild(lhsGraph.GetRoot(), "testChild");
|
||||
lhsGraph.AddChild(lhsGraph.GetRoot(), "extraTestChild");
|
||||
Containers::SceneGraph rhsGraph;
|
||||
rhsGraph.AddChild(rhsGraph.GetRoot(), "testChild");
|
||||
|
||||
bool sceneGraphsEqual =
|
||||
(AreSceneGraphsEqual(lhsGraph, rhsGraph) && AreSceneGraphsEqual(rhsGraph, lhsGraph));
|
||||
|
||||
EXPECT_FALSE(sceneGraphsEqual);
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
|
||||
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
|
||||
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
|
||||
#include <SceneAPI/SceneData/GraphData/MeshData.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace SceneAPI
|
||||
{
|
||||
namespace SceneBuilder
|
||||
{
|
||||
TEST(RenamedNodesMapTests, SanitizeNodeName_ValidNameProvided_ReturnsFalseAndNameUnchanged)
|
||||
{
|
||||
Containers::SceneGraph graph;
|
||||
AZStd::string name = "ValidName";
|
||||
|
||||
bool result = RenamedNodesMap::SanitizeNodeName(name, graph, graph.GetRoot());
|
||||
|
||||
EXPECT_FALSE(result);
|
||||
EXPECT_STREQ("ValidName", name.c_str());
|
||||
}
|
||||
|
||||
TEST(RenamedNodesMapTests, SanitizeNodeName_NameWithInvalidCharacter_ReturnsTrueAndNameChanged)
|
||||
{
|
||||
Containers::SceneGraph graph;
|
||||
AZStd::string check = "Valid";
|
||||
check += Containers::SceneGraph::GetNodeSeperationCharacter();
|
||||
check += "Name";
|
||||
AZStd::string name = check;
|
||||
|
||||
bool result = RenamedNodesMap::SanitizeNodeName(name, graph, graph.GetRoot());
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_STRNE(check.c_str(), name.c_str());
|
||||
}
|
||||
|
||||
TEST(RenamedNodesMapTests, SanitizeNodeName_BlankName_ReturnsTrueAndNameSetToDefault)
|
||||
{
|
||||
Containers::SceneGraph graph;
|
||||
AZStd::string name;
|
||||
|
||||
bool result = RenamedNodesMap::SanitizeNodeName(name, graph, graph.GetRoot(), "Default");
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_STREQ("Default", name.c_str());
|
||||
}
|
||||
|
||||
TEST(RenamedNodesMapTests, SanitizeNodeName_SingleCollision_ReturnsTrueAndNameHasAppendixOf1)
|
||||
{
|
||||
Containers::SceneGraph graph;
|
||||
graph.AddChild(graph.GetRoot(), "Child");
|
||||
AZStd::string name = "Child";
|
||||
|
||||
bool result = RenamedNodesMap::SanitizeNodeName(name, graph, graph.GetRoot());
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_STREQ("Child_1", name.c_str());
|
||||
}
|
||||
|
||||
TEST(RenamedNodesMapTests, SanitizeNodeName_MultipleCollisions_ReturnsTrueAndNameHasAppendixOf2)
|
||||
{
|
||||
Containers::SceneGraph graph;
|
||||
auto child = graph.AddChild(graph.GetRoot(), "Child");
|
||||
graph.AddSibling(child, "Child_1");
|
||||
AZStd::string name = "Child";
|
||||
|
||||
bool result = RenamedNodesMap::SanitizeNodeName(name, graph, graph.GetRoot());
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_STREQ("Child_2", name.c_str());
|
||||
}
|
||||
} // namespace SceneBuilder
|
||||
} // namespace SceneAPI
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <SceneAPI/FbxSceneBuilder/Tests/TestFbxMesh.h>
|
||||
#include <fbxsdk.h>
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace FbxSDKWrapper
|
||||
{
|
||||
TestFbxMesh::TestFbxMesh()
|
||||
: m_vertexControlPoints(nullptr)
|
||||
, m_vertexCount(0)
|
||||
, m_polygonVertexIndices(nullptr)
|
||||
, m_materialIndices(new FbxLayerElementArrayTemplate<int>(eFbxInt))
|
||||
, m_uvElements(FbxGeometryElementUV::Create(nullptr, "TestElements_UV"))
|
||||
, m_vertexColorElements(FbxGeometryElementVertexColor::Create(nullptr, "TestElements_VertexColors"))
|
||||
, m_expectedVertexCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
int TestFbxMesh::GetDeformerCount() const
|
||||
{
|
||||
// For current test need, only have one skin for the mesh
|
||||
return m_skin ? 1 : 0;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<const FbxSkinWrapper> TestFbxMesh::GetSkin(int index) const
|
||||
{
|
||||
// For current test need, only have one skin for the mesh
|
||||
return m_skin;
|
||||
}
|
||||
|
||||
bool TestFbxMesh::GetMaterialIndices(FbxLayerElementArrayTemplate<int>** lockableArray) const
|
||||
{
|
||||
*lockableArray = m_materialIndices;
|
||||
return true;
|
||||
}
|
||||
|
||||
int TestFbxMesh::GetControlPointsCount() const
|
||||
{
|
||||
return static_cast<int>(m_vertexCount);
|
||||
}
|
||||
|
||||
AZStd::vector<Vector3> TestFbxMesh::GetControlPoints() const
|
||||
{
|
||||
return m_vertexControlPoints;
|
||||
}
|
||||
|
||||
int TestFbxMesh::GetPolygonCount() const
|
||||
{
|
||||
return static_cast<int>(m_polygonInfo.size());
|
||||
}
|
||||
|
||||
int TestFbxMesh::GetPolygonSize(int polygonIndex) const
|
||||
{
|
||||
if (m_polygonInfo.find(polygonIndex) != m_polygonInfo.end())
|
||||
{
|
||||
return aznumeric_caster(m_polygonInfo.find(polygonIndex)->second.m_vertexCount);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int* TestFbxMesh::GetPolygonVertices() const
|
||||
{
|
||||
return m_polygonVertexIndices;
|
||||
}
|
||||
|
||||
int TestFbxMesh::GetPolygonVertexIndex(int polygonIndex) const
|
||||
{
|
||||
if (m_polygonInfo.find(polygonIndex) != m_polygonInfo.end())
|
||||
{
|
||||
return aznumeric_caster(m_polygonInfo.find(polygonIndex)->second.m_startVertexIndex);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
FbxUVWrapper TestFbxMesh::GetElementUV(int index)
|
||||
{
|
||||
(void)index;
|
||||
return m_uvElements;
|
||||
}
|
||||
|
||||
int TestFbxMesh::GetElementUVCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
FbxVertexColorWrapper TestFbxMesh::GetElementVertexColor(int index)
|
||||
{
|
||||
(void)index;
|
||||
return m_vertexColorElements;
|
||||
}
|
||||
|
||||
int TestFbxMesh::GetElementVertexColorCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool TestFbxMesh::GetPolygonVertexNormal(int polyIndex, int vertexIndex, Vector3& normal) const
|
||||
{
|
||||
normal = Vector3(1.0f, 0.0f, 0.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestFbxMesh::CreateMesh(std::vector<AZ::Vector3>& points, std::vector<std::vector<int> >& polygonVertexIndices)
|
||||
{
|
||||
m_vertexControlPoints.clear();
|
||||
m_materialIndices->Clear();
|
||||
m_polygonInfo.clear();
|
||||
|
||||
// Create fbx control point (position) data, and associated material index data
|
||||
m_vertexCount = aznumeric_caster(points.size());
|
||||
m_vertexControlPoints.reserve(points.size());
|
||||
for (unsigned int i = 0; i < points.size(); ++i)
|
||||
{
|
||||
m_vertexControlPoints.push_back(Vector3(points[i].GetX(), points[i].GetY(), points[i].GetZ()));
|
||||
m_materialIndices->Add(i);
|
||||
}
|
||||
|
||||
// Create fbx face data
|
||||
m_expectedVertexCount = 0;
|
||||
for (const std::vector<int>& onePolygonIndices : polygonVertexIndices)
|
||||
{
|
||||
for (const int& index : onePolygonIndices)
|
||||
{
|
||||
m_expectedVertexCount++;
|
||||
}
|
||||
}
|
||||
if (m_polygonVertexIndices)
|
||||
{
|
||||
delete m_polygonVertexIndices;
|
||||
}
|
||||
m_polygonVertexIndices = new int[m_expectedVertexCount];
|
||||
size_t i = 0;
|
||||
for (const std::vector<int>& onePolygonIndices : polygonVertexIndices)
|
||||
{
|
||||
m_polygonInfo.insert(std::make_pair<int, TestFbxPolygon>(aznumeric_caster(m_polygonInfo.size()),
|
||||
TestFbxPolygon(i, aznumeric_caster(onePolygonIndices.size()))));
|
||||
for (const int& index : onePolygonIndices)
|
||||
{
|
||||
m_polygonVertexIndices[i] = index;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestFbxMesh::SetSkin(const AZStd::shared_ptr<FbxSkinWrapper>& skin)
|
||||
{
|
||||
m_skin = skin;
|
||||
}
|
||||
|
||||
void TestFbxMesh::CreateExpectMeshInfo(std::vector<std::vector<int> >& expectedFaceVertexIndices)
|
||||
{
|
||||
m_expectedFaceVertexIndices = expectedFaceVertexIndices;
|
||||
}
|
||||
|
||||
size_t TestFbxMesh::GetExpectedVetexCount() const
|
||||
{
|
||||
return m_expectedVertexCount;
|
||||
}
|
||||
|
||||
size_t TestFbxMesh::GetExpectedFaceCount() const
|
||||
{
|
||||
return m_expectedFaceVertexIndices.size();
|
||||
}
|
||||
|
||||
AZ::Vector3 TestFbxMesh::GetExpectedFaceVertexPosition(unsigned int faceIndex, unsigned int vertexIndex) const
|
||||
{
|
||||
return m_vertexControlPoints[m_expectedFaceVertexIndices[faceIndex][vertexIndex]];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <SceneAPI/FbxSDKWrapper/FbxMeshWrapper.h>
|
||||
#include <SceneAPI/FbxSDKWrapper/FbxSkinWrapper.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace FbxSDKWrapper
|
||||
{
|
||||
struct TestFbxPolygon
|
||||
{
|
||||
size_t m_startVertexIndex;
|
||||
size_t m_vertexCount;
|
||||
|
||||
TestFbxPolygon(size_t startVertexIndex, size_t vertexCount)
|
||||
: m_startVertexIndex(startVertexIndex)
|
||||
, m_vertexCount(vertexCount)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// TestFbxMesh
|
||||
// FbxMesh Test Data creation
|
||||
class TestFbxMesh
|
||||
: public FbxMeshWrapper
|
||||
{
|
||||
public:
|
||||
TestFbxMesh();
|
||||
~TestFbxMesh() override = default;
|
||||
|
||||
int GetDeformerCount() const override;
|
||||
AZStd::shared_ptr<const FbxSkinWrapper> GetSkin(int index) const override;
|
||||
bool GetMaterialIndices(FbxLayerElementArrayTemplate<int>** lockableArray) const override;
|
||||
|
||||
int GetControlPointsCount() const;
|
||||
AZStd::vector<Vector3> GetControlPoints() const override;
|
||||
|
||||
int GetPolygonCount() const override;
|
||||
int GetPolygonSize(int polygonIndex) const override;
|
||||
int* GetPolygonVertices() const override;
|
||||
int GetPolygonVertexIndex(int polygonIndex) const;
|
||||
|
||||
FbxUVWrapper GetElementUV(int index = 0) override;
|
||||
int GetElementUVCount() const override;
|
||||
FbxVertexColorWrapper GetElementVertexColor(int index = 0) override;
|
||||
int GetElementVertexColorCount() const override;
|
||||
|
||||
bool GetPolygonVertexNormal(int polyIndex, int vertexIndex, Vector3& normal) const override;
|
||||
|
||||
// Create test data APIs
|
||||
void CreateMesh(std::vector<AZ::Vector3>& points, std::vector<std::vector<int> >& polygonVertexIndices);
|
||||
void CreateExpectMeshInfo(std::vector<std::vector<int> >& expectedFaceVertexIndices);
|
||||
void SetSkin(const AZStd::shared_ptr<FbxSkinWrapper>& skin);
|
||||
|
||||
size_t GetExpectedVetexCount() const;
|
||||
size_t GetExpectedFaceCount() const;
|
||||
AZ::Vector3 GetExpectedFaceVertexPosition(unsigned int faceIndex, unsigned int vertexIndex) const;
|
||||
|
||||
protected:
|
||||
AZStd::vector<Vector3> m_vertexControlPoints; // vertex positions
|
||||
size_t m_vertexCount;
|
||||
int* m_polygonVertexIndices; // store all polygons' vertex indices in sequence. Each index maps to a control point.
|
||||
FbxLayerElementArrayTemplate<int>* m_materialIndices;
|
||||
std::unordered_map<int, TestFbxPolygon> m_polygonInfo;
|
||||
|
||||
FbxUVWrapper m_uvElements;
|
||||
FbxVertexColorWrapper m_vertexColorElements;
|
||||
AZStd::shared_ptr<FbxSkinWrapper> m_skin;
|
||||
|
||||
// Expected converted data
|
||||
unsigned int m_expectedVertexCount;
|
||||
std::vector<std::vector<int> > m_expectedFaceVertexIndices;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <SceneAPI/FbxSceneBuilder/Tests/TestFbxNode.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace FbxSDKWrapper
|
||||
{
|
||||
const std::shared_ptr<FbxMeshWrapper> TestFbxNode::GetMesh() const
|
||||
{
|
||||
return m_testFbxMesh;
|
||||
}
|
||||
|
||||
const char* TestFbxNode::GetName() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
}
|
||||
|
||||
void TestFbxNode::SetMesh(std::shared_ptr<TestFbxMesh> testFbxMesh)
|
||||
{
|
||||
m_testFbxMesh = testFbxMesh;
|
||||
}
|
||||
|
||||
void TestFbxNode::SetName(const char* name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SceneAPI/FbxSDKWrapper/FbxNodeWrapper.h>
|
||||
#include <SceneAPI/FbxSceneBuilder/Tests/TestFbxMesh.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace FbxSDKWrapper
|
||||
{
|
||||
// TestFbxNode
|
||||
// FbxNode Test Data creation
|
||||
class TestFbxNode
|
||||
: public FbxNodeWrapper
|
||||
{
|
||||
public:
|
||||
~TestFbxNode() override = default;
|
||||
|
||||
const std::shared_ptr<FbxMeshWrapper> GetMesh() const override;
|
||||
const char* GetName() const override;
|
||||
|
||||
void SetMesh(std::shared_ptr<TestFbxMesh> testFbxMesh);
|
||||
void SetName(const char* name);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<TestFbxMesh> m_testFbxMesh;
|
||||
AZStd::string m_name;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <SceneAPI/FbxSceneBuilder/Tests/TestFbxSkin.h>
|
||||
#include <SceneAPI/FbxSceneBuilder/Tests/TestFbxNode.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace FbxSDKWrapper
|
||||
{
|
||||
const char* TestFbxSkin::GetName() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
}
|
||||
|
||||
int TestFbxSkin::GetClusterCount() const
|
||||
{
|
||||
return aznumeric_caster(m_links.size());
|
||||
}
|
||||
|
||||
int TestFbxSkin::GetClusterControlPointIndicesCount(int index) const
|
||||
{
|
||||
return aznumeric_caster(m_controlPointIndices[index].size());
|
||||
}
|
||||
|
||||
int TestFbxSkin::GetClusterControlPointIndex(int clusterIndex, int pointIndex) const
|
||||
{
|
||||
return m_controlPointIndices[clusterIndex][pointIndex];
|
||||
}
|
||||
|
||||
double TestFbxSkin::GetClusterControlPointWeight(int clusterIndex, int pointIndex) const
|
||||
{
|
||||
return m_weights[clusterIndex][pointIndex];
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<const FbxNodeWrapper> TestFbxSkin::GetClusterLink(int index) const
|
||||
{
|
||||
return m_links[index];
|
||||
}
|
||||
|
||||
void TestFbxSkin::SetName(const char* name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
void TestFbxSkin::CreateSkinWeightData(AZStd::vector<AZStd::string>& boneNames, AZStd::vector<AZStd::vector<double>>& weights, AZStd::vector<AZStd::vector<int>>& controlPointIndices)
|
||||
{
|
||||
m_links.resize(boneNames.size());
|
||||
for (size_t linkIndex = 0; linkIndex < boneNames.size(); ++linkIndex)
|
||||
{
|
||||
m_links[linkIndex] = AZStd::make_shared<FbxSDKWrapper::TestFbxNode>();
|
||||
m_links[linkIndex]->SetName(boneNames[linkIndex].c_str());
|
||||
}
|
||||
m_weights = weights;
|
||||
m_controlPointIndices = controlPointIndices;
|
||||
}
|
||||
|
||||
void TestFbxSkin::CreateExpectSkinWeightData(AZStd::vector<AZStd::vector<int>>& boneIds, AZStd::vector<AZStd::vector<float>>& weights)
|
||||
{
|
||||
m_expectedBoneIds = boneIds;
|
||||
m_expectedWeights = weights;
|
||||
}
|
||||
|
||||
size_t TestFbxSkin::GetExpectedVertexCount() const
|
||||
{
|
||||
return m_expectedBoneIds.size();
|
||||
}
|
||||
|
||||
size_t TestFbxSkin::GetExpectedLinkCount(size_t vertexIndex) const
|
||||
{
|
||||
return m_expectedBoneIds[vertexIndex].size();
|
||||
}
|
||||
|
||||
int TestFbxSkin::GetExpectedSkinLinkBoneId(size_t vertexIndex, size_t linkIndex) const
|
||||
{
|
||||
return m_expectedBoneIds[vertexIndex][linkIndex];
|
||||
}
|
||||
|
||||
float TestFbxSkin::GetExpectedSkinLinkWeight(size_t vertextIndex, size_t linkIndex) const
|
||||
{
|
||||
return m_expectedWeights[vertextIndex][linkIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <SceneAPI/FbxSDKWrapper/FbxSkinWrapper.h>
|
||||
#include <SceneAPI/FbxSceneBuilder/Tests/TestFbxNode.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace FbxSDKWrapper
|
||||
{
|
||||
class TestFbxSkin
|
||||
: public FbxSkinWrapper
|
||||
{
|
||||
public:
|
||||
~TestFbxSkin() override = default;
|
||||
|
||||
const char* GetName() const override;
|
||||
int GetClusterCount() const override;
|
||||
int GetClusterControlPointIndicesCount(int index) const override;
|
||||
int GetClusterControlPointIndex(int clusterIndex, int pointIndex) const override;
|
||||
double GetClusterControlPointWeight(int clusterIndex, int pointIndex) const override;
|
||||
AZStd::shared_ptr<const FbxNodeWrapper> GetClusterLink(int index) const override;
|
||||
|
||||
void SetName(const char* name);
|
||||
void CreateSkinWeightData(AZStd::vector<AZStd::string>& boneNames, AZStd::vector<AZStd::vector<double>>& weights, AZStd::vector<AZStd::vector<int>>& controlPointIndices);
|
||||
void CreateExpectSkinWeightData(AZStd::vector<AZStd::vector<int>>& boneIds, AZStd::vector<AZStd::vector<float>>& weights);
|
||||
|
||||
size_t GetExpectedVertexCount() const;
|
||||
size_t GetExpectedLinkCount(size_t vertexIndex) const;
|
||||
int GetExpectedSkinLinkBoneId(size_t vertexIndex, size_t linkIndex) const;
|
||||
float GetExpectedSkinLinkWeight(size_t vertexIndex, size_t linkIndex) const;
|
||||
|
||||
protected:
|
||||
AZStd::string m_name;
|
||||
AZStd::vector<AZStd::shared_ptr<FbxSDKWrapper::TestFbxNode>> m_links;
|
||||
AZStd::vector<AZStd::vector<double>> m_weights;
|
||||
AZStd::vector<AZStd::vector<int>> m_controlPointIndices;
|
||||
|
||||
AZStd::vector<AZStd::vector<int>> m_expectedBoneIds;
|
||||
AZStd::vector<AZStd::vector<float>> m_expectedWeights;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
class SceneBuilderTestEnvironment
|
||||
: public AZ::Test::ITestEnvironment
|
||||
{
|
||||
public:
|
||||
virtual ~SceneBuilderTestEnvironment()
|
||||
{}
|
||||
|
||||
protected:
|
||||
void SetupEnvironment() override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
|
||||
|
||||
sceneCoreModule = AZ::DynamicModuleHandle::Create("SceneCore");
|
||||
AZ_Assert(sceneCoreModule, "SceneBuilder unit tests failed to create SceneCore module.");
|
||||
bool loaded = sceneCoreModule->Load(false);
|
||||
AZ_Assert(loaded, "SceneBuilder unit tests failed to load SceneCore module.");
|
||||
auto init = sceneCoreModule->GetFunction<AZ::InitializeDynamicModuleFunction>(AZ::InitializeDynamicModuleFunctionName);
|
||||
AZ_Assert(init, "SceneBuilder unit tests failed to find the initialization function the SceneCore module.");
|
||||
(*init)(AZ::Environment::GetInstance());
|
||||
|
||||
sceneDataModule = AZ::DynamicModuleHandle::Create("SceneData");
|
||||
AZ_Assert(sceneDataModule, "SceneData unit tests failed to create SceneData module.");
|
||||
loaded = sceneDataModule->Load(false);
|
||||
AZ_Assert(loaded, "SceneBuilder unit tests failed to load SceneData module.");
|
||||
init = sceneDataModule->GetFunction<AZ::InitializeDynamicModuleFunction>(AZ::InitializeDynamicModuleFunctionName);
|
||||
AZ_Assert(init, "SceneBuilder unit tests failed to find the initialization function the SceneData module.");
|
||||
(*init)(AZ::Environment::GetInstance());
|
||||
}
|
||||
|
||||
void TeardownEnvironment() override
|
||||
{
|
||||
auto uninit = sceneDataModule->GetFunction<AZ::UninitializeDynamicModuleFunction>(AZ::UninitializeDynamicModuleFunctionName);
|
||||
AZ_Assert(uninit, "SceneBuilder unit tests failed to find the uninitialization function the SceneData module.");
|
||||
(*uninit)();
|
||||
sceneDataModule.reset();
|
||||
|
||||
uninit = sceneCoreModule->GetFunction<AZ::UninitializeDynamicModuleFunction>(AZ::UninitializeDynamicModuleFunctionName);
|
||||
AZ_Assert(uninit, "SceneBuilder unit tests failed to find the uninitialization function the SceneCore module.");
|
||||
(*uninit)();
|
||||
sceneCoreModule.reset();
|
||||
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
}
|
||||
|
||||
private:
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> sceneCoreModule;
|
||||
AZStd::unique_ptr<AZ::DynamicModuleHandle> sceneDataModule;
|
||||
};
|
||||
|
||||
AZ_UNIT_TEST_HOOK(new SceneBuilderTestEnvironment);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
SceneBuilderConfiguration.h
|
||||
SceneImportRequestHandler.h
|
||||
SceneImportRequestHandler.cpp
|
||||
SceneImporter.h
|
||||
SceneImporter.cpp
|
||||
SceneSystem.h
|
||||
SceneSystem.cpp
|
||||
ImportContexts/ImportContexts.h
|
||||
ImportContexts/ImportContexts.cpp
|
||||
ImportContexts/AssImpImportContexts.h
|
||||
ImportContexts/AssImpImportContexts.cpp
|
||||
Importers/Utilities/AssImpMeshImporterUtilities.h
|
||||
Importers/Utilities/AssImpMeshImporterUtilities.cpp
|
||||
Importers/Utilities/RenamedNodesMap.h
|
||||
Importers/Utilities/RenamedNodesMap.cpp
|
||||
Importers/AssImpAnimationImporter.h
|
||||
Importers/AssImpAnimationImporter.cpp
|
||||
Importers/AssImpBitangentStreamImporter.h
|
||||
Importers/AssImpBitangentStreamImporter.cpp
|
||||
Importers/AssImpBlendShapeImporter.h
|
||||
Importers/AssImpBlendShapeImporter.cpp
|
||||
Importers/AssImpBoneImporter.h
|
||||
Importers/AssImpBoneImporter.cpp
|
||||
Importers/AssImpColorStreamImporter.h
|
||||
Importers/AssImpColorStreamImporter.cpp
|
||||
Importers/AssImpImporterUtilities.h
|
||||
Importers/AssImpImporterUtilities.cpp
|
||||
Importers/AssImpMaterialImporter.h
|
||||
Importers/AssImpMaterialImporter.cpp
|
||||
Importers/AssImpMeshImporter.h
|
||||
Importers/AssImpMeshImporter.cpp
|
||||
Importers/AssImpSkinImporter.h
|
||||
Importers/AssImpSkinImporter.cpp
|
||||
Importers/AssImpSkinWeightsImporter.h
|
||||
Importers/AssImpSkinWeightsImporter.cpp
|
||||
Importers/AssImpTangentStreamImporter.h
|
||||
Importers/AssImpTangentStreamImporter.cpp
|
||||
Importers/AssImpTransformImporter.h
|
||||
Importers/AssImpTransformImporter.cpp
|
||||
Importers/AssImpUvMapImporter.h
|
||||
Importers/AssImpUvMapImporter.cpp
|
||||
Importers/AssImpAnimationImporter.h
|
||||
Importers/AssImpAnimationImporter.cpp
|
||||
Importers/ImporterUtilities.h
|
||||
Importers/ImporterUtilities.cpp
|
||||
Importers/ImporterUtilities.inl
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
DllMain.cpp
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Tests/TestsMain.cpp
|
||||
Tests/Importers/SceneImporterUtilitiesTests.cpp
|
||||
Tests/Importers/Utilities/RenamedNodesMapTests.cpp
|
||||
)
|
||||
Reference in New Issue
Block a user