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.