Hello, While working with physics bodies, During their preparation for the physics engine (chaos), we encountered an issue related to the handling of the Physical Material Mask (PMM) when using Material Instance Constants (MICs).
The Problem:
When a MIC is set in the physical material, in the code, it will call a function that is overridden and simply returns the PhysicalMaterialMask. However, this returns null if the mask is only defined in the parent material, not the MIC itself. This caused the physics engine to fail to retrieve the correct mask.
Our Fix:
We updated the code to explicitly look up the PhysicalMaterialMask from the parent material when it is missing in the MIC:
- In
BodyInstance.cpp, insideFBodyInstance::GetComplexPhysicalMaterials, we added a check to detect if the material is a Material Instance, and if so, retrieve the mask from its parent material:
// Gather parent's PMM when the material is a material instance
else if (nullptr != PhysMat)
{
UMaterialInstance* MaterialInstance = Cast<UMaterialInstance>(Material);
if (nullptr != MaterialInstance)
{
Material = MaterialInstance->GetMaterial();
if (nullptr != Material)
{
PhysMatMask = Material->GetPhysicalMaterialMask();
if (nullptr != PhysMatMask)
{
PhysMatMap = Material;
}
}
}
}
- In
PhysicalMaterialMask.h, inside theUPhysicalMaterialMaskclass, we added properties to cache the mask texture and its dimensions for build/runtime usage (previously only available in editor):
inside UPhysicalMaterialMask class added:
/** Copy of the mask texture only available in editor for build usage */
UPROPERTY(VisibleAnywhere, Category = TextureSource)
TArray<uint32> CachedMaskData;
/** SizeX of the mask texture */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = TextureSource)
int32 CachedSizeX;
/** SizeY of the mask texture */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = TextureSource)
int32 CachedSizeY;
These changes solve the problem by ensuring the physics engine always gets a valid PhysicalMaterialMask, even when using material instances that don’t override the mask themselves. Additionally, caching the mask data allows the texture to be correctly accessed in builds, not only in the editor.
Alternative Consideration:
We considered overriding GetPhysicalMaterialMask in the MIC class itself to return the parent’s mask if null. However, this global change may affect all callers and seems against the original design logic, which does not expect this fallback.
Question:
Do you fix is the right way or is there an alternative way we could fix it ?
best regards.
[Attachment Removed]