Files
UnrealEngineUWP/Engine/Source/Developer/SimplygonMeshReduction/Private/SimplygonMeshReduction.cpp
Ori Cohen cbb856a117 Change missing simplygon DLL back to a log instead of a warning.
#codereview Ben.Marsh
#rb none
#lockdown nick.penwarden

[CL 3153993 by Ori Cohen in Main branch]
2016-10-06 17:17:18 -04:00

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