Quick answer: Ensure all source meshes share a common skeleton, use compatible LOD counts, and call FSkeletalMeshMerge::DoMerge() with a valid build settings struct. Material indices shift after merging — reassign materials on the resulting component using the merged material list.
Here is how to fix Unreal skeletal mesh merge not combining materials. You have a modular character: base body, swappable head, separate armor. You use FSkeletalMeshMerge to combine them into one mesh for rendering performance. The merged mesh appears — but the head is black, the armor has the body’s material, or random sections are purple default material. The merge worked on geometry but mis-assigned materials.
The Symptom
After calling FSkeletalMeshMerge::DoMerge():
- The merged mesh renders but parts have wrong material
- Some sections have the default untextured material
- Material count on the merged component does not match the sum of inputs
- Crash on merge with “skeleton mismatch” or similar
What Causes This
Source meshes with different material sections. FSkeletalMeshMerge concatenates mesh sections. If your base body has 2 sections (skin, face) and your armor has 3 (torso, arms, legs), the merged mesh has 5 sections. Material slot indices in the merged mesh shift accordingly.
UVMergeMaterials flag. This flag controls whether materials from different source meshes with identical assigned materials are deduplicated. Enabled: shared materials merge into one slot. Disabled: each source’s materials become separate slots.
Skeletons not compatible. Merged meshes must share a skeleton asset. Meshes rigged to different skeletons (even if the bone hierarchies look similar) cannot merge.
LOD mismatch. Each source mesh must have the same number of LOD levels as the others, or the merge uses LOD0 only and drops LOD data.
Material slots not reassigned after merge. The merge combines meshes but the resulting SkeletalMeshComponent needs its material slots populated — otherwise it uses the base mesh’s materials, which may not cover all new sections.
The Fix
Step 1: Verify source compatibility. All source meshes must:
- Share the same
USkeletonasset - Have the same LOD count
- Use compatible section structure (same vertex format, weights)
In code, check:
USkeleton* BaseSkeleton = BaseMesh->GetSkeleton();
for (USkeletalMesh* SrcMesh : SourceMeshes)
{
if (SrcMesh->GetSkeleton() != BaseSkeleton)
{
UE_LOG(LogTemp, Error, TEXT("Skeleton mismatch: %s"),
*SrcMesh->GetName());
return;
}
}
Step 2: Perform the merge.
void ACharacter::MergeMeshes(TArray<USkeletalMesh*> SourceMeshes)
{
USkeletalMesh* MergedMesh = NewObject<USkeletalMesh>(GetTransientPackage(),
NAME_None, RF_Transient);
TArray<FSkelMeshMergeSectionMapping> SectionMappings;
TArray<FSkelMeshMergeUVTransforms> UvTransforms;
FSkeletalMeshMerge Merger(MergedMesh, SourceMeshes,
SectionMappings, 0, EMeshBufferAccess::Default,
UvTransforms.GetData());
if (Merger.DoMerge())
{
GetMesh()->SetSkeletalMesh(MergedMesh);
ApplyMaterialSlots(MergedMesh);
}
else
{
UE_LOG(LogTemp, Error, TEXT("Mesh merge failed"));
}
}
Step 3: Apply materials to the merged component. After merging, iterate the merged mesh’s material slots and set them on your SkeletalMeshComponent:
void ACharacter::ApplyMaterialSlots(USkeletalMesh* Merged)
{
USkeletalMeshComponent* Mesh = GetMesh();
int32 NumSlots = Merged->GetMaterials().Num();
for (int32 i = 0; i < NumSlots; ++i)
{
UMaterialInterface* Mat = Merged->GetMaterials()[i].MaterialInterface;
Mesh->SetMaterial(i, Mat);
}
}
Materials on the source meshes populate the merged mesh’s material list in merge order. Apply them to the component to see the correct visuals.
Step 4: Verify section count and material count. Log them to confirm the merge produced what you expect:
UE_LOG(LogTemp, Log, TEXT("Merged mesh: %d LODs, %d materials"),
Merged->GetLODNum(), Merged->GetMaterials().Num());
for (int32 i = 0; i < Merged->GetLODNum(); ++i)
{
const FSkeletalMeshLODInfo* Info = Merged->GetLODInfo(i);
UE_LOG(LogTemp, Log, TEXT("LOD %d: %d sections"),
i, Info->LODMaterialMap.Num());
}
Unexpected values indicate source mesh mismatches. Review the inputs.
Performance Considerations
Mesh merging at runtime is expensive — each merge allocates GPU buffers and runs skin weight blending. Do merges on character spawn, not every frame. Cache merged meshes per equipment loadout if the player swaps between a few configurations.
For very modular characters (100+ equipment combinations), consider material swapping with a single master mesh rather than merging — sometimes simpler and faster.
Alternative: LeaderPose/SlavePose
Instead of merging, you can have multiple SkeletalMeshComponents share the base skeleton’s animation via SetLeaderPoseComponent. The slave meshes follow the leader’s bone transforms. Slightly more draw calls than a merged mesh but no merge cost:
ArmorMesh->SetLeaderPoseComponent(BaseMesh);
Same animation plays on all linked meshes. Good for characters where merging is overkill.
“Mesh merging is a tradeoff: higher up-front cost for lower per-frame cost. For static equipment loadouts, it pays. For dynamic swapping, LeaderPose is cheaper.”
Related Issues
For attachment issues, see Unreal Attach Actor Wrong Transform. For material issues broadly, Material Parameter Collection Not Updating covers related material-system bugs.
Shared skeleton. Same LOD count. Apply materials after DoMerge. Check counts with logs.