Integrating latest 47acbe8

This commit is contained in:
alexpete
2021-03-25 13:57:57 -07:00
parent 448c549698
commit 75dc720198
10312 changed files with 2711566 additions and 671451 deletions
@@ -19,13 +19,18 @@
#include <SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h>
#include <SceneAPI/FbxSceneBuilder/FbxImporter.h>
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpColorStreamImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.h>
#endif
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/FbxAnimationImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/FbxBlendShapeImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/FbxBoneImporter.h>
@@ -39,7 +44,6 @@
#include <SceneAPI/FbxSceneBuilder/Importers/FbxTransformImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/FbxUvMapImporter.h>
namespace AZ
{
namespace SceneAPI
@@ -86,13 +90,19 @@ namespace AZ
g_componentDescriptors.push_back(FbxUvMapImporter::CreateDescriptor());
g_componentDescriptors.push_back(FbxTangentStreamImporter::CreateDescriptor());
g_componentDescriptors.push_back(FbxBitangentStreamImporter::CreateDescriptor());
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
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());
#endif
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);
@@ -21,10 +21,8 @@
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/FbxSceneBuilder/FbxImporter.h>
#include <SceneAPI/FbxSceneBuilder/ImportContexts/FbxImportContexts.h>
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
#include <SceneAPI/FbxSceneBuilder/ImportContexts/AssImpImportContexts.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.h>
#endif
#include <SceneAPI/FbxSceneBuilder/Importers/FbxImporterUtilities.h>
#include <SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.h>
#include <SceneAPI/FbxSDKWrapper/FbxSceneWrapper.h>
@@ -32,10 +30,8 @@
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneData/GraphData/TransformData.h>
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
#endif
namespace AZ
{
@@ -59,7 +55,7 @@ namespace AZ
FbxImporter::FbxImporter()
: m_sceneSystem(new FbxSceneSystem())
{
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
if (m_useAssetImporterSDK)
{
m_sceneWrapper = AZStd::make_unique<AssImpSDKWrapper::AssImpSceneWrapper>();
@@ -68,9 +64,7 @@ namespace AZ
{
m_sceneWrapper = AZStd::make_unique<FbxSDKWrapper::FbxSceneWrapper>();
}
#else
m_sceneWrapper = AZStd::make_unique<FbxSDKWrapper::FbxSceneWrapper>();
#endif
BindToCall(&FbxImporter::ImportProcessing);
}
@@ -99,12 +93,10 @@ namespace AZ
{
convertFunc = AZStd::bind(&FbxImporter::ConvertFbxSceneContext, this, AZStd::placeholders::_1);
}
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
else
{
convertFunc = AZStd::bind(&FbxImporter::ConvertFbxScene, this, AZStd::placeholders::_1);
}
#endif
if (convertFunc(context.GetScene()))
{
@@ -284,7 +276,7 @@ namespace AZ
return true;
}
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
bool FbxImporter::ConvertFbxScene(Containers::Scene& scene) const
{
std::shared_ptr<SDKNode::NodeWrapper> fbxRoot = m_sceneWrapper->GetRootNode();
@@ -333,6 +325,8 @@ namespace AZ
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());
@@ -431,7 +425,13 @@ namespace AZ
return true;
}
#endif
void FbxImporter::SanitizeNodeName(AZStd::string& nodeName) const
{
// Replace % with something else so it is safe for use in printfs.
AZStd::replace(nodeName.begin(), nodeName.end(), '%', '_');
}
} // namespace FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -46,15 +46,12 @@ namespace AZ
protected:
bool ConvertFbxSceneContext(Containers::Scene& scene) const;
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
bool ConvertFbxScene(Containers::Scene& scene) const;
#endif
void SanitizeNodeName(AZStd::string& nodeName) const;
AZStd::unique_ptr<SDKScene::SceneWrapperBase> m_sceneWrapper;
AZStd::shared_ptr<FbxSceneSystem> m_sceneSystem;
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
bool m_useAssetImporterSDK = false;
#endif
};
} // namespace FbxSceneBuilder
} // namespace SceneAPI
@@ -13,12 +13,9 @@
#include <AzCore/Math/Vector3.h>
#include <SceneAPI/FbxSDKWrapper/FbxSceneWrapper.h>
#include <SceneAPI/FbxSceneBuilder/FbxSceneSystem.h>
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
#include <SceneAPI/SDKWrapper/AssImpTypeConverter.h>
#include <assimp/scene.h>
#endif
namespace AZ
@@ -52,7 +49,6 @@ namespace AZ
m_adjustTransformInverse.reset(new DataTypes::MatrixType(m_adjustTransform->GetInverseFull()));
}
}
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
else if (azrtti_istypeof<AssImpSDKWrapper::AssImpSceneWrapper>(fbxScene))
{
const AssImpSDKWrapper::AssImpSceneWrapper* assImpScene = azrtti_cast<const AssImpSDKWrapper::AssImpSceneWrapper*>(fbxScene);
@@ -135,7 +131,6 @@ namespace AZ
m_adjustTransformInverse.reset(new DataTypes::MatrixType(m_adjustTransform->GetInverseFull()));
}
}
#endif
}
void FbxSceneSystem::SwapVec3ForUpAxis(Vector3& swapVector) const
@@ -0,0 +1,610 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AssImpTypeConverter.h>
#include <assimp/mesh.h>
#include <assimp/scene.h>
#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/FbxSceneBuilder/FbxSceneSystem.h>
#include <SceneAPI/FbxSceneBuilder/ImportContexts/AssImpImportContexts.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.h>
#include <SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.h>
#include <SceneAPI/FbxSDKWrapper/FbxAnimCurveWrapper.h>
#include <SceneAPI/FbxSDKWrapper/FbxAnimLayerWrapper.h>
#include <SceneAPI/FbxSDKWrapper/FbxNodeWrapper.h>
#include <SceneAPI/FbxSDKWrapper/FbxSceneWrapper.h>
#include <SceneAPI/FbxSDKWrapper/FbxTimeSpanWrapper.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 FbxSceneBuilder
{
const char* AssImpAnimationImporter::s_animationNodeName = "animation";
const FbxSDKWrapper::FbxTimeWrapper::TimeMode AssImpAnimationImporter::s_defaultTimeMode =
FbxSDKWrapper::FbxTimeWrapper::frames30;
AssImpAnimationImporter::AssImpAnimationImporter()
{
BindToCall(&AssImpAnimationImporter::ImportAnimation);
}
void AssImpAnimationImporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<AssImpAnimationImporter, SceneCore::LoadingComponent>()->Version(1);
}
}
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
unsigned int mNumPositionKeys{};
aiVectorKey* mPositionKeys{};
unsigned int mNumRotationKeys{};
aiQuatKey* mRotationKeys{};
unsigned int 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");
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 = [](unsigned numChannels, auto** channels, const aiAnimation* animation, auto& map)
{
map.reserve(numChannels);
for (unsigned channelIndex = 0; channelIndex < numChannels; ++channelIndex)
{
auto* nodeAnim = channels[channelIndex];
AZStd::string name = GetName(nodeAnim);
map.emplace(name, MakeMyPair(animation, nodeAnim));
}
};
for (unsigned animIndex = 0; animIndex < scene->mNumAnimations; ++animIndex)
{
const aiAnimation* animation = scene->mAnimations[animIndex];
mapAnimationsFunc(animation->mNumChannels, animation->mChannels, animation, boneAnimations);
for (unsigned 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;
for (unsigned meshIndex = 0; meshIndex < currentNode->mNumMeshes; ++meshIndex)
{
aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[meshIndex]];
if (NodeToChannelToMorphAnim::iterator channelsForMeshName = meshMorphAnimations.find(mesh->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, mesh);
}
}
}
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];
// 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>();
createdAnimationData->ReserveKeyFrames(
animation->mDuration +
1); // +1 because we start at 0 and the last keyframe is at mDuration instead of mDuration-1
createdAnimationData->SetTimeStepBetweenFrames(1.0 / animation->mTicksPerSecond);
// 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 (unsigned time = 0; time <= animation->mDuration; ++time)
{
createdAnimationData->AddKeyFrame(localTransform);
}
Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
context.m_currentGraphPosition, nodeName.c_str(), AZStd::move(createdAnimationData));
context.m_scene.GetGraph().MakeEndPoint(addNode);
return combinedAnimationResult.GetResult();
}
decltype(boneAnimations) parentFillerAnimations;
// Go through all the animations and make sure we create animations for bones who's parents don't have an animation
for (auto&& anim : boneAnimations)
{
aiNode* node = scene->mRootNode->FindNode(anim.first.c_str());
aiNode* parent = node->mParent;
while (parent && parent != scene->mRootNode)
{
if (!IsPivotNode(parent->mName))
{
if (boneAnimations.find(parent->mName.C_Str()) == boneAnimations.end() &&
parentFillerAnimations.find(parent->mName.C_Str()) == parentFillerAnimations.end())
{
// Create 1 key for each type that just copies the current transform
ConsolidatedNodeAnim emptyAnimation;
aiMatrix4x4 globalTransform = GetConcatenatedLocalTransform(parent);
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();
parentFillerAnimations.insert(
AZStd::make_pair(parent->mName.C_Str(), AZStd::make_pair(anim.second.first, AZStd::move(emptyAnimation))));
}
}
parent = parent->mParent;
}
}
boneAnimations.insert(AZStd::make_move_iterator(parentFillerAnimations.begin()), AZStd::make_move_iterator(parentFillerAnimations.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;
}
unsigned numAnimationKeys = anim->mNumPositionKeys;
if (numAnimationKeys <= 1)
{
numAnimationKeys = AZStd::GetMax(anim->mNumRotationKeys, anim->mNumScalingKeys);
}
auto sampleKeyFrame = [](const auto& keys, unsigned numKeys, double time)
{
AZ_Error("AnimationImporter", numKeys > 0, "Animation key set must have at least 1 key");
if (numKeys == 1)
{
return keys[0].mValue;
}
auto returnValue = keys[0].mValue;
for (unsigned keyIndex = 0; keyIndex < numKeys; ++keyIndex)
{
const auto& key = keys[keyIndex];
// We want to return the key that exactly matches the time if possible, otherwise we'll keep track of the previous time
// If we don't find an exact match and end up going past the desired time (or run out of keyframes) then we return the previous key
if (key.mTime < time)
{
returnValue = key.mValue;
}
else if (key.mTime == time)
{
return key.mValue;
}
else
{
return returnValue;
}
}
return returnValue;
};
AZStd::shared_ptr<SceneData::GraphData::AnimationData> createdAnimationData =
AZStd::make_shared<SceneData::GraphData::AnimationData>();
createdAnimationData->ReserveKeyFrames(animation->mDuration + 1); // +1 because we start at 0 and the last keyframe is at mDuration instead of mDuration-1
createdAnimationData->SetTimeStepBetweenFrames(1.0 / animation->mTicksPerSecond);
for (unsigned time = 0; time <= animation->mDuration; ++time)
{
auto scale = sampleKeyFrame(anim->mScalingKeys, anim->mNumScalingKeys, time);
auto position = sampleKeyFrame(anim->mPositionKeys, anim->mNumPositionKeys, time);
auto rotation = sampleKeyFrame(anim->mRotationKeys, anim->mNumRotationKeys, time);
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;
aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
// 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.
// Helper class to store key data, when translating from AssImp layout to the engine's scene format.
struct KeyData
{
KeyData(float weight, float time) :
m_weight(weight),
m_time(time)
{
}
bool operator<(const KeyData& other) const
{
return m_time < other.m_time;
}
// Naming in the previous SDK (FBX SDK) and in the engine's scene format
// doesn't match AssImp's naming convention.
// weight here is the AssImp's name for the data, it was named value in FBX SDK.
float m_weight = 0;
float m_time = 0;
};
typedef AZStd::map<int, AZStd::vector<KeyData>> ValueToKeyDataMap;
ValueToKeyDataMap valueToKeyDataMap;
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);
}
}
for (const auto& [meshIdx, keys] : valueToKeyDataMap)
{
AZStd::shared_ptr<SceneData::GraphData::BlendShapeAnimationData> morphAnimNode =
AZStd::make_shared<SceneData::GraphData::BlendShapeAnimationData>();
morphAnimNode->ReserveKeyFrames(animation->mDuration + 1);
morphAnimNode->SetTimeStepBetweenFrames(1.0 / animation->mTicksPerSecond);
aiAnimMesh* aiAnimMesh = mesh->mAnimMeshes[meshIdx];
AZStd::string_view nodeName(aiAnimMesh->mName.C_Str());
const unsigned maxKeys = keys.size();
unsigned keyIdx = 0;
for (unsigned time = 0; time <= animation->mDuration; ++time)
{
if (keyIdx < maxKeys - 1 && time >= keys[keyIdx+1].m_time)
{
++keyIdx;
}
float weight_value = keys[keyIdx].m_weight;
if (keyIdx < maxKeys - 1)
{
float nextWeight = keys[keyIdx+1].m_weight;
float normalizedTimeBetweenFrames = 0;
if (keys[keyIdx + 1].m_time != keys[keyIdx].m_time)
{
normalizedTimeBetweenFrames =
(time - keys[keyIdx].m_time) / (keys[keyIdx + 1].m_time - keys[keyIdx].m_time);
}
else
{
AZ_Warning("AnimationImporter", false,
"Morph target mesh %s has keys with duplicate time, at indices %d and %d. The second will be ignored.",
nodeName.data(),
keyIdx,
keyIdx+1);
}
// AssImp and FBX both only support linear interpolation for blend shapes.
weight_value = AZ::Lerp(weight_value, nextWeight, normalizedTimeBetweenFrames);
}
morphAnimNode->AddKeyFrame(weight_value);
}
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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,54 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <SceneAPI/FbxSceneBuilder/ImportContexts/FbxImportContexts.h>
#include <SceneAPI/FbxSDKWrapper/FbxTimeWrapper.h>
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
#include <SceneAPI/FbxSceneBuilder/ImportContexts/AssImpImportContexts.h>
struct aiAnimation;
struct aiMesh;
struct aiMeshMorphAnim;
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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);
protected:
static const char* s_animationNodeName;
static const FbxSDKWrapper::FbxTimeWrapper::TimeMode s_defaultTimeMode;
};
} // namespace FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,119 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/FbxImporterUtilities.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 <assimp/scene.h>
#include <assimp/mesh.h>
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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(1);
}
}
Events::ProcessingResult AssImpBitangentStreamImporter::ImportBitangentStreams(AssImpSceneNodeAppendedContext& context)
{
AZ_TraceContext("Importer", m_defaultNodeName);
if (!context.m_sourceNode.ContainsMesh())
{
return Events::ProcessingResult::Ignored;
}
aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
AZStd::shared_ptr<DataTypes::IGraphObject> parentData =
context.m_scene.GetGraph().GetNodeContent(context.m_currentGraphPosition);
if (!parentData || !parentData->RTTI_IsTypeOf(SceneData::GraphData::MeshData::TYPEINFO_Uuid()))
{
AZ_Error(Utilities::ErrorWindow, false,
"Tried to construct bitangent stream attribute for invalid or non-mesh parent data");
return Events::ProcessingResult::Failure;
}
const SceneData::GraphData::MeshData* const parentMeshData = azrtti_cast<SceneData::GraphData::MeshData*>(parentData.get());
size_t vertexCount = parentMeshData->GetVertexCount();
int sdkMeshIndex = parentMeshData->GetSdkMeshIndex();
if (sdkMeshIndex < 0 || sdkMeshIndex >= currentNode->mNumMeshes)
{
AZ_Error(Utilities::ErrorWindow, false,
"Tried to construct bitangent stream attribute for invalid or non-mesh parent data, mesh index is invalid");
return Events::ProcessingResult::Failure;
}
aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
if (!mesh->HasTangentsAndBitangents())
{
return Events::ProcessingResult::Ignored;
}
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 v = 0; v < mesh->mNumVertices; ++v)
{
const Vector3 bitangent(
AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mBitangents[v]));
bitangentStream->AppendBitangent(bitangent);
}
AZStd::string nodeName(AZStd::string::format("%s",m_defaultNodeName));
Containers::SceneGraph::NodeIndex newIndex =
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
Events::ProcessingResult bitangentResults;
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, bitangentStream, newIndex, nodeName.c_str());
bitangentResults = Events::Process(dataPopulated);
if (bitangentResults != Events::ProcessingResult::Failure)
{
bitangentResults = AddAttributeDataNodeWithContexts(dataPopulated);
}
return bitangentResults;
}
} // namespace FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,40 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <SceneAPI/FbxSceneBuilder/ImportContexts/AssImpImportContexts.h>
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,172 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/string/conversions.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.h>
#include <SceneAPI/FbxSceneBuilder/Importers/FbxImporterUtilities.h>
#include <SceneAPI/FbxSceneBuilder/FbxSceneSystem.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 FbxSceneBuilder
{
AssImpBlendShapeImporter::AssImpBlendShapeImporter()
{
BindToCall(&AssImpBlendShapeImporter::ImportBlendShapes);
}
void AssImpBlendShapeImporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<AssImpBlendShapeImporter, SceneCore::LoadingComponent>()->Version(1);
}
}
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;
}
Events::ProcessingResultCombiner combinedBlendShapeResult;
for (int meshIdx = 0; meshIdx < numMesh; meshIdx++)
{
int meshId = context.m_sourceNode.GetAssImpNode()->mMeshes[meshIdx];
aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[meshId];
if (!aiMesh->mNumAnimMeshes)
{
continue;
}
for (int animIdx = 0; animIdx < aiMesh->mNumAnimMeshes; animIdx++)
{
AZStd::shared_ptr<SceneData::GraphData::BlendShapeData> blendShapeData =
AZStd::make_shared<SceneData::GraphData::BlendShapeData>();
aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[animIdx];
AZStd::string nodeName(aiAnimMesh->mName.C_Str());
size_t dotIndex = nodeName.rfind('.');
if (dotIndex != AZStd::string::npos)
{
nodeName.erase(0, dotIndex + 1);
}
RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition, "BlendShape");
AZ_TraceContext("Blend shape name", nodeName);
int firstMeshVertexIndex = -1;
int previousMeshVertexIndex = -1;
int verticesInMeshFace = 0;
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);
AZ::Vector3 normal;
if (aiAnimMesh->HasNormals())
{
normal = AssImpSDKWrapper::AssImpTypeConverter::ToVector3(aiAnimMesh->mNormals[vertIdx]);
context.m_sourceSceneSystem.SwapVec3ForUpAxis(normal);
normal.NormalizeSafe();
}
blendShapeData->AddVertex(vertex, normal);
blendShapeData->SetVertexIndexToControlPointIndexMap(vertIdx, vertIdx);
}
// 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];
}
blendShapeData->AddFace(blendFace);
}
// 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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,39 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <SceneAPI/FbxSceneBuilder/ImportContexts/AssImpImportContexts.h>
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,197 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/FbxSceneBuilder/FbxSceneSystem.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.h>
#include <SceneAPI/FbxSceneBuilder/Importers/FbxImporterUtilities.h>
#include <SceneAPI/FbxSDKWrapper/FbxNodeWrapper.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 FbxSceneBuilder
{
AssImpBoneImporter::AssImpBoneImporter()
{
BindToCall(&AssImpBoneImporter::ImportBone);
}
void AssImpBoneImporter::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<AssImpBoneImporter, SceneCore::LoadingComponent>()->Version(1);
}
}
void EnumBonesInNode(
const aiScene* scene, const aiNode* node, AZStd::unordered_map<AZStd::string, aiNode*>& mainBoneList,
AZStd::unordered_map<AZStd::string, aiBone*>& boneLookup)
{
/* From AssImp Documentation
a) Create a map or a similar container to store which nodes are necessary for the skeleton. Pre-initialise it for all nodes with a "no".
b) For each bone in the mesh:
b1) Find the corresponding node in the scene's hierarchy by comparing their names.
b2) Mark this node as "yes" in the necessityMap.
b3) Mark all of its parents the same way until you 1) find the mesh's node or 2) the parent of the mesh's node.
c) Recursively iterate over the node hierarchy
c1) If the node is marked as necessary, copy it into the skeleton and check its children
c2) If the node is marked as not necessary, skip it and do not iterate over its children.
*/
for (unsigned meshIndex = 0; meshIndex < node->mNumMeshes; ++meshIndex)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[meshIndex]];
for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
{
aiBone* bone = mesh->mBones[boneIndex];
aiNode* boneNode = scene->mRootNode->FindNode(bone->mName);
aiNode* boneParent = boneNode->mParent;
mainBoneList[bone->mName.C_Str()] = boneNode;
boneLookup[bone->mName.C_Str()] = bone;
while (boneParent && boneParent != node && boneParent != node->mParent && boneParent != scene->mRootNode)
{
mainBoneList[boneParent->mName.C_Str()] = boneParent;
boneParent = boneParent->mParent;
}
}
}
}
void EnumChildren(
const aiScene* scene, const aiNode* node, AZStd::unordered_map<AZStd::string, aiNode*>& mainBoneList,
AZStd::unordered_map<AZStd::string, aiBone*>& boneLookup)
{
EnumBonesInNode(scene, node, mainBoneList, boneLookup);
for (unsigned childIndex = 0; childIndex < node->mNumChildren; ++childIndex)
{
const aiNode* child = node->mChildren[childIndex];
EnumChildren(scene, child, mainBoneList, boneLookup);
}
}
Events::ProcessingResult AssImpBoneImporter::ImportBone(AssImpNodeEncounteredContext& context)
{
AZ_TraceContext("Importer", "Bone");
aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
if (IsPivotNode(currentNode->mName))
{
return Events::ProcessingResult::Ignored;
}
bool isBone = false;
if (NodeParentIsOfType(context.m_scene.GetGraph(), context.m_currentGraphPosition, DataTypes::IBoneData::TYPEINFO_Uuid()))
{
isBone = true;
}
else
{
AZStd::unordered_map<AZStd::string, aiNode*> mainBoneList;
AZStd::unordered_map<AZStd::string, aiBone*> boneLookup;
EnumChildren(scene, scene->mRootNode, mainBoneList, boneLookup);
if (mainBoneList.find(currentNode->mName.C_Str()) != mainBoneList.end())
{
isBone = true;
}
// 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 = currentNode->mTransformation;
aiNode* parent = currentNode->mParent;
while (parent)
{
transform = parent->mTransformation * transform;
parent = parent->mParent;
}
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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,39 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <SceneAPI/FbxSceneBuilder/ImportContexts/AssImpImportContexts.h>
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -58,11 +58,10 @@ namespace AZ
AZStd::shared_ptr<DataTypes::IGraphObject> parentData =
context.m_scene.GetGraph().GetNodeContent(context.m_currentGraphPosition);
AZ_Assert(
parentData && parentData->RTTI_IsTypeOf(SceneData::GraphData::MeshData::TYPEINFO_Uuid()),
"Tried to construct color stream attribute for invalid or non-mesh parent data");
if (!parentData || !parentData->RTTI_IsTypeOf(SceneData::GraphData::MeshData::TYPEINFO_Uuid()))
{
AZ_Error(Utilities::ErrorWindow, false,
"Tried to construct color stream attribute for invalid or non-mesh parent data");
return Events::ProcessingResult::Failure;
}
const SceneData::GraphData::MeshData* const parentMeshData = azrtti_cast<SceneData::GraphData::MeshData*>(parentData.get());
@@ -71,9 +70,7 @@ namespace AZ
int sdkMeshIndex = parentMeshData->GetSdkMeshIndex();
if (sdkMeshIndex < 0)
{
AZ_Error(
Utilities::ErrorWindow,
false,
AZ_Error(Utilities::ErrorWindow, false,
"Tried to construct color stream attribute for invalid or non-mesh parent data, mesh index is missing");
return Events::ProcessingResult::Failure;
}
@@ -0,0 +1,94 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <assimp/scene.h>
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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)
{
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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,38 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <assimp/matrix4x4.h>
#include <AzCore/std/string/string.h>
struct aiNode;
struct aiScene;
struct aiString;
namespace AZ::SceneAPI::FbxSceneBuilder
{
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
@@ -11,9 +11,11 @@
*/
#include <SceneAPI/FbxSceneBuilder/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/FbxSceneBuilder/Importers/FbxImporterUtilities.h>
#include <SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.h>
@@ -54,6 +56,8 @@ namespace AZ
}
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];
@@ -62,8 +66,6 @@ namespace AZ
int materialIndex = assImpMesh->mMaterialIndex;
AZ_TraceContext("Material Index", materialIndex);
AZStd::unordered_map<int, AZStd::shared_ptr<SceneData::GraphData::MaterialData>> materialMap;
auto matFound = materialMap.find(materialIndex);
AZStd::shared_ptr<SceneData::GraphData::MaterialData> material;
@@ -82,19 +84,58 @@ namespace AZ
material->SetMaterialName(assImpMaterial->GetName());
material->SetTexture(DataTypes::IMaterialData::TextureMapType::Diffuse,
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Diffuse).c_str());
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Diffuse)).c_str());
material->SetTexture(DataTypes::IMaterialData::TextureMapType::Specular,
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Specular).c_str());
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Specular)).c_str());
material->SetTexture(DataTypes::IMaterialData::TextureMapType::Bump,
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Bump).c_str());
ResolveTexturePath(context.m_sourceScene.GetSceneFileName(),
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Bump)).c_str());
material->SetTexture(DataTypes::IMaterialData::TextureMapType::Normal,
assImpMaterial->GetTextureFileName(SDKMaterial::MaterialWrapper::MaterialMapType::Normal).c_str());
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)
@@ -135,6 +176,39 @@ namespace AZ
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 FBX 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.
// FBX files may have paths to textures, relative to the FBX file. Check for those, first.
if (AZ::IO::FileIOBase::GetInstance()->Exists(texturePathRelativeToScene.c_str()))
{
return texturePathRelativeToScene;
}
return textureFilePath;
}
} // namespace FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -33,6 +33,9 @@ namespace AZ
static void Reflect(ReflectContext* context);
Events::ProcessingResult ImportMaterials(AssImpSceneNodeAppendedContext& context);
private:
AZStd::string ResolveTexturePath(const AZStd::string& sceneFilePath, const AZStd::string& textureFilePath) const;
};
} // namespace FbxSceneBuilder
} // namespace SceneAPI
@@ -9,19 +9,17 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.h>
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
#include <assimp/scene.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/FbxSceneBuilder/FbxSceneSystem.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
#include <SceneAPI/SceneData/GraphData/MeshData.h>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
namespace AZ
{
@@ -46,80 +44,23 @@ namespace AZ
Events::ProcessingResult AssImpMeshImporter::ImportMesh(AssImpNodeEncounteredContext& context)
{
AZ_TraceContext("Importer", "Mesh");
if (!context.m_sourceNode.ContainsMesh())
{
return Events::ProcessingResult::Ignored;
}
AZStd::unordered_map<int, int> assImpMatIndexToLYIndex;
int lyMeshIndex = 0;
aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
for (int m = 0; m < currentNode->mNumMeshes; ++m)
if (!context.m_sourceNode.ContainsMesh() || IsSkinnedMesh(*currentNode, *scene))
{
AZStd::shared_ptr<SceneData::GraphData::MeshData> newMesh =
AZStd::make_shared<SceneData::GraphData::MeshData>();
newMesh->SetUnitSizeInMeters(context.m_sourceSceneSystem.GetUnitSizeInMeters());
newMesh->SetOriginalUnitSizeInMeters(context.m_sourceSceneSystem.GetOriginalUnitSizeInMeters());
newMesh->SetSdkMeshIndex(m);
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);
context.m_sourceSceneSystem.SwapVec3ForUpAxis(vertex);
context.m_sourceSceneSystem.ConvertUnit(vertex);
newMesh->AddPosition(vertex);
if (mesh->HasNormals())
{
AZ::Vector3 normal(
mesh->mNormals[vertIdx].x,
mesh->mNormals[vertIdx].y,
mesh->mNormals[vertIdx].z);
context.m_sourceSceneSystem.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 has a face with %d vertices, only 3 vertices are supported per face.");
continue;
}
for (int idx = 0; idx < face.mNumIndices; ++idx)
{
meshFace.vertexIndex[idx] = face.mIndices[idx];
}
newMesh->AddFace(meshFace, assImpMatIndexToLYIndex[mesh->mMaterialIndex]);
}
context.m_createdData.push_back(std::move(newMesh));
return Events::ProcessingResult::Ignored;
}
return Events::ProcessingResult::Success;
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 FbxSceneBuilder
} // namespace SceneAPI
@@ -0,0 +1,67 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <assimp/scene.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.h>
#include <SceneAPI/FbxSceneBuilder/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 FbxSceneBuilder
{
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");
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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,39 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <SceneAPI/FbxSceneBuilder/ImportContexts/AssImpImportContexts.h>
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,149 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <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/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/FbxImporterUtilities.h>
#include <SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.h>
#include <SceneAPI/SceneCore/Events/ImportEventContext.h>
#include <SceneAPI/SceneData/GraphData/SkinWeightData.h>
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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(1);
}
}
Events::ProcessingResult AssImpSkinWeightsImporter::ImportSkinWeights(AssImpSceneNodeAppendedContext& context)
{
AZ_TraceContext("Importer", "Skin Weights");
aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
if(currentNode->mNumMeshes <= 0)
{
return Events::ProcessingResult::Ignored;
}
Events::ProcessingResultCombiner combinedSkinWeightsResult;
for(unsigned m = 0; m < currentNode->mNumMeshes; ++m)
{
auto mesh = scene->mMeshes[currentNode->mMeshes[m]];
for(unsigned b = 0; b < mesh->mNumBones; ++b)
{
auto bone = mesh->mBones[b];
if(bone->mNumWeights <= 0)
{
continue;
}
AZStd::string skinWeightName = s_skinWeightName;
skinWeightName += AZStd::to_string(b);
RenamedNodesMap::SanitizeNodeName(skinWeightName, context.m_scene.GetGraph(), context.m_currentGraphPosition);
AZStd::shared_ptr<SceneData::GraphData::SkinWeightData> skinDeformer = BuildSkinWeightData(bone, mesh->mNumVertices);
Containers::SceneGraph::NodeIndex newIndex =
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, skinWeightName.c_str());
AZ_Error("SkinWeightsImporter", newIndex.IsValid(), "Failed to create SceneGraph node for attribute.");
if (!newIndex.IsValid())
{
combinedSkinWeightsResult += Events::ProcessingResult::Failure;
continue;
}
Events::ProcessingResult skinWeightsResult;
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, skinDeformer, newIndex, skinWeightName);
skinWeightsResult = Events::Process(dataPopulated);
if (skinWeightsResult != Events::ProcessingResult::Failure)
{
skinWeightsResult = AddAttributeDataNodeWithContexts(dataPopulated);
}
combinedSkinWeightsResult += skinWeightsResult;
}
}
return combinedSkinWeightsResult.GetResult();
}
AZStd::shared_ptr<SceneData::GraphData::SkinWeightData> AssImpSkinWeightsImporter::BuildSkinWeightData(aiBone* bone, unsigned numVertices)
{
AZStd::shared_ptr<SceneData::GraphData::SkinWeightData> skinWeightData =
AZStd::make_shared<SceneData::GraphData::SkinWeightData>();
// Cache the new object and the link info for now so it can be resolved at a later point when all
// names have been updated.
Pending pending;
pending.m_bone = bone;
pending.m_numVertices = numVertices;
pending.m_skinWeightData = skinWeightData;
m_pendingSkinWeights.push_back(pending);
return skinWeightData;
}
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->AppendLink(it.m_bone->mWeights[weight].mVertexId, link);
}
}
const auto result = m_pendingSkinWeights.empty() ? Events::ProcessingResult::Ignored : Events::ProcessingResult::Success;
m_pendingSkinWeights.clear();
return result;
}
} // namespace FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,78 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <assimp/scene.h>
#include <AzCore/std/containers/vector.h>
#include <SceneAPI/FbxSceneBuilder/ImportContexts/AssImpImportContexts.h>
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/ISkinWeightData.h>
namespace AZ
{
namespace FbxSDKWrapper
{
class FbxMeshWrapper;
}
namespace SceneData
{
namespace GraphData
{
class SkinWeightData;
}
}
namespace SceneAPI
{
namespace Events
{
class PostImportEventContext;
}
namespace FbxSceneBuilder
{
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
{
aiBone* m_bone = nullptr;
unsigned m_numVertices = 0;
AZStd::shared_ptr<SceneData::GraphData::SkinWeightData> m_skinWeightData;
};
AZStd::shared_ptr<SceneData::GraphData::SkinWeightData> BuildSkinWeightData(aiBone* bone, unsigned numVertices);
//! 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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,119 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.h>
#include <SceneAPI/FbxSceneBuilder/Importers/FbxImporterUtilities.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 <assimp/scene.h>
#include <assimp/mesh.h>
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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(1);
}
}
Events::ProcessingResult AssImpTangentStreamImporter::ImportTangentStreams(AssImpSceneNodeAppendedContext& context)
{
AZ_TraceContext("Importer", m_defaultNodeName);
if (!context.m_sourceNode.ContainsMesh())
{
return Events::ProcessingResult::Ignored;
}
aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
AZStd::shared_ptr<DataTypes::IGraphObject> parentData =
context.m_scene.GetGraph().GetNodeContent(context.m_currentGraphPosition);
if (!parentData || !parentData->RTTI_IsTypeOf(SceneData::GraphData::MeshData::TYPEINFO_Uuid()))
{
AZ_Error(Utilities::ErrorWindow, false,
"Tried to construct tangent stream attribute for invalid or non-mesh parent data");
return Events::ProcessingResult::Failure;
}
const SceneData::GraphData::MeshData* const parentMeshData = azrtti_cast<SceneData::GraphData::MeshData*>(parentData.get());
size_t vertexCount = parentMeshData->GetVertexCount();
int sdkMeshIndex = parentMeshData->GetSdkMeshIndex();
if (sdkMeshIndex < 0 || sdkMeshIndex >= currentNode->mNumMeshes)
{
AZ_Error(Utilities::ErrorWindow, false,
"Tried to construct tangent stream attribute for invalid or non-mesh parent data, mesh index is invalid");
return Events::ProcessingResult::Failure;
}
aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
if (!mesh->HasTangentsAndBitangents())
{
return Events::ProcessingResult::Ignored;
}
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 v = 0; v < mesh->mNumVertices; ++v)
{
// Vector4's constructor that takes in a vector3 sets w to 1.0f automatically.
const Vector4 tangent(AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mTangents[v]));
tangentStream->AppendTangent(tangent);
}
AZStd::string nodeName(AZStd::string::format("%s", m_defaultNodeName));
Containers::SceneGraph::NodeIndex newIndex =
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
Events::ProcessingResult tangentResults;
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, tangentStream, newIndex, nodeName.c_str());
tangentResults = Events::Process(dataPopulated);
if (tangentResults != Events::ProcessingResult::Failure)
{
tangentResults = AddAttributeDataNodeWithContexts(dataPopulated);
}
return tangentResults;
}
} // namespace FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,40 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <SceneAPI/FbxSceneBuilder/ImportContexts/AssImpImportContexts.h>
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
namespace AZ
{
namespace SceneAPI
{
namespace FbxSceneBuilder
{
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 FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
@@ -23,6 +23,7 @@
#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
#include <assimp/scene.h>
#include <SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.h>
namespace AZ
{
@@ -45,16 +46,25 @@ namespace AZ
serializeContext->Class<AssImpTransformImporter, SceneCore::LoadingComponent>()->Version(1);
}
}
Events::ProcessingResult AssImpTransformImporter::ImportTransform(AssImpSceneNodeAppendedContext& context)
{
AZ_TraceContext("Importer", "transform");
aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
if (currentNode == scene->mRootNode || IsPivotNode(currentNode->mName))
{
return Events::ProcessingResult::Ignored;
}
DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(context.m_sourceNode.GetAssImpNode()->mTransformation);
aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode);
DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform);
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.");
@@ -63,7 +73,6 @@ namespace AZ
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))
{
@@ -59,10 +59,10 @@ namespace AZ
AZStd::shared_ptr<DataTypes::IGraphObject> parentData =
context.m_scene.GetGraph().GetNodeContent(context.m_currentGraphPosition);
AZ_Assert(parentData && parentData->RTTI_IsTypeOf(SceneData::GraphData::MeshData::TYPEINFO_Uuid()),
"Tried to construct uv stream attribute for invalid or non-mesh parent data");
if (!parentData || !parentData->RTTI_IsTypeOf(SceneData::GraphData::MeshData::TYPEINFO_Uuid()))
{
AZ_Error(Utilities::ErrorWindow, false,
"Tried to construct uv stream attribute for invalid or non-mesh parent data");
return Events::ProcessingResult::Failure;
}
const SceneData::GraphData::MeshData* const parentMeshData =
@@ -75,40 +75,44 @@ namespace AZ
aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
if (!mesh->mTextureCoords[0])
{
return Events::ProcessingResult::Ignored;
}
Events::ProcessingResultCombiner combinedUvMapResults;
AZStd::shared_ptr<SceneData::GraphData::MeshVertexUVData> uvMap =
AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexUVData>();
uvMap->ReserveContainerSpace(vertexCount);
uvMap->SetCustomName(m_defaultNodeName);
for (int v = 0; v < mesh->mNumVertices; ++v)
for (int texCoordIndex = 0; texCoordIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++texCoordIndex)
{
AZ::Vector2 vertexUV(
mesh->mTextureCoords[0][v].x,
mesh->mTextureCoords[0][v].y);
uvMap->AppendUV(vertexUV);
if (!mesh->mTextureCoords[texCoordIndex])
{
continue;
}
AZStd::shared_ptr<SceneData::GraphData::MeshVertexUVData> uvMap =
AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexUVData>();
uvMap->ReserveContainerSpace(vertexCount);
AZStd::string name(AZStd::string::format("%s%d", m_defaultNodeName, texCoordIndex));
uvMap->SetCustomName(name.c_str());
for (int v = 0; v < mesh->mNumVertices; ++v)
{
AZ::Vector2 vertexUV(
mesh->mTextureCoords[texCoordIndex][v].x,
// The engine's V coordinate is reverse of how it's stored in the FBX file.
1.0f - mesh->mTextureCoords[texCoordIndex][v].y);
uvMap->AppendUV(vertexUV);
}
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;
}
Containers::SceneGraph::NodeIndex newIndex =
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, m_defaultNodeName);
Events::ProcessingResult uvMapResults;
AssImpSceneAttributeDataPopulatedContext dataPopulated(context, uvMap, newIndex, m_defaultNodeName);
uvMapResults = Events::Process(dataPopulated);
if (uvMapResults != Events::ProcessingResult::Failure)
{
uvMapResults = AddAttributeDataNodeWithContexts(dataPopulated);
}
combinedUvMapResults += uvMapResults;
return combinedUvMapResults.GetResult();
}
@@ -62,7 +62,6 @@ namespace AZ
SceneNodeFinalizeContext finalizeNode(addedAttributes);
nodeResults += Events::Process(finalizeNode);
}
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
else
{
AssImpSceneDataPopulatedContext* dataPopulatedContext = azrtti_cast<AssImpSceneDataPopulatedContext*>(&dataPopulated);
@@ -75,7 +74,6 @@ namespace AZ
AssImpSceneNodeFinalizeContext finalizeNode(addedAttributes);
nodeResults += Events::Process(finalizeNode);
}
#endif
return nodeResults.GetResult();
}
@@ -99,14 +97,12 @@ namespace AZ
SceneAttributeNodeAppendedContext nodeAppended(*dataPopulatedContext, dataPopulated.m_currentGraphPosition);
nodeResults += Events::Process(nodeAppended);
}
#ifdef ASSET_IMPORTER_SDK_SUPPORTED_TRAIT
else
{
AssImpSceneAttributeDataPopulatedContext* dataPopulatedContext = azrtti_cast<AssImpSceneAttributeDataPopulatedContext*>(&dataPopulated);
AssImpSceneAttributeNodeAppendedContext nodeAppended(*dataPopulatedContext, dataPopulated.m_currentGraphPosition);
nodeResults += Events::Process(nodeAppended);
}
#endif
return nodeResults.GetResult();
}
@@ -16,7 +16,6 @@
#include <SceneAPI/SceneCore/DataTypes/MatrixType.h>
#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
namespace AZ
{
struct Uuid;
@@ -79,4 +79,4 @@ namespace AZ
}
} // namespace FbxSceneBuilder
} // namespace SceneAPI
} // namespace AZ
} // namespace AZ
@@ -0,0 +1,98 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <assimp/mesh.h>
#include <assimp/scene.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <SceneAPI/FbxSceneBuilder/FbxSceneSystem.h>
#include <SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneData/GraphData/BlendShapeData.h>
#include <SceneAPI/SceneData/GraphData/MeshData.h>
namespace AZ::SceneAPI::FbxSceneBuilder
{
bool BuildSceneMeshFromAssImpMesh(aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& 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;
}
for (int m = 0; m < currentNode->mNumMeshes; ++m)
{
auto newMesh = makeMeshFunc();
newMesh->SetUnitSizeInMeters(sceneSystem.GetUnitSizeInMeters());
newMesh->SetOriginalUnitSizeInMeters(sceneSystem.GetOriginalUnitSizeInMeters());
newMesh->SetSdkMeshIndex(m);
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, vertIdx);
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];
}
newMesh->AddFace(meshFace, assImpMatIndexToLYIndex[mesh->mMaterialIndex]);
}
meshes.push_back(newMesh);
}
return true;
}
}
@@ -0,0 +1,48 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
struct aiNode;
struct aiScene;
namespace AZ
{
namespace FbxSDKWrapper
{
class FbxMeshWrapper;
}
namespace SceneData
{
namespace GraphData
{
class MeshData;
class BlendShapeData;
}
}
namespace SceneAPI
{
namespace DataTypes
{
class IGraphObject;
}
class FbxSceneSystem;
namespace FbxSceneBuilder
{
bool BuildSceneMeshFromAssImpMesh(aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& sceneSystem, AZStd::vector<AZStd::shared_ptr<DataTypes::IGraphObject>>& meshes,
const AZStd::function<AZStd::shared_ptr<SceneData::GraphData::MeshData>()>& makeMeshFunc);
}
}
}
@@ -10,16 +10,4 @@
#
set(FILES
../../ImportContexts/AssImpImportContexts.h
../../ImportContexts/AssImpImportContexts.cpp
../../Importers/AssImpColorStreamImporter.h
../../Importers/AssImpColorStreamImporter.cpp
../../Importers/AssImpMaterialImporter.h
../../Importers/AssImpMaterialImporter.cpp
../../Importers/AssImpMeshImporter.h
../../Importers/AssImpMeshImporter.cpp
../../Importers/AssImpUvMapImporter.h
../../Importers/AssImpUvMapImporter.cpp
../../Importers/AssImpTransformImporter.h
../../Importers/AssImpTransformImporter.cpp
)
@@ -21,6 +21,8 @@ set(FILES
ImportContexts/ImportContexts.cpp
ImportContexts/FbxImportContexts.h
ImportContexts/FbxImportContexts.cpp
ImportContexts/AssImpImportContexts.h
ImportContexts/AssImpImportContexts.cpp
Importers/FbxAnimationImporter.h
Importers/FbxAnimationImporter.cpp
Importers/FbxBoneImporter.h
@@ -50,6 +52,36 @@ set(FILES
Importers/FbxUvMapImporter.cpp
Importers/Utilities/FbxMeshImporterUtilities.h
Importers/Utilities/FbxMeshImporterUtilities.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
)