Physical Material Masks?

So I’m trying to add multiple physical materials to a single mesh, using a single material and an (of course) Physical Material Mask asset. Unfortunately, the only documentation I could find about this was from UDK (UDK | PhysicalMaterialMask)- which says to use 1 bit bmp’s. Okay. So, I created one, tried to import it, but apparently it’s an unsupported file type. ([FONT=Courier New]LogImageWrapper: Error: BMP uses an unsupported format (1/1) Error: Texture import failed) The mask asset itself isn’t really specific about what kind of texture it takes for the mask texture, save that it takes… something, and that none are available within my project (or the slot is just disabled altogether, I can’t tell).

Then I come across a post about how there’s now 8 slots for physical materials in each material now (How to add multiple physics materials to static mesh which uses a single material - #3 by MostHost_LA - Content Creation - Unreal Engine Forums), but in engine (as opposed to the docs) they’re just labeled ‘0-7’ with no explanation about how to set them up to work properly. I would guess it requires the use of the Physical Material Mask asset and its embedded mask texture somehow, but I’ll be damned if I can figure out how to get it to work.

SO!- my question is this, am I just chasing this thing down a rabbit hole?- leading nowhere?- like are these assets and slots deprecated and just haven’t been removed yet, or do they exist for some reason, and if so could someone be so kind as to explain those reasons to me, because I’ve been googling, and so far my google-fu hasn’t been good enough lick this one yet.

1 Like
  1. Create a mask texture with no more than eight colors. The colors must be the 8 colors listed in the material editor (and also PhysicalMaterialMask.cpp): red, green, blue, cyan, magenta, yellow, white, and black. I was able to do this by creating an RGB (no alpha) PNG in Blender.

  2. Create a Physical Material Mask asset in the Content Browser. Then, right click on the asset and select Import Mask Texture. Choose the PNG you created in step 1.

  3. Open a Material (normal material, not physical material) in the Material Editor. Assign the Physical Material Mask asset to it, and then select a Physical Material asset for each of the colors used in your mask texture.

  1. Open the static mesh asset you’d like to use and enable Support Physical Material Masks.

Support Physical Material Masks.PNG

  1. Assign the material to the static mesh.

  2. Put the mesh into a level.

  3. In the console, run: ShowFlag.PhysicalMaterialMasks 1. You can also enable this from the viewports Show menu with Show > Advanced > Physical Material Masks. At this point, selected actors will display the physical material mask, if they have one.

  4. Select the asset you added in step (6) in the 3D viewport and confirm the physical material mask appears.

Once you’ve done all this, you can get the physical material from collision hit results, though this currently only works if Chaos physics is enabled.

10 Likes

Hey there!
Is there a way to apply unique mask for material instances? Duplicating master materials for each mesh with unique mask seem a bit off

Not that I know of. It’s a new feature, so my best guess is it simply hasn’t been implemented in material instances yet, but I don’t know that for sure.

1 Like

Hi Doug.

Would you happen to know how the Landscape Physical Material Output works as an alternative to Physical Material Mask?
See my question for context:

Landscapes don’t appear to have a ‘Support Physical Material Masks’ option so I’m not sure if your solution applies to terrains.

Even if it does, the Physical_Material_Mask imports the mask separately from the landscape material’s use of the same mask. I’m not confident that the two separate uses of a puddle mask will align correctly between the rendering (visual) and the LineTrace hits (audio).

I suspect Physical_Material_Mask is not the way to go for my objective, which is why I’m looking for more info on the esoteric Landscape_Physical_Material_Output node.

I haven’t used Landscape Physical Material Output myself. There is, however, an alternative that works well for keeping the visual rendering and line traces in sync, and that is to set the Physical Material in the Landscape’s Layer Info asset.

When you are painting a Landscape, each paint layer has an associated Layer info object.

I have an example project you can check out here:

Note: When warned about Houdini, just disable the plugin if you don’t have it.

  1. Open the StandardLandscape level
  2. Change the editor Mode to Landscape
  3. Click the Paint tab above the 3D viewport
  4. View the following 3 Layers under Target Layers

  1. Click one the Magnifying glasses next to one of the Layer Infos to jump to it in the Content Browser, and then double click the Layer Info asset to open it.
  2. In the Layer Info asset editor, you will see a Phys Material you can set.

image

When you run line traces, the Physical Material returned will be the one that has the highest blended/painted weight (which will match the visuals you see on the Landscape).

Different situations.

To get things working on the landscape all you have to do is use the new output like you would the Grass node.
Output all layers into it from a layer sample node.

Re the changing on a material instance basis.
I don’t see that option yet in .26 latest.
I assume it would be exposed as the Physmat is. Near the very top of the instance once they get to it.

Thanks Doug!

Indeed, the [LandscapeLayerSample] node was the missing component breaking my first attempt. I was erroneously connecting a TextureSample to [ Landscape Physical Material Output] which didn’t work.

Sir, what do you mean “only works if chaos physics is enabled”? I am on 4.27 and enabled all chaos plugins and still the trace hits dont diferentiate between physical materials within the mask! Could you help me?

I have the same issue.

Yep I cant get this to work on UE 5.0.1. Also not sure if I am making the mask properly. Can you provide some more details of how to get 8 colour masks in a RGB texture Doug ?

1 Like

OK Guys, been developping a system to use Physical Material Mask info to spawn meshes… So in order for yor line trace to pick up physical material, you have to go in the sources of the static mesh you are using and activate the “use physical material mask” option. I’m not sure if the wording is exact, but it took me a long long time to figure that one out, and I thought it would help! the 8 color mask is just a basic rgb texture, but select well your colors to exactly correspond to the standards (eg. RED = 255,0,0)

Once you have this setup and you do a complex collision ray cast … You should get the physical material and its associated surface exactly according to the texture.

Its probably cheaper to run color sampling off the same UV texture off the texture in memory, since yuou are "developing a system "…

I think I got this to work right, but am not sure if it is intended way of doing it, and i cant find single example on internet.
Below is my c++ code snippet from Bullet projectile class OnComponentHit function (still experimenting with this!). Someone might find it useful for their projects.

void ABulletProjectile::OnHit(UPrimitiveComponent* InHitComponent, AActor* InOtherActor, UPrimitiveComponent* InOtherComponent, FVector InNormalImpulse, const FHitResult& InHitResult)
{
	float OutDamage = 0.0f;
	float Distance = (StartLocation - InHitResult.ImpactPoint).Size();

	if (Distance < FireDistance)
	{
		EPhysicalSurface SurfaceType = EPhysicalSurface::SurfaceType_Default;

		if (InHitResult.PhysMaterial.Get())
		{
			SurfaceType = static_cast<EPhysicalSurface>(InHitResult.PhysMaterial->SurfaceType);
		}

		if (auto HitComponent = InHitResult.GetComponent())
		{
			if (HitComponent->GetCollisionObjectType() == ECC_WorldStatic)
			{
				FCollisionQueryParams TraceParams;
				TraceParams.bTraceComplex           = true;
				TraceParams.bReturnFaceIndex        = true;
				TraceParams.bReturnPhysicalMaterial = true;
				FHitResult MatHitResult;

				bool bTraceHit = HitComponent->LineTraceComponent(
					MatHitResult, InHitResult.Location,
					InHitResult.Location + InHitResult.ImpactNormal * -(CollisionComponent->GetScaledSphereRadius() * 2.0f),
					TraceParams);

				if (bTraceHit)
				{
					int32 MaterialIndex = 0;
					UMaterialInterface* MeshMaterial = HitComponent->GetMaterialFromCollisionFaceIndex(MatHitResult.FaceIndex, MaterialIndex);
					if (MeshMaterial)
					{
						//  MaterialInstance returns nullptr for PhysMask need to use material parent
						UMaterialInterface* ParentMaterial = nullptr;
						if (MeshMaterial->IsA(UMaterialInstance::StaticClass()))
						{
							UMaterialInstance* MatInstance = Cast<UMaterialInstance>(MeshMaterial);
							ParentMaterial                 = MatInstance->Parent;
						}
						else // already is 
						{
							ParentMaterial = MeshMaterial;
						}

						UPhysicalMaterialMask* PhysMatMask = ParentMaterial->GetPhysicalMaterialMask();

						// my "terrain" static mesh was hit
						if (PhysMatMask)
						{
							FPhysicsMaterialMaskHandle& MaskHandle = PhysMatMask->GetPhysicsMaterialMask();
							if (auto Mask = MaskHandle.Get())
							{
								FVector2D HitUV;
								bool bUVFound = UGameplayStatics::FindCollisionUV(MatHitResult, Mask->UVChannelIndex, HitUV);
								if (bUVFound)
								{
									// debug
									UE_LOG(LogTemp, Error, TEXT("MASK IS VALID! DataNum: %d, SizeX: %d, SizeY: %d, X: %f, Y: %f"), 
										Mask->MaskData.Num(), Mask->SizeX, Mask->SizeY, HitUV.X, HitUV.Y);

									uint32 PhysMatIndex = UPhysicalMaterialMask::GetPhysMatIndex(
										Mask->MaskData, Mask->SizeX, Mask->SizeY, Mask->AddressX, Mask->AddressY, HitUV.X, HitUV.Y);

									if (PhysMatIndex != UPhysicalMaterialMask::INVALID_MASK_INDEX)
									{
										// debug
										UE_LOG(LogTemp, Error, TEXT("PhysMatIndex: %d"), PhysMatIndex);
										auto PhysicsMaterial = ParentMaterial->GetPhysicalMaterialFromMap(PhysMatIndex);
										if (PhysicsMaterial)
										{
											SurfaceType = static_cast<EPhysicalSurface>(PhysicsMaterial->SurfaceType);
										}
									}
								}
							}
						}
						else // other static meshes hit
						{
							auto PhysicsMaterial = MeshMaterial->GetPhysicalMaterial();
							if (PhysicsMaterial)
							{
								SurfaceType = UPhysicalMaterial::DetermineSurfaceType(PhysicsMaterial);
							}
						}
					}
				}
			}
		}

		// this does impact sounds, particles...
		OnHitMaterial(InHitResult, SurfaceType);
...
}

Am still not 100% sure how to create Mask texture properly, while experimenting every time I do some changes to it doesn’t update properly, so I need to restart editor and recompile.

UMaterialInterface* MeshMaterial = HitComponent->GetMaterialFromCollisionFaceIndex(MatHitResult.FaceIndex, MaterialIndex);

You are better off cracking the source code and lifting that precise function to repourpose it.

Also, since it uses a parametrized material you could potentially create a material to use as the parameter.

Still, if apples and oranges are the same, the UVs are the same, and the hit face therefore matches the texture pixel(s).
You could probably just return the correct physical material color/value off a comparison without having to re-run the function or deviate it further.

Then again, assuming your physical material mask is assigned and implemented correcly on the mesh itself, the HitComponent will already return the physical material for you.
And because of that I’m unsure of what you are attempting…

Also, don’t make the asaumption that “other” things you hit won’t have a physmat mask assigned. Its very possible they could. Even on skeletal meshes…

this is my mesh setup (need to enable nanite fallback so it can display phys mask)

support_uv

i think it returns this phys material, (marked blue), or phys mat override in mat instance
SimplePhysMat

Not sure about nanite’s changes;

If you get false positives the most possible answer is that the physmat has the same uv issues people get on materials when you apply nanite to a landscape.
Basically it tiles the texture once on every component from what was shared on the forums.

If you really want to sove the value yourself in your own function (or whatever, evene in BP)… You also have to assume that you need a visual debug of the UVs being applied to the physmat mask avaliable.

Make some sort of debug view that swaps out the shader (material instance) to one using the same UVs applied to the physmat during the calculations you already have.

If it looks the same, then good, one less thing to worry about…

Also, prior to nanite, the physmat mask retuned correct results - at least on meshes back when I tested it…

Doesn’t work in 5.1; 5.2 and 5.3.
When the static mesh enable Show "Physical Material Masks, the engine is crashing everytime.

I think my workaround is to capture the vertex color paint from surface, generate by colors a DecalMesh with small offset by geometry scripting and autoplacing as hidden at location with unique physical material by vertex color.

I would suggest pulling the source and fixing the engine instead of making workarounds.

Its probably something simple/silly enough that it can be sorted out at way less of a time cost than creating a different system to deal with it.

In theory(I do this for my ■■■■, not epics), you should also be able to see git commits and history on the file to track down what changes took place and what (if anythinf) caused it to break.