Merge remote-tracking branch 'upstream/development' into Atom/santorac/OptionalSceneApiMaterialConversion3

This commit is contained in:
santorac
2021-08-19 10:37:01 -07:00
2804 changed files with 58396 additions and 83254 deletions
@@ -21,11 +21,16 @@ namespace AZ
{
AssImpMaterialWrapper::AssImpMaterialWrapper(aiMaterial* aiMaterial)
:SDKMaterial::MaterialWrapper(aiMaterial)
:m_assImpMaterial(aiMaterial)
{
AZ_Assert(aiMaterial, "Asset Importer Material cannot be null");
}
aiMaterial* AssImpMaterialWrapper::GetAssImpMaterial() const
{
return m_assImpMaterial;
}
AZStd::string AssImpMaterialWrapper::GetName() const
{
return m_assImpMaterial->GetName().C_Str();
@@ -194,7 +199,7 @@ namespace AZ
AZStd::string AssImpMaterialWrapper::GetTextureFileName(MaterialMapType textureType) const
{
/// Engine currently doesn't support multiple textures. Right now we only use first texture.
int textureIndex = 0;
unsigned int textureIndex = 0;
aiString absTexturePath;
switch (textureType)
{
@@ -20,6 +20,7 @@ namespace AZ
AZ_RTTI(AssImpMaterialWrapper, "{66992628-CFCE-441B-8849-9344A49AFAC9}", SDKMaterial::MaterialWrapper);
AssImpMaterialWrapper(aiMaterial* aiMaterial);
~AssImpMaterialWrapper() override = default;
aiMaterial* GetAssImpMaterial() const;
AZStd::string GetName() const override;
AZ::u64 GetUniqueId() const override;
AZ::Vector3 GetDiffuseColor() const override;
@@ -38,6 +39,9 @@ namespace AZ
AZStd::optional<bool> GetUseEmissiveMap() const;
AZStd::optional<float> GetEmissiveIntensity() const;
AZStd::optional<bool> GetUseAOMap() const;
protected:
aiMaterial* m_assImpMaterial = nullptr;
};
} // namespace AssImpSDKWrapper
}// namespace AZ
@@ -17,14 +17,16 @@ namespace AZ
namespace AssImpSDKWrapper
{
AssImpNodeWrapper::AssImpNodeWrapper(aiNode* sourceNode)
:SDKNode::NodeWrapper(sourceNode)
: m_assImpNode(sourceNode)
{
AZ_Assert(m_assImpNode, "Asset Importer Node cannot be null");
}
AssImpNodeWrapper::~AssImpNodeWrapper()
aiNode* AssImpNodeWrapper::GetAssImpNode() const
{
return m_assImpNode;
}
const char* AssImpNodeWrapper::GetName() const
{
return m_assImpNode->mName.C_Str();
@@ -20,7 +20,8 @@ namespace AZ
public:
AZ_RTTI(AssImpNodeWrapper, "{1043260B-9076-49B7-AD38-EF62E85F7C1D}", SDKNode::NodeWrapper);
AssImpNodeWrapper(aiNode* sourceNode);
~AssImpNodeWrapper() override;
~AssImpNodeWrapper() override = default;
aiNode* GetAssImpNode() const;
const char* GetName() const override;
AZ::u64 GetUniqueId() const override;
int GetChildCount() const override;
@@ -28,6 +29,9 @@ namespace AZ
const bool ContainsMesh();
bool ContainsBones(const aiScene& scene) const;
int GetMaterialCount() const override;
protected:
aiNode* m_assImpNode = nullptr;
};
} // namespace AssImpSDKWrapper
}// namespace AZ
@@ -25,15 +25,10 @@ namespace AZ
namespace AssImpSDKWrapper
{
AssImpSceneWrapper::AssImpSceneWrapper()
: SDKScene::SceneWrapperBase()
{
}
AssImpSceneWrapper::AssImpSceneWrapper(aiScene* aiScene)
: SDKScene::SceneWrapperBase(aiScene)
{
}
AssImpSceneWrapper::~AssImpSceneWrapper()
: m_assImpScene(aiScene)
{
}
@@ -70,6 +65,9 @@ namespace AZ
// This results in the loss of the offset matrix data for nodes without a mesh which is required for the Transform Importer.
m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, false);
// The remove empty bones flag is on by default, but doesn't do anything internal to AssImp right now.
// This is here as a bread crumb to save others times investigating issues with empty bones.
// m_importer.SetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, false);
m_sceneFileName = fileName;
m_assImpScene = m_importer.ReadFile(fileName,
aiProcess_Triangulate //Triangulates all faces of all meshes
@@ -111,6 +109,11 @@ namespace AZ
m_importer.FreeScene();
}
const aiScene* AssImpSceneWrapper::GetAssImpScene() const
{
return m_assImpScene;
}
AZStd::pair<AssImpSceneWrapper::AxisVector, int32_t> AssImpSceneWrapper::GetUpVectorAndSign() const
{
AZStd::pair<AssImpSceneWrapper::AxisVector, int32_t> result(AxisVector::Z, 1);
@@ -21,13 +21,14 @@ namespace AZ
AZ_RTTI(AssImpSceneWrapper, "{43A61F62-DCD4-4132-B80B-F2FBC80740BC}", SDKScene::SceneWrapperBase);
AssImpSceneWrapper();
AssImpSceneWrapper(aiScene* aiScene);
~AssImpSceneWrapper();
~AssImpSceneWrapper() override = default;
bool LoadSceneFromFile(const char* fileName) override;
bool LoadSceneFromFile(const AZStd::string& fileName) override;
const std::shared_ptr<SDKNode::NodeWrapper> GetRootNode() const override;
std::shared_ptr<SDKNode::NodeWrapper> GetRootNode() override;
virtual const aiScene* GetAssImpScene() const;
void Clear() override;
enum class AxisVector
@@ -43,7 +44,7 @@ namespace AZ
AZStd::string GetSceneFileName() const { return m_sceneFileName; }
protected:
const aiScene* m_assImpScene = nullptr;
Assimp::Importer m_importer;
// FBX SDK automatically resolved relative paths to textures based on the current file location.
@@ -12,21 +12,6 @@ namespace AZ
{
namespace SDKMaterial
{
MaterialWrapper::MaterialWrapper(aiMaterial* assImpMaterial)
: m_assImpMaterial(assImpMaterial)
{
}
MaterialWrapper::~MaterialWrapper()
{
m_assImpMaterial = nullptr;
}
aiMaterial* MaterialWrapper::GetAssImpMaterial()
{
return m_assImpMaterial;
}
AZStd::string MaterialWrapper::GetName() const
{
return AZStd::string();
@@ -34,10 +34,7 @@ namespace AZ
BaseColor
};
MaterialWrapper(aiMaterial* assImpmaterial);
virtual ~MaterialWrapper();
aiMaterial* GetAssImpMaterial();
virtual ~MaterialWrapper() = default;
virtual AZStd::string GetName() const;
virtual AZ::u64 GetUniqueId() const;
@@ -47,9 +44,6 @@ namespace AZ
virtual AZ::Vector3 GetEmissiveColor() const;
virtual float GetOpacity() const;
virtual float GetShininess() const;
protected:
aiMaterial* m_assImpMaterial = nullptr;
};
} // namespace SDKMaterial
} // namespace AZ
@@ -12,21 +12,6 @@ namespace AZ
{
namespace SDKNode
{
NodeWrapper::NodeWrapper(aiNode* aiNode)
: m_assImpNode(aiNode)
{
}
NodeWrapper::~NodeWrapper()
{
m_assImpNode = nullptr;
}
aiNode* NodeWrapper::GetAssImpNode()
{
return m_assImpNode;
}
const char* NodeWrapper::GetName() const
{
return "";
+2 -7
View File
@@ -7,6 +7,7 @@
*/
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
struct aiNode;
@@ -19,9 +20,7 @@ namespace AZ
public:
AZ_RTTI(NodeWrapper, "{5EB0897B-9728-44B7-B056-BA34AAF14715}");
NodeWrapper() = default;
NodeWrapper(aiNode* aiNode);
virtual ~NodeWrapper();
virtual ~NodeWrapper() = default;
enum CurveNodeComponent
{
@@ -30,16 +29,12 @@ namespace AZ
Component_Z
};
aiNode* GetAssImpNode();
virtual const char* GetName() const;
virtual AZ::u64 GetUniqueId() const;
virtual int GetMaterialCount() const;
virtual int GetChildCount()const;
virtual const std::shared_ptr<NodeWrapper> GetChild(int childIndex) const;
aiNode* m_assImpNode = nullptr;
};
} //namespace Node
} //namespace AZ
@@ -13,12 +13,6 @@ namespace AZ
{
const char* SceneWrapperBase::s_defaultSceneName = "myScene";
SceneWrapperBase::SceneWrapperBase(aiScene* aiScene)
: m_assImpScene(aiScene)
{
}
bool SceneWrapperBase::LoadSceneFromFile([[maybe_unused]] const char* fileName)
{
return false;
@@ -40,12 +34,5 @@ namespace AZ
void SceneWrapperBase::Clear()
{
}
const aiScene* SceneWrapperBase::GetAssImpScene() const
{
return m_assImpScene;
}
} //namespace Scene
}// namespace AZ
@@ -20,9 +20,7 @@ namespace AZ
{
public:
AZ_RTTI(SceneWrapperBase, "{703CD344-2C75-4F30-8CE2-6BDEF2511AFD}");
SceneWrapperBase() = default;
virtual ~SceneWrapperBase() = default;
SceneWrapperBase(aiScene* aiScene);
virtual bool LoadSceneFromFile(const char* fileName);
virtual bool LoadSceneFromFile(const AZStd::string& fileName);
@@ -31,10 +29,6 @@ namespace AZ
virtual std::shared_ptr<SDKNode::NodeWrapper> GetRootNode();
virtual void Clear();
virtual const aiScene* GetAssImpScene() const;
const aiScene* m_assImpScene = nullptr;
static const char* s_defaultSceneName;
};
@@ -147,7 +147,9 @@ namespace AZ
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<AssImpAnimationImporter, SceneCore::LoadingComponent>()->Version(5); // [LYN-4226] Invert PostRotation matrix in animation chains
// Revision 5: [LYN-4226] Invert PostRotation matrix in animation chains
// Revision 6: Handle duplicate blend shape animations
serializeContext->Class<AssImpAnimationImporter, SceneCore::LoadingComponent>()->Version(6);
}
}
@@ -445,11 +447,11 @@ namespace AZ
AZStd::unordered_set<AZStd::string> boneList;
for (int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
{
aiMesh* mesh = scene->mMeshes[meshIndex];
for (int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
{
aiBone* bone = mesh->mBones[boneIndex];
@@ -612,10 +614,10 @@ namespace AZ
ValueToKeyDataMap valueToKeyDataMap;
// Key time can be less than zero, normalize to have zero be the lowest time.
double keyOffset = 0;
for (int keyIdx = 0; keyIdx < meshMorphAnim->mNumKeys; keyIdx++)
for (unsigned int keyIdx = 0; keyIdx < meshMorphAnim->mNumKeys; keyIdx++)
{
aiMeshMorphKey& key = meshMorphAnim->mKeys[keyIdx];
for (int valIdx = 0; valIdx < key.mNumValuesAndWeights; ++valIdx)
for (unsigned int valIdx = 0; valIdx < key.mNumValuesAndWeights; ++valIdx)
{
int currentValue = key.mValues[valIdx];
KeyData thisKey(key.mWeights[valIdx], key.mTime);
@@ -631,24 +633,33 @@ namespace AZ
for (const auto& [meshIdx, keys] : valueToKeyDataMap)
{
if (static_cast<AZ::u32>(meshIdx) >= mesh->mNumAnimMeshes)
{
AZ_Error(
"AnimationImporter", false,
"Mesh %s has an animation mesh index reference of %d, but only has %d animation meshes. Skipping importing this. This is an error in the source scene file that should be corrected.",
mesh->mName.C_Str(), meshIdx, mesh->mNumAnimMeshes);
continue;
}
AZStd::shared_ptr<SceneData::GraphData::BlendShapeAnimationData> morphAnimNode =
AZStd::make_shared<SceneData::GraphData::BlendShapeAnimationData>();
const size_t numKeyFrames = GetNumKeyFrames(keys.size(), animation->mDuration, animation->mTicksPerSecond);
const size_t numKeyFrames = GetNumKeyFrames(static_cast<AZ::u32>(keys.size()), animation->mDuration, animation->mTicksPerSecond);
morphAnimNode->ReserveKeyFrames(numKeyFrames);
morphAnimNode->SetTimeStepBetweenFrames(s_defaultTimeStepBetweenFrames);
aiAnimMesh* aiAnimMesh = mesh->mAnimMeshes[meshIdx];
AZStd::string_view nodeName(aiAnimMesh->mName.C_Str());
const AZ::u32 maxKeys = keys.size();
const AZ::u32 maxKeys = static_cast<AZ::u32>(keys.size());
AZ::u32 keyIdx = 0;
for (AZ::u32 frame = 0; frame < numKeyFrames; ++frame)
{
const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
float weight = 0;
if (!SampleKeyFrame(weight, keys, keys.size(), time + keyOffset, keyIdx))
if (!SampleKeyFrame(weight, keys, static_cast<AZ::u32>(keys.size()), time + keyOffset, keyIdx))
{
return Events::ProcessingResult::Failure;
}
@@ -656,12 +667,29 @@ namespace AZ
morphAnimNode->AddKeyFrame(weight);
}
// Some DCC tools, like Maya, include a full path separated by '.' in the node names.
// For example, "cone_skin_blendShapeNode.cone_squash"
// Downstream processing doesn't want anything but the last part of that node name,
// so find the last '.' and remove anything before it.
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()));
// Duplicates can exist if an anim mesh had a name with a suffix like .001, in that case
// AssImp will strip off that suffix. Note that this behavior is separate from the
// scan for a period in the node name that came before this.
AZStd::string originalNodeName(AZStd::string::format("%s_%s", s_animationNodeName, nodeName.data()));
AZStd::string animNodeName(originalNodeName);
if (RenamedNodesMap::SanitizeNodeName(
animNodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition, originalNodeName.c_str()))
{
AZ_Warning(
"AnimationImporter", false,
"Duplicate animations were found with the name %s on mesh %s. The duplicate will be named %s.",
originalNodeName.c_str(), mesh->mName.C_Str(), animNodeName.c_str());
}
Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
context.m_currentGraphPosition, animNodeName.c_str(), AZStd::move(morphAnimNode));
context.m_scene.GetGraph().MakeEndPoint(addNode);
@@ -89,11 +89,11 @@ namespace AZ
bitangentStream->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene);
bitangentStream->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
for (unsigned int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
for (int v = 0; v < mesh->mNumVertices; ++v)
for (unsigned int v = 0; v < mesh->mNumVertices; ++v)
{
if (!mesh->HasTangentsAndBitangents())
{
@@ -40,7 +40,9 @@ namespace AZ
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<AssImpBlendShapeImporter, SceneCore::LoadingComponent>()->Version(3); // LYN-2576
// Revision 3: Fixed an issue where jack.fbx was failing to process
// Revision 4: Handle duplicate blend shape animations
serializeContext->Class<AssImpBlendShapeImporter, SceneCore::LoadingComponent>()->Version(4);
}
}
@@ -80,28 +82,90 @@ namespace AZ
// AssImp separates meshes that have multiple materials.
// This code re-combines them to match previous FBX SDK behavior,
// so they can be separated by engine code instead.
AZStd::map<AZStd::string_view, AZStd::vector<AZStd::pair<int, int>>> animToMeshToAnimMeshIndices;
// Can't de-dupe nodes in the first loop because we can't generate names until we create nodes later.
// Because meshes are split on material at this point and need to be recombined, we can be in a position where
// There is a legit duped anim mesh that needs to be combined based on the outer non-anim mesh,
// or this is a duplicately named anim mesh that needs to be de-duped. There is also the case where both are true,
// it's a duplicate name and the non-anim mesh has to be deduped.
// Helper struct to track an anim mesh and its associated mesh.
struct AnimMeshAndSceneMeshIndex
{
AnimMeshAndSceneMeshIndex(const aiAnimMesh* aiAnimMesh, const aiMesh* aiMesh)
: m_aiAnimMesh(aiAnimMesh)
, m_aiMesh(aiMesh)
{
}
const aiAnimMesh* m_aiAnimMesh = nullptr;
const aiMesh* m_aiMesh = nullptr;
};
// Helper struct to track all anim meshes at an index for all scene meshes.
struct AnimMeshAndSceneMeshes
{
AZStd::vector<AnimMeshAndSceneMeshIndex> m_animMeshAndSceneMeshIndex;
};
// Map the animation index to the list of anim meshes at that index, and the mesh associated with those anim meshes.
AZStd::map<int, AnimMeshAndSceneMeshes> animMeshIndexToSceneMeshes;
for (int nodeMeshIdx = 0; nodeMeshIdx < numMesh; nodeMeshIdx++)
{
int sceneMeshIdx = context.m_sourceNode.GetAssImpNode()->mMeshes[nodeMeshIdx];
const aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[sceneMeshIdx];
for (int animIdx = 0; animIdx < aiMesh->mNumAnimMeshes; animIdx++)
for (unsigned int animIdx = 0; animIdx < aiMesh->mNumAnimMeshes; animIdx++)
{
aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[animIdx];
animToMeshToAnimMeshIndices[aiAnimMesh->mName.C_Str()].emplace_back(nodeMeshIdx, animIdx);
// This code executes if:
// A mesh in the FBX file had multiple materials and blend shapes.
// This means that AssImp splits that mesh to one material per mesh.
// AssImp creates a set of anim meshes for each mesh based on that split.
// This verifies that those anim mesh arrays are in the same order across all split meshes, if it fails
// it means this logic needs to be updated, but it also catches that here earlier in an obvious way,
// instead of failing later in a harder to track way.
if (animMeshIndexToSceneMeshes.contains(animIdx))
{
const AnimMeshAndSceneMeshIndex& firstExistingAnim(
animMeshIndexToSceneMeshes[animIdx].m_animMeshAndSceneMeshIndex[0]);
if (strcmp(
firstExistingAnim.m_aiAnimMesh->mName.C_Str(),
aiAnimMesh->mName.C_Str()) != 0)
{
AZ_Error(
Utilities::ErrorWindow, false,
"Meshes %s and %s on node %s have mismatched animations %s and %s at index %d. This can be resolved by "
"either manually separating meshes by material in the source scene file, or by updating this logic to "
"handle out of order animation indices.",
firstExistingAnim.m_aiMesh->mName.C_Str(),
aiMesh->mName.C_Str(),
context.m_sourceNode.GetName(),
firstExistingAnim.m_aiAnimMesh->mName.C_Str(),
aiAnimMesh->mName.C_Str(), animIdx);
return Events::ProcessingResult::Failure;
}
}
animMeshIndexToSceneMeshes[animIdx].m_animMeshAndSceneMeshIndex.emplace_back(
AnimMeshAndSceneMeshIndex(aiAnimMesh, aiMesh));
}
}
for (const auto& animToMeshIndex : animToMeshToAnimMeshIndices)
for (const auto& animMeshToSceneMeshes : animMeshIndexToSceneMeshes)
{
AZStd::shared_ptr<SceneData::GraphData::BlendShapeData> blendShapeData =
AZStd::make_shared<SceneData::GraphData::BlendShapeData>();
if (animMeshToSceneMeshes.second.m_animMeshAndSceneMeshIndex.size() == 0)
{
AZ_Error(Utilities::ErrorWindow, false, "Blend shape animations were expected but missing on node %s.",
context.m_sourceNode.GetName());
return Events::ProcessingResult::Failure;
}
// Some DCC tools, like Maya, include a full path separated by '.' in the node names.
// For example, "cone_skin_blendShapeNode.cone_squash"
// Downstream processing doesn't want anything but the last part of that node name,
// so find the last '.' and remove anything before it.
AZStd::string nodeName(animToMeshIndex.first);
AZStd::string nodeName(animMeshToSceneMeshes.second.m_animMeshAndSceneMeshIndex[0].m_aiAnimMesh->mName.C_Str());
size_t dotIndex = nodeName.rfind('.');
if (dotIndex != AZStd::string::npos)
{
@@ -109,12 +173,11 @@ namespace AZ
}
int vertexOffset = 0;
RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition, "BlendShape");
AZ_TraceContext("Blend shape name", nodeName);
for (const auto& meshIndex : animToMeshIndex.second)
for (const auto& animMeshAndSceneIndex : animMeshToSceneMeshes.second.m_animMeshAndSceneMeshIndex)
{
int sceneMeshIdx = context.m_sourceNode.GetAssImpNode()->mMeshes[meshIndex.first];
const aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[sceneMeshIdx];
const aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[meshIndex.second];
const aiAnimMesh* aiAnimMesh = animMeshAndSceneIndex.m_aiAnimMesh;
const aiMesh* aiMesh = animMeshAndSceneIndex.m_aiMesh;
AZStd::bitset<SceneData::GraphData::BlendShapeData::MaxNumUVSets> uvSetUsedFlags;
for (AZ::u8 uvSetIndex = 0; uvSetIndex < SceneData::GraphData::BlendShapeData::MaxNumUVSets; ++uvSetIndex)
@@ -130,7 +193,7 @@ namespace AZ
blendShapeData->ReserveData(
aiAnimMesh->mNumVertices, aiAnimMesh->HasTangentsAndBitangents(), uvSetUsedFlags, colorSetUsedFlags);
for (int vertIdx = 0; vertIdx < aiAnimMesh->mNumVertices; ++vertIdx)
for (unsigned int vertIdx = 0; vertIdx < aiAnimMesh->mNumVertices; ++vertIdx)
{
AZ::Vector3 vertex(AssImpSDKWrapper::AssImpTypeConverter::ToVector3(aiAnimMesh->mVertices[vertIdx]));
@@ -184,7 +247,7 @@ namespace AZ
}
// 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)
for (unsigned int faceIdx = 0; faceIdx < aiMesh->mNumFaces; ++faceIdx)
{
aiFace face = aiMesh->mFaces[faceIdx];
DataTypes::IBlendShapeData::Face blendFace;
@@ -199,7 +262,8 @@ namespace AZ
face.mNumIndices);
continue;
}
for (int idx = 0; idx < face.mNumIndices; ++idx)
for (unsigned int idx = 0; idx < face.mNumIndices; ++idx)
{
blendFace.vertexIndex[idx] = face.mIndices[idx] + vertexOffset;
}
@@ -207,11 +271,8 @@ namespace AZ
blendShapeData->AddFace(blendFace);
}
vertexOffset += aiMesh->mNumVertices;
}
// Report problem if no vertex or face converted to MeshData
if (blendShapeData->GetVertexCount() <= 0 || blendShapeData->GetFaceCount() <= 0)
{
@@ -103,10 +103,6 @@ namespace AZ
}
}
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;
@@ -56,7 +56,7 @@ namespace AZ
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
// This node has at least one mesh, verify that the color channel counts are the same for all meshes.
const int expectedColorChannels = scene->mMeshes[currentNode->mMeshes[0]]->GetNumColorChannels();
const unsigned int expectedColorChannels = scene->mMeshes[currentNode->mMeshes[0]]->GetNumColorChannels();
const bool allMeshesHaveSameNumberOfColorChannels =
AZStd::all_of(currentNode->mMeshes + 1, currentNode->mMeshes + currentNode->mNumMeshes, [scene, expectedColorChannels](const unsigned int meshIndex)
{
@@ -80,17 +80,16 @@ namespace AZ
const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
Events::ProcessingResultCombiner combinedVertexColorResults;
for (int colorSetIndex = 0; colorSetIndex < expectedColorChannels; ++colorSetIndex)
for (unsigned int colorSetIndex = 0; colorSetIndex < expectedColorChannels; ++colorSetIndex)
{
AZStd::shared_ptr<SceneData::GraphData::MeshVertexColorData> vertexColors =
AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexColorData>();
vertexColors->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
for (unsigned int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
for (int v = 0; v < mesh->mNumVertices; ++v)
for (unsigned int v = 0; v < mesh->mNumVertices; ++v)
{
if (colorSetIndex < mesh->GetNumColorChannels())
{
@@ -105,7 +105,7 @@ namespace AZ
nodesWithNoMesh.emplace(currentNode->mName.C_Str());
}
for (int childIndex = 0; childIndex < currentNode->mNumChildren; ++childIndex)
for (unsigned int childIndex = 0; childIndex < currentNode->mNumChildren; ++childIndex)
{
queue.push(currentNode->mChildren[childIndex]);
}
@@ -135,18 +135,13 @@ namespace AZ
const aiBone* bone = FindFirstBoneByNodeName(node, boneByNameMap);
if (bone)
{
const DataTypes::MatrixType inverseOffsetMatrix = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(bone->mOffsetMatrix).GetInverseFull();
const aiBone* parentBone = FindFirstBoneByNodeName(node->mParent, boneByNameMap);
if (parentBone)
{
DataTypes::MatrixType inverseOffsetMatrix = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(bone->mOffsetMatrix).GetInverseFull();
const DataTypes::MatrixType parentBoneOffsetMatrix = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(parentBone->mOffsetMatrix);
return parentBoneOffsetMatrix * inverseOffsetMatrix;
}
else
{
return inverseOffsetMatrix;
}
}
return AssImpSDKWrapper::AssImpTypeConverter::ToTransform(GetConcatenatedLocalTransform(node));
@@ -176,7 +171,7 @@ namespace AZ
return true;
}
for (int childIndex = 0; childIndex < node->mNumChildren; ++childIndex)
for (unsigned int childIndex = 0; childIndex < node->mNumChildren; ++childIndex)
{
const aiNode* childNode = node->mChildren[childIndex];
if (RecursiveHasChildBone(childNode, boneByNameMap))
@@ -56,9 +56,9 @@ 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)
for (unsigned int idx = 0; idx < context.m_sourceNode.GetAssImpNode()->mNumMeshes; ++idx)
{
int meshIndex = context.m_sourceNode.m_assImpNode->mMeshes[idx];
int meshIndex = context.m_sourceNode.GetAssImpNode()->mMeshes[idx];
const aiMesh* assImpMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[meshIndex];
AZ_Assert(assImpMesh, "Asset Importer Mesh should not be null.");
int materialIndex = assImpMesh->mMaterialIndex;
@@ -91,11 +91,11 @@ namespace AZ
tangentStream->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene);
tangentStream->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
for (unsigned int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
for (int v = 0; v < mesh->mNumVertices; ++v)
for (unsigned int v = 0; v < mesh->mNumVertices; ++v)
{
if (!mesh->HasTangentsAndBitangents())
{
@@ -62,7 +62,7 @@ namespace AZ
// so they can be separated by engine code instead.
bool foundTextureCoordinates = false;
AZStd::array<int, AI_MAX_NUMBER_OF_TEXTURECOORDS> meshesPerTextureCoordinateIndex = {};
for (int localMeshIndex = 0; localMeshIndex < currentNode->mNumMeshes; ++localMeshIndex)
for (unsigned int localMeshIndex = 0; localMeshIndex < currentNode->mNumMeshes; ++localMeshIndex)
{
aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[localMeshIndex]];
for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex)
@@ -110,7 +110,7 @@ namespace AZ
uvMap->ReserveContainerSpace(vertexCount);
bool customNameFound = false;
AZStd::string name(AZStd::string::format("%s%d", m_defaultNodeName, texCoordIndex));
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
for (unsigned int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
if(mesh->mTextureCoords[texCoordIndex])
@@ -136,7 +136,7 @@ namespace AZ
}
}
for (int v = 0; v < mesh->mNumVertices; ++v)
for (unsigned int v = 0; v < mesh->mNumVertices; ++v)
{
if (mesh->mTextureCoords[texCoordIndex])
{
@@ -40,7 +40,7 @@ namespace AZ::SceneAPI::SceneBuilder
// This code re-combines them to match previous FBX SDK behavior,
// so they can be separated by engine code instead.
int vertOffset = 0;
for (int m = 0; m < currentNode->mNumMeshes; ++m)
for (unsigned int m = 0; m < currentNode->mNumMeshes; ++m)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[m]];
@@ -50,7 +50,7 @@ namespace AZ::SceneAPI::SceneBuilder
assImpMatIndexToLYIndex.insert(AZStd::pair<int, int>(mesh->mMaterialIndex, lyMeshIndex++));
}
for (int vertIdx = 0; vertIdx < mesh->mNumVertices; ++vertIdx)
for (unsigned int vertIdx = 0; vertIdx < mesh->mNumVertices; ++vertIdx)
{
AZ::Vector3 vertex(mesh->mVertices[vertIdx].x, mesh->mVertices[vertIdx].y, mesh->mVertices[vertIdx].z);
@@ -68,7 +68,7 @@ namespace AZ::SceneAPI::SceneBuilder
}
}
for (int faceIdx = 0; faceIdx < mesh->mNumFaces; ++faceIdx)
for (unsigned int faceIdx = 0; faceIdx < mesh->mNumFaces; ++faceIdx)
{
aiFace face = mesh->mFaces[faceIdx];
AZ::SceneAPI::DataTypes::IMeshData::Face meshFace;
@@ -82,7 +82,7 @@ namespace AZ::SceneAPI::SceneBuilder
face.mNumIndices);
continue;
}
for (int idx = 0; idx < face.mNumIndices; ++idx)
for (unsigned int idx = 0; idx < face.mNumIndices; ++idx)
{
meshFace.vertexIndex[idx] = face.mIndices[idx] + vertOffset;
}
@@ -222,7 +222,12 @@ namespace AZ
int childCount = node.m_node->GetChildCount();
for (int i = 0; i < childCount; ++i)
{
std::shared_ptr<AssImpSDKWrapper::AssImpNodeWrapper> child = std::make_shared<AssImpSDKWrapper::AssImpNodeWrapper>(node.m_node->GetChild(i)->GetAssImpNode());
const std::shared_ptr<SDKNode::NodeWrapper> nodeWrapper = node.m_node->GetChild(i);
auto assImpNodeWrapper = azrtti_cast<AssImpSDKWrapper::AssImpNodeWrapper*>(nodeWrapper.get());
AZ_Assert(assImpNodeWrapper, "Child node is not the expected AssImpNodeWrapper type");
std::shared_ptr<AssImpSDKWrapper::AssImpNodeWrapper> child = std::make_shared<AssImpSDKWrapper::AssImpNodeWrapper>(assImpNodeWrapper->GetAssImpNode());
if (child)
{
nodes.emplace(AZStd::move(child), newNode);
@@ -11,6 +11,7 @@
#include <AzCore/std/string/string.h>
#include <AzCore/RTTI/RTTI.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
#include <AzCore/std/string/fixed_string.h>
namespace AZ
{
@@ -18,7 +19,8 @@ namespace AZ
{
namespace DataTypes
{
const static AZStd::string s_advancedDisabledString = "Disabled";
static const char* s_advancedDisabledString = "Disabled";
class IMeshAdvancedRule
: public IRule
{
@@ -104,7 +104,7 @@ namespace AZ
}
AZStd::shared_ptr<Containers::Scene> AssetImportRequest::LoadSceneFromVerifiedPath(const AZStd::string& assetFilePath, const Uuid& sourceGuid,
RequestingApplication requester)
RequestingApplication requester, const Uuid& loadingComponentUuid)
{
AZStd::string sceneName;
AzFramework::StringFunc::Path::GetFileName(assetFilePath.c_str(), sceneName);
@@ -113,7 +113,7 @@ namespace AZ
// Unique pointer, will deactivate and clean up once going out of scope.
SceneCore::EntityConstructor::EntityPointer loaders =
SceneCore::EntityConstructor::BuildEntity("Scene Loading", SceneCore::LoadingComponent::TYPEINFO_Uuid());
SceneCore::EntityConstructor::BuildEntity("Scene Loading", loadingComponentUuid);
ProcessingResultCombiner areAllPrepared;
AssetImportRequestBus::BroadcastResult(areAllPrepared, &AssetImportRequestBus::Events::PrepareForAssetLoading, *scene, requester);
@@ -102,8 +102,9 @@ namespace AZ
//! @param sourceGuid The guid assigned to the source file (not the manifest).
//! @param requester The application making the request to load the file. This can be used to optimize the type and amount of data
//! to load.
//! @param loadingComponentUuid The UUID assigned to the loading component.
static AZStd::shared_ptr<Containers::Scene> LoadSceneFromVerifiedPath(const AZStd::string& assetFilePath,
const Uuid&sourceGuid, RequestingApplication requester);
const Uuid& sourceGuid, RequestingApplication requester, const Uuid& loadingComponentUuid);
//! Utility function to determine if a given file path points to a scene manifest file (.assetinfo).
//! @param filePath A relative or absolute path to the file to check.
@@ -11,6 +11,7 @@
#include <AzCore/Math/Guid.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
#include <SceneAPI/SceneCore/Mocks/Events/MockAssetImportRequest.h>
namespace AZ
@@ -184,7 +185,7 @@ namespace AZ
EXPECT_CALL(handler, UpdateManifest(_, _, _)).Times(0);
AZStd::shared_ptr<Containers::Scene> result =
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic);
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic, SceneCore::LoadingComponent::TYPEINFO_Uuid());
EXPECT_EQ(nullptr, result);
}
@@ -207,7 +208,7 @@ namespace AZ
EXPECT_CALL(handler, UpdateManifest(_, _, _)).Times(0);
AZStd::shared_ptr<Containers::Scene> result =
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic);
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic, SceneCore::LoadingComponent::TYPEINFO_Uuid());
EXPECT_EQ(nullptr, result);
}
@@ -230,7 +231,7 @@ namespace AZ
EXPECT_CALL(handler, UpdateManifest(_, _, _)).Times(0);
AZStd::shared_ptr<Containers::Scene> result =
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic);
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic, SceneCore::LoadingComponent::TYPEINFO_Uuid());
EXPECT_EQ(nullptr, result);
}
@@ -253,7 +254,7 @@ namespace AZ
EXPECT_CALL(handler, UpdateManifest(_, _, _)).Times(0);
AZStd::shared_ptr<Containers::Scene> result =
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic);
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic, SceneCore::LoadingComponent::TYPEINFO_Uuid());
EXPECT_EQ(nullptr, result);
}
@@ -285,7 +286,7 @@ namespace AZ
EXPECT_CALL(manifestHandler, UpdateManifest(_, _, _)).Times(1);
AZStd::shared_ptr<Containers::Scene> result =
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic);
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic, SceneCore::LoadingComponent::TYPEINFO_Uuid());
EXPECT_EQ(nullptr, result);
}
@@ -313,7 +314,7 @@ namespace AZ
EXPECT_CALL(manifestHandler, UpdateManifest(_, _, _)).Times(1);
AZStd::shared_ptr<Containers::Scene> result =
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic);
AssetImportRequest::LoadSceneFromVerifiedPath("test.asset", m_testId, Events::AssetImportRequest::RequestingApplication::Generic, SceneCore::LoadingComponent::TYPEINFO_Uuid());
EXPECT_NE(nullptr, result);
}
@@ -9,6 +9,15 @@
#include "DebugOutput.h"
#include <AzCore/std/optional.h>
#include <AzCore/IO/SystemFile.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace AZ::SceneAPI::Utilities
{
void DebugOutput::Write(const char* name, const char* data)
@@ -118,4 +127,63 @@ namespace AZ::SceneAPI::Utilities
{
return m_output;
}
void WriteAndLog(AZ::IO::SystemFile& dbgFile, const char* strToWrite)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "%s", strToWrite);
dbgFile.Write(strToWrite, strlen(strToWrite));
dbgFile.Write("\n", strlen("\n"));
}
void DebugOutput::BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene, AZStd::string productName)
{
const int debugSceneGraphVersion = 1;
AZStd::string debugSceneFile;
AzFramework::StringFunc::Path::ConstructFull(outputFolder, productName.c_str(), debugSceneFile);
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "outputFolder %s, name %s.\n", outputFolder, productName.c_str());
AZ::IO::SystemFile dbgFile;
if (dbgFile.Open(debugSceneFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
WriteAndLog(dbgFile, AZStd::string::format("ProductName: %s", productName.c_str()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", debugSceneGraphVersion).c_str());
WriteAndLog(dbgFile, scene->GetName().c_str());
const AZ::SceneAPI::Containers::SceneGraph& sceneGraph = scene->GetGraph();
auto names = sceneGraph.GetNameStorage();
auto content = sceneGraph.GetContentStorage();
auto pairView = AZ::SceneAPI::Containers::Views::MakePairView(names, content);
auto view = AZ::SceneAPI::Containers::Views::MakeSceneGraphDownwardsView<
AZ::SceneAPI::Containers::Views::BreadthFirst>(
sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true);
for (auto&& viewIt : view)
{
if (viewIt.second == nullptr)
{
continue;
}
AZ::SceneAPI::DataTypes::IGraphObject* graphObject = const_cast<AZ::SceneAPI::DataTypes::IGraphObject*>(viewIt.second.get());
WriteAndLog(dbgFile, AZStd::string::format("Node Name: %s", viewIt.first.GetName()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("Node Path: %s", viewIt.first.GetPath()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("Node Type: %s", graphObject->RTTI_GetTypeName()).c_str());
AZ::SceneAPI::Utilities::DebugOutput debugOutput;
viewIt.second->GetDebugOutput(debugOutput);
if (!debugOutput.GetOutput().empty())
{
WriteAndLog(dbgFile, debugOutput.GetOutput().c_str());
}
}
dbgFile.Close();
static const AZ::Data::AssetType dbgSceneGraphAssetType("{07F289D1-4DC7-4C40-94B4-0A53BBCB9F0B}");
productList.AddProduct(productName, AZ::Uuid::CreateName(productName.c_str()), dbgSceneGraphAssetType,
AZStd::nullopt, AZStd::nullopt);
}
}
}
@@ -13,8 +13,25 @@
#include <SceneAPI/SceneCore/Utilities/HashHelper.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
#include <cinttypes>
namespace AZ
{
namespace SceneAPI
{
namespace Containers
{
class Scene;
}
namespace Events
{
struct ExportProduct;
class ExportProductList;
}
}
}
namespace AZ::SceneAPI::Utilities
{
class DebugOutput
@@ -41,6 +58,8 @@ namespace AZ::SceneAPI::Utilities
SCENE_CORE_API const AZStd::string& GetOutput() const;
SCENE_CORE_API static void BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene, AZStd::string productName);
protected:
AZStd::string m_output;
};
@@ -18,11 +18,6 @@ namespace AZ
{
namespace DataTypes = AZ::SceneAPI::DataTypes;
const AZStd::string MaterialData::s_DiffuseMapName = "Diffuse";
const AZStd::string MaterialData::s_SpecularMapName = "Specular";
const AZStd::string MaterialData::s_BumpMapName = "Bump";
const AZStd::string MaterialData::s_emptyString = "";
MaterialData::MaterialData()
: m_isNoDraw(false)
, m_diffuseColor(AZ::Vector3::CreateOne())
@@ -72,7 +67,7 @@ namespace AZ
return result->second;
}
return s_emptyString;
return m_emptyString;
}
void MaterialData::SetNoDraw(bool isNoDraw)
@@ -11,6 +11,7 @@
#include <SceneAPI/SceneData/SceneDataConfiguration.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMaterialData.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/fixed_string.h>
namespace AZ
{
@@ -96,10 +97,7 @@ namespace AZ
bool m_isNoDraw;
const static AZStd::string s_DiffuseMapName;
const static AZStd::string s_SpecularMapName;
const static AZStd::string s_BumpMapName;
const static AZStd::string s_emptyString;
const AZStd::string m_emptyString;
// A unique id which is used to identify a material in a fbx.
// This is the same as the ID in the fbx file's FbxNode
@@ -40,7 +40,7 @@ namespace AZ
void ManifestWidget::BuildFromScene(const AZStd::shared_ptr<Containers::Scene>& scene)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
AZ_PROFILE_FUNCTION(Editor);
ui->m_tabs->clear();
m_pages.clear();
@@ -80,7 +80,7 @@ namespace AZ
bool ManifestWidget::AddObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
AZ_PROFILE_FUNCTION(Editor);
for (ManifestWidgetPage* page : m_pages)
{
if (page->SupportsType(object))
@@ -76,7 +76,7 @@ namespace AZ
bool ManifestWidgetPage::AddObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
AZ_PROFILE_FUNCTION(Editor);
if (!SupportsType(object))
{
return false;
@@ -218,7 +218,7 @@ namespace AZ
void ManifestWidgetPage::RefreshPage()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
AZ_PROFILE_FUNCTION(Editor);
m_propertyEditor->InvalidateAll();
m_propertyEditor->ExpandAll();
}