You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Changed the name of "DisplayFactor" property in SkeletalMeshLODInfo into "ScreenSize" and redirected property [CL 2221943 by Hyojong Shin in Main branch]
1600 lines
61 KiB
C++
1600 lines
61 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "UnrealEd.h"
|
|
#include "RawMesh.h"
|
|
#include "MeshUtilities.h"
|
|
#include "SimplygonSDK.h"
|
|
#include "MeshBoneReduction.h"
|
|
#include "MaterialExportUtils.h"
|
|
#include "ComponentReregisterContext.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "SimplygonMeshReduction"
|
|
|
|
class FSimplygonMeshReductionModule : public IMeshReductionModule
|
|
{
|
|
public:
|
|
// IModuleInterface interface.
|
|
virtual void StartupModule() override;
|
|
virtual void ShutdownModule() override;
|
|
|
|
// IMeshReductionModule interface.
|
|
virtual class IMeshReduction* GetMeshReductionInterface() override;
|
|
virtual class IMeshMerging* GetMeshMergingInterface() override;
|
|
};
|
|
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogSimplygon, Log, All);
|
|
IMPLEMENT_MODULE(FSimplygonMeshReductionModule, SimplygonMeshReduction);
|
|
|
|
#define SIMPLYGON_COLOR_CHANNEL "VertexColors"
|
|
|
|
/** Receives error messages generated by Simplygon. These errors are presented via a message box. */
|
|
class FDefaultErrorHandler : public SimplygonSDK::rerrorhandler
|
|
{
|
|
public :
|
|
virtual void HandleError(
|
|
SimplygonSDK::IObject* Object,
|
|
const char* InterfaceName,
|
|
const char* MethodName,
|
|
SimplygonSDK::rid ErrorType,
|
|
const char* ErrorText )
|
|
{
|
|
FString ErrorString = FString::Printf(TEXT("Simplygon Error:\n\nInterface: %s\nMethod: %s\nError: (%d) %s"),
|
|
ANSI_TO_TCHAR(InterfaceName),
|
|
ANSI_TO_TCHAR(MethodName),
|
|
ErrorType,
|
|
ANSI_TO_TCHAR(ErrorText)
|
|
);
|
|
UE_LOG(LogSimplygon, Log, TEXT("%s"), *ErrorString);
|
|
//FMessageDialog::Open(EAppMsgType::Ok, *ErrorString);
|
|
}
|
|
};
|
|
|
|
/** Receives progress events from Simplygon and updates the status window. */
|
|
class FDefaultEventHandler : public SimplygonSDK::robserver
|
|
{
|
|
public:
|
|
virtual void Execute(
|
|
SimplygonSDK::IObject* Object,
|
|
SimplygonSDK::rid EventId,
|
|
void* EventParameterBlock,
|
|
unsigned int EventParameterBlockSize )
|
|
{
|
|
if ( EventId == SimplygonSDK::SG_EVENT_PROGRESS )
|
|
{
|
|
check( sizeof(int32) == EventParameterBlockSize );
|
|
int32 ProgressPercent = *((int32*)EventParameterBlock);
|
|
GWarn->UpdateProgress( ProgressPercent, 100 );
|
|
|
|
// We are required to pass '1' back through the EventParametersBlock for the process to continue.
|
|
*((int32*)EventParameterBlock) = 1;
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Winding modes. */
|
|
enum EWindingMode
|
|
{
|
|
/** Maintain the winding of the mesh. */
|
|
WINDING_Keep,
|
|
/** Reverse the winding of the mesh. */
|
|
WINDING_Reverse,
|
|
WINDING_MAX
|
|
};
|
|
|
|
class FSimplygonMeshReduction
|
|
: public IMeshReduction
|
|
, public IMeshMerging
|
|
{
|
|
public:
|
|
virtual const FString& GetVersionString() const override
|
|
{
|
|
return VersionString;
|
|
}
|
|
|
|
virtual void Reduce(
|
|
FRawMesh& OutReducedMesh,
|
|
float& OutMaxDeviation,
|
|
const FRawMesh& InMesh,
|
|
const FMeshReductionSettings& InSettings
|
|
)
|
|
{
|
|
SimplygonSDK::spGeometryData GeometryData = CreateGeometryFromRawMesh(InMesh);
|
|
check(GeometryData);
|
|
|
|
SimplygonSDK::spReductionProcessor ReductionProcessor = SDK->CreateReductionProcessor();
|
|
ReductionProcessor->AddObserver(&EventHandler, SimplygonSDK::SG_EVENT_PROGRESS);
|
|
ReductionProcessor->SetGeometry(GeometryData);
|
|
|
|
SimplygonSDK::spRepairSettings RepairSettings = ReductionProcessor->GetRepairSettings();
|
|
RepairSettings->SetWeldDist(InSettings.WeldingThreshold);
|
|
RepairSettings->SetTjuncDist(InSettings.WeldingThreshold);
|
|
|
|
SimplygonSDK::spReductionSettings ReductionSettings = ReductionProcessor->GetReductionSettings();
|
|
SetReductionSettings(ReductionSettings, InSettings, GeometryData->GetTriangleCount());
|
|
|
|
SimplygonSDK::spNormalCalculationSettings NormalSettings = ReductionProcessor->GetNormalCalculationSettings();
|
|
SetNormalSettings(NormalSettings, InSettings);
|
|
|
|
ReductionProcessor->RunProcessing();
|
|
|
|
CreateRawMeshFromGeometry(OutReducedMesh, GeometryData, WINDING_Keep);
|
|
OutMaxDeviation = ReductionProcessor->GetMaxDeviation();
|
|
}
|
|
|
|
bool ReduceLODModel(
|
|
FStaticLODModel * SrcModel,
|
|
FStaticLODModel *& OutModel,
|
|
FBoxSphereBounds & Bounds,
|
|
float &MaxDeviation,
|
|
const FReferenceSkeleton& RefSkeleton,
|
|
const FSkeletalMeshOptimizationSettings& Settings
|
|
)
|
|
{
|
|
const bool bUsingMaxDeviation = (Settings.ReductionMethod == SMOT_MaxDeviation && Settings.MaxDeviationPercentage > 0.0f);
|
|
const bool bUsingReductionRatio = (Settings.ReductionMethod == SMOT_NumOfTriangles && Settings.NumOfTrianglesPercentage < 1.0f);
|
|
const bool bProcessGeometry = ( bUsingMaxDeviation || bUsingReductionRatio );
|
|
const bool bProcessBones = (Settings.BoneReductionRatio < 1.0f || Settings.MaxBonesPerVertex < MAX_TOTAL_INFLUENCES);
|
|
const bool bOptimizeMesh = (bProcessGeometry || bProcessBones);
|
|
|
|
// We'll need to store the max deviation after optimization if we wish to recalculate the LOD's display distance
|
|
MaxDeviation = 0.0f;
|
|
|
|
if ( bOptimizeMesh )
|
|
{
|
|
//Create a simplygon scene. The scene by default contains a bone table that is required for bone reduction.
|
|
SimplygonSDK::spScene Scene = SDK->CreateScene();
|
|
check( Scene );
|
|
|
|
// Create bone hierarchy for simplygon.
|
|
TArray<SimplygonSDK::rid> BoneTableIDs;
|
|
CreateSkeletalHierarchy(Scene, RefSkeleton, BoneTableIDs);
|
|
|
|
// Create a new scene mesh object
|
|
SimplygonSDK::spGeometryData GeometryData = CreateGeometryFromSkeletalLODModel( Scene, *SrcModel, BoneTableIDs );
|
|
|
|
FDefaultEventHandler SimplygonEventHandler;
|
|
SimplygonSDK::spReductionProcessor ReductionProcessor = SDK->CreateReductionProcessor();
|
|
ReductionProcessor->AddObserver( &EventHandler, SimplygonSDK::SG_EVENT_PROGRESS );
|
|
ReductionProcessor->SetSceneRoot(Scene->GetRootNode());
|
|
|
|
SimplygonSDK::spRepairSettings RepairSettings = ReductionProcessor->GetRepairSettings();
|
|
RepairSettings->SetWeldDist( Settings.WeldingThreshold );
|
|
RepairSettings->SetTjuncDist( Settings.WeldingThreshold );
|
|
|
|
SimplygonSDK::spReductionSettings ReductionSettings = ReductionProcessor->GetReductionSettings();
|
|
SetReductionSettings( Settings, Bounds.SphereRadius, GeometryData->GetTriangleCount(), ReductionSettings );
|
|
|
|
SimplygonSDK::spNormalCalculationSettings NormalSettings = ReductionProcessor->GetNormalCalculationSettings();
|
|
SetNormalSettings( Settings, NormalSettings );
|
|
|
|
if(bProcessBones)
|
|
{
|
|
SimplygonSDK::spBoneSettings BoneSettings = ReductionProcessor->GetBoneSettings();
|
|
SetBoneSettings( Settings, BoneSettings );
|
|
}
|
|
|
|
ReductionProcessor->RunProcessing();
|
|
|
|
// We require the max deviation if we're calculating the LOD's display distance.
|
|
MaxDeviation = ReductionProcessor->GetMaxDeviation();
|
|
|
|
CreateSkeletalLODModelFromGeometry( GeometryData, RefSkeleton, OutModel );
|
|
}
|
|
|
|
return bOptimizeMesh;
|
|
}
|
|
virtual bool ReduceSkeletalMesh(
|
|
USkeletalMesh* SkeletalMesh,
|
|
int32 LODIndex,
|
|
const FSkeletalMeshOptimizationSettings& Settings,
|
|
bool bCalcLODDistance
|
|
)
|
|
{
|
|
check( SkeletalMesh );
|
|
check( LODIndex >= 0 );
|
|
check( LODIndex <= SkeletalMesh->LODInfo.Num() );
|
|
|
|
FSkeletalMeshResource* SkeletalMeshResource = SkeletalMesh->GetImportedResource();
|
|
check(SkeletalMeshResource);
|
|
check( LODIndex <= SkeletalMeshResource->LODModels.Num() );
|
|
|
|
FStaticLODModel* SrcModel = &SkeletalMesh->PreModifyMesh();
|
|
|
|
TComponentReregisterContext<USkinnedMeshComponent> ReregisterContext;
|
|
SkeletalMesh->ReleaseResources();
|
|
SkeletalMesh->ReleaseResourcesFence.Wait();
|
|
|
|
// Insert a new LOD model entry if needed.
|
|
if ( LODIndex == SkeletalMeshResource->LODModels.Num() )
|
|
{
|
|
SkeletalMeshResource->LODModels.AddRawItem(0);
|
|
}
|
|
|
|
// We'll need to store the max deviation after optimization if we wish to recalculate the LOD's display distance
|
|
float MaxDeviation = 0.0f;
|
|
|
|
// Swap in a new model, delete the old.
|
|
check( LODIndex < SkeletalMeshResource->LODModels.Num() );
|
|
FStaticLODModel** LODModels = SkeletalMeshResource->LODModels.GetTypedData();
|
|
delete LODModels[LODIndex];
|
|
|
|
// Copy over LOD info from LOD0 if there is no previous info.
|
|
if ( LODIndex == SkeletalMesh->LODInfo.Num() )
|
|
{
|
|
FSkeletalMeshLODInfo* NewLODInfo = new( SkeletalMesh->LODInfo ) FSkeletalMeshLODInfo;
|
|
FSkeletalMeshLODInfo& OldLODInfo = SkeletalMesh->LODInfo[0];
|
|
*NewLODInfo = OldLODInfo;
|
|
|
|
// creates LOD Material map for a newly generated LOD model
|
|
int32 NumSections = LODModels[0]->NumNonClothingSections();
|
|
if (NewLODInfo->LODMaterialMap.Num() != NumSections)
|
|
{
|
|
NewLODInfo->LODMaterialMap.Empty(NumSections);
|
|
NewLODInfo->LODMaterialMap.AddUninitialized(NumSections);
|
|
}
|
|
|
|
for (int32 Index = 0; Index < NumSections; Index++)
|
|
{
|
|
NewLODInfo->LODMaterialMap[Index] = Index;
|
|
}
|
|
}
|
|
|
|
// now try bone reduction process if it's setup
|
|
TMap<FBoneIndexType, FBoneIndexType> BonesToRemove;
|
|
|
|
IMeshBoneReductionModule& MeshBoneReductionModule = FModuleManager::Get().LoadModuleChecked<IMeshBoneReductionModule>("MeshBoneReduction");
|
|
IMeshBoneReduction * MeshBoneReductionInterface = MeshBoneReductionModule.GetMeshBoneReductionInterface();
|
|
|
|
// See if we'd like to remove extra bones first
|
|
if (MeshBoneReductionInterface->GetBoneReductionData( SkeletalMesh, LODIndex, BonesToRemove ))
|
|
{
|
|
// if we do, now create new model and make a copy of SrcMesh to cut the bone count
|
|
FStaticLODModel * NewSrcModel = new FStaticLODModel();
|
|
|
|
// 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 );
|
|
*NewSrcModel = *SrcModel;
|
|
SrcModel->RawPointIndices.Unlock();
|
|
SrcModel->LegacyRawPointIndices.Unlock();
|
|
|
|
// The index buffer needs to be rebuilt on copy.
|
|
FMultiSizeIndexContainerData IndexBufferData;
|
|
SrcModel->MultiSizeIndexContainer.GetIndexBufferData( IndexBufferData );
|
|
NewSrcModel->MultiSizeIndexContainer.RebuildIndexBuffer( IndexBufferData );
|
|
|
|
// now fix up SrcModel to NewSrcModel
|
|
SrcModel = NewSrcModel;
|
|
|
|
// fix up chunks to remove the bones that set to be removed
|
|
for ( int32 ChunkIndex=0; ChunkIndex< NewSrcModel->Chunks.Num(); ++ChunkIndex )
|
|
{
|
|
MeshBoneReductionInterface->FixUpChunkBoneMaps(NewSrcModel->Chunks[ChunkIndex], BonesToRemove);
|
|
}
|
|
}
|
|
|
|
FStaticLODModel * NewModel = new FStaticLODModel();
|
|
LODModels[LODIndex] = NewModel;
|
|
|
|
// Reduce LOD model with SrcMesh
|
|
if ( ReduceLODModel(SrcModel, NewModel, SkeletalMesh->Bounds, MaxDeviation, SkeletalMesh->RefSkeleton, Settings) )
|
|
{
|
|
if( bCalcLODDistance )
|
|
{
|
|
float ViewDistance = CalculateViewDistance( MaxDeviation );
|
|
SkeletalMesh->LODInfo[LODIndex].ScreenSize = 2.0f * SkeletalMesh->Bounds.SphereRadius / ViewDistance;
|
|
}
|
|
|
|
// Flag this LOD as having been simplified.
|
|
SkeletalMesh->LODInfo[LODIndex].bHasBeenSimplified = true;
|
|
SkeletalMesh->bHasBeenSimplified = true;
|
|
}
|
|
else
|
|
{
|
|
// 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();
|
|
SkeletalMesh->LODInfo[LODIndex].bHasBeenSimplified = false;
|
|
}
|
|
|
|
SkeletalMesh->CalculateRequiredBones( SkeletalMeshResource->LODModels[LODIndex], SkeletalMesh->RefSkeleton, &BonesToRemove );
|
|
SkeletalMesh->PostEditChange();
|
|
SkeletalMesh->InitResources();
|
|
|
|
if ( LODIndex >= SkeletalMesh->OptimizationSettings.Num() )
|
|
{
|
|
FSkeletalMeshOptimizationSettings DefaultSettings;
|
|
const FSkeletalMeshOptimizationSettings SettingsToCopy =
|
|
SkeletalMesh->OptimizationSettings.Num() ? SkeletalMesh->OptimizationSettings.Last() : DefaultSettings;
|
|
while ( LODIndex >= SkeletalMesh->OptimizationSettings.Num() )
|
|
{
|
|
SkeletalMesh->OptimizationSettings.Add( SettingsToCopy );
|
|
}
|
|
}
|
|
check( LODIndex < SkeletalMesh->OptimizationSettings.Num() );
|
|
SkeletalMesh->OptimizationSettings[ LODIndex ] = Settings;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsSupported() const
|
|
{
|
|
return true;
|
|
}
|
|
static FSimplygonMeshReduction* Create()
|
|
{
|
|
FString DllPath(FPaths::Combine(*FPaths::EngineDir(), TEXT("Binaries/ThirdParty/NotForLicensees/Simplygon")));
|
|
FString DllFilename(FPaths::Combine(*DllPath, TEXT("SimplygonSDKEpicUE4Releasex64.dll")));
|
|
if( !FPaths::FileExists(DllFilename) )
|
|
{
|
|
DllFilename = FPaths::Combine(*DllPath, TEXT("SimplygonSDKRuntimeReleasex64.dll"));
|
|
}
|
|
|
|
// If the DLL just doesn't exist, fail gracefully. Licensees and Rocket users will not necessarily have Simplygon.
|
|
if( !FPaths::FileExists(DllFilename) )
|
|
{
|
|
UE_LOG(LogSimplygon,Warning,TEXT("Simplygon DLL not present - disabling."));
|
|
return NULL;
|
|
}
|
|
|
|
// Otherwise fail
|
|
void* DLLHandle = FPlatformProcess::GetDllHandle(*DllFilename);
|
|
if (DLLHandle == NULL)
|
|
{
|
|
int32 ErrorNum = FPlatformMisc::GetLastError();
|
|
TCHAR ErrorMsg[1024];
|
|
FPlatformMisc::GetSystemErrorMessage( ErrorMsg, 1024, ErrorNum );
|
|
UE_LOG(LogSimplygon,Error,TEXT("Failed to get Simplygon DLL handle: %s (%d)"),ErrorMsg,ErrorNum);
|
|
return NULL;
|
|
}
|
|
|
|
// Get API function pointers of interest
|
|
typedef void (*GetInterfaceVersionSimplygonSDKPtr)(ANSICHAR*);
|
|
GetInterfaceVersionSimplygonSDKPtr GetInterfaceVersionSimplygonSDK = (GetInterfaceVersionSimplygonSDKPtr)FPlatformProcess::GetDllExport( DLLHandle, TEXT( "GetInterfaceVersionSimplygonSDK" ) );
|
|
|
|
typedef int (*InitializeSimplygonSDKPtr)(const char* LicenseData , SimplygonSDK::ISimplygonSDK** OutInterfacePtr);
|
|
InitializeSimplygonSDKPtr InitializeSimplygonSDK = (InitializeSimplygonSDKPtr)FPlatformProcess::GetDllExport( DLLHandle, TEXT( "InitializeSimplygonSDK" ) );
|
|
|
|
if ((GetInterfaceVersionSimplygonSDK == NULL) || (InitializeSimplygonSDK == NULL))
|
|
{
|
|
// Couldn't find the functions we need.
|
|
UE_LOG(LogSimplygon,Warning,TEXT("Failed to acquire Simplygon DLL exports."));
|
|
FPlatformProcess::FreeDllHandle( DLLHandle );
|
|
return NULL;
|
|
}
|
|
|
|
ANSICHAR VersionHash[200];
|
|
GetInterfaceVersionSimplygonSDK(VersionHash);
|
|
if (FCStringAnsi::Strcmp(VersionHash, SimplygonSDK::GetInterfaceVersionHash()) != 0)
|
|
{
|
|
UE_LOG(LogSimplygon,Warning,TEXT("Library version mismatch. Header=%s Lib=%s"),ANSI_TO_TCHAR(SimplygonSDK::GetInterfaceVersionHash()),ANSI_TO_TCHAR(VersionHash));
|
|
return NULL;
|
|
}
|
|
|
|
const char* LicenseData = NULL;
|
|
TArray<uint8> LicenseFileContents;
|
|
if (FFileHelper::LoadFileToArray(LicenseFileContents, *FPaths::Combine(*DllPath, TEXT("license.dat")), FILEREAD_Silent) && LicenseFileContents.Num() > 0)
|
|
{
|
|
LicenseData = (const char*)LicenseFileContents.GetTypedData();
|
|
}
|
|
|
|
SimplygonSDK::ISimplygonSDK* SDK = NULL;
|
|
int32 Result = InitializeSimplygonSDK(LicenseData, &SDK);
|
|
if (Result != SimplygonSDK::SG_ERROR_NOERROR && Result != SimplygonSDK::SG_ERROR_ALREADYINITIALIZED)
|
|
{
|
|
UE_LOG(LogSimplygon,Warning,TEXT("Failed to initialize Simplygon. Error: %d."),Result);
|
|
SDK = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
return new FSimplygonMeshReduction(SDK);
|
|
}
|
|
|
|
struct FMaterialCastingProperties
|
|
{
|
|
bool bCastMaterials;
|
|
bool bCastNormals;
|
|
|
|
FMaterialCastingProperties()
|
|
: bCastNormals(false)
|
|
, bCastMaterials(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
// IMeshMerging interface
|
|
virtual void BuildProxy(
|
|
const TArray<FRawMesh>& InputMeshes,
|
|
const TArray<MaterialExportUtils::FFlattenMaterial>& InputMaterials,
|
|
const struct FMeshProxySettings& InProxySettings,
|
|
FRawMesh& OutProxyMesh,
|
|
MaterialExportUtils::FFlattenMaterial& OutMaterial) override
|
|
{
|
|
if (InputMeshes.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Create a Simplygon Scene
|
|
SimplygonSDK::spScene Scene = SDK->CreateScene();
|
|
//Material table for the original materials
|
|
SimplygonSDK::spMaterialTable OriginalMaterials = Scene->GetMaterialTable();
|
|
|
|
//Build simplygon materials from the original asset materials
|
|
FMaterialCastingProperties CastProperties;
|
|
CastProperties.bCastMaterials = CreateSGMaterialFromFlattenMaterial(InputMaterials, OriginalMaterials, CastProperties);
|
|
|
|
//For each raw mesh in array create a scene mesh and populate with geometry data
|
|
for(int32 MeshIndex = 0; MeshIndex < InputMeshes.Num() ; ++MeshIndex)
|
|
{
|
|
SimplygonSDK::spSceneMesh Mesh = SDK->CreateSceneMesh();
|
|
SimplygonSDK::spGeometryData GeometryData = CreateGeometryFromRawMesh(InputMeshes[MeshIndex]);
|
|
check(GeometryData)
|
|
|
|
#ifdef DEBUG_PROXY_MESH
|
|
SimplygonSDK::spWavefrontExporter objexp = SDK->CreateWavefrontExporter();
|
|
objexp->SetExportFilePath( "d:/BeforeProxyMesh.obj" );
|
|
objexp->SetSingleGeometry( GeometryData);
|
|
objexp->RunExport();
|
|
#endif
|
|
Mesh->SetGeometry(GeometryData);
|
|
Mesh->SetName(TCHAR_TO_ANSI(*FString::Printf(TEXT("Mesh%d"),MeshIndex)));
|
|
Scene->GetRootNode()->AddChild(Mesh);
|
|
}
|
|
|
|
|
|
//Create a remesher
|
|
SimplygonSDK::spRemeshingProcessor RemeshingProcessor = SDK->CreateRemeshingProcessor();
|
|
|
|
//Setup the remesher
|
|
RemeshingProcessor->AddObserver(&EventHandler, SimplygonSDK::SG_EVENT_PROGRESS);
|
|
RemeshingProcessor->GetRemeshingSettings()->SetOnScreenSize(InProxySettings.ScreenSize);
|
|
RemeshingProcessor->GetRemeshingSettings()->SetMergeDistance(InProxySettings.MergeDistance);
|
|
RemeshingProcessor->GetRemeshingSettings()->SetUseGroundPlane(InProxySettings.bUseClippingPlane);
|
|
RemeshingProcessor->GetRemeshingSettings()->SetGroundPlaneAxisIndex(InProxySettings.AxisIndex); // 0:X-Axis, 1:Y-Axis, 2:Z-Axis
|
|
RemeshingProcessor->GetRemeshingSettings()->SetGroundPlaneLevel(InProxySettings.ClippingLevel);
|
|
|
|
// Goal is: If user specifies negative clipping plane -> negative halfspace should be clipped
|
|
if (InProxySettings.AxisIndex <= 1) // Invert X and Y axis
|
|
{
|
|
RemeshingProcessor->GetRemeshingSettings()->SetGroundPlaneNegativeHalfspace(!InProxySettings.bPlaneNegativeHalfspace);
|
|
}
|
|
else
|
|
{
|
|
RemeshingProcessor->GetRemeshingSettings()->SetGroundPlaneNegativeHalfspace(InProxySettings.bPlaneNegativeHalfspace);
|
|
}
|
|
|
|
RemeshingProcessor->GetRemeshingSettings()->SetTransferNormals( false );
|
|
RemeshingProcessor->GetRemeshingSettings()->SetMergeDistance( InProxySettings.MergeDistance );
|
|
RemeshingProcessor->GetRemeshingSettings()->SetHardEdgeAngle( FMath::DegreesToRadians(InProxySettings.HardAngleThreshold) );//This should be a user settings in the popup dialog!
|
|
RemeshingProcessor->SetSceneRoot(Scene->GetRootNode());
|
|
|
|
//Setup the mapping image used for casting
|
|
SimplygonSDK::spMappingImageSettings MappingSettings = RemeshingProcessor->GetMappingImageSettings();
|
|
MappingSettings->SetGenerateMappingImage( true );
|
|
MappingSettings->SetGenerateTexCoords( true );
|
|
MappingSettings->SetGenerateTangents( false );
|
|
MappingSettings->SetWidth( InProxySettings.TextureWidth );
|
|
MappingSettings->SetHeight( InProxySettings.TextureHeight );
|
|
|
|
//Start remeshing the geometry
|
|
RemeshingProcessor->RemeshGeometry();
|
|
|
|
if(CastProperties.bCastMaterials)
|
|
{
|
|
//Collect the mapping image from the remeshing process
|
|
SimplygonSDK::spMappingImage MappingImage = RemeshingProcessor->GetMappingImage();
|
|
|
|
//Create a new material for the proxy geometry
|
|
SimplygonSDK::spMaterial OutputMaterialLOD = SDK->CreateMaterial();
|
|
|
|
//Create Image data where the diffuse data is stored
|
|
SimplygonSDK::spImageData OutputDiffuseData = SDK->CreateImageData();
|
|
//Cast diffuse texture data
|
|
{
|
|
// Cast the data using a color caster
|
|
SimplygonSDK::spColorCaster cast = SDK->CreateColorCaster();
|
|
cast->SetColorType( SimplygonSDK::SG_MATERIAL_CHANNEL_DIFFUSE );
|
|
cast->SetSourceMaterials( OriginalMaterials );
|
|
cast->SetMappingImage( MappingImage ); // The mapping image we got from the remeshing process.
|
|
cast->SetOutputChannels( 4 ); // RGB, 3 channels! (1 would be for grey scale, and 4 would be for RGBA.)
|
|
cast->SetOutputChannelBitDepth( 8 ); // 8 bits per channel. So in this case we will have 24bit colors RGB.
|
|
cast->SetDilation( 10 ); // To avoid mip-map artifacts, the empty pixels on the map needs to be filled to a degree aswell.
|
|
cast->SetOutputImage(OutputDiffuseData);
|
|
cast->CastMaterials(); // Fetch!
|
|
|
|
// set the material properties
|
|
// Set the diffuse multiplier for the texture. 1 means it will not differ from original texture,
|
|
// For example: 0 would ignore a specified color and 2 would make a color twice as pronounced as the others.
|
|
OutputMaterialLOD->SetDiffuseRed(1);
|
|
OutputMaterialLOD->SetDiffuseGreen(1);
|
|
OutputMaterialLOD->SetDiffuseBlue(1);
|
|
OutputMaterialLOD->SetLayeredTextureImage(SimplygonSDK::SG_MATERIAL_CHANNEL_DIFFUSE, 0, OutputDiffuseData);
|
|
OutputMaterialLOD->SetLayeredTextureLevel(SimplygonSDK::SG_MATERIAL_CHANNEL_DIFFUSE,0,0);
|
|
}
|
|
|
|
|
|
//Cast normal texture data
|
|
if(CastProperties.bCastNormals)
|
|
{
|
|
//Create Image data where the normal data is stored
|
|
SimplygonSDK::spImageData OutputNormalData = SDK->CreateImageData();
|
|
|
|
// Cast the data using a normal caster
|
|
SimplygonSDK::spNormalCaster cast = SDK->CreateNormalCaster();
|
|
cast->SetSourceMaterials( OriginalMaterials );
|
|
cast->SetMappingImage( MappingImage ); // The mapping image we got from the remeshing process.
|
|
cast->SetOutputChannels( 3 ); // RGB, 3 channels! (1 would be for grey scale, and 4 would be for RGBA.)
|
|
cast->SetOutputChannelBitDepth( 8 ); // 8 bits per channel. So in this case we will have 24bit colors RGB.
|
|
cast->SetDilation( 10 ); // To avoid mip-map artifacts, the empty pixels on the map needs to be filled to a degree aswell.
|
|
cast->SetFlipBackfacingNormals(false);
|
|
cast->SetGenerateTangentSpaceNormals(true);
|
|
cast->SetOutputImage(OutputNormalData);
|
|
cast->CastMaterials(); // Fetch!
|
|
|
|
OutputMaterialLOD->SetLayeredTextureImage(SimplygonSDK::SG_MATERIAL_CHANNEL_NORMALS, 0, OutputNormalData);
|
|
OutputMaterialLOD->SetLayeredTextureLevel(SimplygonSDK::SG_MATERIAL_CHANNEL_NORMALS, 0, 0);
|
|
}
|
|
|
|
//Create a new material table for the new materials
|
|
SimplygonSDK::spMaterialTable OutputTable = SDK->CreateMaterialTable();
|
|
OutputTable->AddMaterial(OutputMaterialLOD);
|
|
|
|
//Convert the simplygon material to unreal materials
|
|
CreateFlattenMaterialFromSGMaterial(OutputTable, OutMaterial);
|
|
|
|
}
|
|
|
|
//Collect the proxy mesh
|
|
SimplygonSDK::spSceneMesh ProxyMesh = SimplygonSDK::Cast<SimplygonSDK::ISceneMesh>(Scene->GetRootNode()->GetChild(0));
|
|
|
|
#ifdef DEBUG_PROXY_MESH
|
|
SimplygonSDK::spWavefrontExporter objexp = SDK->CreateWavefrontExporter();
|
|
objexp->SetExportFilePath( "d:/AfterProxyMesh.obj" );
|
|
objexp->SetSingleGeometry( ProxyMesh->GetGeometry() );
|
|
objexp->RunExport();
|
|
#endif
|
|
|
|
//Convert geometry data to raw mesh data
|
|
SimplygonSDK::spGeometryData outGeom = ProxyMesh->GetGeometry();
|
|
CreateRawMeshFromGeometry(OutProxyMesh, ProxyMesh->GetGeometry(), WINDING_Keep);
|
|
|
|
// Since mesh proxies have 1 texture channel
|
|
// put copy to channel 1 for lightmaps
|
|
OutProxyMesh.WedgeTexCoords[1].Empty();
|
|
OutProxyMesh.WedgeTexCoords[1].Append(OutProxyMesh.WedgeTexCoords[0]);
|
|
|
|
// Default smoothing
|
|
OutProxyMesh.FaceSmoothingMasks.SetNum(OutProxyMesh.FaceMaterialIndices.Num());
|
|
for (uint32& SmoothingMask : OutProxyMesh.FaceSmoothingMasks)
|
|
{
|
|
SmoothingMask = 1;
|
|
}
|
|
}
|
|
|
|
private:
|
|
SimplygonSDK::ISimplygonSDK* SDK;
|
|
FDefaultErrorHandler ErrorHandler;
|
|
FDefaultEventHandler EventHandler;
|
|
FString VersionString;
|
|
|
|
explicit FSimplygonMeshReduction(SimplygonSDK::ISimplygonSDK* InSDK)
|
|
: SDK(InSDK)
|
|
{
|
|
check(SDK);
|
|
SDK->SetErrorHandler(&ErrorHandler);
|
|
SDK->SetGlobalSetting("DefaultTBNType", SimplygonSDK::SG_TANGENTSPACEMETHOD_ORTHONORMAL_LEFTHANDED);
|
|
|
|
const TCHAR* LibraryVersion = TEXT("Simplygon_5_5_2156");
|
|
const TCHAR* UnrealVersionGuid = TEXT("18f808c3cf724e5a994f57de5c83cc4b");
|
|
VersionString = FString::Printf(TEXT("%s_%s"), LibraryVersion, UnrealVersionGuid);
|
|
}
|
|
|
|
SimplygonSDK::spGeometryData CreateGeometryFromRawMesh(const FRawMesh& RawMesh)
|
|
{
|
|
int32 NumVertices = RawMesh.VertexPositions.Num();
|
|
int32 NumWedges = RawMesh.WedgeIndices.Num();
|
|
int32 NumTris = NumWedges / 3;
|
|
|
|
if (NumWedges == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
SimplygonSDK::spGeometryData GeometryData = SDK->CreateGeometryData();
|
|
GeometryData->SetVertexCount(NumVertices);
|
|
GeometryData->SetTriangleCount(NumTris);
|
|
|
|
SimplygonSDK::spRealArray Positions = GeometryData->GetCoords();
|
|
for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex)
|
|
{
|
|
FVector TempPos = RawMesh.VertexPositions[VertexIndex];
|
|
TempPos.Z = -TempPos.Z;
|
|
Positions->SetTuple(VertexIndex, (float*)&TempPos);
|
|
}
|
|
|
|
SimplygonSDK::spRidArray Indices = GeometryData->GetVertexIds();
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
Indices->SetItem(WedgeIndex, RawMesh.WedgeIndices[WedgeIndex]);
|
|
}
|
|
|
|
for(int32 TexCoordIndex = 0; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; ++TexCoordIndex)
|
|
{
|
|
if (RawMesh.WedgeTexCoords[TexCoordIndex].Num() == NumWedges)
|
|
{
|
|
GeometryData->AddTexCoords(TexCoordIndex);
|
|
SimplygonSDK::spRealArray TexCoords = GeometryData->GetTexCoords(TexCoordIndex);
|
|
check(TexCoords->GetTupleSize() == 2);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
TexCoords->SetTuple(WedgeIndex, (float*)&RawMesh.WedgeTexCoords[TexCoordIndex][WedgeIndex]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RawMesh.WedgeColors.Num() == NumWedges)
|
|
{
|
|
GeometryData->AddColors(0);
|
|
SimplygonSDK::spRealArray LinearColors = GeometryData->GetColors(0);
|
|
check(LinearColors);
|
|
check(LinearColors->GetTupleSize() == 4);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
FLinearColor LinearColor(RawMesh.WedgeColors[WedgeIndex]);
|
|
LinearColors->SetTuple(WedgeIndex, (float*)&LinearColor);
|
|
}
|
|
}
|
|
|
|
if (RawMesh.WedgeTangentZ.Num() == NumWedges)
|
|
{
|
|
if (RawMesh.WedgeTangentX.Num() == NumWedges && RawMesh.WedgeTangentY.Num() == NumWedges)
|
|
{
|
|
GeometryData->AddTangents(0);
|
|
SimplygonSDK::spRealArray Tangents = GeometryData->GetTangents(0);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
FVector TempTangent = RawMesh.WedgeTangentX[WedgeIndex];
|
|
TempTangent.Z = -TempTangent.Z;
|
|
Tangents->SetTuple(WedgeIndex, (float*)&TempTangent);
|
|
}
|
|
|
|
GeometryData->AddBitangents(0);
|
|
SimplygonSDK::spRealArray Bitangents = GeometryData->GetBitangents(0);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
FVector TempBitangent = RawMesh.WedgeTangentY[WedgeIndex];
|
|
TempBitangent.Z = -TempBitangent.Z;
|
|
Bitangents->SetTuple(WedgeIndex, (float*)&TempBitangent);
|
|
}
|
|
}
|
|
GeometryData->AddNormals();
|
|
SimplygonSDK::spRealArray Normals = GeometryData->GetNormals();
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
FVector TempNormal = RawMesh.WedgeTangentZ[WedgeIndex];
|
|
TempNormal.Z = -TempNormal.Z;
|
|
Normals->SetTuple(WedgeIndex, (float*)&TempNormal);
|
|
}
|
|
}
|
|
|
|
// Per-triangle data.
|
|
GeometryData->AddMaterialIds();
|
|
SimplygonSDK::spRidArray MaterialIndices = GeometryData->GetMaterialIds();
|
|
for (int32 TriIndex = 0; TriIndex < NumTris; ++TriIndex)
|
|
{
|
|
MaterialIndices->SetItem(TriIndex, RawMesh.FaceMaterialIndices[TriIndex]);
|
|
}
|
|
|
|
GeometryData->AddGroupIds();
|
|
SimplygonSDK::spRidArray GroupIds = GeometryData->GetGroupIds();
|
|
for (int32 TriIndex = 0; TriIndex < NumTris; ++TriIndex)
|
|
{
|
|
GroupIds->SetItem(TriIndex, RawMesh.FaceSmoothingMasks[TriIndex]);
|
|
}
|
|
|
|
return GeometryData;
|
|
}
|
|
|
|
void CreateRawMeshFromGeometry(FRawMesh& OutRawMesh, const SimplygonSDK::spGeometryData& GeometryData, EWindingMode WindingMode)
|
|
{
|
|
check(GeometryData);
|
|
|
|
SimplygonSDK::spRealArray Positions = GeometryData->GetCoords();
|
|
SimplygonSDK::spRidArray Indices = GeometryData->GetVertexIds();
|
|
SimplygonSDK::spRidArray MaterialIndices = GeometryData->GetMaterialIds();
|
|
SimplygonSDK::spRidArray GroupIds = GeometryData->GetGroupIds();
|
|
SimplygonSDK::spRealArray LinearColors = GeometryData->GetColors(0);
|
|
SimplygonSDK::spRealArray Normals = GeometryData->GetNormals();
|
|
SimplygonSDK::spRealArray Tangents = GeometryData->GetTangents(0);
|
|
SimplygonSDK::spRealArray Bitangents = GeometryData->GetBitangents(0);
|
|
|
|
check(Positions);
|
|
check(Indices);
|
|
|
|
FRawMesh& RawMesh = OutRawMesh;
|
|
const bool bReverseWinding = (WindingMode == WINDING_Reverse);
|
|
int32 NumTris = GeometryData->GetTriangleCount();
|
|
int32 NumWedges = NumTris * 3;
|
|
int32 NumVertices = GeometryData->GetVertexCount();
|
|
|
|
RawMesh.VertexPositions.Empty(NumVertices);
|
|
RawMesh.VertexPositions.AddUninitialized(NumVertices);
|
|
for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex)
|
|
{
|
|
Positions->GetTuple(VertexIndex, (float*)&RawMesh.VertexPositions[VertexIndex]);
|
|
RawMesh.VertexPositions[VertexIndex].Z = -RawMesh.VertexPositions[VertexIndex].Z;
|
|
}
|
|
|
|
RawMesh.WedgeIndices.Empty(NumWedges);
|
|
RawMesh.WedgeIndices.AddUninitialized(NumWedges);
|
|
for (int32 TriIndex = 0; TriIndex < NumTris; ++TriIndex)
|
|
{
|
|
for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
|
|
{
|
|
const uint32 DestIndex = bReverseWinding ? (2 - CornerIndex) : CornerIndex;
|
|
RawMesh.WedgeIndices[TriIndex * 3 + DestIndex] = Indices->GetItem(TriIndex * 3 + CornerIndex);
|
|
}
|
|
}
|
|
|
|
for(int32 TexCoordIndex = 0; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; ++TexCoordIndex)
|
|
{
|
|
SimplygonSDK::spRealArray TexCoords = GeometryData->GetTexCoords(TexCoordIndex);
|
|
if (TexCoords)
|
|
{
|
|
RawMesh.WedgeTexCoords[TexCoordIndex].Empty(NumWedges);
|
|
RawMesh.WedgeTexCoords[TexCoordIndex].AddUninitialized(NumWedges);
|
|
for (int32 TriIndex = 0; TriIndex < NumTris; ++TriIndex)
|
|
{
|
|
for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
|
|
{
|
|
const uint32 DestIndex = bReverseWinding ? (2 - CornerIndex) : CornerIndex;
|
|
TexCoords->GetTuple(TriIndex * 3 + CornerIndex, (float*)&RawMesh.WedgeTexCoords[TexCoordIndex][TriIndex * 3 + DestIndex]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (LinearColors)
|
|
{
|
|
RawMesh.WedgeColors.Empty(NumWedges);
|
|
RawMesh.WedgeColors.AddUninitialized(NumWedges);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
FLinearColor LinearColor;
|
|
LinearColors->GetTuple(WedgeIndex, (float*)&LinearColor);
|
|
RawMesh.WedgeColors[WedgeIndex] = LinearColor.ToFColor(true);
|
|
}
|
|
}
|
|
|
|
if (Normals)
|
|
{
|
|
if (Tangents && Bitangents)
|
|
{
|
|
RawMesh.WedgeTangentX.Empty(NumWedges);
|
|
RawMesh.WedgeTangentX.AddUninitialized(NumWedges);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
Tangents->GetTuple(WedgeIndex, (float*)&RawMesh.WedgeTangentX[WedgeIndex]);
|
|
RawMesh.WedgeTangentX[WedgeIndex].Z = -RawMesh.WedgeTangentX[WedgeIndex].Z;
|
|
}
|
|
|
|
RawMesh.WedgeTangentY.Empty(NumWedges);
|
|
RawMesh.WedgeTangentY.AddUninitialized(NumWedges);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
Bitangents->GetTuple(WedgeIndex, (float*)&RawMesh.WedgeTangentY[WedgeIndex]);
|
|
RawMesh.WedgeTangentY[WedgeIndex].Z = -RawMesh.WedgeTangentY[WedgeIndex].Z;
|
|
}
|
|
}
|
|
|
|
RawMesh.WedgeTangentZ.Empty(NumWedges);
|
|
RawMesh.WedgeTangentZ.AddUninitialized(NumWedges);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
Normals->GetTuple(WedgeIndex, (float*)&RawMesh.WedgeTangentZ[WedgeIndex]);
|
|
RawMesh.WedgeTangentZ[WedgeIndex].Z = -RawMesh.WedgeTangentZ[WedgeIndex].Z;
|
|
}
|
|
}
|
|
|
|
RawMesh.FaceMaterialIndices.Empty(NumTris);
|
|
RawMesh.FaceMaterialIndices.AddZeroed(NumTris);
|
|
if (MaterialIndices)
|
|
{
|
|
for (int32 TriIndex = 0; TriIndex < NumTris; ++TriIndex)
|
|
{
|
|
RawMesh.FaceMaterialIndices[TriIndex] = MaterialIndices->GetItem(TriIndex);
|
|
}
|
|
}
|
|
|
|
RawMesh.FaceSmoothingMasks.Empty(NumTris);
|
|
RawMesh.FaceSmoothingMasks.AddZeroed(NumTris);
|
|
if (GroupIds)
|
|
{
|
|
for (int32 TriIndex = 0; TriIndex < NumTris; ++TriIndex)
|
|
{
|
|
RawMesh.FaceSmoothingMasks[TriIndex] = GroupIds->GetItem(TriIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetReductionSettings(SimplygonSDK::spReductionSettings ReductionSettings, const FMeshReductionSettings& Settings, int32 NumTris)
|
|
{
|
|
float MaxDeviation = Settings.MaxDeviation > 0.0f ? Settings.MaxDeviation : FLT_MAX;
|
|
float MinReductionRatio = FMath::Max<float>(1.0f / NumTris, 0.05f);
|
|
float MaxReductionRatio = (Settings.MaxDeviation > 0.0f && Settings.PercentTriangles == 1.0f) ? MinReductionRatio : 1.0f;
|
|
float ReductionRatio = FMath::Clamp(Settings.PercentTriangles, MinReductionRatio, MaxReductionRatio);
|
|
|
|
unsigned int FeatureFlagsMask = SimplygonSDK::SG_FEATUREFLAGS_GROUP;
|
|
if (Settings.TextureImportance != EMeshFeatureImportance::Off)
|
|
{
|
|
FeatureFlagsMask |= (SimplygonSDK::SG_FEATUREFLAGS_TEXTURE0 | SimplygonSDK::SG_FEATUREFLAGS_MATERIAL);
|
|
}
|
|
if (Settings.ShadingImportance != EMeshFeatureImportance::Off)
|
|
{
|
|
FeatureFlagsMask |= SimplygonSDK::SG_FEATUREFLAGS_SHADING;
|
|
}
|
|
|
|
const float ImportanceTable[] =
|
|
{
|
|
0.0f, // OFF
|
|
0.125f, // Lowest
|
|
0.35f, // Low,
|
|
1.0f, // Normal
|
|
2.8f, // High
|
|
8.0f, // Highest
|
|
};
|
|
|
|
static_assert(ARRAY_COUNT(ImportanceTable) == (EMeshFeatureImportance::Highest + 1), "Importance table size mismatch."); // -1 because of TEMP_BROKEN(?)
|
|
check(Settings.SilhouetteImportance < EMeshFeatureImportance::Highest+1); // -1 because of TEMP_BROKEN(?)
|
|
check(Settings.TextureImportance < EMeshFeatureImportance::Highest+1); // -1 because of TEMP_BROKEN(?)
|
|
check(Settings.ShadingImportance < EMeshFeatureImportance::Highest+1); // -1 because of TEMP_BROKEN(?)
|
|
|
|
ReductionSettings->SetFeatureFlags(FeatureFlagsMask);
|
|
ReductionSettings->SetMaxDeviation(MaxDeviation);
|
|
ReductionSettings->SetReductionRatio(ReductionRatio);
|
|
ReductionSettings->SetGeometryImportance(ImportanceTable[Settings.SilhouetteImportance]);
|
|
ReductionSettings->SetTextureImportance(ImportanceTable[Settings.TextureImportance]);
|
|
ReductionSettings->SetMaterialImportance(ImportanceTable[Settings.TextureImportance]);
|
|
ReductionSettings->SetShadingImportance( ImportanceTable[Settings.ShadingImportance]);
|
|
ReductionSettings->SetAllowDirectX(false);
|
|
}
|
|
|
|
void SetNormalSettings(SimplygonSDK::spNormalCalculationSettings NormalSettings, const FMeshReductionSettings& Settings)
|
|
{
|
|
NormalSettings->SetReplaceNormals(Settings.bRecalculateNormals);
|
|
NormalSettings->SetScaleByArea(false);
|
|
NormalSettings->SetScaleByAngle(false);
|
|
NormalSettings->SetHardEdgeAngle(Settings.HardAngleThreshold);
|
|
}
|
|
|
|
/**
|
|
* Creates a Simplygon scene representation of the skeletal hierarchy.
|
|
* @param InScene - The Simplygon scene.
|
|
* @param InSkeleton - The skeletal hierarchy from which to create the Simplygon representation.
|
|
* @param OutBoneTableIDs - A mapping of Bone IDs from RefSkeleton to Simplygon Bone Table IDs
|
|
*/
|
|
void CreateSkeletalHierarchy( SimplygonSDK::spScene& InScene, const FReferenceSkeleton& InSkeleton, TArray<SimplygonSDK::rid>& OutBoneTableIDs )
|
|
{
|
|
TArray<SimplygonSDK::spSceneBone> BoneArray;
|
|
|
|
//Create Simplygon scene nodes for each bone in our Skeletal Hierarchy
|
|
for(int32 BoneIndex=0; BoneIndex < InSkeleton.GetNum(); ++BoneIndex)
|
|
{
|
|
SimplygonSDK::spSceneBone SceneBone = SDK->CreateSceneBone();
|
|
BoneArray.Add(SceneBone);
|
|
}
|
|
|
|
SimplygonSDK::spSceneBoneTable BoneTable = InScene->GetBoneTable();
|
|
for(int32 BoneIndex=0; BoneIndex < InSkeleton.GetNum(); ++BoneIndex)
|
|
{
|
|
int32 ParentIndex = InSkeleton.GetParentIndex(BoneIndex);
|
|
SimplygonSDK::spSceneBone CurrentBone = BoneArray[BoneIndex];
|
|
|
|
//We add the bone to the scene's bone table. This returns a uid that we must apply to the geometry data.
|
|
SimplygonSDK::rid BoneID = BoneTable->AddBone(CurrentBone);
|
|
OutBoneTableIDs.Add(BoneID);
|
|
|
|
//Handle root bone. Add to Scene root.
|
|
if(BoneIndex == 0)
|
|
{
|
|
InScene->GetRootNode()->AddChild(CurrentBone);
|
|
}
|
|
else
|
|
{
|
|
SimplygonSDK::spSceneBone ParentBone = BoneArray[ParentIndex];
|
|
ParentBone->AddChild(CurrentBone);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a Simplygon geometry representation from a skeletal mesh LOD model.
|
|
* @param Scene - The Simplygon scene.
|
|
* @param LODModel - The skeletal mesh LOD model from which to create the geometry data.
|
|
* @param BoneIDs - A maps of Bone IDs from RefSkeleton to Simplygon BoneTable IDs
|
|
* @returns a Simplygon geometry data representation of the skeletal mesh LOD.
|
|
*/
|
|
SimplygonSDK::spGeometryData CreateGeometryFromSkeletalLODModel( SimplygonSDK::spScene& Scene, const FStaticLODModel& LODModel, const TArray<SimplygonSDK::rid>& BoneIDs )
|
|
{
|
|
TArray<FSoftSkinVertex> Vertices;
|
|
FMultiSizeIndexContainerData IndexData;
|
|
LODModel.GetVertices( Vertices );
|
|
LODModel.MultiSizeIndexContainer.GetIndexBufferData( IndexData );
|
|
|
|
const uint32 VertexCount = LODModel.NumVertices;
|
|
const uint32 TexCoordCount = LODModel.NumTexCoords;
|
|
check( TexCoordCount <= MAX_TEXCOORDS );
|
|
|
|
uint32 TriCount = 0;
|
|
#if WITH_APEX_CLOTHING
|
|
const uint32 SectionCount = (uint32)LODModel.NumNonClothingSections();
|
|
#else
|
|
const uint32 SectionCount = LODModel.Sections.Num();
|
|
#endif // #if WITH_APEX_CLOTHING
|
|
for ( uint32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex )
|
|
{
|
|
TriCount += LODModel.Sections[ SectionIndex ].NumTriangles;
|
|
}
|
|
|
|
SimplygonSDK::spGeometryData GeometryData = SDK->CreateGeometryData();
|
|
GeometryData->SetVertexCount( VertexCount );
|
|
GeometryData->SetTriangleCount( TriCount );
|
|
|
|
// Per-vertex data.
|
|
GeometryData->AddBoneIds( MAX_TOTAL_INFLUENCES );
|
|
GeometryData->AddBoneWeights( MAX_TOTAL_INFLUENCES );
|
|
SimplygonSDK::spRealArray Positions = GeometryData->GetCoords();
|
|
SimplygonSDK::spRidArray BoneIds = GeometryData->GetBoneIds();
|
|
SimplygonSDK::spRealArray BoneWeights = GeometryData->GetBoneWeights();
|
|
|
|
// Per-corner per-triangle data.
|
|
SimplygonSDK::spRidArray Indices = GeometryData->GetVertexIds();
|
|
SimplygonSDK::spRealArray TexCoords[MAX_TEXCOORDS];
|
|
for( uint32 TexCoordIndex = 0; TexCoordIndex < TexCoordCount; ++TexCoordIndex )
|
|
{
|
|
GeometryData->AddTexCoords( TexCoordIndex );
|
|
TexCoords[TexCoordIndex] = GeometryData->GetTexCoords( TexCoordIndex );
|
|
check( TexCoords[TexCoordIndex]->GetTupleSize() == 2 );
|
|
}
|
|
GeometryData->AddNormals();
|
|
GeometryData->AddTangents(0);
|
|
GeometryData->AddBitangents(0);
|
|
SimplygonSDK::spRealArray Normals = GeometryData->GetNormals();
|
|
SimplygonSDK::spRealArray Tangents = GeometryData->GetTangents(0);
|
|
SimplygonSDK::spRealArray Bitangents = GeometryData->GetBitangents(0);
|
|
|
|
SimplygonSDK::spUnsignedCharArray Colors;
|
|
if ( LODModel.ColorVertexBuffer.GetNumVertices() == VertexCount )
|
|
{
|
|
GeometryData->AddBaseTypeUserTriangleVertexField( SimplygonSDK::TYPES_ID_UCHAR, SIMPLYGON_COLOR_CHANNEL, 4 );
|
|
SimplygonSDK::spValueArray ColorValues = GeometryData->GetUserTriangleVertexField( SIMPLYGON_COLOR_CHANNEL );
|
|
check( ColorValues );
|
|
Colors = SimplygonSDK::IUnsignedCharArray::SafeCast( ColorValues );
|
|
check( Colors );
|
|
}
|
|
|
|
// Per-triangle data.
|
|
GeometryData->AddMaterialIds();
|
|
SimplygonSDK::spRidArray MaterialIndices = GeometryData->GetMaterialIds();
|
|
|
|
// Add per-vertex data. This data needs to be added per-chunk so that we can properly map bones.
|
|
#if WITH_APEX_CLOTHING
|
|
const int32 ChunkCount = LODModel.NumNonClothingSections();
|
|
#else
|
|
const int32 ChunkCount = LODModel.Chunks.Num();
|
|
#endif // #if WITH_APEX_CLOTHING
|
|
uint32 FirstVertex = 0;
|
|
for ( int32 ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex )
|
|
{
|
|
const FSkelMeshChunk& Chunk = LODModel.Chunks[ ChunkIndex ];
|
|
const uint32 LastVertex = FirstVertex + (uint32)Chunk.RigidVertices.Num() + (uint32)Chunk.SoftVertices.Num();
|
|
for ( uint32 VertexIndex = FirstVertex; VertexIndex < LastVertex; ++VertexIndex )
|
|
{
|
|
FSoftSkinVertex& Vertex = Vertices[ VertexIndex ];
|
|
SimplygonSDK::rid VertexBoneIds[MAX_TOTAL_INFLUENCES];
|
|
SimplygonSDK::real VertexBoneWeights[MAX_TOTAL_INFLUENCES];
|
|
|
|
uint32 TotalInfluence = 0;
|
|
for ( uint32 InfluenceIndex = 0; InfluenceIndex < MAX_TOTAL_INFLUENCES; ++InfluenceIndex )
|
|
{
|
|
int32 BoneIndex = Vertex.InfluenceBones[InfluenceIndex];
|
|
const uint8 BoneInfluence = Vertex.InfluenceWeights[InfluenceIndex];
|
|
TotalInfluence += BoneInfluence;
|
|
|
|
if ( BoneInfluence > 0 )
|
|
{
|
|
check( BoneIndex < Chunk.BoneMap.Num() );
|
|
uint32 BoneID = BoneIDs[ Chunk.BoneMap[ BoneIndex ] ];
|
|
VertexBoneIds[InfluenceIndex] = BoneID;
|
|
VertexBoneWeights[InfluenceIndex] = BoneInfluence / 255.0f;
|
|
}
|
|
else
|
|
{
|
|
//Set BoneID and BoneWeight to -1 and 0 respectively as required by simplygon.
|
|
VertexBoneIds[InfluenceIndex] = -1;
|
|
VertexBoneWeights[InfluenceIndex] = 0;
|
|
}
|
|
}
|
|
check( TotalInfluence == 255 );
|
|
|
|
Positions->SetTuple( VertexIndex, (float*)&Vertex.Position );
|
|
BoneIds->SetTuple( VertexIndex, VertexBoneIds );
|
|
BoneWeights->SetTuple( VertexIndex, VertexBoneWeights );
|
|
}
|
|
FirstVertex = LastVertex;
|
|
}
|
|
|
|
// Add per-vertex per-triangle data.
|
|
for ( uint32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex )
|
|
{
|
|
const FSkelMeshSection& Section = LODModel.Sections[ SectionIndex ];
|
|
const uint32 FirstIndex = Section.BaseIndex;
|
|
const uint32 LastIndex = FirstIndex + Section.NumTriangles * 3;
|
|
|
|
for ( uint32 Index = FirstIndex; Index < LastIndex; ++Index )
|
|
{
|
|
uint32 VertexIndex = IndexData.Indices[ Index ];
|
|
FSoftSkinVertex& Vertex = Vertices[ VertexIndex ];
|
|
|
|
FVector Normal = Vertex.TangentZ;
|
|
FVector Tangent = Vertex.TangentX;
|
|
FVector Bitangent = Vertex.TangentY;
|
|
|
|
Indices->SetItem( Index, VertexIndex );
|
|
Normals->SetTuple( Index, (float*)&Normal );
|
|
Tangents->SetTuple( Index, (float*)&Tangent );
|
|
Bitangents->SetTuple( Index, (float*)&Bitangent );
|
|
|
|
for( uint32 TexCoordIndex = 0; TexCoordIndex < TexCoordCount; ++TexCoordIndex )
|
|
{
|
|
FVector2D TexCoord = Vertex.UVs[TexCoordIndex];
|
|
TexCoords[TexCoordIndex]->SetTuple( Index, (float*)&TexCoord );
|
|
}
|
|
|
|
if ( Colors )
|
|
{
|
|
FColor Color = LODModel.ColorVertexBuffer.VertexColor( VertexIndex );
|
|
Colors->SetTuple( Index, (UCHAR*)&Color );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add per-triangle data.
|
|
for ( uint32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex )
|
|
{
|
|
const FSkelMeshSection& Section = LODModel.Sections[ SectionIndex ];
|
|
const uint32 FirstTriIndex = Section.BaseIndex / 3;
|
|
const uint32 LastTriIndex = FirstTriIndex + Section.NumTriangles - 1;
|
|
const uint16 MaterialIndex = Section.MaterialIndex;
|
|
for ( uint32 TriIndex = FirstTriIndex; TriIndex <= LastTriIndex; ++TriIndex )
|
|
{
|
|
MaterialIndices->SetItem( TriIndex, MaterialIndex );
|
|
check( MaterialIndices->GetItem( TriIndex) == MaterialIndex );
|
|
}
|
|
}
|
|
|
|
// Create a new simplygon scene mesh object
|
|
SimplygonSDK::spSceneMesh Mesh = SDK->CreateSceneMesh();
|
|
|
|
// Assign the geometry data to the mesh
|
|
Mesh->SetGeometry( GeometryData );
|
|
|
|
// Add Mesh to the scene
|
|
Scene->GetRootNode()->AddChild( Mesh );
|
|
|
|
return GeometryData;
|
|
}
|
|
|
|
/**
|
|
* Holds data needed to create skeletal mesh skinning streams.
|
|
*/
|
|
struct FSkeletalMeshData
|
|
{
|
|
TArray<FVertInfluence> Influences;
|
|
TArray<FMeshWedge> Wedges;
|
|
TArray<FMeshFace> Faces;
|
|
TArray<FVector> Points;
|
|
uint32 TexCoordCount;
|
|
};
|
|
|
|
/**
|
|
* Extracts mesh data from the Simplygon geometry representation in to a set of data usable by skeletal meshes.
|
|
* @param GeometryData - the Simplygon geometry.
|
|
* @param MeshData - the skeletal mesh output data.
|
|
*/
|
|
void ExtractSkeletalDataFromGeometry( const SimplygonSDK::spGeometryData& GeometryData, FSkeletalMeshData& MeshData )
|
|
{
|
|
TArray<FVector> PointNormals;
|
|
TArray<uint32> PointList;
|
|
TArray<uint32> PointInfluenceMap;
|
|
|
|
check( GeometryData );
|
|
|
|
SimplygonSDK::spRealArray Positions = GeometryData->GetCoords();
|
|
SimplygonSDK::spRidArray Indices = GeometryData->GetVertexIds();
|
|
SimplygonSDK::spRidArray MaterialIndices = GeometryData->GetMaterialIds();
|
|
SimplygonSDK::spValueArray VertexColorValues = GeometryData->GetUserTriangleVertexField( SIMPLYGON_COLOR_CHANNEL );
|
|
SimplygonSDK::spUnsignedCharArray VertexColors = SimplygonSDK::IUnsignedCharArray::SafeCast( VertexColorValues );
|
|
SimplygonSDK::spRealArray Normals = GeometryData->GetNormals();
|
|
SimplygonSDK::spRealArray Tangents = GeometryData->GetTangents(0);
|
|
SimplygonSDK::spRealArray Bitangents = GeometryData->GetBitangents(0);
|
|
SimplygonSDK::spRidArray BoneIds = GeometryData->GetBoneIds();
|
|
SimplygonSDK::spRealArray BoneWeights = GeometryData->GetBoneWeights();
|
|
SimplygonSDK::spRealArray TexCoords[MAX_TEXCOORDS];
|
|
uint32 TexCoordCount = 0;
|
|
for( uint32 TexCoordIndex = 0; TexCoordIndex < MAX_TEXCOORDS; ++TexCoordIndex )
|
|
{
|
|
TexCoords[TexCoordIndex] = GeometryData->GetTexCoords(TexCoordIndex);
|
|
if ( TexCoords[TexCoordIndex] == NULL )
|
|
{
|
|
break;
|
|
}
|
|
TexCoordCount++;
|
|
}
|
|
MeshData.TexCoordCount = TexCoordCount;
|
|
|
|
check( Positions );
|
|
check( Indices );
|
|
check( MaterialIndices );
|
|
check( Normals );
|
|
check( Tangents );
|
|
check( Bitangents );
|
|
check( BoneIds );
|
|
check( BoneWeights );
|
|
|
|
const bool bHaveColors = (VertexColors != NULL);
|
|
const uint32 VertexCount = GeometryData->GetVertexCount();
|
|
const uint32 TriCount = GeometryData->GetTriangleCount();
|
|
|
|
// Initialize the lists of points, wedges, and faces.
|
|
MeshData.Points.AddZeroed( VertexCount );
|
|
PointNormals.AddZeroed( VertexCount );
|
|
PointList.Reserve( VertexCount );
|
|
PointInfluenceMap.Reserve( VertexCount );
|
|
for ( uint32 PointIndex = 0; PointIndex < VertexCount; ++PointIndex )
|
|
{
|
|
PointList.Add( INDEX_NONE );
|
|
PointInfluenceMap.Add( INDEX_NONE );
|
|
}
|
|
MeshData.Wedges.AddZeroed( TriCount * 3 );
|
|
MeshData.Faces.AddZeroed( TriCount );
|
|
|
|
//The number of influences may have changed if we have specified a max number of bones per vertex.
|
|
uint32 NumOfInfluences = FMath::Min<uint32>( BoneIds->GetTupleSize() , MAX_TOTAL_INFLUENCES );
|
|
|
|
// Per-vertex data.
|
|
for ( uint32 VertexIndex = 0; VertexIndex < VertexCount; ++VertexIndex )
|
|
{
|
|
FVector& Point = MeshData.Points[ VertexIndex ];
|
|
SimplygonSDK::rid VertexBoneIds[MAX_TOTAL_INFLUENCES];
|
|
SimplygonSDK::real VertexBoneWeights[MAX_TOTAL_INFLUENCES];
|
|
Positions->GetTuple( VertexIndex, (float*)&Point );
|
|
BoneIds->GetTuple( VertexIndex, VertexBoneIds );
|
|
BoneWeights->GetTuple( VertexIndex, VertexBoneWeights );
|
|
PointInfluenceMap[ VertexIndex ] = (uint32)MeshData.Influences.Num();
|
|
for ( uint32 InfluenceIndex = 0; InfluenceIndex < NumOfInfluences; ++InfluenceIndex )
|
|
{
|
|
const uint16 BoneIndex = VertexBoneIds[InfluenceIndex];
|
|
const float BoneWeight = VertexBoneWeights[InfluenceIndex];
|
|
if ( InfluenceIndex == 0 || BoneWeight > 0.0f )
|
|
{
|
|
FVertInfluence* VertInfluence = new(MeshData.Influences) FVertInfluence;
|
|
VertInfluence->BoneIndex = BoneIndex;
|
|
VertInfluence->Weight = BoneWeight;
|
|
VertInfluence->VertIndex = VertexIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Per-triangle and per-corner data.
|
|
for ( uint32 TriIndex = 0; TriIndex < TriCount; ++TriIndex )
|
|
{
|
|
// Per-triangle data.
|
|
FMeshFace& Face = MeshData.Faces[ TriIndex ];
|
|
Face.MeshMaterialIndex = MaterialIndices->GetItem( TriIndex );
|
|
|
|
// Per-corner data.
|
|
for( uint32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex )
|
|
{
|
|
const uint32 WedgeIndex = TriIndex * 3 + CornerIndex;
|
|
const uint32 BasePointIndex = (uint32)Indices->GetItem( WedgeIndex );
|
|
uint32 PointIndex = BasePointIndex;
|
|
|
|
check( BasePointIndex < (uint32)PointList.Num() );
|
|
|
|
// Duplicate points where needed to create hard edges.
|
|
FVector WedgeNormal;
|
|
Normals->GetTuple( WedgeIndex, (float*)&WedgeNormal );
|
|
WedgeNormal.Normalize();
|
|
FVector PointNormal = PointNormals[ PointIndex ];
|
|
if ( PointNormal.SizeSquared() < KINDA_SMALL_NUMBER )
|
|
{
|
|
PointNormals[ PointIndex ] = WedgeNormal;
|
|
}
|
|
else
|
|
{
|
|
while ( (PointNormal | WedgeNormal) - 1.0f < -KINDA_SMALL_NUMBER )
|
|
{
|
|
PointIndex = PointList[ PointIndex ];
|
|
if ( PointIndex == INDEX_NONE )
|
|
{
|
|
break;
|
|
}
|
|
check( PointIndex < (uint32)PointList.Num() );
|
|
PointNormal = PointNormals[ PointIndex ];
|
|
}
|
|
|
|
if ( PointIndex == INDEX_NONE )
|
|
{
|
|
FVector Point = MeshData.Points[ BasePointIndex ];
|
|
PointIndex = MeshData.Points.Add( Point );
|
|
check( PointIndex == (uint32)PointList.Num() );
|
|
check( PointIndex == (uint32)PointInfluenceMap.Num() );
|
|
check( PointIndex == (uint32)PointNormals.Num() );
|
|
PointNormals.Add( WedgeNormal );
|
|
uint32 NextPointIndex = PointList[ BasePointIndex ];
|
|
check( NextPointIndex < (uint32)PointList.Num() || NextPointIndex == INDEX_NONE );
|
|
PointList[ BasePointIndex ] = PointIndex;
|
|
PointList.Add( NextPointIndex );
|
|
PointInfluenceMap.Add( (uint32)MeshData.Influences.Num() );
|
|
|
|
int32 InfluenceIndex = PointInfluenceMap[ BasePointIndex ];
|
|
while ( MeshData.Influences[ InfluenceIndex ].VertIndex == BasePointIndex )
|
|
{
|
|
FVertInfluence* NewVertInfluence = new( MeshData.Influences ) FVertInfluence;
|
|
NewVertInfluence->BoneIndex = MeshData.Influences[ InfluenceIndex ].BoneIndex;
|
|
NewVertInfluence->Weight = MeshData.Influences[ InfluenceIndex ].Weight;
|
|
NewVertInfluence->VertIndex = PointIndex;
|
|
InfluenceIndex++;
|
|
}
|
|
|
|
check( PointNormals.Num() == MeshData.Points.Num() );
|
|
check( PointList.Num() == MeshData.Points.Num() );
|
|
check( PointInfluenceMap.Num() == MeshData.Points.Num() );
|
|
}
|
|
}
|
|
|
|
check( PointIndex != INDEX_NONE );
|
|
check( ( MeshData.Points[ PointIndex ] - MeshData.Points[ BasePointIndex ] ).SizeSquared() == 0.0f );
|
|
|
|
FMeshWedge& Wedge = MeshData.Wedges[ WedgeIndex ];
|
|
Wedge.iVertex = PointIndex;
|
|
for( uint32 TexCoordIndex = 0; TexCoordIndex < TexCoordCount; ++TexCoordIndex )
|
|
{
|
|
TexCoords[TexCoordIndex]->GetTuple( WedgeIndex, (float*)&Wedge.UVs[TexCoordIndex] );
|
|
}
|
|
|
|
if( bHaveColors )
|
|
{
|
|
VertexColors->GetTuple( WedgeIndex, (uint8 *)&Wedge.Color );
|
|
}
|
|
else
|
|
{
|
|
Wedge.Color = FColor( 255, 255, 255, 255 );
|
|
}
|
|
|
|
Face.iWedge[CornerIndex] = WedgeIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a skeletal mesh LOD model from the Simplygon geometry representation.
|
|
* @param GeometryData - the Simplygon geometry representation from which to create a skeletal mesh LOD.
|
|
* @param SkeletalMesh - the skeletal mesh in to which the LOD model will be added.
|
|
* @param NewModel - the LOD model in to which the geometry will be stored.
|
|
*/
|
|
void CreateSkeletalLODModelFromGeometry( const SimplygonSDK::spGeometryData& GeometryData, const FReferenceSkeleton& RefSkeleton, FStaticLODModel* NewModel )
|
|
{
|
|
check( GeometryData );
|
|
|
|
FSkeletalMeshData MeshData;
|
|
ExtractSkeletalDataFromGeometry( GeometryData, MeshData );
|
|
|
|
// Create dummy map of 'point to original'
|
|
TArray<int32> DummyMap;
|
|
DummyMap.AddUninitialized(MeshData.Points.Num());
|
|
for(int32 PointIdx = 0; PointIdx<MeshData.Points.Num(); PointIdx++)
|
|
{
|
|
DummyMap[PointIdx] = PointIdx;
|
|
}
|
|
|
|
IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities");
|
|
// Create skinning streams for NewModel.
|
|
MeshUtilities.BuildSkeletalMesh(
|
|
*NewModel,
|
|
RefSkeleton,
|
|
MeshData.Influences,
|
|
MeshData.Wedges,
|
|
MeshData.Faces,
|
|
MeshData.Points,
|
|
DummyMap
|
|
);
|
|
|
|
// Set texture coordinate count on the new model.
|
|
NewModel->NumTexCoords = MeshData.TexCoordCount;
|
|
NewModel->Size = 0;
|
|
}
|
|
|
|
/**
|
|
* Sets reduction settings for Simplygon.
|
|
* @param Settings - The skeletal mesh optimization settings.
|
|
* @param ReductionSettings - The reduction settings to set for Simplygon.
|
|
*/
|
|
void SetReductionSettings( const FSkeletalMeshOptimizationSettings& Settings, float BoundsRadius, int32 SourceTriCount, SimplygonSDK::spReductionSettings ReductionSettings )
|
|
{
|
|
// Compute max deviation from quality.
|
|
float MaxDeviation = Settings.MaxDeviationPercentage > 0.0f ? Settings.MaxDeviationPercentage * BoundsRadius : SimplygonSDK::REAL_MAX;
|
|
// Set the reduction ratio such that at least 1 triangle or 5% of the original triangles remain, whichever is larger.
|
|
float MinReductionRatio = FMath::Max<float>(1.0f / SourceTriCount, 0.05f);
|
|
float MaxReductionRatio = (Settings.MaxDeviationPercentage > 0.0f && Settings.NumOfTrianglesPercentage == 1.0f) ? MinReductionRatio : 1.0f;
|
|
float ReductionRatio = FMath::Clamp(Settings.NumOfTrianglesPercentage, MinReductionRatio, MaxReductionRatio);
|
|
|
|
//Enable feature flags for those features where we have set an importance
|
|
unsigned int FeatureFlagsMask = SimplygonSDK::SG_FEATUREFLAGS_GROUP;
|
|
if( Settings.TextureImportance != SMOI_Off )
|
|
{
|
|
FeatureFlagsMask |= (SimplygonSDK::SG_FEATUREFLAGS_TEXTURE0 | SimplygonSDK::SG_FEATUREFLAGS_MATERIAL);
|
|
}
|
|
if( Settings.ShadingImportance != SMOI_Off )
|
|
{
|
|
FeatureFlagsMask |= SimplygonSDK::SG_FEATUREFLAGS_SHADING;
|
|
}
|
|
|
|
const float ImportanceTable[] =
|
|
{
|
|
0.0f, // OFF
|
|
0.125f, // Lowest
|
|
0.35f, // Low,
|
|
1.0f, // Normal
|
|
2.8f, // High
|
|
8.0f, // Highest
|
|
};
|
|
|
|
static_assert(ARRAY_COUNT(ImportanceTable) == SMOI_MAX, "Bad importance table size.");
|
|
check( Settings.SilhouetteImportance < SMOI_MAX );
|
|
check( Settings.TextureImportance < SMOI_MAX );
|
|
check( Settings.ShadingImportance < SMOI_MAX );
|
|
check( Settings.SkinningImportance < SMOI_MAX );
|
|
|
|
ReductionSettings->SetFeatureFlags( FeatureFlagsMask );
|
|
ReductionSettings->SetMaxDeviation( MaxDeviation );
|
|
ReductionSettings->SetReductionRatio( ReductionRatio );
|
|
ReductionSettings->SetGeometryImportance( ImportanceTable[Settings.SilhouetteImportance] );
|
|
ReductionSettings->SetTextureImportance( ImportanceTable[Settings.TextureImportance] );
|
|
ReductionSettings->SetMaterialImportance( ImportanceTable[Settings.TextureImportance] );
|
|
ReductionSettings->SetShadingImportance( ImportanceTable[Settings.ShadingImportance] );
|
|
ReductionSettings->SetSkinningImportance( ImportanceTable[Settings.SkinningImportance] );
|
|
}
|
|
|
|
/**
|
|
* Sets vertex normal settings for Simplygon.
|
|
* @param Settings - The skeletal mesh optimization settings.
|
|
* @param NormalSettings - The normal settings to set for Simplygon.
|
|
*/
|
|
void SetNormalSettings( const FSkeletalMeshOptimizationSettings& Settings, SimplygonSDK::spNormalCalculationSettings NormalSettings )
|
|
{
|
|
NormalSettings->SetReplaceNormals( Settings.bRecalcNormals );
|
|
NormalSettings->SetScaleByArea( false );
|
|
NormalSettings->SetScaleByAngle( false );
|
|
NormalSettings->SetHardEdgeAngle( Settings.NormalsThreshold );
|
|
}
|
|
|
|
/**
|
|
* Sets Bone Lod settings for Simplygon.
|
|
* @param Settings - The skeletal mesh optimization settings.
|
|
* @param NormalSettings - The Bone LOD to set for Simplygon.
|
|
*/
|
|
void SetBoneSettings( const FSkeletalMeshOptimizationSettings& Settings, SimplygonSDK::spBoneSettings BoneSettings)
|
|
{
|
|
BoneSettings->SetBoneLodProcess( SimplygonSDK::SG_BONEPROCESSING_RATIO_PROCESSING );
|
|
BoneSettings->SetBoneLodRatio ( Settings.BoneReductionRatio );
|
|
BoneSettings->SetMaxBonePerVertex( Settings.MaxBonesPerVertex );
|
|
}
|
|
|
|
/**
|
|
* Calculates the view distance that a mesh should be displayed at.
|
|
* @param MaxDeviation - The maximum surface-deviation between the reduced geometry and the original. This value should be acquired from Simplygon
|
|
* @returns The calculated view distance
|
|
*/
|
|
float CalculateViewDistance( float MaxDeviation )
|
|
{
|
|
// We want to solve for the depth in world space given the screen space distance between two pixels
|
|
//
|
|
// Assumptions:
|
|
// 1. There is no scaling in the view matrix.
|
|
// 2. The horizontal FOV is 90 degrees.
|
|
// 3. The backbuffer is 1920x1080.
|
|
//
|
|
// If we project two points at (X,Y,Z) and (X',Y,Z) from view space, we get their screen
|
|
// space positions: (X/Z, Y'/Z) and (X'/Z, Y'/Z) where Y' = Y * AspectRatio.
|
|
//
|
|
// The distance in screen space is then sqrt( (X'-X)^2/Z^2 + (Y'-Y')^2/Z^2 )
|
|
// or (X'-X)/Z. This is in clip space, so PixelDist = 1280 * 0.5 * (X'-X)/Z.
|
|
//
|
|
// Solving for Z: ViewDist = (X'-X * 640) / PixelDist
|
|
|
|
const float ViewDistance = (MaxDeviation * 960.0f);
|
|
return ViewDistance;
|
|
}
|
|
|
|
/**
|
|
* ProxyLOD Related Methods
|
|
*/
|
|
void SetMaterialChannel(
|
|
const TArray<FColor>& InSamples,
|
|
FIntPoint InTextureSize,
|
|
SimplygonSDK::spMaterial& InSGMaterial,
|
|
const char* SGMaterialChannelName)
|
|
{
|
|
int32 NumTexels = InTextureSize.X * InTextureSize.Y;
|
|
check(NumTexels == InSamples.Num())
|
|
|
|
if (NumTexels > 1)
|
|
{
|
|
SimplygonSDK::spImageData ImageData = SDK->CreateImageData();
|
|
ImageData->AddColors(SimplygonSDK::TYPES_ID_UCHAR, SimplygonSDK::SG_IMAGEDATA_FORMAT_RGBA);
|
|
ImageData->Set2DSize(InTextureSize.X, InTextureSize.Y);
|
|
SimplygonSDK::spUnsignedCharArray ImageColors = SimplygonSDK::SafeCast<SimplygonSDK::IUnsignedCharArray>(ImageData->GetColors());
|
|
|
|
//Set the texture data to simplygon color data texel per texel
|
|
ImageColors->SetTupleCount(NumTexels);
|
|
for (int32 TexelIndex = 0; TexelIndex < NumTexels; TexelIndex++)
|
|
{
|
|
// BGRA -> RGBA
|
|
uint8 Texel[4];
|
|
Texel[0] = InSamples[TexelIndex].R;
|
|
Texel[1] = InSamples[TexelIndex].G;
|
|
Texel[2] = InSamples[TexelIndex].B;
|
|
Texel[3] = InSamples[TexelIndex].A;
|
|
|
|
ImageColors->SetTuple(TexelIndex, Texel);
|
|
}
|
|
InSGMaterial->SetLayeredTextureImage(SGMaterialChannelName, 0, ImageData);
|
|
InSGMaterial->SetLayeredTextureLevel(SGMaterialChannelName, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
// handle uniform value
|
|
}
|
|
}
|
|
|
|
//Material conversions
|
|
bool CreateSGMaterialFromFlattenMaterial(
|
|
const TArray<MaterialExportUtils::FFlattenMaterial>& InputMaterials,
|
|
SimplygonSDK::spMaterialTable& OutSGMaterialTable,
|
|
FMaterialCastingProperties& OutCastProperties)
|
|
{
|
|
if (InputMaterials.Num() == 0)
|
|
{
|
|
//If there are no materials, feed Simplygon with a default material instead.
|
|
UE_LOG(LogSimplygon, Log, TEXT("Input meshes do not contain any materials. A proxy without material will be generated."));
|
|
return false;
|
|
}
|
|
|
|
for (int32 MaterialIndex = 0; MaterialIndex < InputMaterials.Num(); MaterialIndex++)
|
|
{
|
|
SimplygonSDK::spMaterial SGMaterial = SDK->CreateMaterial();
|
|
const MaterialExportUtils::FFlattenMaterial& FlattenMaterial = InputMaterials[MaterialIndex];
|
|
|
|
//Create UE4 Channels
|
|
//Simplygon 5.5 has three new channels already present called base color metallic roughness
|
|
//To conform with older simplygon versions:
|
|
if(!SGMaterial->HasUserChannel(SimplygonSDK::SG_MATERIAL_CHANNEL_BASECOLOR))
|
|
SGMaterial->AddUserChannel(SimplygonSDK::SG_MATERIAL_CHANNEL_BASECOLOR);
|
|
|
|
if(!SGMaterial->HasUserChannel(SimplygonSDK::SG_MATERIAL_CHANNEL_METALLIC))
|
|
SGMaterial->AddUserChannel(SimplygonSDK::SG_MATERIAL_CHANNEL_METALLIC);
|
|
|
|
if(!SGMaterial->HasUserChannel(SimplygonSDK::SG_MATERIAL_CHANNEL_ROUGHNESS))
|
|
SGMaterial->AddUserChannel(SimplygonSDK::SG_MATERIAL_CHANNEL_ROUGHNESS);
|
|
|
|
SGMaterial->SetName(TCHAR_TO_ANSI(*FString::Printf(TEXT("Material%d"), MaterialIndex)));
|
|
|
|
// Does current material have BaseColor?
|
|
if (FlattenMaterial.DiffuseSamples.Num())
|
|
{
|
|
SetMaterialChannel(FlattenMaterial.DiffuseSamples, FlattenMaterial.DiffuseSize, SGMaterial, SimplygonSDK::SG_MATERIAL_CHANNEL_DIFFUSE);
|
|
}
|
|
|
|
//Does current material have a normalmap?
|
|
if (FlattenMaterial.NormalSamples.Num())
|
|
{
|
|
OutCastProperties.bCastNormals = true;
|
|
SetMaterialChannel(FlattenMaterial.NormalSamples, FlattenMaterial.NormalSize, SGMaterial, SimplygonSDK::SG_MATERIAL_CHANNEL_NORMALS);
|
|
}
|
|
|
|
OutSGMaterialTable->AddMaterial(SGMaterial);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CreateFlattenMaterialFromSGMaterial(
|
|
SimplygonSDK::spMaterialTable& InSGMaterialTable,
|
|
MaterialExportUtils::FFlattenMaterial& OutMaterial)
|
|
{
|
|
//Get the diffuse data from SGMaterial
|
|
SimplygonSDK::spImageData SGDiffuseData = InSGMaterialTable->GetMaterial(0)->GetLayeredTextureImage(SimplygonSDK::SG_MATERIAL_CHANNEL_DIFFUSE, 0);
|
|
{
|
|
SimplygonSDK::spUnsignedCharArray DiffuseImageColors = SimplygonSDK::SafeCast<SimplygonSDK::IUnsignedCharArray>( SGDiffuseData->GetColors() );
|
|
|
|
OutMaterial.DiffuseSize.X = SGDiffuseData->GetXSize();
|
|
OutMaterial.DiffuseSize.Y = SGDiffuseData->GetYSize();
|
|
int32 TexelsCount = OutMaterial.DiffuseSize.X*OutMaterial.DiffuseSize.Y;
|
|
|
|
//Fill diffuse texture with color data collected from simplygon
|
|
OutMaterial.DiffuseSamples.SetNumUninitialized(TexelsCount);
|
|
for (int32 TexelIndex = 0; TexelIndex < TexelsCount; ++TexelIndex)
|
|
{
|
|
uint8 DiffuseData[4];
|
|
DiffuseImageColors->GetTuple(TexelIndex, (unsigned char*)&DiffuseData);
|
|
|
|
OutMaterial.DiffuseSamples[TexelIndex].R = DiffuseData[0];
|
|
OutMaterial.DiffuseSamples[TexelIndex].G = DiffuseData[1];
|
|
OutMaterial.DiffuseSamples[TexelIndex].B = DiffuseData[2];
|
|
OutMaterial.DiffuseSamples[TexelIndex].A = DiffuseData[3];
|
|
}
|
|
}
|
|
|
|
//Get the normal data from SGMaterial
|
|
SimplygonSDK::spImageData SGNormalData = InSGMaterialTable->GetMaterial(0)->GetLayeredTextureImage(SimplygonSDK::SG_MATERIAL_CHANNEL_NORMALS, 0);
|
|
if (SGNormalData)
|
|
{
|
|
SimplygonSDK::spUnsignedCharArray NormalImageColors = SimplygonSDK::SafeCast<SimplygonSDK::IUnsignedCharArray>( SGNormalData->GetColors() );
|
|
OutMaterial.NormalSize.X = SGNormalData->GetXSize();
|
|
OutMaterial.NormalSize.Y = SGNormalData->GetYSize();
|
|
int32 TexelsCount = OutMaterial.NormalSize.X*OutMaterial.NormalSize.Y;
|
|
|
|
//Fill normal texture with color data collected from simplygon
|
|
OutMaterial.NormalSamples.SetNumUninitialized(TexelsCount);
|
|
for (int32 TexelIndex = 0; TexelIndex < TexelsCount; ++TexelIndex)
|
|
{
|
|
uint8 NormalData[3];
|
|
NormalImageColors->GetTuple(TexelIndex, (unsigned char*)&NormalData);
|
|
|
|
OutMaterial.NormalSamples[TexelIndex].R = NormalData[0];
|
|
OutMaterial.NormalSamples[TexelIndex].G = NormalData[1];
|
|
OutMaterial.NormalSamples[TexelIndex].B = NormalData[2];
|
|
OutMaterial.NormalSamples[TexelIndex].A = FColor::White.A;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
TScopedPointer<FSimplygonMeshReduction> GSimplygonMeshReduction;
|
|
|
|
void FSimplygonMeshReductionModule::StartupModule()
|
|
{
|
|
GSimplygonMeshReduction = FSimplygonMeshReduction::Create();
|
|
}
|
|
|
|
void FSimplygonMeshReductionModule::ShutdownModule()
|
|
{
|
|
GSimplygonMeshReduction = NULL;
|
|
}
|
|
|
|
IMeshReduction* FSimplygonMeshReductionModule::GetMeshReductionInterface()
|
|
{
|
|
return GSimplygonMeshReduction;
|
|
}
|
|
|
|
IMeshMerging* FSimplygonMeshReductionModule::GetMeshMergingInterface()
|
|
{
|
|
return GSimplygonMeshReduction;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |