Skip to content

Commit

Permalink
Add a Specular Glossiness to Metallic Roughness conversion step to wo…
Browse files Browse the repository at this point in the history
…rkflows. (#36)

* Update glTF.CPP to 1.5.19
Update driectxtex to 2018.6.1.2

* lower case Document

* Updates for future 1809 (rs5).
Add abilibty to apply Draco mesh compression.
Modify SerializeBinary to allow for BufferView's that we don't understand natively.

* Switch to use a new NuGet package for Draco consumption

* Add a Specular Glossiness to Metallic Roughness conversion step to workflows.
Refactored texture loading and packing utils into generic textures utils.
Resolved packing, resizing and compressing texture issues with sRGB and alpha channels.
Update draco build to allow for ARM.

* small fixes

* Update README with latest features.
Switch draco compression to an option; it makes loading slower.

* Tweek the mesh compression options and language.

* Found and fixed a couple of issues with mesh compression.
  • Loading branch information
najadojo authored and robertos committed Aug 3, 2018
1 parent 08e261e commit 37a5498
Show file tree
Hide file tree
Showing 47 changed files with 1,384 additions and 498 deletions.
8 changes: 7 additions & 1 deletion README.md
Expand Up @@ -7,19 +7,25 @@ Additionally the repository includes a command line tool that uses these steps i
[![Build status](https://ci.appveyor.com/api/projects/status/4n8m94mpc03dcuxt?svg=true)](https://ci.appveyor.com/project/robertos/gltf-toolkit)

## Features

The current release includes code for:

- Packing PBR material textures using [DirectXTex](http://github.com/Microsoft/DirectXTex) for use with the [MSFT_packing_occlusionRoughnessMetallic](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_packing_occlusionRoughnessMetallic) and [MSFT_packing_normalRoughnessMetallic](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_packing_normalRoughnessMetallic) extensions.
- Compressing textures as BC3, BC5 and BC7 and generate mip maps using [DirectXTex](http://github.com/Microsoft/DirectXTex) for use with the [MSFT_texture_dds](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds) extension.
- Compressing textures as BC3, BC5 or BC7 and generate mip maps using [DirectXTex](http://github.com/Microsoft/DirectXTex) for use with the [MSFT_texture_dds](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds) extension.
- Removing [KHR_materials_pbrSpecularGlossiness](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness) by converting material prameters to metallic-roughness.
- Mesh compression using [KHR_draco_mesh_compression](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression) extension; this can only be used on 1809 and later and should only be used for assets that are transmitted over the network as load time is increased with compression.
- Merging multiple glTF assets into a asset with multiple levels of detail using the [MSFT_lod](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_lod) extension.
- A command line tool that combines these components to create optimized glTF assets for the Windows Mixed Reality Home
- A UWP compatible Windows Runtime component to perform conversions between GLTF and GLB, as well as optimize assets for Windows Mixed Reality at runtime

## Dependencies

This project consumes the following projects through NuGet packages:

- [Microsoft.glTF.CPP](https://www.nuget.org/packages/Microsoft.glTF.CPP), licensed under the MIT license
- [DirectXTex](http://github.com/Microsoft/DirectXTex), licensed under the MIT license
- [RapidJSON](https://github.com/Tencent/rapidjson/), licensed under the MIT license
- [Draco](https://github.com/google/draco/), licensed under Apache License 2.0

## Building

Expand Down
25 changes: 23 additions & 2 deletions WindowsMRAssetConverter/CommandLine.cpp
Expand Up @@ -15,10 +15,13 @@ const wchar_t * PARAM_SHARE_MATERIALS = L"-share-materials";
const wchar_t * PARAM_MIN_VERSION = L"-min-version";
const wchar_t * PARAM_PLATFORM = L"-platform";
const wchar_t * PARAM_REPLACE_TEXTURES = L"-replace-textures";
const wchar_t * PARAM_COMPRESS_MESHES = L"-compress-meshes";
const wchar_t * PARAM_VALUE_VERSION_1709 = L"1709";
const wchar_t * PARAM_VALUE_VERSION_1803 = L"1803";
const wchar_t * PARAM_VALUE_VERSION_1809 = L"1809";
const wchar_t * PARAM_VALUE_VERSION_RS3 = L"rs3";
const wchar_t * PARAM_VALUE_VERSION_RS4 = L"rs4";
const wchar_t * PARAM_VALUE_VERSION_RS5 = L"rs5";
const wchar_t * PARAM_VALUE_VERSION_LATEST = L"latest";
const wchar_t * PARAM_VALUE_HOLOGRAPHIC = L"holographic";
const wchar_t * PARAM_VALUE_HOLOLENS= L"hololens";
Expand Down Expand Up @@ -60,12 +63,13 @@ void CommandLine::PrintHelp()
<< indent << "[" << std::wstring(PARAM_OUTFILE) << L" <output file path>]" << std::endl
<< indent << "[" << std::wstring(PARAM_TMPDIR) << L" <temporary folder>] - default is the system temp folder for the user" << std::endl
<< indent << "[" << std::wstring(PARAM_PLATFORM) << " <" << PARAM_VALUE_ALL << " | " << PARAM_VALUE_HOLOGRAPHIC << " | " << PARAM_VALUE_DESKTOP << ">] - defaults to " << PARAM_VALUE_DESKTOP << std::endl
<< indent << "[" << std::wstring(PARAM_MIN_VERSION) << " <" << PARAM_VALUE_VERSION_1709 << " | " << PARAM_VALUE_VERSION_1803 << " | " << PARAM_VALUE_VERSION_LATEST << ">] - defaults to " << PARAM_VALUE_VERSION_1709 << std::endl
<< indent << "[" << std::wstring(PARAM_MIN_VERSION) << " <" << PARAM_VALUE_VERSION_1709 << " | " << PARAM_VALUE_VERSION_1803 << " | " << PARAM_VALUE_VERSION_1809 << " | " << PARAM_VALUE_VERSION_LATEST << ">] - defaults to " << PARAM_VALUE_VERSION_1709 << std::endl
<< indent << "[" << std::wstring(PARAM_LOD) << " <path to each lower LOD asset in descending order of quality>]" << std::endl
<< indent << "[" << std::wstring(PARAM_SCREENCOVERAGE) << " <LOD screen coverage values>]" << std::endl
<< indent << "[" << std::wstring(PARAM_SHARE_MATERIALS) << "] - disabled if not present" << std::endl
<< indent << "[" << std::wstring(PARAM_MAXTEXTURESIZE) << " <Max texture size in pixels>] - defaults to 512" << std::endl
<< indent << "[" << std::wstring(PARAM_REPLACE_TEXTURES) << "] - disabled if not present" << std::endl
<< indent << "[" << std::wstring(PARAM_COMPRESS_MESHES) << "] - compress meshes with Draco" << std::endl
<< std::endl
<< "Example:" << std::endl
<< indent << "WindowsMRAssetConverter FileToConvert.gltf "
Expand All @@ -85,7 +89,7 @@ void CommandLine::ParseCommandLineArguments(
int argc, wchar_t *argv[],
std::wstring& inputFilePath, AssetType& inputAssetType, std::wstring& outFilePath, std::wstring& tempDirectory,
std::vector<std::wstring>& lodFilePaths, std::vector<double>& screenCoveragePercentages, size_t& maxTextureSize,
bool& shareMaterials, Version& minVersion, Platform& targetPlatforms, bool& replaceTextures)
bool& shareMaterials, Version& minVersion, Platform& targetPlatforms, bool& replaceTextures, bool& compressMeshes)
{
CommandLineParsingState state = CommandLineParsingState::Initial;

Expand All @@ -103,6 +107,7 @@ void CommandLine::ParseCommandLineArguments(
minVersion = MIN_VERSION_DEFAULT;
targetPlatforms = PLATFORM_DEFAULT;
replaceTextures = false;
compressMeshes = false;

state = CommandLineParsingState::InputRead;

Expand Down Expand Up @@ -157,6 +162,18 @@ void CommandLine::ParseCommandLineArguments(
replaceTextures = true;
state = CommandLineParsingState::InputRead;
}
else if (param == PARAM_COMPRESS_MESHES)
{
if (minVersion >= CommandLine::Version::Version1809)
{
compressMeshes = true;
}
else
{
throw std::invalid_argument("Invalid min version specified with mesh compression; must be at least 1809.");
}
state = CommandLineParsingState::InputRead;
}
else
{
switch (state)
Expand Down Expand Up @@ -190,6 +207,10 @@ void CommandLine::ParseCommandLineArguments(
{
minVersion = Version::Version1803;
}
else if (_wcsicmp(param.c_str(), PARAM_VALUE_VERSION_1809) == 0 || _wcsicmp(param.c_str(), PARAM_VALUE_VERSION_RS5) == 0)
{
minVersion = Version::Version1809;
}
else if (_wcsicmp(param.c_str(), PARAM_VALUE_VERSION_LATEST) == 0)
{
minVersion = Version::Latest;
Expand Down
5 changes: 3 additions & 2 deletions WindowsMRAssetConverter/CommandLine.h
Expand Up @@ -19,7 +19,8 @@ namespace CommandLine
{
Version1709, // Fall Creators Update (RS3)
Version1803, // Spring Creators Update (RS4)
Latest = Version1803
Version1809, // Fall 2018 Update (RS5)
Latest = Version1809
};

void PrintHelp();
Expand All @@ -28,6 +29,6 @@ namespace CommandLine
int argc, wchar_t *argv[],
std::wstring& inputFilePath, AssetType& inputAssetType, std::wstring& outFilePath, std::wstring& tempDirectory,
std::vector<std::wstring>& lodFilePaths, std::vector<double>& screenCoveragePercentages, size_t& maxTextureSize,
bool& sharedMaterials, Version& minVersion, Platform& targetPlatforms, bool& replaceTextures);
bool& sharedMaterials, Version& minVersion, Platform& targetPlatforms, bool& replaceTextures, bool& compressMeshes);
};

68 changes: 39 additions & 29 deletions WindowsMRAssetConverter/WindowsMRAssetConverter.cpp
Expand Up @@ -5,13 +5,16 @@

#include <GLTFSDK/GLTF.h>
#include <GLTFSDK/Deserialize.h>
#include <GLTFSDK/IStreamFactory.h>
#include <GLTFSDK/IStreamWriter.h>
#include <GLTFSDK/GLBResourceReader.h>
#include <GLTFSDK/ExtensionsKHR.h>
#include <GLTFTexturePackingUtils.h>
#include <GLTFTextureCompressionUtils.h>
#include <GLTFSpecularGlossinessUtils.h>
#include <GLTFLODUtils.h>
#include <SerializeBinary.h>
#include <GLBtoGLTF.h>
#include <GLTFMeshCompressionUtils.h>

#include "CommandLine.h"
#include "FileSystem.h"
Expand Down Expand Up @@ -42,53 +45,44 @@ class GLTFStreamReader : public IStreamReader
const std::wstring m_uriBase;
};

class GLBStreamFactory : public Microsoft::glTF::IStreamFactory
class GLBStreamWriter : public Microsoft::glTF::IStreamWriter
{
public:
GLBStreamFactory(const std::wstring& filename) :
m_stream(std::make_shared<std::ofstream>(filename, std::ios_base::binary | std::ios_base::out)),
m_tempStream(std::make_shared<std::stringstream>(std::ios_base::binary | std::ios_base::in | std::ios_base::out))
GLBStreamWriter(const std::wstring& filename) :
m_stream(std::make_shared<std::ofstream>(filename, std::ios_base::binary | std::ios_base::out))
{ }

std::shared_ptr<std::istream> GetInputStream(const std::string&) const override
{
throw std::logic_error("Not implemented");
}

std::shared_ptr<std::ostream> GetOutputStream(const std::string&) const override
{
return m_stream;
}

std::shared_ptr<std::iostream> GetTemporaryStream(const std::string&) const override
{
return m_tempStream;
}
private:
std::shared_ptr<std::ofstream> m_stream;
std::shared_ptr<std::stringstream> m_tempStream;
};

GLTFDocument LoadAndConvertDocumentForWindowsMR(
Document LoadAndConvertDocumentForWindowsMR(
std::wstring& inputFilePath,
AssetType inputAssetType,
const std::wstring& tempDirectory,
size_t maxTextureSize,
TexturePacking packing,
bool processTextures = true,
bool retainOriginalImages = true)
bool processTextures,
bool retainOriginalImages,
bool meshCompression)
{
// Load the document
std::experimental::filesystem::path inputFilePathFS(inputFilePath);
std::wstring inputFileName = inputFilePathFS.filename();
std::wcout << L"Loading input document: " << inputFileName << L"..." << std::endl;

std::string tempDirectoryA(tempDirectory.begin(), tempDirectory.end());

if (inputAssetType == AssetType::GLB)
{
// Convert the GLB to GLTF in the temp directory

std::string inputFilePathA(inputFilePath.begin(), inputFilePath.end());
std::string tempDirectoryA(tempDirectory.begin(), tempDirectory.end());

// inputGltfName is the path to the converted GLTF without extension
std::wstring inputGltfName = inputFilePathFS.stem();
Expand All @@ -100,18 +94,22 @@ GLTFDocument LoadAndConvertDocumentForWindowsMR(
}

auto stream = std::make_shared<std::ifstream>(inputFilePath, std::ios::in);
GLTFDocument document = DeserializeJson(*stream);
Document document = Deserialize(*stream, KHR::GetKHRExtensionDeserializer());

// Get the base path from where to read all the assets

GLTFStreamReader streamReader(FileSystem::GetBasePath(inputFilePath));
auto streamReader = std::make_shared<GLTFStreamReader>(FileSystem::GetBasePath(inputFilePath));

if (processTextures)
{
std::wcout << L"Specular Glossiness conversion..." << std::endl;

// 0. Specular Glossiness conversion
document = GLTFSpecularGlossinessUtils::ConvertMaterials(streamReader, document, tempDirectoryA);

std::wcout << L"Packing textures..." << std::endl;

// 1. Texture Packing
auto tempDirectoryA = std::string(tempDirectory.begin(), tempDirectory.end());
document = GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(streamReader, document, packing, tempDirectoryA);

std::wcout << L"Compressing textures - this can take a few minutes..." << std::endl;
Expand All @@ -120,6 +118,13 @@ GLTFDocument LoadAndConvertDocumentForWindowsMR(
document = GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(streamReader, document, tempDirectoryA, maxTextureSize, retainOriginalImages);
}

if (meshCompression)
{
std::wcout << L"Compressing meshes - this can take a few minutes..." << std::endl;

document = GLTFMeshCompressionUtils::CompressMeshes(streamReader, document, {}, tempDirectoryA);
}

return document;
}

Expand Down Expand Up @@ -148,10 +153,11 @@ int wmain(int argc, wchar_t *argv[])
CommandLine::Version minVersion;
CommandLine::Platform targetPlatforms;
bool replaceTextures;
bool meshCompression = false;

CommandLine::ParseCommandLineArguments(
argc, argv, inputFilePath, inputAssetType, outFilePath, tempDirectory, lodFilePaths, screenCoveragePercentages,
maxTextureSize, shareMaterials, minVersion, targetPlatforms, replaceTextures);
maxTextureSize, shareMaterials, minVersion, targetPlatforms, replaceTextures, meshCompression);

TexturePacking packing = TexturePacking::None;

Expand Down Expand Up @@ -182,25 +188,29 @@ int wmain(int argc, wchar_t *argv[])

compatibleVersionsText += L"Desktop (version 1709+)";
}
else
else if (minVersion == CommandLine::Version::Version1803)
{
compatibleVersionsText += L"Desktop (version 1803+)";
}
else
{
compatibleVersionsText += L"Desktop (version 1809+)";
}
}

std::wcout << L"\nThis will generate an asset compatible with " << compatibleVersionsText << L"\n" << std::endl;

// Load document, and perform steps:
// 1. Texture Packing
// 2. Texture Compression
auto document = LoadAndConvertDocumentForWindowsMR(inputFilePath, inputAssetType, tempDirectory, maxTextureSize, packing, true /* processTextures */, !replaceTextures);
auto document = LoadAndConvertDocumentForWindowsMR(inputFilePath, inputAssetType, tempDirectory, maxTextureSize, packing, true /* processTextures */, !replaceTextures, meshCompression);

// 3. LOD Merging
if (lodFilePaths.size() > 0)
{
std::wcout << L"Merging LODs..." << std::endl;

std::vector<GLTFDocument> lodDocuments;
std::vector<Document> lodDocuments;
std::vector<std::wstring> lodDocumentRelativePaths;
lodDocuments.push_back(document);

Expand All @@ -210,7 +220,7 @@ int wmain(int argc, wchar_t *argv[])
auto lod = lodFilePaths[i];
auto subFolder = FileSystem::CreateSubFolder(tempDirectory, L"lod" + std::to_wstring(i + 1));

lodDocuments.push_back(LoadAndConvertDocumentForWindowsMR(lod, AssetTypeUtils::AssetTypeFromFilePath(lod), subFolder, maxTextureSize, packing, !shareMaterials, !replaceTextures));
lodDocuments.push_back(LoadAndConvertDocumentForWindowsMR(lod, AssetTypeUtils::AssetTypeFromFilePath(lod), subFolder, maxTextureSize, packing, !shareMaterials, !replaceTextures, meshCompression));

lodDocumentRelativePaths.push_back(FileSystem::GetRelativePathWithTrailingSeparator(FileSystem::GetBasePath(inputFilePath), FileSystem::GetBasePath(lod)));
}
Expand Down Expand Up @@ -251,8 +261,8 @@ int wmain(int argc, wchar_t *argv[])
return accessor.componentType;
};

GLTFStreamReader streamReader(FileSystem::GetBasePath(inputFilePath));
SerializeBinary(document, streamReader, std::make_unique<GLBStreamFactory>(outFilePath), accessorConversion);
auto streamReader = std::make_shared<GLTFStreamReader>(FileSystem::GetBasePath(inputFilePath));
SerializeBinary(document, streamReader, std::make_shared<GLBStreamWriter>(outFilePath), accessorConversion);

std::wcout << L"Done!" << std::endl;
std::wcout << L"Output file: " << outFilePath << std::endl;
Expand Down
10 changes: 6 additions & 4 deletions WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj
Expand Up @@ -206,15 +206,17 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets" Condition="Exists('..\packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets')" />
<Import Project="..\packages\directxtex_desktop_2015.2018.2.9.1\build\native\directxtex_desktop_2015.targets" Condition="Exists('..\packages\directxtex_desktop_2015.2018.2.9.1\build\native\directxtex_desktop_2015.targets')" />
<Import Project="..\packages\Microsoft.glTF.CPP.1.3.46.0\build\native\Microsoft.glTF.CPP.targets" Condition="Exists('..\packages\Microsoft.glTF.CPP.1.3.46.0\build\native\Microsoft.glTF.CPP.targets')" />
<Import Project="..\packages\directxtex_desktop_2015.2018.6.1.2\build\native\directxtex_desktop_2015.targets" Condition="Exists('..\packages\directxtex_desktop_2015.2018.6.1.2\build\native\directxtex_desktop_2015.targets')" />
<Import Project="..\packages\Microsoft.glTF.CPP.1.5.19.0\build\native\Microsoft.glTF.CPP.targets" Condition="Exists('..\packages\Microsoft.glTF.CPP.1.5.19.0\build\native\Microsoft.glTF.CPP.targets')" />
<Import Project="..\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets" Condition="Exists('..\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets'))" />
<Error Condition="!Exists('..\packages\directxtex_desktop_2015.2018.2.9.1\build\native\directxtex_desktop_2015.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\directxtex_desktop_2015.2018.2.9.1\build\native\directxtex_desktop_2015.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.glTF.CPP.1.3.46.0\build\native\Microsoft.glTF.CPP.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.glTF.CPP.1.3.46.0\build\native\Microsoft.glTF.CPP.targets'))" />
<Error Condition="!Exists('..\packages\directxtex_desktop_2015.2018.6.1.2\build\native\directxtex_desktop_2015.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\directxtex_desktop_2015.2018.6.1.2\build\native\directxtex_desktop_2015.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.glTF.CPP.1.5.19.0\build\native\Microsoft.glTF.CPP.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.glTF.CPP.1.5.19.0\build\native\Microsoft.glTF.CPP.targets'))" />
<Error Condition="!Exists('..\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets'))" />
</Target>
</Project>

0 comments on commit 37a5498

Please sign in to comment.