Files
UnrealEngineUWP/Engine/Source/Developer/MeshBoneReduction/Private/MeshBoneReduction.cpp
Jaroslaw Palczynski ebce413232 UE4 Refactoring. Changed OVERRIDE and FINAL macros to keywords override and final.
[CL 2104397 by Jaroslaw Palczynski in Main branch]
2014-06-13 06:14:46 -04:00

290 lines
9.3 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "Engine.h"
#include "MeshBoneReduction.h"
#include "Developer/MeshUtilities/Public/MeshUtilities.h"
class FMeshBoneReductionModule : public IMeshBoneReductionModule
{
public:
// IModuleInterface interface.
virtual void StartupModule() override;
virtual void ShutdownModule() override;
// IMeshBoneReductionModule interface.
virtual class IMeshBoneReduction* GetMeshBoneReductionInterface() override;
};
DEFINE_LOG_CATEGORY_STATIC(LogMeshBoneReduction, Log, All);
IMPLEMENT_MODULE(FMeshBoneReductionModule, IMeshBoneReductionModule);
class FMeshBoneReduction : public IMeshBoneReduction
{
bool GetBoneReductionData( const USkeletalMesh* SkeletalMesh, int32 DesiredLOD, TMap<FBoneIndexType, FBoneIndexType> &OutBonesToReplace )
{
const TArray<FMeshBoneInfo> & RefBoneInfo = SkeletalMesh->RefSkeleton.GetRefBoneInfo();
USkeleton * Skeleton = SkeletalMesh->Skeleton;
if (Skeleton)
{
TArray<FBoneIndexType> BoneIndicesToRemove;
// it accumulate from LOD 0 -> LOD N if N+1 is DesiredLOD
// since we don't like to keep the bones that weren't included in (LOD-1)
for ( int LODIndex=0; LODIndex < DesiredLOD && Skeleton->BoneReductionSettingsForLODs.Num() > LODIndex; ++LODIndex )
{
// first gather indices. we don't want to add bones to replace if that "to-be-replace" will be removed as well
for (int32 Index = 0; Index < Skeleton->BoneReductionSettingsForLODs[LODIndex].BonesToRemove.Num(); ++Index)
{
int32 BoneIndex = SkeletalMesh->RefSkeleton.FindBoneIndex(Skeleton->BoneReductionSettingsForLODs[LODIndex].BonesToRemove[Index]);
// we don't allow root to be removed
if ( BoneIndex > 0 )
{
BoneIndicesToRemove.AddUnique(BoneIndex);
}
}
}
// now make sure the parent isn't the one to be removed, find the one that won't be removed
for (int32 Index = 0; Index < BoneIndicesToRemove.Num(); ++Index)
{
int32 BoneIndex = BoneIndicesToRemove[Index];
int32 ParentIndex = RefBoneInfo[BoneIndex].ParentIndex;
while (BoneIndicesToRemove.Contains(ParentIndex))
{
ParentIndex = RefBoneInfo[ParentIndex].ParentIndex;
}
OutBonesToReplace.Add(BoneIndex, ParentIndex);
}
}
return ( OutBonesToReplace.Num() > 0 );
}
void FixUpChunkBoneMaps( FSkelMeshChunk & Chunk, const TMap<FBoneIndexType, FBoneIndexType> &BonesToRepair )
{
// now you have list of bones, remove them from vertex influences
{
TMap<uint8, uint8> BoneMapRemapTable;
// first go through bone map and see if this contains BonesToRemove
int32 BoneMapSize = Chunk.BoneMap.Num();
int32 AdjustIndex=0;
for (int32 BoneMapIndex=0; BoneMapIndex < BoneMapSize; ++BoneMapIndex )
{
// look for this bone to be removed or not?
const FBoneIndexType * ParentBoneIndex = BonesToRepair.Find(Chunk.BoneMap[BoneMapIndex]);
if ( ParentBoneIndex )
{
// this should not happen, I don't ever remove root
check (*ParentBoneIndex!=INDEX_NONE);
// if Parent already exists in the current BoneMap, we just have to fix up the mapping
int32 ParentBoneMapIndex = Chunk.BoneMap.Find(*ParentBoneIndex);
// if it exists
if (ParentBoneMapIndex != INDEX_NONE)
{
// if parent index is higher, we have to decrease it to match to new index
if (ParentBoneMapIndex > BoneMapIndex)
{
--ParentBoneMapIndex;
}
// remove current chunk count, will replace with parent
Chunk.BoneMap.RemoveAt(BoneMapIndex);
}
else
{
// if parent doens't exists, we have to add one
// this doesn't change bone map size
Chunk.BoneMap.RemoveAt(BoneMapIndex);
ParentBoneMapIndex = Chunk.BoneMap.Add(*ParentBoneIndex);
}
// first fix up all indices of BoneMapRemapTable for the indices higher than BoneMapIndex, since BoneMapIndex is being removed
for (auto Iter = BoneMapRemapTable.CreateIterator(); Iter; ++Iter)
{
uint8 & Value = Iter.Value();
check (Value != BoneMapIndex);
if (Value > BoneMapIndex)
{
--Value;
}
}
int32 OldIndex = BoneMapIndex+AdjustIndex;
int32 NewIndex = ParentBoneMapIndex;
// you still have to add no matter what even if same since indices might change after added
{
// add to remap table
check (OldIndex < 256 && OldIndex >= 0);
check (NewIndex < 256 && NewIndex >= 0);
check (BoneMapRemapTable.Contains((uint8)OldIndex) == false);
BoneMapRemapTable.Add((uint8)OldIndex, (uint8)NewIndex);
}
// reduce index since the item is removed
--BoneMapIndex;
--BoneMapSize;
// this is to adjust the later indices. We need to refix their indices
++AdjustIndex;
}
else if (AdjustIndex > 0)
{
int32 OldIndex = BoneMapIndex+AdjustIndex;
int32 NewIndex = BoneMapIndex;
check (OldIndex < 256 && OldIndex >= 0);
check (NewIndex < 256 && NewIndex >= 0);
check (BoneMapRemapTable.Contains((uint8)OldIndex) == false);
BoneMapRemapTable.Add((uint8)OldIndex, (uint8)NewIndex);
}
}
if ( BoneMapRemapTable.Num() > 0 )
{
// fix up rigid verts
for (int32 VertIndex=0; VertIndex < Chunk.RigidVertices.Num(); ++VertIndex)
{
FRigidSkinVertex & Vert = Chunk.RigidVertices[VertIndex];
uint8 *RemappedBone = BoneMapRemapTable.Find(Vert.Bone);
if (RemappedBone)
{
Vert.Bone = *RemappedBone;
}
}
// fix up soft verts
for (int32 VertIndex=0; VertIndex < Chunk.SoftVertices.Num(); ++VertIndex)
{
FSoftSkinVertex & Vert = Chunk.SoftVertices[VertIndex];
bool ShouldRenormalize = false;
for(int32 InfluenceIndex = 0;InfluenceIndex < MAX_TOTAL_INFLUENCES;InfluenceIndex++)
{
uint8 *RemappedBone = BoneMapRemapTable.Find(Vert.InfluenceBones[InfluenceIndex]);
if (RemappedBone)
{
Vert.InfluenceBones[InfluenceIndex] = *RemappedBone;
ShouldRenormalize = true;
}
}
if (ShouldRenormalize)
{
// should see if same bone exists
for(int32 InfluenceIndex = 0;InfluenceIndex < MAX_TOTAL_INFLUENCES;InfluenceIndex++)
{
for(int32 InfluenceIndex2 = InfluenceIndex+1;InfluenceIndex2 < MAX_TOTAL_INFLUENCES;InfluenceIndex2++)
{
// cannot be 0 because we don't allow removing root
if (Vert.InfluenceBones[InfluenceIndex] != 0 && Vert.InfluenceBones[InfluenceIndex] == Vert.InfluenceBones[InfluenceIndex2])
{
Vert.InfluenceWeights[InfluenceIndex] += Vert.InfluenceWeights[InfluenceIndex2];
// reset
Vert.InfluenceBones[InfluenceIndex2] = 0;
Vert.InfluenceWeights[InfluenceIndex2] = 0;
}
}
}
}
}
}
// @todo fix up RequiredBones/ActiveBoneIndices?
}
}
void ReduceBoneCounts( USkeletalMesh* SkeletalMesh, int32 DesiredLOD )
{
check (SkeletalMesh);
USkeleton * Skeleton = SkeletalMesh->Skeleton;
check (Skeleton);
// find all the bones to remove from Skeleton settings
TMap<FBoneIndexType, FBoneIndexType> BonesToRemove;
if (GetBoneReductionData(SkeletalMesh, DesiredLOD, BonesToRemove) == false)
{
return;
}
FStaticLODModel& SrcModel = SkeletalMesh->PreModifyMesh();
TComponentReregisterContext<USkinnedMeshComponent> ReregisterContext;
SkeletalMesh->ReleaseResources();
SkeletalMesh->ReleaseResourcesFence.Wait();
FSkeletalMeshResource* SkeletalMeshResource = SkeletalMesh->GetImportedResource();
check(SkeletalMeshResource);
// Insert a new LOD model entry if needed.
if ( DesiredLOD == SkeletalMeshResource->LODModels.Num() )
{
SkeletalMeshResource->LODModels.AddRawItem(0);
}
FStaticLODModel** LODModels = SkeletalMeshResource->LODModels.GetTypedData();
delete LODModels[DesiredLOD];
FStaticLODModel* NewModel = new FStaticLODModel();
LODModels[DesiredLOD] = NewModel;
// Bulk data arrays need to be locked before a copy can be made.
SrcModel.RawPointIndices.Lock( LOCK_READ_ONLY );
SrcModel.LegacyRawPointIndices.Lock( LOCK_READ_ONLY );
*NewModel = SrcModel;
SrcModel.RawPointIndices.Unlock();
SrcModel.LegacyRawPointIndices.Unlock();
// The index buffer needs to be rebuilt on copy.
FMultiSizeIndexContainerData IndexBufferData;
SrcModel.MultiSizeIndexContainer.GetIndexBufferData( IndexBufferData );
NewModel->MultiSizeIndexContainer.RebuildIndexBuffer( IndexBufferData );
// Required bones are recalculated later on.
NewModel->RequiredBones.Empty();
// fix up chunks
for ( int32 ChunkIndex=0; ChunkIndex< NewModel->Chunks.Num(); ++ChunkIndex )
{
FixUpChunkBoneMaps(NewModel->Chunks[ChunkIndex], BonesToRemove);
}
// Copy over LOD info from LOD0 if there is no previous info.
if ( DesiredLOD == SkeletalMesh->LODInfo.Num() )
{
FSkeletalMeshLODInfo* NewLODInfo = new( SkeletalMesh->LODInfo ) FSkeletalMeshLODInfo;
FSkeletalMeshLODInfo& OldLODInfo = SkeletalMesh->LODInfo[0];
*NewLODInfo = OldLODInfo;
}
SkeletalMesh->CalculateRequiredBones( SkeletalMeshResource->LODModels[DesiredLOD], SkeletalMesh->RefSkeleton, &BonesToRemove );
SkeletalMesh->PostEditChange();
SkeletalMesh->InitResources();
}
};
TScopedPointer<FMeshBoneReduction> GMeshBoneReduction;
void FMeshBoneReductionModule::StartupModule()
{
GMeshBoneReduction = new FMeshBoneReduction();
}
void FMeshBoneReductionModule::ShutdownModule()
{
GMeshBoneReduction = NULL;
}
IMeshBoneReduction* FMeshBoneReductionModule::GetMeshBoneReductionInterface()
{
return GMeshBoneReduction;
}