You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#codereview Ben.Marsh #rb none #lockdown nick.penwarden [CL 3153993 by Ori Cohen in Main branch]
2780 lines
104 KiB
C++
2780 lines
104 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Core.h"
|
|
#include "UnrealEd.h"
|
|
#include "Engine.h"
|
|
#include "RawMesh.h"
|
|
#include "MeshUtilities.h"
|
|
#include "MaterialUtilities.h"
|
|
#include "SimplygonTypes.h"
|
|
#include "StaticMeshResources.h"
|
|
#include "Components/SplineMeshComponent.h"
|
|
#include "SimplygonSDK.h"
|
|
#include "ScopedTimers.h"
|
|
|
|
#include "MeshMergeData.h"
|
|
|
|
// Standard Simplygon channels have some issues with extracting color data back from simplification,
|
|
// so we use this workaround with user channels
|
|
static const char* USER_MATERIAL_CHANNEL_METALLIC = "UserMetallic";
|
|
static const char* USER_MATERIAL_CHANNEL_ROUGHNESS = "UserRoughness";
|
|
static const char* USER_MATERIAL_CHANNEL_SPECULAR = "UserSpecular";
|
|
|
|
//@third party code BEGIN SIMPLYGON
|
|
#define USE_USER_OPACITY_CHANNEL 1
|
|
#if USE_USER_OPACITY_CHANNEL
|
|
static const char* USER_MATERIAL_CHANNEL_OPACITY = "UserOpacity";
|
|
#endif
|
|
//@third party code END SIMPLYGON
|
|
static const TCHAR* SG_UE_INTEGRATION_REV = TEXT("@305");
|
|
|
|
#ifdef __clang__
|
|
// SimplygonSDK.h uses 'deprecated' pragma which Clang does not recognize
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown pragma ignored [-Wunknown-pragmas]
|
|
#endif
|
|
|
|
#include "SimplygonSDK.h"
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
#include "MeshBoneReduction.h"
|
|
#include "ComponentReregisterContext.h"
|
|
//@third party code BEGIN SIMPLYGON
|
|
#include "ImageUtils.h"
|
|
|
|
// Notes about IChartAggregator:
|
|
// - Available since Simplygon 7.0 (defined(SIMPLYGONSDK_VERSION) && SIMPLYGONSDK_VERSION >= 0x700).
|
|
// - Use of pure IChartAggregator will re-introduce bugs with UVs outside of 0..1 range, so UVs should
|
|
// be scaled.
|
|
// - IChartAggregator probably needs more settings to be provided, because it will grow number of mesh
|
|
// vertices a lot (up to 3x of mesh face count).
|
|
#define USE_SIMPLYGON_CHART_AGGREGATOR 0
|
|
|
|
// Use Engine's FLayoutUV class to generate non-overlapping texture coordinates. If disabled, Simplygon
|
|
// will be used.
|
|
#define USE_FLAYOUT_UV 0
|
|
|
|
//#define DEBUG_PROXY_MESH
|
|
|
|
#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:
|
|
|
|
FDefaultEventHandler() : Task(nullptr), PreviousProgress( 0 ) {}
|
|
FScopedSlowTask* Task;
|
|
|
|
int32 PreviousProgress;
|
|
|
|
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);
|
|
|
|
if (IsInGameThread())
|
|
{
|
|
if ( Task != nullptr)
|
|
{
|
|
Task->EnterProgressFrame(ProgressPercent - PreviousProgress);
|
|
PreviousProgress = ProgressPercent;
|
|
}
|
|
}
|
|
|
|
// 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
|
|
};
|
|
|
|
static void* GSimplygonSDKDLLHandle = nullptr;
|
|
|
|
class FSimplygonMeshReduction
|
|
: public IMeshReduction
|
|
, public IMeshMerging
|
|
{
|
|
public:
|
|
virtual ~FSimplygonMeshReduction()
|
|
{
|
|
}
|
|
|
|
virtual const FString& GetVersionString() const override
|
|
{
|
|
return VersionString;
|
|
}
|
|
|
|
virtual void Reduce(
|
|
FRawMesh& OutReducedMesh,
|
|
float& OutMaxDeviation,
|
|
const FRawMesh& InMesh,
|
|
const TMultiMap<int32, int32>& InOverlappingCorners,
|
|
const FMeshReductionSettings& InSettings
|
|
) override
|
|
{
|
|
SimplygonSDK::spGeometryData GeometryData = CreateGeometryFromRawMesh(InMesh);
|
|
check(GeometryData);
|
|
|
|
SimplygonSDK::spScene Scene = SDK->CreateScene();
|
|
|
|
SimplygonSDK::spSceneMesh Mesh = SDK->CreateSceneMesh();
|
|
Mesh->SetGeometry(GeometryData);
|
|
Mesh->SetName(TCHAR_TO_ANSI(*FString::Printf(TEXT("UnrealMesh"))));
|
|
Scene->GetRootNode()->AddChild(Mesh);
|
|
|
|
SimplygonSDK::spReductionProcessor ReductionProcessor = SDK->CreateReductionProcessor();
|
|
ReductionProcessor->AddObserver(&EventHandler, SimplygonSDK::SG_EVENT_PROGRESS);
|
|
ReductionProcessor->SetScene(Scene);
|
|
|
|
SimplygonSDK::spRepairSettings RepairSettings = ReductionProcessor->GetRepairSettings();
|
|
RepairSettings->SetWeldDist(InSettings.WeldingThreshold);
|
|
RepairSettings->SetTjuncDist(InSettings.WeldingThreshold);
|
|
|
|
SimplygonSDK::spReductionSettings ReductionSettings = ReductionProcessor->GetReductionSettings();
|
|
SetReductionSettings(ReductionSettings, InSettings, GeometryData->GetTriangleCount());
|
|
|
|
//Set visibility settings
|
|
SimplygonSDK::spVisibilitySettings VisibilitySettings = ReductionProcessor->GetVisibilitySettings();
|
|
VisibilitySettings->SetCullOccludedGeometry(InSettings.bCullOccluded);
|
|
int32 TempAggressiveness = InSettings.VisibilityAggressiveness + 1; //+1 because there is an offset in aggressiveness options
|
|
VisibilitySettings->SetVisibilityWeightsPower(TempAggressiveness);
|
|
VisibilitySettings->SetUseVisibilityWeightsInReducer(InSettings.bVisibilityAided);
|
|
|
|
SimplygonSDK::spNormalCalculationSettings NormalSettings = ReductionProcessor->GetNormalCalculationSettings();
|
|
SetNormalSettings(NormalSettings, InSettings);
|
|
|
|
ReductionProcessor->RunProcessing();
|
|
|
|
SimplygonSDK::spSceneMesh ReducedMesh = SimplygonSDK::Cast<SimplygonSDK::ISceneMesh>(Scene->GetRootNode()->GetChild(0));
|
|
CreateRawMeshFromGeometry(OutReducedMesh, ReducedMesh->GetGeometry(), WINDING_Keep);
|
|
OutMaxDeviation = ReductionProcessor->GetResultDeviation();
|
|
}
|
|
|
|
bool ReduceLODModel(
|
|
FStaticLODModel * SrcModel,
|
|
FStaticLODModel *& OutModel,
|
|
const 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->SetScene(Scene);
|
|
|
|
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->GetResultDeviation();
|
|
|
|
CreateSkeletalLODModelFromGeometry( GeometryData, RefSkeleton, OutModel );
|
|
}
|
|
|
|
return bOptimizeMesh;
|
|
}
|
|
|
|
/** internal only access function, so that you can use with register or with no-register */
|
|
void Reduce(
|
|
USkeletalMesh* SkeletalMesh,
|
|
FSkeletalMeshResource* SkeletalMeshResource,
|
|
FStaticLODModel* SrcModel,
|
|
int32 LODIndex,
|
|
int32 BaseLOD,
|
|
const FSkeletalMeshOptimizationSettings& Settings,
|
|
bool bCalcLODDistance
|
|
)
|
|
{
|
|
// Insert a new LOD model entry if needed.
|
|
if (LODIndex == SkeletalMeshResource->LODModels.Num())
|
|
{
|
|
SkeletalMeshResource->LODModels.Add(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.GetData();
|
|
//delete LODModels[LODIndex]; -- keep model valid until we'll be ready to replace it; required to be able to refresh UI with mesh stats
|
|
|
|
// 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[BaseLOD];
|
|
*NewLODInfo = OldLODInfo;
|
|
|
|
}
|
|
|
|
// 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, AdjacencyIndexBufferData;
|
|
SrcModel->MultiSizeIndexContainer.GetIndexBufferData(IndexBufferData);
|
|
SrcModel->AdjacencyMultiSizeIndexContainer.GetIndexBufferData(AdjacencyIndexBufferData);
|
|
NewSrcModel->RebuildIndexBuffer(&IndexBufferData, &AdjacencyIndexBufferData);
|
|
|
|
// now fix up SrcModel to NewSrcModel
|
|
SrcModel = NewSrcModel;
|
|
//todo: check - memory leak here - SrcModel is not released?
|
|
|
|
// fix up chunks to remove the bones that set to be removed
|
|
for (int32 SectionIndex = 0; SectionIndex < NewSrcModel->Sections.Num(); ++SectionIndex)
|
|
{
|
|
MeshBoneReductionInterface->FixUpSectionBoneMaps(NewSrcModel->Sections[SectionIndex], BonesToRemove);
|
|
}
|
|
}
|
|
|
|
FStaticLODModel * NewModel = new FStaticLODModel();
|
|
LODModels[LODIndex] = NewModel;
|
|
|
|
// Reduce LOD model with SrcMesh
|
|
if (ReduceLODModel(SrcModel, NewModel, SkeletalMesh->GetImportedBounds(), MaxDeviation, SkeletalMesh->RefSkeleton, Settings))
|
|
{
|
|
if (bCalcLODDistance)
|
|
{
|
|
ensure(LODIndex != 0);
|
|
|
|
if (LODIndex == 1)
|
|
{
|
|
SkeletalMesh->LODInfo[LODIndex].ScreenSize = 1.f;
|
|
}
|
|
else
|
|
{
|
|
SkeletalMesh->LODInfo[LODIndex].ScreenSize = SkeletalMesh->LODInfo[LODIndex - 1].ScreenSize * 0.5;
|
|
}
|
|
}
|
|
|
|
// If base lod has a customized LODMaterialMap and this LOD doesn't (could have if changes are applied instead of freshly generated, copy over the data into new new LOD
|
|
if (SkeletalMesh->LODInfo[LODIndex].LODMaterialMap.Num() == 0 && SkeletalMesh->LODInfo[BaseLOD].LODMaterialMap.Num() != 0)
|
|
{
|
|
SkeletalMesh->LODInfo[LODIndex].LODMaterialMap = SkeletalMesh->LODInfo[BaseLOD].LODMaterialMap;
|
|
}
|
|
else
|
|
{
|
|
// Assuming the reducing step has set all material indices correctly, we double check if something went wrong
|
|
// make sure we don't have more materials
|
|
int32 TotalSectionCount = NewModel->Sections.Num();
|
|
if (SkeletalMesh->LODInfo[LODIndex].LODMaterialMap.Num() > TotalSectionCount)
|
|
{
|
|
SkeletalMesh->LODInfo[LODIndex].LODMaterialMap = SkeletalMesh->LODInfo[BaseLOD].LODMaterialMap;
|
|
// Something went wrong during the reduce step during regenerate
|
|
check(SkeletalMesh->LODInfo[BaseLOD].LODMaterialMap.Num() == TotalSectionCount);
|
|
}
|
|
}
|
|
|
|
// 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, AdjacencyIndexBufferData;
|
|
SrcModel->MultiSizeIndexContainer.GetIndexBufferData(IndexBufferData);
|
|
SrcModel->AdjacencyMultiSizeIndexContainer.GetIndexBufferData(AdjacencyIndexBufferData);
|
|
NewModel->RebuildIndexBuffer(&IndexBufferData, &AdjacencyIndexBufferData);
|
|
|
|
// Required bones are recalculated later on.
|
|
NewModel->RequiredBones.Empty();
|
|
SkeletalMesh->LODInfo[LODIndex].bHasBeenSimplified = false;
|
|
}
|
|
|
|
SkeletalMesh->CalculateRequiredBones(SkeletalMeshResource->LODModels[LODIndex], SkeletalMesh->RefSkeleton, &BonesToRemove);
|
|
|
|
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;
|
|
}
|
|
|
|
virtual bool ReduceSkeletalMesh(
|
|
USkeletalMesh* SkeletalMesh,
|
|
int32 LODIndex,
|
|
const FSkeletalMeshOptimizationSettings& Settings,
|
|
bool bCalcLODDistance,
|
|
bool bReregisterComponent = true
|
|
) override
|
|
{
|
|
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();
|
|
|
|
int32 BaseLOD = 0;
|
|
// only allow to set BaseLOD if the LOD is less than this
|
|
if (Settings.BaseLOD> 0)
|
|
{
|
|
if (Settings.BaseLOD < LODIndex && SkeletalMeshResource->LODModels.IsValidIndex(Settings.BaseLOD))
|
|
{
|
|
BaseLOD = Settings.BaseLOD;
|
|
SrcModel = &SkeletalMeshResource->LODModels[BaseLOD];
|
|
}
|
|
else
|
|
{
|
|
// warn users
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Building LOD %d - Invalid Base LOD entered. Using Base LOD 0"), LODIndex);
|
|
}
|
|
}
|
|
|
|
if (bReregisterComponent)
|
|
{
|
|
TComponentReregisterContext<USkinnedMeshComponent> ReregisterContext;
|
|
SkeletalMesh->ReleaseResources();
|
|
SkeletalMesh->ReleaseResourcesFence.Wait();
|
|
|
|
Reduce(SkeletalMesh, SkeletalMeshResource, SrcModel, LODIndex, BaseLOD, Settings, bCalcLODDistance);
|
|
|
|
SkeletalMesh->PostEditChange();
|
|
SkeletalMesh->InitResources();
|
|
}
|
|
else
|
|
{
|
|
Reduce(SkeletalMesh, SkeletalMeshResource, SrcModel, LODIndex, BaseLOD, Settings, bCalcLODDistance);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool IsSupported() const override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void Destroy()
|
|
{
|
|
if (GSimplygonSDKDLLHandle != nullptr)
|
|
{
|
|
typedef int(*DeinitializeSimplygonSDKPtr)();
|
|
DeinitializeSimplygonSDKPtr DeinitializeSimplygonSDK = (DeinitializeSimplygonSDKPtr) FPlatformProcess::GetDllExport(GSimplygonSDKDLLHandle, TEXT("DeinitializeSimplygonSDK"));
|
|
DeinitializeSimplygonSDK();
|
|
FPlatformProcess::FreeDllHandle(GSimplygonSDKDLLHandle);
|
|
GSimplygonSDKDLLHandle = nullptr;
|
|
}
|
|
}
|
|
|
|
static FSimplygonMeshReduction* Create()
|
|
{
|
|
if (FParse::Param(FCommandLine::Get(), TEXT("NoSimplygon")))
|
|
{
|
|
//The user specified that simplygon should not be used
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Simplygon is disabled with -NoSimplygon flag"));
|
|
return NULL;
|
|
}
|
|
//@third party BEGIN SIMPLYGON
|
|
const FString SUPPORTED_SIMPLYGON_VERSION = "8";
|
|
//@third party END SIMPLYGON
|
|
|
|
// Load d3d compiler first
|
|
FString DllPath;
|
|
#if !PLATFORM_64BITS
|
|
DllPath = FPaths::Combine(*FPaths::EngineDir(), TEXT("Binaries/ThirdParty/Windows/DirectX/x86/d3dcompiler_47.dll"));
|
|
#else
|
|
DllPath = FPaths::Combine(*FPaths::EngineDir(), TEXT("Binaries/ThirdParty/Windows/DirectX/x64/d3dcompiler_47.dll"));
|
|
#endif
|
|
if (!FPaths::FileExists(DllPath))
|
|
{
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Could not find d3dcompiler_47 DLL, which is required for loading Simplygon."));
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
void* D3DHandle = FPlatformProcess::GetDllHandle(*DllPath);
|
|
}
|
|
|
|
|
|
DllPath = (FPaths::Combine(*FPaths::EngineDir(), TEXT("Binaries/ThirdParty/NotForLicensees/Simplygon")));
|
|
FString DllFilename(FPaths::Combine(*DllPath, TEXT("SimplygonSDKRuntimeReleasex64.dll")));
|
|
|
|
// If the DLL just doesn't exist, fail gracefully. Licensees and Subscribers will not necessarily have Simplygon.
|
|
if( !FPaths::FileExists(DllFilename) )
|
|
{
|
|
UE_LOG(LogSimplygon,Log,TEXT("Simplygon DLL not present - disabling."));
|
|
return NULL;
|
|
}
|
|
|
|
// Otherwise fail
|
|
GSimplygonSDKDLLHandle = FPlatformProcess::GetDllHandle(*DllFilename);
|
|
if (GSimplygonSDKDLLHandle == 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( GSimplygonSDKDLLHandle, TEXT( "GetInterfaceVersionSimplygonSDK" ) );
|
|
|
|
typedef int (*InitializeSimplygonSDKPtr)(const char* LicenseData , SimplygonSDK::ISimplygonSDK** OutInterfacePtr);
|
|
InitializeSimplygonSDKPtr InitializeSimplygonSDK = (InitializeSimplygonSDKPtr)FPlatformProcess::GetDllExport( GSimplygonSDKDLLHandle, 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( GSimplygonSDKDLLHandle );
|
|
GSimplygonSDKDLLHandle = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
//@third party BEGIN SIMPLYGON
|
|
//NOTE: Only major version check is required. This removes the dependency to update each time the SDK is updated to a minor release with bug fixes
|
|
//TODO : Tokenize the version string. This would allow for better granularity when a tight coupling to the dll is required. (i.e Custom Dll Version)
|
|
FString MajorVersion, MinorAndBuildRevision;
|
|
FString SimplygonHeaderVersioString = ANSI_TO_TCHAR(SimplygonSDK::GetHeaderVersion());
|
|
SimplygonHeaderVersioString.Split(TEXT("."), &MajorVersion, &MinorAndBuildRevision);
|
|
|
|
if (SUPPORTED_SIMPLYGON_VERSION.Compare(MajorVersion) != 0)
|
|
{
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Simplygon version doesn't match the version expected by the Simplygon UE4 integration"));
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Min version %s, found version %s"), *SUPPORTED_SIMPLYGON_VERSION, ANSI_TO_TCHAR(SimplygonSDK::GetHeaderVersion()));
|
|
FPlatformProcess::FreeDllHandle(GSimplygonSDKDLLHandle);
|
|
GSimplygonSDKDLLHandle = NULL;
|
|
return NULL;
|
|
}
|
|
//@third party END SIMPLYGON
|
|
|
|
|
|
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));
|
|
FPlatformProcess::FreeDllHandle(GSimplygonSDKDLLHandle);
|
|
GSimplygonSDKDLLHandle = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
// Workaround for the fact Simplygon stomps memory
|
|
class FSimplygonLicenseData
|
|
{
|
|
uint8* LicenseDataMem;
|
|
int32 RealDataOffset;
|
|
public:
|
|
FSimplygonLicenseData(const TCHAR* InDllPath)
|
|
: LicenseDataMem(nullptr)
|
|
, RealDataOffset(0)
|
|
{
|
|
TArray<uint8> LicenseFileContents;
|
|
if (FFileHelper::LoadFileToArray(LicenseFileContents, *FPaths::Combine(InDllPath, TEXT("Simplygon_8_license.dat")), FILEREAD_Silent))
|
|
{
|
|
if (LicenseFileContents.Num() > 0)
|
|
{
|
|
// Allocate with a big slack at the beginning and end to workaround the fact Simplygon stomps memory
|
|
const int32 LicenseDataSizeWithSlack = LicenseFileContents.Num() * 3 * sizeof(uint8);
|
|
LicenseDataMem = (uint8*)FMemory::Malloc(LicenseDataSizeWithSlack);
|
|
FMemory::Memzero(LicenseDataMem, LicenseDataSizeWithSlack);
|
|
// Copy data to somewhere in the middle of allocated memory
|
|
RealDataOffset = LicenseFileContents.Num();
|
|
FMemory::Memcpy(LicenseDataMem + RealDataOffset, LicenseFileContents.GetData(), LicenseFileContents.Num() * sizeof(uint8));
|
|
}
|
|
}
|
|
}
|
|
~FSimplygonLicenseData()
|
|
{
|
|
FMemory::Free(LicenseDataMem);
|
|
}
|
|
const char* GetLicenseData() const
|
|
{
|
|
return (const char*)(LicenseDataMem + RealDataOffset);
|
|
}
|
|
bool IsValid() const
|
|
{
|
|
return !!LicenseDataMem;
|
|
}
|
|
} LicenseDataContainer(*DllPath);
|
|
|
|
FSimplygonMeshReduction* Result = nullptr;
|
|
if (LicenseDataContainer.IsValid())
|
|
{
|
|
SimplygonSDK::ISimplygonSDK* SDK = NULL;
|
|
int32 InitResult = InitializeSimplygonSDK(LicenseDataContainer.GetLicenseData(), &SDK);
|
|
|
|
if (InitResult != SimplygonSDK::SG_ERROR_NOERROR && InitResult != SimplygonSDK::SG_ERROR_ALREADYINITIALIZED)
|
|
{
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Failed to initialize Simplygon. Error: %d."), Result);
|
|
FPlatformProcess::FreeDllHandle(GSimplygonSDKDLLHandle);
|
|
GSimplygonSDKDLLHandle = nullptr;
|
|
}
|
|
else
|
|
{
|
|
Result = new FSimplygonMeshReduction(SDK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Failed to load Simplygon license file."));
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
struct FMaterialCastingProperties
|
|
{
|
|
bool bCastMaterials;
|
|
bool bCastNormals;
|
|
bool bCastMetallic;
|
|
bool bCastRoughness;
|
|
bool bCastSpecular;
|
|
|
|
FMaterialCastingProperties()
|
|
: bCastMaterials(false)
|
|
, bCastNormals(false)
|
|
, bCastMetallic(false)
|
|
, bCastRoughness(false)
|
|
, bCastSpecular(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
// Processor is spRemeshingProcessor or spAggregationProcessor
|
|
template <typename ProcessorClass>
|
|
void SimplygonProcessLOD(
|
|
ProcessorClass Processor,
|
|
const TArray<FMeshMergeData>& DataArray,
|
|
const TArray<FFlattenMaterial>& FlattenedMaterials,
|
|
const FSimplygonMaterialLODSettings& MaterialLODSettings,
|
|
FFlattenMaterial& OutMaterial)
|
|
{
|
|
if (!MaterialLODSettings.bActive)
|
|
{
|
|
UE_LOG(LogSimplygon, Log, TEXT("Processing with %s."), *FString(Processor->GetClass()));
|
|
Processor->RunProcessing();
|
|
UE_LOG(LogSimplygon, Log, TEXT("Processing done."));
|
|
return;
|
|
}
|
|
|
|
// Setup the mapping image used for casting
|
|
SetupMappingImage(
|
|
MaterialLODSettings,
|
|
Processor->GetMappingImageSettings(),
|
|
/*InAggregateProcess = */ TAreTypesEqual<ProcessorClass, SimplygonSDK::spAggregationProcessor>::Value,
|
|
/*InRemoveUVs = */ true);
|
|
|
|
// Convert FFlattenMaterial array to Simplygon materials
|
|
SimplygonSDK::spMaterialTable InputMaterialTable = SDK->CreateMaterialTable();
|
|
//@third party BEGIN SIMPLYGON
|
|
SimplygonSDK::spTextureTable InputTextureTable = Processor->GetScene()->GetTextureTable();
|
|
CreateSGMaterialFromFlattenMaterial(FlattenedMaterials, MaterialLODSettings, InputMaterialTable, InputTextureTable, true);
|
|
//@third party END SIMPLYGON
|
|
|
|
|
|
// Perform LOD processing
|
|
UE_LOG(LogSimplygon, Log, TEXT("Processing with %s."), *FString(Processor->GetClass()));
|
|
double ProcessingTime = 0.0;
|
|
{
|
|
FScopedDurationTimer ProcessingTimer(ProcessingTime);
|
|
Processor->RunProcessing();
|
|
}
|
|
UE_LOG(LogSimplygon, Log, TEXT("Processing done in %.2f s."), ProcessingTime);
|
|
|
|
// Cast input materials to output materials and convert to FFlattenMaterial
|
|
UE_LOG(LogSimplygon, Log, TEXT("Casting materials."));
|
|
SimplygonSDK::spMappingImage MappingImage = Processor->GetMappingImage();
|
|
//@third party BEGIN SIMPLYGON
|
|
SimplygonSDK::spMaterial SgMaterial = RebakeMaterials(MaterialLODSettings, MappingImage, InputMaterialTable, InputTextureTable);
|
|
CreateFlattenMaterialFromSGMaterial(SgMaterial, InputTextureTable, OutMaterial);
|
|
//@third party END SIMPLYGON
|
|
|
|
// Fill flatten material samples alpha values with 255 (to prevent the data from being optimized away)
|
|
OutMaterial.FillAlphaValues(255);
|
|
|
|
if (FlattenedMaterials.Num())
|
|
{
|
|
OutMaterial.BlendMode = FlattenedMaterials[0].BlendMode;
|
|
OutMaterial.bTwoSided = FlattenedMaterials[0].bTwoSided;
|
|
OutMaterial.bDitheredLODTransition = FlattenedMaterials[0].bDitheredLODTransition;
|
|
OutMaterial.EmissiveScale = FlattenedMaterials[0].EmissiveScale;
|
|
}
|
|
|
|
UE_LOG(LogSimplygon, Log, TEXT("Casting done."));
|
|
}
|
|
|
|
|
|
class FProxyLODTask : FRunnable
|
|
{
|
|
public:
|
|
FProxyLODTask(const TArray<struct FMeshMergeData>& InData,
|
|
const struct FMeshProxySettings& InProxySettings,
|
|
const TArray<struct FFlattenMaterial>& InputMaterials,
|
|
const FGuid InJobGUID, FProxyCompleteDelegate& InDelegate,
|
|
SimplygonSDK::ISimplygonSDK* InSDK, FSimplygonMeshReduction* InReduction)
|
|
: Data(InData)
|
|
, ProxySettings(InProxySettings)
|
|
, Materials(InputMaterials)
|
|
, JobGUID(InJobGUID)
|
|
, Delegate(InDelegate)
|
|
, SDK(InSDK)
|
|
, Reduction(InReduction)
|
|
{
|
|
}
|
|
|
|
virtual bool Init()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
virtual uint32 Run()
|
|
{
|
|
FRawMesh OutProxyMesh;
|
|
FFlattenMaterial OutMaterial;
|
|
|
|
if (!Data.Num())
|
|
{
|
|
UE_LOG(LogSimplygon, Log, TEXT("The selected meshes are not valid. Make sure to select static meshes only."));
|
|
OutProxyMesh.Empty();
|
|
return 1;
|
|
}
|
|
|
|
//Create a Simplygon Scene
|
|
SimplygonSDK::spScene Scene = SDK->CreateScene();
|
|
|
|
SimplygonSDK::spGeometryValidator GeometryValidator = SDK->CreateGeometryValidator();
|
|
TArray<FBox2D> GlobalTexcoordBounds;
|
|
|
|
|
|
for (int32 MeshIndex = 0; MeshIndex < Data.Num(); ++MeshIndex)
|
|
{
|
|
GlobalTexcoordBounds.Append(Data[MeshIndex].TexCoordBounds);
|
|
}
|
|
|
|
//For each raw mesh in array create a scene mesh and populate with geometry data
|
|
for (int32 MeshIndex = 0; MeshIndex < Data.Num(); ++MeshIndex)
|
|
{
|
|
SimplygonSDK::spGeometryData GeometryData = Reduction->CreateGeometryFromRawMesh(*Data[MeshIndex].RawMesh, Data[MeshIndex].TexCoordBounds, Data[MeshIndex].NewUVs);
|
|
if (!GeometryData)
|
|
{
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Geometry data is NULL"));
|
|
continue;
|
|
}
|
|
|
|
GeometryData->CleanupNanValues();
|
|
|
|
//Validate the geometry
|
|
Reduction->ValidateGeometry(GeometryValidator, GeometryData);
|
|
|
|
check(GeometryData)
|
|
|
|
#ifdef DEBUG_PROXY_MESH
|
|
SimplygonSDK::spWavefrontExporter objexp = SDK->CreateWavefrontExporter();
|
|
objexp->SetExportFilePath("d:/BeforeProxyMesh.obj");
|
|
objexp->SetSingleGeometry(GeometryData);
|
|
objexp->RunExport();
|
|
#endif
|
|
|
|
SimplygonSDK::spSceneMesh Mesh = SDK->CreateSceneMesh();
|
|
Mesh->SetGeometry(GeometryData);
|
|
Mesh->SetName(TCHAR_TO_ANSI(*FString::Printf(TEXT("UnrealMesh%d"), MeshIndex)));
|
|
Scene->GetRootNode()->AddChild(Mesh);
|
|
|
|
}
|
|
|
|
//Create a remesher
|
|
SimplygonSDK::spRemeshingProcessor RemeshingProcessor = SDK->CreateRemeshingProcessor();
|
|
|
|
//Setup the remesher
|
|
// TODO add more settings back in
|
|
RemeshingProcessor->GetRemeshingSettings()->SetOnScreenSize(ProxySettings.ScreenSize);
|
|
RemeshingProcessor->GetRemeshingSettings()->SetMergeDistance(ProxySettings.MergeDistance);
|
|
RemeshingProcessor->SetScene(Scene);
|
|
|
|
FSimplygonMaterialLODSettings MaterialLODSettings(ProxySettings.MaterialSettings);
|
|
MaterialLODSettings.bActive = true;
|
|
|
|
// Process data
|
|
Reduction->SimplygonProcessLOD<SimplygonSDK::spRemeshingProcessor>(RemeshingProcessor, Data, Materials, MaterialLODSettings, 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();
|
|
Reduction->CreateRawMeshFromGeometry(OutProxyMesh, ProxyMesh->GetGeometry(), WINDING_Keep);
|
|
|
|
// Default smoothing
|
|
OutProxyMesh.FaceSmoothingMasks.SetNum(OutProxyMesh.FaceMaterialIndices.Num());
|
|
for (uint32& SmoothingMask : OutProxyMesh.FaceSmoothingMasks)
|
|
{
|
|
SmoothingMask = 1;
|
|
}
|
|
|
|
Delegate.ExecuteIfBound(OutProxyMesh, OutMaterial, JobGUID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
virtual void Stop()
|
|
{
|
|
|
|
}
|
|
|
|
void StartJobAsync()
|
|
{
|
|
Thread = FRunnableThread::Create(this, TEXT("SimplygonMeshReductionTask"), 0, TPri_BelowNormal);
|
|
}
|
|
|
|
bool IsRunning() const
|
|
{
|
|
return (Thread != nullptr);
|
|
}
|
|
void Wait()
|
|
{
|
|
Thread->WaitForCompletion();
|
|
}
|
|
private:
|
|
FRunnableThread* Thread;
|
|
TArray<FMeshMergeData> Data;
|
|
struct FMeshProxySettings ProxySettings;
|
|
TArray<FFlattenMaterial> Materials;
|
|
FGuid JobGUID;
|
|
FProxyCompleteDelegate Delegate;
|
|
SimplygonSDK::ISimplygonSDK* SDK;
|
|
FSimplygonMeshReduction* Reduction;
|
|
};
|
|
|
|
virtual void ProxyLOD(const TArray<FMeshMergeData>& InData,
|
|
const struct FMeshProxySettings& InProxySettings,
|
|
const TArray<FFlattenMaterial>& InputMaterials,
|
|
const FGuid InJobGUID) override
|
|
{
|
|
FScopedSlowTask SlowTask(100.f, (LOCTEXT("SimplygonProxyLOD_ProxyLOD", "Generating Proxy Mesh using Simplygon")));
|
|
SlowTask.MakeDialog();
|
|
|
|
FRawMesh OutProxyMesh;
|
|
FFlattenMaterial OutMaterial;
|
|
|
|
if (!InData.Num())
|
|
{
|
|
UE_LOG(LogSimplygon, Log, TEXT("The selected meshes are not valid. Make sure to select static meshes only."));
|
|
OutProxyMesh.Empty();
|
|
return;
|
|
}
|
|
|
|
//Create a Simplygon Scene
|
|
SimplygonSDK::spScene Scene = SDK->CreateScene();
|
|
|
|
SimplygonSDK::spGeometryValidator GeometryValidator = SDK->CreateGeometryValidator();
|
|
TArray<FBox2D> GlobalTexcoordBounds;
|
|
|
|
for (int32 MeshIndex = 0; MeshIndex < InData.Num(); ++MeshIndex)
|
|
{
|
|
GlobalTexcoordBounds.Append(InData[MeshIndex].TexCoordBounds);
|
|
}
|
|
|
|
// Create Selection Sets for tagging geometry for processing and clipping geometry
|
|
SimplygonSDK::spSelectionSet processingSet = SDK->CreateSelectionSet();
|
|
processingSet->SetName("RemeshingProcessingSet");
|
|
SimplygonSDK::spSelectionSet clippingGeometrySet = SDK->CreateSelectionSet();
|
|
clippingGeometrySet->SetName("ClippingObjectSet");
|
|
|
|
//For each raw mesh in array create a scene mesh and populate with geometry data
|
|
for (int32 MeshIndex = 0; MeshIndex < InData.Num(); ++MeshIndex)
|
|
{
|
|
SimplygonSDK::spGeometryData GeometryData = CreateGeometryFromRawMesh(*InData[MeshIndex].RawMesh, InData[MeshIndex].TexCoordBounds, InData[MeshIndex].NewUVs);
|
|
if (!GeometryData)
|
|
{
|
|
FailedDelegate.ExecuteIfBound(InJobGUID, TEXT("Simplygon failed to generate Geometry Data"));
|
|
continue;
|
|
}
|
|
|
|
GeometryData->CleanupNanValues();
|
|
|
|
//Validate the geometry
|
|
ValidateGeometry(GeometryValidator, GeometryData);
|
|
|
|
#ifdef DEBUG_PROXY_MESH
|
|
SimplygonSDK::spWavefrontExporter objexp = SDK->CreateWavefrontExporter();
|
|
objexp->SetExportFilePath("d:/BeforeProxyMesh.obj");
|
|
objexp->SetSingleGeometry(GeometryData);
|
|
objexp->RunExport();
|
|
#endif
|
|
SimplygonSDK::spSceneMesh Mesh = SDK->CreateSceneMesh();
|
|
Mesh->SetGeometry(GeometryData);
|
|
Mesh->SetName(TCHAR_TO_ANSI(*FString::Printf(TEXT("UnrealMesh%d"), MeshIndex)));
|
|
Scene->GetRootNode()->AddChild(Mesh);
|
|
|
|
// if mesh is clipping geometry add to clipping set else processing set
|
|
if (!InData[MeshIndex].bIsClippingMesh)
|
|
{
|
|
processingSet->AddItem(Mesh->GetNodeGUID());
|
|
}
|
|
else if (InData[MeshIndex].bIsClippingMesh)
|
|
{
|
|
clippingGeometrySet->AddItem(Mesh->GetNodeGUID());
|
|
}
|
|
}
|
|
|
|
//add the sets to the scene
|
|
Scene->GetSelectionSetTable()->AddItem(processingSet);
|
|
Scene->GetSelectionSetTable()->AddItem(clippingGeometrySet);
|
|
|
|
//Create a remesher
|
|
SimplygonSDK::spRemeshingProcessor RemeshingProcessor = SDK->CreateRemeshingProcessor();
|
|
RemeshingProcessor->Clear();
|
|
|
|
//Setup the remesher
|
|
EventHandler.Task = &SlowTask;
|
|
RemeshingProcessor->AddObserver(&EventHandler, SimplygonSDK::SG_EVENT_PROGRESS);
|
|
RemeshingProcessor->GetRemeshingSettings()->SetOnScreenSize(InProxySettings.ScreenSize);
|
|
RemeshingProcessor->GetRemeshingSettings()->SetMergeDistance(InProxySettings.MergeDistance);
|
|
|
|
// Setup sets for the remeshing processor
|
|
bool bUseClippingGeometry = clippingGeometrySet->GetItemCount() > 0 ? true : false;
|
|
|
|
RemeshingProcessor->GetRemeshingSettings()->SetUseClippingGeometry(bUseClippingGeometry);
|
|
RemeshingProcessor->GetRemeshingSettings()->SetProcessSelectionSetName("RemeshingProcessingSet");
|
|
|
|
if (bUseClippingGeometry)
|
|
{
|
|
RemeshingProcessor->GetRemeshingSettings()->SetClippingGeometrySelectionSetName("ClippingObjectSet");
|
|
RemeshingProcessor->GetRemeshingSettings()->SetUseClippingGeometryEmptySpaceOverride(false);
|
|
}
|
|
|
|
RemeshingProcessor->SetScene(Scene);
|
|
|
|
FSimplygonMaterialLODSettings MaterialLODSettings(InProxySettings.MaterialSettings);
|
|
MaterialLODSettings.bActive = true;
|
|
|
|
// Process data
|
|
SimplygonProcessLOD<SimplygonSDK::spRemeshingProcessor>(RemeshingProcessor, InData, InputMaterials, MaterialLODSettings, OutMaterial);
|
|
|
|
//Collect the proxy mesh
|
|
const uint32 ChildNodeCount = Scene->GetRootNode()->GetChildCount();
|
|
|
|
SimplygonSDK::spSceneMesh ProxyMesh = nullptr;
|
|
for (uint32 ChildNodeIndex = 0; ChildNodeIndex < ChildNodeCount; ++ChildNodeIndex)
|
|
{
|
|
auto ChildNode = Scene->GetRootNode()->GetChild(ChildNodeIndex);
|
|
SimplygonSDK::spSceneMesh Mesh = SimplygonSDK::SafeCast<SimplygonSDK::ISceneMesh>(ChildNode);
|
|
if (Mesh != NULL)
|
|
{
|
|
ProxyMesh = Mesh;
|
|
}
|
|
}
|
|
|
|
if (ProxyMesh == nullptr)
|
|
{
|
|
FailedDelegate.ExecuteIfBound(InJobGUID, TEXT("Simplygon failed to generate a proxy mesh"));
|
|
}
|
|
else
|
|
{
|
|
#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);
|
|
|
|
// Default smoothing
|
|
OutProxyMesh.FaceSmoothingMasks.SetNum(OutProxyMesh.FaceMaterialIndices.Num());
|
|
for (uint32& SmoothingMask : OutProxyMesh.FaceSmoothingMasks)
|
|
{
|
|
SmoothingMask = 1;
|
|
}
|
|
|
|
CompleteDelegate.ExecuteIfBound(OutProxyMesh, OutMaterial, InJobGUID);
|
|
}
|
|
|
|
EventHandler.Task = nullptr;
|
|
}
|
|
|
|
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);
|
|
SDK->SetGlobalSetting("AllowDirectX", true);
|
|
|
|
const TCHAR* LibraryVersion = ANSI_TO_TCHAR(InSDK->GetVersion());
|
|
const TCHAR* UnrealVersionGuid = TEXT("18f808c3cf724e5a994f57de5c83cc4b");
|
|
VersionString = FString::Printf(TEXT("%s.%s_%s"), LibraryVersion, SG_UE_INTEGRATION_REV,UnrealVersionGuid);
|
|
UE_LOG(LogSimplygon, Display, TEXT("Initialized with Simplygon %s"), *VersionString);
|
|
}
|
|
|
|
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 = GetConversionMatrix().TransformPosition(TempPos);
|
|
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 = GetConversionMatrix().TransformPosition(TempTangent);
|
|
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 = GetConversionMatrix().TransformPosition(TempBitangent);
|
|
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 = GetConversionMatrix().TransformPosition(TempNormal);
|
|
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;
|
|
|
|
}
|
|
|
|
// This is a copy of CreateGeometryFromRawMesh with additional features for material LOD.
|
|
SimplygonSDK::spGeometryData CreateGeometryFromRawMesh(const FRawMesh& RawMesh, const TArray<FBox2D> & TextureBounds, const TArray<FVector2D>& InTexCoords)
|
|
{
|
|
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 = GetConversionMatrix().TransformPosition(TempPos);
|
|
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)
|
|
{
|
|
const TArray<FVector2D>& SrcTexCoords = (TexCoordIndex == 0 && InTexCoords.Num() == NumWedges) ? InTexCoords : RawMesh.WedgeTexCoords[TexCoordIndex];
|
|
if (SrcTexCoords.Num() == NumWedges)
|
|
{
|
|
GeometryData->AddTexCoords(TexCoordIndex);
|
|
SimplygonSDK::spRealArray TexCoords = GeometryData->GetTexCoords(TexCoordIndex);
|
|
check(TexCoords->GetTupleSize() == 2);
|
|
int32 WedgeIndex = 0;
|
|
for (int32 TriIndex = 0; TriIndex < NumTris; ++TriIndex)
|
|
{
|
|
int32 MaterialIndex = RawMesh.FaceMaterialIndices[TriIndex];
|
|
// Compute texture bounds for current material.
|
|
float MinU = 0, ScaleU = 1;
|
|
float MinV = 0, ScaleV = 1;
|
|
|
|
if (TextureBounds.IsValidIndex(MaterialIndex) && TexCoordIndex == 0 && InTexCoords.Num() == 0)
|
|
{
|
|
const FBox2D& Bounds = TextureBounds[MaterialIndex];
|
|
if (Bounds.GetArea() > 0)
|
|
{
|
|
MinU = Bounds.Min.X;
|
|
MinV = Bounds.Min.Y;
|
|
ScaleU = 1.0f / (Bounds.Max.X - Bounds.Min.X);
|
|
ScaleV = 1.0f / (Bounds.Max.Y - Bounds.Min.Y);
|
|
}
|
|
}
|
|
for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex, ++WedgeIndex)
|
|
{
|
|
const FVector2D& TexCoord = SrcTexCoords[WedgeIndex];
|
|
float UV[2];
|
|
UV[0] = (TexCoord.X - MinU) * ScaleU;
|
|
UV[1] = (TexCoord.Y - MinV) * ScaleV;
|
|
TexCoords->SetTuple(WedgeIndex, UV);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = GetConversionMatrix().TransformPosition(TempTangent);
|
|
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 = GetConversionMatrix().TransformPosition(TempBitangent);
|
|
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 = GetConversionMatrix().TransformPosition(TempNormal);
|
|
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);
|
|
SimplygonSDK::spRealData sgTuple = SDK->CreateRealData();
|
|
for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex)
|
|
{
|
|
//Positions->GetTuple(VertexIndex, (float*)&RawMesh.VertexPositions[VertexIndex]);
|
|
Positions->GetTuple(VertexIndex, sgTuple);
|
|
SimplygonSDK::real* vertexPos = sgTuple->GetData();
|
|
RawMesh.VertexPositions[VertexIndex] = GetConversionMatrix().TransformPosition(FVector(vertexPos[0], vertexPos[1], vertexPos[2]));
|
|
}
|
|
|
|
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]);
|
|
TexCoords->GetTuple(TriIndex * 3 + CornerIndex, sgTuple);
|
|
|
|
SimplygonSDK::real* sgTextCoors = sgTuple->GetData();
|
|
RawMesh.WedgeTexCoords[TexCoordIndex][TriIndex * 3 + DestIndex] = FVector2D(sgTextCoors[0], sgTextCoors[1]);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RawMesh.WedgeColors.Empty(NumWedges);
|
|
RawMesh.WedgeColors.AddUninitialized(NumWedges);
|
|
if (LinearColors)
|
|
{
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
//LinearColors->GetTuple(WedgeIndex, (float*)&LinearColor);
|
|
LinearColors->GetTuple(WedgeIndex, sgTuple);
|
|
SimplygonSDK::real* sgVertexColor = sgTuple->GetData();
|
|
FLinearColor LinearColor(sgVertexColor[0], sgVertexColor[1], sgVertexColor[2], sgVertexColor[3]);
|
|
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]);
|
|
Tangents->GetTuple(WedgeIndex, sgTuple);
|
|
SimplygonSDK::real* sgTangents = sgTuple->GetData();
|
|
RawMesh.WedgeTangentX[WedgeIndex] = GetConversionMatrix().TransformPosition( FVector(sgTangents[0], sgTangents[1], sgTangents[2]) );
|
|
}
|
|
|
|
RawMesh.WedgeTangentY.Empty(NumWedges);
|
|
RawMesh.WedgeTangentY.AddUninitialized(NumWedges);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
//Bitangents->GetTuple(WedgeIndex, (float*)&RawMesh.WedgeTangentY[WedgeIndex]);
|
|
Bitangents->GetTuple(WedgeIndex, sgTuple);
|
|
SimplygonSDK::real* sgBitangents = sgTuple->GetData();
|
|
RawMesh.WedgeTangentY[WedgeIndex] = GetConversionMatrix().TransformPosition(FVector(sgBitangents[0], sgBitangents[1], sgBitangents[2]));
|
|
}
|
|
}
|
|
|
|
RawMesh.WedgeTangentZ.Empty(NumWedges);
|
|
RawMesh.WedgeTangentZ.AddUninitialized(NumWedges);
|
|
for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex)
|
|
{
|
|
//Normals->GetTuple(WedgeIndex, (float*)&RawMesh.WedgeTangentZ[WedgeIndex]);
|
|
Normals->GetTuple(WedgeIndex, sgTuple);
|
|
SimplygonSDK::real* sgNormal = sgTuple->GetData();
|
|
RawMesh.WedgeTangentZ[WedgeIndex] = GetConversionMatrix().TransformPosition(FVector(sgNormal[0], sgNormal[1], sgNormal[2]));
|
|
}
|
|
}
|
|
|
|
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 : SimplygonSDK::REAL_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);
|
|
|
|
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(?)
|
|
check(Settings.VertexColorImportance < EMeshFeatureImportance::Highest + 1); // -1 because of TEMP_BROKEN(?)
|
|
|
|
ReductionSettings->SetStopCondition(SimplygonSDK::SG_STOPCONDITION_ANY);
|
|
ReductionSettings->SetReductionTargets(SimplygonSDK::SG_REDUCTIONTARGET_TRIANGLERATIO | SimplygonSDK::SG_REDUCTIONTARGET_MAXDEVIATION);
|
|
ReductionSettings->SetMaxDeviation(MaxDeviation);
|
|
ReductionSettings->SetTriangleRatio(ReductionRatio);
|
|
ReductionSettings->SetGeometryImportance(ImportanceTable[Settings.SilhouetteImportance]);
|
|
ReductionSettings->SetTextureImportance(ImportanceTable[Settings.TextureImportance]);
|
|
ReductionSettings->SetMaterialImportance(ImportanceTable[Settings.TextureImportance]);
|
|
ReductionSettings->SetShadingImportance( ImportanceTable[Settings.ShadingImportance]);
|
|
ReductionSettings->SetVertexColorImportance(ImportanceTable[Settings.VertexColorImportance]);
|
|
////ReductionSettings->SetAllowDirectX(true);
|
|
|
|
//Automatic Symmetry Detection
|
|
ReductionSettings->SetKeepSymmetry(Settings.bKeepSymmetry);
|
|
ReductionSettings->SetUseAutomaticSymmetryDetection(Settings.bKeepSymmetry);
|
|
|
|
//Set reposition vertices to be enabled by default
|
|
ReductionSettings->SetDataCreationPreferences(2); //2 = reposition vertices enabled
|
|
ReductionSettings->SetGenerateGeomorphData(true);
|
|
}
|
|
|
|
void SetNormalSettings(SimplygonSDK::spNormalCalculationSettings NormalSettings, const FMeshReductionSettings& Settings)
|
|
{
|
|
NormalSettings->SetReplaceNormals(Settings.bRecalculateNormals);
|
|
NormalSettings->SetScaleByArea(false);
|
|
NormalSettings->SetScaleByAngle(false);
|
|
NormalSettings->SetHardEdgeAngleInRadians( FMath::DegreesToRadians(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.GetRawBoneNum(); ++BoneIndex)
|
|
{
|
|
SimplygonSDK::spSceneBone SceneBone = SDK->CreateSceneBone();
|
|
BoneArray.Add(SceneBone);
|
|
}
|
|
|
|
SimplygonSDK::spSceneBoneTable BoneTable = InScene->GetBoneTable();
|
|
for(int32 BoneIndex=0; BoneIndex < InSkeleton.GetRawBoneNum(); ++BoneIndex)
|
|
{
|
|
int32 ParentIndex = InSkeleton.GetParentIndex(BoneIndex);
|
|
SimplygonSDK::spSceneBone CurrentBone = BoneArray[BoneIndex];
|
|
|
|
/*if(InBoneTree[BoneIndex].bLockBone)
|
|
{
|
|
CurrentBone->SetLockFromBoneLOD(true);
|
|
}
|
|
|
|
if(InBoneTree[BoneIndex].bRemoveBone)
|
|
{
|
|
CurrentBone->SetForceBoneRemoval(true);
|
|
}*/
|
|
|
|
|
|
//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-section so that we can properly map bones.
|
|
uint32 FirstVertex = 0;
|
|
for ( uint32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex )
|
|
{
|
|
const FSkelMeshSection& Section = LODModel.Sections[SectionIndex];
|
|
const uint32 LastVertex = FirstVertex + (uint32)Section.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 < Section.BoneMap.Num() );
|
|
uint32 BoneID = BoneIDs[Section.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 );
|
|
|
|
Vertex.Position = GetConversionMatrix().TransformPosition(Vertex.Position);
|
|
|
|
//Vertex.Position.Z = -Vertex.Position.Z;
|
|
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;
|
|
Normal = GetConversionMatrix().TransformPosition(Normal);
|
|
|
|
FVector Tangent = Vertex.TangentX;
|
|
Tangent = GetConversionMatrix().TransformPosition(Tangent);
|
|
|
|
FVector Bitangent = Vertex.TangentY;
|
|
Bitangent = GetConversionMatrix().TransformPosition(Bitangent);
|
|
|
|
|
|
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 );
|
|
|
|
SimplygonSDK::spRealData sgPositionData = SDK->CreateRealData();
|
|
SimplygonSDK::spRidData sgBoneIdsData = SDK->CreateRidData();
|
|
SimplygonSDK::spRealData sgBoneWeightsData = SDK->CreateRealData();
|
|
SimplygonSDK::spRealData sgTangentData = SDK->CreateRealData();
|
|
SimplygonSDK::spRealData sgTexCoordData = SDK->CreateRealData();
|
|
SimplygonSDK::spUnsignedCharData sgVertexColorData = SDK->CreateUnsignedCharData();
|
|
|
|
// 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 );
|
|
Positions->GetTuple( VertexIndex, sgPositionData );
|
|
SimplygonSDK::real* sgPosition = sgPositionData->GetData();
|
|
Point = FVector(sgPosition[0], sgPosition[1], sgPosition[2]);
|
|
|
|
|
|
//BoneIds->GetTuple( VertexIndex, VertexBoneIds );
|
|
BoneIds->GetTuple(VertexIndex, sgBoneIdsData);
|
|
SimplygonSDK::rid* sgBoneId = sgBoneIdsData->GetData();
|
|
|
|
|
|
//BoneWeights->GetTuple( VertexIndex, VertexBoneWeights );
|
|
BoneWeights->GetTuple(VertexIndex, sgBoneWeightsData);
|
|
SimplygonSDK::real* sgBoneWeights = sgBoneWeightsData->GetData();
|
|
|
|
PointInfluenceMap[ VertexIndex ] = (uint32)MeshData.Influences.Num();
|
|
for ( uint32 InfluenceIndex = 0; InfluenceIndex < NumOfInfluences; ++InfluenceIndex )
|
|
{
|
|
const uint16 BoneIndex = sgBoneId[InfluenceIndex];
|
|
const float BoneWeight = sgBoneWeights[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 ? MaterialIndices->GetItem( TriIndex ) : 0;
|
|
|
|
|
|
// 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 );
|
|
Normals->GetTuple(WedgeIndex, sgTangentData);
|
|
SimplygonSDK::real* sgNormal = sgTangentData->GetData();
|
|
WedgeNormal = FVector(sgNormal[0], sgNormal[1], sgNormal[2]);
|
|
WedgeNormal.Normalize();
|
|
|
|
FVector WedgeTangent, WedgeBitangent;
|
|
//Tangents->GetTuple( WedgeIndex, (float*)&WedgeTangent );
|
|
Tangents->GetTuple(WedgeIndex, sgTangentData);
|
|
SimplygonSDK::real* sgTangent = sgTangentData->GetData();
|
|
WedgeTangent = FVector(sgTangent[0], sgTangent[1], sgTangent[2]);
|
|
|
|
//Bitangents->GetTuple( WedgeIndex, (float*)&WedgeBitangent );
|
|
Bitangents->GetTuple(WedgeIndex, sgTangentData);
|
|
SimplygonSDK::real* sgBitangent = sgTangentData->GetData();
|
|
WedgeBitangent = FVector(sgBitangent[0], sgBitangent[1], sgBitangent[2]);
|
|
|
|
Face.TangentX[CornerIndex] = WedgeTangent;
|
|
Face.TangentY[CornerIndex] = WedgeBitangent;
|
|
Face.TangentZ[CornerIndex] = WedgeNormal;
|
|
|
|
|
|
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] );
|
|
TexCoords[TexCoordIndex]->GetTuple(WedgeIndex, sgTexCoordData);
|
|
SimplygonSDK::real* sgTexCoord = sgTexCoordData->GetData();
|
|
Wedge.UVs[TexCoordIndex] = FVector2D(sgTexCoordData[0], sgTexCoordData[1]);
|
|
}
|
|
|
|
if( bHaveColors )
|
|
{
|
|
//VertexColors->GetTuple( WedgeIndex, (uint8 *)&Wedge.Color );
|
|
VertexColors->GetTuple(WedgeIndex, sgVertexColorData);
|
|
uint8* sgColors = sgVertexColorData->GetData();
|
|
Wedge.Color = FColor(sgColors[0], sgColors[1], sgColors[2], sgColors[3]);
|
|
}
|
|
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 );
|
|
|
|
|
|
TArray<FVector>& Vertices = MeshData.Points;
|
|
for (int32 VertexIndex = 0; VertexIndex < MeshData.Points.Num(); ++VertexIndex)
|
|
{
|
|
Vertices[VertexIndex] = GetConversionMatrix().TransformPosition(Vertices[VertexIndex]);
|
|
}
|
|
|
|
for (int32 FaceIndex = 0; FaceIndex < MeshData.Faces.Num(); ++FaceIndex)
|
|
{
|
|
FMeshFace& Face = MeshData.Faces[FaceIndex];
|
|
for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
|
|
{
|
|
Face.TangentX[CornerIndex] = GetConversionMatrix().TransformPosition(Face.TangentX[CornerIndex]);
|
|
Face.TangentY[CornerIndex] = GetConversionMatrix().TransformPosition(Face.TangentY[CornerIndex]);
|
|
Face.TangentZ[CornerIndex] = GetConversionMatrix().TransformPosition(Face.TangentZ[CornerIndex]);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
|
|
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->SetStopCondition(SimplygonSDK::SG_STOPCONDITION_ANY);
|
|
ReductionSettings->SetReductionTargets(SimplygonSDK::SG_REDUCTIONTARGET_TRIANGLERATIO | SimplygonSDK::SG_REDUCTIONTARGET_MAXDEVIATION);
|
|
ReductionSettings->SetMaxDeviation( MaxDeviation );
|
|
ReductionSettings->SetTriangleRatio( 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] );
|
|
|
|
ReductionSettings->SetDataCreationPreferences(2); //2 = reposition vertices enabled
|
|
ReductionSettings->SetGenerateGeomorphData(true);
|
|
}
|
|
|
|
/**
|
|
* 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->SetHardEdgeAngleInRadians(FMath::DegreesToRadians(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->SetBoneReductionTargets( SimplygonSDK::SG_BONEREDUCTIONTARGET_BONERATIO );
|
|
BoneSettings->SetBoneRatio ( Settings.BoneReductionRatio );
|
|
BoneSettings->SetMaxBonePerVertex( Settings.MaxBonesPerVertex );
|
|
}
|
|
|
|
/**
|
|
* ProxyLOD Related Methods
|
|
*/
|
|
void SetMaterialChannelData(
|
|
const TArray<FColor>& InSamples,
|
|
FIntPoint InTextureSize,
|
|
SimplygonSDK::spMaterial& InSGMaterial,
|
|
//@third party BEGIN SIMPLYGON
|
|
SimplygonSDK::spTextureTable& TextureTable,
|
|
//@third party END SIMPLYGON
|
|
|
|
const char* SGMaterialChannelName)
|
|
{
|
|
if (InSamples.Num() > 1)
|
|
{
|
|
int32 NumTexels = InTextureSize.X * InTextureSize.Y;
|
|
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;
|
|
|
|
if (SGMaterialChannelName == SimplygonSDK::SG_MATERIAL_CHANNEL_DIFFUSE ||
|
|
SGMaterialChannelName == SimplygonSDK::SG_MATERIAL_CHANNEL_BASECOLOR)
|
|
{
|
|
Texel[3] = InSamples[TexelIndex].A;
|
|
}
|
|
else
|
|
{
|
|
Texel[3] = FColor::White.A;
|
|
}
|
|
|
|
ImageColors->SetTuple(TexelIndex, Texel);
|
|
}
|
|
|
|
//@third party BEGIN SIMPLYGON
|
|
// Need to add a texture to the texture table in order to be able
|
|
// to reference it in the shading network.
|
|
FGuid TextureGuid = FGuid::NewGuid();
|
|
SimplygonSDK::spTexture Texture = SDK->CreateTexture();
|
|
Texture->SetImageData(ImageData);
|
|
Texture->SetName(TCHAR_TO_ANSI(*TextureGuid.ToString()));
|
|
TextureTable->AddTexture(Texture);
|
|
|
|
//Create texture node
|
|
SimplygonSDK::spShadingTextureNode TextureNode = SDK->CreateShadingTextureNode();
|
|
TextureNode->SetTextureLevel(0); // UV Coordinate to use
|
|
TextureNode->SetTextureName(TCHAR_TO_ANSI(*TextureGuid.ToString()));
|
|
TextureNode->SetUseSRGB(false);
|
|
//hook node into desired channel.
|
|
InSGMaterial->SetShadingNetwork(SGMaterialChannelName, TextureNode);
|
|
//@third party END SIMPLYGON
|
|
|
|
|
|
|
|
}
|
|
else if (InSamples.Num() == 1)
|
|
{
|
|
//@third party BEGIN SIMPLYGON
|
|
// Setup a color node
|
|
SimplygonSDK::spShadingColorNode ColorNode = SDK->CreateShadingColorNode();
|
|
ColorNode->SetColor(InSamples[0].R / 255.0f, InSamples[0].G / 255.0f, InSamples[0].B / 255.0f, 1.0f);
|
|
InSGMaterial->SetShadingNetwork(SGMaterialChannelName, ColorNode);
|
|
//@third party END SIMPLYGON
|
|
|
|
}
|
|
else
|
|
{
|
|
//@third party BEGIN SIMPLYGON
|
|
SimplygonSDK::spShadingColorNode ColorNode = SDK->CreateShadingColorNode();
|
|
ColorNode->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
InSGMaterial->SetShadingNetwork(SGMaterialChannelName, ColorNode);
|
|
//@third party END SIMPLYGON
|
|
|
|
}
|
|
}
|
|
|
|
//Material conversions
|
|
//@third party BEGIN SIMPLYGON
|
|
void GetMaterialChannelData(const SimplygonSDK::spMaterial& InSGMaterial, const SimplygonSDK::spTextureTable& TextureTable, const char* SGMaterialChannelName, TArray<FColor>& OutSamples, FIntPoint& OutTextureSize)
|
|
//@third party END SIMPLYGON
|
|
|
|
{
|
|
//@third party BEGIN SIMPLYGON
|
|
SimplygonSDK::spShadingNode ExitNode = InSGMaterial->GetShadingNetwork(SGMaterialChannelName);
|
|
if (ExitNode == nullptr)
|
|
{
|
|
//Current channel has not been baked
|
|
return;
|
|
}
|
|
|
|
SimplygonSDK::spShadingTextureNode TextureNode = SimplygonSDK::Cast<SimplygonSDK::IShadingTextureNode>(ExitNode);
|
|
SimplygonSDK::rid TextureId = TextureTable->FindTextureId(TextureNode->GetTextureName());
|
|
|
|
if (TextureId < 0)
|
|
{
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Could not retrieve texture for texture table for channel %s"), ANSI_TO_TCHAR(SGMaterialChannelName));
|
|
return;
|
|
}
|
|
|
|
SimplygonSDK::spTexture Texture = TextureTable->GetTexture(TextureId);
|
|
SimplygonSDK::spImageData SGChannelData = Texture->GetImageData();
|
|
//@third party END SIMPLYGON
|
|
|
|
|
|
if (SGChannelData)
|
|
{
|
|
SimplygonSDK::spUnsignedCharArray ImageColors = SimplygonSDK::SafeCast<SimplygonSDK::IUnsignedCharArray>(SGChannelData->GetColors());
|
|
|
|
OutTextureSize.X = SGChannelData->GetXSize();
|
|
OutTextureSize.Y = SGChannelData->GetYSize();
|
|
|
|
int32 TexelsCount = OutTextureSize.X*OutTextureSize.Y;
|
|
OutSamples.Empty(TexelsCount);
|
|
OutSamples.AddUninitialized(TexelsCount);
|
|
|
|
SimplygonSDK::spUnsignedCharData sgImageCharData = SDK->CreateUnsignedCharData();
|
|
for (int32 TexelIndex = 0; TexelIndex < TexelsCount; ++TexelIndex)
|
|
{
|
|
//uint8 ColorData[4];
|
|
//ImageColors->GetTuple(TexelIndex, (unsigned char*)&ColorData);
|
|
|
|
ImageColors->GetTuple(TexelIndex, sgImageCharData);
|
|
uint8* ColorData = sgImageCharData->GetData();
|
|
|
|
OutSamples[TexelIndex].R = ColorData[0];
|
|
OutSamples[TexelIndex].G = ColorData[1];
|
|
OutSamples[TexelIndex].B = ColorData[2];
|
|
if (SGMaterialChannelName == SimplygonSDK::SG_MATERIAL_CHANNEL_DIFFUSE ||
|
|
SGMaterialChannelName == SimplygonSDK::SG_MATERIAL_CHANNEL_BASECOLOR)
|
|
{
|
|
OutSamples[TexelIndex].A = ColorData[3];
|
|
}
|
|
else
|
|
{
|
|
OutSamples[TexelIndex].A = FColor::White.A;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CreateFlattenMaterialFromSGMaterial(
|
|
SimplygonSDK::spMaterialTable& InSGMaterialTable,
|
|
//@third party BEGIN SIMPLYGON
|
|
SimplygonSDK::spTextureTable& TextureTable,
|
|
//@third party END SIMPLYGON
|
|
|
|
FFlattenMaterial& OutMaterial)
|
|
{
|
|
FIntPoint Size;
|
|
SimplygonSDK::spMaterial SGMaterial = InSGMaterialTable->GetMaterial(0);
|
|
|
|
// Diffuse
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_DIFFUSE, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Diffuse), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Diffuse, Size);
|
|
|
|
// Normal
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_NORMALS, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Normal), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Normal, Size);
|
|
|
|
// Metallic
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, USER_MATERIAL_CHANNEL_METALLIC, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Metallic), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Metallic, Size);
|
|
|
|
// Roughness
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, USER_MATERIAL_CHANNEL_ROUGHNESS, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Roughness), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Roughness, Size);
|
|
|
|
// Specular
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, USER_MATERIAL_CHANNEL_SPECULAR, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Specular), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Specular, Size);
|
|
}
|
|
|
|
void CreateFlattenMaterialFromSGMaterial(
|
|
SimplygonSDK::spMaterial& SGMaterial,
|
|
//@third party BEGIN SIMPLYGON
|
|
SimplygonSDK::spTextureTable& TextureTable,
|
|
//@third party END SIMPLYGON
|
|
|
|
FFlattenMaterial& OutMaterial)
|
|
{
|
|
FIntPoint Size;
|
|
|
|
// Diffuse
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_BASECOLOR, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Diffuse), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Diffuse, Size);
|
|
|
|
// Normal
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_NORMALS, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Normal), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Normal, Size);
|
|
|
|
// Metallic
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_METALNESS, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Metallic), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Metallic, Size);
|
|
|
|
// Roughness
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_ROUGHNESS, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Roughness), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Roughness, Size);
|
|
|
|
// Specular
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_SPECULAR, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Specular), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Specular, Size);
|
|
|
|
// Opacity
|
|
Size = FIntPoint::ZeroValue;
|
|
#if USE_USER_OPACITY_CHANNEL
|
|
GetMaterialChannelData(SGMaterial, TextureTable, USER_MATERIAL_CHANNEL_OPACITY, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Opacity), Size);
|
|
#else
|
|
GetMaterialChannelData(SGMaterial, TextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_OPACITY, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Opacity), Size);
|
|
#endif
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Opacity, Size);
|
|
|
|
// Emissive
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_EMISSIVE, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::Emissive), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::Emissive, Size);
|
|
|
|
Size = FIntPoint::ZeroValue;
|
|
GetMaterialChannelData(SGMaterial, TextureTable, USER_MATERIAL_CHANNEL_SUBSURFACE_COLOR, OutMaterial.GetPropertySamples(EFlattenMaterialProperties::SubSurface), Size);
|
|
OutMaterial.SetPropertySize(EFlattenMaterialProperties::SubSurface, Size);
|
|
}
|
|
|
|
static FIntPoint ComputeMappingImageSize(const FMaterialSimplificationSettings& Settings)
|
|
{
|
|
FIntPoint ImageSize = Settings.BaseColorMapSize;
|
|
ImageSize = ImageSize.ComponentMax(Settings.NormalMapSize);
|
|
ImageSize = ImageSize.ComponentMax(Settings.MetallicMapSize);
|
|
ImageSize = ImageSize.ComponentMax(Settings.RoughnessMapSize);
|
|
ImageSize = ImageSize.ComponentMax(Settings.SpecularMapSize);
|
|
return ImageSize;
|
|
}
|
|
|
|
bool CreateSGMaterialFromFlattenMaterial(
|
|
const TArray<FFlattenMaterial>& InputMaterials,
|
|
const FSimplygonMaterialLODSettings& InMaterialLODSettings,
|
|
SimplygonSDK::spMaterialTable& OutSGMaterialTable,
|
|
//@third party BEGIN SIMPLYGON
|
|
SimplygonSDK::spTextureTable& OutTextureTable,
|
|
//@third party END SIMPLYGON
|
|
|
|
bool bReleaseInputMaterials)
|
|
{
|
|
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 FFlattenMaterial& FlattenMaterial = InputMaterials[MaterialIndex];
|
|
|
|
#if USE_USER_OPACITY_CHANNEL
|
|
if (!SGMaterial->HasUserChannel(USER_MATERIAL_CHANNEL_OPACITY))
|
|
SGMaterial->AddUserChannel(USER_MATERIAL_CHANNEL_OPACITY);
|
|
#endif
|
|
SGMaterial->AddUserChannel(USER_MATERIAL_CHANNEL_SUBSURFACE_COLOR);
|
|
|
|
SGMaterial->SetName(TCHAR_TO_ANSI(*FString::Printf(TEXT("Material%d"), MaterialIndex)));
|
|
|
|
// Does current material have BaseColor?
|
|
if (FlattenMaterial.DoesPropertyContainData(EFlattenMaterialProperties::Diffuse))
|
|
{
|
|
//@third party BEGIN SIMPLYGON
|
|
//NOTE: Currently bBakeVertexData is marked as deprecated in FMaterialProxySetting. We can add the support before 4.15 lockdown
|
|
|
|
SetMaterialChannelData(FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::Diffuse), FlattenMaterial.GetPropertySize(EFlattenMaterialProperties::Diffuse), SGMaterial, OutTextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_BASECOLOR);
|
|
}
|
|
|
|
// Does current material have Metallic?
|
|
if (FlattenMaterial.DoesPropertyContainData(EFlattenMaterialProperties::Metallic))
|
|
{
|
|
SetMaterialChannelData(FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::Metallic), FlattenMaterial.GetPropertySize(EFlattenMaterialProperties::Metallic), SGMaterial, OutTextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_METALNESS);
|
|
}
|
|
|
|
// Does current material have Specular?
|
|
if (FlattenMaterial.DoesPropertyContainData(EFlattenMaterialProperties::Specular))
|
|
{
|
|
SetMaterialChannelData(FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::Specular), FlattenMaterial.GetPropertySize(EFlattenMaterialProperties::Specular), SGMaterial, OutTextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_SPECULAR);
|
|
}
|
|
|
|
// Does current material have Roughness?
|
|
if (FlattenMaterial.DoesPropertyContainData(EFlattenMaterialProperties::Roughness))
|
|
{
|
|
SetMaterialChannelData(FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::Roughness), FlattenMaterial.GetPropertySize(EFlattenMaterialProperties::Roughness), SGMaterial, OutTextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_ROUGHNESS);
|
|
}
|
|
|
|
//Does current material have a normalmap?
|
|
if (FlattenMaterial.DoesPropertyContainData(EFlattenMaterialProperties::Normal))
|
|
{
|
|
SetMaterialChannelData(FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::Normal), FlattenMaterial.GetPropertySize(EFlattenMaterialProperties::Normal), SGMaterial, OutTextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_NORMALS);
|
|
}
|
|
|
|
// Does current material have Opacity?
|
|
if (FlattenMaterial.DoesPropertyContainData(EFlattenMaterialProperties::Opacity))
|
|
{
|
|
#if USE_USER_OPACITY_CHANNEL
|
|
SetMaterialChannelData(FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::Opacity), FlattenMaterial.GetPropertySize(EFlattenMaterialProperties::Opacity), SGMaterial, OutTextureTable, USER_MATERIAL_CHANNEL_OPACITY);
|
|
#else
|
|
SetMaterialChannelData(FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::Opacity), FlattenMaterial.GetPropertySize(EFlattenMaterialProperties::Opacity), SGMaterial, OutTextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_OPACITY);
|
|
#endif
|
|
}
|
|
|
|
if (FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::Emissive).Num())
|
|
{
|
|
SetMaterialChannelData(FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::Emissive), FlattenMaterial.GetPropertySize(EFlattenMaterialProperties::Emissive), SGMaterial, OutTextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_EMISSIVE);
|
|
}
|
|
else
|
|
{
|
|
TArray<FColor> BlackEmissive;
|
|
BlackEmissive.AddZeroed(1);
|
|
//@third party BEGIN SIMPLYGON
|
|
SetMaterialChannelData(BlackEmissive, FIntPoint(1,1), SGMaterial, OutTextureTable, SimplygonSDK::SG_MATERIAL_CHANNEL_EMISSIVE);
|
|
//@third party END SIMPLYGON
|
|
|
|
}
|
|
|
|
if (FlattenMaterial.DoesPropertyContainData(EFlattenMaterialProperties::SubSurface))
|
|
{
|
|
SetMaterialChannelData(FlattenMaterial.GetPropertySamples(EFlattenMaterialProperties::SubSurface), FlattenMaterial.GetPropertySize(EFlattenMaterialProperties::SubSurface), SGMaterial, OutTextureTable, USER_MATERIAL_CHANNEL_SUBSURFACE_COLOR);
|
|
}
|
|
|
|
OutSGMaterialTable->AddMaterial(SGMaterial);
|
|
|
|
if (bReleaseInputMaterials)
|
|
{
|
|
// Release FlattenMaterial. Using const_cast here to avoid removal of "const" from input data here
|
|
// and above the call chain.
|
|
const_cast<FFlattenMaterial*>(&FlattenMaterial)->ReleaseData();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* The method converts ESimplygonTextureSamplingQuality
|
|
* @param InSamplingQuality The Caster Settings used to setup the Simplygon Caster.
|
|
* @param InMappingImage Simplygon MappingImage.
|
|
* @return
|
|
*/
|
|
uint8 GetSamples(ESimplygonTextureSamplingQuality::Type InSamplingQuality)
|
|
{
|
|
switch (InSamplingQuality)
|
|
{
|
|
case ESimplygonTextureSamplingQuality::Poor:
|
|
return 1;
|
|
case ESimplygonTextureSamplingQuality::Low:
|
|
return 2;
|
|
case ESimplygonTextureSamplingQuality::Medium:
|
|
return 6;
|
|
case ESimplygonTextureSamplingQuality::High:
|
|
return 8;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
uint8 ConvertColorChannelToInt(ESimplygonColorChannels::Type InSamplingQuality)
|
|
{
|
|
switch (InSamplingQuality)
|
|
{
|
|
case ESimplygonColorChannels::RGBA:
|
|
return 4;
|
|
case ESimplygonColorChannels::RGB:
|
|
return 3;
|
|
case ESimplygonColorChannels::L:
|
|
return 1;
|
|
}
|
|
|
|
return 3;
|
|
}
|
|
|
|
/**
|
|
* Use Simplygon Color Caster to Cast a Color Channel
|
|
* @param InCasterSettings The Caster Settings used to setup the Simplygon Caster.
|
|
* @param InMappingImage Simplygon MappingImage.
|
|
* @param InMaterialTable Input MaterialTable used by the Simplygon Caster.
|
|
* @param InTextureTable Simplygon TextureTable used by the Simplyogn Caster.
|
|
* @param OutColorData The Simplygon Output ImageData.
|
|
*/
|
|
void CastColorChannel(const FSimplygonChannelCastingSettings& InCasterSettings,
|
|
SimplygonSDK::spMappingImage& InMappingImage,
|
|
SimplygonSDK::spMaterialTable& InMaterialTable,
|
|
const SimplygonSDK::spTextureTable& InTextureTable,
|
|
SimplygonSDK::spImageData& OutColorData)
|
|
{
|
|
SimplygonSDK::spColorCaster cast = SDK->CreateColorCaster();
|
|
|
|
cast->SetColorType( GetSimplygonMaterialChannel(InCasterSettings.MaterialChannel) );
|
|
cast->SetSourceMaterials( InMaterialTable );
|
|
cast->SetSourceTextures(InTextureTable);
|
|
cast->SetMappingImage( InMappingImage ); // The mapping image we got from the remeshing process.
|
|
cast->SetOutputChannels( ConvertColorChannelToInt(InCasterSettings.ColorChannels) ); // RGB, 3 channels! (1 would be for grey scale, and 4 would be for RGBA.)
|
|
cast->SetOutputChannelBitDepth( InCasterSettings.BitsPerChannel ); // 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(OutColorData);
|
|
cast->SetBakeOpacityInAlpha(false);
|
|
//cast->SetBakeVertexColors(InCasterSettings.bBakeVertexColors);
|
|
//@third party BEGIN SIMPLYGON
|
|
cast->SetOutputSRGB(InCasterSettings.bUseSRGB);
|
|
//@third party END SIMPLYGON
|
|
|
|
cast->CastMaterials(); // Fetch!
|
|
}
|
|
|
|
/**
|
|
* Use Simplygon Normal Caster to Cast a Normals Channel
|
|
* @param InCasterSettings The Caster Settings used to setup the Simplygon Caster.
|
|
* @param InMappingImage Simplygon MappingImage.
|
|
* @param InMaterialTable Input MaterialTable used by the Simplygon Caster.
|
|
* @param InTextureTable Simplygon TextureTable used by the Simplyogn Caster.
|
|
* @param OutColorData The Simplygon Output ImageData.
|
|
*/
|
|
void CastNormalsChannel(FSimplygonChannelCastingSettings InCasterSettings,
|
|
SimplygonSDK::spMappingImage& InMappingImage,
|
|
SimplygonSDK::spMaterialTable& InMaterialTable,
|
|
const SimplygonSDK::spTextureTable& InTextureTable,
|
|
SimplygonSDK::spImageData& OutColorData,
|
|
int32 DestinationMaterialIndex = -1)
|
|
{
|
|
SimplygonSDK::spNormalCaster cast = SDK->CreateNormalCaster();
|
|
cast->SetSourceMaterials( InMaterialTable );
|
|
cast->SetSourceTextures(InTextureTable);
|
|
cast->SetMappingImage( InMappingImage ); // The mapping image we got from the remeshing process.
|
|
cast->SetOutputChannels( ConvertColorChannelToInt(InCasterSettings.ColorChannels) ); // 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(InCasterSettings.bFlipBackfacingNormals);
|
|
cast->SetGenerateTangentSpaceNormals(InCasterSettings.bUseTangentSpaceNormals);
|
|
cast->SetFlipGreen(false);
|
|
//cast->SetNormalMapTextureLevel();
|
|
cast->SetDestMaterialId(DestinationMaterialIndex);
|
|
cast->SetOutputImage(OutColorData);
|
|
cast->CastMaterials(); // Fetch!
|
|
}
|
|
|
|
/**
|
|
* Use Simplygon Opacity Caster to Cast an Opacity Channel
|
|
* @param InCasterSettings The Caster Settings used to setup the Simplygon Caster.
|
|
* @param InMappingImage Simplygon MappingImage.
|
|
* @param InMaterialTable Input MaterialTable used by the Simplygon Caster.
|
|
* @param InTextureTable Simplygon TextureTable used by the Simplyogn Caster.
|
|
* @param OutColorData The Simplygon Output ImageData.
|
|
*/
|
|
void CastOpacityChannel(FSimplygonChannelCastingSettings InCasterSettings,
|
|
SimplygonSDK::spMappingImage& InMappingImage,
|
|
SimplygonSDK::spMaterialTable& InMaterialTable,
|
|
const SimplygonSDK::spTextureTable& InTextureTable,
|
|
SimplygonSDK::spImageData& OutColorData)
|
|
{
|
|
SimplygonSDK::spOpacityCaster cast = SDK->CreateOpacityCaster();
|
|
cast->SetSourceMaterials( InMaterialTable );
|
|
cast->SetSourceTextures(InTextureTable);
|
|
cast->SetMappingImage( InMappingImage ); // The mapping image we got from the remeshing process.
|
|
cast->SetOutputChannels( ConvertColorChannelToInt(InCasterSettings.ColorChannels) ); // 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(OutColorData);
|
|
cast->CastMaterials(); // Fetch!
|
|
}
|
|
|
|
/**
|
|
* Sets Mapping Image Setting for Simplygon.
|
|
* @param InMaterialLODSettings The MaterialLOD Settings used for setting up Simplygon MappingImageSetting.
|
|
* @param InMappingImageSettings The Simplygon MappingImage Settings that is being setup.
|
|
*/
|
|
void SetupMappingImage(const FSimplygonMaterialLODSettings& InMaterialLODSettings,
|
|
SimplygonSDK::spMappingImageSettings InMappingImageSettings,
|
|
bool InAggregateProcess,
|
|
bool InRemoveUVs)
|
|
{
|
|
if (!InMaterialLODSettings.bActive)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int32 NumInputMaterials = 1;
|
|
int32 NumOutputMaterials = 1;
|
|
|
|
//if (InMaterialLODSettings.bReuseExistingCharts || InAggregateProcess) - we're always using UVs from the mesh because new UVs are already generated with GenerateUniqueUVs() call
|
|
if (InAggregateProcess || NumOutputMaterials > 1)
|
|
{
|
|
InMappingImageSettings->SetTexCoordGeneratorType(SimplygonSDK::SG_TEXCOORDGENERATORTYPE_CHARTAGGREGATOR);
|
|
//InMappingImageSettings->SetChartAggregatorMode(SimplygonSDK::SG_CHARTAGGREGATORMODE_SURFACEAREA);
|
|
}
|
|
else
|
|
{
|
|
InMappingImageSettings->SetTexCoordGeneratorType(SimplygonSDK::SG_TEXCOORDGENERATORTYPE_PARAMETERIZER);
|
|
}
|
|
|
|
InMappingImageSettings->SetGenerateMappingImage(true);
|
|
InMappingImageSettings->SetTexCoordLevel(0);
|
|
if (NumOutputMaterials > 1)
|
|
{
|
|
InMappingImageSettings->SetGenerateTexCoords(true); //! check if it is possible to avoid this
|
|
}
|
|
|
|
|
|
for (int32 MaterialIndex = 0; MaterialIndex < NumOutputMaterials; MaterialIndex++)
|
|
{
|
|
InMappingImageSettings->SetGutterSpace(MaterialIndex, InMaterialLODSettings.GutterSpace);
|
|
InMappingImageSettings->SetMultisamplingLevel(MaterialIndex, GetSamples(InMaterialLODSettings.SamplingQuality));
|
|
}
|
|
|
|
if (InRemoveUVs)
|
|
{
|
|
InMappingImageSettings->SetUseFullRetexturing(true);
|
|
}
|
|
|
|
InMappingImageSettings->SetGenerateTangents(false);
|
|
|
|
bool bAutomaticSizes = InMaterialLODSettings.bUseAutomaticSizes;
|
|
InMappingImageSettings->SetUseAutomaticTextureSize(bAutomaticSizes);
|
|
|
|
if (!bAutomaticSizes)
|
|
{
|
|
for (int32 MaterialIndex = 0; MaterialIndex < NumOutputMaterials; MaterialIndex++)
|
|
{
|
|
InMappingImageSettings->SetWidth(MaterialIndex, InMaterialLODSettings.GetTextureResolutionFromEnum(InMaterialLODSettings.TextureWidth));
|
|
InMappingImageSettings->SetHeight(MaterialIndex, InMaterialLODSettings.GetTextureResolutionFromEnum(InMaterialLODSettings.TextureHeight));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InMappingImageSettings->SetForcePower2Texture(true);
|
|
}
|
|
}
|
|
|
|
SimplygonSDK::spMaterial RebakeMaterials(const FSimplygonMaterialLODSettings& InMaterialLODSettings,
|
|
SimplygonSDK::spMappingImage& InMappingImage,
|
|
SimplygonSDK::spMaterialTable& InSGMaterialTable,
|
|
//@third party BEGIN SIMPLYGON
|
|
const SimplygonSDK::spTextureTable& TextureTable,
|
|
//@third party END SIMPLYGON
|
|
|
|
int32 DestinationMaterialIndex = -1)
|
|
{
|
|
SimplygonSDK::spMaterial OutMaterial = SDK->CreateMaterial();
|
|
#if USE_USER_OPACITY_CHANNEL
|
|
if(!OutMaterial->HasUserChannel(USER_MATERIAL_CHANNEL_OPACITY))
|
|
OutMaterial->AddUserChannel(USER_MATERIAL_CHANNEL_OPACITY);
|
|
#endif
|
|
OutMaterial->AddUserChannel(USER_MATERIAL_CHANNEL_SUBSURFACE_COLOR);
|
|
|
|
for(int ChannelIndex=0; ChannelIndex < InMaterialLODSettings.ChannelsToCast.Num(); ChannelIndex++)
|
|
{
|
|
FSimplygonChannelCastingSettings CasterSetting = InMaterialLODSettings.ChannelsToCast[ChannelIndex];
|
|
|
|
if(CasterSetting.bActive)
|
|
{
|
|
SimplygonSDK::spImageData OutColorData = SDK->CreateImageData();
|
|
|
|
switch(CasterSetting.Caster)
|
|
{
|
|
case ESimplygonCasterType::Color:
|
|
CastColorChannel(CasterSetting,InMappingImage,InSGMaterialTable, TextureTable, OutColorData);
|
|
break;
|
|
case ESimplygonCasterType::Normals:
|
|
CastNormalsChannel(CasterSetting,InMappingImage,InSGMaterialTable, TextureTable, OutColorData, DestinationMaterialIndex);
|
|
break;
|
|
case ESimplygonCasterType::Opacity:
|
|
#if USE_USER_OPACITY_CHANNEL
|
|
CastColorChannel(CasterSetting,InMappingImage,InSGMaterialTable, TextureTable, OutColorData);
|
|
#else
|
|
CastOpacityChannel(CasterSetting,InMappingImage,InSGMaterialTable,OutColorData, TextureTable);
|
|
#endif
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//@third party BEGIN SIMPLYGON
|
|
FGuid TextureGuid = FGuid::NewGuid();
|
|
SimplygonSDK::spTexture BakedTexture = SDK->CreateTexture();
|
|
BakedTexture->SetImageData(OutColorData);
|
|
BakedTexture->SetName(TCHAR_TO_ANSI(*TextureGuid.ToString()));
|
|
TextureTable->AddTexture(BakedTexture);
|
|
|
|
SimplygonSDK::spShadingTextureNode BakedTextureNode = SDK->CreateShadingTextureNode();
|
|
BakedTextureNode->SetTextureLevel(0);
|
|
BakedTextureNode->SetTextureName(TCHAR_TO_ANSI(*TextureGuid.ToString()));
|
|
OutMaterial->SetShadingNetwork(GetSimplygonMaterialChannel(CasterSetting.MaterialChannel), BakedTextureNode);
|
|
//@third party END SIMPLYGON
|
|
|
|
}
|
|
}
|
|
|
|
return OutMaterial;
|
|
}
|
|
|
|
/*
|
|
* (1, 0, 0)
|
|
* (0, 0, 1)
|
|
* (0, 1, 0)
|
|
*/
|
|
const FMatrix& GetConversionMatrix()
|
|
{
|
|
static FMatrix m;
|
|
static bool bInitialized = false;
|
|
if (!bInitialized)
|
|
{
|
|
m.SetIdentity();
|
|
m.M[1][1] = 0.0f;
|
|
m.M[2][1] = 1.0f;
|
|
|
|
m.M[1][2] = 1.0f;
|
|
m.M[2][2] = 0.0f;
|
|
bInitialized = true;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
void ValidateGeometry(SimplygonSDK::spGeometryValidator& GeometryValidator, SimplygonSDK::spGeometryData& GeometryData)
|
|
{
|
|
bool bGeometryValid = GeometryValidator->ValidateGeometry(GeometryData);
|
|
if (!bGeometryValid)
|
|
{
|
|
uint32 error_val = GeometryValidator->GetErrorValue();
|
|
if ((error_val & SimplygonSDK::SG_VALIDATIONERROR_ZERO_LENGTH_NORMAL) != 0)
|
|
{
|
|
SimplygonSDK::spNormalRepairer rep = SDK->CreateNormalRepairer();
|
|
rep->SetRepairOnlyInvalidNormals(true);
|
|
rep->SetGeometry(GeometryData);
|
|
rep->RunProcessing();
|
|
}
|
|
else
|
|
{
|
|
FString ErrorMessage = ANSI_TO_TCHAR(GeometryValidator->GetErrorString());
|
|
UE_LOG(LogSimplygon, Warning, TEXT("Invalid mesh data: %s."), *ErrorMessage);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
TScopedPointer<FSimplygonMeshReduction> GSimplygonMeshReduction;
|
|
|
|
|
|
void FSimplygonMeshReductionModule::StartupModule()
|
|
{
|
|
GSimplygonMeshReduction = FSimplygonMeshReduction::Create();
|
|
}
|
|
|
|
void FSimplygonMeshReductionModule::ShutdownModule()
|
|
{
|
|
FSimplygonMeshReduction::Destroy();
|
|
GSimplygonMeshReduction = NULL;
|
|
}
|
|
|
|
#define USE_SIMPLYGON_SWARM 0
|
|
|
|
IMeshReduction* FSimplygonMeshReductionModule::GetMeshReductionInterface()
|
|
{
|
|
#if !USE_SIMPLYGON_SWARM
|
|
return GSimplygonMeshReduction;
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
IMeshMerging* FSimplygonMeshReductionModule::GetMeshMergingInterface()
|
|
{
|
|
#if !USE_SIMPLYGON_SWARM
|
|
return GSimplygonMeshReduction;
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|