Get a mesh's morph targets based on the scene graph hierarchy, instead of mesh name (#1128)

When building a mesh's morph targets, the exporter has to identify the base
mesh in addition to each morph target mesh. Previously this was done by
searching the entire scene graph for nodes
* of type IBlendShapeData
* whose parent's name matches the name of the Atom model

This is problematic for a few reasons. The first is that the Atom model's
name may have been based on the optimized mesh node. When this happens,
the `OptimizedMeshSuffix` that is used in the Scene Graph node's name is
stripped off of the Atom model's name. The result is that the *unoptimized*
mesh is used as the base mesh for the blend shapes, instead of the
optimized blend shape. This of course results in disaster, since the
optimizer reorders the vertices, and the base mesh will not match the
optimized one. The second is that it is not really necessary to do the
search based on the node name at all. All of a mesh's blend shapes are
child nodes of the base IMeshData node. With this change, the base mesh
is located based on the node data pointer, and all of its child
IBlendShapeData nodes are added to the set of blend shapes to process. This
way, the Atom model's name isn't involved in the lookup.
main
Chris Burel 5 years ago committed by GitHub
parent b73de269ee
commit 5d4226df16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,9 +14,11 @@
#include <Atom/RPI.Reflect/Model/MorphTargetDelta.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/Containers/Utilities/SceneGraphUtilities.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/Containers/Views/FilterIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzCore/Asset/AssetManagerBus.h>
@ -27,62 +29,42 @@ namespace AZ::RPI
AZStd::unordered_map<AZStd::string, MorphTargetExporter::SourceBlendShapeInfo> MorphTargetExporter::GetBlendShapeInfos(
const Containers::Scene& scene,
const AZStd::optional<AZStd::string>& filterMeshName) const
const MeshData* meshData) const
{
const Containers::SceneGraph& sceneGraph = scene.GetGraph();
const auto contentStorage = sceneGraph.GetContentStorage();
const auto nameStorage = sceneGraph.GetNameStorage();
AZStd::unordered_map<AZStd::string, SourceBlendShapeInfo> result;
const auto foundBaseMeshIter = AZStd::find_if(sceneGraph.GetContentStorage().cbegin(), sceneGraph.GetContentStorage().cend(), [meshData](const auto& nodeData)
{
return nodeData.get() == meshData;
});
if (foundBaseMeshIter == sceneGraph.GetContentStorage().cend())
{
return {};
}
const auto baseMeshNodeIndex = sceneGraph.ConvertToNodeIndex(foundBaseMeshIter);
const auto keyValueView = Containers::Views::MakePairView(nameStorage, contentStorage);
const auto filteredView = Containers::Views::MakeFilterView(keyValueView, Containers::DerivedTypeFilter<DataTypes::IBlendShapeData>());
for (const auto& [name, object] : filteredView)
const auto childBlendShapeDatas = Containers::MakeDerivedFilterView<DataTypes::IBlendShapeData>(
Containers::Views::MakeSceneGraphChildView(sceneGraph, baseMeshNodeIndex, sceneGraph.GetContentStorage().cbegin(), true)
);
AZStd::unordered_map<AZStd::string, SourceBlendShapeInfo> result;
for (auto it = childBlendShapeDatas.cbegin(); it != childBlendShapeDatas.cend(); ++it)
{
const Containers::SceneGraph::NodeIndex sceneNodeIndex = sceneGraph.Find(name.GetPath());
const Containers::SceneGraph::NodeIndex blendShapeNodeIndex = sceneGraph.ConvertToNodeIndex(it.GetBaseIterator().GetBaseIterator().GetHierarchyIterator());
AZStd::set<AZ::Crc32> types;
Events::GraphMetaInfoBus::Broadcast(&Events::GraphMetaInfo::GetVirtualTypes, types, scene, sceneNodeIndex);
if (types.find(Events::GraphMetaInfo::GetIgnoreVirtualType()) == types.end())
Events::GraphMetaInfoBus::Broadcast(&Events::GraphMetaInfo::GetVirtualTypes, types, scene, blendShapeNodeIndex);
if (!types.contains(Events::GraphMetaInfo::GetIgnoreVirtualType()))
{
const char* sceneNodePath = name.GetPath();
const Containers::SceneGraph::NodeIndex nodeIndex = sceneGraph.Find(sceneNodePath);
if (nodeIndex.IsValid())
{
const AZStd::string meshNodeName = SourceBlendShapeInfo::GetMeshNodeName(sceneGraph, nodeIndex);
if (!filterMeshName.has_value() ||
(filterMeshName.has_value() && filterMeshName.value() == meshNodeName))
{
const AZStd::string blendShapeName = sceneGraph.GetNodeName(nodeIndex).GetName();
SourceBlendShapeInfo& blendShapeInfo = result[blendShapeName];
blendShapeInfo.m_sceneNodeIndices.push_back(nodeIndex);
}
}
else
{
AZ_Warning(ModelAssetBuilderComponent::s_builderName, false, "Cannot retrieve scene graph index for blend shape node with path %s.", sceneNodePath);
}
const AZStd::string blendShapeName{sceneGraph.GetNodeName(blendShapeNodeIndex).GetName(), sceneGraph.GetNodeName(blendShapeNodeIndex).GetNameLength()};
result[blendShapeName].m_sceneNodeIndices.emplace_back(blendShapeNodeIndex);
}
}
return result;
}
AZStd::string MorphTargetExporter::SourceBlendShapeInfo::GetMeshNodeName(const Containers::SceneGraph& sceneGraph,
const Containers::SceneGraph::NodeIndex& sceneNodeIndex)
{
const auto* blendShapeData =
azrtti_cast<const DataTypes::IBlendShapeData*>(sceneGraph.GetNodeContent(sceneNodeIndex).get());
AZ_Assert(blendShapeData, "Cannot get mesh node name from scene node. Node is expected to be a blend shape.");
if (blendShapeData)
{
Containers::SceneGraph::NodeIndex morphMeshParentIndex = sceneGraph.GetNodeParent(sceneNodeIndex);
return sceneGraph.GetNodeName(morphMeshParentIndex).GetName();
}
return {};
}
void MorphTargetExporter::ProduceMorphTargets(const Containers::Scene& scene,
uint32_t vertexOffset,
const ModelAssetBuilderComponent::SourceMeshContent& sourceMesh,
@ -92,9 +74,14 @@ namespace AZ::RPI
{
const Containers::SceneGraph& sceneGraph = scene.GetGraph();
#if defined(AZ_ENABLE_TRACING)
const auto baseMeshIt = AZStd::find(sceneGraph.GetContentStorage().cbegin(), sceneGraph.GetContentStorage().cend(), sourceMesh.m_meshData);
const Containers::SceneGraph::NodeIndex baseMeshIndex = sceneGraph.ConvertToNodeIndex(baseMeshIt);
const AZStd::string_view baseMeshName{sceneGraph.GetNodeName(baseMeshIndex).GetName(), sceneGraph.GetNodeName(baseMeshIndex).GetNameLength()};
#endif
// Get the blend shapes for the given mesh
const AZStd::string_view meshName = sourceMesh.m_name.GetStringView();
AZStd::unordered_map<AZStd::string, SourceBlendShapeInfo> blendShapeInfos = GetBlendShapeInfos(scene, meshName);
AZStd::unordered_map<AZStd::string, SourceBlendShapeInfo> blendShapeInfos = GetBlendShapeInfos(scene, sourceMesh.m_meshData.get());
for (const auto& iter : blendShapeInfos)
{
@ -109,12 +96,12 @@ namespace AZ::RPI
{
#if defined(AZ_ENABLE_TRACING)
const Containers::SceneGraph::NodeIndex morphMeshParentIndex = sceneGraph.GetNodeParent(sceneNodeIndex);
const char* meshNodeName = sceneGraph.GetNodeName(morphMeshParentIndex).GetName();
const AZStd::string_view sourceMeshName{sceneGraph.GetNodeName(morphMeshParentIndex).GetName(), sceneGraph.GetNodeName(morphMeshParentIndex).GetNameLength()};
#endif
AZ_Assert(AZ::StringFunc::Equal(sourceMesh.m_name.GetCStr(), meshNodeName, /*bCaseSensitive=*/true),
"Scene graph mesh node (%s) has a different name than the product mesh (%s).",
meshNodeName, sourceMesh.m_name.GetCStr());
AZ_Assert(AZ::StringFunc::Equal(baseMeshName, sourceMeshName, /*bCaseSensitive=*/true),
"Scene graph mesh node (%.*s) has a different name than the product mesh (%.*s).",
AZ_STRING_ARG(sourceMeshName), AZ_STRING_ARG(baseMeshName));
const DataTypes::MatrixType globalTransform = Utilities::BuildWorldTransform(sceneGraph, sceneNodeIndex);
BuildMorphTargetMesh(vertexOffset, sourceMesh, productMesh, metaAssetCreator, blendShapeName, blendShapeData, globalTransform, coordSysConverter, scene.GetSourceFilename());

@ -39,12 +39,9 @@ namespace AZ
struct SourceBlendShapeInfo
{
AZStd::vector<AZ::SceneAPI::Containers::SceneGraph::NodeIndex> m_sceneNodeIndices;
static AZStd::string GetMeshNodeName(const AZ::SceneAPI::Containers::SceneGraph& sceneGraph,
const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& sceneNodeIndex);
};
//! Retrieve all scene graph nodes per blend shape for all available blend shapes.
AZStd::unordered_map<AZStd::string, SourceBlendShapeInfo> GetBlendShapeInfos(const AZ::SceneAPI::Containers::Scene& scene, const AZStd::optional<AZStd::string>& filterMeshName = AZStd::nullopt) const;
AZStd::unordered_map<AZStd::string, SourceBlendShapeInfo> GetBlendShapeInfos(const AZ::SceneAPI::Containers::Scene& scene, const MeshData* meshData) const;
//! Calculate position delta tolerance that is used to indicate whether a given vertex is part of the sparse set of morphed vertices
//! or if it will be skipped and optimized out due to a hardly visible or no movement at all.

Loading…
Cancel
Save