Added configurable physics materials per asset in PhysX group in FBX Settings. (#1186)

- Added back the' Physics Materials from Asset' tick in the collider components.
- Made physics materials names case insensitive.
- Refactored how to gather material information from fbx and used the same code for exporter and physx groups.
This commit is contained in:
Aaron Ruiz Mora
2021-06-10 12:22:16 +01:00
committed by GitHub
parent adf6d93a06
commit 9d41954d0e
20 changed files with 374 additions and 231 deletions
@@ -300,7 +300,7 @@ namespace Physics
{
auto foundMaterialConfiguration = AZStd::find_if(m_materialLibrary.begin(), m_materialLibrary.end(), [&materialName](const auto& data)
{
return data.m_configuration.m_surfaceType == materialName;
return AZ::StringFunc::Equal(data.m_configuration.m_surfaceType, materialName, false/*bCaseSensitive*/);
});
if (foundMaterialConfiguration != m_materialLibrary.end())
@@ -377,15 +377,16 @@ namespace Physics
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<Physics::MaterialSelection>("Physics Material", "Select physics material library and which materials to use for the object")
editContext->Class<Physics::MaterialSelection>("Physics Materials", "Select which physics materials to use for each element of this object")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "Mesh Surfaces", "Specify which Physics Material to use for each element of this object")
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "", "")
->ElementAttribute(Attributes::MaterialLibraryAssetId, &MaterialSelection::GetMaterialLibraryId)
->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &MaterialSelection::GetMaterialSlotLabel)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->ElementAttribute(AZ::Edit::Attributes::ReadOnly, &MaterialSelection::AreMaterialSlotsReadOnly)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
}
@@ -25,6 +25,8 @@ namespace AZ
namespace Physics
{
static constexpr AZStd::string_view DefaultPhysicsMaterialLabel = "<Default Physics Material>";
/// Physics material
/// =========================
/// This is the interface to the wrapper around native material type (such as PxMaterial in PhysX gem)
@@ -17,21 +17,6 @@
namespace Physics
{
namespace Internal
{
bool ShapeConfigurationVersionConverter(
[[maybe_unused]] AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() <= 1)
{
classElement.RemoveElementByName(AZ_CRC_CE("UseMaterialsFromAsset"));
}
return true;
}
}
void ShapeConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
@@ -181,9 +166,10 @@ namespace Physics
->RegisterGenericType<AZStd::shared_ptr<PhysicsAssetShapeConfiguration>>();
serializeContext->Class<PhysicsAssetShapeConfiguration, ShapeConfiguration>()
->Version(2, &Internal::ShapeConfigurationVersionConverter)
->Version(3)
->Field("PhysicsAsset", &PhysicsAssetShapeConfiguration::m_asset)
->Field("AssetScale", &PhysicsAssetShapeConfiguration::m_assetScale)
->Field("UseMaterialsFromAsset", &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset)
->Field("SubdivisionLevel", &PhysicsAssetShapeConfiguration::m_subdivisionLevel)
;
@@ -196,6 +182,7 @@ namespace Physics
->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_assetScale, "Asset Scale", "The scale of the asset shape")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset, "Physics materials from asset", "Auto-set physics materials using asset's physics material names")
;
}
}
@@ -140,7 +140,7 @@ namespace Physics
AZ::Data::Asset<AZ::Data::AssetData> m_asset{ AZ::Data::AssetLoadBehavior::PreLoad };
AZ::Vector3 m_assetScale = AZ::Vector3::CreateOne();
bool m_useMaterialsFromAsset = false; // Not reflected or exposed to the user until there is a way to auto-match mesh's materials with physics materials
bool m_useMaterialsFromAsset = true;
AZ::u8 m_subdivisionLevel = 4; ///< The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling.
};
@@ -93,7 +93,7 @@ namespace PhysX
}
}
void ConfigStringLineEditCtrl::SetForbiddenStrings(const AZStd::unordered_set<AZStd::string>& forbiddenStrings)
void ConfigStringLineEditCtrl::SetForbiddenStrings(const UniqueStringContainer::StringSet& forbiddenStrings)
{
m_forbiddenStrings = forbiddenStrings;
}
@@ -122,7 +122,7 @@ namespace PhysX
void ConfigStringLineEditValidator::OnEditStart(AZ::Crc32 stringGroupId
, const AZStd::string& stringToEdit
, const AZStd::unordered_set<AZStd::string>& forbiddenStrings
, const UniqueStringContainer::StringSet& forbiddenStrings
, int stringMaxLength
, bool removeEditedString)
{
@@ -229,23 +229,23 @@ namespace PhysX
}
else if (attrib == Physics::MaterialConfiguration::s_forbiddenStringSet)
{
AZStd::unordered_set<AZStd::string> forbiddenStringsUnorderedSet;
UniqueStringContainer::StringSet forbiddenStringsUnorderedSet;
AZStd::set<AZStd::string> forbiddenStringsSet;
AZStd::vector<AZStd::string> forbiddenStringsVector;
if (attrValue->Read<AZStd::unordered_set<AZStd::string>>(forbiddenStringsUnorderedSet))
if (attrValue->Read<UniqueStringContainer::StringSet>(forbiddenStringsUnorderedSet))
{
GUI->SetForbiddenStrings(forbiddenStringsUnorderedSet);
}
else if (attrValue->Read<AZStd::set<AZStd::string>>(forbiddenStringsSet))
{
forbiddenStringsUnorderedSet = AZStd::unordered_set<AZStd::string>(forbiddenStringsSet.begin()
forbiddenStringsUnorderedSet = UniqueStringContainer::StringSet(forbiddenStringsSet.begin()
, forbiddenStringsSet.end());
GUI->SetForbiddenStrings(forbiddenStringsUnorderedSet);
}
else if (attrValue->Read<AZStd::vector<AZStd::string>>(forbiddenStringsVector))
{
forbiddenStringsUnorderedSet = AZStd::unordered_set<AZStd::string>(forbiddenStringsVector.begin()
forbiddenStringsUnorderedSet = UniqueStringContainer::StringSet(forbiddenStringsVector.begin()
, forbiddenStringsVector.end());
GUI->SetForbiddenStrings(forbiddenStringsUnorderedSet);
}
@@ -47,7 +47,7 @@ namespace PhysX
void OnEditStart(AZ::Crc32 stringGroupId
, const AZStd::string& stringToEdit
, const AZStd::unordered_set<AZStd::string>& forbiddenStrings
, const UniqueStringContainer::StringSet& forbiddenStrings
, int stringMaxLength
, bool removeEditedString = true);
@@ -62,7 +62,7 @@ namespace PhysX
private:
AZ::Crc32 m_currStringGroup = s_groupStringNotUnique; ///< Group of string field undergoing edit.
int m_currStringMaxLen = s_qtLineEditMaxLen; ///< Max length of string field undergoing edit.
AZStd::unordered_set<AZStd::string> m_forbiddenStrings; ///< Value of string edit widget cannot be any of these strings.
UniqueStringContainer::StringSet m_forbiddenStrings; ///< Value of string edit widget cannot be any of these strings.
UniqueStringContainer m_uniqueStringContainer; ///< Collection of groups of unique strings. Serves for validation and fixing of string input.
};
@@ -79,7 +79,7 @@ namespace PhysX
, ConfigStringLineEditValidator* validator = nullptr);
virtual ~ConfigStringLineEditCtrl();
void SetForbiddenStrings(const AZStd::unordered_set<AZStd::string>& forbiddenStrings);
void SetForbiddenStrings(const UniqueStringContainer::StringSet& forbiddenStrings);
void SetUniqueGroup(AZ::Crc32 uniqueGroup);
AZStd::string Value() const;
@@ -92,7 +92,7 @@ namespace PhysX
protected:
void ConnectWidgets() override;
AZStd::unordered_set<AZStd::string> m_forbiddenStrings; ///< Value of this line edit ctrl cannot be any of these forbidden strings.
UniqueStringContainer::StringSet m_forbiddenStrings; ///< Value of this line edit ctrl cannot be any of these forbidden strings.
ConfigStringLineEditValidator* m_pValidator = nullptr; ///< Validator for line edit widget.
AZ::Crc32 m_uniqueGroup = ConfigStringLineEditValidator::s_groupStringNotUnique; ///< String group in which line edit value must remain unique.
};
+4 -4
View File
@@ -15,12 +15,12 @@
#include <AzFramework/Physics/PropertyTypes.h>
#include <QString>
namespace PhysX
{
namespace Editor
{
static const char* const DefaultPhysicsMaterialLabel = "<Default Physics Material>";
AZ::u32 MaterialIdWidget::GetHandlerName() const
{
return Physics::Edit::MaterialIdSelector;
@@ -74,7 +74,7 @@ namespace PhysX
auto lockToDefault = [gui]()
{
gui->addItem(DefaultPhysicsMaterialLabel);
gui->addItem(QLatin1String(Physics::DefaultPhysicsMaterialLabel.data(), Physics::DefaultPhysicsMaterialLabel.size()));
gui->setCurrentIndex(0);
return false;
};
@@ -103,7 +103,7 @@ namespace PhysX
// Add default physics material first
m_libraryIds.push_back(Physics::MaterialId());
gui->addItem(DefaultPhysicsMaterialLabel);
gui->addItem(QLatin1String(Physics::DefaultPhysicsMaterialLabel.data(), Physics::DefaultPhysicsMaterialLabel.size()));
for (const auto& material : materials)
{
@@ -23,7 +23,7 @@ namespace PhysX
StringGroups::iterator stringGroupsIter = m_stringGroups.find(stringGroupId);
if (stringGroupsIter == m_stringGroups.end())
{
m_stringGroups.emplace(stringGroupId, AZStd::unordered_set<AZStd::string>());
m_stringGroups.emplace(stringGroupId, StringSet());
}
m_stringGroups[stringGroupId].insert(stringIn);
}
@@ -31,7 +31,7 @@ namespace PhysX
AZStd::string UniqueStringContainer::GetUniqueString(AZ::Crc32 stringGroupId
, const AZStd::string& stringIn
, AZ::u64 maxStringLength
, const AZStd::unordered_set<AZStd::string>& forbiddenStrings) const
, const StringSet& forbiddenStrings) const
{
StringGroups::const_iterator stringGroupsIter = m_stringGroups.find(stringGroupId);
@@ -45,7 +45,7 @@ namespace PhysX
}
AZStd::string stringOut;
const AZStd::unordered_set<AZStd::string>& stringGroup = (stringGroupsIter == m_stringGroups.end())? AZStd::unordered_set<AZStd::string>():stringGroupsIter->second;
const StringSet& stringGroup = (stringGroupsIter == m_stringGroups.end())? StringSet():stringGroupsIter->second;
// Attempts to append a post-fix value, e.g. "_1" etc., to the original string so it is unique.
// A unique post-fix index can be found by iterating total number of invalid string plus 1.
@@ -86,8 +86,8 @@ namespace PhysX
return true;
}
const AZStd::unordered_set<AZStd::string>& stringGroup = stringGroupsIter->second;
return stringGroup.find(stringIn) == stringGroup.end();
const StringSet& stringSet = stringGroupsIter->second;
return stringSet.find(stringIn) == stringSet.end();
}
void UniqueStringContainer::RemoveString(AZ::Crc32 stringGroupId
+29 -4
View File
@@ -16,13 +16,38 @@
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/string/string.h>
#include <AzCore/StringFunc/StringFunc.h>
namespace PhysX
{
/// Class that keeps track of unique strings in groups.
/// Class that keeps track of unique strings (case insensitive) in groups.
class UniqueStringContainer
{
public:
struct CaseInsensitiveStringHash
{
AZ_TYPE_INFO(UniqueStringContainer::CaseInsensitiveStringHash, "{EB80F2A1-2DEB-47CC-ABF7-592F492C20A9}");
size_t operator()(const AZStd::string& str) const
{
AZStd::string lowerStr = str;
AZStd::to_lower(lowerStr.begin(), lowerStr.end());
return AZStd::hash<AZStd::string>{}(lowerStr);
}
};
struct CaseInsensitiveStringEqual
{
AZ_TYPE_INFO(UniqueStringContainer::CaseInsensitiveStringEqual, "{6ADEA1D9-27B8-4C7A-913D-EC8191F1B6A9}");
bool operator()(const AZStd::string& arg0, const AZStd::string& arg1) const
{
return AZ::StringFunc::Equal(arg0, arg1, false/*bCaseSensitive*/);
}
};
using StringSet = AZStd::unordered_set<AZStd::string, CaseInsensitiveStringHash, CaseInsensitiveStringEqual>;
/// Add a unique string to a group of unique strings.
void AddString(AZ::Crc32 stringGroupId, const AZStd::string& stringIn);
@@ -30,7 +55,7 @@ namespace PhysX
AZStd::string GetUniqueString(AZ::Crc32 stringGroupId
, const AZStd::string& stringIn
, AZ::u64 maxStringLength
, const AZStd::unordered_set<AZStd::string>& forbiddenStrings) const;
, const StringSet& forbiddenStrings) const;
/// Checks if a string would be unique in the identified string group.
bool IsStringUnique(AZ::Crc32 stringGroupId, const AZStd::string& stringIn) const;
@@ -39,7 +64,7 @@ namespace PhysX
void RemoveString(AZ::Crc32 stringGroupId, const AZStd::string& stringIn);
private:
using StringGroups = AZStd::unordered_map<AZ::Crc32, AZStd::unordered_set<AZStd::string>>;
using StringGroups = AZStd::unordered_map<AZ::Crc32, StringSet>;
StringGroups m_stringGroups; ///< Collection of groups of unique strings, each group identified by an ID.
};
}
} // namespace PhysX
+2 -2
View File
@@ -62,8 +62,8 @@ namespace PhysX
using ShapeConfigurationList = AZStd::vector<ShapeConfigurationPair>;
ShapeConfigurationList m_colliderShapes; //!< Shapes data with optional collider configuration override.
AZStd::vector<AZStd::string> m_surfaceNames; //!< List of all surface names.
AZStd::vector<AZStd::string> m_materialNames; //!< List of all material names.
AZStd::vector<AZStd::string> m_materialNames; //!< List of material names of the mesh asset.
AZStd::vector<AZStd::string> m_physicsMaterialNames; //!< List of physics material names associated with each material.
AZStd::vector<AZ::u16> m_materialIndexPerShape; //!< An index of the material in m_materialNames for each shape.
};
@@ -698,10 +698,10 @@ namespace PhysX
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
ValidateMaterialSurfaces();
ValidateAssetMaterials();
}
void EditorColliderComponent::ValidateMaterialSurfaces()
void EditorColliderComponent::ValidateAssetMaterials()
{
const AZ::Data::Asset<Pipeline::MeshAsset>& physicsAsset = m_shapeConfiguration.m_physicsAsset.m_pxAsset;
@@ -712,7 +712,7 @@ namespace PhysX
// Here we check the material indices assigned to every shape and validate that every index is used at least once.
// It's not an error if the validation fails here but something we want to let the designers know about.
[[maybe_unused]] size_t surfacesNum = physicsAsset->m_assetData.m_surfaceNames.size();
[[maybe_unused]] size_t materialsNum = physicsAsset->m_assetData.m_materialNames.size();
const AZStd::vector<AZ::u16>& indexPerShape = physicsAsset->m_assetData.m_materialIndexPerShape;
AZStd::unordered_set<AZ::u16> usedIndices;
@@ -728,10 +728,10 @@ namespace PhysX
usedIndices.insert(index);
}
AZ_Warning("PhysX", usedIndices.size() == surfacesNum,
"EditorColliderComponent::ValidateMaterialSurfaces. Entity: %s. Number of surfaces used by the shape (%d) does not match the "
"total number of surfaces in the asset (%d). Please check that there are no convex meshes with per-face materials. Asset: %s",
GetEntity()->GetName().c_str(), usedIndices.size(), surfacesNum, physicsAsset.GetHint().c_str())
AZ_Warning("PhysX", usedIndices.size() == materialsNum,
"EditorColliderComponent::ValidateMaterialSurfaces. Entity: %s. Number of materials used by the shape (%d) does not match the "
"total number of materials in the asset (%d). Please check that there are no convex meshes with per-face materials. Asset: %s",
GetEntity()->GetName().c_str(), usedIndices.size(), materialsNum, physicsAsset.GetHint().c_str())
}
void EditorColliderComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
@@ -244,7 +244,7 @@ namespace PhysX
AZ::Data::AssetId FindMatchingPhysicsAsset(const AZ::Data::Asset<AZ::Data::AssetData>& renderMeshAsset,
const AZStd::vector<AZ::Data::AssetId>& physicsAssets);
void ValidateMaterialSurfaces();
void ValidateAssetMaterials();
void InitEventHandlers();
DebugDraw::Collider m_colliderDebugDraw;
+10 -8
View File
@@ -410,7 +410,7 @@ namespace PhysX
}
// Set the slots from the mesh asset
materialSelection.SetMaterialSlots(meshAsset->m_assetData.m_surfaceNames);
materialSelection.SetMaterialSlots(meshAsset->m_assetData.m_materialNames);
if (!assetConfiguration.m_useMaterialsFromAsset)
{
@@ -419,12 +419,14 @@ namespace PhysX
}
// Update material IDs in the selection for each slot
const AZStd::vector<AZStd::string>& meshMaterialNames = meshAsset->m_assetData.m_materialNames;
for (size_t slotIndex = 0; slotIndex < meshMaterialNames.size(); ++slotIndex)
const AZStd::vector<AZStd::string>& physicsMaterialNames = meshAsset->m_assetData.m_physicsMaterialNames;
for (size_t slotIndex = 0; slotIndex < physicsMaterialNames.size(); ++slotIndex)
{
const AZStd::string& physicsMaterialNameFromPhysicsAsset = meshMaterialNames[slotIndex];
if (physicsMaterialNameFromPhysicsAsset == DefaultPhysicsMaterialNameFromPhysicsAsset)
const AZStd::string& physicsMaterialNameFromPhysicsAsset = physicsMaterialNames[slotIndex];
if (physicsMaterialNameFromPhysicsAsset.empty() ||
physicsMaterialNameFromPhysicsAsset == Physics::DefaultPhysicsMaterialLabel)
{
materialSelection.SetMaterialId(Physics::MaterialId(), slotIndex);
continue;
}
@@ -436,9 +438,9 @@ namespace PhysX
else
{
AZ_Warning("PhysX", false,
"UpdateMaterialSelectionFromPhysicsAsset: Physics material '%s' not found in the material library. Mesh surface '%s' will use the default material.",
"UpdateMaterialSelectionFromPhysicsAsset: Physics material '%s' not found in the material library. Mesh material '%s' will use the default physics material.",
physicsMaterialNameFromPhysicsAsset.c_str(),
meshAsset->m_assetData.m_surfaceNames[slotIndex].c_str());
meshAsset->m_assetData.m_materialNames[slotIndex].c_str());
}
}
}
@@ -516,7 +518,7 @@ namespace PhysX
auto it = AZStd::find_if(m_materials.begin(), m_materials.end(), [&materialName](const auto& data)
{
return data.second->GetSurfaceTypeName() == materialName;
return AZ::StringFunc::Equal(data.second->GetSurfaceTypeName(), materialName, false/*bCaseSensitive*/);
});
if (it != m_materials.end())
{
-6
View File
@@ -21,12 +21,6 @@
namespace PhysX
{
/// Name used by physx asset exporter to indicate that the default
/// physics material should be used for a mesh surface. The exporter
/// will use it as the fallback option when it's not possible to obtain
/// the surface information from the mesh material.
static const char* const DefaultPhysicsMaterialNameFromPhysicsAsset = "<Default>";
/// PhysX implementation of Physics::Material interface
/// ===================================================
///
@@ -151,8 +151,8 @@ namespace PhysX
serializeContext->Class<MeshAssetData>()
->Field("ColliderShapes", &MeshAssetData::m_colliderShapes)
->Field("SurfaceNames", &MeshAssetData::m_surfaceNames)
->Field("MaterialNames", &MeshAssetData::m_materialNames)
->Field("SurfaceNames", &MeshAssetData::m_materialNames)
->Field("MaterialNames", &MeshAssetData::m_physicsMaterialNames)
->Field("MaterialIndexPerShape", &MeshAssetData::m_materialIndexPerShape)
;
}
@@ -97,6 +97,10 @@ namespace PhysX
nodeSelectionList.AddSelectedNode(graph.GetNodeName(nodeIndex).GetPath());
}
}
// Update list of materials slots after the group's node selection list has been gathered
group->SetSceneGraph(&graph);
group->UpdateMaterialSlots();
}
AZ::SceneAPI::Events::ProcessingResult MeshBehavior::UpdateManifest(AZ::SceneAPI::Containers::Scene& scene, ManifestAction action,
@@ -129,6 +133,8 @@ namespace PhysX
// in the same way again. To guarantee the same uuid, generate a stable one instead.
group->OverrideId(AZ::SceneAPI::DataTypes::Utilities::CreateStableUuid(scene, MeshGroup::TYPEINFO_Uuid()));
group->SetSceneGraph(&scene.GetGraph());
EBUS_EVENT(AZ::SceneAPI::Events::ManifestMetaInfoBus, InitializeObject, scene, *group);
scene.GetManifest().AddEntry(AZStd::move(group));
@@ -149,10 +155,15 @@ namespace PhysX
}
AZ::SceneAPI::Utilities::SceneGraphSelector::UpdateNodeSelection(scene.GetGraph(), group.GetSceneNodeSelectionList());
// Update list of materials slots after the group's node selection list has been updated
group.SetSceneGraph(&scene.GetGraph());
group.UpdateMaterialSlots();
updated = true;
}
return updated ? AZ::SceneAPI::Events::ProcessingResult::Success : AZ::SceneAPI::Events::ProcessingResult::Ignored;
}
} // namespace SceneAPI
} // namespace AZ
} // namespace Pipeline
} // namespace PhysX
+145 -156
View File
@@ -68,16 +68,6 @@ namespace PhysX
}
} pxDefaultErrorCallback;
// A struct to store the asset-wide material names shared by multiple shapes
struct AssetMaterialsData
{
// Material names coming from FBX, these will be Mesh Surfaces in the Collider Component
AZStd::vector<AZStd::string> m_fbxMaterialNames;
// Look-up table for fbxMaterialNames
AZStd::unordered_map<AZStd::string, size_t> m_materialIndexByName;
};
// A struct to store the geometry data per FBX node
struct NodeCollisionGeomExportData
{
@@ -153,7 +143,7 @@ namespace PhysX
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MeshExporter, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(4);
serializeContext->Class<MeshExporter, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(5);
}
}
@@ -182,100 +172,49 @@ namespace PhysX
return newIndex;
}
// Building a map between FBX material name and the corresponding Cry surface type that is set in the .mtl file.
void BuildMaterialToSurfaceTypeMap(const AZStd::string& materialFilename,
AZStd::unordered_map<AZStd::string, AZStd::string>& materialToSurfaceTypeMap)
bool UpdateAssetPhysicsMaterials(
const AZStd::vector<AZStd::string>& newMaterials,
AZStd::vector<AZStd::string>& materials,
AZStd::vector<AZStd::string>& physicsMaterials)
{
AZ::IO::SystemFile mtlFile;
bool fileOpened = mtlFile.Open(materialFilename.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY);
if (fileOpened && mtlFile.Length() != 0)
if (materials.size() != physicsMaterials.size())
{
//Read material override file into a buffer
AZStd::vector<char> buffer(mtlFile.Length());
mtlFile.Read(mtlFile.Length(), buffer.data());
mtlFile.Close();
//Apparently in rapidxml if 'parse_no_data_nodes' isn't set it creates both value and data nodes
//with the data nodes having precedence such that updating values doesn't work.
AZ::rapidxml::xml_document<char> document;
document.parse<AZ::rapidxml::parse_no_data_nodes>(buffer.data());
//Parse MTL file for materials and/or submaterials.
AZ::rapidxml::xml_node<char>* rootMaterialNode = document.first_node(AZ::GFxFramework::MaterialExport::g_materialString);
AZ::rapidxml::xml_node<char>* subMaterialNode = rootMaterialNode->first_node(AZ::GFxFramework::MaterialExport::g_subMaterialString);
if (subMaterialNode)
{
for (AZ::rapidxml::xml_node<char>* materialNode = subMaterialNode->first_node(AZ::GFxFramework::MaterialExport::g_materialString);
materialNode;
materialNode = materialNode->next_sibling(AZ::GFxFramework::MaterialExport::g_materialString))
{
AZ::rapidxml::xml_attribute<char>* nameAttribute = materialNode->first_attribute(AZ::GFxFramework::MaterialExport::g_nameString);
if (nameAttribute)
{
AZStd::string materialName = nameAttribute->value();
AZStd::string surfaceTypeName = DefaultPhysicsMaterialNameFromPhysicsAsset;
AZ::rapidxml::xml_attribute<char>* surfaceTypeNode = materialNode->first_attribute("SurfaceType");
if (surfaceTypeNode && surfaceTypeNode->value_size() != 0)
{
surfaceTypeName = surfaceTypeNode->value();
}
materialToSurfaceTypeMap[materialName] = surfaceTypeName;
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "A SubMaterial without Name found in the .mtl file: %s", materialFilename.c_str());
}
}
}
else
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "No SubMaterial node in the .mtl file: %s", materialFilename.c_str());
}
}
}
void UpdateAssetMaterialsFromCrySurfaceTypes(const AZStd::vector<AZStd::string>& fbxMaterialNames,
const AZStd::unordered_map<AZStd::string, AZStd::string>& materialToSurfaceTypeMap,
MeshAssetData& assetData)
{
AZStd::vector<AZStd::string>& materialNames = assetData.m_materialNames;
AZ_Assert(materialNames.empty(),
"UpdateAssetMaterialsFromCrySurfaceTypes: Mesh Asset Data should not have materials already assigned.");
materialNames.clear();
materialNames.reserve(fbxMaterialNames.size());
for (const AZStd::string& fbxMaterial : fbxMaterialNames)
{
AZStd::string materialName;
// Here we assign the actual engine surface type based on the material name
auto materialToSurfaceIt = materialToSurfaceTypeMap.find(fbxMaterial);
if (materialToSurfaceIt != materialToSurfaceTypeMap.end()
&& !materialToSurfaceIt->second.empty())
{
materialName = materialToSurfaceIt->second;
// Remove the mat_ prefix since the material library generated from surface types doesn't have it.
if (materialName.find("mat_") == 0)
{
materialName = materialName.substr(4);
}
}
else
{
materialName = DefaultPhysicsMaterialNameFromPhysicsAsset;
}
materialNames.emplace_back(AZStd::move(materialName));
AZ_TracePrintf(
AZ::SceneAPI::Utilities::WarningWindow,
"Materials and Physics Materials have different number of elements. %d materials and %d physics materials.",
materials.size(), physicsMaterials.size());
return false;
}
// Asset mesh surfaces match FBX materials. These are the names that users see in the Collider Component in the Editor.
assetData.m_surfaceNames = fbxMaterialNames;
AZStd::vector<AZStd::string> newPhysicsMaterials;
newPhysicsMaterials.reserve(newMaterials.size());
// In the new material list, the materials might have changed slots.
// Form the new list of physics materials by looking at the previous list
// and keeping the same physics materials association when found.
for (const auto& newMaterial : newMaterials)
{
AZStd::string physicsMaterialName = Physics::DefaultPhysicsMaterialLabel;
for (size_t slotId = 0; slotId < materials.size(); ++slotId)
{
if (AZ::StringFunc::Equal(materials[slotId], newMaterial, false/*bCaseSensitive*/))
{
if (!physicsMaterials[slotId].empty())
{
physicsMaterialName = physicsMaterials[slotId];
}
break;
}
}
newPhysicsMaterials.emplace_back(AZStd::move(physicsMaterialName));
}
materials = newMaterials;
physicsMaterials = AZStd::move(newPhysicsMaterials);
return true;
}
bool ValidateCookedTriangleMesh(void* assetData, AZ::u32 assetDataSize)
@@ -320,6 +259,86 @@ namespace PhysX
return materialNames;
}
AZStd::optional<AssetMaterialsData> GatherMaterialsFromMeshGroup(
const MeshGroup& meshGroup,
const AZ::SceneAPI::Containers::SceneGraph& sceneGraph)
{
AssetMaterialsData assetMaterialData;
const auto& sceneNodeSelectionList = meshGroup.GetSceneNodeSelectionList();
size_t selectedNodeCount = sceneNodeSelectionList.GetSelectedNodeCount();
for (size_t index = 0; index < selectedNodeCount; index++)
{
AZ::SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex = sceneGraph.Find(sceneNodeSelectionList.GetSelectedNode(index));
if (!nodeIndex.IsValid())
{
AZ_TracePrintf(
AZ::SceneAPI::Utilities::WarningWindow,
"Node '%s' was not found in the scene graph.",
sceneNodeSelectionList.GetSelectedNode(index).c_str()
);
continue;
}
auto nodeMesh = azrtti_cast<const AZ::SceneAPI::DataTypes::IMeshData*>(*sceneGraph.ConvertToStorageIterator(nodeIndex));
if (!nodeMesh)
{
continue;
}
AZStd::string_view nodeName = sceneGraph.GetNodeName(nodeIndex).GetName();
const AZStd::vector<AZStd::string> localFbxMaterialsList = GenerateLocalNodeMaterialMap(sceneGraph, nodeIndex);
if (localFbxMaterialsList.empty())
{
AZ_TracePrintf(
AZ::SceneAPI::Utilities::WarningWindow,
"Node '%.*s' does not have any material assigned to it. Material '%s' will be used.",
nodeName.size(), nodeName, DefaultMaterialName
);
}
const AZ::u32 faceCount = nodeMesh->GetFaceCount();
assetMaterialData.m_nodesToPerFaceMaterialIndices.emplace(nodeName, AZStd::vector<AZ::u16>(faceCount));
// Convex and primitive methods can only have 1 material
const bool limitToOneMaterial = meshGroup.GetExportAsConvex() || meshGroup.GetExportAsPrimitive();
for (AZ::u32 faceIndex = 0; faceIndex < faceCount; ++faceIndex)
{
AZStd::string materialName = DefaultMaterialName;
if (!localFbxMaterialsList.empty())
{
int materialId = nodeMesh->GetFaceMaterialId(faceIndex);
if (materialId >= localFbxMaterialsList.size())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow,
"materialId %d for face %d is out of bound for localFbxMaterialsList (size %d).",
materialId, faceIndex, localFbxMaterialsList.size());
return AZStd::nullopt;
}
materialName = localFbxMaterialsList[materialId];
// Keep using the first material when it has to be limited to one.
if (limitToOneMaterial &&
assetMaterialData.m_fbxMaterialNames.size() == 1 &&
assetMaterialData.m_fbxMaterialNames[0] != materialName)
{
materialName = assetMaterialData.m_fbxMaterialNames[0];
}
}
AZ::u16 materialIndex = InsertMaterialIndexByName(materialName, assetMaterialData);
assetMaterialData.m_nodesToPerFaceMaterialIndices[nodeName][faceIndex] = materialIndex;
}
}
return assetMaterialData;
}
}
static physx::PxMeshMidPhase::Enum GetMidPhaseStructureType(const AZStd::string& platformIdentifier)
@@ -468,21 +487,11 @@ namespace PhysX
return cookingSuccessful;
}
// Utility function finding out the .mtl file for a given FBX (at the moment it's the same name as FBX but with .mtl extension)
static AZStd::string GetAssetMaterialFilename(const AZ::SceneAPI::Events::ExportEventContext& context)
{
const AZ::SceneAPI::Containers::Scene& scene = context.GetScene();
AZStd::string materialFilename = scene.GetSourceFilename();
AzFramework::StringFunc::Path::ReplaceExtension(materialFilename, ".mtl");
return materialFilename;
}
// Processes the collected data and writes into a file
static AZ::SceneAPI::Events::ProcessingResult WritePxMeshAsset(
AZ::SceneAPI::Events::ExportEventContext& context,
const AZStd::vector<NodeCollisionGeomExportData>& totalExportData,
const AssetMaterialsData &assetMaterialsData,
const Utils::AssetMaterialsData &assetMaterialsData,
const MeshGroup& meshGroup)
{
SceneEvents::ProcessingResult result = SceneEvents::ProcessingResult::Ignored;
@@ -492,14 +501,18 @@ namespace PhysX
MeshAssetData assetData;
const AZStd::string& materialFilename = GetAssetMaterialFilename(context);
// Read the information about surface type for each material from the .mtl file
AZStd::unordered_map<AZStd::string, AZStd::string> fbxMaterialToCrySurfaceTypeMap;
Utils::BuildMaterialToSurfaceTypeMap(materialFilename, fbxMaterialToCrySurfaceTypeMap);
// Assign the materials into cooked data
Utils::UpdateAssetMaterialsFromCrySurfaceTypes(assetMaterialsData.m_fbxMaterialNames, fbxMaterialToCrySurfaceTypeMap, assetData);
assetData.m_materialNames = meshGroup.GetMaterialSlots();
assetData.m_physicsMaterialNames = meshGroup.GetPhysicsMaterials();
// Updating materials lists from new materials gathered from fbx
// because this exporter runs when the FBX is being processed, which
// could have a different content from when the mesh group info was
// entered in FBX Settings Editor.
if (!Utils::UpdateAssetPhysicsMaterials(assetMaterialsData.m_fbxMaterialNames, assetData.m_materialNames, assetData.m_physicsMaterialNames))
{
return SceneEvents::ProcessingResult::Failure;
}
for (const NodeCollisionGeomExportData& subMesh : totalExportData)
{
@@ -714,9 +727,15 @@ namespace PhysX
for (const MeshGroup& pxMeshGroup : view)
{
// Gather material data from asset for the mesh group
AZStd::optional<Utils::AssetMaterialsData> assetMaterialData = Utils::GatherMaterialsFromMeshGroup(pxMeshGroup, graph);
if (!assetMaterialData)
{
return SceneEvents::ProcessingResult::Failure;
}
// Export data per node
AZStd::vector<NodeCollisionGeomExportData> totalExportData;
AssetMaterialsData assetMaterialData;
const AZStd::string& groupName = pxMeshGroup.GetName();
@@ -764,7 +783,6 @@ namespace PhysX
const AZ::SceneAPI::Containers::SceneGraph::Name& nodeName = graph.GetNodeName(nodeIndex);
const AZStd::vector<AZStd::string> localFbxMaterialsList = Utils::GenerateLocalNodeMaterialMap(graph, nodeIndex);
const AZ::SceneAPI::DataTypes::MatrixType worldTransform = SceneUtil::BuildWorldTransform(graph, nodeIndex);
NodeCollisionGeomExportData nodeExportData;
@@ -783,53 +801,24 @@ namespace PhysX
}
nodeExportData.m_indices.resize(faceCount * 3);
nodeExportData.m_perFaceMaterialIndices.resize(faceCount);
if (localFbxMaterialsList.empty())
nodeExportData.m_perFaceMaterialIndices = assetMaterialData->m_nodesToPerFaceMaterialIndices[nodeExportData.m_nodeName];
if (nodeExportData.m_perFaceMaterialIndices.size() != faceCount)
{
AZ_TracePrintf(
AZ::SceneAPI::Utilities::WarningWindow,
"Node '%s' does not have any material assigned to it. Material '%s' will be used.",
nodeExportData.m_nodeName.c_str(), DefaultMaterialName
"Node '%s' material information face count %d does not match the node's %d.",
nodeExportData.m_nodeName.c_str(), nodeExportData.m_perFaceMaterialIndices.size(), faceCount
);
return SceneEvents::ProcessingResult::Failure;
}
// Convex and primitive methods can only have 1 material
const bool limitToOneMaterial = pxMeshGroup.GetExportAsConvex() || pxMeshGroup.GetExportAsPrimitive();
for (AZ::u32 faceIndex = 0; faceIndex < faceCount; ++faceIndex)
{
AZStd::string materialName = DefaultMaterialName;
if (!localFbxMaterialsList.empty())
{
int materialId = nodeMesh->GetFaceMaterialId(faceIndex);
if (materialId >= localFbxMaterialsList.size())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow,
"materialId %d for face %d is out of bound for localFbxMaterialsList (size %d).",
materialId, faceIndex, localFbxMaterialsList.size());
return SceneEvents::ProcessingResult::Failure;
}
materialName = localFbxMaterialsList[materialId];
// Keep using the first material when it has to be limited to one.
if (limitToOneMaterial &&
assetMaterialData.m_fbxMaterialNames.size() == 1 &&
assetMaterialData.m_fbxMaterialNames[0] != materialName)
{
materialName = assetMaterialData.m_fbxMaterialNames[0];
}
}
const AZ::SceneAPI::DataTypes::IMeshData::Face& face = nodeMesh->GetFaceInfo(faceIndex);
nodeExportData.m_indices[faceIndex * 3] = face.vertexIndex[0];
nodeExportData.m_indices[faceIndex * 3 + 1] = face.vertexIndex[1];
nodeExportData.m_indices[faceIndex * 3 + 2] = face.vertexIndex[2];
AZ::u16 materialIndex = Utils::InsertMaterialIndexByName(materialName, assetMaterialData);
nodeExportData.m_perFaceMaterialIndices[faceIndex] = materialIndex;
}
if (pxMeshGroup.GetDecomposeMeshes())
@@ -880,7 +869,7 @@ namespace PhysX
if (!totalExportData.empty())
{
result += WritePxMeshAsset(context, totalExportData, assetMaterialData, pxMeshGroup);
result += WritePxMeshAsset(context, totalExportData, *assetMaterialData, pxMeshGroup);
}
}
+32 -2
View File
@@ -26,6 +26,7 @@ namespace AZ
namespace Containers
{
class Scene;
class SceneGraph;
}
namespace DataTypes
@@ -57,5 +58,34 @@ namespace PhysX
private:
AZ::SceneAPI::Events::ProcessingResult ExportMeshObject(AZ::SceneAPI::Events::ExportEventContext& context, const AZStd::shared_ptr<const AZ::SceneAPI::DataTypes::IMeshData>& meshToExport, const AZStd::string& nodePath, const Pipeline::MeshGroup& pxMeshGroup) const;
};
}
}
namespace Utils
{
//! A struct to store the materials of the mesh nodes selected in a mesh group.
struct AssetMaterialsData
{
//! Material names coming from FBX.
AZStd::vector<AZStd::string> m_fbxMaterialNames;
//! Look-up table for fbxMaterialNames.
AZStd::unordered_map<AZStd::string, size_t> m_materialIndexByName;
//! Map of mesh nodes to their list of material indices associated to each face.
AZStd::unordered_map<AZStd::string, AZStd::vector<AZ::u16>> m_nodesToPerFaceMaterialIndices;
};
//! Returns the list of materials assigned to the triangles
//! of the mesh nodes selected in a mesh group.
AZStd::optional<AssetMaterialsData> GatherMaterialsFromMeshGroup(
const MeshGroup& meshGroup,
const AZ::SceneAPI::Containers::SceneGraph& sceneGraph);
//! Function to update a list of materials and physics materials from a new list.
//! All those new materials not found in the previous list will fallback to default physics material.
bool UpdateAssetPhysicsMaterials(
const AZStd::vector<AZStd::string>& newMaterials,
AZStd::vector<AZStd::string>& materials,
AZStd::vector<AZStd::string>& physicsMaterials);
} // namespace Utils
} // namespace Pipeline
} // namespace PhysX
+84 -2
View File
@@ -20,8 +20,11 @@
#include <SceneAPI/SceneCore/DataTypes/GraphData/IBoneData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneData/Rules/MaterialRule.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <Source/Pipeline/MeshGroup.h>
#include <Source/Pipeline/MeshExporter.h>
#include <Source/Material.h>
#include <PxPhysicsAPI.h>
@@ -602,6 +605,8 @@ namespace PhysX
->Field("PrimitiveAssetParams", &MeshGroup::m_primitiveAssetParams)
->Field("DecomposeMeshes", &MeshGroup::m_decomposeMeshes)
->Field("ConvexDecompositionParams", &MeshGroup::m_convexDecompositionParams)
->Field("MaterialSlots", &MeshGroup::m_materialSlots)
->Field("PhysicsMaterials", &MeshGroup::m_physicsMaterials)
->Field("rules", &MeshGroup::m_rules);
if (
@@ -622,6 +627,7 @@ namespace PhysX
"<span>Select the meshes to be included in the mesh group.</span>")
->Attribute("FilterName", "meshes")
->Attribute("FilterType", AZ::SceneAPI::DataTypes::IMeshData::TYPEINFO_Uuid())
->Attribute(AZ::Edit::Attributes::ChangeNotify, &MeshGroup::OnNodeSelectionChanged)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &MeshGroup::m_exportMethod, "Export As",
"<span>The cooking method to be applied to this mesh group. For the asset to be usable as "
@@ -629,14 +635,14 @@ namespace PhysX
->EnumAttribute(MeshExportMethod::TriMesh, "Triangle Mesh")
->EnumAttribute(MeshExportMethod::Convex, "Convex")
->EnumAttribute(MeshExportMethod::Primitive, "Primitive")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &MeshGroup::OnExportMethodChanged)
->DataElement(AZ_CRC("DecomposeMeshes", 0xe0e2ac1e), &MeshGroup::m_decomposeMeshes, "Decompose Meshes",
"<span>If enables, this option will apply the V-HACD algorithm to split each node "
"into approximately convex parts. Each part will individually be exported as a convex "
"collider using the parameters configured above.</span>")
->Attribute(AZ::Edit::Attributes::Visibility, &MeshGroup::GetDecomposeMeshesVisibility)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &MeshGroup::OnDecomposeMeshesChanged)
->DataElement(AZ_CRC("TriangleMeshAssetParams", 0x1a408def), &MeshGroup::m_triangleMeshAssetParams, "Triangle Mesh Asset Parameters",
"<span>Configure the parameters controlling the exported triangle mesh asset.</span>")
@@ -658,6 +664,12 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::Visibility, &MeshGroup::GetDecomposeMeshes)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &MeshGroup::m_physicsMaterials, "Physics Materials",
"<span>Configure which physics materials to use for each element.</span>")
->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &MeshGroup::GetMaterialSlotLabel)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->DataElement(AZ::Edit::UIHandlers::Default, &MeshGroup::m_rules, "",
"Add or remove rules to fine-tune the export process.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20));
@@ -710,6 +722,37 @@ namespace PhysX
return (GetExportAsConvex() || GetExportAsPrimitive()) && m_decomposeMeshes;
}
const AZStd::vector<AZStd::string>& MeshGroup::GetPhysicsMaterials() const
{
return m_physicsMaterials;
}
const AZStd::vector<AZStd::string>& MeshGroup::GetMaterialSlots() const
{
return m_materialSlots;
}
void MeshGroup::SetSceneGraph(const AZ::SceneAPI::Containers::SceneGraph* graph)
{
m_graph = graph;
}
void MeshGroup::UpdateMaterialSlots()
{
if (!m_graph)
{
return;
}
AZStd::optional<Utils::AssetMaterialsData> assetMaterialData = Utils::GatherMaterialsFromMeshGroup(*this, *m_graph);
if (!assetMaterialData)
{
return;
}
Utils::UpdateAssetPhysicsMaterials(assetMaterialData->m_fbxMaterialNames, m_materialSlots, m_physicsMaterials);
}
AZ::SceneAPI::Containers::RuleContainer& MeshGroup::GetRuleContainer()
{
return m_rules;
@@ -770,11 +813,50 @@ namespace PhysX
return m_convexDecompositionParams;
}
AZ::u32 MeshGroup::OnNodeSelectionChanged()
{
UpdateMaterialSlots();
return AZ::Edit::PropertyRefreshLevels::EntireTree;
}
AZ::u32 MeshGroup::OnExportMethodChanged()
{
UpdateMaterialSlots();
return AZ::Edit::PropertyRefreshLevels::EntireTree;
}
AZ::u32 MeshGroup::OnDecomposeMeshesChanged()
{
UpdateMaterialSlots();
return AZ::Edit::PropertyRefreshLevels::EntireTree;
}
bool MeshGroup::GetDecomposeMeshesVisibility() const
{
return GetExportAsConvex() || GetExportAsPrimitive();
}
AZStd::string MeshGroup::GetMaterialSlotLabel(int index) const
{
if (index < m_materialSlots.size())
{
// When limited to one material, clarify in the label the material
// will be used for the entire object.
if (index == 0 && (GetExportAsConvex() || GetExportAsPrimitive()))
{
return m_materialSlots[index] + " (entire object)";
}
else
{
return m_materialSlots[index];
}
}
else
{
return "<Unknown>";
}
}
bool MeshGroup::VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
// Remove the material rule.
@@ -23,6 +23,11 @@
namespace AZ
{
class ReflectContext;
namespace SceneAPI::Containers
{
class SceneGraph;
}
}
namespace PhysX
@@ -185,6 +190,11 @@ namespace PhysX
bool GetExportAsTriMesh() const;
bool GetExportAsPrimitive() const;
bool GetDecomposeMeshes() const;
const AZStd::vector<AZStd::string>& GetPhysicsMaterials() const;
const AZStd::vector<AZStd::string>& GetMaterialSlots() const;
void SetSceneGraph(const AZ::SceneAPI::Containers::SceneGraph* graph);
void UpdateMaterialSlots();
AZ::SceneAPI::Containers::RuleContainer& GetRuleContainer() override;
const AZ::SceneAPI::Containers::RuleContainer& GetRuleContainerConst() const override;
@@ -207,8 +217,14 @@ namespace PhysX
protected:
static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
AZ::u32 OnNodeSelectionChanged();
AZ::u32 OnExportMethodChanged();
AZ::u32 OnDecomposeMeshesChanged();
bool GetDecomposeMeshesVisibility() const;
AZStd::string GetMaterialSlotLabel(int index) const;
AZ::Uuid m_id{};
AZStd::string m_name{};
AZ::SceneAPI::SceneData::SceneNodeSelectionList m_nodeSelectionList{};
@@ -219,6 +235,10 @@ namespace PhysX
PrimitiveAssetParams m_primitiveAssetParams{};
ConvexDecompositionParams m_convexDecompositionParams{};
AZ::SceneAPI::Containers::RuleContainer m_rules{};
AZStd::vector<AZStd::string> m_materialSlots;
AZStd::vector<AZStd::string> m_physicsMaterials;
const AZ::SceneAPI::Containers::SceneGraph* m_graph = nullptr;
};
}
}