You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
UE::AssetUtils::GetStaticMeshLODMaterialListBySection() now returns the slot name for each Material BP function GetSectionMaterialListFromStaticMesh() now returns slot names. Fixed issue in this function where LODIndex was clamped to Render LODs, not SourceModel LODs. This function now also works for RenderData LODs. In CopyMeshToStaticMesh, for RenderData LODs, the MaterialID is set to the MaterialIndex referenced by each mesh section. So in this case in GetSectionMaterialListFromStaticMesh, the Asset Material List is returned directly. This is not really a "per Section Material List' as the function name implies, however it allows this function to provide comparable behavior between SourceModel and RenderData LODs. Add new BP function RemapToNewMaterialIDsByMaterial() which figures out MaterialID remapping based on original and new Material lists (assuming MaterialID indexes into original Material list) Add BP functions GetNumStaticMeshLODsOfType() and CheckStaticMeshHasAvailableLOD(), these utilities are useful for dealing w/ multi-LOD assets #rb jimmy.andrews [CL 29052212 by ryan schmidt in ue5-main branch]
206 lines
5.5 KiB
C++
206 lines
5.5 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AssetUtils/StaticMeshMaterialUtil.h"
|
|
|
|
#include "Engine/StaticMesh.h"
|
|
#include "Materials/MaterialInterface.h"
|
|
#include "MeshDescription.h"
|
|
#include "StaticMeshAttributes.h"
|
|
|
|
using namespace UE::AssetUtils;
|
|
|
|
|
|
bool UE::AssetUtils::GetStaticMeshLODAssetMaterials(
|
|
UStaticMesh* StaticMeshAsset,
|
|
int32 LODIndex,
|
|
FStaticMeshLODMaterialSetInfo& MaterialInfoOut)
|
|
{
|
|
if (!StaticMeshAsset)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
if (StaticMeshAsset->IsSourceModelValid(LODIndex) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Need to access the mesh here because # Sections == # PolygonGroups and this info doesn't seem to be anywhere else.
|
|
// Otherwise do not use the mesh in this function (would be nice to avoid this call if possible)
|
|
const FMeshDescription* SourceMesh = StaticMeshAsset->GetMeshDescription(LODIndex);
|
|
int32 NumSections = SourceMesh->PolygonGroups().Num();
|
|
|
|
|
|
TArray<FStaticMaterial> StaticMaterials = StaticMeshAsset->GetStaticMaterials();
|
|
MaterialInfoOut.MaterialSlots.Reset();
|
|
for (FStaticMaterial Mat : StaticMaterials)
|
|
{
|
|
MaterialInfoOut.MaterialSlots.Add( FStaticMeshMaterialSlot{ Mat.MaterialInterface, Mat.MaterialSlotName } );
|
|
}
|
|
|
|
// This is complicated. A UStaticMesh has N MaterialSlots and each LOD has M Sections.
|
|
// Each Section can have any MaterialSlot assigned to it, ie it is not necessarily 1-1 or in-order.
|
|
// The SectionInfoMap is a TMap that will contain the SectionIndex-to-SlotIndex mapping
|
|
// *if* the mapping is not (SectionIndex == SlotIndex), or has ever been edited.
|
|
// So if the SectionIndex is not found in the SectionInfoMap, then it should be used as the SlotIndex directly.
|
|
|
|
const FMeshSectionInfoMap& SectionInfoMap = StaticMeshAsset->GetSectionInfoMap();
|
|
MaterialInfoOut.LODIndex = LODIndex;
|
|
MaterialInfoOut.NumSections = NumSections;
|
|
|
|
MaterialInfoOut.SectionSlotIndexes.SetNum(MaterialInfoOut.NumSections);
|
|
MaterialInfoOut.SectionMaterials.SetNum(MaterialInfoOut.NumSections);
|
|
for (int32 SectionIndex = 0; SectionIndex < MaterialInfoOut.NumSections; ++SectionIndex)
|
|
{
|
|
MaterialInfoOut.SectionSlotIndexes[SectionIndex] = -1;
|
|
MaterialInfoOut.SectionMaterials[SectionIndex] = nullptr;
|
|
|
|
if (SectionInfoMap.IsValidSection(LODIndex, SectionIndex) == false)
|
|
{
|
|
// did not find this section
|
|
if ( StaticMaterials.IsValidIndex(SectionIndex) )
|
|
{
|
|
MaterialInfoOut.SectionSlotIndexes[SectionIndex] = SectionIndex;
|
|
MaterialInfoOut.SectionMaterials[SectionIndex] = MaterialInfoOut.MaterialSlots[SectionIndex].Material;
|
|
}
|
|
else
|
|
{
|
|
ensure(false); // material list is broken? use default material.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FMeshSectionInfo SectionInfo = SectionInfoMap.Get(LODIndex, SectionIndex);
|
|
if ( StaticMaterials.IsValidIndex(SectionInfo.MaterialIndex) )
|
|
{
|
|
MaterialInfoOut.SectionSlotIndexes[SectionIndex] = SectionInfo.MaterialIndex;
|
|
MaterialInfoOut.SectionMaterials[SectionIndex] = MaterialInfoOut.MaterialSlots[SectionInfo.MaterialIndex].Material;
|
|
}
|
|
else
|
|
{
|
|
ensure(false); // this is *not* supposed to be able to happen! SectionMap is broken...
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
#else
|
|
// TODO: how would we handle this for runtime static mesh?
|
|
return false;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UE::AssetUtils::GetStaticMeshLODMaterialListBySection(
|
|
UStaticMesh* StaticMeshAsset,
|
|
int32 LODIndex,
|
|
TArray<UMaterialInterface*>& MaterialListOut,
|
|
TArray<int32>& MaterialIndexOut,
|
|
TArray<FName>& MaterialSlotNameOut)
|
|
{
|
|
#if WITH_EDITOR
|
|
// need valid MeshDescription in Editor path
|
|
if (StaticMeshAsset->IsMeshDescriptionValid(LODIndex) == false)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
FStaticMeshLODMaterialSetInfo MaterialSetInfo;
|
|
if (GetStaticMeshLODAssetMaterials(StaticMeshAsset, LODIndex, MaterialSetInfo) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
const FMeshDescription* SourceMesh = StaticMeshAsset->GetMeshDescription(LODIndex);
|
|
|
|
// # Sections == # PolygonGroups
|
|
int32 NumPolygonGroups = SourceMesh->PolygonGroups().Num();
|
|
|
|
MaterialListOut.Reset();
|
|
MaterialIndexOut.Reset();
|
|
for (int32 k = 0; k < NumPolygonGroups; ++k)
|
|
{
|
|
int32 UseSlotIndex = -1;
|
|
if (k < MaterialSetInfo.SectionSlotIndexes.Num())
|
|
{
|
|
UseSlotIndex = MaterialSetInfo.SectionSlotIndexes[k];
|
|
}
|
|
else if (MaterialSetInfo.SectionSlotIndexes.Num() > 0)
|
|
{
|
|
UseSlotIndex = 0;
|
|
}
|
|
|
|
if (UseSlotIndex >= 0)
|
|
{
|
|
MaterialIndexOut.Add(UseSlotIndex);
|
|
MaterialListOut.Add(MaterialSetInfo.MaterialSlots[UseSlotIndex].Material);
|
|
MaterialSlotNameOut.Add(MaterialSetInfo.MaterialSlots[UseSlotIndex].SlotName);
|
|
}
|
|
else
|
|
{
|
|
MaterialIndexOut.Add(-1);
|
|
MaterialListOut.Add(nullptr);
|
|
MaterialSlotNameOut.Add(NAME_None);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
#else
|
|
// TODO: how would we handle this for runtime static mesh?
|
|
return false;
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
FName UE::AssetUtils::GenerateNewMaterialSlotName(
|
|
const TArray<FStaticMaterial>& ExistingMaterials,
|
|
UMaterialInterface* SlotMaterial,
|
|
int32 NewSlotIndex)
|
|
{
|
|
FString MaterialName = (SlotMaterial) ? SlotMaterial->GetName() : TEXT("Material");
|
|
FName BaseName(MaterialName);
|
|
|
|
bool bFound = false;
|
|
for (const FStaticMaterial& Mat : ExistingMaterials)
|
|
{
|
|
if (Mat.MaterialSlotName == BaseName || Mat.ImportedMaterialSlotName == BaseName)
|
|
{
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (bFound == false && SlotMaterial != nullptr)
|
|
{
|
|
return BaseName;
|
|
}
|
|
|
|
bFound = true;
|
|
while (bFound)
|
|
{
|
|
bFound = false;
|
|
|
|
BaseName = FName(FString::Printf(TEXT("%s_%d"), *MaterialName, NewSlotIndex++));
|
|
for (const FStaticMaterial& Mat : ExistingMaterials)
|
|
{
|
|
if (Mat.MaterialSlotName == BaseName || Mat.ImportedMaterialSlotName == BaseName)
|
|
{
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return BaseName;
|
|
} |