You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/PhysX/Code/Source/Material.cpp

441 lines
16 KiB
C++

/*
* 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 <PhysX_precompiled.h>
#include "Material.h"
#include <AzCore/std/smart_ptr/make_shared.h>
#include <PxPhysicsAPI.h>
namespace PhysX
{
Material::Material(Material&& material)
: m_pxMaterial(AZStd::move(material.m_pxMaterial))
, m_surfaceType(material.m_surfaceType)
, m_surfaceString(AZStd::move(material.m_surfaceString))
{
m_pxMaterial->userData = this;
}
Material& Material::operator=(Material&& material)
{
m_pxMaterial = AZStd::move(material.m_pxMaterial);
m_surfaceType = material.m_surfaceType;
m_surfaceString = AZStd::move(material.m_surfaceString);
return *this;
}
static Material::CombineMode FromPxCombineMode(physx::PxCombineMode::Enum pxMode)
{
switch (pxMode)
{
case physx::PxCombineMode::eAVERAGE: return Material::CombineMode::Average;
case physx::PxCombineMode::eMULTIPLY: return Material::CombineMode::Multiply;
case physx::PxCombineMode::eMAX: return Material::CombineMode::Maximum;
case physx::PxCombineMode::eMIN: return Material::CombineMode::Minimum;
default: return Material::CombineMode::Average;
}
}
static physx::PxCombineMode::Enum ToPxCombineMode(Material::CombineMode mode)
{
switch (mode)
{
case Material::CombineMode::Average: return physx::PxCombineMode::eAVERAGE;
case Material::CombineMode::Multiply: return physx::PxCombineMode::eMULTIPLY;
case Material::CombineMode::Maximum: return physx::PxCombineMode::eMAX;
case Material::CombineMode::Minimum: return physx::PxCombineMode::eMIN;
default: return physx::PxCombineMode::eAVERAGE;
}
}
Material::Material(const Physics::MaterialConfiguration& materialConfiguration)
{
float staticFriction = materialConfiguration.m_staticFriction;
AZ_Warning("PhysX Material", staticFriction >= 0.0f, "Static friction %f for material %s is out of range [0, PX_MAX_F32)",
staticFriction, materialConfiguration.m_surfaceType.c_str());
float dynamicFriction = materialConfiguration.m_dynamicFriction;
AZ_Warning("PhysX Material", dynamicFriction >= 0.0f, "Dynamic friction %f for material %s is out of range [0, PX_MAX_F32)",
dynamicFriction, materialConfiguration.m_surfaceType.c_str());
float restitution = materialConfiguration.m_restitution;
AZ_Warning("PhysX Material", restitution >= 0 && restitution <= 1.0f, "Restitution %f for material %s is out of range [0, 1]",
restitution, materialConfiguration.m_surfaceType.c_str());
// Clamp the values to valid ranges
staticFriction = AZ::GetMax(0.0f, staticFriction);
dynamicFriction = AZ::GetMax(0.0f, dynamicFriction);
restitution = AZ::GetClamp(restitution, 0.0f, 1.0f);
SetDensity(materialConfiguration.m_density);
auto pxMaterial = PxGetPhysics().createMaterial(staticFriction, dynamicFriction, restitution);
auto materialDestructor = [](auto material)
{
material->release();
material->userData = nullptr;
};
pxMaterial->setFrictionCombineMode(ToPxCombineMode(materialConfiguration.m_frictionCombine));
pxMaterial->setRestitutionCombineMode(ToPxCombineMode(materialConfiguration.m_restitutionCombine));
pxMaterial->userData = this;
m_pxMaterial = PxMaterialUniquePtr(pxMaterial, materialDestructor);
m_surfaceType = AZ::Crc32(materialConfiguration.m_surfaceType.c_str());
m_surfaceString = materialConfiguration.m_surfaceType;
Physics::LegacySurfaceTypeRequestsBus::BroadcastResult(
m_cryEngineSurfaceId,
&Physics::LegacySurfaceTypeRequestsBus::Events::GetLegacySurfaceTypeFronName,
m_surfaceString);
}
void Material::UpdateWithConfiguration(const Physics::MaterialConfiguration& configuration)
{
AZ_Assert(m_pxMaterial != nullptr, "Material can't be null!");
SetRestitution(configuration.m_restitution);
SetStaticFriction(configuration.m_staticFriction);
SetDynamicFriction(configuration.m_dynamicFriction);
SetFrictionCombineMode(configuration.m_frictionCombine);
SetRestitutionCombineMode(configuration.m_restitutionCombine);
SetDensity(configuration.m_density);
m_surfaceType = AZ::Crc32(configuration.m_surfaceType.c_str());
m_surfaceString = configuration.m_surfaceType;
Physics::LegacySurfaceTypeRequestsBus::BroadcastResult(
m_cryEngineSurfaceId,
&Physics::LegacySurfaceTypeRequestsBus::Events::GetLegacySurfaceTypeFronName,
m_surfaceString);
}
physx::PxMaterial* Material::GetPxMaterial()
{
return m_pxMaterial.get();
}
AZ::Crc32 Material::GetSurfaceType() const
{
return m_surfaceType;
}
void Material::SetSurfaceType(AZ::Crc32 surfaceType)
{
m_surfaceType = surfaceType;
}
float Material::GetDynamicFriction() const
{
return m_pxMaterial ? m_pxMaterial->getDynamicFriction() : 0.0f;
}
void Material::SetDynamicFriction(float dynamicFriction)
{
AZ_Warning("PhysX Material", dynamicFriction >= 0.0f,
"SetDynamicFriction: Dynamic friction %f for material %s is out of range [0, PX_MAX_F32)",
dynamicFriction, m_surfaceString.c_str());
if (m_pxMaterial)
{
m_pxMaterial->setDynamicFriction(AZ::GetMax(0.0f, dynamicFriction));
}
}
float Material::GetStaticFriction() const
{
return m_pxMaterial ? m_pxMaterial->getStaticFriction() : 0.0f;
}
void Material::SetStaticFriction(float staticFriction)
{
AZ_Warning("PhysX Material", staticFriction >= 0.0f,
"SetStaticFriction: Static friction %f for material %s is out of range [0, PX_MAX_F32)",
staticFriction, m_surfaceString.c_str());
if (m_pxMaterial)
{
m_pxMaterial->setStaticFriction(AZ::GetMax(0.0f, staticFriction));
}
}
float Material::GetRestitution() const
{
return m_pxMaterial ? m_pxMaterial->getRestitution() : 0.0f;
}
void Material::SetRestitution(float restitution)
{
AZ_Warning("PhysX Material", restitution >= 0 && restitution <= 1.0f,
"SetRestitution: Restitution %f for material %s is out of range [0, 1]",
restitution, m_surfaceString.c_str());
if (m_pxMaterial)
{
m_pxMaterial->setRestitution(AZ::GetClamp(restitution, 0.0f, 1.0f));
}
}
Material::CombineMode Material::GetFrictionCombineMode() const
{
return m_pxMaterial ? FromPxCombineMode(m_pxMaterial->getFrictionCombineMode()) : CombineMode::Average;
}
void Material::SetFrictionCombineMode(CombineMode mode)
{
if (m_pxMaterial)
{
m_pxMaterial->setFrictionCombineMode(ToPxCombineMode(mode));
}
}
Material::CombineMode Material::GetRestitutionCombineMode() const
{
return m_pxMaterial ? FromPxCombineMode(m_pxMaterial->getRestitutionCombineMode()) : CombineMode::Average;
}
void Material::SetRestitutionCombineMode(CombineMode mode)
{
if (m_pxMaterial)
{
m_pxMaterial->setRestitutionCombineMode(ToPxCombineMode(mode));
}
}
float Material::GetDensity() const
{
return m_density;
}
void Material::SetDensity(const float density)
{
using Physics::MaterialConfiguration;
AZ_Warning("PhysX Material", density >= MaterialConfiguration::MinDensityLimit && density <= MaterialConfiguration::MaxDensityLimit,
"Density %f for material %s should be in range [%f, %f].", density, m_surfaceString.c_str(),
MaterialConfiguration::MinDensityLimit, MaterialConfiguration::MaxDensityLimit);
m_density = AZ::GetClamp(density,
MaterialConfiguration::MinDensityLimit, MaterialConfiguration::MaxDensityLimit);
}
AZ::u32 Material::GetCryEngineSurfaceId() const
{
return m_cryEngineSurfaceId;
}
void* Material::GetNativePointer()
{
return m_pxMaterial.get();
}
MaterialsManager::MaterialsManager()
{
}
MaterialsManager::~MaterialsManager()
{
}
void MaterialsManager::Connect()
{
Physics::PhysicsMaterialRequestBus::Handler::BusConnect();
MaterialManagerRequestsBus::Handler::BusConnect();
}
void MaterialsManager::Disconnect()
{
MaterialManagerRequestsBus::Handler::BusDisconnect();
Physics::PhysicsMaterialRequestBus::Handler::BusDisconnect();
}
void MaterialsManager::GetMaterials(const Physics::MaterialSelection& materialSelection
, AZStd::vector<AZStd::weak_ptr<Physics::Material>>& outMaterials)
{
outMaterials.clear();
outMaterials.reserve(materialSelection.GetMaterialIdsAssignedToSlots().size());
// Ensure PxMaterial instances are initialized if possible.
InitializeMaterials(materialSelection);
for (const auto& id : materialSelection.GetMaterialIdsAssignedToSlots())
{
Physics::MaterialFromAssetConfiguration configuration;
if (materialSelection.GetMaterialConfiguration(configuration, id))
{
auto iterator = m_materialsFromAssets.find(id.GetUuid());
if (iterator != m_materialsFromAssets.end())
{
outMaterials.push_back(iterator->second);
}
else
{
outMaterials.push_back(GetDefaultMaterial());
}
}
else
{
// It is important to return exactly the amount of materials specified in materialSelection
// If a number of materials different to what was cooked is assigned on a physx mesh it will lead to undefined
// behavior and subtle bugs. Unfortunately, there's no warning or assertion on physx side at the shape creation time,
// nor mention of this in the documentation
outMaterials.push_back(GetDefaultMaterial());
}
}
}
AZStd::weak_ptr<Physics::Material> MaterialsManager::GetMaterialByName(const AZStd::string& name)
{
auto it = AZStd::find_if(m_materialsFromAssets.begin(), m_materialsFromAssets.end(),
[&name](const AZStd::pair<AZ::Uuid, AZStd::shared_ptr<Material>>& elem)
{
return elem.second.get()->GetSurfaceTypeName() == name;
});
if (it != m_materialsFromAssets.end())
{
return it->second;
}
return {};
}
AZ::u32 MaterialsManager::GetFirstSelectedMaterialIndex(const Physics::MaterialSelection& materialSelection)
{
const AZ::u32 defaultMaterialIndex = 0;
if (!materialSelection.IsMaterialLibraryValid())
{
return defaultMaterialIndex;
}
auto materialAsset = AZ::Data::AssetManager::Instance().GetAsset<Physics::MaterialLibraryAsset>(materialSelection.GetMaterialLibraryAssetId(), AZ::Data::AssetLoadBehavior::Default);
materialAsset.BlockUntilLoadComplete();
AZStd::vector<Physics::MaterialFromAssetConfiguration> materialList = materialAsset.Get()->GetMaterialsData();
const AZStd::vector<Physics::MaterialId>& selectedMaterials = materialSelection.GetMaterialIdsAssignedToSlots();
if (selectedMaterials.size() == 0)
{
return defaultMaterialIndex;
}
for (AZ::u32 i=0; i < materialList.size(); ++i)
{
if (materialList[i].m_id == selectedMaterials[0])
{
return i + 1; // Index 0 is reserved for Default material.
}
}
return defaultMaterialIndex;
}
void MaterialsManager::GetPxMaterials(const Physics::MaterialSelection& materialSelection
, AZStd::vector<physx::PxMaterial*>& outMaterials)
{
outMaterials.clear();
if (materialSelection.GetMaterialIdsAssignedToSlots().empty())
{
// if the materialSelection is invalid we still
// return a default material as a fallback behavior
outMaterials.push_back(GetDefaultMaterial()->GetPxMaterial());
return;
}
outMaterials.reserve(materialSelection.GetMaterialIdsAssignedToSlots().size());
// Ensure PxMaterial instances are initialized if possible.
InitializeMaterials(materialSelection);
for (const auto& id : materialSelection.GetMaterialIdsAssignedToSlots())
{
Physics::MaterialFromAssetConfiguration configuration;
if (materialSelection.GetMaterialConfiguration(configuration, id))
{
auto iterator = m_materialsFromAssets.find(id.GetUuid());
if (iterator != m_materialsFromAssets.end())
{
outMaterials.push_back(iterator->second->GetPxMaterial());
}
else
{
outMaterials.push_back(GetDefaultMaterial()->GetPxMaterial());
}
}
else
{
// It is important to return exactly the amount of materials specified in materialSelection
// If a number of materials different to what was cooked is assigned on a physx mesh it will lead to undefined
// behavior and subtle bugs. Unfortunately, there's no warning or assertion on physx side at the shape creation time,
// nor mention of this in the documentation
outMaterials.push_back(GetDefaultMaterial()->GetPxMaterial());
}
}
}
AZStd::shared_ptr<Physics::Material> MaterialsManager::GetGenericDefaultMaterial()
{
return GetDefaultMaterial();
}
const AZStd::shared_ptr<Material>& MaterialsManager::GetDefaultMaterial()
{
if (!m_defaultMaterial)
{
m_defaultMaterial = AZStd::make_shared<Material>(Physics::MaterialConfiguration());
}
return m_defaultMaterial;
}
void MaterialsManager::ReleaseAllMaterials()
{
m_defaultMaterial = nullptr;
m_materialsFromAssets.clear();
Physics::PhysicsMaterialNotificationsBus::Broadcast(&Physics::PhysicsMaterialNotificationsBus::Events::MaterialsReleased);
}
void MaterialsManager::InitializeMaterials(const Physics::MaterialSelection& materialSelection)
{
const AZStd::vector<Physics::MaterialId>& materialIds = materialSelection.GetMaterialIdsAssignedToSlots();
for (const auto& id : materialIds)
{
Physics::MaterialFromAssetConfiguration configuration;
if (!materialSelection.GetMaterialConfiguration(configuration, id))
{
continue; // Default material skips code below.
}
auto materialId = configuration.m_id;
if (materialId.IsNull())
{
materialId = Physics::MaterialId::Create();
}
auto iterator = m_materialsFromAssets.find(materialId.GetUuid());
if (iterator != m_materialsFromAssets.end())
{
iterator->second->UpdateWithConfiguration(configuration.m_configuration);
}
else
{
auto newMaterial = AZStd::make_shared<Material>(configuration.m_configuration);
m_materialsFromAssets.emplace(materialId.GetUuid(), newMaterial);
}
}
}
}