You're breathtaking!
This commit is contained in:
20
Source/Engine/ContentExporters/AssetExporters.h
Normal file
20
Source/Engine/ContentExporters/AssetExporters.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_ASSETS_EXPORTER
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
class AssetExporters
|
||||
{
|
||||
public:
|
||||
|
||||
static ExportAssetResult ExportTexture(ExportAssetContext& context);
|
||||
static ExportAssetResult ExportCubeTexture(ExportAssetContext& context);
|
||||
static ExportAssetResult ExportAudioClip(ExportAssetContext& context);
|
||||
static ExportAssetResult ExportModel(ExportAssetContext& context);
|
||||
static ExportAssetResult ExportSkinnedModel(ExportAssetContext& context);
|
||||
};
|
||||
|
||||
#endif
|
||||
136
Source/Engine/ContentExporters/AssetsExportingManager.cpp
Normal file
136
Source/Engine/ContentExporters/AssetsExportingManager.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_ASSETS_EXPORTER
|
||||
|
||||
#include "AssetsExportingManager.h"
|
||||
#include "AssetExporters.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Content/Assets/Texture.h"
|
||||
#include "Engine/Content/Assets/CubeTexture.h"
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
#include "Engine/Content/Assets/SkinnedModel.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Render2D/SpriteAtlas.h"
|
||||
#include "Engine/Audio/AudioClip.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
|
||||
Dictionary<String, ExportAssetFunction> AssetsExportingManager::Exporters;
|
||||
|
||||
class AssetsExportingManagerService : public EngineService
|
||||
{
|
||||
public:
|
||||
|
||||
AssetsExportingManagerService()
|
||||
: EngineService(TEXT("AssetsExportingManager"), -300)
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() override;
|
||||
void Dispose() override;
|
||||
};
|
||||
|
||||
AssetsExportingManagerService AssetsExportingManagerServiceInstance;
|
||||
|
||||
ExportAssetContext::ExportAssetContext(const String& inputPath, const String& outputFolder, void* arg)
|
||||
{
|
||||
InputPath = inputPath;
|
||||
OutputFilename = StringUtils::GetFileNameWithoutExtension(inputPath);
|
||||
OutputFolder = outputFolder;
|
||||
CustomArg = arg;
|
||||
}
|
||||
|
||||
ExportAssetResult ExportAssetContext::Run(const ExportAssetFunction& callback)
|
||||
{
|
||||
ASSERT(callback.IsBinded());
|
||||
|
||||
// Check if input file exists
|
||||
if (!FileSystem::FileExists(InputPath))
|
||||
return ExportAssetResult::MissingInputFile;
|
||||
|
||||
// Load asset (it will perform any required auto-conversions to have valid data)
|
||||
auto asset = Content::LoadAsync<::Asset>(InputPath);
|
||||
if (asset == nullptr || asset->WaitForLoaded())
|
||||
return ExportAssetResult::CannotLoadAsset;
|
||||
Asset = asset;
|
||||
|
||||
// Call action
|
||||
return callback(*this);
|
||||
}
|
||||
|
||||
const ExportAssetFunction* AssetsExportingManager::GetExporter(const String& typeName)
|
||||
{
|
||||
return Exporters.TryGet(typeName);
|
||||
}
|
||||
|
||||
bool AssetsExportingManager::CanExport(const String& inputPath)
|
||||
{
|
||||
AssetInfo info;
|
||||
if (!Content::GetAssetInfo(inputPath, info))
|
||||
return false;
|
||||
|
||||
return Exporters.ContainsKey(info.TypeName);
|
||||
}
|
||||
|
||||
bool AssetsExportingManager::Export(const String& inputPath, const String& outputFolder, void* arg)
|
||||
{
|
||||
AssetInfo info;
|
||||
if (!Content::GetAssetInfo(inputPath, info))
|
||||
{
|
||||
LOG(Warning, "Cannot find asset at location {0}", inputPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
ExportAssetFunction callback;
|
||||
if (!Exporters.TryGet(info.TypeName, callback))
|
||||
{
|
||||
LOG(Warning, "Cannot find exporter for the asset at location {0} (typename: {1})", inputPath, info.TypeName);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Export(callback, inputPath, outputFolder, arg);
|
||||
}
|
||||
|
||||
bool AssetsExportingManager::Export(const ExportAssetFunction& callback, const String& inputPath, const String& outputFolder, void* arg)
|
||||
{
|
||||
LOG(Info, "Exporting asset '{0}' to '{1}'...", inputPath, outputFolder);
|
||||
|
||||
const auto startTime = DateTime::Now();
|
||||
|
||||
ExportAssetContext context(inputPath, outputFolder, arg);
|
||||
const auto result = context.Run(callback);
|
||||
|
||||
if (result != ExportAssetResult::Ok)
|
||||
{
|
||||
LOG(Error, "Asset exporting failed! Result: {0}", ::ToString(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto endTime = DateTime::Now();
|
||||
const auto exportTime = endTime - startTime;
|
||||
LOG(Info, "Asset exported in {0}ms", Math::RoundToInt((float)exportTime.GetTotalMilliseconds()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetsExportingManagerService::Init()
|
||||
{
|
||||
// Initialize with in-build exporters
|
||||
AssetsExportingManager::Exporters.Add(Texture::TypeName, AssetExporters::ExportTexture);
|
||||
AssetsExportingManager::Exporters.Add(SpriteAtlas::TypeName, AssetExporters::ExportTexture);
|
||||
AssetsExportingManager::Exporters.Add(CubeTexture::TypeName, AssetExporters::ExportCubeTexture);
|
||||
AssetsExportingManager::Exporters.Add(AudioClip::TypeName, AssetExporters::ExportAudioClip);
|
||||
AssetsExportingManager::Exporters.Add(Model::TypeName, AssetExporters::ExportModel);
|
||||
AssetsExportingManager::Exporters.Add(SkinnedModel::TypeName, AssetExporters::ExportSkinnedModel);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AssetsExportingManagerService::Dispose()
|
||||
{
|
||||
// Cleanup
|
||||
AssetsExportingManager::Exporters.Clear();
|
||||
AssetsExportingManager::Exporters.SetCapacity(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
60
Source/Engine/ContentExporters/AssetsExportingManager.h
Normal file
60
Source/Engine/ContentExporters/AssetsExportingManager.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_ASSETS_EXPORTER
|
||||
|
||||
#include "Types.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
|
||||
/// <summary>
|
||||
/// Assets Importing service allows to import or create new assets
|
||||
/// </summary>
|
||||
class AssetsExportingManager
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The asset exporting callbacks. Identified by the asset typename.
|
||||
/// </summary>
|
||||
static Dictionary<String, ExportAssetFunction> Exporters;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the asset export for thee given asset typename.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The asset typename.</param>
|
||||
/// <returns>Exporter or null if not found.</returns>
|
||||
static const ExportAssetFunction* GetExporter(const String& typeName);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the asset at the given location can be exports.
|
||||
/// </summary>
|
||||
/// <param name="inputPath">The input asset path.</param>
|
||||
/// <returns>True if can export it, otherwise false.</returns>
|
||||
static bool CanExport(const String& inputPath);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Exports the asset.
|
||||
/// </summary>
|
||||
/// <param name="inputPath">The input asset path.</param>
|
||||
/// <param name="outputFolder">The output path.</param>
|
||||
/// <param name="arg">The custom argument.</param>
|
||||
/// <returns>True if fails, otherwise false.</returns>
|
||||
static bool Export(const String& inputPath, const String& outputFolder, void* arg = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Exports the asset.
|
||||
/// </summary>
|
||||
/// <param name="callback">The custom callback.</param>
|
||||
/// <param name="inputPath">The input asset path.</param>
|
||||
/// <param name="outputFolder">The output path.</param>
|
||||
/// <param name="arg">The custom argument.</param>
|
||||
/// <returns>True if fails, otherwise false.</returns>
|
||||
static bool Export(const ExportAssetFunction& callback, const String& inputPath, const String& outputFolder, void* arg = nullptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
27
Source/Engine/ContentExporters/ContentExporters.Build.cs
Normal file
27
Source/Engine/ContentExporters/ContentExporters.Build.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// Content exporting module.
|
||||
/// </summary>
|
||||
public class ContentExporters : EngineModule
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.PrivateDependencies.Add("AudioTool");
|
||||
options.PrivateDependencies.Add("TextureTool");
|
||||
|
||||
options.PublicDefinitions.Add("COMPILE_WITH_ASSETS_EXPORTER");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
}
|
||||
}
|
||||
49
Source/Engine/ContentExporters/ExportAudio.cpp
Normal file
49
Source/Engine/ContentExporters/ExportAudio.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "AssetExporters.h"
|
||||
|
||||
#if COMPILE_WITH_ASSETS_EXPORTER
|
||||
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Audio/AudioClip.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Tools/AudioTool/OggVorbisEncoder.h"
|
||||
|
||||
ExportAssetResult AssetExporters::ExportAudioClip(ExportAssetContext& context)
|
||||
{
|
||||
#if COMPILE_WITH_OGG_VORBIS
|
||||
// Prepare
|
||||
auto asset = (AudioClip*)context.Asset.Get();
|
||||
auto lock = asset->Storage->LockSafe();
|
||||
auto path = GET_OUTPUT_PATH(context, "ogg");
|
||||
|
||||
// Get audio data
|
||||
Array<byte> rawData;
|
||||
AudioDataInfo rawDataInfo;
|
||||
if (asset->ExtractDataRaw(rawData, rawDataInfo))
|
||||
return ExportAssetResult::CannotLoadData;
|
||||
|
||||
// Encode PCM data
|
||||
BytesContainer encodedData;
|
||||
OggVorbisEncoder encoder;
|
||||
if (encoder.Convert(rawData.Get(), rawDataInfo, encodedData, 1.0f))
|
||||
{
|
||||
LOG(Warning, "Failed to compress audio data");
|
||||
return ExportAssetResult::Error;
|
||||
}
|
||||
|
||||
// Save to file
|
||||
if (File::WriteAllBytes(path, encodedData.Get(), encodedData.Length()))
|
||||
{
|
||||
LOG(Warning, "Failed to save data to file");
|
||||
return ExportAssetResult::Error;
|
||||
}
|
||||
|
||||
return ExportAssetResult::Ok;
|
||||
#else
|
||||
LOG(Warning, "OggVorbis support is disabled.");
|
||||
return ExportAssetResult::Error;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
231
Source/Engine/ContentExporters/ExportModel.cpp
Normal file
231
Source/Engine/ContentExporters/ExportModel.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "AssetExporters.h"
|
||||
|
||||
#if COMPILE_WITH_ASSETS_EXPORTER
|
||||
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
#include "Engine/Content/Assets/SkinnedModel.h"
|
||||
#include "Engine/Serialization/FileWriteStream.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Core/DeleteMe.h"
|
||||
|
||||
ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
|
||||
{
|
||||
// Prepare
|
||||
auto asset = (Model*)context.Asset.Get();
|
||||
auto lock = asset->Storage->LockSafe();
|
||||
auto path = GET_OUTPUT_PATH(context, "obj");
|
||||
const int32 lodIndex = 0;
|
||||
|
||||
// Fetch chunk with data
|
||||
const auto chunkIndex = MODEL_LOD_TO_CHUNK_INDEX(lodIndex);
|
||||
if (asset->LoadChunk(chunkIndex))
|
||||
return ExportAssetResult::CannotLoadData;
|
||||
const auto chunk = asset->GetChunk(chunkIndex);
|
||||
if (!chunk)
|
||||
return ExportAssetResult::CannotLoadData;
|
||||
|
||||
MemoryReadStream stream(chunk->Get(), chunk->Size());
|
||||
FileWriteStream* output = FileWriteStream::Open(path);
|
||||
if (output == nullptr)
|
||||
return ExportAssetResult::Error;
|
||||
DeleteMe<FileWriteStream> outputDeleteMe(output);
|
||||
|
||||
const auto name = StringUtils::GetFileNameWithoutExtension(asset->GetPath()).ToStringAnsi();
|
||||
output->WriteTextFormatted("# Exported model {0}\n", name.Get());
|
||||
|
||||
// Extract all meshes
|
||||
const auto& lod = asset->LODs[lodIndex];
|
||||
int32 vertexStart = 1; // OBJ counts vertices from 1 not from 0
|
||||
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
|
||||
{
|
||||
auto& mesh = lod.Meshes[meshIndex];
|
||||
|
||||
// #MODEL_DATA_FORMAT_USAGE
|
||||
uint32 vertices;
|
||||
stream.ReadUint32(&vertices);
|
||||
uint32 triangles;
|
||||
stream.ReadUint32(&triangles);
|
||||
uint32 indicesCount = triangles * 3;
|
||||
bool use16BitIndexBuffer = indicesCount <= MAX_uint16;
|
||||
uint32 ibStride = use16BitIndexBuffer ? sizeof(uint16) : sizeof(uint32);
|
||||
if (vertices == 0 || triangles == 0)
|
||||
return ExportAssetResult::Error;
|
||||
auto vb0 = stream.Read<VB0ElementType>(vertices);
|
||||
auto vb1 = stream.Read<VB1ElementType>(vertices);
|
||||
bool hasColors = stream.ReadBool();
|
||||
VB2ElementType18* vb2 = nullptr;
|
||||
if (hasColors)
|
||||
{
|
||||
vb2 = stream.Read<VB2ElementType18>(vertices);
|
||||
}
|
||||
auto ib = stream.Read<byte>(indicesCount * ibStride);
|
||||
|
||||
output->WriteTextFormatted("# Mesh {0}\n", meshIndex);
|
||||
|
||||
for (uint32 i = 0; i < vertices; i++)
|
||||
{
|
||||
auto v = vb0[i].Position;
|
||||
output->WriteTextFormatted("v {0} {1} {2}\n", v.X, v.Y, v.Z);
|
||||
}
|
||||
|
||||
output->WriteChar('\n');
|
||||
|
||||
for (uint32 i = 0; i < vertices; i++)
|
||||
{
|
||||
auto v = vb1[i].TexCoord;
|
||||
output->WriteTextFormatted("vt {0} {1}\n", ConvertHalfToFloat(v.X), ConvertHalfToFloat(v.Y));
|
||||
}
|
||||
|
||||
output->WriteChar('\n');
|
||||
|
||||
for (uint32 i = 0; i < vertices; i++)
|
||||
{
|
||||
auto v = vb1[i].Normal.ToVector3() * 2.0f - 1.0f;
|
||||
output->WriteTextFormatted("vn {0} {1} {2}\n", v.X, v.Y, v.Z);
|
||||
}
|
||||
|
||||
output->WriteChar('\n');
|
||||
|
||||
if (use16BitIndexBuffer)
|
||||
{
|
||||
auto t = (uint16*)ib;
|
||||
for (uint32 i = 0; i < triangles; i++)
|
||||
{
|
||||
auto i0 = vertexStart + *t++;
|
||||
auto i1 = vertexStart + *t++;
|
||||
auto i2 = vertexStart + *t++;
|
||||
output->WriteTextFormatted("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto t = (uint32*)ib;
|
||||
for (uint32 i = 0; i < triangles; i++)
|
||||
{
|
||||
auto i0 = vertexStart + *t++;
|
||||
auto i1 = vertexStart + *t++;
|
||||
auto i2 = vertexStart + *t++;
|
||||
output->WriteTextFormatted("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2);
|
||||
}
|
||||
}
|
||||
|
||||
output->WriteChar('\n');
|
||||
|
||||
vertexStart += vertices;
|
||||
}
|
||||
|
||||
if (output->HasError())
|
||||
return ExportAssetResult::Error;
|
||||
|
||||
return ExportAssetResult::Ok;
|
||||
}
|
||||
|
||||
ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context)
|
||||
{
|
||||
// Prepare
|
||||
auto asset = (SkinnedModel*)context.Asset.Get();
|
||||
auto lock = asset->Storage->LockSafe();
|
||||
auto path = GET_OUTPUT_PATH(context, "obj");
|
||||
const int32 lodIndex = 0;
|
||||
|
||||
// Fetch chunk with data
|
||||
const auto chunkIndex = 1;
|
||||
if (asset->LoadChunk(chunkIndex))
|
||||
return ExportAssetResult::CannotLoadData;
|
||||
const auto chunk = asset->GetChunk(chunkIndex);
|
||||
if (!chunk)
|
||||
return ExportAssetResult::CannotLoadData;
|
||||
|
||||
MemoryReadStream stream(chunk->Get(), chunk->Size());
|
||||
FileWriteStream* output = FileWriteStream::Open(path);
|
||||
if (output == nullptr)
|
||||
return ExportAssetResult::Error;
|
||||
DeleteMe<FileWriteStream> outputDeleteMe(output);
|
||||
|
||||
const auto name = StringUtils::GetFileNameWithoutExtension(asset->GetPath()).ToStringAnsi();
|
||||
output->WriteTextFormatted("# Exported model {0}\n", name.Get());
|
||||
|
||||
// Extract all meshes
|
||||
const auto& lod = asset->LODs[lodIndex];
|
||||
int32 vertexStart = 1; // OBJ counts vertices from 1 not from 0
|
||||
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
|
||||
{
|
||||
auto& mesh = lod.Meshes[meshIndex];
|
||||
|
||||
// #MODEL_DATA_FORMAT_USAGE
|
||||
uint32 vertices;
|
||||
stream.ReadUint32(&vertices);
|
||||
uint32 triangles;
|
||||
stream.ReadUint32(&triangles);
|
||||
uint32 indicesCount = triangles * 3;
|
||||
bool use16BitIndexBuffer = indicesCount <= MAX_uint16;
|
||||
uint32 ibStride = use16BitIndexBuffer ? sizeof(uint16) : sizeof(uint32);
|
||||
if (vertices == 0 || triangles == 0)
|
||||
return ExportAssetResult::Error;
|
||||
auto vb0 = stream.Read<VB0SkinnedElementType>(vertices);
|
||||
auto ib = stream.Read<byte>(indicesCount * ibStride);
|
||||
|
||||
output->WriteTextFormatted("# Mesh {0}\n", meshIndex);
|
||||
|
||||
for (uint32 i = 0; i < vertices; i++)
|
||||
{
|
||||
auto v = vb0[i].Position;
|
||||
output->WriteTextFormatted("v {0} {1} {2}\n", v.X, v.Y, v.Z);
|
||||
}
|
||||
|
||||
output->WriteChar('\n');
|
||||
|
||||
for (uint32 i = 0; i < vertices; i++)
|
||||
{
|
||||
auto v = vb0[i].TexCoord;
|
||||
output->WriteTextFormatted("vt {0} {1}\n", ConvertHalfToFloat(v.X), ConvertHalfToFloat(v.Y));
|
||||
}
|
||||
|
||||
output->WriteChar('\n');
|
||||
|
||||
for (uint32 i = 0; i < vertices; i++)
|
||||
{
|
||||
auto v = vb0[i].Normal.ToVector3() * 2.0f - 1.0f;
|
||||
output->WriteTextFormatted("vn {0} {1} {2}\n", v.X, v.Y, v.Z);
|
||||
}
|
||||
|
||||
output->WriteChar('\n');
|
||||
|
||||
if (use16BitIndexBuffer)
|
||||
{
|
||||
auto t = (uint16*)ib;
|
||||
for (uint32 i = 0; i < triangles; i++)
|
||||
{
|
||||
auto i0 = vertexStart + *t++;
|
||||
auto i1 = vertexStart + *t++;
|
||||
auto i2 = vertexStart + *t++;
|
||||
output->WriteTextFormatted("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto t = (uint32*)ib;
|
||||
for (uint32 i = 0; i < triangles; i++)
|
||||
{
|
||||
auto i0 = vertexStart + *t++;
|
||||
auto i1 = vertexStart + *t++;
|
||||
auto i2 = vertexStart + *t++;
|
||||
output->WriteTextFormatted("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2);
|
||||
}
|
||||
}
|
||||
|
||||
output->WriteChar('\n');
|
||||
|
||||
vertexStart += vertices;
|
||||
}
|
||||
|
||||
if (output->HasError())
|
||||
return ExportAssetResult::Error;
|
||||
|
||||
return ExportAssetResult::Ok;
|
||||
}
|
||||
|
||||
#endif
|
||||
119
Source/Engine/ContentExporters/ExportTexture.cpp
Normal file
119
Source/Engine/ContentExporters/ExportTexture.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_ASSETS_EXPORTER
|
||||
|
||||
#include "AssetExporters.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Content/Assets/CubeTexture.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||
|
||||
ExportAssetResult AssetExporters::ExportTexture(ExportAssetContext& context)
|
||||
{
|
||||
// Prepare
|
||||
auto asset = (TextureBase*)context.Asset.Get();
|
||||
auto lock = asset->Storage->LockSafe();
|
||||
auto path = GET_OUTPUT_PATH(context, "png");
|
||||
|
||||
// Load the top mip data
|
||||
const int32 chunkIndex = 0;
|
||||
if (asset->LoadChunk(chunkIndex))
|
||||
return ExportAssetResult::CannotLoadData;
|
||||
BytesContainer data;
|
||||
asset->GetMipData(0, data);
|
||||
if (data.IsInvalid())
|
||||
return ExportAssetResult::Error;
|
||||
|
||||
// Peek image description
|
||||
const auto format = asset->Format();
|
||||
const int32 width = asset->Width();
|
||||
const int32 height = asset->Height();
|
||||
uint32 rowPitch, slicePitch;
|
||||
RenderTools::ComputePitch(format, width, height, rowPitch, slicePitch);
|
||||
|
||||
// Setup texture data
|
||||
TextureData textureData;
|
||||
textureData.Width = width;
|
||||
textureData.Height = height;
|
||||
textureData.Depth = 1;
|
||||
textureData.Format = format;
|
||||
textureData.Items.Resize(1);
|
||||
auto& item = textureData.Items[0];
|
||||
item.Mips.Resize(1);
|
||||
auto& mip = item.Mips[0];
|
||||
mip.RowPitch = rowPitch;
|
||||
mip.DepthPitch = slicePitch;
|
||||
mip.Lines = height;
|
||||
mip.Data.Link(data.Get(), slicePitch);
|
||||
|
||||
// Export to file
|
||||
if (TextureTool::ExportTexture(path, textureData))
|
||||
{
|
||||
return ExportAssetResult::Error;
|
||||
}
|
||||
|
||||
return ExportAssetResult::Ok;
|
||||
}
|
||||
|
||||
ExportAssetResult AssetExporters::ExportCubeTexture(ExportAssetContext& context)
|
||||
{
|
||||
// Prepare
|
||||
auto asset = (CubeTexture*)context.Asset.Get();
|
||||
auto lock = asset->Storage->LockSafe();
|
||||
auto path = GET_OUTPUT_PATH(context, "dds");
|
||||
|
||||
// Load the asset data
|
||||
if (asset->LoadChunks(ALL_ASSET_CHUNKS))
|
||||
return ExportAssetResult::CannotLoadData;
|
||||
|
||||
// Peek image description
|
||||
const auto format = asset->Format();
|
||||
const int32 width = asset->Width();
|
||||
const int32 height = asset->Height();
|
||||
const int32 mipLevels = asset->StreamingTexture()->TotalMipLevels();
|
||||
const int32 arraySize = 6;
|
||||
|
||||
// Setup texture data
|
||||
TextureData textureData;
|
||||
textureData.Width = width;
|
||||
textureData.Height = height;
|
||||
textureData.Depth = 1;
|
||||
textureData.Format = format;
|
||||
textureData.Items.Resize(arraySize);
|
||||
for (int32 arrayIndex = 0; arrayIndex < 6; arrayIndex++)
|
||||
{
|
||||
auto& item = textureData.Items[arrayIndex];
|
||||
item.Mips.Resize(mipLevels);
|
||||
for (int32 mipIndex = 0; mipIndex < mipLevels; mipIndex++)
|
||||
{
|
||||
auto& mip = item.Mips[mipIndex];
|
||||
|
||||
const int32 mipWidth = Math::Max(1, width >> mipIndex);
|
||||
const int32 mipHeight = Math::Max(1, height >> mipIndex);
|
||||
uint32 rowPitch, slicePitch;
|
||||
RenderTools::ComputePitch(format, mipWidth, mipHeight, rowPitch, slicePitch);
|
||||
|
||||
mip.RowPitch = rowPitch;
|
||||
mip.DepthPitch = slicePitch;
|
||||
mip.Lines = mipHeight;
|
||||
|
||||
BytesContainer wholeMipData;
|
||||
asset->GetMipData(mipIndex, wholeMipData);
|
||||
if (wholeMipData.IsInvalid())
|
||||
return ExportAssetResult::Error;
|
||||
|
||||
mip.Data.Link(wholeMipData.Get() + slicePitch * arrayIndex, slicePitch);
|
||||
}
|
||||
}
|
||||
|
||||
// Export to file
|
||||
if (TextureTool::ExportTexture(path, textureData))
|
||||
{
|
||||
return ExportAssetResult::Error;
|
||||
}
|
||||
|
||||
return ExportAssetResult::Ok;
|
||||
}
|
||||
|
||||
#endif
|
||||
87
Source/Engine/ContentExporters/Types.h
Normal file
87
Source/Engine/ContentExporters/Types.h
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_ASSETS_EXPORTER
|
||||
|
||||
#include "Engine/Content/Config.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Core/Enums.h"
|
||||
#include "Engine/Core/NonCopyable.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
|
||||
class JsonWriter;
|
||||
class ExportAssetContext;
|
||||
|
||||
/// <summary>
|
||||
/// Export asset callback result
|
||||
/// </summary>
|
||||
DECLARE_ENUM_6(ExportAssetResult, Ok, Abort, Error, CannotLoadAsset, MissingInputFile, CannotLoadData);
|
||||
|
||||
/// <summary>
|
||||
/// Create/Import new asset callback function
|
||||
/// </summary>
|
||||
typedef Function<ExportAssetResult(ExportAssetContext&)> ExportAssetFunction;
|
||||
|
||||
/// <summary>
|
||||
/// Exporting asset context structure
|
||||
/// </summary>
|
||||
class ExportAssetContext : public NonCopyable
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The asset reference (prepared by the context to be used by callback).
|
||||
/// </summary>
|
||||
AssetReference<Asset> Asset;
|
||||
|
||||
/// <summary>
|
||||
/// Path of the input file
|
||||
/// </summary>
|
||||
String InputPath;
|
||||
|
||||
/// <summary>
|
||||
/// Recommended output filename
|
||||
/// </summary>
|
||||
String OutputFilename;
|
||||
|
||||
/// <summary>
|
||||
/// Output file directory
|
||||
/// </summary>
|
||||
String OutputFolder;
|
||||
|
||||
/// <summary>
|
||||
/// Custom argument for the importing function
|
||||
/// </summary>
|
||||
void* CustomArg;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExportAssetContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="inputPath">The input path.</param>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="arg">The custom argument.</param>
|
||||
ExportAssetContext(const String& inputPath, const String& outputPath, void* arg);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="ExportAssetContext"/> class.
|
||||
/// </summary>
|
||||
~ExportAssetContext()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Runs the specified callback.
|
||||
/// </summary>
|
||||
/// <param name="callback">The export asset callback.</param>
|
||||
/// <returns>Operation result.</returns>
|
||||
ExportAssetResult Run(const ExportAssetFunction& callback);
|
||||
};
|
||||
|
||||
#define GET_OUTPUT_PATH(context, extension) (context.OutputFolder / context.OutputFilename + TEXT("." extension))
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user