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 d9786d6..5152b63 100644 Binary files a/glTF-Toolkit.UWP.Test/Assets/3DModels/WaterBottle.glb and b/glTF-Toolkit.UWP.Test/Assets/3DModels/WaterBottle.glb differ 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); }