From 37a54981b87931779d18ed2bdae775896fac6e88 Mon Sep 17 00:00:00 2001 From: Jamie Marconi Date: Fri, 3 Aug 2018 12:14:49 -0700 Subject: [PATCH] Add a Specular Glossiness to Metallic Roughness conversion step to workflows. (#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. --- README.md | 8 +- WindowsMRAssetConverter/CommandLine.cpp | 25 +- WindowsMRAssetConverter/CommandLine.h | 5 +- .../WindowsMRAssetConverter.cpp | 68 ++-- .../WindowsMRAssetConverter.vcxproj | 10 +- WindowsMRAssetConverter/packages.config | 5 +- glTF-Toolkit.Test/GLBSerializerTests.cpp | 49 ++- glTF-Toolkit.Test/GLBtoGLTFTests.cpp | 14 +- glTF-Toolkit.Test/GLTFLODUtilsTests.cpp | 57 ++-- .../GLTFTextureCompressionUtilsTests.cpp | 20 +- .../GLTFTexturePackingUtilsTests.cpp | 22 +- glTF-Toolkit.Test/Helpers/TestUtils.h | 6 +- .../Resources/gltf/CubeAsset3D.gltf | 2 +- .../Resources/gltf/CubeWithLOD.gltf | 2 +- glTF-Toolkit.Test/glTF-Toolkit.Test.vcxproj | 8 +- glTF-Toolkit.Test/packages.config | 4 +- .../Assets/3DModels/WaterBottle.glb | Bin 17382364 -> 17382364 bytes glTF-Toolkit.UWP.Test/UWPTest.cs | 8 +- .../glTF-Toolkit.UWP.Test.csproj | 6 +- glTF-Toolkit.UWP/GLTFSerialization.cpp | 10 +- glTF-Toolkit.UWP/GLTFStreams.h | 19 +- glTF-Toolkit.UWP/WindowsMRConversion.cpp | 43 ++- glTF-Toolkit.UWP/WindowsMRConversion.h | 1 + glTF-Toolkit.UWP/glTF-Toolkit.UWP.vcxproj | 10 +- glTF-Toolkit.UWP/packages.config | 5 +- glTF-Toolkit/glTF-Toolkit.vcxproj | 20 +- glTF-Toolkit/glTF-Toolkit.vcxproj.filters | 24 +- glTF-Toolkit/inc/GLBtoGLTF.h | 6 +- glTF-Toolkit/inc/GLTFLODUtils.h | 12 +- glTF-Toolkit/inc/GLTFMeshCompressionUtils.h | 64 ++++ glTF-Toolkit/inc/GLTFSDK.h | 5 +- .../inc/GLTFSpecularGlossinessUtils.h | 40 +++ .../inc/GLTFTextureCompressionUtils.h | 8 +- glTF-Toolkit/inc/GLTFTextureLoadingUtils.h | 25 -- glTF-Toolkit/inc/GLTFTexturePackingUtils.h | 4 +- glTF-Toolkit/inc/GLTFTextureUtils.h | 47 +++ glTF-Toolkit/inc/SerializeBinary.h | 8 +- glTF-Toolkit/packages.config | 5 +- glTF-Toolkit/src/GLBtoGLTF.cpp | 14 +- glTF-Toolkit/src/GLTFLODUtils.cpp | 80 +++-- glTF-Toolkit/src/GLTFMeshCompressionUtils.cpp | 290 ++++++++++++++++++ .../src/GLTFSpecularGlossinessUtils.cpp | 264 ++++++++++++++++ .../src/GLTFTextureCompressionUtils.cpp | 26 +- glTF-Toolkit/src/GLTFTextureLoadingUtils.cpp | 56 ---- glTF-Toolkit/src/GLTFTexturePackingUtils.cpp | 178 ++++------- glTF-Toolkit/src/GLTFTextureUtils.cpp | 118 +++++++ glTF-Toolkit/src/SerializeBinary.cpp | 181 +++++++++-- 47 files changed, 1384 insertions(+), 498 deletions(-) create mode 100644 glTF-Toolkit/inc/GLTFMeshCompressionUtils.h create mode 100644 glTF-Toolkit/inc/GLTFSpecularGlossinessUtils.h delete mode 100644 glTF-Toolkit/inc/GLTFTextureLoadingUtils.h create mode 100644 glTF-Toolkit/inc/GLTFTextureUtils.h create mode 100644 glTF-Toolkit/src/GLTFMeshCompressionUtils.cpp create mode 100644 glTF-Toolkit/src/GLTFSpecularGlossinessUtils.cpp delete mode 100644 glTF-Toolkit/src/GLTFTextureLoadingUtils.cpp create mode 100644 glTF-Toolkit/src/GLTFTextureUtils.cpp diff --git a/README.md b/README.md index 047be4b..8fc6eb4 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,13 @@ 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 @@ -17,9 +21,11 @@ The current release includes code for: ## 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 diff --git a/WindowsMRAssetConverter/CommandLine.cpp b/WindowsMRAssetConverter/CommandLine.cpp index c3dc363..c2cad5e 100644 --- a/WindowsMRAssetConverter/CommandLine.cpp +++ b/WindowsMRAssetConverter/CommandLine.cpp @@ -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"; @@ -60,12 +63,13 @@ void CommandLine::PrintHelp() << indent << "[" << std::wstring(PARAM_OUTFILE) << L" ]" << std::endl << indent << "[" << std::wstring(PARAM_TMPDIR) << L" ] - 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) << " ]" << std::endl << indent << "[" << std::wstring(PARAM_SCREENCOVERAGE) << " ]" << std::endl << indent << "[" << std::wstring(PARAM_SHARE_MATERIALS) << "] - disabled if not present" << std::endl << indent << "[" << std::wstring(PARAM_MAXTEXTURESIZE) << " ] - 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 " @@ -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& lodFilePaths, std::vector& 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; @@ -103,6 +107,7 @@ void CommandLine::ParseCommandLineArguments( minVersion = MIN_VERSION_DEFAULT; targetPlatforms = PLATFORM_DEFAULT; replaceTextures = false; + compressMeshes = false; state = CommandLineParsingState::InputRead; @@ -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) @@ -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; diff --git a/WindowsMRAssetConverter/CommandLine.h b/WindowsMRAssetConverter/CommandLine.h index c9e48cf..bc09c53 100644 --- a/WindowsMRAssetConverter/CommandLine.h +++ b/WindowsMRAssetConverter/CommandLine.h @@ -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(); @@ -28,6 +29,6 @@ namespace CommandLine int argc, wchar_t *argv[], std::wstring& inputFilePath, AssetType& inputAssetType, std::wstring& outFilePath, std::wstring& tempDirectory, std::vector& lodFilePaths, std::vector& screenCoveragePercentages, size_t& maxTextureSize, - bool& sharedMaterials, Version& minVersion, Platform& targetPlatforms, bool& replaceTextures); + bool& sharedMaterials, Version& minVersion, Platform& targetPlatforms, bool& replaceTextures, bool& compressMeshes); }; diff --git a/WindowsMRAssetConverter/WindowsMRAssetConverter.cpp b/WindowsMRAssetConverter/WindowsMRAssetConverter.cpp index 0534638..6426ae2 100644 --- a/WindowsMRAssetConverter/WindowsMRAssetConverter.cpp +++ b/WindowsMRAssetConverter/WindowsMRAssetConverter.cpp @@ -5,13 +5,16 @@ #include #include -#include +#include #include +#include #include #include +#include #include #include #include +#include #include "CommandLine.h" #include "FileSystem.h" @@ -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(filename, std::ios_base::binary | std::ios_base::out)), - m_tempStream(std::make_shared(std::ios_base::binary | std::ios_base::in | std::ios_base::out)) + GLBStreamWriter(const std::wstring& filename) : + m_stream(std::make_shared(filename, std::ios_base::binary | std::ios_base::out)) { } - std::shared_ptr GetInputStream(const std::string&) const override - { - throw std::logic_error("Not implemented"); - } - std::shared_ptr GetOutputStream(const std::string&) const override { return m_stream; } - std::shared_ptr GetTemporaryStream(const std::string&) const override - { - return m_tempStream; - } private: std::shared_ptr m_stream; - std::shared_ptr 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(); @@ -100,18 +94,22 @@ GLTFDocument LoadAndConvertDocumentForWindowsMR( } auto stream = std::make_shared(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(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; @@ -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; } @@ -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; @@ -182,10 +188,14 @@ 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; @@ -193,14 +203,14 @@ int wmain(int argc, wchar_t *argv[]) // 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 lodDocuments; + std::vector lodDocuments; std::vector lodDocumentRelativePaths; lodDocuments.push_back(document); @@ -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))); } @@ -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(outFilePath), accessorConversion); + auto streamReader = std::make_shared(FileSystem::GetBasePath(inputFilePath)); + SerializeBinary(document, streamReader, std::make_shared(outFilePath), accessorConversion); std::wcout << L"Done!" << std::endl; std::wcout << L"Output file: " << outFilePath << std::endl; diff --git a/WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj b/WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj index add75b1..851a0f9 100644 --- a/WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj +++ b/WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj @@ -206,15 +206,17 @@ - - + + + 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}. - - + + + \ No newline at end of file diff --git a/WindowsMRAssetConverter/packages.config b/WindowsMRAssetConverter/packages.config index 05850ae..bc2a591 100644 --- a/WindowsMRAssetConverter/packages.config +++ b/WindowsMRAssetConverter/packages.config @@ -1,6 +1,7 @@  - - + + + \ No newline at end of file diff --git a/glTF-Toolkit.Test/GLBSerializerTests.cpp b/glTF-Toolkit.Test/GLBSerializerTests.cpp index 5c83f43..7811554 100644 --- a/glTF-Toolkit.Test/GLBSerializerTests.cpp +++ b/glTF-Toolkit.Test/GLBSerializerTests.cpp @@ -5,7 +5,7 @@ #include #include "GLTFSDK/IStreamWriter.h" -#include "GLTFSDK/GLTFConstants.h" +#include "GLTFSDK/Constants.h" #include "GLTFSDK/Deserialize.h" #include "GLTFSDK/GLBResourceWriter.h" #include "GLTFSDK/GLBResourceReader.h" @@ -23,31 +23,20 @@ using namespace Microsoft::glTF::Toolkit; namespace Microsoft::glTF::Toolkit::Test { - class InMemoryStreamFactory : public Microsoft::glTF::IStreamFactory + class InMemoryStream : public Microsoft::glTF::IStreamWriter { public: - InMemoryStreamFactory(std::shared_ptr stream) : - m_stream(stream), - m_tempStream(std::make_shared(std::ios_base::app | std::ios_base::binary | std::ios_base::in | std::ios_base::out)) + InMemoryStream(std::shared_ptr stream) : + m_stream(stream) { } - std::shared_ptr GetInputStream(const std::string& uri) const override + std::shared_ptr GetOutputStream(const std::string&) const { - return uri == GLB_BUFFER_ID ? m_tempStream : m_stream; + return m_stream; } - std::shared_ptr GetOutputStream(const std::string& uri) const override - { - return uri == GLB_BUFFER_ID ? m_tempStream : m_stream; - } - - std::shared_ptr GetTemporaryStream(const std::string&) const override - { - return m_tempStream; - } private: std::shared_ptr m_stream; - std::shared_ptr m_tempStream; }; TEST_CLASS(GLBSerializerTests) @@ -60,24 +49,24 @@ namespace Microsoft::glTF::Toolkit::Test return json; } - static std::shared_ptr ImportGLB(const std::shared_ptr& streamReader, const std::shared_ptr& glbStream) + static std::shared_ptr ImportGLB(const std::shared_ptr& streamReader, const std::shared_ptr& glbStream) { - GLBResourceReader resourceReader(*streamReader, glbStream); + GLBResourceReader resourceReader(streamReader, glbStream); auto json = resourceReader.GetJson(); - auto doc = DeserializeJson(json); + auto doc = Deserialize(json); - return std::make_shared(doc); + return std::make_shared(doc); } - static std::shared_ptr ImportGLTF(const std::shared_ptr& streamReader, const std::shared_ptr& stream) + static std::shared_ptr ImportGLTF(const std::shared_ptr& streamReader, const std::shared_ptr& stream) { - GLTFResourceReader resourceReader(*streamReader); + GLTFResourceReader resourceReader(streamReader); auto json = std::string(std::istreambuf_iterator(*stream), std::istreambuf_iterator()); - auto doc = DeserializeJson(json); + auto doc = Deserialize(json); - return std::make_shared(doc); + return std::make_shared(doc); } const char* c_waterBottleJson = "Resources\\gltf\\WaterBottle\\WaterBottle.gltf"; @@ -90,17 +79,17 @@ namespace Microsoft::glTF::Toolkit::Test { // Deserialize input json auto inputJson = std::string(std::istreambuf_iterator(*input), std::istreambuf_iterator()); - auto doc = DeserializeJson(inputJson); + auto doc = Deserialize(inputJson); - // Serialize GLTFDocument to GLB - TestStreamReader streamReader(TestUtils::GetAbsolutePath(c_waterBottleJson)); + // Serialize Document to GLB + auto streamReader = std::make_shared(TestUtils::GetAbsolutePath(c_waterBottleJson)); auto stream = std::make_shared(std::ios_base::app | std::ios_base::binary | std::ios_base::in | std::ios_base::out); - SerializeBinary(doc, streamReader, std::make_unique(stream)); + SerializeBinary(doc, streamReader, std::make_shared(stream)); // Deserialize the GLB again auto glbReader = std::make_unique(streamReader, stream); auto outputJson = glbReader->GetJson(); - auto outputDoc = DeserializeJson(outputJson); + auto outputDoc = Deserialize(outputJson); // Check some structural elements Assert::AreEqual(doc.nodes.Size(), outputDoc.nodes.Size()); diff --git a/glTF-Toolkit.Test/GLBtoGLTFTests.cpp b/glTF-Toolkit.Test/GLBtoGLTFTests.cpp index dd72477..c2dbcf4 100644 --- a/glTF-Toolkit.Test/GLBtoGLTFTests.cpp +++ b/glTF-Toolkit.Test/GLBtoGLTFTests.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include #include @@ -37,9 +37,9 @@ namespace Microsoft::glTF::Toolkit::Test } // Setup a GLTF document with 3 bufferviews and 2 images - GLTFDocument setupGLBDocument1() + Document setupGLBDocument1() { - GLTFDocument glbDoc("0", {}); + Document glbDoc; Scene sc; sc.id = "0"; glbDoc.scenes.Append(std::move(sc)); Accessor acc0; acc0.bufferViewId = "0"; acc0.byteOffset = 0; acc0.id = "0"; @@ -88,7 +88,7 @@ namespace Microsoft::glTF::Toolkit::Test { TEST_METHOD(GLBtoGLTF_NoImagesJSON) { - GLTFDocument glbDoc("0", {}); + Document glbDoc; Scene s1; s1.id = "0"; glbDoc.scenes.Append(std::move(s1)); Accessor acc; acc.bufferViewId = "0"; acc.byteOffset = 36; acc.id = "0"; @@ -112,7 +112,7 @@ namespace Microsoft::glTF::Toolkit::Test { glbDoc.images.Append(std::move(img)); }); - GLTFDocument expectedGLTFDoc("0", {}); + Document expectedGLTFDoc; Scene s2; s2.id = "0"; expectedGLTFDoc.scenes.Append(std::move(s2)); auto actualGLTFDoc = GLBToGLTF::CreateGLTFDocument(glbDoc, "name"); @@ -125,7 +125,7 @@ namespace Microsoft::glTF::Toolkit::Test TEST_METHOD(GLBtoGLTF_ImagesWithOffsetJSON) { - GLTFDocument glbDoc("0", {}); + Document glbDoc; Scene sc; sc.id = "0"; glbDoc.scenes.Append(std::move(sc)); Accessor acc0; acc0.bufferViewId = "0"; acc0.byteOffset = 0; acc0.id = "0"; @@ -161,7 +161,7 @@ namespace Microsoft::glTF::Toolkit::Test }); auto actualGLTFDoc = GLBToGLTF::CreateGLTFDocument(glbDoc, "test"); - GLTFDocument expectedGLTFDoc("0", {}); + Document expectedGLTFDoc; Accessor exp_acc0; exp_acc0.bufferViewId = "0"; exp_acc0.byteOffset = 0; exp_acc0.id = "0"; Accessor exp_acc1; exp_acc1.bufferViewId = "1"; exp_acc1.byteOffset = 4; exp_acc1.id = "3"; expectedGLTFDoc.accessors.Append(std::move(exp_acc0)); diff --git a/glTF-Toolkit.Test/GLTFLODUtilsTests.cpp b/glTF-Toolkit.Test/GLTFLODUtilsTests.cpp index cd2e723..cf3c8f9 100644 --- a/glTF-Toolkit.Test/GLTFLODUtilsTests.cpp +++ b/glTF-Toolkit.Test/GLTFLODUtilsTests.cpp @@ -5,12 +5,13 @@ #include #include "GLTFSDK/IStreamWriter.h" -#include "GLTFSDK/GLTFConstants.h" +#include "GLTFSDK/Constants.h" #include "GLTFSDK/Serialize.h" #include "GLTFSDK/Deserialize.h" #include "GLTFSDK/GLBResourceReader.h" #include "GLTFSDK/GLTFResourceWriter.h" #include "GLTFSDK/RapidJsonUtils.h" +#include "GLTFSDK/ExtensionsKHR.h" #include "GLTFLODUtils.h" @@ -25,7 +26,7 @@ namespace Microsoft::glTF::Toolkit::Test { TEST_CLASS(GLTFLODUtilsTests) { - static void CheckGLTFLODNodeCountAgainstOriginal(GLTFDocument& doc, GLTFDocument& docWLod, size_t lodCount) + static void CheckGLTFLODNodeCountAgainstOriginal(Document& doc, Document& docWLod, size_t lodCount) { // All elements in the lod'd doc should be double the original Assert::IsTrue(doc.buffers.Size() * lodCount == docWLod.buffers.Size()); @@ -48,9 +49,9 @@ namespace Microsoft::glTF::Toolkit::Test auto readwriter = std::make_shared(); try { - GLTFResourceReader resourceReader(*readwriter); + GLTFResourceReader resourceReader(readwriter); auto inputJson = std::string(std::istreambuf_iterator(*input), std::istreambuf_iterator()); - auto doc = DeserializeJson(inputJson); + auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer()); auto lods = GLTFLODUtils::ParseDocumentNodeLODs(doc); Assert::IsTrue(GLTFLODUtils::NumberOfNodeLODLevels(doc, lods) == expectedNumberOfLods); @@ -63,14 +64,14 @@ namespace Microsoft::glTF::Toolkit::Test } } - static std::shared_ptr ImportGLTF(const std::shared_ptr& streamReader, const std::shared_ptr& stream) + static std::shared_ptr ImportGLTF(const std::shared_ptr& streamReader, const std::shared_ptr& stream) { - GLTFResourceReader resourceReader(*streamReader); + GLTFResourceReader resourceReader(streamReader); auto json = std::string(std::istreambuf_iterator(*stream), std::istreambuf_iterator()); - auto doc = DeserializeJson(json); + auto doc = Deserialize(json, KHR::GetKHRExtensionDeserializer()); - return std::make_shared(doc); + return std::make_shared(doc); } const char* c_cubeAsset3DJson = "Resources\\gltf\\cubeAsset3D.gltf"; @@ -92,17 +93,17 @@ namespace Microsoft::glTF::Toolkit::Test try { // Deserialize input json - GLTFResourceReader resourceReader(*readwriter); + GLTFResourceReader resourceReader(readwriter); auto inputJson = std::string(std::istreambuf_iterator(*input), std::istreambuf_iterator()); - auto doc = DeserializeJson(inputJson); + auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer()); - std::vector docs; + std::vector docs; docs.push_back(doc); docs.push_back(doc); auto newlodgltfDoc = GLTFLODUtils::MergeDocumentsAsLODs(docs); - // Serialize GLTFDocument back to json - auto outputJson = Serialize(newlodgltfDoc); + // Serialize Document back to json + auto outputJson = Serialize(newlodgltfDoc, KHR::GetKHRExtensionSerializer()); CheckGLTFLODNodeCountAgainstOriginal(doc, newlodgltfDoc, 2); @@ -146,11 +147,11 @@ namespace Microsoft::glTF::Toolkit::Test try { // Deserialize input json - GLTFResourceReader resourceReader(*readwriter); + GLTFResourceReader resourceReader(readwriter); auto inputJson = std::string(std::istreambuf_iterator(*input), std::istreambuf_iterator()); - auto doc = DeserializeJson(inputJson); + auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer()); - std::vector docs; + std::vector docs; docs.push_back(doc); docs.push_back(doc); docs.push_back(doc); @@ -188,8 +189,8 @@ namespace Microsoft::glTF::Toolkit::Test Assert::IsTrue(containsLOD2RootNode); Assert::IsTrue(containsLOD2PolyNode); - // Serialize GLTFDocument back to json - auto outputJson = Serialize(newlodgltfDoc); + // Serialize Document back to json + auto outputJson = Serialize(newlodgltfDoc, KHR::GetKHRExtensionSerializer()); } catch (std::exception ex) { @@ -206,11 +207,11 @@ namespace Microsoft::glTF::Toolkit::Test try { // Deserialize input json - GLTFResourceReader resourceReader(*readwriter); + GLTFResourceReader resourceReader(readwriter); auto inputJson = std::string(std::istreambuf_iterator(*input), std::istreambuf_iterator()); - auto doc = DeserializeJson(inputJson); + auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer()); - std::vector docs; + std::vector docs; docs.push_back(doc); docs.push_back(doc); docs.push_back(doc); @@ -240,8 +241,8 @@ namespace Microsoft::glTF::Toolkit::Test Assert::IsTrue(rootNodeContainsCoverage); - // Serialize GLTFDocument back to json - auto outputJson = Serialize(newlodgltfDoc); + // Serialize Document back to json + auto outputJson = Serialize(newlodgltfDoc, KHR::GetKHRExtensionSerializer()); } catch (std::exception ex) { @@ -287,13 +288,13 @@ namespace Microsoft::glTF::Toolkit::Test try { // Deserialize input json - GLTFResourceReader resourceReader(*readwriter); + GLTFResourceReader resourceReader(readwriter); auto inputJson = std::string(std::istreambuf_iterator(*input), std::istreambuf_iterator()); - auto doc = DeserializeJson(inputJson); + auto doc = Deserialize(inputJson, KHR::GetKHRExtensionDeserializer()); - // Serialize GLTFDocument back to json - auto outputJson = Serialize(doc); - auto outputDoc = DeserializeJson(outputJson); + // Serialize Document back to json + auto outputJson = Serialize(doc, KHR::GetKHRExtensionSerializer()); + auto outputDoc = Deserialize(outputJson, KHR::GetKHRExtensionDeserializer()); // Compare input and output GLTFDocuments Assert::AreNotSame(doc == outputDoc, true, L"Input gltf and output gltf are not equal"); diff --git a/glTF-Toolkit.Test/GLTFTextureCompressionUtilsTests.cpp b/glTF-Toolkit.Test/GLTFTextureCompressionUtilsTests.cpp index 4663fb6..ef5b5bb 100644 --- a/glTF-Toolkit.Test/GLTFTextureCompressionUtilsTests.cpp +++ b/glTF-Toolkit.Test/GLTFTextureCompressionUtilsTests.cpp @@ -4,7 +4,7 @@ #include #include "GLTFSDK/IStreamWriter.h" -#include "GLTFSDK/GLTFConstants.h" +#include "GLTFSDK/Constants.h" #include "GLTFSDK/Serialize.h" #include "GLTFSDK/Deserialize.h" #include "GLTFSDK/GLBResourceReader.h" @@ -63,7 +63,7 @@ namespace Microsoft::glTF::Toolkit::Test // This asset has all textures TestUtils::LoadAndExecuteGLTFTest(c_waterBottleORMJson, [](auto doc, auto path) { - auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(TestStreamReader(path), doc, doc.textures.Get("0"), TextureCompression::None, ""); + auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(std::make_shared(path), doc, doc.textures.Get("0"), TextureCompression::None, ""); // Check that nothing changed Assert::IsTrue(doc == compressedDoc); @@ -78,7 +78,7 @@ namespace Microsoft::glTF::Toolkit::Test auto maxTextureSize = std::numeric_limits::max(); auto generateMipMaps = false; auto retainOriginalImages = true; - auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(TestStreamReader(path), doc, doc.textures.Get("0"), TextureCompression::BC3, "", maxTextureSize, generateMipMaps, retainOriginalImages); + auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(std::make_shared(path), doc, doc.textures.Get("0"), TextureCompression::BC3, "", maxTextureSize, generateMipMaps, retainOriginalImages); auto originalTexture = doc.textures.Get("0"); auto compressedTexture = compressedDoc.textures.Get("0"); @@ -117,7 +117,7 @@ namespace Microsoft::glTF::Toolkit::Test auto maxTextureSize = std::numeric_limits::max(); auto generateMipMaps = false; auto retainOriginalImages = false; - auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(TestStreamReader(path), doc, doc.textures.Get("0"), TextureCompression::BC3, "", maxTextureSize, generateMipMaps, retainOriginalImages); + auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(std::make_shared(path), doc, doc.textures.Get("0"), TextureCompression::BC3, "", maxTextureSize, generateMipMaps, retainOriginalImages); auto originalTexture = doc.textures.Get("0"); auto compressedTexture = compressedDoc.textures.Get("0"); @@ -159,7 +159,7 @@ namespace Microsoft::glTF::Toolkit::Test auto maxTextureSize = std::numeric_limits::max(); auto generateMipMaps = true; auto retainOriginalImages = true; - auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(TestStreamReader(path), doc, doc.textures.Get("0"), TextureCompression::BC7, "", maxTextureSize, generateMipMaps, retainOriginalImages); + auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(std::make_shared(path), doc, doc.textures.Get("0"), TextureCompression::BC7, "", maxTextureSize, generateMipMaps, retainOriginalImages); auto originalTexture = doc.textures.Get("0"); auto compressedTexture = compressedDoc.textures.Get("0"); @@ -197,7 +197,7 @@ namespace Microsoft::glTF::Toolkit::Test { auto maxTextureSize = std::numeric_limits::max(); auto retainOriginalImages = true; - auto compressedDoc = GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(TestStreamReader(path), doc, "", maxTextureSize, retainOriginalImages); + auto compressedDoc = GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(std::make_shared(path), doc, "", maxTextureSize, retainOriginalImages); // Check that the materials and textures have not been replaced // Check that the textures has not been replaced @@ -211,12 +211,12 @@ namespace Microsoft::glTF::Toolkit::Test auto compressedMaterial = compressedDoc.materials.Get("0"); // Check that all relevant textures now have the extension - Assert::IsTrue(doc.textures.Get(originalMaterial.metallicRoughness.baseColorTextureId).extensions.size() + 1 == compressedDoc.textures.Get(compressedMaterial.metallicRoughness.baseColorTextureId).extensions.size()); - Assert::IsTrue(doc.textures.Get(originalMaterial.emissiveTextureId).extensions.size() + 1 == compressedDoc.textures.Get(compressedMaterial.emissiveTextureId).extensions.size()); + Assert::IsTrue(doc.textures.Get(originalMaterial.metallicRoughness.baseColorTexture.textureId).extensions.size() + 1 == compressedDoc.textures.Get(compressedMaterial.metallicRoughness.baseColorTexture.textureId).extensions.size()); + Assert::IsTrue(doc.textures.Get(originalMaterial.emissiveTexture.textureId).extensions.size() + 1 == compressedDoc.textures.Get(compressedMaterial.emissiveTexture.textureId).extensions.size()); // TODO: read the WMR (MSFT_packing...) textures as well // Check the new extension is not empty - auto ddsExtension = compressedDoc.textures.Get(compressedMaterial.emissiveTextureId).extensions.at(std::string(EXTENSION_MSFT_TEXTURE_DDS)); + auto ddsExtension = compressedDoc.textures.Get(compressedMaterial.emissiveTexture.textureId).extensions.at(std::string(EXTENSION_MSFT_TEXTURE_DDS)); Assert::IsFalse(ddsExtension.empty()); // Check the new extension contains a DDS image @@ -241,7 +241,7 @@ namespace Microsoft::glTF::Toolkit::Test auto maxTextureSize = std::numeric_limits::max(); auto generateMipMaps = false; auto retainOriginalImages = true; - auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(TestStreamReader(path), doc, doc.textures.Get("0"), TextureCompression::BC3, "", maxTextureSize, generateMipMaps, retainOriginalImages); + auto compressedDoc = GLTFTextureCompressionUtils::CompressTextureAsDDS(std::make_shared(path), doc, doc.textures.Get("0"), TextureCompression::BC3, "", maxTextureSize, generateMipMaps, retainOriginalImages); auto originalUri = compressedDoc.images.Get("0").uri; auto compressedUri = compressedDoc.images.Get("1").uri; diff --git a/glTF-Toolkit.Test/GLTFTexturePackingUtilsTests.cpp b/glTF-Toolkit.Test/GLTFTexturePackingUtilsTests.cpp index db8b311..e7923e6 100644 --- a/glTF-Toolkit.Test/GLTFTexturePackingUtilsTests.cpp +++ b/glTF-Toolkit.Test/GLTFTexturePackingUtilsTests.cpp @@ -4,7 +4,7 @@ #include #include "GLTFSDK/IStreamWriter.h" -#include "GLTFSDK/GLTFConstants.h" +#include "GLTFSDK/Constants.h" #include "GLTFSDK/Serialize.h" #include "GLTFSDK/Deserialize.h" #include "GLTFSDK/GLBResourceReader.h" @@ -36,7 +36,7 @@ namespace Microsoft::glTF::Toolkit::Test TestUtils::LoadAndExecuteGLTFTest(gltfRelativePath, [packing](auto doc, auto path) { auto material = doc.materials.Elements()[0]; - auto packedDoc = GLTFTexturePackingUtils::PackMaterialForWindowsMR(TestStreamReader(path), doc, material, packing, ""); + auto packedDoc = GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::make_shared(path), doc, material, packing, ""); auto packedMaterial = packedDoc.materials.Elements()[0]; // Check that the material changed @@ -72,7 +72,7 @@ namespace Microsoft::glTF::Toolkit::Test Assert::IsTrue(ormJson[MSFT_PACKING_ORM_RMOTEXTURE_KEY].HasMember(MSFT_PACKING_INDEX_KEY)); } - if (!material.normalTexture.id.empty()) + if (!material.normalTexture.textureId.empty()) { // Check the new extension contains a normal texture Assert::IsTrue(ormJson[MSFT_PACKING_ORM_NORMALTEXTURE_KEY].IsObject()); @@ -106,7 +106,7 @@ namespace Microsoft::glTF::Toolkit::Test TestUtils::LoadAndExecuteGLTFTest(c_cubeAsset3DJson, [](auto doc, auto path) { auto material = doc.materials.Elements()[0]; - auto packedDoc = GLTFTexturePackingUtils::PackMaterialForWindowsMR(TestStreamReader(path), doc, material, TexturePacking::OcclusionRoughnessMetallic, ""); + auto packedDoc = GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::make_shared(path), doc, material, TexturePacking::OcclusionRoughnessMetallic, ""); // Check that nothing changed Assert::IsTrue(doc == packedDoc); @@ -119,7 +119,7 @@ namespace Microsoft::glTF::Toolkit::Test TestUtils::LoadAndExecuteGLTFTest(c_waterBottleJson, [](auto doc, auto path) { auto material = doc.materials.Elements()[0]; - auto packedDoc = GLTFTexturePackingUtils::PackMaterialForWindowsMR(TestStreamReader(path), doc, material, TexturePacking::None, ""); + auto packedDoc = GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::make_shared(path), doc, material, TexturePacking::None, ""); // Check that nothing changed Assert::IsTrue(doc == packedDoc); @@ -163,7 +163,7 @@ namespace Microsoft::glTF::Toolkit::Test // This asset has no materials TestUtils::LoadAndExecuteGLTFTest(c_cubeAsset3DJson, [](auto doc, auto path) { - auto packedDoc = GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(TestStreamReader(path), doc, TexturePacking::OcclusionRoughnessMetallic, ""); + auto packedDoc = GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(std::make_shared(path), doc, TexturePacking::OcclusionRoughnessMetallic, ""); // Check that nothing changed Assert::IsTrue(doc == packedDoc); @@ -175,7 +175,7 @@ namespace Microsoft::glTF::Toolkit::Test // This asset has all textures TestUtils::LoadAndExecuteGLTFTest(c_waterBottleJson, [](auto doc, auto path) { - auto packedDoc = GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(TestStreamReader(path), doc, TexturePacking::None, ""); + auto packedDoc = GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(std::make_shared(path), doc, TexturePacking::None, ""); // Check that nothing changed Assert::IsTrue(doc == packedDoc); @@ -184,18 +184,18 @@ namespace Microsoft::glTF::Toolkit::Test TEST_METHOD(GLTFTexturePackingUtils_PackAllWithOneMaterial) { - std::unique_ptr documentPackedSingleTexture; - std::unique_ptr documentPackedAllTextures; + std::unique_ptr documentPackedSingleTexture; + std::unique_ptr documentPackedAllTextures; // This asset has all textures TestUtils::LoadAndExecuteGLTFTest(c_waterBottleJson, [&documentPackedSingleTexture](auto doc, auto path) { - documentPackedSingleTexture = std::make_unique(GLTFTexturePackingUtils::PackMaterialForWindowsMR(TestStreamReader(path), doc, doc.materials.Elements()[0], TexturePacking::OcclusionRoughnessMetallic, "")); + documentPackedSingleTexture = std::make_unique(GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::make_shared(path), doc, doc.materials.Elements()[0], TexturePacking::OcclusionRoughnessMetallic, "")); }); TestUtils::LoadAndExecuteGLTFTest(c_waterBottleJson, [&documentPackedAllTextures](auto doc, auto path) { - documentPackedAllTextures = std::make_unique(GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(TestStreamReader(path), doc, TexturePacking::OcclusionRoughnessMetallic, "")); + documentPackedAllTextures = std::make_unique(GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(std::make_shared(path), doc, TexturePacking::OcclusionRoughnessMetallic, "")); }); // Assert there's one material diff --git a/glTF-Toolkit.Test/Helpers/TestUtils.h b/glTF-Toolkit.Test/Helpers/TestUtils.h index 019a181..7f409f3 100644 --- a/glTF-Toolkit.Test/Helpers/TestUtils.h +++ b/glTF-Toolkit.Test/Helpers/TestUtils.h @@ -7,7 +7,7 @@ #include #include "GLTFSDK/IStreamWriter.h" -#include "GLTFSDK/GLTFConstants.h" +#include "GLTFSDK/Constants.h" #include "GLTFSDK/Serialize.h" #include "GLTFSDK/Deserialize.h" @@ -114,7 +114,7 @@ namespace Microsoft::glTF::Toolkit::Test return tempStream; } - typedef std::function GLTFAction; + typedef std::function GLTFAction; static void LoadAndExecuteGLTFTest(const char * gltfRelativePath, GLTFAction action) { @@ -125,7 +125,7 @@ namespace Microsoft::glTF::Toolkit::Test { // Deserialize input json auto inputJson = std::string(std::istreambuf_iterator(*input), std::istreambuf_iterator()); - auto doc = DeserializeJson(inputJson); + auto doc = Deserialize(inputJson); action(doc, absolutePath); } diff --git a/glTF-Toolkit.Test/Resources/gltf/CubeAsset3D.gltf b/glTF-Toolkit.Test/Resources/gltf/CubeAsset3D.gltf index af2f88b..373bf6c 100644 --- a/glTF-Toolkit.Test/Resources/gltf/CubeAsset3D.gltf +++ b/glTF-Toolkit.Test/Resources/gltf/CubeAsset3D.gltf @@ -120,5 +120,5 @@ } ], "scene": 0, - "extensionsUsed": ["KHR_materials_PBRSpecularGlossiness"] + "extensionsUsed": ["KHR_materials_pbrSpecularGlossiness"] } diff --git a/glTF-Toolkit.Test/Resources/gltf/CubeWithLOD.gltf b/glTF-Toolkit.Test/Resources/gltf/CubeWithLOD.gltf index 4855e1e..81eab5b 100644 --- a/glTF-Toolkit.Test/Resources/gltf/CubeWithLOD.gltf +++ b/glTF-Toolkit.Test/Resources/gltf/CubeWithLOD.gltf @@ -97,5 +97,5 @@ } ], "scene": 0, - "extensionsUsed": ["KHR_materials_PBRSpecularGlossiness"] + "extensionsUsed": ["KHR_materials_pbrSpecularGlossiness", "MSFT_lod"] } diff --git a/glTF-Toolkit.Test/glTF-Toolkit.Test.vcxproj b/glTF-Toolkit.Test/glTF-Toolkit.Test.vcxproj index 1759a63..0ad12b0 100644 --- a/glTF-Toolkit.Test/glTF-Toolkit.Test.vcxproj +++ b/glTF-Toolkit.Test/glTF-Toolkit.Test.vcxproj @@ -237,15 +237,15 @@ - - + + 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}. - - + + \ No newline at end of file diff --git a/glTF-Toolkit.Test/packages.config b/glTF-Toolkit.Test/packages.config index 05850ae..3bc3368 100644 --- a/glTF-Toolkit.Test/packages.config +++ b/glTF-Toolkit.Test/packages.config @@ -1,6 +1,6 @@  - - + + \ No newline at end of file diff --git a/glTF-Toolkit.UWP.Test/Assets/3DModels/WaterBottle.glb b/glTF-Toolkit.UWP.Test/Assets/3DModels/WaterBottle.glb index d9786d6046a67b2277761fdffc8ad21db1e0df66..5152b63e1177ce46dc62f864c53b96f58c219316 100644 GIT binary patch delta 1120 zcmWmEX?za^9LMq39J3V>awhgK_pzB!lU#*D?vP_NbKA1HLX=@7H#wu+S4GLK70rE1 zIp#`~a^wz$_y58B^XT{V`^&hyFf4|c2 zZehuB@d@#Pn5dNW@YMA15HgXOEMz5=Y=jZKd4ea&P7a&@j0gfe&kGc%1SKg&Y06NRa+Ie6kwj6EXe#j{m8n8iUgBk9sKzT)=T&M@ zlUmfK4zKY#b*V>v-r!9d(2z#FMPr)Klx8%i1uc1-R!$8VhHcP?;|KlqbN{Kel~<_cH2#y?!=1~<9IZSHWFf4Rqf n9`GOk^N>elZGQs6WC|v8Fj<1h8cb*~*@6iRN%IFn@<%@gF;DXC delta 1120 zcmWmEXPgfN9LMq3;m#d$HYJC@>~&p(E_-K0c6M>a(TU@d%^78%D?2M&8I`@M&S)5+ zbWt>I!r8q458j_gzo*|{dd8CQbfy)JOp5ED*sE_sAT|(FDLPOgB`UDKMQB8LV)wX& zxIk=lYO9F&PO@)U)6 znj%C|lwuSoKnb3qB&8@#8OlHNAm8eWK&l1CPJkJZfNENE`60y8YHL6pCn$)5; zb*M`{>eGORG~yK+(}bop<5il|g4bwCD_YZrwzQ)?uhW5!bfPm|=t?)<;7z*IgE-#e zZF_iqRx9hOvxeJRk5O6PU;( zCNqVpOe2Nqr1B9TGlQATVm5P_%RD||J_}gLA{O&0OIXS>ma~GDtYS5v@i}W)%R1Kc z1z++N8`#JuHnWATY-2k+*vT$-vxmLx<7>X*ThiFi0SsdL;V8%WiQ}B$ zBtP>Dr}&k0e&aM}_?Sk^yL5m diff --git a/glTF-Toolkit.UWP.Test/UWPTest.cs b/glTF-Toolkit.UWP.Test/UWPTest.cs index bb93459..0d3300c 100644 --- a/glTF-Toolkit.UWP.Test/UWPTest.cs +++ b/glTF-Toolkit.UWP.Test/UWPTest.cs @@ -49,19 +49,19 @@ public async Task GLBDeserializeSerialize() // compare one of the extracted images to the source images StorageFile sourceImageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/3DModels/WaterBottle_diffuse.png")); StorageFile outputImageFile = await outputFolder.GetFileAsync(glbBaseName + "_image5.png"); - Assert.IsTrue(await CompareFilesAsync(sourceImageFile, outputImageFile)); + Assert.IsTrue(await CompareFilesAsync(sourceImageFile, outputImageFile), "images"); // compare the extracted model (.bin) to the source model (.bin) file StorageFile sourceBinFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/3DModels/" + glbBaseName + ".bin")); StorageFile outputBinFile = await outputFolder.GetFileAsync(glbBaseName + ".bin"); - Assert.IsTrue(await CompareFilesAsync(sourceBinFile, outputBinFile)); + Assert.IsTrue(await CompareFilesAsync(sourceBinFile, outputBinFile), "bins"); // Pack the gltf back into a glb file StorageFile gltfFile = await outputFolder.GetFileAsync(glbBaseName + ".gltf"); StorageFile outputGlbFile = await GLTFSerialization.PackGLTFAsync(gltfFile, outputFolder, glbFileName); // compare the new glb to the old glb - Assert.IsTrue(await CompareFilesAsync(sourceGlbFile, outputGlbFile)); + Assert.IsTrue(await CompareFilesAsync(sourceGlbFile, outputGlbFile), "glb"); } [TestMethod] @@ -74,7 +74,7 @@ public async Task GLBConvertToWindowsMR() StorageFolder outputFolder = await CreateTemporaryOutputFolderAsync("Out_" + glbBaseName); - var converted = await WindowsMRConversion.ConvertAssetForWindowsMR(sourceGlbFile, outputFolder, 512, TexturePacking.OcclusionRoughnessMetallic); + var converted = await WindowsMRConversion.ConvertAssetForWindowsMR(sourceGlbFile, outputFolder, 512, TexturePacking.OcclusionRoughnessMetallic, true); Assert.IsTrue(converted.Name == "WaterBottle_converted.glb"); } diff --git a/glTF-Toolkit.UWP.Test/glTF-Toolkit.UWP.Test.csproj b/glTF-Toolkit.UWP.Test/glTF-Toolkit.UWP.Test.csproj index 40f809f..0813489 100644 --- a/glTF-Toolkit.UWP.Test/glTF-Toolkit.UWP.Test.csproj +++ b/glTF-Toolkit.UWP.Test/glTF-Toolkit.UWP.Test.csproj @@ -131,13 +131,13 @@ - 6.0.7 + 6.2.0-Preview1-26502-02 - 1.2.0 + 1.3.2 - 1.2.0 + 1.3.2 diff --git a/glTF-Toolkit.UWP/GLTFSerialization.cpp b/glTF-Toolkit.UWP/GLTFSerialization.cpp index 674edd6..efca1d9 100644 --- a/glTF-Toolkit.UWP/GLTFSerialization.cpp +++ b/glTF-Toolkit.UWP/GLTFSerialization.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include using namespace Concurrency; using namespace Microsoft::glTF; @@ -50,18 +50,18 @@ IAsyncOperation^ GLTFSerialization::PackGLTFAsync(StorageFile^ sou return create_task([stream]() { - return std::make_shared(DeserializeJson(*stream)); + return std::make_shared(Deserialize(*stream)); }) - .then([sourceGltf, outputFolder, glbName](std::shared_ptr document) + .then([sourceGltf, outputFolder, glbName](std::shared_ptr document) { return create_task(sourceGltf->GetParentAsync()) .then([outputFolder, glbName, document](StorageFolder^ gltfFolder) { - GLTFStreamReader streamReader(gltfFolder); + auto streamReader = std::make_shared(gltfFolder); String^ outputGlbPath = outputFolder->Path + "\\" + glbName; std::wstring outputGlbPathW = outputGlbPath->Data(); - SerializeBinary(*document, streamReader, std::make_unique(outputGlbPathW)); + SerializeBinary(*document, streamReader, std::make_shared(outputGlbPathW)); return outputFolder->GetFileAsync(glbName); }); diff --git a/glTF-Toolkit.UWP/GLTFStreams.h b/glTF-Toolkit.UWP/GLTFStreams.h index b540b8f..e67b3fe 100644 --- a/glTF-Toolkit.UWP/GLTFStreams.h +++ b/glTF-Toolkit.UWP/GLTFStreams.h @@ -5,7 +5,7 @@ #include #include -#include +#include namespace Microsoft::glTF::Toolkit::UWP { @@ -36,30 +36,19 @@ namespace Microsoft::glTF::Toolkit::UWP std::experimental::filesystem::path 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(filename, std::ios_base::binary | std::ios_base::out)), - m_tempStream(std::make_shared(std::ios_base::binary | std::ios_base::in | std::ios_base::out)) + GLBStreamWriter(const std::wstring& filename) : + m_stream(std::make_shared(filename, std::ios_base::binary | std::ios_base::out)) { } - std::shared_ptr GetInputStream(const std::string&) const override - { - throw std::logic_error("Not implemented"); - } - std::shared_ptr GetOutputStream(const std::string&) const override { return m_stream; } - std::shared_ptr GetTemporaryStream(const std::string&) const override - { - return m_tempStream; - } private: std::shared_ptr m_stream; - std::shared_ptr m_tempStream; }; } diff --git a/glTF-Toolkit.UWP/WindowsMRConversion.cpp b/glTF-Toolkit.UWP/WindowsMRConversion.cpp index fa83c0a..d45ddb6 100644 --- a/glTF-Toolkit.UWP/WindowsMRConversion.cpp +++ b/glTF-Toolkit.UWP/WindowsMRConversion.cpp @@ -9,12 +9,15 @@ #include #include #include +#include +#include #include #include -#include +#include #include -#include +#include +#include using namespace concurrency; using namespace Platform; @@ -48,10 +51,15 @@ IAsyncOperation^ WindowsMRConversion::ConvertAssetForWindowsMR(Sto } IAsyncOperation^ WindowsMRConversion::ConvertAssetForWindowsMR(StorageFile ^ gltfOrGlbFile, StorageFolder ^ outputFolder, size_t maxTextureSize, TexturePacking packing) +{ + return ConvertAssetForWindowsMR(gltfOrGlbFile, outputFolder, maxTextureSize, packing, false); +} + +IAsyncOperation^ WindowsMRConversion::ConvertAssetForWindowsMR(StorageFile ^ gltfOrGlbFile, StorageFolder ^ outputFolder, size_t maxTextureSize, TexturePacking packing, bool meshCompression) { auto isGlb = gltfOrGlbFile->FileType == L".glb"; - return create_async([gltfOrGlbFile, maxTextureSize, outputFolder, isGlb, packing]() + return create_async([gltfOrGlbFile, maxTextureSize, outputFolder, isGlb, packing, meshCompression]() { return create_task([gltfOrGlbFile, isGlb]() { @@ -64,23 +72,26 @@ IAsyncOperation^ WindowsMRConversion::ConvertAssetForWindowsMR(Sto return task_from_result(gltfOrGlbFile); } }) - .then([maxTextureSize, outputFolder, isGlb, packing](StorageFile^ gltfFile) + .then([maxTextureSize, outputFolder, isGlb, packing, meshCompression](StorageFile^ gltfFile) { auto stream = std::make_shared(gltfFile->Path->Data(), std::ios::in); - GLTFDocument document = DeserializeJson(*stream); + Document document = Deserialize(*stream, KHR::GetKHRExtensionDeserializer()); return create_task(gltfFile->GetParentAsync()) - .then([document, maxTextureSize, outputFolder, gltfFile, isGlb, packing](StorageFolder^ baseFolder) + .then([document, maxTextureSize, outputFolder, gltfFile, isGlb, packing, meshCompression](StorageFolder^ baseFolder) { - GLTFStreamReader streamReader(baseFolder); - - // 1. Texture Packing + auto streamReader = std::make_shared(baseFolder); auto tempDirectory = std::wstring(ApplicationData::Current->TemporaryFolder->Path->Data()); auto tempDirectoryA = std::string(tempDirectory.begin(), tempDirectory.end()); - auto convertedDoc = GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(streamReader, document, static_cast(packing), tempDirectoryA); + + // 0. Specular Glossiness conversion + auto convertedDoc = GLTFSpecularGlossinessUtils::ConvertMaterials(streamReader, document, tempDirectoryA); + + // 1. Texture Packing + convertedDoc = GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(streamReader, convertedDoc, static_cast(packing), tempDirectoryA); // 2. Texture Compression - convertedDoc = GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(streamReader, document, tempDirectoryA, maxTextureSize, false /* retainOriginalImages */); + convertedDoc = GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(streamReader, convertedDoc, tempDirectoryA, maxTextureSize, false /* retainOriginalImages */); // 3. Make sure there's a default scene set if (!convertedDoc.HasDefaultScene()) @@ -88,7 +99,13 @@ IAsyncOperation^ WindowsMRConversion::ConvertAssetForWindowsMR(Sto convertedDoc.defaultSceneId = convertedDoc.scenes.Elements()[0].id; } - // 4. GLB Export + // 4. Compress the meshes + if (meshCompression) + { + convertedDoc = GLTFMeshCompressionUtils::CompressMeshes(streamReader, convertedDoc, {}, tempDirectoryA); + } + + // 5. GLB Export // The Windows MR Fall Creators update has restrictions on the supported // component types of accessors. @@ -125,7 +142,7 @@ IAsyncOperation^ WindowsMRConversion::ConvertAssetForWindowsMR(Sto glbName += L".glb"; std::wstring outputGlbPathW = std::wstring(outputFolder->Path->Data()) + L"\\" + glbName; - SerializeBinary(convertedDoc, streamReader, std::make_unique(outputGlbPathW), accessorConversion); + SerializeBinary(convertedDoc, streamReader, std::make_shared(outputGlbPathW), accessorConversion); return create_task(outputFolder->GetFileAsync(ref new String(glbName.c_str()))); }); diff --git a/glTF-Toolkit.UWP/WindowsMRConversion.h b/glTF-Toolkit.UWP/WindowsMRConversion.h index 4e81968..c8a0ef0 100644 --- a/glTF-Toolkit.UWP/WindowsMRConversion.h +++ b/glTF-Toolkit.UWP/WindowsMRConversion.h @@ -22,5 +22,6 @@ namespace Microsoft::glTF::Toolkit::UWP static Windows::Foundation::IAsyncOperation^ ConvertAssetForWindowsMR(Windows::Storage::StorageFile^ gltfOrGlbFile, Windows::Storage::StorageFolder^ outputFolder); static Windows::Foundation::IAsyncOperation^ ConvertAssetForWindowsMR(Windows::Storage::StorageFile^ gltfOrGlbFile, Windows::Storage::StorageFolder^ outputFolder, size_t maxTextureSize); static Windows::Foundation::IAsyncOperation^ ConvertAssetForWindowsMR(Windows::Storage::StorageFile^ gltfOrGlbFile, Windows::Storage::StorageFolder^ outputFolder, size_t maxTextureSize, UWP::TexturePacking packing); + static Windows::Foundation::IAsyncOperation^ ConvertAssetForWindowsMR(Windows::Storage::StorageFile^ gltfOrGlbFile, Windows::Storage::StorageFolder^ outputFolder, size_t maxTextureSize, UWP::TexturePacking packing, bool meshCompression); }; } diff --git a/glTF-Toolkit.UWP/glTF-Toolkit.UWP.vcxproj b/glTF-Toolkit.UWP/glTF-Toolkit.UWP.vcxproj index cd94b9b..079f7df 100644 --- a/glTF-Toolkit.UWP/glTF-Toolkit.UWP.vcxproj +++ b/glTF-Toolkit.UWP/glTF-Toolkit.UWP.vcxproj @@ -284,15 +284,17 @@ - - + + + 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}. - - + + + \ No newline at end of file diff --git a/glTF-Toolkit.UWP/packages.config b/glTF-Toolkit.UWP/packages.config index 86a7c78..4e14ddb 100644 --- a/glTF-Toolkit.UWP/packages.config +++ b/glTF-Toolkit.UWP/packages.config @@ -1,6 +1,7 @@  - - + + + \ No newline at end of file diff --git a/glTF-Toolkit/glTF-Toolkit.vcxproj b/glTF-Toolkit/glTF-Toolkit.vcxproj index 0c6e58a..9ed241d 100644 --- a/glTF-Toolkit/glTF-Toolkit.vcxproj +++ b/glTF-Toolkit/glTF-Toolkit.vcxproj @@ -141,19 +141,23 @@ + + - + + + - + Create @@ -163,15 +167,17 @@ - - + + + 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}. - - + + + - + \ No newline at end of file diff --git a/glTF-Toolkit/glTF-Toolkit.vcxproj.filters b/glTF-Toolkit/glTF-Toolkit.vcxproj.filters index f9cb8d6..3ec7cb1 100644 --- a/glTF-Toolkit/glTF-Toolkit.vcxproj.filters +++ b/glTF-Toolkit/glTF-Toolkit.vcxproj.filters @@ -21,9 +21,6 @@ inc - - inc - inc @@ -42,6 +39,15 @@ inc + + inc + + + inc + + + inc + @@ -53,9 +59,6 @@ src - - src - src @@ -68,5 +71,14 @@ src + + src + + + src + + + src + \ No newline at end of file diff --git a/glTF-Toolkit/inc/GLBtoGLTF.h b/glTF-Toolkit/inc/GLBtoGLTF.h index 02de2ca..e051986 100644 --- a/glTF-Toolkit/inc/GLBtoGLTF.h +++ b/glTF-Toolkit/inc/GLBtoGLTF.h @@ -37,7 +37,7 @@ namespace Microsoft::glTF::Toolkit /// /// The binary content of the buffer views as a vector. /// - static std::vector SaveBin(std::istream* in, const Microsoft::glTF::GLTFDocument& glbDoc, const size_t bufferOffset, const size_t newBufferlength); + static std::vector SaveBin(std::istream* in, const Microsoft::glTF::Document& glbDoc, const size_t bufferOffset, const size_t newBufferlength); /// /// Loads all images in a glTF-Binary (GLB) asset into a map relating each image identifier to the contents of that image. @@ -49,7 +49,7 @@ namespace Microsoft::glTF::Toolkit /// /// A map relating each image identifier to the contents of that image. /// - static std::unordered_map> GetImagesData(std::istream* in, const Microsoft::glTF::GLTFDocument& glbDoc, const std::string& name, const size_t bufferOffset); + static std::unordered_map> GetImagesData(std::istream* in, const Microsoft::glTF::Document& glbDoc, const std::string& name, const size_t bufferOffset); /// /// Creates the glTF manifest that represents a GLB file after unpacking. @@ -59,6 +59,6 @@ namespace Microsoft::glTF::Toolkit /// /// A new glTF manifest that represents the same file, but with images and resources referenced by URI instead of embedded ina GLB buffer. /// - static Microsoft::glTF::GLTFDocument CreateGLTFDocument(const Microsoft::glTF::GLTFDocument& glbDoc, const std::string& name); + static Microsoft::glTF::Document CreateGLTFDocument(const Microsoft::glTF::Document& glbDoc, const std::string& name); }; } diff --git a/glTF-Toolkit/inc/GLTFLODUtils.h b/glTF-Toolkit/inc/GLTFLODUtils.h index 61c3e96..f335f08 100644 --- a/glTF-Toolkit/inc/GLTFLODUtils.h +++ b/glTF-Toolkit/inc/GLTFLODUtils.h @@ -22,20 +22,20 @@ namespace Microsoft::glTF::Toolkit /// /// A map that relates each node ID to a vector of its levels of detail node IDs. /// The glTF document containing LODs to be parsed. - static LODMap ParseDocumentNodeLODs(const GLTFDocument& doc); + static LODMap ParseDocumentNodeLODs(const Document& doc); /// - /// Inserts each LOD GLTFDocument as a node LOD (at the root level) of the specified primary GLTF asset. + /// Inserts each LOD Document as a node LOD (at the root level) of the specified primary GLTF asset. /// Note: Animation is not currently supported. /// /// The primary GLTF Document with the inserted LOD node. /// A vector of glTF documents to merge as LODs. The first element of the vector is assumed to be the primary LOD. /// A vector of relative path prefixes to the non-LOD0 LOD gltf documents. Used for finding resources in those LODs. /// If not specified, all resources are assumed to be in the same directory. - static GLTFDocument MergeDocumentsAsLODs(const std::vector& docs, const std::vector& relativePaths = std::vector(), const bool& sharedMaterials = false); + static Document MergeDocumentsAsLODs(const std::vector& docs, const std::vector& relativePaths = std::vector(), const bool& sharedMaterials = false); /// - /// Inserts each LOD GLTFDocument as a node LOD (at the root level) of the specified primary GLTF asset. + /// Inserts each LOD Document as a node LOD (at the root level) of the specified primary GLTF asset. /// Note: Animation is not currently supported. /// /// The primary GLTF Document with the inserted LOD node. @@ -44,7 +44,7 @@ namespace Microsoft::glTF::Toolkit /// vector is larger than the size of , lower coverage values will cause the asset to be invisible. /// A vector of relative path prefixes to the non-LOD0 LOD gltf documents. Used for finding resources in those LODs. /// If not specified, all resources are assumed to be in the same directory. - static GLTFDocument MergeDocumentsAsLODs(const std::vector& docs, const std::vector& screenCoveragePercentages, const std::vector& relativePaths = std::vector(), const bool& sharedMaterials = false); + static Document MergeDocumentsAsLODs(const std::vector& docs, const std::vector& screenCoveragePercentages, const std::vector& relativePaths = std::vector(), const bool& sharedMaterials = false); /// /// Determines the highest number of Node LODs for a given glTF asset. @@ -52,7 +52,7 @@ namespace Microsoft::glTF::Toolkit /// The glTF asset for which to count the max number of node LODs. /// A map containing the parsed node LODs in the document. /// The highest number of Node LODs in the asset. - static uint32_t NumberOfNodeLODLevels(const GLTFDocument& doc, const LODMap& lods); + static uint32_t NumberOfNodeLODLevels(const Document& doc, const LODMap& lods); }; } diff --git a/glTF-Toolkit/inc/GLTFMeshCompressionUtils.h b/glTF-Toolkit/inc/GLTFMeshCompressionUtils.h new file mode 100644 index 0000000..6be5233 --- /dev/null +++ b/glTF-Toolkit/inc/GLTFMeshCompressionUtils.h @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#pragma once + +#include "GLTFSDK.h" +#include "GLTFSDK/BufferBuilder.h" + +namespace Microsoft::glTF::Toolkit +{ + /// Draco compression options. + struct CompressionOptions + { + int PositionQuantizationBits = 14; + int TexCoordQuantizationBits = 12; + int NormalQuantizationBits = 10; + int ColorQuantizationBits = 8; + int GenericQuantizationBits = 12; + int Speed = 3; + }; + + /// + /// Utilities to compress textures in a glTF asset. + /// + class GLTFMeshCompressionUtils + { + public: + /// + /// Applies to every mesh in the document, following the same parameter structure as that function. + /// + /// A stream reader that is capable of accessing the resources used in the glTF asset by URI. + /// The document from which the mesh will be loaded. + /// The compression options that will be used. + /// The output directory to which compressed data should be saved. + /// + /// A new glTF manifest that uses the KHR_draco_mesh_compression extension to point to the compressed meshes. + /// + static Document CompressMeshes( + std::shared_ptr streamReader, + const Document & doc, + CompressionOptions options, + const std::string& outputDirectory); + + /// + /// Applies Draco mesh compression to the supplied mesh and creates a new set of vertex buffers for all the primitive attributes. + /// + /// A stream reader that is capable of accessing the resources used in the glTF asset by URI. + /// The document from which the mesh will be loaded. + /// The mesh which the mesh will be compressed. + /// The compression options that will be used. + /// The output buffer builder that handles bufferId generation for the return document. + /// Out parameter of BufferView Ids that are no longer in use and should be removed. + /// + /// A new glTF manifest that uses the KHR_draco_mesh_compression extension to point to the compressed meshes. + /// + static Document CompressMesh( + std::shared_ptr streamReader, + const Document & doc, + CompressionOptions options, + const Mesh & mesh, + BufferBuilder* builder, + std::unordered_set& bufferViewsToRemove); + }; +} \ No newline at end of file diff --git a/glTF-Toolkit/inc/GLTFSDK.h b/glTF-Toolkit/inc/GLTFSDK.h index 341c6cc..ce5d7ec 100644 --- a/glTF-Toolkit/inc/GLTFSDK.h +++ b/glTF-Toolkit/inc/GLTFSDK.h @@ -7,16 +7,15 @@ #pragma warning(disable : 4634) #pragma warning(disable : 4996) -#include +#include #include #include #include #include #include #include -#include #include #include -#include +#include #pragma warning(pop) \ No newline at end of file diff --git a/glTF-Toolkit/inc/GLTFSpecularGlossinessUtils.h b/glTF-Toolkit/inc/GLTFSpecularGlossinessUtils.h new file mode 100644 index 0000000..1f04b8f --- /dev/null +++ b/glTF-Toolkit/inc/GLTFSpecularGlossinessUtils.h @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#pragma once + +#include "GLTFSDK.h" + +namespace Microsoft::glTF::Toolkit +{ + /// + /// Utilities to remove Specular Glossiness from a glTF asset. + /// + class GLTFSpecularGlossinessUtils + { + public: + /// + /// Applies to every material in the document, following the same parameter structure as that function. + /// + /// A stream reader that is capable of accessing the resources used in the glTF asset by URI. + /// The document from which the mesh will be loaded. + /// The output directory to which compressed data should be saved. + /// + /// A new glTF manifest without the KHR_materials_pbrSpecularGlossiness extension. + /// + static Document ConvertMaterials(std::shared_ptr streamReader, const Document & doc, const std::string& outputDirectory); + + /// + /// Removes the KHR_materials_pbrSpecularGlossiness extension by converting the parameters to Metal Roughness. + /// + /// A stream reader that is capable of accessing the resources used in the glTF asset by URI. + /// The document from which the mesh will be loaded. + /// The material to be converted. + /// The output directory to which compressed data should be saved. + /// + /// A new glTF manifest without the KHR_materials_pbrSpecularGlossiness extension. + /// + static Document ConvertMaterial(std::shared_ptr streamReader, const Document & doc, const Material & material, const std::string& outputDirectory); + + }; +} \ No newline at end of file diff --git a/glTF-Toolkit/inc/GLTFTextureCompressionUtils.h b/glTF-Toolkit/inc/GLTFTextureCompressionUtils.h index 08a5b8c..49d2f5a 100644 --- a/glTF-Toolkit/inc/GLTFTextureCompressionUtils.h +++ b/glTF-Toolkit/inc/GLTFTextureCompressionUtils.h @@ -43,7 +43,7 @@ namespace Microsoft::glTF::Toolkit /// If true, also generates mip maps when compressing. /// If true, retains the original image on the resulting glTF. If false, /// replaces that image (making the glTF incompatible with most core glTF 2.0 viewers). - /// Returns a new GLTFDocument that contains a new reference to the compressed dds file added as part + /// Returns a new Document that contains a new reference to the compressed dds file added as part /// of the MSFT_texture_dds extension. /// /// Example Input: @@ -84,7 +84,7 @@ namespace Microsoft::glTF::Toolkit /// /// /// - static GLTFDocument CompressTextureAsDDS(const IStreamReader& streamReader, const GLTFDocument & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits::max(), bool generateMipMaps = true, bool retainOriginalImage = true); + static Document CompressTextureAsDDS(std::shared_ptr streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits::max(), bool generateMipMaps = true, bool retainOriginalImage = true); /// /// Applies to all textures in the document that are accessible via materials according to the @@ -97,10 +97,10 @@ namespace Microsoft::glTF::Toolkit /// If true, also generates mip maps when compressing. /// If true, retains the original image on the resulting glTF. If false, /// replaces that image (making the glTF incompatible with most core glTF 2.0 viewers). - /// Returns a new GLTFDocument that contains alternate textures for all applicable materials following the requirements of the Windows + /// Returns a new Document that contains alternate textures for all applicable materials following the requirements of the Windows /// Mixed Reality home using the MSFT_texture_dds extension. /// - static GLTFDocument CompressAllTexturesForWindowsMR(const IStreamReader& streamReader, const GLTFDocument & doc, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits::max(), bool retainOriginalImages = true); + static Document CompressAllTexturesForWindowsMR(std::shared_ptr streamReader, const Document & doc, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits::max(), bool retainOriginalImages = true); /// /// Compresses a DirectX::ScratchImage in place using the specified compression. diff --git a/glTF-Toolkit/inc/GLTFTextureLoadingUtils.h b/glTF-Toolkit/inc/GLTFTextureLoadingUtils.h deleted file mode 100644 index 7602b14..0000000 --- a/glTF-Toolkit/inc/GLTFTextureLoadingUtils.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE in the project root for license information.#pragma once - -#include "GLTFSDK.h" -#include - -namespace Microsoft::glTF::Toolkit -{ - /// - /// Utilities to load textures from glTF assets. - /// - class GLTFTextureLoadingUtils - { - public: - /// - /// Loads a texture into a scratch image in the DXGI_FORMAT_R32G32B32A32_FLOAT format for in-memory processing. - /// - /// A scratch image containing the loaded texture in the DXGI_FORMAT_R32G32B32A32_FLOAT format. - /// A stream reader that is capable of accessing the resources used in the glTF asset by URI. - /// The document from which the texture will be loaded. - /// The identifier of the texture to be loaded. - static DirectX::ScratchImage LoadTexture(const IStreamReader& streamReader, const GLTFDocument& doc, const std::string& textureId); - }; -} - diff --git a/glTF-Toolkit/inc/GLTFTexturePackingUtils.h b/glTF-Toolkit/inc/GLTFTexturePackingUtils.h index df48802..9343eed 100644 --- a/glTF-Toolkit/inc/GLTFTexturePackingUtils.h +++ b/glTF-Toolkit/inc/GLTFTexturePackingUtils.h @@ -42,7 +42,7 @@ namespace Microsoft::glTF::Toolkit /// /// A new glTF manifest that uses the MSFT_packing_occlusionRoughnessMetallic extension to point to the packed textures. /// - static GLTFDocument PackMaterialForWindowsMR(const IStreamReader& streamReader, const GLTFDocument & doc, const Material & material, TexturePacking packing, const std::string& outputDirectory); + static Document PackMaterialForWindowsMR(std::shared_ptr streamReader, const Document & doc, const Material & material, TexturePacking packing, const std::string& outputDirectory); /// /// Applies to every material in the document, following the same parameter structure as that function. @@ -54,7 +54,7 @@ namespace Microsoft::glTF::Toolkit /// /// A new glTF manifest that uses the MSFT_packing_occlusionRoughnessMetallic extension to point to the packed textures. /// - static GLTFDocument PackAllMaterialsForWindowsMR(const IStreamReader& streamReader, const GLTFDocument & doc, TexturePacking packing, const std::string& outputDirectory); + static Document PackAllMaterialsForWindowsMR(std::shared_ptr streamReader, const Document & doc, TexturePacking packing, const std::string& outputDirectory); }; } diff --git a/glTF-Toolkit/inc/GLTFTextureUtils.h b/glTF-Toolkit/inc/GLTFTextureUtils.h new file mode 100644 index 0000000..78100d2 --- /dev/null +++ b/glTF-Toolkit/inc/GLTFTextureUtils.h @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information.#pragma once + +#include "GLTFSDK.h" +#include + +namespace Microsoft::glTF::Toolkit +{ + enum Channel + { + Red = 0, + Green = 4, + Blue = 8, + Alpha = 12 + }; + + /// + /// Utilities to load textures from glTF assets. + /// + class GLTFTextureUtils + { + public: + /// + /// Loads a texture into a scratch image in the DXGI_FORMAT_R32G32B32A32_FLOAT format for in-memory processing. + /// + /// A scratch image containing the loaded texture in the DXGI_FORMAT_R32G32B32A32_FLOAT format. + /// A stream reader that is capable of accessing the resources used in the glTF asset by URI. + /// The document from which the texture will be loaded. + /// The identifier of the texture to be loaded. + static DirectX::ScratchImage LoadTexture(std::shared_ptr streamReader, const Document& doc, const std::string& textureId, bool treatAsLinear = true); + + /// + /// Gets the value of channel `channel` in pixel index `offset` in image `imageData` + /// assumed to be formatted as DXGI_FORMAT_R32G32B32A32_FLOAT + /// + static float* GetChannelValue(uint8_t * imageData, size_t offset, Channel channel); + + static std::string SaveAsPng(DirectX::ScratchImage* image, const std::string& fileName, const std::string& directory, const GUID* targetFormat = &GUID_WICPixelFormat24bppBGR); + + static std::string AddImageToDocument(Document& doc, const std::string& imageUri); + + static void ResizeToLargest(std::unique_ptr& image1, std::unique_ptr& image2); + + static void ResizeIfNeeded(const std::unique_ptr& image, size_t resizedWidth, size_t resizedHeight); + }; +} + diff --git a/glTF-Toolkit/inc/SerializeBinary.h b/glTF-Toolkit/inc/SerializeBinary.h index d54ab2b..bc1fa92 100644 --- a/glTF-Toolkit/inc/SerializeBinary.h +++ b/glTF-Toolkit/inc/SerializeBinary.h @@ -20,18 +20,18 @@ namespace Microsoft::glTF::Toolkit /// /// Serializes a glTF asset as a glTF binary (GLB) file. /// - /// The glTF asset manifest to be serialized. + /// The glTF asset manifest to be serialized. /// A stream reader that is capable of accessing the resources used in the glTF asset by URI. /// A stream factory that is capable of creating an output stream where the GLB will be saved, and a temporary stream for /// use during the serialization process. - void SerializeBinary(const GLTFDocument& gltfDocument, const IStreamReader& inputStreamReader, std::unique_ptr&& outputStreamFactory, const AccessorConversionStrategy& accessorConversion = nullptr); + void SerializeBinary(const Document& document, std::shared_ptr inputStreamReader, std::shared_ptr outputStreamWriter, const AccessorConversionStrategy& accessorConversion = nullptr); /// /// Serializes a glTF asset as a glTF binary (GLB) file. /// - /// The glTF asset manifest to be serialized. + /// The glTF asset manifest to be serialized. /// A resource reader that is capable of accessing the resources used in the document. /// A stream factory that is capable of creating an output stream where the GLB will be saved, and a temporary stream for /// use during the serialization process. - void SerializeBinary(const GLTFDocument& gltfDocument, const GLTFResourceReader& resourceReader, std::unique_ptr&& outputStreamFactory, const AccessorConversionStrategy& accessorConversion = nullptr); + void SerializeBinary(const Document& document, const GLTFResourceReader& resourceReader, std::shared_ptr outputStreamWriter, const AccessorConversionStrategy& accessorConversion = nullptr); } diff --git a/glTF-Toolkit/packages.config b/glTF-Toolkit/packages.config index 05850ae..bc2a591 100644 --- a/glTF-Toolkit/packages.config +++ b/glTF-Toolkit/packages.config @@ -1,6 +1,7 @@  - - + + + \ No newline at end of file diff --git a/glTF-Toolkit/src/GLBtoGLTF.cpp b/glTF-Toolkit/src/GLBtoGLTF.cpp index 4fbe35b..399af00 100644 --- a/glTF-Toolkit/src/GLBtoGLTF.cpp +++ b/glTF-Toolkit/src/GLBtoGLTF.cpp @@ -41,7 +41,7 @@ namespace } } -std::vector GLBToGLTF::SaveBin(std::istream* input, const GLTFDocument& glbDoc, const size_t bufferOffset, const size_t newBufferlength) +std::vector GLBToGLTF::SaveBin(std::istream* input, const Document& glbDoc, const size_t bufferOffset, const size_t newBufferlength) { if (newBufferlength == 0) { @@ -110,7 +110,7 @@ std::vector GLBToGLTF::SaveBin(std::istream* input, const GLTFDocument& gl return result; } -std::unordered_map> GLBToGLTF::GetImagesData(std::istream* input, const GLTFDocument& glbDoc, const std::string& name, const size_t bufferOffset) +std::unordered_map> GLBToGLTF::GetImagesData(std::istream* input, const Document& glbDoc, const std::string& name, const size_t bufferOffset) { input->seekg(0, std::ios::beg); std::unordered_map imageIDs; @@ -179,9 +179,9 @@ std::unordered_map> GLBToGLTF::GetImagesData(std: // Create modified gltf from original by removing image buffer segments and updating // images, bufferViews and accessors fields accordingly -GLTFDocument GLBToGLTF::CreateGLTFDocument(const GLTFDocument& glbDoc, const std::string& name) +Document GLBToGLTF::CreateGLTFDocument(const Document& glbDoc, const std::string& name) { - GLTFDocument gltfDoc(glbDoc); + Document gltfDoc(glbDoc); gltfDoc.images.Clear(); gltfDoc.buffers.Clear(); @@ -291,12 +291,12 @@ void GLBToGLTF::UnpackGLB(const std::string& glbPath, const std::string& outDire { // read glb file into json auto glbStream = std::make_shared(glbPath, std::ios::binary); - auto streamReader = std::make_unique(); - GLBResourceReader reader(*streamReader, glbStream); + auto streamReader = std::make_shared(); + GLBResourceReader reader(streamReader, glbStream); // get original json auto json = reader.GetJson(); - auto doc = DeserializeJson(json); + auto doc = Deserialize(json); // create new modified json auto gltfDoc = GLBToGLTF::CreateGLTFDocument(doc, gltfName); diff --git a/glTF-Toolkit/src/GLTFLODUtils.cpp b/glTF-Toolkit/src/GLTFLODUtils.cpp index 3e55cd7..ce76b14 100644 --- a/glTF-Toolkit/src/GLTFLODUtils.cpp +++ b/glTF-Toolkit/src/GLTFLODUtils.cpp @@ -8,10 +8,10 @@ #include "GLTFLODUtils.h" #include "GLTFSDK/GLTF.h" -#include "GLTFSDK/GLTFConstants.h" +#include "GLTFSDK/Constants.h" #include "GLTFSDK/Deserialize.h" #include "GLTFSDK/RapidJsonUtils.h" -#include "GLTFSDK/Schema.h" +#include "GLTFSDK/ExtensionsKHR.h" #include #include @@ -33,6 +33,16 @@ namespace id = (id.empty()) ? "" : std::to_string(std::stoi(id) + offset); } + inline void AddIndexOffset(MeshPrimitive& primitive, const char* attributeName, size_t offset) + { + // an empty id string indicates that the id is not inuse and therefore should not be updated + auto attributeItr = primitive.attributes.find(attributeName); + if (attributeItr != primitive.attributes.end()) + { + attributeItr->second = std::to_string(std::stoi(attributeItr->second) + offset); + } + } + inline void AddIndexOffsetPacked(rapidjson::Value& json, const char* textureId, size_t offset) { if (json.HasMember(textureId)) @@ -68,7 +78,7 @@ namespace } template - std::string SerializeExtensionMSFTLod(const T&, const std::vector& lods, const GLTFDocument& gltfDocument) + std::string SerializeExtensionMSFTLod(const T&, const std::vector& lods, const Document& document) { // Omit MSFT_lod entirely if no LODs are available if (lods.empty()) @@ -86,14 +96,14 @@ namespace { for (const auto& lodId : lods) { - lodIndices.push_back(ToKnownSizeType(gltfDocument.materials.GetIndex(lodId))); + lodIndices.push_back(ToKnownSizeType(document.materials.GetIndex(lodId))); } } else if (std::is_same()) { for (const auto& lodId : lods) { - lodIndices.push_back(ToKnownSizeType(gltfDocument.nodes.GetIndex(lodId))); + lodIndices.push_back(ToKnownSizeType(document.nodes.GetIndex(lodId))); } } else @@ -110,9 +120,9 @@ namespace return stringBuffer.GetString(); } - GLTFDocument AddGLTFNodeLOD(const GLTFDocument& primary, LODMap& primaryLods, const GLTFDocument& lod, const std::wstring& relativePath = L"", bool sharedMaterials = false) + Document AddGLTFNodeLOD(const Document& primary, LODMap& primaryLods, const Document& lod, const std::wstring& relativePath = L"", bool sharedMaterials = false) { - Microsoft::glTF::GLTFDocument gltfLod(primary); + Microsoft::glTF::Document gltfLod(primary); auto primaryScenes = primary.scenes.Elements(); auto lodScenes = lod.scenes.Elements(); @@ -274,15 +284,18 @@ namespace material.name += nodeLodLabel; AddIndexOffset(material.id, materialOffset); - AddIndexOffset(material.normalTexture.id, texturesOffset); - AddIndexOffset(material.occlusionTexture.id, texturesOffset); - AddIndexOffset(material.emissiveTextureId, texturesOffset); + AddIndexOffset(material.normalTexture.textureId, texturesOffset); + AddIndexOffset(material.occlusionTexture.textureId, texturesOffset); + AddIndexOffset(material.emissiveTexture.textureId, texturesOffset); - AddIndexOffset(material.metallicRoughness.baseColorTextureId, texturesOffset); - AddIndexOffset(material.metallicRoughness.metallicRoughnessTextureId, texturesOffset); + AddIndexOffset(material.metallicRoughness.baseColorTexture.textureId, texturesOffset); + AddIndexOffset(material.metallicRoughness.metallicRoughnessTexture.textureId, texturesOffset); - AddIndexOffset(material.specularGlossiness.diffuseTextureId, texturesOffset); - AddIndexOffset(material.specularGlossiness.specularGlossinessTextureId, texturesOffset); + if (material.HasExtension()) + { + AddIndexOffset(material.GetExtension().diffuseTexture.textureId, texturesOffset); + AddIndexOffset(material.GetExtension().specularGlossinessTexture.textureId, texturesOffset); + } // MSFT_packing_occlusionRoughnessMetallic packed textures auto ormExtensionIt = material.extensions.find(EXTENSION_MSFT_PACKING_ORM); @@ -333,15 +346,15 @@ namespace for (auto &primitive : mesh.primitives) { - AddIndexOffset(primitive.positionsAccessorId, accessorOffset); - AddIndexOffset(primitive.normalsAccessorId, accessorOffset); AddIndexOffset(primitive.indicesAccessorId, accessorOffset); - AddIndexOffset(primitive.uv0AccessorId, accessorOffset); - AddIndexOffset(primitive.uv1AccessorId, accessorOffset); - AddIndexOffset(primitive.color0AccessorId, accessorOffset); - AddIndexOffset(primitive.tangentsAccessorId, accessorOffset); - AddIndexOffset(primitive.joints0AccessorId, accessorOffset); - AddIndexOffset(primitive.weights0AccessorId, accessorOffset); + AddIndexOffset(primitive, ACCESSOR_POSITION, accessorOffset); + AddIndexOffset(primitive, ACCESSOR_NORMAL, accessorOffset); + AddIndexOffset(primitive, ACCESSOR_TEXCOORD_0, accessorOffset); + AddIndexOffset(primitive, ACCESSOR_TEXCOORD_1, accessorOffset); + AddIndexOffset(primitive, ACCESSOR_COLOR_0, accessorOffset); + AddIndexOffset(primitive, ACCESSOR_TANGENT, accessorOffset); + AddIndexOffset(primitive, ACCESSOR_JOINTS_0, accessorOffset); + AddIndexOffset(primitive, ACCESSOR_WEIGHTS_0, accessorOffset); if (sharedMaterials) { @@ -363,9 +376,12 @@ namespace localMaterial.metallicRoughness.baseColorFactor == globalMaterial.metallicRoughness.baseColorFactor && localMaterial.metallicRoughness.metallicFactor == globalMaterial.metallicRoughness.metallicFactor && localMaterial.occlusionTexture.strength == globalMaterial.occlusionTexture.strength && - localMaterial.specularGlossiness.diffuseFactor == globalMaterial.specularGlossiness.diffuseFactor && - localMaterial.specularGlossiness.glossinessFactor == globalMaterial.specularGlossiness.glossinessFactor && - localMaterial.specularGlossiness.specularFactor == globalMaterial.specularGlossiness.specularFactor; + localMaterial.HasExtension() == globalMaterial.HasExtension() && + (!localMaterial.HasExtension() || + (localMaterial.GetExtension().diffuseFactor == globalMaterial.GetExtension().diffuseFactor && + localMaterial.GetExtension().glossinessFactor == globalMaterial.GetExtension().glossinessFactor && + localMaterial.GetExtension().specularFactor == globalMaterial.GetExtension().specularFactor) + ); } ); @@ -454,10 +470,8 @@ namespace newAnimation.samplers.Append(std::move(newSampler)); } - size_t channelsOffset = baseAnimation.channels.size(); for (auto channel : lodAnimation.channels) { - AddIndexOffset(channel.id, channelsOffset); AddIndexOffset(channel.target.nodeId, nodeOffset); AddIndexOffset(channel.samplerId, samplerOffset); @@ -485,7 +499,7 @@ namespace } } -LODMap GLTFLODUtils::ParseDocumentNodeLODs(const GLTFDocument& doc) +LODMap GLTFLODUtils::ParseDocumentNodeLODs(const Document& doc) { LODMap lodMap; @@ -497,14 +511,14 @@ LODMap GLTFLODUtils::ParseDocumentNodeLODs(const GLTFDocument& doc) return lodMap; } -GLTFDocument GLTFLODUtils::MergeDocumentsAsLODs(const std::vector& docs, const std::vector& relativePaths, const bool& sharedMaterials) +Document GLTFLODUtils::MergeDocumentsAsLODs(const std::vector& docs, const std::vector& relativePaths, const bool& sharedMaterials) { if (docs.empty()) { throw std::invalid_argument("MergeDocumentsAsLODs passed empty vector"); } - GLTFDocument gltfPrimary(docs[0]); + Document gltfPrimary(docs[0]); LODMap lods = ParseDocumentNodeLODs(gltfPrimary); for (size_t i = 1; i < docs.size(); i++) @@ -532,9 +546,9 @@ GLTFDocument GLTFLODUtils::MergeDocumentsAsLODs(const std::vector& return gltfPrimary; } -GLTFDocument GLTFLODUtils::MergeDocumentsAsLODs(const std::vector& docs, const std::vector& screenCoveragePercentages, const std::vector& relativePaths, const bool& sharedMaterials) +Document GLTFLODUtils::MergeDocumentsAsLODs(const std::vector& docs, const std::vector& screenCoveragePercentages, const std::vector& relativePaths, const bool& sharedMaterials) { - GLTFDocument merged = MergeDocumentsAsLODs(docs, relativePaths, sharedMaterials); + Document merged = MergeDocumentsAsLODs(docs, relativePaths, sharedMaterials); if (screenCoveragePercentages.size() == 0) { @@ -571,7 +585,7 @@ GLTFDocument GLTFLODUtils::MergeDocumentsAsLODs(const std::vector& return merged; } -uint32_t GLTFLODUtils::NumberOfNodeLODLevels(const GLTFDocument& doc, const LODMap& lods) +uint32_t GLTFLODUtils::NumberOfNodeLODLevels(const Document& doc, const LODMap& lods) { size_t maxLODLevel = 0; for (auto node : doc.nodes.Elements()) diff --git a/glTF-Toolkit/src/GLTFMeshCompressionUtils.cpp b/glTF-Toolkit/src/GLTFMeshCompressionUtils.cpp new file mode 100644 index 0000000..f89acf6 --- /dev/null +++ b/glTF-Toolkit/src/GLTFMeshCompressionUtils.cpp @@ -0,0 +1,290 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#include "pch.h" +#include "AccessorUtils.h" + +#include "GLTFMeshCompressionUtils.h" +#include "GLTFSDK/MeshPrimitiveUtils.h" +#include "GLTFSDK/ExtensionsKHR.h" +#include "GLTFSDK/BufferBuilder.h" + +#pragma warning(push) +#pragma warning(disable: 4018 4081 4244 4267 4389) +#include "draco/compression/encode.h" +#include "draco/core/cycle_timer.h" +#include "draco/io/mesh_io.h" +#include "draco/io/point_cloud_io.h" +#pragma warning(pop) + +// Usings for glTF +using namespace Microsoft::glTF; +using namespace Microsoft::glTF::Toolkit; + +std::wstring PathConcat(const std::wstring& part1, const std::wstring& part2) +{ + wchar_t uriAbsoluteRaw[MAX_PATH]; + // Note: PathCchCombine will return the last argument if it's an absolute path + if (FAILED(::PathCchCombine(uriAbsoluteRaw, ARRAYSIZE(uriAbsoluteRaw), part1.c_str(), part2.c_str()))) + { + auto msg = L"Could not combine the path names: " + part1 + L" and " + part2; + throw std::invalid_argument(std::string(msg.begin(), msg.end())); + } + + return uriAbsoluteRaw; +} + +std::string PathConcat(const std::string& part1, const std::string& part2) +{ + std::wstring part1W = std::wstring(part1.begin(), part1.end()); + std::wstring part2W = std::wstring(part2.begin(), part2.end()); + + auto pathW = PathConcat(part1W, part2W); + return std::string(pathW.begin(), pathW.end()); +} + +class FilepathStreamWriter : public IStreamWriter +{ +public: + FilepathStreamWriter(std::string uriBase) : m_uriBase(uriBase) {} + + virtual ~FilepathStreamWriter() override {} + virtual std::shared_ptr GetOutputStream(const std::string& filename) const override + { + return std::make_shared(PathConcat(m_uriBase, filename), std::ios::binary); + } +private: + const std::string m_uriBase; +}; + +draco::GeometryAttribute::Type GetTypeFromAttributeName(const std::string& name) +{ + if (name == ACCESSOR_POSITION) + { + return draco::GeometryAttribute::Type::POSITION; + } + if (name == ACCESSOR_NORMAL) + { + return draco::GeometryAttribute::Type::NORMAL; + } + if (name == ACCESSOR_TEXCOORD_0) + { + return draco::GeometryAttribute::Type::TEX_COORD; + } + if (name == ACCESSOR_TEXCOORD_1) + { + return draco::GeometryAttribute::Type::TEX_COORD; + } + if (name == ACCESSOR_COLOR_0) + { + return draco::GeometryAttribute::Type::COLOR; + } + if (name == ACCESSOR_JOINTS_0) + { + return draco::GeometryAttribute::Type::GENERIC; + } + if (name == ACCESSOR_WEIGHTS_0) + { + return draco::GeometryAttribute::Type::GENERIC; + } + if (name == ACCESSOR_TANGENT) + { + return draco::GeometryAttribute::Type::GENERIC; + } + return draco::GeometryAttribute::Type::GENERIC; +} + +draco::DataType GetDataType(const Accessor& accessor) +{ + switch (accessor.componentType) + { + case COMPONENT_BYTE: return draco::DataType::DT_INT8; + case COMPONENT_UNSIGNED_BYTE: return draco::DataType::DT_UINT8; + case COMPONENT_SHORT: return draco::DataType::DT_INT16; + case COMPONENT_UNSIGNED_SHORT: return draco::DataType::DT_UINT16; + case COMPONENT_UNSIGNED_INT: return draco::DataType::DT_UINT32; + case COMPONENT_FLOAT: return draco::DataType::DT_FLOAT32; + } + return draco::DataType::DT_INVALID; +} + +template +int InitializePointAttribute(draco::Mesh& dracoMesh, const std::string& attributeName, const Document& doc, GLTFResourceReader& reader, Accessor& accessor) +{ + auto stride = sizeof(T) * Accessor::GetTypeCount(accessor.type); + auto numComponents = Accessor::GetTypeCount(accessor.type); + draco::PointAttribute pointAttr; + pointAttr.Init(GetTypeFromAttributeName(attributeName), nullptr, numComponents, GetDataType(accessor), accessor.normalized, stride, 0); + int attId = dracoMesh.AddAttribute(pointAttr, true, static_cast(accessor.count)); + auto attrActual = dracoMesh.attribute(attId); + + std::vector values = reader.ReadBinaryData(doc, accessor); + + if ((accessor.min.empty() || accessor.max.empty()) && !values.empty()) + { + auto minmax = AccessorUtils::CalculateMinMax(accessor, values); + accessor.min = minmax.first; + accessor.max = minmax.second; + } + + for (draco::PointIndex i(0); i < static_cast(accessor.count); ++i) + { + attrActual->SetAttributeValue(attrActual->mapped_index(i), &values[i.value() * numComponents]); + } + if (dracoMesh.num_points() == 0) + { + dracoMesh.set_num_points(static_cast(accessor.count)); + } + else if (dracoMesh.num_points() != accessor.count) + { + throw GLTFException("Inconsistent points count."); + } + + return attId; +} + +void SetEncoderOptions(draco::Encoder& encoder, const CompressionOptions& options) +{ + encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, options.PositionQuantizationBits); + encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, options.TexCoordQuantizationBits); + encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, options.NormalQuantizationBits); + encoder.SetAttributeQuantization(draco::GeometryAttribute::COLOR, options.ColorQuantizationBits); + encoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC, options.GenericQuantizationBits); + encoder.SetSpeedOptions(options.Speed, options.Speed); + encoder.SetTrackEncodedProperties(true); +} + +Document GLTFMeshCompressionUtils::CompressMesh( + std::shared_ptr streamReader, + const Document & doc, + CompressionOptions options, + const Mesh & mesh, + BufferBuilder* builder, + std::unordered_set& bufferViewsToRemove) +{ + GLTFResourceReader reader(streamReader); + Document resultDocument(doc); + draco::Encoder encoder; + SetEncoderOptions(encoder, options); + + Mesh resultMesh(mesh); + resultMesh.primitives.clear(); + for (const auto& primitive : mesh.primitives) + { + if (primitive.HasExtension()) + { + continue; + } + auto dracoExtension = std::make_unique(); + draco::Mesh dracoMesh; + auto indices = MeshPrimitiveUtils::GetIndices32(doc, reader, primitive); + size_t numFaces = indices.size() / 3; + dracoMesh.SetNumFaces(numFaces); + for (uint32_t i = 0; i < numFaces; i++) + { + draco::Mesh::Face face; + face[0] = indices[(i * 3) + 0]; + face[1] = indices[(i * 3) + 1]; + face[2] = indices[(i * 3) + 2]; + dracoMesh.SetFace(draco::FaceIndex(i), face); + } + + Accessor indiciesAccessor(doc.accessors[primitive.indicesAccessorId]); + bufferViewsToRemove.emplace(indiciesAccessor.bufferViewId); + indiciesAccessor.bufferViewId = ""; + indiciesAccessor.byteOffset = 0; + resultDocument.accessors.Replace(indiciesAccessor); + + for (const auto& attribute : primitive.attributes) + { + const auto& accessor = doc.accessors[attribute.second]; + Accessor attributeAccessor(accessor); + int attId; + switch (accessor.componentType) + { + case COMPONENT_BYTE: attId = InitializePointAttribute(dracoMesh, attribute.first, doc, reader, attributeAccessor); break; + case COMPONENT_UNSIGNED_BYTE: attId = InitializePointAttribute(dracoMesh, attribute.first, doc, reader, attributeAccessor); break; + case COMPONENT_SHORT: attId = InitializePointAttribute(dracoMesh, attribute.first, doc, reader, attributeAccessor); break; + case COMPONENT_UNSIGNED_SHORT: attId = InitializePointAttribute(dracoMesh, attribute.first, doc, reader, attributeAccessor); break; + case COMPONENT_UNSIGNED_INT: attId = InitializePointAttribute(dracoMesh, attribute.first, doc, reader, attributeAccessor); break; + case COMPONENT_FLOAT: attId = InitializePointAttribute(dracoMesh, attribute.first, doc, reader, attributeAccessor); break; + default: throw GLTFException("Unknown component type."); + } + + bufferViewsToRemove.emplace(accessor.bufferViewId); + attributeAccessor.bufferViewId = ""; + attributeAccessor.byteOffset = 0; + resultDocument.accessors.Replace(attributeAccessor); + + dracoExtension->attributes.emplace(attribute.first, dracoMesh.attribute(attId)->unique_id()); + } + if (primitive.targets.size() > 0) + { + // Set sequential encoding to preserve order of vertices. + encoder.SetEncodingMethod(draco::MESH_SEQUENTIAL_ENCODING); + } + + dracoMesh.DeduplicateAttributeValues(); + dracoMesh.DeduplicatePointIds(); + draco::EncoderBuffer buffer; + const draco::Status status = encoder.EncodeMeshToBuffer(dracoMesh, &buffer); + if (!status.ok()) { + throw GLTFException(std::string("Failed to encode the mesh: ") + status.error_msg()); + } + + // We must update the original accessors to the encoding out values. + Accessor encodedIndexAccessor(resultDocument.accessors[primitive.indicesAccessorId]); + encodedIndexAccessor.count = encoder.num_encoded_faces() * 3; + resultDocument.accessors.Replace(encodedIndexAccessor); + + for (const auto& dracoAttribute : dracoExtension->attributes) + { + auto accessorId = primitive.attributes.at(dracoAttribute.first); + Accessor encodedAccessor(resultDocument.accessors[accessorId]); + encodedAccessor.count = encoder.num_encoded_points(); + resultDocument.accessors.Replace(encodedAccessor); + } + + // Finally put the encoded data in place. + auto bufferView = builder->AddBufferView(buffer.data(), buffer.size()); + dracoExtension->bufferViewId = bufferView.id; + MeshPrimitive resultPrim(primitive); + resultPrim.SetExtension(std::move(dracoExtension)); + resultMesh.primitives.emplace_back(resultPrim); + } + resultDocument.meshes.Replace(resultMesh); + + return resultDocument; +} + +Document GLTFMeshCompressionUtils::CompressMeshes(std::shared_ptr streamReader, const Document & doc, CompressionOptions options, const std::string& outputDirectory) +{ + Document resultDocument(doc); + + auto writerStream = std::make_shared(outputDirectory); + auto writer = std::make_unique(writerStream); + writer->SetUriPrefix(PathConcat(outputDirectory, "MeshCompression")); + std::unique_ptr builder = std::make_unique(std::move(writer), + [&doc](const BufferBuilder& builder) { return std::to_string(doc.buffers.Size() + builder.GetBufferCount()); }, + [&doc](const BufferBuilder& builder) { return std::to_string(doc.bufferViews.Size() + builder.GetBufferViewCount()); }, + [&doc](const BufferBuilder& builder) { return std::to_string(doc.accessors.Size() + builder.GetAccessorCount()); }); + auto buffer = builder->AddBuffer(); + std::unordered_set bufferViewsToRemove; + for (const auto& mesh : doc.meshes.Elements()) + { + resultDocument = CompressMesh(streamReader, resultDocument, options, mesh, builder.get(), bufferViewsToRemove); + } + for (const auto& bufferViewId : bufferViewsToRemove) + { + if (resultDocument.bufferViews.Has(bufferViewId)) + { + resultDocument.bufferViews.Remove(bufferViewId); + } + } + + builder->Output(resultDocument); + resultDocument.extensionsUsed.emplace(KHR::MeshPrimitives::DRACOMESHCOMPRESSION_NAME); + resultDocument.extensionsRequired.emplace(KHR::MeshPrimitives::DRACOMESHCOMPRESSION_NAME); + + return resultDocument; +} diff --git a/glTF-Toolkit/src/GLTFSpecularGlossinessUtils.cpp b/glTF-Toolkit/src/GLTFSpecularGlossinessUtils.cpp new file mode 100644 index 0000000..fb80e5c --- /dev/null +++ b/glTF-Toolkit/src/GLTFSpecularGlossinessUtils.cpp @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#include "pch.h" + +#include "GLTFTextureUtils.h" +#include "GLTFSDK/ExtensionsKHR.h" +#include "GLTFSDK/PBRUtils.h" + +#include "GLTFSpecularGlossinessUtils.h" + +// Usings for glTF +using namespace Microsoft::glTF; +using namespace Toolkit; +using namespace DirectX; + +float SolveMetallic(float diffusePerceivedBrightness, float specularPerceivedBrightness, float oneMinusSpecularStrength) +{ + if (specularPerceivedBrightness < DIELECTRIC_SPECULAR.r) + { + return 0.0f; + } + + const float a = DIELECTRIC_SPECULAR.r; + const float b = diffusePerceivedBrightness * oneMinusSpecularStrength / (1.0f - a) + specularPerceivedBrightness - 2.0f * a; + const float c = a - specularPerceivedBrightness; + const float d = std::max(0.0f, b * b - 4.0f * a * c); + return std::clamp((-b + std::sqrt(d)) / (2.0f * a), 0.0f, 1.0f); +} + +void ConvertEntrySpecularGlossinessToMetallicRoughness( + const XMVECTORF32& diffuseColor, + const XMVECTORF32& specGloss, + XMVECTORF32& diffuseOut, + float& metallicOut, + float& roughnessOut) +{ + const Color3 gltfDiffuse(diffuseColor[0], diffuseColor[1], diffuseColor[2]); + const Color3 gltfSpecular(specGloss[0], specGloss[1], specGloss[2]); + + // roughness + roughnessOut = 1.0f - specGloss[3]; + + // metalness + const float oneMinusSpecularStrength = 1.0f - gltfSpecular.GetMaxComponent(); + metallicOut = SolveMetallic(gltfDiffuse.GetPerceivedBrightness(), gltfSpecular.GetPerceivedBrightness(), oneMinusSpecularStrength); + + // diffuse color + const Color3 baseColorFromDiffuse = gltfDiffuse.Scale(oneMinusSpecularStrength / (1.0f - DIELECTRIC_SPECULAR.r) / std::max(1e-4f, 1.0f - metallicOut)); + const Color3 baseColorFromSpecular = gltfSpecular.Subtract(DIELECTRIC_SPECULAR.Scale(1.0f - metallicOut).Scale(1.0f / std::max(1e-4f, metallicOut))); + Color3 baseColor = Color3::Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallicOut * metallicOut); + baseColor.Clamp(0.0f, 1.0f); + + diffuseOut = { baseColor.r, baseColor.g, baseColor.b, diffuseColor[3] }; +} + + +void ConvertTextureSpecularGlossinessToMetallicRoughness( + ScratchImage& out_metallicRoughnessTexture, + ScratchImage& out_modulatedDiffuseTexture, + const std::unique_ptr& diffuseTexture, + const XMVECTORF32& diffuseFactor, + const std::unique_ptr& specularGlossinessTexture, + const XMVECTORF32& specularFactor) +{ + size_t targetWidth = 4; + size_t targetHeight = 4; + + uint8_t* diffusePixels = nullptr; + if (diffuseTexture != nullptr) + { + targetWidth = diffuseTexture->GetMetadata().width; + targetHeight = diffuseTexture->GetMetadata().height; + diffusePixels = diffuseTexture->GetPixels(); + } + else if (specularGlossinessTexture != nullptr) + { + targetWidth = specularGlossinessTexture->GetMetadata().width; + targetHeight = specularGlossinessTexture->GetMetadata().height; + } + + uint8_t* specGlossPixels = nullptr; + if (specularGlossinessTexture) + { + GLTFTextureUtils::ResizeIfNeeded(specularGlossinessTexture, targetWidth, targetHeight); + specGlossPixels = specularGlossinessTexture->GetPixels(); + } + + out_modulatedDiffuseTexture.Initialize2D(DXGI_FORMAT_R32G32B32A32_FLOAT, targetWidth, targetHeight, 1, 1); + out_metallicRoughnessTexture.Initialize2D(DXGI_FORMAT_R32G32B32A32_FLOAT, targetWidth, targetHeight, 1, 1); + + auto diffuseOutPixels = out_modulatedDiffuseTexture.GetPixels(); + auto metalRoughPixels = out_metallicRoughnessTexture.GetPixels(); + + for (uint32_t i = 0; i < targetHeight * targetWidth; ++i) + { + XMVECTORF32 diffuseColor { 1.0f, 1.0f, 1.0f, 1.0f }; + if (diffusePixels != nullptr) + { + memcpy_s(&diffuseColor, 16, GLTFTextureUtils::GetChannelValue(diffusePixels, i, Red), 16); + } + diffuseColor.v = diffuseColor * diffuseFactor; + + XMVECTORF32 specGloss { 1.0f, 1.0f, 1.0f, 1.0f }; + if (specularGlossinessTexture != nullptr) + { + memcpy_s(&specGloss, 16, GLTFTextureUtils::GetChannelValue(specGlossPixels, i, Red), 16); + } + specGloss.v = specGloss * specularFactor; + + float metallic; + float roughness; + XMVECTORF32 diffuseColorOut; + ConvertEntrySpecularGlossinessToMetallicRoughness(diffuseColor, specGloss, diffuseColorOut, metallic, roughness); + + *GLTFTextureUtils::GetChannelValue(metalRoughPixels, i, Green) = roughness; + *GLTFTextureUtils::GetChannelValue(metalRoughPixels, i, Blue) = metallic; + auto diffuseOutPtr = GLTFTextureUtils::GetChannelValue(diffuseOutPixels, i, Red); + memcpy_s(diffuseOutPtr, 16, diffuseColorOut, 16); + } +} + + +Document GLTFSpecularGlossinessUtils::ConvertMaterial(std::shared_ptr streamReader, const Document & doc, const Material & material, const std::string& outputDirectory) +{ + if (!material.HasExtension()) + { + return doc; + } + + Document resultDoc(doc); + Material resultMaterial(material); + resultMaterial.RemoveExtension(); + + const auto& specularGlossiness = material.GetExtension(); + + XMVECTORF32 diffuseFactorIn = { + specularGlossiness.diffuseFactor.r, + specularGlossiness.diffuseFactor.g, + specularGlossiness.diffuseFactor.b, + specularGlossiness.diffuseFactor.a + }; + + XMVECTORF32 specularFactor = { + specularGlossiness.specularFactor.r, + specularGlossiness.specularFactor.g, + specularGlossiness.specularFactor.b, + specularGlossiness.glossinessFactor + }; + + // First, we check if there actually is a diffuse or specular glossiness texture to convert. + // If not, we only perform the conversion on the materials parameters and early out. + if (specularGlossiness.diffuseTexture.textureId.empty() && + specularGlossiness.specularGlossinessTexture.textureId.empty()) + { + XMVECTORF32 diffuseFactor; + float metallicFactor; + float roughnessFactor; + + ConvertEntrySpecularGlossinessToMetallicRoughness(diffuseFactorIn, specularFactor, diffuseFactor, metallicFactor, roughnessFactor); + resultMaterial.metallicRoughness.baseColorFactor.r = diffuseFactor.f[0]; + resultMaterial.metallicRoughness.baseColorFactor.g = diffuseFactor.f[1]; + resultMaterial.metallicRoughness.baseColorFactor.b = diffuseFactor.f[2]; + resultMaterial.metallicRoughness.baseColorFactor.a = diffuseFactor.f[3]; + resultMaterial.metallicRoughness.metallicFactor = metallicFactor; + resultMaterial.metallicRoughness.roughnessFactor = roughnessFactor; + + resultDoc.materials.Replace(resultMaterial); + } + + std::string samplerId; + + // Diffuse texture + std::unique_ptr diffuseTexture; + if (!specularGlossiness.diffuseTexture.textureId.empty()) + { + try + { + diffuseTexture = std::make_unique(GLTFTextureUtils::LoadTexture(streamReader, doc, specularGlossiness.diffuseTexture.textureId, false)); + samplerId = doc.textures[specularGlossiness.diffuseTexture.textureId].samplerId; + } + catch (GLTFException) + { + throw GLTFException("Failed to load diffuse texture."); + } + } + + // SpecularGlossiness texture + std::unique_ptr specularGlossinessTexture; + if (!specularGlossiness.specularGlossinessTexture.textureId.empty()) + { + try + { + specularGlossinessTexture = std::make_unique(GLTFTextureUtils::LoadTexture(streamReader, doc, specularGlossiness.specularGlossinessTexture.textureId, false)); + samplerId = samplerId.empty() ? doc.textures[specularGlossiness.specularGlossinessTexture.textureId].samplerId : samplerId; + } + catch (GLTFException) + { + throw GLTFException("Failed to load specular glossiness texture."); + } + } + + ScratchImage metallicRoughnessTexture; + ScratchImage modulatedDiffuseTexture; + + ConvertTextureSpecularGlossinessToMetallicRoughness( + metallicRoughnessTexture, + modulatedDiffuseTexture, + diffuseTexture, + diffuseFactorIn, // will be baked into texture + specularGlossinessTexture, + specularFactor); + + Material::PBRMetallicRoughness gltfPBRMetallicRoughness; + + { + DirectX::ScratchImage converted; + if (FAILED(DirectX::Convert(*metallicRoughnessTexture.GetImage(0, 0, 0), DXGI_FORMAT_B8G8R8X8_UNORM, DirectX::TEX_FILTER_SRGB_IN, DirectX::TEX_THRESHOLD_DEFAULT, converted))) + { + throw GLTFException("Failed to convert texture to DXGI_FORMAT_B8G8R8X8_UNORM for processing."); + } + + auto metallicRoughnessPath = GLTFTextureUtils::SaveAsPng(&converted, "metallicRoughness_" + material.id + ".png", outputDirectory); + auto metallicRoughnessImageId = GLTFTextureUtils::AddImageToDocument(resultDoc, metallicRoughnessPath); + Texture mrTexture; + mrTexture.samplerId = samplerId; + mrTexture.imageId = metallicRoughnessImageId; + gltfPBRMetallicRoughness.metallicRoughnessTexture.textureId = resultDoc.textures.Append(mrTexture, AppendIdPolicy::GenerateOnEmpty).id; + } + + { + DirectX::ScratchImage converted; + if (FAILED(DirectX::Convert(*modulatedDiffuseTexture.GetImage(0, 0, 0), DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DirectX::TEX_FILTER_DEFAULT, DirectX::TEX_THRESHOLD_DEFAULT, converted))) + { + throw GLTFException("Failed to convert texture to DXGI_FORMAT_B8G8R8A8_UNORM_SRGB for processing."); + } + auto diffusePath = GLTFTextureUtils::SaveAsPng(&converted, "diffuse_" + material.id + ".png", outputDirectory, &GUID_WICPixelFormat32bppBGRA); + auto diffuseImageId = GLTFTextureUtils::AddImageToDocument(resultDoc, diffusePath); + Texture diffusGltfTexture; + diffusGltfTexture.samplerId = samplerId; + diffusGltfTexture.imageId = diffuseImageId; + gltfPBRMetallicRoughness.baseColorTexture.textureId = resultDoc.textures.Append(diffusGltfTexture, AppendIdPolicy::GenerateOnEmpty).id; + } + + resultMaterial.metallicRoughness = gltfPBRMetallicRoughness; + resultDoc.materials.Replace(resultMaterial); + + return resultDoc; +} + + +Document GLTFSpecularGlossinessUtils::ConvertMaterials(std::shared_ptr streamReader, const Document & doc, const std::string & outputDirectory) +{ + Document resultDocument(doc); + for (const auto& material : doc.materials.Elements()) + { + resultDocument = ConvertMaterial(streamReader, resultDocument, material, outputDirectory); + } + + resultDocument.extensionsUsed.erase(KHR::Materials::PBRSPECULARGLOSSINESS_NAME); + resultDocument.extensionsRequired.erase(KHR::Materials::PBRSPECULARGLOSSINESS_NAME); + + return resultDocument; +} diff --git a/glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp b/glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp index 0984470..04b24bf 100644 --- a/glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp +++ b/glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp @@ -3,7 +3,7 @@ #include "pch.h" -#include "GLTFTextureLoadingUtils.h" +#include "GLTFTextureUtils.h" #include "GLTFTexturePackingUtils.h" #include "GLTFTextureCompressionUtils.h" #include "DeviceResources.h" @@ -20,9 +20,9 @@ using namespace Microsoft::glTF::Toolkit; const char* Microsoft::glTF::Toolkit::EXTENSION_MSFT_TEXTURE_DDS = "MSFT_texture_dds"; -GLTFDocument GLTFTextureCompressionUtils::CompressTextureAsDDS(const IStreamReader& streamReader, const GLTFDocument & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize, bool generateMipMaps, bool retainOriginalImage) +Document GLTFTextureCompressionUtils::CompressTextureAsDDS(std::shared_ptr streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize, bool generateMipMaps, bool retainOriginalImage) { - GLTFDocument outputDoc(doc); + Document outputDoc(doc); // Early return cases: // - No compression requested @@ -36,7 +36,7 @@ GLTFDocument GLTFTextureCompressionUtils::CompressTextureAsDDS(const IStreamRead return outputDoc; } - auto image = std::make_unique(GLTFTextureLoadingUtils::LoadTexture(streamReader, doc, texture.id)); + auto image = std::make_unique(GLTFTextureUtils::LoadTexture(streamReader, doc, texture.id, compression == TextureCompression::BC7_SRGB ? false : true)); // Resize up to a multiple of 4 auto metadata = image->GetMetadata(); @@ -65,7 +65,7 @@ GLTFDocument GLTFTextureCompressionUtils::CompressTextureAsDDS(const IStreamRead if (resizedWidth != metadata.width || resizedHeight != metadata.height) { auto resized = std::make_unique(); - if (FAILED(DirectX::Resize(image->GetImages(), image->GetImageCount(), image->GetMetadata(), resizedWidth, resizedHeight, DirectX::TEX_FILTER_DEFAULT, *resized))) + if (FAILED(DirectX::Resize(image->GetImages(), image->GetImageCount(), image->GetMetadata(), resizedWidth, resizedHeight, DirectX::TEX_FILTER_SEPARATE_ALPHA, *resized))) { throw GLTFException("Failed to resize image."); } @@ -76,7 +76,7 @@ GLTFDocument GLTFTextureCompressionUtils::CompressTextureAsDDS(const IStreamRead if (generateMipMaps) { auto mipChain = std::make_unique(); - if (FAILED(DirectX::GenerateMipMaps(image->GetImages(), image->GetImageCount(), image->GetMetadata(), DirectX::TEX_FILTER_DEFAULT, 0, *mipChain))) + if (FAILED(DirectX::GenerateMipMaps(image->GetImages(), image->GetImageCount(), image->GetMetadata(), DirectX::TEX_FILTER_SEPARATE_ALPHA, 0, *mipChain))) { throw GLTFException("Failed to generate mip maps."); } @@ -176,9 +176,9 @@ GLTFDocument GLTFTextureCompressionUtils::CompressTextureAsDDS(const IStreamRead return outputDoc; } -GLTFDocument GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(const IStreamReader& streamReader, const GLTFDocument & doc, const std::string& outputDirectory, size_t maxTextureSize, bool retainOriginalImages) +Document GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(std::shared_ptr streamReader, const Document & doc, const std::string& outputDirectory, size_t maxTextureSize, bool retainOriginalImages) { - GLTFDocument outputDoc(doc); + Document outputDoc(doc); for (auto material : doc.materials.Elements()) { @@ -191,8 +191,8 @@ GLTFDocument GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(const }; // Compress base and emissive texture as BC7 - compressIfNotEmpty(material.metallicRoughness.baseColorTextureId, TextureCompression::BC7_SRGB); - compressIfNotEmpty(material.emissiveTextureId, TextureCompression::BC7_SRGB); + compressIfNotEmpty(material.metallicRoughness.baseColorTexture.textureId, TextureCompression::BC7_SRGB); + compressIfNotEmpty(material.emissiveTexture.textureId, TextureCompression::BC7_SRGB); // Get textures from the MSFT_packing_occlusionRoughnessMetallic extension if (material.extensions.find(EXTENSION_MSFT_PACKING_ORM) != material.extensions.end()) @@ -246,7 +246,6 @@ void GLTFTextureCompressionUtils::CompressImage(DirectX::ScratchImage& image, Te return; } - DWORD compressionFlags = DirectX::TEX_COMPRESS_DEFAULT; DXGI_FORMAT compressionFormat = DXGI_FORMAT_BC7_UNORM; switch (compression) { @@ -261,7 +260,6 @@ void GLTFTextureCompressionUtils::CompressImage(DirectX::ScratchImage& image, Te break; case TextureCompression::BC7_SRGB: compressionFormat = DXGI_FORMAT_BC7_UNORM_SRGB; - compressionFlags |= DirectX::TEX_COMPRESS_SRGB_IN; break; default: throw std::invalid_argument("Invalid compression specified."); @@ -279,7 +277,7 @@ void GLTFTextureCompressionUtils::CompressImage(DirectX::ScratchImage& image, Te if (device != nullptr) { - if (SUCCEEDED(DirectX::Compress(device.Get(), image.GetImages(), image.GetImageCount(), image.GetMetadata(), compressionFormat, compressionFlags, 0, compressedImage))) + if (SUCCEEDED(DirectX::Compress(device.Get(), image.GetImages(), image.GetImageCount(), image.GetMetadata(), compressionFormat, DirectX::TEX_COMPRESS_DEFAULT, 1, compressedImage))) { gpuCompressionSuccessful = true; } @@ -293,7 +291,7 @@ void GLTFTextureCompressionUtils::CompressImage(DirectX::ScratchImage& image, Te if (!gpuCompressionSuccessful) { // Try software compression - if (FAILED(DirectX::Compress(image.GetImages(), image.GetImageCount(), image.GetMetadata(), compressionFormat, compressionFlags | DirectX::TEX_COMPRESS_PARALLEL, 0, compressedImage))) + if (FAILED(DirectX::Compress(image.GetImages(), image.GetImageCount(), image.GetMetadata(), compressionFormat, DirectX::TEX_COMPRESS_PARALLEL, 1, compressedImage))) { throw GLTFException("Failed to compress data using software compression"); } diff --git a/glTF-Toolkit/src/GLTFTextureLoadingUtils.cpp b/glTF-Toolkit/src/GLTFTextureLoadingUtils.cpp deleted file mode 100644 index e541070..0000000 --- a/glTF-Toolkit/src/GLTFTextureLoadingUtils.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE in the project root for license information. - -#include "pch.h" - -#include "GLTFTextureLoadingUtils.h" - -using namespace Microsoft::glTF; -using namespace Microsoft::glTF::Toolkit; - -namespace -{ -} - -DirectX::ScratchImage GLTFTextureLoadingUtils::LoadTexture(const IStreamReader& streamReader, const GLTFDocument& doc, const std::string& textureId) -{ - DirectX::ScratchImage output; - - const Texture& texture = doc.textures.Get(textureId); - - GLTFResourceReader gltfResourceReader(streamReader); - - const Image& image = doc.images.Get(texture.imageId); - - std::vector imageData = gltfResourceReader.ReadBinaryData(doc, image); - - auto data = std::make_unique(imageData.size()); - memcpy_s(data.get(), imageData.size(), imageData.data(), imageData.size()); - - DirectX::TexMetadata info; - if (FAILED(DirectX::LoadFromDDSMemory(data.get(), imageData.size(), DirectX::DDS_FLAGS_NONE, &info, output))) - { - // DDS failed, try WIC - // Note: try DDS first since WIC can load some DDS (but not all), so we wouldn't want to get - // a partial or invalid DDS loaded from WIC. - if (FAILED(DirectX::LoadFromWICMemory(data.get(), imageData.size(), DirectX::WIC_FLAGS_IGNORE_SRGB, &info, output))) - { - throw GLTFException("Failed to load image - Image could not be loaded as DDS or read by WIC."); - } - } - - if (info.format == DXGI_FORMAT_R32G32B32A32_FLOAT) - { - return output; - } - else - { - DirectX::ScratchImage converted; - if (FAILED(DirectX::Convert(*output.GetImage(0, 0, 0), DXGI_FORMAT_R32G32B32A32_FLOAT, DirectX::TEX_FILTER_DEFAULT, DirectX::TEX_THRESHOLD_DEFAULT, converted))) - { - throw GLTFException("Failed to convert texture to DXGI_FORMAT_R32G32B32A32_FLOAT for processing."); - } - - return converted; - } -} \ No newline at end of file diff --git a/glTF-Toolkit/src/GLTFTexturePackingUtils.cpp b/glTF-Toolkit/src/GLTFTexturePackingUtils.cpp index d93ecfd..6083c92 100644 --- a/glTF-Toolkit/src/GLTFTexturePackingUtils.cpp +++ b/glTF-Toolkit/src/GLTFTexturePackingUtils.cpp @@ -5,7 +5,7 @@ #include -#include "GLTFTextureLoadingUtils.h" +#include "GLTFTextureUtils.h" #include "GLTFTexturePackingUtils.h" using namespace Microsoft::glTF; @@ -21,57 +21,7 @@ const char* Microsoft::glTF::Toolkit::MSFT_PACKING_NRM_KEY = "normalRoughnessMet namespace { - enum Channel - { - Red = 0, - Green = 4, - Blue = 8, - Alpha = 12 - }; - - // Constants for the format DXGI_FORMAT_R32G32B32A32_FLOAT - const size_t DXGI_FORMAT_R32G32B32A32_FLOAT_STRIDE = 16; - - // Gets the value of channel `channel` in pixel index `offset` in image `imageData` - // assumed to be formatted as DXGI_FORMAT_R32G32B32A32_FLOAT - float* GetChannelValue(uint8_t * imageData, size_t offset, Channel channel) - { - return reinterpret_cast(imageData + offset * DXGI_FORMAT_R32G32B32A32_FLOAT_STRIDE + channel); - } - - std::string SaveAsPng(std::unique_ptr& image, const std::string& fileName, const std::string& directory, const GUID* targetFormat = &GUID_WICPixelFormat24bppBGR) - { - wchar_t outputImageFullPath[MAX_PATH]; - auto fileNameW = std::wstring(fileName.begin(), fileName.end()); - auto directoryW = std::wstring(directory.begin(), directory.end()); - - if (FAILED(::PathCchCombine(outputImageFullPath, ARRAYSIZE(outputImageFullPath), directoryW.c_str(), fileNameW.c_str()))) - { - throw GLTFException("Failed to compose output file path."); - } - - const DirectX::Image* img = image->GetImage(0, 0, 0); - if (FAILED(SaveToWICFile(*img, DirectX::WIC_FLAGS::WIC_FLAGS_NONE, GUID_ContainerFormatPng, outputImageFullPath, targetFormat))) - { - throw GLTFException("Failed to save file."); - } - - std::wstring outputImageFullPathStr(outputImageFullPath); - return std::string(outputImageFullPathStr.begin(), outputImageFullPathStr.end()); - } - - std::string AddImageToDocument(GLTFDocument& doc, const std::string& imageUri) - { - Image image; - auto imageId = std::to_string(doc.images.Size()); - image.id = imageId; - image.uri = imageUri; - doc.images.Append(std::move(image)); - - return imageId; - } - - void AddTextureToExtension(const std::string& imageId, TexturePacking packing, GLTFDocument& doc, rapidjson::Value& packedExtensionJson, rapidjson::MemoryPoolAllocator<>& a) + void AddTextureToExtension(const std::string& imageId, TexturePacking packing, Document& doc, rapidjson::Value& packedExtensionJson, rapidjson::MemoryPoolAllocator<>& a) { Texture packedTexture; auto textureId = std::to_string(doc.textures.Size()); @@ -98,40 +48,11 @@ namespace throw GLTFException("Invalid packing."); } } - - void ResizeIfNeeded(std::unique_ptr& image, size_t resizedWidth, size_t resizedHeight) - { - auto metadata = image->GetMetadata(); - if (resizedWidth != metadata.width || resizedHeight != metadata.height) - { - auto resized = std::make_unique(); - if (FAILED(DirectX::Resize(image->GetImages(), image->GetImageCount(), metadata, resizedWidth, resizedHeight, DirectX::TEX_FILTER_DEFAULT, *resized))) - { - throw GLTFException("Failed to resize image while packing."); - } - - image = std::move(resized); - } - } - - void ResizeToLargest(std::unique_ptr& image1, std::unique_ptr& image2) - { - auto metadata1 = image1->GetMetadata(); - auto metadata2 = image2->GetMetadata(); - if (metadata1.height != metadata2.height || metadata1.width != metadata2.width) - { - auto resizedWidth = std::max(metadata1.width, metadata2.width); - auto resizedHeight = std::max(metadata1.height, metadata2.height); - - ResizeIfNeeded(image1, resizedWidth, resizedHeight); - ResizeIfNeeded(image2, resizedWidth, resizedHeight); - } - } } -GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamReader& streamReader, const GLTFDocument& doc, const Material& material, TexturePacking packing, const std::string& outputDirectory) +Document GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::shared_ptr streamReader, const Document& doc, const Material& material, TexturePacking packing, const std::string& outputDirectory) { - GLTFDocument outputDoc(doc); + Document outputDoc(doc); // No packing requested, return copy of document if (packing == TexturePacking::None) @@ -140,9 +61,9 @@ GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamRead } // Read images from material - auto metallicRoughness = material.metallicRoughness.metallicRoughnessTextureId; - auto normal = material.normalTexture.id; - auto occlusion = material.occlusionTexture.id; + auto metallicRoughness = material.metallicRoughness.metallicRoughnessTexture.textureId; + auto normal = material.normalTexture.textureId; + auto occlusion = material.occlusionTexture.textureId; bool hasMR = !metallicRoughness.empty(); bool hasNormal = !normal.empty(); @@ -174,7 +95,7 @@ GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamRead { try { - metallicRoughnessImage = std::make_unique(GLTFTextureLoadingUtils::LoadTexture(streamReader, doc, metallicRoughness)); + metallicRoughnessImage = std::make_unique(GLTFTextureUtils::LoadTexture(streamReader, doc, metallicRoughness)); } catch (GLTFException) { @@ -189,7 +110,7 @@ GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamRead { try { - occlusionImage = std::make_unique(GLTFTextureLoadingUtils::LoadTexture(streamReader, doc, occlusion)); + occlusionImage = std::make_unique(GLTFTextureUtils::LoadTexture(streamReader, doc, occlusion)); } catch (GLTFException) { @@ -199,7 +120,7 @@ GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamRead if (hasMR && hasOcclusion && packingIncludesOrm) { - ResizeToLargest(metallicRoughnessImage, occlusionImage); + GLTFTextureUtils::ResizeToLargest(metallicRoughnessImage, occlusionImage); } bool packingIncludesNrm = (packing & TexturePacking::NormalRoughnessMetallic) > 0; @@ -209,7 +130,7 @@ GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamRead { try { - normalImage = std::make_unique(GLTFTextureLoadingUtils::LoadTexture(streamReader, doc, normal)); + normalImage = std::make_unique(GLTFTextureUtils::LoadTexture(streamReader, doc, normal)); } catch (GLTFException) { @@ -219,7 +140,7 @@ GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamRead if (hasMR && hasNormal && packingIncludesNrm) { - ResizeToLargest(metallicRoughnessImage, normalImage); + GLTFTextureUtils::ResizeToLargest(metallicRoughnessImage, normalImage); } uint8_t *mrPixels = metallicRoughnessImage != nullptr ? metallicRoughnessImage->GetPixels() : nullptr; @@ -241,30 +162,37 @@ GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamRead } else { - auto orm = std::make_unique(); + DirectX::ScratchImage orm; auto sourceImage = hasMR ? *metallicRoughnessImage->GetImage(0, 0, 0) : *occlusionImage->GetImage(0, 0, 0); - if (FAILED(orm->Initialize2D(sourceImage.format, sourceImage.width, sourceImage.height, 1, 1))) + if (FAILED(orm.Initialize2D(sourceImage.format, sourceImage.width, sourceImage.height, 1, 1))) { throw GLTFException("Failed to initialize from texture."); } - auto ormPixels = orm->GetPixels(); - auto metadata = orm->GetMetadata(); + auto ormPixels = orm.GetPixels(); + auto metadata = orm.GetMetadata(); for (size_t i = 0; i < metadata.width * metadata.height; i += 1) { // Occlusion: Occ [R] -> ORM [R] - *GetChannelValue(ormPixels, i, Channel::Red) = hasOcclusion ? *GetChannelValue(occlusionPixels, i, Channel::Red) : 255.0f; + *GLTFTextureUtils::GetChannelValue(ormPixels, i, Channel::Red) = hasOcclusion ? *GLTFTextureUtils::GetChannelValue(occlusionPixels, i, Channel::Red) : 255.0f; // Roughness: MR [G] -> ORM [G] - *GetChannelValue(ormPixels, i, Channel::Green) = hasMR ? *GetChannelValue(mrPixels, i, Channel::Green) : 255.0f; + *GLTFTextureUtils::GetChannelValue(ormPixels, i, Channel::Green) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Green) : 255.0f; // Metalness: MR [B] -> ORM [B] - *GetChannelValue(ormPixels, i, Channel::Blue) = hasMR ? *GetChannelValue(mrPixels, i, Channel::Blue) : 255.0f; + *GLTFTextureUtils::GetChannelValue(ormPixels, i, Channel::Blue) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Blue) : 255.0f; } - auto imagePath = SaveAsPng(orm, "packing_orm_" + material.id + ".png", outputDirectory); + // Convert with assumed sRGB because PNG defaults to that color space. + DirectX::ScratchImage converted; + if (FAILED(DirectX::Convert(*orm.GetImage(0, 0, 0), DXGI_FORMAT_B8G8R8X8_UNORM, DirectX::TEX_FILTER_SRGB_IN, DirectX::TEX_THRESHOLD_DEFAULT, converted))) + { + throw GLTFException("Failed to convert texture to DXGI_FORMAT_B8G8R8X8_UNORM for storage."); + } - ormImageId = AddImageToDocument(outputDoc, imagePath); + auto imagePath = GLTFTextureUtils::SaveAsPng(&converted, "packing_orm_" + material.id + ".png", outputDirectory); + + ormImageId = GLTFTextureUtils::AddImageToDocument(outputDoc, imagePath); } AddTextureToExtension(ormImageId, TexturePacking::OcclusionRoughnessMetallic, outputDoc, ormExtensionJson, ormAllocator); @@ -272,63 +200,71 @@ GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamRead if (packing & TexturePacking::RoughnessMetallicOcclusion && (hasMR || hasOcclusion)) { - auto rmo = std::make_unique(); + DirectX::ScratchImage rmo; auto sourceImage = hasMR ? *metallicRoughnessImage->GetImage(0, 0, 0) : *occlusionImage->GetImage(0, 0, 0); - if (FAILED(rmo->Initialize2D(sourceImage.format, sourceImage.width, sourceImage.height, 1, 1))) + if (FAILED(rmo.Initialize2D(sourceImage.format, sourceImage.width, sourceImage.height, 1, 1))) { throw GLTFException("Failed to initialize from texture."); } - auto rmoPixels = rmo->GetPixels(); - auto metadata = rmo->GetMetadata(); + auto rmoPixels = rmo.GetPixels(); + auto metadata = rmo.GetMetadata(); for (size_t i = 0; i < metadata.width * metadata.height; i += 1) { // Roughness: MR [G] -> RMO [R] - *GetChannelValue(rmoPixels, i, Channel::Red) = hasMR ? *GetChannelValue(mrPixels, i, Channel::Green) : 255.0f; + *GLTFTextureUtils::GetChannelValue(rmoPixels, i, Channel::Red) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Green) : 255.0f; // Metalness: MR [B] -> RMO [G] - *GetChannelValue(rmoPixels, i, Channel::Green) = hasMR ? *GetChannelValue(mrPixels, i, Channel::Blue) : 255.0f; + *GLTFTextureUtils::GetChannelValue(rmoPixels, i, Channel::Green) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Blue) : 255.0f; // Occlusion: Occ [R] -> RMO [B] - *GetChannelValue(rmoPixels, i, Channel::Blue) = hasOcclusion ? *GetChannelValue(occlusionPixels, i, Channel::Red) : 255.0f; + *GLTFTextureUtils::GetChannelValue(rmoPixels, i, Channel::Blue) = hasOcclusion ? *GLTFTextureUtils::GetChannelValue(occlusionPixels, i, Channel::Red) : 255.0f; + } + + // Convert with assumed sRGB because PNG defaults to that color space. + DirectX::ScratchImage converted; + if (FAILED(DirectX::Convert(*rmo.GetImage(0, 0, 0), DXGI_FORMAT_B8G8R8X8_UNORM, DirectX::TEX_FILTER_SRGB_IN, DirectX::TEX_THRESHOLD_DEFAULT, converted))) + { + throw GLTFException("Failed to convert texture to DXGI_FORMAT_B8G8R8X8_UNORM for storage."); } - auto imagePath = SaveAsPng(rmo, "packing_rmo_" + material.id + ".png", outputDirectory); + auto imagePath = GLTFTextureUtils::SaveAsPng(&converted, "packing_rmo_" + material.id + ".png", outputDirectory); // Add back to GLTF - auto rmoImageId = AddImageToDocument(outputDoc, imagePath); + auto rmoImageId = GLTFTextureUtils::AddImageToDocument(outputDoc, imagePath); AddTextureToExtension(rmoImageId, TexturePacking::RoughnessMetallicOcclusion, outputDoc, ormExtensionJson, ormAllocator); } if (packingIncludesNrm && (hasMR || hasNormal)) { - auto nrm = std::make_unique(); + DirectX::ScratchImage nrm; auto sourceImage = hasMR ? *metallicRoughnessImage->GetImage(0, 0, 0) : *normalImage->GetImage(0, 0, 0); - if (FAILED(nrm->Initialize2D(sourceImage.format, sourceImage.width, sourceImage.height, 1, 1))) + if (FAILED(nrm.Initialize2D(sourceImage.format, sourceImage.width, sourceImage.height, 1, 1))) { throw GLTFException("Failed to initialize from texture."); } - auto nrmPixels = nrm->GetPixels(); - auto metadata = nrm->GetMetadata(); + auto nrmPixels = nrm.GetPixels(); + auto metadata = nrm.GetMetadata(); for (size_t i = 0; i < metadata.width * metadata.height; i += 1) { // Normal: N [RG] -> NRM [RG] - *GetChannelValue(nrmPixels, i, Channel::Red) = hasNormal ? *GetChannelValue(normalPixels, i, Channel::Red) : 255.0f; - *GetChannelValue(nrmPixels, i, Channel::Green) = hasNormal ? *GetChannelValue(normalPixels, i, Channel::Green) : 255.0f; + *GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Red) = hasNormal ? *GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Red) : 255.0f; + *GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Green) = hasNormal ? *GLTFTextureUtils::GetChannelValue(normalPixels, i, Channel::Green) : 255.0f; // Roughness: MR [G] -> NRM [B] - *GetChannelValue(nrmPixels, i, Channel::Blue) = hasMR ? *GetChannelValue(mrPixels, i, Channel::Green) : 255.0f; + *GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Blue) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Green) : 255.0f; // Metalness: MR [B] -> NRM [A] - *GetChannelValue(nrmPixels, i, Channel::Alpha) = hasMR ? *GetChannelValue(mrPixels, i, Channel::Blue) : 255.0f; + *GLTFTextureUtils::GetChannelValue(nrmPixels, i, Channel::Alpha) = hasMR ? *GLTFTextureUtils::GetChannelValue(mrPixels, i, Channel::Blue) : 255.0f; } - auto imagePath = SaveAsPng(nrm, "packing_nrm_" + material.id + ".png", outputDirectory, &GUID_WICPixelFormat32bppBGRA); + // sRGB conversion not needed for PNG in BGRA + auto imagePath = GLTFTextureUtils::SaveAsPng(&nrm, "packing_nrm_" + material.id + ".png", outputDirectory, &GUID_WICPixelFormat32bppBGRA); // Add back to GLTF - auto nrmImageId = AddImageToDocument(outputDoc, imagePath); + auto nrmImageId = GLTFTextureUtils::AddImageToDocument(outputDoc, imagePath); AddTextureToExtension(nrmImageId, TexturePacking::NormalRoughnessMetallic, outputDoc, nrmExtensionJson, nrmAllocator); } @@ -369,9 +305,9 @@ GLTFDocument GLTFTexturePackingUtils::PackMaterialForWindowsMR(const IStreamRead return outputDoc; } -GLTFDocument GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(const IStreamReader& streamReader, const GLTFDocument & doc, TexturePacking packing, const std::string& outputDirectory) +Document GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(std::shared_ptr streamReader, const Document & doc, TexturePacking packing, const std::string& outputDirectory) { - GLTFDocument outputDoc(doc); + Document outputDoc(doc); // No packing requested, return copy of document if (packing == TexturePacking::None) diff --git a/glTF-Toolkit/src/GLTFTextureUtils.cpp b/glTF-Toolkit/src/GLTFTextureUtils.cpp new file mode 100644 index 0000000..78c2ee5 --- /dev/null +++ b/glTF-Toolkit/src/GLTFTextureUtils.cpp @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +#include "pch.h" + +#include "GLTFTextureUtils.h" + +using namespace Microsoft::glTF; +using namespace Microsoft::glTF::Toolkit; + +DirectX::ScratchImage GLTFTextureUtils::LoadTexture(std::shared_ptr streamReader, const Document& doc, const std::string& textureId, bool treatAsLinear) +{ + DirectX::ScratchImage output; + + const Texture& texture = doc.textures.Get(textureId); + + GLTFResourceReader gltfResourceReader(streamReader); + + const Image& image = doc.images.Get(texture.imageId); + + std::vector imageData = gltfResourceReader.ReadBinaryData(doc, image); + + DirectX::TexMetadata info; + if (FAILED(DirectX::LoadFromDDSMemory(imageData.data(), imageData.size(), DirectX::DDS_FLAGS_NONE, &info, output))) + { + // DDS failed, try WIC + // Note: try DDS first since WIC can load some DDS (but not all), so we wouldn't want to get + // a partial or invalid DDS loaded from WIC. + if (FAILED(DirectX::LoadFromWICMemory(imageData.data(), imageData.size(), treatAsLinear ? DirectX::WIC_FLAGS_IGNORE_SRGB : DirectX::WIC_FLAGS_NONE, &info, output))) + { + throw GLTFException("Failed to load image - Image could not be loaded as DDS or read by WIC."); + } + } + + if (info.format == DXGI_FORMAT_R32G32B32A32_FLOAT && treatAsLinear) + { + return output; + } + else + { + DirectX::ScratchImage converted; + if (FAILED(DirectX::Convert(*output.GetImage(0, 0, 0), DXGI_FORMAT_R32G32B32A32_FLOAT, treatAsLinear ? DirectX::TEX_FILTER_DEFAULT : DirectX::TEX_FILTER_SRGB_IN, DirectX::TEX_THRESHOLD_DEFAULT, converted))) + { + throw GLTFException("Failed to convert texture to DXGI_FORMAT_R32G32B32A32_FLOAT for processing."); + } + + return converted; + } +} + +// Constants for the format DXGI_FORMAT_R32G32B32A32_FLOAT +constexpr size_t DXGI_FORMAT_R32G32B32A32_FLOAT_STRIDE = 16; + +float* GLTFTextureUtils::GetChannelValue(uint8_t * imageData, size_t offset, Channel channel) +{ + return reinterpret_cast(imageData + offset * DXGI_FORMAT_R32G32B32A32_FLOAT_STRIDE + channel); +} + +std::string GLTFTextureUtils::SaveAsPng(DirectX::ScratchImage* image, const std::string& fileName, const std::string& directory, const GUID* targetFormat) +{ + wchar_t outputImageFullPath[MAX_PATH]; + auto fileNameW = std::wstring(fileName.begin(), fileName.end()); + auto directoryW = std::wstring(directory.begin(), directory.end()); + + if (FAILED(::PathCchCombine(outputImageFullPath, ARRAYSIZE(outputImageFullPath), directoryW.c_str(), fileNameW.c_str()))) + { + throw GLTFException("Failed to compose output file path."); + } + + const DirectX::Image* img = image->GetImage(0, 0, 0); + if (FAILED(SaveToWICFile(*img, DirectX::WIC_FLAGS::WIC_FLAGS_NONE, GUID_ContainerFormatPng, outputImageFullPath, targetFormat))) + { + throw GLTFException("Failed to save file."); + } + + std::wstring outputImageFullPathStr(outputImageFullPath); + return std::string(outputImageFullPathStr.begin(), outputImageFullPathStr.end()); +} + +std::string GLTFTextureUtils::AddImageToDocument(Document& doc, const std::string& imageUri) +{ + Image image; + auto imageId = std::to_string(doc.images.Size()); + image.id = imageId; + image.uri = imageUri; + doc.images.Append(std::move(image)); + + return imageId; +} + +void GLTFTextureUtils::ResizeIfNeeded(const std::unique_ptr& image, size_t resizedWidth, size_t resizedHeight) +{ + auto metadata = image->GetMetadata(); + if (resizedWidth != metadata.width || resizedHeight != metadata.height) + { + DirectX::ScratchImage resized; + if (FAILED(DirectX::Resize(image->GetImages(), image->GetImageCount(), metadata, resizedWidth, resizedHeight, DirectX::TEX_FILTER_DEFAULT, resized))) + { + throw GLTFException("Failed to resize image while packing."); + } + + *image = std::move(resized); + } +} + +void GLTFTextureUtils::ResizeToLargest(std::unique_ptr& image1, std::unique_ptr& image2) +{ + auto metadata1 = image1->GetMetadata(); + auto metadata2 = image2->GetMetadata(); + if (metadata1.height != metadata2.height || metadata1.width != metadata2.width) + { + auto resizedWidth = std::max(metadata1.width, metadata2.width); + auto resizedHeight = std::max(metadata1.height, metadata2.height); + + ResizeIfNeeded(image1, resizedWidth, resizedHeight); + ResizeIfNeeded(image2, resizedWidth, resizedHeight); + } +} \ No newline at end of file diff --git a/glTF-Toolkit/src/SerializeBinary.cpp b/glTF-Toolkit/src/SerializeBinary.cpp index 56815f7..4d394fa 100644 --- a/glTF-Toolkit/src/SerializeBinary.cpp +++ b/glTF-Toolkit/src/SerializeBinary.cpp @@ -7,11 +7,12 @@ #include "SerializeBinary.h" #include "GLTFSDK/GLTF.h" -#include "GLTFSDK/GLTFDocument.h" +#include "GLTFSDK/Document.h" #include "GLTFSDK/GLBResourceReader.h" -#include "GLTFSDK/GLBResourceWriter2.h" +#include "GLTFSDK/GLBResourceWriter.h" #include "GLTFSDK/Serialize.h" #include "GLTFSDK/BufferBuilder.h" +#include "GLTFSDK/ExtensionsKHR.h" using namespace Microsoft::glTF; using namespace Microsoft::glTF::Toolkit; @@ -23,9 +24,9 @@ namespace auto extension = uri.substr(uri.rfind('.') + 1, 3); std::transform(extension.begin(), extension.end(), extension.begin(), [](char c) { return static_cast(::tolower(static_cast(c))); }); - if (extension == FILE_EXT_DDS) + if (extension == "dds") { - return MIMETYPE_DDS; + return "image/vnd-ms.dds"; } if (extension == FILE_EXT_JPEG) @@ -99,7 +100,7 @@ namespace } template - void SerializeAccessor(const Accessor& accessor, const GLTFDocument& doc, const GLTFResourceReader& reader, BufferBuilder& builder, const AccessorConversionStrategy& accessorConversion) + void SerializeAccessor(const Accessor& accessor, const Document& doc, const GLTFResourceReader& reader, BufferBuilder& builder, const AccessorConversionStrategy& accessorConversion) { builder.AddBufferView(doc.bufferViews.Get(accessor.bufferViewId).target); const std::vector& accessorContents = reader.ReadBinaryData(doc, accessor); @@ -121,7 +122,7 @@ namespace } } - void SerializeAccessor(const Accessor& accessor, const GLTFDocument& doc, const GLTFResourceReader& reader, BufferBuilder& builder, const AccessorConversionStrategy& accessorConversion) + void SerializeAccessor(const Accessor& accessor, const Document& doc, const GLTFResourceReader& reader, BufferBuilder& builder, const AccessorConversionStrategy& accessorConversion) { switch (accessor.componentType) { @@ -149,41 +150,100 @@ namespace } } -void Microsoft::glTF::Toolkit::SerializeBinary(const GLTFDocument& gltfDocument, +void Microsoft::glTF::Toolkit::SerializeBinary(const Document& document, const GLTFResourceReader& resourceReader, - std::unique_ptr&& outputStreamFactory, + std::shared_ptr outputStreamWriter, const AccessorConversionStrategy& accessorConversion) { - auto writer = std::make_unique(std::move(outputStreamFactory), std::string()); + auto writer = std::make_unique(std::move(outputStreamWriter)); - GLTFDocument outputDoc(gltfDocument); + Document outputDoc(document); outputDoc.buffers.Clear(); outputDoc.bufferViews.Clear(); outputDoc.accessors.Clear(); - std::unique_ptr builder = std::make_unique(std::move(writer)); + // Get the collection of bufferViews we won't move around + IndexedContainer staticBufferViews = document.bufferViews; + for (const auto& accessor : document.accessors.Elements()) + { + if (!accessor.bufferViewId.empty() && staticBufferViews.Has(accessor.bufferViewId)) + { + staticBufferViews.Remove(accessor.bufferViewId); + } + } + + for (const auto& image : outputDoc.images.Elements()) + { + if (!image.bufferViewId.empty() && staticBufferViews.Has(image.bufferViewId)) + { + staticBufferViews.Remove(image.bufferViewId); + } + } + + size_t currentAccessorId = 0; + std::string currentAccessorIdStr = std::to_string(currentAccessorId); + size_t currentBufferViewId = 0; + std::string currentBufferViewIdStr = std::to_string(currentBufferViewId); + auto AdvanceAccessorId = [¤tAccessorId, ¤tAccessorIdStr]() + { + currentAccessorId++; + currentAccessorIdStr = std::to_string(currentAccessorId); + }; + auto AdvanceBufferViewId = [¤tBufferViewId, ¤tBufferViewIdStr, &staticBufferViews]() + { + do + { + currentBufferViewId++; + currentBufferViewIdStr = std::to_string(currentBufferViewId); + } while (staticBufferViews.Has(currentBufferViewIdStr)); + }; + std::unique_ptr builder = std::make_unique(std::move(writer), + [](const BufferBuilder&) { return GLB_BUFFER_ID; }, + [¤tBufferViewIdStr](const BufferBuilder&) { return currentBufferViewIdStr; }, + [¤tAccessorIdStr](const BufferBuilder&) { return currentAccessorIdStr; }); // GLB buffer builder->AddBuffer(GLB_BUFFER_ID); + // Add those bufferView to the builder. + for (const auto& bufferView : staticBufferViews.Elements()) + { + currentBufferViewIdStr = bufferView.id; + auto data = resourceReader.ReadBinaryData(document, bufferView); + builder->AddBufferView(data); + } + // Return value to tracked state + currentBufferViewIdStr = std::to_string(currentBufferViewId); + if (staticBufferViews.Has(currentBufferViewIdStr)) + { + AdvanceBufferViewId(); + } + // Serialize accessors - for (auto accessor : gltfDocument.accessors.Elements()) + for (const auto& accessor : document.accessors.Elements()) { - if (accessor.count > 0) + if (!accessor.bufferViewId.empty() && accessor.count > 0) { - SerializeAccessor(accessor, gltfDocument, resourceReader, *builder, accessorConversion); + SerializeAccessor(accessor, document, resourceReader, *builder, accessorConversion); + AdvanceBufferViewId(); } + else + { + outputDoc.accessors.Append(accessor); + } + AdvanceAccessorId(); } // Serialize images - for (auto image : outputDoc.images.Elements()) + for (const auto& image : outputDoc.images.Elements()) { Image newImage(image); - auto data = resourceReader.ReadBinaryData(gltfDocument, image); + auto data = resourceReader.ReadBinaryData(document, image); auto imageBufferView = builder->AddBufferView(data); + AdvanceBufferViewId(); newImage.bufferViewId = imageBufferView.id; if (image.mimeType.empty()) @@ -196,10 +256,79 @@ void Microsoft::glTF::Toolkit::SerializeBinary(const GLTFDocument& gltfDocument, outputDoc.images.Replace(newImage); } + // Collect anything in extensions that looks like it should to be packed for the GLB. + for (auto& extension : outputDoc.extensions) + { + rapidjson::Document extensionJson; + extensionJson.Parse(extension.second.c_str()); + if (!extensionJson.IsObject()) + { + continue; + } + for (auto& member : extensionJson.GetObject()) + { + if (!member.value.IsArray()) + { + continue; + } + for (auto& possibleBuffer : member.value.GetArray()) + { + if (!possibleBuffer.IsObject()) + { + continue; + } + // Build an Image to object to use to load the data from. + Image tmpImg; + if (possibleBuffer.HasMember("uri")) + { + tmpImg.uri = possibleBuffer["uri"].GetString(); + } + else + { + continue; + } + try + { + auto data = resourceReader.ReadBinaryData(document, tmpImg); + auto bufferView = builder->AddBufferView(data); + AdvanceBufferViewId(); + + possibleBuffer.RemoveMember("uri"); + possibleBuffer.RemoveMember("bufferView"); + possibleBuffer.AddMember("bufferView", rapidjson::Value(std::stoi(bufferView.id)), extensionJson.GetAllocator()); + } + catch (...) + { + // Didn't work out. + continue; + } + } + } + + rapidjson::StringBuffer buffer; + rapidjson::Writer jsonWriter(buffer); + extensionJson.Accept(jsonWriter); + + extension.second = buffer.GetString(); + } + + // Fill in any gaps in the bufferViewList. + for (const auto& bufferView : staticBufferViews.Elements()) + { + auto bufferViewId = std::stoul(bufferView.id); + while (bufferViewId > currentBufferViewId) + { + std::vector data; + data.resize(4); + builder->AddBufferView(data); + AdvanceBufferViewId(); + } + } + builder->Output(outputDoc); // Add extensions and extras to bufferViews, if any - for (auto bufferView : gltfDocument.bufferViews.Elements()) + for (auto bufferView : document.bufferViews.Elements()) { auto fixedBufferView = outputDoc.bufferViews.Get(bufferView.id); fixedBufferView.extensions = bufferView.extensions; @@ -208,18 +337,26 @@ void Microsoft::glTF::Toolkit::SerializeBinary(const GLTFDocument& gltfDocument, outputDoc.bufferViews.Replace(fixedBufferView); } - auto manifest = Serialize(outputDoc); + // We may have put the bufferViews in the IndexedContainer out of order sort them now. + auto finalBufferViewList = outputDoc.bufferViews; + outputDoc.bufferViews.Clear(); + for (size_t i = 0; i < finalBufferViewList.Size(); i++) + { + outputDoc.bufferViews.Append(finalBufferViewList[std::to_string(i)]); + } + + auto manifest = Serialize(outputDoc, KHR::GetKHRExtensionSerializer()); - auto outputWriter = dynamic_cast(&builder->GetResourceWriter()); + auto outputWriter = dynamic_cast(&builder->GetResourceWriter()); if (outputWriter != nullptr) { outputWriter->Flush(manifest, std::string()); } } -void Microsoft::glTF::Toolkit::SerializeBinary(const GLTFDocument& gltfDocument, const IStreamReader& inputStreamReader, - std::unique_ptr&& outputStreamFactory, +void Microsoft::glTF::Toolkit::SerializeBinary(const Document& document, std::shared_ptr inputStreamReader, + std::shared_ptr outputStreamWriter, const AccessorConversionStrategy& accessorConversion) { - SerializeBinary(gltfDocument, GLTFResourceReader{inputStreamReader}, std::move(outputStreamFactory), accessorConversion); + SerializeBinary(document, GLTFResourceReader{inputStreamReader}, std::move(outputStreamWriter), accessorConversion); }