You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Restoring previous FDatasmithSceneExporter::Reset() behavior but marking the function as deprecated. It's previous usage of reseting the export progress without resetting the whole exporter state was discutable and confusing. If users want to fully reset the exporter they don't need the Reset() function. - Fixing Datasmith Facade inverse scaling error during position conversion #preflight 624b48d9dc6183e3f547d3b1 #rb Johan.Duparc Kerim.Borchaev [CL 19625080 by benoit deschenes in ue5-main branch]
360 lines
11 KiB
C++
360 lines
11 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "DatasmithSceneExporter.h"
|
|
|
|
#include "DatasmithAnimationSerializer.h"
|
|
#include "DatasmithExportOptions.h"
|
|
#include "DatasmithExporterManager.h"
|
|
#include "DatasmithLogger.h"
|
|
#include "DatasmithMaterialElements.h"
|
|
#include "DatasmithProgressManager.h"
|
|
#include "DatasmithSceneFactory.h"
|
|
#include "DatasmithSceneXmlWriter.h"
|
|
#include "DatasmithUtils.h"
|
|
|
|
#include "GenericPlatform/GenericPlatformFile.h"
|
|
#include "Containers/Array.h"
|
|
#include "Algo/Find.h"
|
|
#include "Containers/Set.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "HAL/PlatformFileManager.h"
|
|
#include "Math/UnrealMathUtility.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Misc/SecureHash.h"
|
|
#include "UObject/GarbageCollection.h"
|
|
|
|
// Hash function to use FMD5Hash in TMap
|
|
inline uint32 GetTypeHash(const FMD5Hash& Hash)
|
|
{
|
|
uint32* HashAsInt32 = (uint32*)Hash.GetBytes();
|
|
return HashAsInt32[0] ^ HashAsInt32[1] ^ HashAsInt32[2] ^ HashAsInt32[3];
|
|
}
|
|
|
|
class FDatasmithSceneExporterImpl
|
|
{
|
|
public:
|
|
FDatasmithSceneExporterImpl();
|
|
|
|
// call this before export the actual bitmaps
|
|
void CheckBumpMaps( TSharedRef< IDatasmithScene > DatasmithScene );
|
|
void UpdateTextureElements( TSharedRef< IDatasmithScene > DatasmithScene );
|
|
void UpdateAssetOutputPath();
|
|
|
|
static EDatasmithTextureMode GetTextureModeFromPropertyName(const FString& PropertyName);
|
|
static FString GetFileNameWithHash(const FString& FullPath);
|
|
|
|
FString Name;
|
|
FString OutputPath;
|
|
FString AssetsOutputPath;
|
|
|
|
uint64 ExportStartCycles;
|
|
|
|
TSharedPtr<IDatasmithProgressManager> ProgressManager;
|
|
TSharedPtr<FDatasmithLogger> Logger;
|
|
};
|
|
|
|
FDatasmithSceneExporterImpl::FDatasmithSceneExporterImpl()
|
|
: ExportStartCycles(0)
|
|
{
|
|
}
|
|
|
|
void FDatasmithSceneExporterImpl::UpdateTextureElements( TSharedRef< IDatasmithScene > DatasmithScene )
|
|
{
|
|
FDatasmithTextureUtils::CalculateTextureHashes(DatasmithScene);
|
|
|
|
// No need to do anything if user required to keep images at original location or no output path is set
|
|
if (FDatasmithExportOptions::PathTexturesMode == EDSResizedTexturesPath::OriginalFolder || AssetsOutputPath.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
|
|
TMap<FMD5Hash, FString> HashFilePathMap;
|
|
FDatasmithUniqueNameProvider TextureFileNameProvider;
|
|
|
|
for (int32 i = 0; i < DatasmithScene->GetTexturesCount(); i++)
|
|
{
|
|
TSharedPtr< IDatasmithTextureElement > TextureElement = DatasmithScene->GetTexture(i);
|
|
|
|
FString TextureFileName = TextureElement->GetFile();
|
|
|
|
float RatioDone = float(i + 1) / float( DatasmithScene->GetTexturesCount() );
|
|
if (ProgressManager.IsValid())
|
|
{
|
|
ProgressManager->ProgressEvent(RatioDone, *FPaths::GetBaseFilename( TextureFileName ));
|
|
}
|
|
|
|
FString& NewFilename = HashFilePathMap.FindOrAdd(TextureElement->GetFileHash());
|
|
|
|
// If this texture has not been exported yet, find a unique name for it and copy its file to the asset output path.
|
|
if (NewFilename.IsEmpty())
|
|
{
|
|
const FString UniqueFileName = TextureFileNameProvider.GenerateUniqueName( FPaths::GetBaseFilename( TextureFileName ) );
|
|
TextureFileNameProvider.AddExistingName( UniqueFileName );
|
|
const FString FileExtension = FPaths::GetExtension(TextureFileName, /*bIncludeDot=*/true);
|
|
NewFilename = FPaths::Combine(AssetsOutputPath, UniqueFileName + FileExtension);
|
|
|
|
// Copy image file to new location
|
|
PlatformFile.CopyFile(*NewFilename, *TextureFileName);
|
|
}
|
|
|
|
// Update texture element
|
|
if (TextureFileName != NewFilename)
|
|
{
|
|
TextureElement->SetFile(*NewFilename);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDatasmithSceneExporterImpl::CheckBumpMaps( TSharedRef< IDatasmithScene > DatasmithScene )
|
|
{
|
|
for ( int32 MaterialIndex = 0; MaterialIndex < DatasmithScene->GetMaterialsCount(); ++MaterialIndex )
|
|
{
|
|
TSharedPtr< IDatasmithBaseMaterialElement > BaseMaterialElement = DatasmithScene->GetMaterial( MaterialIndex );
|
|
|
|
if ( BaseMaterialElement->IsA( EDatasmithElementType::Material ) )
|
|
{
|
|
const TSharedPtr< IDatasmithMaterialElement >& MaterialElement = StaticCastSharedPtr< IDatasmithMaterialElement >( BaseMaterialElement );
|
|
|
|
for (int32 j = 0; j < MaterialElement->GetShadersCount(); ++j )
|
|
{
|
|
TSharedPtr< IDatasmithShaderElement >& Shader = MaterialElement->GetShader(j);
|
|
|
|
if (Shader->GetBumpComp()->GetMode() == EDatasmithCompMode::Regular && Shader->GetBumpComp()->GetParamSurfacesCount() == 1 &&
|
|
Shader->GetNormalComp()->GetParamSurfacesCount() == 0)
|
|
{
|
|
FString TextureName = Shader->GetBumpComp()->GetParamTexture(0);
|
|
FString NormalTextureName = TextureName + TEXT("_Norm");
|
|
|
|
if ( !TextureName.IsEmpty() )
|
|
{
|
|
FDatasmithTextureSampler UVs = Shader->GetBumpComp()->GetParamTextureSampler(0);
|
|
|
|
TSharedPtr< IDatasmithTextureElement > TextureElement;
|
|
TSharedPtr< IDatasmithTextureElement > NormalTextureElement;
|
|
for ( int32 TextureIndex = 0; TextureIndex < DatasmithScene->GetTexturesCount(); ++TextureIndex )
|
|
{
|
|
if ( DatasmithScene->GetTexture( TextureIndex )->GetName() == TextureName )
|
|
{
|
|
TextureElement = DatasmithScene->GetTexture( TextureIndex );
|
|
}
|
|
else if ( DatasmithScene->GetTexture( TextureIndex )->GetName() == NormalTextureName )
|
|
{
|
|
NormalTextureElement = DatasmithScene->GetTexture( TextureIndex );
|
|
}
|
|
}
|
|
|
|
if ( TextureElement )
|
|
{
|
|
if ( !NormalTextureElement )
|
|
{
|
|
NormalTextureElement = FDatasmithSceneFactory::CreateTexture( *NormalTextureName );
|
|
|
|
NormalTextureElement->SetRGBCurve( 1.f );
|
|
NormalTextureElement->SetFile( TextureElement->GetFile() );
|
|
NormalTextureElement->SetFileHash( TextureElement->GetFileHash() );
|
|
NormalTextureElement->SetTextureMode( EDatasmithTextureMode::Bump );
|
|
|
|
DatasmithScene->AddTexture( NormalTextureElement );
|
|
}
|
|
|
|
Shader->GetNormalComp()->AddSurface( *NormalTextureName, UVs );
|
|
Shader->GetBumpComp()->ClearSurface();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDatasmithSceneExporterImpl::UpdateAssetOutputPath()
|
|
{
|
|
if (Name.IsEmpty())
|
|
{
|
|
// Just set the AssetsOutputPath to OutputPath, if the scene exporter has not been named
|
|
AssetsOutputPath = OutputPath;
|
|
}
|
|
else if (!OutputPath.IsEmpty())
|
|
{
|
|
AssetsOutputPath = FPaths::Combine(OutputPath, Name + TEXT("_Assets"));
|
|
}
|
|
}
|
|
|
|
EDatasmithTextureMode FDatasmithSceneExporterImpl::GetTextureModeFromPropertyName(const FString& PropertyName)
|
|
{
|
|
if (PropertyName.Find(TEXT("BUMP")) != INDEX_NONE)
|
|
{
|
|
return EDatasmithTextureMode::Bump;
|
|
}
|
|
else if (PropertyName.Find(TEXT("SPECULAR")) != INDEX_NONE)
|
|
{
|
|
return EDatasmithTextureMode::Specular;
|
|
}
|
|
else if (PropertyName.Find(TEXT("NORMAL")) != INDEX_NONE)
|
|
{
|
|
return EDatasmithTextureMode::Normal;
|
|
}
|
|
|
|
return EDatasmithTextureMode::Diffuse;
|
|
};
|
|
|
|
FString FDatasmithSceneExporterImpl::GetFileNameWithHash(const FString& FullPath)
|
|
{
|
|
FString Hash = FMD5::HashAnsiString(*FullPath);
|
|
FString FileName = FPaths::GetBaseFilename(FullPath);
|
|
FString Extension = FPaths::GetExtension(FileName);
|
|
FileName = FileName + TEXT("_") + Hash + Extension;
|
|
|
|
return FileName;
|
|
}
|
|
|
|
FDatasmithSceneExporter::FDatasmithSceneExporter()
|
|
: Impl( MakeUnique< FDatasmithSceneExporterImpl >() )
|
|
{
|
|
}
|
|
|
|
FDatasmithSceneExporter::~FDatasmithSceneExporter() = default;
|
|
|
|
|
|
void FDatasmithSceneExporter::PreExport()
|
|
{
|
|
// Collect start time to log amount of time spent to export scene
|
|
Impl->ExportStartCycles = FPlatformTime::Cycles64();
|
|
}
|
|
|
|
void FDatasmithSceneExporter::Export( TSharedRef< IDatasmithScene > DatasmithScene, bool bCleanupUnusedElements )
|
|
{
|
|
if ( Impl->ExportStartCycles == 0 )
|
|
{
|
|
Impl->ExportStartCycles = FPlatformTime::Cycles64();
|
|
}
|
|
|
|
FString FilePath = FPaths::Combine(Impl->OutputPath, Impl->Name ) + TEXT(".") + FDatasmithUtils::GetFileExtension();
|
|
|
|
TUniquePtr<FArchive> Archive( IFileManager::Get().CreateFileWriter( *FilePath ) );
|
|
|
|
if ( !Archive.IsValid() )
|
|
{
|
|
if ( Impl->Logger.IsValid() )
|
|
{
|
|
Impl->Logger->AddGeneralError( *( TEXT("Unable to create file ") + FilePath + TEXT(", Aborting the export process") ) );
|
|
}
|
|
return;
|
|
}
|
|
|
|
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
|
|
PlatformFile.CreateDirectoryTree( *Impl->AssetsOutputPath );
|
|
|
|
// Add Bump maps from Material objects to scene as TextureElement
|
|
Impl->CheckBumpMaps( DatasmithScene );
|
|
|
|
FDatasmithSceneUtils::CleanUpScene(DatasmithScene, bCleanupUnusedElements);
|
|
|
|
// Update TextureElements
|
|
Impl->UpdateTextureElements( DatasmithScene );
|
|
|
|
// Convert paths to relative
|
|
FString AbsoluteDir = Impl->OutputPath + TEXT("/");
|
|
|
|
for ( int32 MeshIndex = 0; MeshIndex < DatasmithScene->GetMeshesCount(); ++MeshIndex )
|
|
{
|
|
TSharedPtr< IDatasmithMeshElement > Mesh = DatasmithScene->GetMesh( MeshIndex );
|
|
|
|
FString RelativePath = Mesh->GetFile();
|
|
FPaths::MakePathRelativeTo( RelativePath, *AbsoluteDir );
|
|
|
|
Mesh->SetFile( *RelativePath );
|
|
}
|
|
|
|
for ( int32 TextureIndex = 0; TextureIndex < DatasmithScene->GetTexturesCount(); ++TextureIndex )
|
|
{
|
|
TSharedPtr< IDatasmithTextureElement > Texture = DatasmithScene->GetTexture( TextureIndex );
|
|
|
|
FString TextureFile = Texture->GetFile();
|
|
FPaths::MakePathRelativeTo( TextureFile, *AbsoluteDir );
|
|
Texture->SetFile( *TextureFile );
|
|
}
|
|
|
|
FDatasmithAnimationSerializer AnimSerializer;
|
|
int32 NumSequences = DatasmithScene->GetLevelSequencesCount();
|
|
for (int32 SequenceIndex = 0; SequenceIndex < NumSequences; ++SequenceIndex)
|
|
{
|
|
const TSharedPtr<IDatasmithLevelSequenceElement>& LevelSequence = DatasmithScene->GetLevelSequence(SequenceIndex);
|
|
if (LevelSequence.IsValid())
|
|
{
|
|
FString AnimFilePath = FPaths::Combine(Impl->AssetsOutputPath, LevelSequence->GetName()) + DATASMITH_ANIMATION_EXTENSION;
|
|
|
|
if (AnimSerializer.Serialize(LevelSequence.ToSharedRef(), *AnimFilePath))
|
|
{
|
|
TUniquePtr<FArchive> AnimArchive(IFileManager::Get().CreateFileReader(*AnimFilePath));
|
|
if (AnimArchive)
|
|
{
|
|
LevelSequence->SetFileHash(FMD5Hash::HashFileFromArchive(AnimArchive.Get()));
|
|
}
|
|
|
|
FPaths::MakePathRelativeTo(AnimFilePath, *AbsoluteDir);
|
|
LevelSequence->SetFile(*AnimFilePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Log time spent to export scene in seconds
|
|
int ElapsedTime = (int)FPlatformTime::ToSeconds64(FPlatformTime::Cycles64() - Impl->ExportStartCycles);
|
|
DatasmithScene->SetExportDuration( ElapsedTime );
|
|
|
|
FDatasmithSceneXmlWriter DatasmithSceneXmlWriter;
|
|
DatasmithSceneXmlWriter.Serialize( DatasmithScene, *Archive );
|
|
|
|
Archive->Close();
|
|
|
|
// Run the garbage collector at this point so that we're in a good state for the next export
|
|
FDatasmithExporterManager::RunGarbageCollection();
|
|
}
|
|
|
|
void FDatasmithSceneExporter::Reset()
|
|
{
|
|
Impl->ProgressManager = nullptr;
|
|
Impl->Logger = nullptr;
|
|
|
|
Impl->ExportStartCycles = 0;
|
|
}
|
|
|
|
void FDatasmithSceneExporter::SetProgressManager( const TSharedPtr< IDatasmithProgressManager >& InProgressManager )
|
|
{
|
|
Impl->ProgressManager = InProgressManager;
|
|
}
|
|
|
|
void FDatasmithSceneExporter::SetLogger( const TSharedPtr< FDatasmithLogger >& InLogger )
|
|
{
|
|
Impl->Logger = InLogger;
|
|
}
|
|
|
|
void FDatasmithSceneExporter::SetName(const TCHAR* InName)
|
|
{
|
|
Impl->Name = InName;
|
|
Impl->UpdateAssetOutputPath();
|
|
}
|
|
|
|
const TCHAR* FDatasmithSceneExporter::GetName() const
|
|
{
|
|
return *Impl->Name;
|
|
}
|
|
|
|
void FDatasmithSceneExporter::SetOutputPath( const TCHAR* InOutputPath )
|
|
{
|
|
Impl->OutputPath = InOutputPath;
|
|
FPaths::NormalizeDirectoryName( Impl->OutputPath );
|
|
Impl->UpdateAssetOutputPath();
|
|
}
|
|
|
|
const TCHAR* FDatasmithSceneExporter::GetOutputPath() const
|
|
{
|
|
return *Impl->OutputPath;
|
|
}
|
|
|
|
const TCHAR* FDatasmithSceneExporter::GetAssetsOutputPath() const
|
|
{
|
|
return *Impl->AssetsOutputPath;
|
|
}
|