Files
2025-05-18 13:04:45 +08:00

211 lines
6.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DatasmithMeshExporter.h"
#include "DatasmithCore.h"
#include "DatasmithCloth.h" // UE_DEPRECATED(5.5, "The experimental Cloth importer is no longer supported.")
#include "DatasmithExporterManager.h"
#include "DatasmithMesh.h"
#include "DatasmithMeshUObject.h"
#include "DatasmithMeshSerialization.h"
#include "DatasmithSceneFactory.h"
#include "DatasmithUtils.h"
#include "Containers/LockFreeList.h"
#include "HAL/FileManager.h"
#include "Misc/Guid.h"
#include "Misc/Paths.h"
#include "Serialization/MemoryWriter.h"
struct FDatasmithMeshExporterOptions
{
FDatasmithMeshExporterOptions(const FString& InFullPath, FDatasmithMesh& InMesh, EDSExportLightmapUV InLightmapUV, FDatasmithMesh* InCollisionMesh = nullptr)
: MeshFullPath(InFullPath)
, Mesh(InMesh)
, LightmapUV(InLightmapUV)
, CollisionMesh(InCollisionMesh)
{}
FString MeshFullPath;
FDatasmithMesh& Mesh;
EDSExportLightmapUV LightmapUV;
FDatasmithMesh* CollisionMesh;
};
class FDatasmithMeshExporterImpl
{
public:
bool DoExport(TSharedPtr<IDatasmithMeshElement>& MeshElement, const FDatasmithMeshExporterOptions& ExportOptions);
FString LastError;
private:
bool WriteMeshFile(const FDatasmithMeshExporterOptions& ExporterOptions, FMD5Hash& OutHash);
};
bool FDatasmithMeshExporterImpl::DoExport(TSharedPtr<IDatasmithMeshElement>& MeshElement, const FDatasmithMeshExporterOptions& ExportOptions)
{
FDatasmithMesh& Mesh = ExportOptions.Mesh;
// If the mesh doesn't have a name, use the filename as its name
if (FCString::Strlen(Mesh.GetName()) == 0)
{
Mesh.SetName(*FPaths::GetBaseFilename(ExportOptions.MeshFullPath));
}
FMD5Hash Hash;
if (WriteMeshFile(ExportOptions, Hash))
{
// If no existing MeshElement provided, create one.
if (!MeshElement)
{
MeshElement = FDatasmithSceneFactory::CreateMesh(Mesh.GetName());
}
MeshElement->SetFile(*ExportOptions.MeshFullPath);
MeshElement->SetFileHash(Hash);
FBox3f Extents = Mesh.GetExtents();
float Width = Extents.Max[0] - Extents.Min[0];
float Height = Extents.Max[2] - Extents.Min[2];
float Depth = Extents.Max[1] - Extents.Min[1];
MeshElement->SetDimensions(Mesh.ComputeArea(), Width, Height, Depth);
MeshElement->SetLightmapSourceUV(Mesh.GetLightmapSourceUVChannel());
return true;
}
else
{
UE_LOG(LogDatasmith, Warning, TEXT("Cannot export mesh %s: %s"), Mesh.GetName(), *LastError);
}
return false;
}
FDatasmithMeshExporter::FDatasmithMeshExporter()
: Impl(MakeUnique<FDatasmithMeshExporterImpl>())
{}
FDatasmithMeshExporter::~FDatasmithMeshExporter() = default;
FString GetMeshFilePath(const TCHAR* Filepath, const TCHAR* Filename)
{
FString NormalizedFilepath = Filepath;
FPaths::NormalizeDirectoryName(NormalizedFilepath);
FString NormalizedFilename = Filename;
FPaths::NormalizeFilename(NormalizedFilename);
return FPaths::Combine(*NormalizedFilepath, FPaths::SetExtension(NormalizedFilename, UDatasmithMesh::GetFileExtension()));
}
bool FDatasmithMeshExporterImpl::WriteMeshFile(const FDatasmithMeshExporterOptions& ExporterOptions, FMD5Hash& OutHash)
{
FDatasmithPackedMeshes Pack;
auto PackMeshModels = [&](FDatasmithMesh& Mesh, bool bIsCollisionMesh) -> FDatasmithMeshModels
{
FDatasmithMeshModels Models;
Models.bIsCollisionMesh = bIsCollisionMesh;
Models.MeshName = Mesh.GetName();
Models.SourceModels.Reserve(Mesh.GetLODsCount() + 1);
FMeshDescription& BaseMeshDescription = Models.SourceModels.AddDefaulted_GetRef();
FDatasmithMeshUtils::ToMeshDescription(Mesh, BaseMeshDescription, FDatasmithMeshUtils::GenerateBox);
for (int32 LodIndex = 0; LodIndex < Mesh.GetLODsCount(); ++LodIndex)
{
if (FDatasmithMesh* LodMesh = Mesh.GetLOD(LodIndex))
{
FMeshDescription& LodMeshDescription = Models.SourceModels.AddDefaulted_GetRef();
FDatasmithMeshUtils::ToMeshDescription(*LodMesh, LodMeshDescription, FDatasmithMeshUtils::GenerateBox);
}
}
return Models;
};
Pack.Meshes.Add(PackMeshModels(ExporterOptions.Mesh, false));
if (ExporterOptions.CollisionMesh)
{
Pack.Meshes.Add(PackMeshModels(*ExporterOptions.CollisionMesh, true));
}
TUniquePtr<FArchive> Archive(IFileManager::Get().CreateFileWriter(*ExporterOptions.MeshFullPath));
if (!Archive.IsValid())
{
LastError = FString::Printf(TEXT("Failed writing to file %s"), *ExporterOptions.MeshFullPath);
return false;
}
int32 LegacyMeshCount = 0;
*Archive << LegacyMeshCount; // the legacy importer expect a mesh count on the first bytes. Just in case a new file would end up parsed by the legacy code...
OutHash = Pack.Serialize(*Archive);
return !Archive->IsError();
}
TSharedPtr<IDatasmithMeshElement> FDatasmithMeshExporter::ExportToUObject(const TCHAR* Filepath, const TCHAR* Filename, FDatasmithMesh& Mesh, FDatasmithMesh* CollisionMesh, EDSExportLightmapUV LightmapUV)
{
FString FullPath(GetMeshFilePath(Filepath, Filename));
TSharedPtr<IDatasmithMeshElement> ExportedMeshElement;
FDatasmithMeshExporterOptions ExportOptions(FullPath, Mesh, LightmapUV, CollisionMesh);
Impl->DoExport(ExportedMeshElement, ExportOptions);
return ExportedMeshElement;
}
bool FDatasmithMeshExporter::ExportToUObject(TSharedPtr<IDatasmithMeshElement>& MeshElement, const TCHAR* Filepath, FDatasmithMesh& Mesh, FDatasmithMesh* CollisionMesh, EDSExportLightmapUV LightmapUV)
{
FString FullPath(GetMeshFilePath(Filepath, MeshElement->GetName()));
FDatasmithMeshExporterOptions ExportOptions(FullPath, Mesh, LightmapUV, CollisionMesh);
return Impl->DoExport(MeshElement, ExportOptions);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
bool FDatasmithMeshExporter::ExportCloth(FDatasmithCloth& Cloth, TSharedPtr<IDatasmithClothElement>& ClothElement, const TCHAR* FilePath, const TCHAR* AssetsOutputPath) const // UE_DEPRECATED(5.5, "The experimental Cloth importer is no longer supported.")
{
FString Path = FPaths::SetExtension(FilePath, TEXT(".udscloth"));
FPaths::NormalizeFilename(Path);
FString RelativeFilePath = Path;
if (AssetsOutputPath)
{
FString Tmp(AssetsOutputPath);
Tmp += TEXT('/');
FPaths::MakePathRelativeTo(RelativeFilePath, *Tmp);
}
ClothElement->SetFile(*RelativeFilePath);
TUniquePtr<FArchive> Archive(IFileManager::Get().CreateFileWriter(*Path));
if (!Archive.IsValid())
{
Impl->LastError = FString::Printf(TEXT("Failed writing to file %s"), *Path);
return false;
}
FDatasmithPackedCloths Pack;
FDatasmithClothInfo& Info = Pack.ClothInfos.AddDefaulted_GetRef();
Info.Cloth = Cloth;
FMD5Hash OutHash = Pack.Serialize(*Archive);
return true;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
FString FDatasmithMeshExporter::GetLastError() const
{
return Impl->LastError;
}