Full Code of Microsoft/glTF-Toolkit for AI

master 505b91d9a42f cached
84 files
17.0 MB
101.0k tokens
105 symbols
1 requests
Download .txt
Showing preview only (424K chars total). Download the full file or copy to clipboard to get everything.
Repository: Microsoft/glTF-Toolkit
Branch: master
Commit: 505b91d9a42f
Files: 84
Total size: 17.0 MB

Directory structure:
gitextract_t5s8ddnm/

├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── WindowsMRAssetConverter/
│   ├── AssetType.cpp
│   ├── AssetType.h
│   ├── CommandLine.cpp
│   ├── CommandLine.h
│   ├── FileSystem.cpp
│   ├── FileSystem.h
│   ├── README.md
│   ├── WindowsMRAssetConverter.cpp
│   ├── WindowsMRAssetConverter.vcxproj
│   ├── WindowsMRAssetConverter.vcxproj.filters
│   ├── packages.config
│   ├── stdafx.cpp
│   ├── stdafx.h
│   └── targetver.h
├── glTF-Toolkit/
│   ├── glTF-Toolkit.vcxproj
│   ├── glTF-Toolkit.vcxproj.filters
│   ├── inc/
│   │   ├── AccessorUtils.h
│   │   ├── DeviceResources.h
│   │   ├── GLBtoGLTF.h
│   │   ├── GLTFLODUtils.h
│   │   ├── GLTFMeshCompressionUtils.h
│   │   ├── GLTFSDK.h
│   │   ├── GLTFSpecularGlossinessUtils.h
│   │   ├── GLTFTextureCompressionUtils.h
│   │   ├── GLTFTexturePackingUtils.h
│   │   ├── GLTFTextureUtils.h
│   │   ├── SerializeBinary.h
│   │   └── pch.h
│   ├── packages.config
│   └── src/
│       ├── DeviceResources.cpp
│       ├── GLBtoGLTF.cpp
│       ├── GLTFLODUtils.cpp
│       ├── GLTFMeshCompressionUtils.cpp
│       ├── GLTFSpecularGlossinessUtils.cpp
│       ├── GLTFTextureCompressionUtils.cpp
│       ├── GLTFTexturePackingUtils.cpp
│       ├── GLTFTextureUtils.cpp
│       ├── SerializeBinary.cpp
│       └── pch.cpp
├── glTF-Toolkit.Test/
│   ├── GLBSerializerTests.cpp
│   ├── GLBtoGLTFTests.cpp
│   ├── GLTFLODUtilsTests.cpp
│   ├── GLTFTextureCompressionUtilsTests.cpp
│   ├── GLTFTexturePackingUtilsTests.cpp
│   ├── Helpers/
│   │   ├── StreamMock.h
│   │   ├── TestUtils.h
│   │   └── WStringUtils.h
│   ├── Resources/
│   │   └── gltf/
│   │       ├── CubeAsset3D.gltf
│   │       ├── CubeWithLOD.gltf
│   │       ├── TextureTest/
│   │       │   └── TextureTest.gltf
│   │       ├── WaterBottle/
│   │       │   └── WaterBottle.gltf
│   │       └── WaterBottle_ORM/
│   │           ├── WaterBottle.gltf
│   │           ├── WaterBottle_WindowsMR.gltf
│   │           ├── WaterBottle_baseColor.DDS
│   │           ├── WaterBottle_emissive.DDS
│   │           ├── WaterBottle_normal.dds
│   │           └── WaterBottle_occlusionRoughnessMetallic.dds
│   ├── glTF-Toolkit.Test.vcxproj
│   ├── glTF-Toolkit.Test.vcxproj.filters
│   └── packages.config
├── glTF-Toolkit.UWP/
│   ├── GLTFSerialization.cpp
│   ├── GLTFSerialization.h
│   ├── GLTFStreams.h
│   ├── WindowsMRConversion.cpp
│   ├── WindowsMRConversion.h
│   ├── glTF-Toolkit.UWP.vcxproj
│   ├── glTF-Toolkit.UWP.vcxproj.filters
│   ├── packages.config
│   ├── pch.cpp
│   └── pch.h
├── glTF-Toolkit.UWP.Test/
│   ├── Assets/
│   │   └── 3DModels/
│   │       └── WaterBottle.glb
│   ├── Package.appxmanifest
│   ├── Properties/
│   │   ├── AssemblyInfo.cs
│   │   └── UnitTestApp.rd.xml
│   ├── UWPTest.cs
│   ├── UnitTestApp.xaml
│   ├── UnitTestApp.xaml.cs
│   └── glTF-Toolkit.UWP.Test.csproj
└── glTF-Toolkit.sln

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Typescript v1 declaration files
typings/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush
.cr/

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs


================================================
FILE: CODE_OF_CONDUCT.md
================================================
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.


================================================
FILE: LICENSE
================================================
    MIT License

    Copyright (c) Microsoft Corporation. All rights reserved.

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE


================================================
FILE: README.md
================================================
# Microsoft glTF Toolkit

This project contains a collection of tools and libraries to modify and optimize glTF assets.

Additionally the repository includes a command line tool that uses these steps in sequence in order to convert a glTF 2.0 core asset for use in the Windows Mixed Reality home, following the published [documentation](https://developer.microsoft.com/en-us/windows/mixed-reality/creating_3d_models_for_use_in_the_windows_mixed_reality_home). The latest release of the Windows Mixed Reality Asset converter is available on the [releases tab](https://github.com/Microsoft/glTF-Toolkit/releases).

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

## Dependencies

This project consumes the following projects through NuGet packages:

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

## Building

This project can be built using Visual Studio 2017 Update 4 on Windows 10 Fall Creators Update (16299.0).

## Contributing

This project welcomes contributions and suggestions.  Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

## License

Copyright (c) Microsoft Corporation. All rights reserved.

Licensed under the [MIT License](LICENSE).


================================================
FILE: SECURITY.md
================================================
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.8 BLOCK -->

## Security

Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).

If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.

## Reporting Security Issues

**Please do not report security vulnerabilities through public GitHub issues.**

Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).

If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).

You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 

Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:

  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
  * Full paths of source file(s) related to the manifestation of the issue
  * The location of the affected source code (tag/branch/commit or direct URL)
  * Any special configuration required to reproduce the issue
  * Step-by-step instructions to reproduce the issue
  * Proof-of-concept or exploit code (if possible)
  * Impact of the issue, including how an attacker might exploit the issue

This information will help us triage your report more quickly.

If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.

## Preferred Languages

We prefer all communications to be in English.

## Policy

Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).

<!-- END MICROSOFT SECURITY.MD BLOCK -->


================================================
FILE: WindowsMRAssetConverter/AssetType.cpp
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#include "stdafx.h"
#include "AssetType.h"

const wchar_t * EXTENSION_GLTF = L".gltf";
const wchar_t * EXTENSION_GLB = L".glb";

AssetType AssetTypeUtils::AssetTypeFromFilePath(const std::wstring& assetPath)
{
    const wchar_t *inputExtensionRaw = nullptr;
    if (FAILED(PathCchFindExtension(assetPath.c_str(), assetPath.length() + 1, &inputExtensionRaw)))
    {
        throw std::invalid_argument("Invalid input file extension.");
    }

    if (_wcsicmp(inputExtensionRaw, EXTENSION_GLTF) == 0)
    {
        return AssetType::GLTF;
    }
    else if (_wcsicmp(inputExtensionRaw, EXTENSION_GLB) == 0)
    {
        return AssetType::GLB;
    }
    else
    {
        throw std::invalid_argument("Invalid file, please provide a GLTF or GLB.");
    }
}

================================================
FILE: WindowsMRAssetConverter/AssetType.h
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#pragma once

extern const wchar_t * EXTENSION_GLTF;
extern const wchar_t * EXTENSION_GLB;

enum class AssetType
{
    GLTF,
    GLB
};

namespace AssetTypeUtils
{
    AssetType AssetTypeFromFilePath(const std::wstring& assetPath);
}


================================================
FILE: WindowsMRAssetConverter/CommandLine.cpp
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#include "stdafx.h"
#include "CommandLine.h"
#include "FileSystem.h"

// Constants
const wchar_t * PARAM_OUTFILE = L"-o";
const wchar_t * PARAM_TMPDIR = L"-temp-directory";
const wchar_t * PARAM_LOD = L"-lod";
const wchar_t * PARAM_SCREENCOVERAGE = L"-screen-coverage";
const wchar_t * PARAM_MAXTEXTURESIZE = L"-max-texture-size";
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";
const wchar_t * PARAM_VALUE_DESKTOP = L"desktop";
const wchar_t * PARAM_VALUE_PC = L"pc";
const wchar_t * PARAM_VALUE_ALL = L"all";
const wchar_t * SUFFIX_CONVERTED = L"_converted";
const wchar_t * CLI_INDENT = L"    ";
const size_t MAXTEXTURESIZE_DEFAULT = 512;
const size_t MAXTEXTURESIZE_MAX = 4096;
const CommandLine::Version MIN_VERSION_DEFAULT = CommandLine::Version::Version1709;
const CommandLine::Platform PLATFORM_DEFAULT = CommandLine::Platform::Desktop;

enum class CommandLineParsingState
{
    Initial,
    InputRead,
    ReadOutFile,
    ReadTmpDir,
    ReadLods,
    ReadScreenCoverage,
    ReadMaxTextureSize,
    ReadMinVersion,
    ReadPlatform
};

void CommandLine::PrintHelp()
{
    auto indent = std::wstring(CLI_INDENT);
    std::wcerr << std::endl
        << L"Windows Mixed Reality Asset Converter" << std::endl
        << L"=====================================" << std::endl
        << std::endl
        << L"A command line tool to convert core GLTF 2.0 assets for use in "
        << L"the Windows Mixed Reality home, with the proper texture packing, compression and merged LODs." << std::endl << std::endl
        << L"Usage: WindowsMRAssetConverter <path to GLTF/GLB>" << std::endl
        << std::endl
        << L"Optional arguments:" << std::endl
        << indent << "[" << std::wstring(PARAM_OUTFILE) << L" <output file path>]" << std::endl
        << indent << "[" << std::wstring(PARAM_TMPDIR) << L" <temporary folder>] - default is the system temp folder for the user" << std::endl
        << indent << "[" << std::wstring(PARAM_PLATFORM) << " <" << PARAM_VALUE_ALL << " | " << PARAM_VALUE_HOLOGRAPHIC << " | " << PARAM_VALUE_DESKTOP << ">] - defaults to " << PARAM_VALUE_DESKTOP << std::endl
        << indent << "[" << std::wstring(PARAM_MIN_VERSION) << " <" << PARAM_VALUE_VERSION_1709 << " | " << PARAM_VALUE_VERSION_1803 << " | " << PARAM_VALUE_VERSION_1809 << " | " << PARAM_VALUE_VERSION_LATEST << ">] - defaults to " << PARAM_VALUE_VERSION_1709 << std::endl
        << indent << "[" << std::wstring(PARAM_LOD) << " <path to each lower LOD asset in descending order of quality>]" << std::endl
        << indent << "[" << std::wstring(PARAM_SCREENCOVERAGE) << " <LOD screen coverage values>]" << std::endl
        << indent << "[" << std::wstring(PARAM_SHARE_MATERIALS) << "] - disabled if not present" << std::endl
        << indent << "[" << std::wstring(PARAM_MAXTEXTURESIZE) << " <Max texture size in pixels>] - defaults to 512" << std::endl
        << indent << "[" << std::wstring(PARAM_REPLACE_TEXTURES) << "] - disabled if not present" << std::endl
        << indent << "[" << std::wstring(PARAM_COMPRESS_MESHES) << "] - compress meshes with Draco" << std::endl
        << std::endl
        << "Example:" << std::endl
        << indent << "WindowsMRAssetConverter FileToConvert.gltf "
        << std::wstring(PARAM_OUTFILE) << " ConvertedFile.glb "
        << std::wstring(PARAM_LOD) << " Lod1.gltf Lod2.gltf "
        << std::wstring(PARAM_SCREENCOVERAGE) << " 0.5 0.2 0.01" << std::endl
        << std::endl
        << "The above will convert \"FileToConvert.gltf\" into \"ConvertedFile.glb\" in the "
        << "current directory." << std::endl
        << std::endl
        << "If the file is a GLB and the output name is not specified, defaults to the same name as input "
        << "+ \"_converted.glb\"." << std::endl
        << std::endl;
}

void CommandLine::ParseCommandLineArguments(
    int argc, wchar_t *argv[],
    std::wstring& inputFilePath, AssetType& inputAssetType, std::wstring& outFilePath, std::wstring& tempDirectory,
    std::vector<std::wstring>& lodFilePaths, std::vector<double>& screenCoveragePercentages, size_t& maxTextureSize,
    bool& shareMaterials, Version& minVersion, Platform& targetPlatforms, bool& replaceTextures, bool& compressMeshes)
{
    CommandLineParsingState state = CommandLineParsingState::Initial;

    inputFilePath = FileSystem::GetFullPath(std::wstring(argv[1]));

    inputAssetType = AssetTypeUtils::AssetTypeFromFilePath(inputFilePath);

    // Reset input parameters
    outFilePath = L"";
    tempDirectory = L"";
    lodFilePaths.clear();
    screenCoveragePercentages.clear();
    maxTextureSize = MAXTEXTURESIZE_DEFAULT;
    shareMaterials = false;
    minVersion = MIN_VERSION_DEFAULT;
    targetPlatforms = PLATFORM_DEFAULT;
    replaceTextures = false;
    compressMeshes = false;

    state = CommandLineParsingState::InputRead;

    std::wstring outFile;
    std::wstring tmpDir;
    for (int i = 2; i < argc; i++)
    {
        std::wstring param = argv[i];

        if (param == PARAM_OUTFILE)
        {
            outFile = L"";
            state = CommandLineParsingState::ReadOutFile;
        }
        else if (param == PARAM_TMPDIR)
        {
            tmpDir = L"";
            state = CommandLineParsingState::ReadTmpDir;
        }
        else if (param == PARAM_LOD)
        {
            lodFilePaths.clear();
            state = CommandLineParsingState::ReadLods;
        }
        else if (param == PARAM_SCREENCOVERAGE)
        {
            screenCoveragePercentages.clear();
            state = CommandLineParsingState::ReadScreenCoverage;
        }
        else if (param == PARAM_MAXTEXTURESIZE)
        {
            maxTextureSize = MAXTEXTURESIZE_DEFAULT;
            state = CommandLineParsingState::ReadMaxTextureSize;
        }
        else if (param == PARAM_SHARE_MATERIALS)
        {
            shareMaterials = true;
            state = CommandLineParsingState::InputRead;
        }
        else if (param == PARAM_MIN_VERSION)
        {
            minVersion = MIN_VERSION_DEFAULT;
            state = CommandLineParsingState::ReadMinVersion;
        }
        else if (param == PARAM_PLATFORM)
        {
            targetPlatforms = PLATFORM_DEFAULT;
            state = CommandLineParsingState::ReadPlatform;
        }
        else if (param == PARAM_REPLACE_TEXTURES)
        {
            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)
            {
            case CommandLineParsingState::ReadOutFile:
                outFile = FileSystem::GetFullPath(param);
                state = CommandLineParsingState::InputRead;
                break;
            case CommandLineParsingState::ReadTmpDir:
                tmpDir = FileSystem::GetFullPath(param);
                state = CommandLineParsingState::InputRead;
                break;
            case CommandLineParsingState::ReadLods:
                lodFilePaths.push_back(FileSystem::GetFullPath(param));
                break;
            case CommandLineParsingState::ReadScreenCoverage:
            {
                auto paramA = std::string(param.begin(), param.end());
                screenCoveragePercentages.push_back(std::atof(paramA.c_str()));
                break;
            }
            case CommandLineParsingState::ReadMaxTextureSize:
                maxTextureSize = std::min(static_cast<size_t>(std::stoul(param.c_str())), MAXTEXTURESIZE_MAX);
                break;
            case CommandLineParsingState::ReadMinVersion:
                if (_wcsicmp(param.c_str(), PARAM_VALUE_VERSION_1709) == 0 || _wcsicmp(param.c_str(), PARAM_VALUE_VERSION_RS3) == 0)
                {
                    minVersion = Version::Version1709;
                }
                else if (_wcsicmp(param.c_str(), PARAM_VALUE_VERSION_1803) == 0 || _wcsicmp(param.c_str(), PARAM_VALUE_VERSION_RS4) == 0)
                {
                    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;
                }
                else
                {
                    throw std::invalid_argument("Invalid min version specified. For help, try the command again without parameters.");
                }
                state = CommandLineParsingState::InputRead;
                break;
            case CommandLineParsingState::ReadPlatform:
                if (_wcsicmp(param.c_str(), PARAM_VALUE_ALL) == 0)
                {
                    targetPlatforms = (Platform) (Platform::Desktop | Platform::Holographic);
                } 
                else if (_wcsicmp(param.c_str(), PARAM_VALUE_HOLOGRAPHIC) == 0 || _wcsicmp(param.c_str(), PARAM_VALUE_HOLOLENS) == 0)
                {
                    targetPlatforms = Platform::Holographic;
                }
                else if (_wcsicmp(param.c_str(), PARAM_VALUE_DESKTOP) == 0 || _wcsicmp(param.c_str(), PARAM_VALUE_PC) == 0)
                {
                    targetPlatforms = Platform::Desktop;
                }
                else
                {
                    throw std::invalid_argument("Invalid platform specified. For help, try the command again without parameters.");
                }
                state = CommandLineParsingState::InputRead;
                break;
            case CommandLineParsingState::Initial:
            case CommandLineParsingState::InputRead:
            default:
                // Invalid argument detected
                throw std::invalid_argument("Invalid usage. For help, try the command again without parameters.");
            }
        }
    }

    if (!std::experimental::filesystem::exists(inputFilePath))
    {
        throw std::invalid_argument("Input file not found.");
    }

    for (auto& lodFilePath : lodFilePaths)
    {
        if (!std::experimental::filesystem::exists(lodFilePath))
        {
            throw  std::invalid_argument("Lod file not found.");
        }
    }

    if (outFile.empty())
    {
        std::wstring inputFilePathWithoutExtension = inputFilePath;
        if (FAILED(PathCchRemoveExtension(&inputFilePathWithoutExtension[0], inputFilePathWithoutExtension.length() + 1)))
        {
            throw std::invalid_argument("Invalid input file extension.");
        }

        outFile = std::wstring(&inputFilePathWithoutExtension[0]);

        if (inputAssetType == AssetType::GLB)
        {
            outFile += SUFFIX_CONVERTED;
        }

        outFile += EXTENSION_GLB;
    }

    outFilePath = outFile;

    if (tmpDir.empty())
    {
        tmpDir = FileSystem::CreateTempFolder();
    }

    tempDirectory = tmpDir;
}


================================================
FILE: WindowsMRAssetConverter/CommandLine.h
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#pragma once

#include <vector>
#include "AssetType.h"

namespace CommandLine
{
    enum Platform
    {
        None = 0x0,
        Holographic = 0x1,
        Desktop = 0x2
    };

    enum class Version
    {
        Version1709, // Fall Creators Update (RS3)
        Version1803,  // Spring Creators Update (RS4)
        Version1809,  // Fall 2018 Update (RS5)
        Latest = Version1809
    };

    void PrintHelp();

    void ParseCommandLineArguments(
        int argc, wchar_t *argv[],
        std::wstring& inputFilePath, AssetType& inputAssetType, std::wstring& outFilePath, std::wstring& tempDirectory,
        std::vector<std::wstring>& lodFilePaths, std::vector<double>& screenCoveragePercentages, size_t& maxTextureSize,
        bool& sharedMaterials, Version& minVersion, Platform& targetPlatforms, bool& replaceTextures, bool& compressMeshes);
};



================================================
FILE: WindowsMRAssetConverter/FileSystem.cpp
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#include "stdafx.h"
#include "FileSystem.h"

std::wstring FileSystem::GetRelativePathWithTrailingSeparator(std::wstring from, std::wstring to)
{
    // once c++17 filesystem is fully supported, this should become something like:
    // return std::filesystem::relative(to, from) + std::filesystem::path::preferred_separator;

    std::experimental::filesystem::path fromFS(std::experimental::filesystem::canonical(from));
    std::experimental::filesystem::path toFS(std::experimental::filesystem::canonical(to));

    std::experimental::filesystem::path result;
    auto fromIter = fromFS.begin();
    auto toIter = toFS.begin();

    while (fromIter != fromFS.end() || toIter != toFS.end())
    {
        const auto& f = *fromIter;
        const auto& t = *toIter;
        if (f == t)
        {
            fromIter++;
            toIter++;
        }
        else
        {
            while (fromIter != fromFS.end())
            {
                result /= "..";
                fromIter++;
            }

            while (toIter != toFS.end())
            {
                result /= *toIter;
                toIter++;
            }

            result += std::experimental::filesystem::path::preferred_separator;
        }
    }

    return result;
}

std::wstring FileSystem::GetBasePath(const std::wstring& path)
{
    std::wstring pathCopy(path);
    wchar_t *basePath = &pathCopy[0];
    if (FAILED(PathCchRemoveFileSpec(basePath, pathCopy.length() + 1)))
    {
        throw std::invalid_argument("Invalid input path.");
    }

    return std::move(std::wstring(basePath));
}


std::wstring FileSystem::GetFullPath(const std::wstring& path)
{
    wchar_t fullPath[MAX_PATH];
    if (GetFullPathName(path.c_str(), ARRAYSIZE(fullPath), fullPath, NULL) == 0)
    {
        throw std::invalid_argument("Invalid input file path.");
    }
    return std::move(std::wstring(fullPath));
}

std::wstring FileSystem::CreateSubFolder(const std::wstring& parentPath, const std::wstring& subFolderName)
{
    std::wstring errorMessageW = L"Could not create a sub-folder of " + parentPath + L".";
    std::string errorMessage(errorMessageW.begin(), errorMessageW.end());

    wchar_t subFolderPath[MAX_PATH];
    if (FAILED(PathCchCombine(subFolderPath, ARRAYSIZE(subFolderPath), parentPath.c_str(), (subFolderName + L"\\").c_str())))
    {
        throw std::runtime_error(errorMessage);
    }

    if (CreateDirectory(subFolderPath, NULL) == 0 && GetLastError() != ERROR_ALREADY_EXISTS)
    {
        throw std::runtime_error(errorMessage);
    }

    return std::move(std::wstring(subFolderPath));
}

std::wstring FileSystem::CreateTempFolder()
{
    std::wstring errorMessageW = L"Could not get a temporary folder. Try specifying one in the command line.";
    std::string errorMessage(errorMessageW.begin(), errorMessageW.end());

    wchar_t tmpDirRaw[MAX_PATH];
    auto returnValue = GetTempPath(MAX_PATH, tmpDirRaw);
    if (returnValue > MAX_PATH || (returnValue == 0))
    {
        throw std::runtime_error(errorMessage);
    }

    // Get a random folder to drop the files
    GUID guid = { 0 };
    if (FAILED(CoCreateGuid(&guid)))
    {
        throw std::runtime_error(errorMessage);
    }

    wchar_t guidRaw[MAX_PATH];
    if (StringFromGUID2(guid, guidRaw, ARRAYSIZE(guidRaw)) == 0)
    {
        throw std::runtime_error(errorMessage);
    }

    return std::move(CreateSubFolder(tmpDirRaw, guidRaw));
}

================================================
FILE: WindowsMRAssetConverter/FileSystem.h
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#pragma once
#include <filesystem>

namespace FileSystem
{
    std::wstring GetRelativePathWithTrailingSeparator(std::wstring from, std::wstring to);
    std::wstring GetBasePath(const std::wstring& path);
    std::wstring GetFullPath(const std::wstring& path);
    std::wstring CreateSubFolder(const std::wstring& parentPath, const std::wstring& subFolderName);
    std::wstring CreateTempFolder();
};



================================================
FILE: WindowsMRAssetConverter/README.md
================================================
# Windows Mixed Reality Asset Converter

A command line tool to convert core GLTF 2.0 for use in the Windows Mixed Reality home, following the published [documentation](https://developer.microsoft.com/en-us/windows/mixed-reality/creating_3d_models_for_use_in_the_windows_mixed_reality_home).

Note that this tool does not enforce any limits specified in the documentation (polygon count, texture size, etc.), so you might still encounter issues placing models if you do not conform to those limits.

## Usage
WindowsMRAssetConverter _&lt;path to GLTF/GLB&gt;_

## Optional arguments
- `-o <output file path>`
  - Specifies the output file name and directory for the output GLB.
  - If the file is a GLB and the output name is not specified, the tool defaults to the same name as input + "_converted.glb".

- `-platform <all | desktop | holographic>`
  - **Default:** `desktop` 
  - `desktop`: optimizes assets for immersive PC-based headsets using the [MSFT_packing_occlusionRoughnessMetallic](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_packing_occlusionRoughnessMetallic) extension.
  - `holographic`: optimizes assets for HoloLens using the [MSFT_packing_normalRoughnessMetallic](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_packing_normalRoughnessMetallic) extension.
  - `all`: creates assets optimized for both holographic and desktop devices, but with a larger file size.

- `-min-version <1709 | 1803 | latest>`
  - **Default:** `1709`
  - Specifies the minimum version of Windows 10 supported by this asset.
  - The current options are `1709` (Fall Creators Update) and `1803` (Spring Creators Update), as well as `latest` which is currently the same as `1803`.
  - Supporting Windows 10 version 1709 results in assets with a larger file size. If your app is compatible with Windows 10 1803+ only, it is recommended to set `-min-version 1803`.
  - This setting does not have any effect on the Holographic platform.

- `-lod <path to each lower LOD asset in descending order of quality>`
  - Specifies a list of assets that represent levels of detail, from higher to lower, that should be merged with the main asset and used as alternates when the asset is displayed from a distance (with limited screen coverage).

- `-screen-coverage <LOD screen coverage values>`
  - Specifies the maximum screen coverage values for each of the levels of detail, according to the [MSFT_lod](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_lod) extension specification.

- `-share-materials`
  - If enabled, creates assets that share materials between different levels of detail. 
  - This assumes all LOD documents use the same indices for each material, and uses the textures from the most detailed level.

- `-temp-directory <temporary folder>`
  - **Default:** system temp folder for the user
  - Allows overriding the temporary folder where intermediate files (packed/compressed textures, converted GLBs) will be placed.

- `-max-texture-size <Max texture size in pixels>`
  - **Default:** 512
  - Allows overriding the maximum texture dimension (width/height) when compressing textures. The recommended maximum dimension in the [documentation](https://developer.microsoft.com/en-us/windows/mixed-reality/creating_3d_models_for_use_in_the_windows_mixed_reality_home#texture_resolutions_and_workflow) is 512, and the allowed maximum is 4096.

- `-replace-textures`
  - If enabled, replaces all textures with their DDS compressed equivalents during the compression step. 
  - This results in a smaller file size, but the resulting file will not be compatible with most glTF viewers.


## Example
`WindowsMRAssetConverter FileToConvert.gltf -o ConvertedFile.glb -platform all -lod Lod1.gltf Lod2.gltf -screen-coverage 0.5 0.2 0.01`

The above will convert _FileToConvert.gltf_ into _ConvertedFile.glb_ in the current directory.

## Pipeline overview

Each asset goes through the following steps when converting for compatibility with the Windows Mixed Reality home:

1. **Conversion from GLB** - Any GLB files are converted to loose glTF + assets, to simplify the code for reading resources
1. **Texture packing** - The textures that are relevant for the Windows MR home are packed according to the [documentation](https://developer.microsoft.com/en-us/windows/mixed-reality/creating_3d_models_for_use_in_the_windows_mixed_reality_home#materials) using 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 as necessary
1. **Texture compression** - All textures that are used in the Windows MR home must be compressed as DDS BC5 or BC7 according to the [documentation](https://developer.microsoft.com/en-us/windows/mixed-reality/creating_3d_models_for_use_in_the_windows_mixed_reality_home#materials). This step also generates mip maps for the textures, and resizes them down if necessary
1. **LOD merging** - All assets that represent levels of detail are merged into the main asset using the [MSFT_lod](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_lod) extension
1. **GLB export** - The resulting assets are exported as a GLB with all resources. As part of this step, accessors are modified to conform to the [glTF implementation notes in the documentation](https://developer.microsoft.com/en-us/windows/mixed-reality/creating_3d_models_for_use_in_the_windows_mixed_reality_home#gltf_implementation_notes): component types are converted to types supported by the Windows MR home, and the min and max values are calculated before serializing the accessors to the GLB

## Additional resources

- [Creating 3D models for use in the Windows Mixed Reality home](https://developer.microsoft.com/en-us/windows/mixed-reality/creating_3d_models_for_use_in_the_windows_mixed_reality_home)
- [Microsoft glTF LOD Extension Specification](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_lod)
- [PC Mixed Reality Texture Packing Extensions Specification](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_packing_occlusionRoughnessMetallic)
- [Holographic Mixed Reality Texture Packing Extensions Specification](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_packing_normalRoughnessMetallic)
- [Microsoft DDS Textures glTF extensions specification](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds)
- [Implementing 3D app launchers](https://developer.microsoft.com/en-us/windows/mixed-reality/implementing_3d_app_launchers)
- [Implementing 3D deep links for your app in the Windows Mixed Reality home](https://developer.microsoft.com/en-us/windows/mixed-reality/implementing_3d_deep_links_for_your_app_in_the_windows_mixed_reality_home)
- [Navigating the Windows Mixed Reality home](https://developer.microsoft.com/en-us/windows/mixed-reality/navigating_the_windows_mixed_reality_home)


================================================
FILE: WindowsMRAssetConverter/WindowsMRAssetConverter.cpp
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#include "stdafx.h"

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

#include "CommandLine.h"
#include "FileSystem.h"
#include "GLTFTextureUtils.h"

using namespace Microsoft::glTF;
using namespace Microsoft::glTF::Toolkit;

class GLTFStreamReader : public IStreamReader
{
public:
    GLTFStreamReader(std::wstring uriBase) : m_uriBase(uriBase) {}

    virtual ~GLTFStreamReader() override {}
    virtual std::shared_ptr<std::istream> GetInputStream(const std::string& filename) const override
    {
        std::wstring filenameW = std::wstring(filename.begin(), filename.end());

        wchar_t uriAbsoluteRaw[MAX_PATH];
        // Note: PathCchCombine will return the last argument if it's an absolute path
        if (FAILED(::PathCchCombine(uriAbsoluteRaw, ARRAYSIZE(uriAbsoluteRaw), m_uriBase.c_str(), filenameW.c_str())))
        {
            throw std::invalid_argument("Could not get the base path for the GLTF resources. Try specifying the full path.");
        }

        return std::make_shared<std::ifstream>(uriAbsoluteRaw, std::ios::binary);
    }
private:
    const std::wstring m_uriBase;
};

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

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

private:
    std::shared_ptr<std::ofstream> m_stream;
};

Document ProcessTextures(
    size_t maxTextureSize, 
    TexturePacking packing, 
    bool retainOriginalImages, 
    const std::wstring& tempDirectory,
    const Document& document, 
    const std::shared_ptr<GLTFStreamReader>& streamReader)
{
    Document resultDocument(document);

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

    std::wcout << L"Specular Glossiness conversion..." << std::endl;

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

    std::wcout << L"Removing redundant textures and images..." << std::endl;

    // 1. Remove redundant textures and images
    resultDocument = GLTFTextureUtils::RemoveRedundantTexturesAndImages(resultDocument);

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

    // 2. Texture Packing
    resultDocument = GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(streamReader, resultDocument, packing, tempDirectoryA);

    std::wcout << L"Compressing textures - this can take a few minutes..." << std::endl;

    // 3. Texture Compression
    resultDocument = GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(streamReader, resultDocument, tempDirectoryA, maxTextureSize, retainOriginalImages);

    return resultDocument;
}

Document LoadAndConvertDocumentForWindowsMR(
    std::wstring& inputFilePath,
    AssetType inputAssetType,
    const std::wstring& tempDirectory,
    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());

        // inputGltfName is the path to the converted GLTF without extension
        std::wstring inputGltfName = inputFilePathFS.stem();
        std::string inputGltfNameA = std::string(inputGltfName.begin(), inputGltfName.end());

        GLBToGLTF::UnpackGLB(inputFilePathA, tempDirectoryA, inputGltfNameA);

        inputFilePath = tempDirectory + inputGltfName + EXTENSION_GLTF;
    }

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

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

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

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

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

    return document;
}

int wmain(int argc, wchar_t *argv[])
{
    if (argc < 2)
    {
        CommandLine::PrintHelp();
        return 0;
    }

    // Initialize COM
    CoInitialize(NULL);

    try
    {
        // Arguments
        std::wstring inputFilePath;
        AssetType inputAssetType;
        std::wstring outFilePath;
        std::wstring tempDirectory;
        std::vector<std::wstring> lodFilePaths;
        std::vector<double> screenCoveragePercentages;
        size_t maxTextureSize;
        bool shareMaterials;
        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, meshCompression);

        TexturePacking packing = TexturePacking::None;

        std::wstring compatibleVersionsText;

        if ((targetPlatforms & CommandLine::Platform::Holographic) > 0)
        {
            compatibleVersionsText += L"HoloLens";

            // Holographic mode: NRM
            packing = (TexturePacking)(packing | TexturePacking::NormalRoughnessMetallic);
        }

        if ((targetPlatforms & CommandLine::Platform::Desktop) > 0)
        {
            if (!compatibleVersionsText.empty())
            {
                compatibleVersionsText += L" and ";
            }

            // Desktop 1803+ mode: ORM
            packing = (TexturePacking)(packing | TexturePacking::OcclusionRoughnessMetallic);

            if (minVersion == CommandLine::Version::Version1709)
            {
                // Desktop 1709 mode: RMO
                packing = (TexturePacking)(packing | TexturePacking::RoughnessMetallicOcclusion);

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

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

        // Load document, and perform steps:
        // 1. Mesh Compression
        auto document = LoadAndConvertDocumentForWindowsMR(inputFilePath, inputAssetType, tempDirectory, meshCompression);

        // 2. LOD Merging
        if (!lodFilePaths.empty())
        {
            std::wcout << L"Merging LODs..." << std::endl;

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

            for (size_t i = 0; i < lodFilePaths.size(); i++)
            {
                // Apply the same optimizations for each LOD
                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, meshCompression));
            
                lodDocumentRelativePaths.push_back(FileSystem::GetRelativePathWithTrailingSeparator(FileSystem::GetBasePath(inputFilePath), FileSystem::GetBasePath(lod)));
            }

            document = GLTFLODUtils::MergeDocumentsAsLODs(lodDocuments, screenCoveragePercentages, lodDocumentRelativePaths, shareMaterials);
        }

        // 3. Texture Packing
        // 4. Texture Compression
        auto streamReader = std::make_shared<GLTFStreamReader>(FileSystem::GetBasePath(inputFilePath));
        document = ProcessTextures(maxTextureSize, packing, !replaceTextures, tempDirectory, document, streamReader);

        // 5. Make sure there's a default scene
        if (!document.HasDefaultScene())
        {
            document.defaultSceneId = document.scenes.Elements()[0].id;
        }

        // 6. GLB Export
        std::wcout << L"Exporting as GLB..." << std::endl;

        // The Windows MR Fall Creators update has restrictions on the supported
        // component types of accessors.
        AccessorConversionStrategy accessorConversion = [](const Accessor& accessor)
        {
            if (accessor.type == AccessorType::TYPE_SCALAR)
            {
                switch (accessor.componentType)
                {
                case ComponentType::COMPONENT_BYTE:
                case ComponentType::COMPONENT_UNSIGNED_BYTE:
                case ComponentType::COMPONENT_SHORT:
                    return ComponentType::COMPONENT_UNSIGNED_SHORT;
                default:
                    return accessor.componentType;
                }
            }
            else if (accessor.type == AccessorType::TYPE_VEC2 || accessor.type == AccessorType::TYPE_VEC3)
            {
                return ComponentType::COMPONENT_FLOAT;
            }

            return accessor.componentType;
        };

        SerializeBinary(document, streamReader, std::make_shared<GLBStreamWriter>(outFilePath), accessorConversion);

        std::wcout << L"Done!" << std::endl;
        std::wcout << L"Output file: " << outFilePath << std::endl;
    }
    catch (std::exception ex)
    {
        std::cerr << ex.what() << std::endl;
        return 1;
    }

    return 0;
}

================================================
FILE: WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>15.0</VCProjectVersion>
    <ProjectGuid>{8A19D99C-78DC-4267-AB57-DB1DDBFBEFDF}</ProjectGuid>
    <Keyword>Win32Proj</Keyword>
    <RootNamespace>WindowsMRAssetConverter</RootNamespace>
    <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v141</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v141</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v141</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v141</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <LinkIncremental>true</LinkIncremental>
    <OutDir>$(SolutionDir)Built\Out\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutDir>
    <IntDir>$(SolutionDir)Built\Int\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <LinkIncremental>true</LinkIncremental>
    <OutDir>$(SolutionDir)Built\Out\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutDir>
    <IntDir>$(SolutionDir)Built\Int\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <LinkIncremental>false</LinkIncremental>
    <OutDir>$(SolutionDir)Built\Out\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutDir>
    <IntDir>$(SolutionDir)Built\Int\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <LinkIncremental>false</LinkIncremental>
    <OutDir>$(SolutionDir)Built\Out\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutDir>
    <IntDir>$(SolutionDir)Built\Int\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
  </PropertyGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level4</WarningLevel>
      <Optimization>Disabled</Optimization>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>$(SolutionDir)glTF-Toolkit\inc</AdditionalIncludeDirectories>
      <LanguageStandard>stdcpp17</LanguageStandard>
      <AdditionalOptions>/permissive- %(AdditionalOptions)</AdditionalOptions>
      <TreatWarningAsError>true</TreatWarningAsError>
      <DisableSpecificWarnings>4996</DisableSpecificWarnings>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <AdditionalDependencies>d3d11.lib;dxgi.lib;pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level4</WarningLevel>
      <Optimization>Disabled</Optimization>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>$(SolutionDir)glTF-Toolkit\inc</AdditionalIncludeDirectories>
      <LanguageStandard>stdcpp17</LanguageStandard>
      <AdditionalOptions>/permissive- %(AdditionalOptions)</AdditionalOptions>
      <TreatWarningAsError>true</TreatWarningAsError>
      <DisableSpecificWarnings>4996</DisableSpecificWarnings>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <AdditionalDependencies>d3d11.lib;dxgi.lib;pathcch.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level4</WarningLevel>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>$(SolutionDir)glTF-Toolkit\inc</AdditionalIncludeDirectories>
      <LanguageStandard>stdcpp17</LanguageStandard>
      <AdditionalOptions>/permissive- %(AdditionalOptions)</AdditionalOptions>
      <TreatWarningAsError>true</TreatWarningAsError>
      <DisableSpecificWarnings>4996</DisableSpecificWarnings>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <AdditionalDependencies>d3d11.lib;dxgi.lib;pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level4</WarningLevel>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>$(SolutionDir)glTF-Toolkit\inc</AdditionalIncludeDirectories>
      <LanguageStandard>stdcpp17</LanguageStandard>
      <AdditionalOptions>/permissive- %(AdditionalOptions)</AdditionalOptions>
      <TreatWarningAsError>true</TreatWarningAsError>
      <DisableSpecificWarnings>4996</DisableSpecificWarnings>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <AdditionalDependencies>d3d11.lib;dxgi.lib;pathcch.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClInclude Include="AssetType.h" />
    <ClInclude Include="CommandLine.h" />
    <ClInclude Include="FileSystem.h" />
    <ClInclude Include="stdafx.h" />
    <ClInclude Include="targetver.h" />
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="CommandLine.cpp" />
    <ClCompile Include="FileSystem.cpp" />
    <ClCompile Include="AssetType.cpp" />
    <ClCompile Include="stdafx.cpp">
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
    </ClCompile>
    <ClCompile Include="WindowsMRAssetConverter.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\glTF-Toolkit\glTF-Toolkit.vcxproj">
      <Project>{ff0275f1-58cb-4745-ba81-f6c1df66e206}</Project>
      <UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <None Include="README.md" />
    <None Include="packages.config" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
    <Import Project="$(SolutionDir)packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets" Condition="Exists('$(SolutionDir)packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets')" />
    <Import Project="$(SolutionDir)\packages\directxtex_desktop_2015.2018.8.5.1\build\native\directxtex_desktop_2015.targets" Condition="Exists('$(SolutionDir)\packages\directxtex_desktop_2015.2018.8.5.1\build\native\directxtex_desktop_2015.targets')" />
    <Import Project="$(SolutionDir)\packages\Microsoft.glTF.CPP.1.6.1.0\build\native\Microsoft.glTF.CPP.targets" Condition="Exists('$(SolutionDir)\packages\Microsoft.glTF.CPP.1.6.1.0\build\native\Microsoft.glTF.CPP.targets')" />
    <Import Project="$(SolutionDir)\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets" Condition="Exists('$(SolutionDir)\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets')" />
  </ImportGroup>
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('$(SolutionDir)packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets'))" />
    <Error Condition="!Exists('$(SolutionDir)\packages\directxtex_desktop_2015.2018.8.5.1\build\native\directxtex_desktop_2015.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\directxtex_desktop_2015.2018.8.5.1\build\native\directxtex_desktop_2015.targets'))" />
    <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.glTF.CPP.1.6.1.0\build\native\Microsoft.glTF.CPP.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.glTF.CPP.1.6.1.0\build\native\Microsoft.glTF.CPP.targets'))" />
    <Error Condition="!Exists('$(SolutionDir)\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets'))" />
  </Target>
</Project>

================================================
FILE: WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj.filters
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <Filter Include="Source Files">
      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
    </Filter>
    <Filter Include="Header Files">
      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
    </Filter>
    <Filter Include="Resource Files">
      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
    </Filter>
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="stdafx.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="targetver.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="AssetType.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="CommandLine.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="FileSystem.h">
      <Filter>Header Files</Filter>
    </ClInclude>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="stdafx.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="WindowsMRAssetConverter.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="CommandLine.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="FileSystem.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="AssetType.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <None Include="packages.config" />
    <None Include="README.md" />
  </ItemGroup>
</Project>

================================================
FILE: WindowsMRAssetConverter/packages.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="directxtex_desktop_2015" version="2018.8.5.1" targetFramework="native" />
  <package id="draco.CPP" version="1.3.3.1" targetFramework="native" />
  <package id="Microsoft.glTF.CPP" version="1.6.1.0" targetFramework="native" />
  <package id="rapidjson.temprelease" version="0.0.2.20" targetFramework="native" />
</packages>

================================================
FILE: WindowsMRAssetConverter/stdafx.cpp
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#include "stdafx.h"

================================================
FILE: WindowsMRAssetConverter/stdafx.h
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#pragma once

// Use the C++ standard templated min/max
#define NOMINMAX

// DirectX apps don't need GDI
#define NODRAWTEXT
#define NOGDI
#define NOBITMAP

// Include <mcx.h> if you need this
#define NOMCX

// Include <winsvc.h> if you need this
#define NOSERVICE

// WinHelp is deprecated
#define NOHELP

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincodec.h>
#include <pathcch.h>
#include <shlwapi.h>

#include <wrl/client.h>

#include <d3d11_1.h>
#include <dxgi1_2.h>
#include <DirectXMath.h>
#include <DirectXColors.h>

#include <algorithm>
#include <exception>
#include <memory>
#include <stdexcept>
#include <fstream>
#include <iostream>
#include <sstream>
#include <set>

#include <stdio.h>

================================================
FILE: WindowsMRAssetConverter/targetver.h
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#pragma once

// Including SDKDDKVer.h defines the highest available Windows platform.

// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.

#include <SDKDDKVer.h>


================================================
FILE: glTF-Toolkit/glTF-Toolkit.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|ARM">
      <Configuration>Debug</Configuration>
      <Platform>ARM</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|ARM">
      <Configuration>Release</Configuration>
      <Platform>ARM</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug Static Runtime|ARM">
      <Configuration>Debug Static Runtime</Configuration>
      <Platform>ARM</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug Static Runtime|Win32">
      <Configuration>Debug Static Runtime</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release Static Runtime|ARM">
      <Configuration>Release Static Runtime</Configuration>
      <Platform>ARM</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release Static Runtime|Win32">
      <Configuration>Release Static Runtime</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug Static Runtime|x64">
      <Configuration>Debug Static Runtime</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release Static Runtime|x64">
      <Configuration>Release Static Runtime</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>15.0</VCProjectVersion>
    <ProjectGuid>{FF0275F1-58CB-4745-BA81-F6C1DF66E206}</ProjectGuid>
    <Keyword>Win32Proj</Keyword>
    <RootNamespace>glTFToolkit</RootNamespace>
    <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Label="Configuration">
    <ConfigurationType>StaticLibrary</ConfigurationType>
    <CharacterSet>Unicode</CharacterSet>
    <PlatformToolset>v141</PlatformToolset>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)'=='Debug' Or '$(Configuration)'=='Debug Static Runtime'" Label="Configuration">
    <UseDebugLibraries>true</UseDebugLibraries>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='Release Static Runtime'" Label="Configuration">
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <UseDebugLibraries>false</UseDebugLibraries>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Platform)'=='ARM'" Label="Configuration">
    <WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared" />
  <ImportGroup Label="PropertySheets">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup>
    <OutDir>$(SolutionDir)Built\Out\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutDir>
    <IntDir>$(SolutionDir)Built\Int\$(PlatformToolset)\$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='Release Static Runtime'">
    <LinkIncremental>false</LinkIncremental>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)'=='Debug' Or '$(Configuration)'=='Debug Static Runtime'">
    <LinkIncremental>true</LinkIncremental>
  </PropertyGroup>
  <ItemDefinitionGroup>
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <LanguageStandard>stdcpp17</LanguageStandard>
      <WarningLevel>Level4</WarningLevel>
      <AdditionalIncludeDirectories>inc;%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)</AdditionalIncludeDirectories>
      <AdditionalOptions>/permissive- %(AdditionalOptions)</AdditionalOptions>
      <TreatWarningAsError>true</TreatWarningAsError>
      <GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <RuntimeLibrary Condition="'$(Configuration)'=='Release Static Runtime'">MultiThreaded</RuntimeLibrary>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug' Or '$(Configuration)'=='Debug Static Runtime'">
    <ClCompile>
      <Optimization>Disabled</Optimization>
      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <RuntimeLibrary Condition="'$(Configuration)'=='Debug Static Runtime'">MultiThreadedDebug</RuntimeLibrary>
    </ClCompile>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='Release Static Runtime'">
    <ClCompile>
      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Platform)'=='x86'">
    <ClCompile>
      <PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>
  </ItemDefinitionGroup>
  <ItemGroup>
    <None Include="packages.config" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="inc\AccessorUtils.h" />
    <ClInclude Include="inc\DeviceResources.h" />
    <ClInclude Include="inc\GLBtoGLTF.h" />
    <ClInclude Include="inc\GLTFLODUtils.h" />
    <ClInclude Include="inc\GLTFMeshCompressionUtils.h" />
    <ClInclude Include="inc\GLTFSDK.h" />
    <ClInclude Include="inc\GLTFSpecularGlossinessUtils.h" />
    <ClInclude Include="inc\GLTFTextureCompressionUtils.h" />
    <ClInclude Include="inc\GLTFTextureUtils.h" />
    <ClInclude Include="inc\GLTFTexturePackingUtils.h" />
    <ClInclude Include="inc\pch.h" />
    <ClInclude Include="inc\SerializeBinary.h" />
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="src\GLTFMeshCompressionUtils.cpp" />
    <ClCompile Include="src\DeviceResources.cpp" />
    <ClCompile Include="src\GLBtoGLTF.cpp" />
    <ClCompile Include="src\GLTFLODUtils.cpp" />
    <ClCompile Include="src\GLTFSpecularGlossinessUtils.cpp" />
    <ClCompile Include="src\GLTFTextureCompressionUtils.cpp" />
    <ClCompile Include="src\GLTFTextureUtils.cpp" />
    <ClCompile Include="src\GLTFTexturePackingUtils.cpp" />
    <ClCompile Include="src\pch.cpp">
      <PrecompiledHeader>Create</PrecompiledHeader>
    </ClCompile>
    <ClCompile Include="src\SerializeBinary.cpp" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
    <Import Project="$(SolutionDir)packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets" Condition="Exists('$(SolutionDir)packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets')" />
    <Import Project="$(SolutionDir)\packages\directxtex_desktop_2015.2018.8.5.1\build\native\directxtex_desktop_2015.targets" Condition="Exists('$(SolutionDir)\packages\directxtex_desktop_2015.2018.8.5.1\build\native\directxtex_desktop_2015.targets')" />
    <Import Project="$(SolutionDir)\packages\Microsoft.glTF.CPP.1.6.1.0\build\native\Microsoft.glTF.CPP.targets" Condition="Exists('$(SolutionDir)\packages\Microsoft.glTF.CPP.1.6.1.0\build\native\Microsoft.glTF.CPP.targets')" />
    <Import Project="$(SolutionDir)\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets" Condition="Exists('$(SolutionDir)\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets')" />
  </ImportGroup>
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('$(SolutionDir)packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)packages\rapidjson.temprelease.0.0.2.20\build\native\rapidjson.temprelease.targets'))" />
    <Error Condition="!Exists('$(SolutionDir)\packages\directxtex_desktop_2015.2018.8.5.1\build\native\directxtex_desktop_2015.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\directxtex_desktop_2015.2018.8.5.1\build\native\directxtex_desktop_2015.targets'))" />
    <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.glTF.CPP.1.6.1.0\build\native\Microsoft.glTF.CPP.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.glTF.CPP.1.6.1.0\build\native\Microsoft.glTF.CPP.targets'))" />
    <Error Condition="!Exists('$(SolutionDir)\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\draco.CPP.1.3.3.1\build\native\draco.CPP.targets'))" />
  </Target>
</Project>

================================================
FILE: glTF-Toolkit/glTF-Toolkit.vcxproj.filters
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <None Include="packages.config" />
  </ItemGroup>
  <ItemGroup>
    <Filter Include="inc">
      <UniqueIdentifier>{2156fb82-8d5e-4cc3-b2ac-e13a18bc665f}</UniqueIdentifier>
    </Filter>
    <Filter Include="src">
      <UniqueIdentifier>{620de9b6-9c44-44b6-9d93-99834c5efdf2}</UniqueIdentifier>
    </Filter>
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="inc\DeviceResources.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\GLTFLODUtils.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\GLTFTextureCompressionUtils.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\GLTFTexturePackingUtils.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\SerializeBinary.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\pch.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\GLBtoGLTF.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\AccessorUtils.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\GLTFSDK.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\GLTFMeshCompressionUtils.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\GLTFSpecularGlossinessUtils.h">
      <Filter>inc</Filter>
    </ClInclude>
    <ClInclude Include="inc\GLTFTextureUtils.h">
      <Filter>inc</Filter>
    </ClInclude>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="src\DeviceResources.cpp">
      <Filter>src</Filter>
    </ClCompile>
    <ClCompile Include="src\GLTFLODUtils.cpp">
      <Filter>src</Filter>
    </ClCompile>
    <ClCompile Include="src\GLTFTextureCompressionUtils.cpp">
      <Filter>src</Filter>
    </ClCompile>
    <ClCompile Include="src\GLTFTexturePackingUtils.cpp">
      <Filter>src</Filter>
    </ClCompile>
    <ClCompile Include="src\SerializeBinary.cpp">
      <Filter>src</Filter>
    </ClCompile>
    <ClCompile Include="src\pch.cpp">
      <Filter>src</Filter>
    </ClCompile>
    <ClCompile Include="src\GLBtoGLTF.cpp">
      <Filter>src</Filter>
    </ClCompile>
    <ClCompile Include="src\GLTFMeshCompressionUtils.cpp">
      <Filter>src</Filter>
    </ClCompile>
    <ClCompile Include="src\GLTFSpecularGlossinessUtils.cpp">
      <Filter>src</Filter>
    </ClCompile>
    <ClCompile Include="src\GLTFTextureUtils.cpp">
      <Filter>src</Filter>
    </ClCompile>
  </ItemGroup>
</Project>

================================================
FILE: glTF-Toolkit/inc/AccessorUtils.h
================================================
// 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 <functional>
#include <vector>

namespace Microsoft::glTF::Toolkit
{
    /// <summary>
    /// Utilities to manipulate accessors in a glTF asset.
    /// </summary>
    class AccessorUtils
    {
    public:
        // Note: XML Documentation cannot be applied to templated types per https://docs.microsoft.com/en-us/cpp/ide/xml-documentation-visual-cpp
        // Calculates the min and max values for an accessor according to the glTF 2.0 specification.
        // accessor is: The accessor definition for which the min and max values will be calculated.</param>
        // accessorContents is: The raw data contained in the accessor.
        // returns: A pair containing the min and max vectors for the accessor, in that order.
        template <typename T>
        static std::pair<std::vector<float>, std::vector<float>> CalculateMinMax(const Accessor& accessor, const std::vector<T>& accessorContents)
        {
            auto typeCount = Accessor::GetTypeCount(accessor.type);
            auto min = std::vector<float>(typeCount);
            auto max = std::vector<float>(typeCount);

            if (accessorContents.size() < typeCount)
            {
                throw std::invalid_argument("The accessor must contain data in order to calculate min and max.");
            }

            // Initialize min and max with the first elements of the array
            for (size_t j = 0; j < typeCount; j++)
            {
                auto current = static_cast<float>(accessorContents[j]);
                min[j] = current;
                max[j] = current;
            }

            for (size_t i = 1; i < accessor.count; i++)
            {
                for (size_t j = 0; j < typeCount; j++)
                {
                    auto current = static_cast<float>(accessorContents[i * typeCount + j]);
                    min[j] = std::min(min[j], current);
                    max[j] = std::max(max[j], current);
                }
            }

            return std::make_pair(min, max);
        }
    };
}



================================================
FILE: glTF-Toolkit/inc/DeviceResources.h
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#pragma once

namespace DX
{
    /// <summary>
    /// Controls all the DirectX device resources.
    /// </summary>
    class DeviceResources
    {
    public:
        DeviceResources(D3D_FEATURE_LEVEL minFeatureLevel = D3D_FEATURE_LEVEL_10_0);

        void CreateDeviceResources();
        void HandleDeviceLost();

        // Direct3D Accessors.
        ID3D11Device1*          GetD3DDevice() const                    { return m_d3dDevice.Get(); }
        ID3D11DeviceContext1*   GetD3DDeviceContext() const             { return m_d3dContext.Get(); }
        IDXGISwapChain1*        GetSwapChain() const                    { return m_swapChain.Get(); }
        D3D_FEATURE_LEVEL       GetDeviceFeatureLevel() const           { return m_d3dFeatureLevel; }

    private:
        void GetHardwareAdapter(IDXGIAdapter1** ppAdapter);

        // Direct3D objects.
        Microsoft::WRL::ComPtr<ID3D11Device1>               m_d3dDevice;
        Microsoft::WRL::ComPtr<ID3D11DeviceContext1>        m_d3dContext;
        Microsoft::WRL::ComPtr<IDXGISwapChain1>             m_swapChain;
        Microsoft::WRL::ComPtr<ID3DUserDefinedAnnotation>   m_d3dAnnotation;

        // Direct3D properties.
        D3D_FEATURE_LEVEL                               m_d3dMinFeatureLevel;

        // Cached device properties.
        D3D_FEATURE_LEVEL                               m_d3dFeatureLevel;
    };

    // Helper class for COM exceptions
    class com_exception : public std::exception
    {
    public:
        com_exception(HRESULT hr) : result(hr) {}

        virtual const char* what() const override
        {
            static char s_str[64] = { 0 };
            sprintf_s(s_str, "Failure with HRESULT of %08X", result);
            return s_str;
        }

    private:
        HRESULT result;
    };

    // Helper utility converts D3D API failures into exceptions.
    inline void ThrowIfFailed(HRESULT hr)
    {
        if (FAILED(hr))
        {
            throw com_exception(hr);
        }
    }

}

================================================
FILE: glTF-Toolkit/inc/GLBtoGLTF.h
================================================
// 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
{
    /// <summary>
    /// Utilities to convert glTF-Binary files (GLB) to
    /// unpacked glTF assets.
    /// </summary>
    class GLBToGLTF
    {
    public:
        /// <summary>
        /// Unpacks a GLB asset into a GLTF manifest and its 
        /// resources (bin files and images).
        /// </summary>
        /// <param name="glbPath">The path to the GLB file to unpack.</param>
        /// <param name="outDirectory">The directory to which the glTF manifest and resources will be unpacked.</param>
        /// <param name="gltfName">
        /// The name of the output glTF manifest file, without the extension. 
        /// This name will be used as a prefix to all unpacked resources.
        /// </param>
        static void UnpackGLB(const std::string& glbPath, const std::string& outDirectory, const std::string& gltfName);

        /// <summary>
        /// Extracts the contents of all buffer views from a GLB file into a 
        /// byte vector that can be saves as a bin file to be used in a glTF file.
        /// </summary>
        /// <param name="in">A stream pointing to the GLB file.</param>
        /// <param name="glbDoc">The manifest describing the GLB asset.</param>
        /// <param name="bufferOffset">The offset on the input file where the GLB buffer starts.</param>
        /// <param name="newBufferLength">The length of the new buffer (sum of all buffer view lengths).</param>
        /// <returns>
        /// The binary content of the buffer views as a vector.
        /// </returns>
        static std::vector<char> SaveBin(std::istream* in, const Microsoft::glTF::Document& glbDoc, const size_t bufferOffset, const size_t newBufferlength, std::unordered_set<std::string>& unpackedBufferViews);

        /// <summary>
        /// Loads all images in a glTF-Binary (GLB) asset into a map relating each image identifier to the contents of that image.
        /// </summary>
        /// <param name="in">A stream pointing to the GLB file.</param>
        /// <param name="glbDoc">The manifest describing the GLB asset.</param>
        /// <param name="name">The name that should be used when creating the identifiers for the image files.</param>
        /// <param name="bufferOffset">The offset on the input file where the GLB buffer starts.</param>
        /// <returns>
        /// A map relating each image identifier to the contents of that image.
        /// </returns>
        static std::unordered_map<std::string, std::vector<char>> GetImagesData(std::istream* in, const Microsoft::glTF::Document& glbDoc, const std::string& name, const size_t bufferOffset);

        /// <summary>
        /// Creates the glTF manifest that represents a GLB file after unpacking.
        /// </summary>
        /// <param name="glbDoc">The original manifest contained in the GLB file.</param>
        /// <param name="name">The name that should be used when creating the identifiers for the image and bin files when unpacking.</param>
        /// <returns>
        /// A new glTF manifest that represents the same file, but with images and resources referenced by URI instead of embedded ina GLB buffer.
        /// </returns>
        static Microsoft::glTF::Document CreateGLTFDocument(const Microsoft::glTF::Document& glbDoc, const std::string& name, std::unordered_set<std::string>& unpackedBufferViews);
    };
}


================================================
FILE: glTF-Toolkit/inc/GLTFLODUtils.h
================================================
// 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
{
    extern const char* EXTENSION_MSFT_LOD;
    extern const char* MSFT_LOD_IDS_KEY;
    typedef std::unordered_map<std::string, std::shared_ptr<std::vector<std::string>>> LODMap;

    /// <summary>
    /// Utilities to load and merge levels of detail (LOD) in glTF assets using the MSFT_lod extension.
    /// </summary>
    class GLTFLODUtils
    {
    public:
        /// <summary>
        /// Parses the node LODs in a GLTF asset as a map that can be used to read LOD values for each node.
        /// </summary>
        /// <returns>A map that relates each node ID to a vector of its levels of detail node IDs.</returns>
        /// <param name="doc">The glTF document containing LODs to be parsed.</param>
        static LODMap ParseDocumentNodeLODs(const Document& doc);

        /// <summary>
        /// Inserts each LOD Document as a node LOD (at the root level) of the specified primary GLTF asset.
        /// Note: Animation is not currently supported.
        /// </summary>
        /// <returns>The primary GLTF Document with the inserted LOD node.</returns>
        /// <param name="docs">A vector of glTF documents to merge as LODs. The first element of the vector is assumed to be the primary LOD.</param>
        /// <param name="relativePaths">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.</param>
        static Document MergeDocumentsAsLODs(const std::vector<Document>& docs, const std::vector<std::wstring>& relativePaths = std::vector<std::wstring>(), const bool& sharedMaterials = false);

        /// <summary>
        /// Inserts each LOD Document as a node LOD (at the root level) of the specified primary GLTF asset.
        /// Note: Animation is not currently supported.
        /// </summary>
        /// <returns>The primary GLTF Document with the inserted LOD node.</returns>
        /// <param name="docs">A vector of glTF documents to merge as LODs. The first element of the vector is assumed to be the primary LOD.</param>
        /// <param name="screenCoveragePercentages">A vector with the screen coverage percentages corresponding to each LOD. If the size of this 
        /// vector is larger than the size of <see name="docs" />, lower coverage values will cause the asset to be invisible.</param>
        /// <param name="relativePaths">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.</param>
        static Document MergeDocumentsAsLODs(const std::vector<Document>& docs, const std::vector<double>& screenCoveragePercentages, const std::vector<std::wstring>& relativePaths = std::vector<std::wstring>(), const bool& sharedMaterials = false);

        /// <summary>
        /// Determines the highest number of Node LODs for a given glTF asset.
        /// </summary>
        /// <param name="doc">The glTF asset for which to count the max number of node LODs.</param>
        /// <param name="lods">A map containing the parsed node LODs in the document.</param>
        /// <returns>The highest number of Node LODs in the asset.</returns>
        static uint32_t NumberOfNodeLODLevels(const Document& doc, const LODMap& lods);
    };
}



================================================
FILE: glTF-Toolkit/inc/GLTFMeshCompressionUtils.h
================================================
// 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
{
    /// <summary>Draco compression options.</summary>
    struct CompressionOptions
    {
        int PositionQuantizationBits = 14;
        int TexCoordQuantizationBits = 12;
        int NormalQuantizationBits = 10;
        int ColorQuantizationBits = 8;
        int GenericQuantizationBits = 12;
        int Speed = 3;
    };

    /// <summary>
    /// Utilities to compress textures in a glTF asset.
    /// </summary>
    class GLTFMeshCompressionUtils
    {
    public:
        /// <summary>
        /// Applies <see cref="CompressMesh" /> to every mesh in the document, following the same parameter structure as that function.
        /// </summary>
        /// <param name="streamReader">A stream reader that is capable of accessing the resources used in the glTF asset by URI.</param>
        /// <param name="doc">The document from which the mesh will be loaded.</param>
        /// <param name="options">The compression options that will be used.</param>
        /// <param name="outputDirectory">The output directory to which compressed data should be saved.</param>
        /// <returns>
        /// A new glTF manifest that uses the KHR_draco_mesh_compression extension to point to the compressed meshes.
        /// </returns>
        static Document CompressMeshes(
            std::shared_ptr<IStreamReader> streamReader,
            const Document & doc,
            CompressionOptions options,
            const std::string& outputDirectory);

        /// <summary>
        /// Applies Draco mesh compression to the supplied mesh and creates a new set of vertex buffers for all the primitive attributes.
        /// </summary>
        /// <param name="streamReader">A stream reader that is capable of accessing the resources used in the glTF asset by URI.</param>
        /// <param name="doc">The document from which the mesh will be loaded.</param>
        /// <param name="mesh">The mesh which the mesh will be compressed.</param>
        /// <param name="options">The compression options that will be used.</param>
        /// <param name="builder">The output buffer builder that handles bufferId generation for the return document.</param>
        /// <param name="bufferViewsToRemove">Out parameter of BufferView Ids that are no longer in use and should be removed.</param>
        /// <returns>
        /// A new glTF manifest that uses the KHR_draco_mesh_compression extension to point to the compressed meshes.
        /// </returns>
        static Document CompressMesh(
            std::shared_ptr<IStreamReader> streamReader,
            const Document & doc,
            CompressionOptions options,
            const Mesh & mesh,
            BufferBuilder* builder,
            std::unordered_set<std::string>& bufferViewsToRemove);
    };
}

================================================
FILE: glTF-Toolkit/inc/GLTFSDK.h
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#pragma once

#pragma warning(push)
#pragma warning(disable : 4634)
#pragma warning(disable : 4996)

#include <GLTFSDK/Document.h>
#include <GLTFSDK/Deserialize.h>
#include <GLTFSDK/Serialize.h>
#include <GLTFSDK/GLTFResourceWriter.h>
#include <GLTFSDK/GLBResourceReader.h>
#include <GLTFSDK/GLTFResourceReader.h>
#include <GLTFSDK/IStreamReader.h>
#include <GLTFSDK/RapidJsonUtils.h>
#include <GLTFSDK/GLTF.h>
#include <GLTFSDK/Constants.h>

#pragma warning(pop)

================================================
FILE: glTF-Toolkit/inc/GLTFSpecularGlossinessUtils.h
================================================
// 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
{
    /// <summary>
    /// Utilities to remove Specular Glossiness from a glTF asset.
    /// </summary>
    class GLTFSpecularGlossinessUtils
    {
    public:
        /// <summary>
        /// Applies <see cref="ConvertMaterial" /> to every material in the document, following the same parameter structure as that function.
        /// </summary>
        /// <param name="streamReader">A stream reader that is capable of accessing the resources used in the glTF asset by URI.</param>
        /// <param name="doc">The document from which the mesh will be loaded.</param>
        /// <param name="outputDirectory">The output directory to which compressed data should be saved.</param>
        /// <returns>
        /// A new glTF manifest without the KHR_materials_pbrSpecularGlossiness extension.
        /// </returns>
        static Document ConvertMaterials(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const std::string& outputDirectory);

        /// <summary>
        /// Removes the KHR_materials_pbrSpecularGlossiness extension by converting the parameters to Metal Roughness.
        /// </summary>
        /// <param name="streamReader">A stream reader that is capable of accessing the resources used in the glTF asset by URI.</param>
        /// <param name="doc">The document from which the mesh will be loaded.</param>
        /// <param name="material">The material to be converted.</param>
        /// <param name="outputDirectory">The output directory to which compressed data should be saved.</param>
        /// <returns>
        /// A new glTF manifest without the KHR_materials_pbrSpecularGlossiness extension.
        /// </returns>
        static Document ConvertMaterial(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const Material & material, const std::string& outputDirectory);

    };
}

================================================
FILE: glTF-Toolkit/inc/GLTFTextureCompressionUtils.h
================================================
// 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 DirectX
{
    class ScratchImage;
}

namespace Microsoft::glTF::Toolkit
{
    extern const char* EXTENSION_MSFT_TEXTURE_DDS;

    /// <summary>Supported compression algorithms for textures.</summary>
    enum class TextureCompression
    {
        None,
        BC3,
        BC5,
        BC7,
        BC7_SRGB
    };

    /// <summary>
    /// Utilities to compress textures in a glTF asset.
    /// </summary>
    class GLTFTextureCompressionUtils
    {
    public:
        /// <summary>Compresses a texture in a glTF from a WIC-readable format (PNG, JPEG, BMP, GIF, TIFF, HD Photo, ICO) 
        /// into a DDS with the appropriate compression.
        /// <para>If a dds extension already exists for this texture, do nothing.</para>
        /// <param name="streamReader">The stream reader that will be used to get streams to each image from its URI.</param>
        /// <param name="doc">Input glTF document.</param>
        /// <param name="texture">Texture object that is contained in input document. If texture does not exist in document, 
        /// throws exception.</param>
        /// <param name="compression">The desired block compression method (e.g. BC5, BC7).</param>
        /// <param name="outputDirectory">The output directory to which compressed files should be saved.</param>
        /// <param name="maxTextureSize">The maximum texture size to which textures should be resized before compression, in pixels.</param>
        /// <param name="generateMipMaps">If true, also generates mip maps when compressing.</param>
        /// <param name="retainOriginalImage">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).</param>
        /// <returns>Returns a new Document that contains a new reference to the compressed dds file added as part 
        /// of the MSFT_texture_dds extension.</returns>
        /// <example>
        /// Example Input:
        /// <code>
        /// "textures": [
        ///    {
        ///        "source": 0,
        ///    }
        /// ],
        /// "images": [
        /// {
        ///    "uri": "defaultTexture.png"
        /// }
        /// ]
        /// </code>
        ///
        /// Example Output (BC7 Compression, with retainOriginalImage == true):
        /// <code>
        /// "textures": 
        /// [
        ///    {
        ///        "source": 0,
        ///        "extensions": {
        ///            "MSFT_texture_dds": {
        ///                "source": 1
        ///            }
        ///        }
        ///    }
        /// ],
        /// "images": [
        /// {
        ///    "uri": "defaultTexture.png"
        /// },
        /// {
        ///    "uri": "defaultTexture-BC7.DDS"
        /// }
        /// ]
        /// </code>
        /// </example>
        /// </summary>
        static Document CompressTextureAsDDS(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits<size_t>::max(), bool generateMipMaps = true, bool retainOriginalImage = true, bool treatAsLinear = true);

        /// <summary>
        /// Applies <see cref="CompressTextureAsDDS" /> to all textures in the document that are accessible via materials according to the 
        /// requirements of the Windows Mixed Reality home.
        /// <para>Normal textures get compressed with BC5, while baseColorTexture, occlusion, metallicRoughness and emissive textures get compressed with BC7.</para>
        /// <param name="streamReader">The stream reader that will be used to get streams to each image from its URI.</param>
        /// <param name="doc">Input glTF document.</param>
        /// <param name="outputDirectory">The output directory to which compressed files should be saved.</param>
        /// <param name="maxTextureSize">The maximum texture size to which textures should be resized before compression, in pixels.</param>
        /// <param name="generateMipMaps">If true, also generates mip maps when compressing.</param>
        /// <param name="retainOriginalImage">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).</param>
        /// <returns>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.</returns>
        /// </summary>
        static Document CompressAllTexturesForWindowsMR(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const std::string& outputDirectory, size_t maxTextureSize = std::numeric_limits<size_t>::max(), bool retainOriginalImages = true);

        /// <summary>
        /// Compresses a DirectX::ScratchImage in place using the specified compression.
        /// </summary>
        /// <param name="image">The image to compress.</param>
        /// <param name="compression">The desired compression algorithm.</param>
        static void CompressImage(DirectX::ScratchImage& image, TextureCompression compression);
    };
}

================================================
FILE: glTF-Toolkit/inc/GLTFTexturePackingUtils.h
================================================
// 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
{
    extern const char* EXTENSION_MSFT_PACKING_ORM;
    extern const char* EXTENSION_MSFT_PACKING_NRM;
    extern const char* MSFT_PACKING_INDEX_KEY;
    extern const char* MSFT_PACKING_ORM_ORMTEXTURE_KEY;
    extern const char* MSFT_PACKING_ORM_RMOTEXTURE_KEY;
    extern const char* MSFT_PACKING_ORM_NORMALTEXTURE_KEY;
    extern const char* MSFT_PACKING_NRM_KEY;

    /// <summary>Texture packing flags. May be combined to pack multiple formats at once.</summary>
    enum TexturePacking
    {
        None = 0x0,
        OcclusionRoughnessMetallic = 0x1,
        RoughnessMetallicOcclusion = 0x2,
        NormalRoughnessMetallic = 0x4
    };

    /// <summary>
    /// Utilities to pack textures from glTF assets and refer to them from an asset
    /// using the MSFT_packing_occlusionRoughnessMetallic extension.
    /// </summary>
    class GLTFTexturePackingUtils
    {
    public:
        /// <summary>
        /// Packs a single material's textures for Windows Mixed Reality for all the packing schemes selected, and adds the resulting texture(s) back to the material in the document.
        /// </summary>
        /// <param name="streamReader">A stream reader that is capable of accessing the resources used in the glTF asset by URI.</param>
        /// <param name="doc">The document from which the texture will be loaded.</param>
        /// <param name="material">The material to be packed.</param>
        /// <param name="packing">The packing scheme that will be used to pick the textures and choose their order.</param>
        /// <param name="outputDirectory">The output directory to which packed textures should be saved.</param>
        /// <returns>
        /// A new glTF manifest that uses the MSFT_packing_occlusionRoughnessMetallic extension to point to the packed textures.
        /// </returns>
        static Document PackMaterialForWindowsMR(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const Material & material, TexturePacking packing, const std::string& outputDirectory);

        /// <summary>
        /// Applies <see cref="PackMaterialForWindowsMR" /> to every material in the document, following the same parameter structure as that function.
        /// </summary>
        /// <param name="streamReader">A stream reader that is capable of accessing the resources used in the glTF asset by URI.</param>
        /// <param name="doc">The document from which the texture will be loaded.</param>
        /// <param name="packing">The packing scheme that will be used to pick the textures and choose their order.</param>
        /// <param name="outputDirectory">The output directory to which packed textures should be saved.</param>
        /// <returns>
        /// A new glTF manifest that uses the MSFT_packing_occlusionRoughnessMetallic extension to point to the packed textures.
        /// </returns>
        static Document PackAllMaterialsForWindowsMR(std::shared_ptr<IStreamReader> streamReader, const Document & doc, TexturePacking packing, const std::string& outputDirectory);

        static std::unordered_set<int> GetTextureIndicesFromMsftExtensions(const Material& material);
    };
}



================================================
FILE: glTF-Toolkit/inc/GLTFTextureUtils.h
================================================
// 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 <DirectXTex.h>
#include <GLTFSDK/Document.h>
#include <wincodec.h>

namespace Microsoft::glTF::Toolkit
{
    enum Channel
    {
        Red = 0,
        Green = 4,
        Blue = 8,
        Alpha = 12
    };

    /// <summary>
    /// Utilities to load textures from glTF assets.
    /// </summary>
    class GLTFTextureUtils
    {
    public:
        /// <summary>
        /// Loads a texture into a scratch image in the DXGI_FORMAT_R32G32B32A32_FLOAT format for in-memory processing.
        /// </summary>
        /// <returns>A scratch image containing the loaded texture in the DXGI_FORMAT_R32G32B32A32_FLOAT format.</returns>
        /// <param name="streamReader">A stream reader that is capable of accessing the resources used in the glTF asset by URI.</param>
        /// <param name="doc">The document from which the texture will be loaded.</param>
        /// <param name="textureId">The identifier of the texture to be loaded.</param>
        static DirectX::ScratchImage LoadTexture(std::shared_ptr<const IStreamReader> streamReader, const Document& doc, const std::string& textureId, bool treatAsLinear = true);

        /// <summary>
        /// Gets the value of channel `channel` in pixel index `offset` in image `imageData`
        /// assumed to be formatted as DXGI_FORMAT_R32G32B32A32_FLOAT
        /// </summary>
        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<DirectX::ScratchImage>& image1, std::unique_ptr<DirectX::ScratchImage>& image2);

        static void ResizeIfNeeded(const std::unique_ptr<DirectX::ScratchImage>& image, size_t resizedWidth, size_t resizedHeight);

        static Document RemoveRedundantTexturesAndImages(const Document& doc);
    };
}



================================================
FILE: glTF-Toolkit/inc/SerializeBinary.h
================================================
// 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 <functional>
#include <memory>
#include <vector>

#include "AccessorUtils.h"

namespace Microsoft::glTF::Toolkit
{
    /// <summary>
    /// A function that determines to which type an accessor should be converted,
    /// based on the accessor metadata.
    /// </summary>
    typedef std::function<ComponentType(const Accessor&)> AccessorConversionStrategy;

    /// <summary>
    /// Serializes a glTF asset as a glTF binary (GLB) file.
    /// </summary>
    /// <param name="Document">The glTF asset manifest to be serialized.</param>
    /// <param name="inputStreamReader">A stream reader that is capable of accessing the resources used in the glTF asset by URI.</param>
    /// <param name="outputStreamFactory">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.</param>
    void SerializeBinary(const Document& document, std::shared_ptr<const IStreamReader> inputStreamReader, std::shared_ptr<const IStreamWriter> outputStreamWriter, const AccessorConversionStrategy& accessorConversion = nullptr);

    /// <summary>
    /// Serializes a glTF asset as a glTF binary (GLB) file.
    /// </summary>
    /// <param name="Document">The glTF asset manifest to be serialized.</param>
    /// <param name="resourceReader">A resource reader that is capable of accessing the resources used in the document.</param>
    /// <param name="outputStreamFactory">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.</param>
    void SerializeBinary(const Document& document, const GLTFResourceReader& resourceReader, std::shared_ptr<const IStreamWriter> outputStreamWriter, const AccessorConversionStrategy& accessorConversion = nullptr);
}


================================================
FILE: glTF-Toolkit/inc/pch.h
================================================
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#pragma once

// Use the C++ standard templated min/max
#define NOMINMAX

// DirectX apps don't need GDI
#define NODRAWTEXT
#define NOGDI
#define NOBITMAP

// Include <mcx.h> if you need this
#define NOMCX

// Include <winsvc.h> if you need this
#define NOSERVICE

// WinHelp is deprecated
#define NOHELP

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincodec.h>
#include <pathcch.h>

#include <wrl/client.h>

#include <d3d11_1.h>
#include <dxgi1_2.h>
#include <DirectXMath.h>
#include <DirectXColors.h>

// Silence C4996 for CodeCVT deprecations. CodeCVT is still used for UTF8 conversions in GLTFLODUtils.cpp
// TODO: Remove
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
#include <string>
#undef _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING

#include <algorithm>
#include <exception>
#include <fstream>
#include <iostream>
#include <memory>
#include <set>
#include <sstream>
#include <stdexcept>

#include <stdio.h>

================================================
FILE: glTF-Toolkit/packages.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="directxtex_desktop_2015" version="2018.8.5.1" targetFramework="native" />
  <package id="draco.CPP" version="1.3.3.1" targetFramework="native" />
  <package id="Microsoft.glTF.CPP" version="1.6.1.0" targetFramework="native" />
  <package id="rapidjson.temprelease" version="0.0.2.20" targetFramework="native" />
</packages>

================================================
FILE: glTF-Toolkit/src/DeviceResources.cpp
================================================
// 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 "DeviceResources.h"

using namespace DirectX;
using namespace DX;

using Microsoft::WRL::ComPtr;

namespace
{
#if defined(_DEBUG)
    // Check for SDK Layer support.
    inline bool SdkLayersAvailable()
    {
        HRESULT hr = D3D11CreateDevice(
            nullptr,
            D3D_DRIVER_TYPE_NULL,       // There is no need to create a real hardware device.
            0,
            D3D11_CREATE_DEVICE_DEBUG,  // Check for the SDK layers.
            nullptr,                    // Any feature level will do.
            0,
            D3D11_SDK_VERSION,
            nullptr,                    // No need to keep the D3D device reference.
            nullptr,                    // No need to know the feature level.
            nullptr                     // No need to keep the D3D device context reference.
            );

        return SUCCEEDED(hr);
    }
#endif
};

// Constructor for DeviceResources.
DeviceResources::DeviceResources(D3D_FEATURE_LEVEL minFeatureLevel) :
    m_d3dMinFeatureLevel(minFeatureLevel),
    m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1)
{
}

// Configures the Direct3D device, and stores handles to it and the device context.
void DeviceResources::CreateDeviceResources() 
{
    UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

#if defined(_DEBUG)
    if (SdkLayersAvailable())
    {
        // If the project is in a debug build, enable debugging via SDK Layers with this flag.
        creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
    }
    else
    {
        OutputDebugStringA("WARNING: Direct3D Debug Device is not available\n");
    }
#endif

    // Determine DirectX hardware feature levels this app will support.
    static const D3D_FEATURE_LEVEL s_featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1,
    };

    UINT featLevelCount = 0;
    for (; featLevelCount < _countof(s_featureLevels); ++featLevelCount)
    {
        if (s_featureLevels[featLevelCount] < m_d3dMinFeatureLevel)
            break;
    }

    if (!featLevelCount)
    {
        throw std::out_of_range("minFeatureLevel too high");
    }

    ComPtr<IDXGIAdapter1> adapter;
    GetHardwareAdapter(adapter.GetAddressOf());

    // Create the Direct3D 11 API device object and a corresponding context.
    ComPtr<ID3D11Device> device;
    ComPtr<ID3D11DeviceContext> context;

    HRESULT hr = E_FAIL;
    if (adapter)
    {
        hr = D3D11CreateDevice(
            adapter.Get(),
            D3D_DRIVER_TYPE_UNKNOWN,
            0,
            creationFlags,
            s_featureLevels,
            featLevelCount,
            D3D11_SDK_VERSION,
            device.GetAddressOf(),      // Returns the Direct3D device created.
            &m_d3dFeatureLevel,         // Returns feature level of device created.
            context.GetAddressOf()      // Returns the device immediate context.
            );
    }
#if defined(NDEBUG)
    else
    {
        throw std::exception("No Direct3D hardware device found");
    }
#else
    if (FAILED(hr))
    {
        // If the initialization fails, fall back to the WARP device.
        // For more information on WARP, see: 
        // http://go.microsoft.com/fwlink/?LinkId=286690
        hr = D3D11CreateDevice(
            nullptr,
            D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device.
            0,
            creationFlags,
            s_featureLevels,
            featLevelCount,
            D3D11_SDK_VERSION,
            device.GetAddressOf(),
            &m_d3dFeatureLevel,
            context.GetAddressOf()
            );

        if (SUCCEEDED(hr))
        {
            OutputDebugStringA("Direct3D Adapter - WARP\n");
        }
    }
#endif

    ThrowIfFailed(hr);

#ifndef NDEBUG
    ComPtr<ID3D11Debug> d3dDebug;
    if (SUCCEEDED(device.As(&d3dDebug)))
    {
        ComPtr<ID3D11InfoQueue> d3dInfoQueue;
        if (SUCCEEDED(d3dDebug.As(&d3dInfoQueue)))
        {
#ifdef _DEBUG
            d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
            d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
#endif
            D3D11_MESSAGE_ID hide [] =
            {
                D3D11_MESSAGE_ID_SETPRIVATEDATA_CHANGINGPARAMS,
            };
            D3D11_INFO_QUEUE_FILTER filter = {};
            filter.DenyList.NumIDs = _countof(hide);
            filter.DenyList.pIDList = hide;
            d3dInfoQueue->AddStorageFilterEntries(&filter);
        }
    }
#endif

    ThrowIfFailed(device.As(&m_d3dDevice));
    ThrowIfFailed(context.As(&m_d3dContext));
    ThrowIfFailed(context.As(&m_d3dAnnotation));
}

// Recreate all device resources and set them back to the current state.
void DeviceResources::HandleDeviceLost()
{
    m_swapChain.Reset();
    m_d3dContext.Reset();
    m_d3dAnnotation.Reset();

#ifdef _DEBUG
    {
        ComPtr<ID3D11Debug> d3dDebug;
        if (SUCCEEDED(m_d3dDevice.As(&d3dDebug)))
        {
            d3dDebug->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY);
        }
    }
#endif

    m_d3dDevice.Reset();

    CreateDeviceResources();
}

// This method acquires the first available hardware adapter.
// If no such adapter can be found, *ppAdapter will be set to nullptr.
void DeviceResources::GetHardwareAdapter(IDXGIAdapter1** ppAdapter)
{
    *ppAdapter = nullptr;

    ComPtr<IDXGIFactory1> dxgiFactory;
    ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(dxgiFactory.GetAddressOf())));

    ComPtr<IDXGIAdapter1> adapter;
    for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != dxgiFactory->EnumAdapters1(adapterIndex, adapter.ReleaseAndGetAddressOf()); adapterIndex++)
    {
        DXGI_ADAPTER_DESC1 desc;
        adapter->GetDesc1(&desc);

        if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
        {
            // Don't select the Basic Render Driver adapter.
            continue;
        }

#ifdef _DEBUG
        wchar_t buff[256] = {};
        swprintf_s(buff, L"Direct3D Adapter (%u): VID:%04X, PID:%04X - %ls\n", adapterIndex, desc.VendorId, desc.DeviceId, desc.Description);
        OutputDebugStringW(buff);
#endif

        break;
    }

    *ppAdapter = adapter.Detach();
}

================================================
FILE: glTF-Toolkit/src/GLBtoGLTF.cpp
================================================
// 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 "GLBtoGLTF.h"
#include "GLTFSDK/ExtensionsKHR.h"

using namespace Microsoft::glTF;
using namespace Microsoft::glTF::Toolkit;

static std::unordered_map<std::string, std::string> s_gltfMimeTypes = 
{
    { MIMETYPE_PNG, FILE_EXT_PNG },
    { MIMETYPE_JPEG, FILE_EXT_JPEG },
    { "image/vnd-ms.dds", "dds" },
    { "text/plain", "glsl" },
    { "audio/wav", "wav" },
};

std::string GuessFileExtension(const std::string& mimeType)
{
    auto itr = s_gltfMimeTypes.find(mimeType);
    if (itr != s_gltfMimeTypes.end())
    {
        return itr->second;
    }
    return BUFFER_EXTENSION;
}

namespace
{
    class StreamMock : public IStreamReader
    {
    public:
        StreamMock() : m_stream(std::make_shared<std::stringstream>(std::ios_base::app | std::ios_base::binary | std::ios_base::in | std::ios_base::out))
        {
        }

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

    private:
        std::shared_ptr<std::stringstream> m_stream;
    };

    size_t GetGLBBufferChunkOffset(std::ifstream* input)
    {
        // get offset from beginning of glb binary to beginning of buffer chunk
        input->seekg(GLB2_HEADER_BYTE_SIZE, std::ios::beg);
        uint32_t length = 0;
        for (int i = 0; i < GLB_CHUNK_TYPE_SIZE*CHAR_BIT; i += CHAR_BIT)
        {
            uint8_t c = static_cast<uint8_t>(input->get());
            length |= ((uint16_t)c << i);
        }
        // 28 is total length of non-json blocks from start of glb blob
        // 28 = (GLB2_HEADER_BYTE_SIZE = 12bytes) + (uint32 = 4bytes) * 4
        return length + GLB2_HEADER_BYTE_SIZE + GLB_CHUNK_TYPE_SIZE * 4;
    }
}

std::vector<char> GLBToGLTF::SaveBin(std::istream* input, const Document& glbDoc, const size_t bufferOffset, const size_t newBufferlength, std::unordered_set<std::string>& unpackedBufferViews)
{
    if (newBufferlength == 0)
    {
        return {};
    }

    const auto images = glbDoc.images.Elements();
    const auto bufferViews = glbDoc.bufferViews.Elements();

    // gather all non-image bufferViews in UsedBufferViews
    std::vector<BufferView> usedBufferViews(bufferViews.size());
    auto end = copy_if(bufferViews.begin(), bufferViews.end(), usedBufferViews.begin(), [unpackedBufferViews](const auto& a)
    {
        return unpackedBufferViews.count(a.id) == 0;
    });
    usedBufferViews.resize(distance(usedBufferViews.begin(), end));

    // sort buffer views by offset
    sort(usedBufferViews.begin(), usedBufferViews.end(), [](const BufferView& a, const BufferView& b)
    {
        return a.byteOffset < b.byteOffset;
    });

    std::vector<char> result(newBufferlength);
    size_t vecpos = 0; // number of chunks read
    size_t currOffset = bufferOffset; // offset into buffer
    input->seekg(bufferOffset, std::ios::beg);

    for (const auto& bufferView : usedBufferViews)
    {
        // traverse through original buffer while grabbing non-image buffer segments
        size_t nextOffset = bufferOffset + bufferView.byteOffset;
        if (currOffset < nextOffset)
        {
            // skip over buffer segments of no interest
            size_t chunkLength = nextOffset - currOffset;
            input->seekg(chunkLength, std::ios::cur);
            currOffset += chunkLength;
        }

        if (vecpos % GLB_BUFFER_OFFSET_ALIGNMENT != 0)
        {
            // Alignment padding
            // Accessor component sizes can be 1, 2, 4.
            // Aligning to 4 will satisfy requirements but wastes space
            vecpos += (GLB_BUFFER_OFFSET_ALIGNMENT - (vecpos % GLB_BUFFER_OFFSET_ALIGNMENT));
        }

        // read and increment vecpos + offset
        input->read(&result[vecpos], bufferView.byteLength);
        currOffset += bufferView.byteLength;
        vecpos += bufferView.byteLength;
    }

    if (vecpos == 0)
    {
        return {};
    }

    return result;
}

std::unordered_map<std::string, std::vector<char>> 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<std::string, int> imageIDs;
    std::vector<Image> images = std::vector<Image>(glbDoc.images.Elements());
    if (images.size() == 0)
    {
        return {};
    }

    int imgId = 0;
    for (const auto& img : images)
    {
        // save mapping of original image order
        imageIDs[img.bufferViewId] = imgId;
        imgId++;
    }

    // sort images by buffer offset so only traverse once
    sort(images.begin(), images.end(), [glbDoc](const auto& a, const auto& b)
    {
        return glbDoc.bufferViews.Get(a.bufferViewId).byteOffset < glbDoc.bufferViews.Get(b.bufferViewId).byteOffset;
    });

    size_t currOffset = bufferOffset; // offset into buffer
    input->seekg(bufferOffset, std::ios::beg);

    std::unordered_map<std::string, std::vector<char>> imageStream;
    for (const auto& img : images)
    {
        // traverse through buffer while saving images
        auto bufferView = glbDoc.bufferViews.Get(img.bufferViewId);
        size_t nextImageOffset = bufferOffset + bufferView.byteOffset;
        if (currOffset < nextImageOffset)
        {
            // skip over non-image buffer segments
            size_t chunkLength = nextImageOffset - currOffset;
            input->seekg(chunkLength, std::ios::cur);
            currOffset = nextImageOffset;
        }
        // read and increment offset
        std::vector<char> result;
        result.resize(bufferView.byteLength);
        input->read(&result[0], bufferView.byteLength);
        currOffset += bufferView.byteLength;

        // write image file
        std::string outname = name + "_image" + std::to_string(imageIDs[img.bufferViewId]) + "." + GuessFileExtension(img.mimeType);

        imageStream[outname] = std::move(result);
    }
    return imageStream;
}

std::unordered_map<std::string, std::vector<char>> GetExtensionsData(std::istream* input, const Document& glbDoc, const std::string& name, const size_t bufferOffset)
{
    std::unordered_map<std::string, std::vector<char>> extensionStreams;
    // Collect anything in extensions that looks like it should be unpacked.
    for (const auto& extension : glbDoc.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;
                }
                std::string bufferViewId{};
                std::string mimeType{};
                if (possibleBuffer.HasMember("bufferView"))
                {
                    bufferViewId = std::to_string(possibleBuffer["bufferView"].GetUint());
                    
                }
                else
                {
                    continue;
                }
                if (possibleBuffer.HasMember("mimeType"))
                {
                    mimeType = possibleBuffer["mimeType"].GetString();
                }
                try
                {
                    auto bufferView = glbDoc.bufferViews.Get(bufferViewId);
                    auto filename = name + "_" + extension.first + "_" + member.name.GetString() + "_" + bufferViewId + "." + GuessFileExtension(mimeType);

                    size_t offset = bufferOffset + bufferView.byteOffset;
                    input->seekg(offset, std::ios::beg);
                    std::vector<char> result;
                    result.resize(bufferView.byteLength);
                    input->read(&result[0], bufferView.byteLength);

                    extensionStreams[filename] = std::move(result);
                }
                catch (...)
                {
                    // Didn't work out.
                    continue;
                }
            }
        }
    }
    return extensionStreams;
}

// Create modified gltf from original by removing image buffer segments and updating
// images, bufferViews and accessors fields accordingly
Document GLBToGLTF::CreateGLTFDocument(const Document& glbDoc, const std::string& name, std::unordered_set<std::string>& unpackedBufferViews)
{
    Document gltfDoc(glbDoc);

    gltfDoc.images.Clear();
    gltfDoc.buffers.Clear();
    gltfDoc.bufferViews.Clear();
    gltfDoc.accessors.Clear();

    const auto images = glbDoc.images.Elements();
    const auto buffers = glbDoc.buffers.Elements();
    const auto bufferViews = glbDoc.bufferViews.Elements();
    const auto accessors = glbDoc.accessors.Elements();
    std::unordered_map<std::string, std::string> bufferViewIndex;

    size_t updatedBufferSize = 0;
    int imgId = 0;
    for (const auto& im : images)
    {
        // find which buffer segments correspond to images
        unpackedBufferViews.insert(im.bufferViewId);

        // update image fields with image names instead of buffer views
        Image updatedImage;
        updatedImage.id = std::to_string(imgId);
        updatedImage.uri = name + "_image" + std::to_string(imgId) + "." + GuessFileExtension(im.mimeType);

        gltfDoc.images.Append(std::move(updatedImage));
        imgId++;
    }

    // Collect anything in extensions that looks like it should be unpacked.
    for (auto& extension : gltfDoc.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;
                }
                std::string bufferViewId{};
                std::string mimeType{};
                if (possibleBuffer.HasMember("bufferView"))
                {
                    bufferViewId = std::to_string(possibleBuffer["bufferView"].GetUint());
                    unpackedBufferViews.insert(bufferViewId);
                }
                else
                {
                    continue;
                }
                if (possibleBuffer.HasMember("mimeType"))
                {
                    mimeType = possibleBuffer["mimeType"].GetString();
                }
                try
                {
                    possibleBuffer.RemoveMember("uri");
                    possibleBuffer.RemoveMember("mimeType");
                    possibleBuffer.RemoveMember("bufferView");
                    auto filename = name + "_" + extension.first + "_" + member.name.GetString() + "_" + bufferViewId + "." + GuessFileExtension(mimeType);
                    possibleBuffer.AddMember("uri", rapidjson::Value(filename.c_str(), extensionJson.GetAllocator()), extensionJson.GetAllocator());
                }
                catch (...)
                {
                    // Didn't work out.
                    continue;
                }
            }
        }

        rapidjson::StringBuffer buffer;
        rapidjson::Writer<rapidjson::StringBuffer> jsonWriter(buffer);
        extensionJson.Accept(jsonWriter);

        extension.second = buffer.GetString();
    }

    // gather all non-image bufferViews in UsedBufferViews
    std::vector<BufferView> usedBufferViews(bufferViews.size());
    auto end = copy_if(bufferViews.begin(), bufferViews.end(), usedBufferViews.begin(), [&unpackedBufferViews](const auto& a)
    {
        return unpackedBufferViews.count(a.id) == 0;
    });

    usedBufferViews.resize(distance(usedBufferViews.begin(), end));

    // group buffer views by buffer, then sort them by byteOffset to calculate their new byteOffsets
    sort(usedBufferViews.begin(), usedBufferViews.end(), [](const auto& a, const auto& b)
    {
        return a.byteOffset < b.byteOffset;
    });

    int updatedBufferViewId = 0;
    size_t currentOffset = 0;
    for (const auto& b : usedBufferViews)
    {
        // provide new byte ranges for bufferviews
        size_t padding = 0;
        auto updatedBufferView = b;
        updatedBufferView.id = std::to_string(updatedBufferViewId);

        if (currentOffset % GLB_BUFFER_OFFSET_ALIGNMENT != 0)
        {
            // alignment padding as in SaveBin
            padding = (GLB_BUFFER_OFFSET_ALIGNMENT - (currentOffset % GLB_BUFFER_OFFSET_ALIGNMENT));
            currentOffset += padding;
        }

        updatedBufferView.byteOffset = currentOffset;
        currentOffset += b.byteLength;
        gltfDoc.bufferViews.Append(std::move(updatedBufferView));
        bufferViewIndex[b.id] = std::to_string(updatedBufferViewId);
        updatedBufferSize += (b.byteLength + padding);
        updatedBufferViewId++;
    }

    if (!buffers.empty())
    {
        auto updatedBuffer = buffers[0];
        updatedBuffer.byteLength = updatedBufferSize;
        updatedBuffer.uri = name + "." + BUFFER_EXTENSION;
        gltfDoc.buffers.Append(std::move(updatedBuffer));
    }

    for (const auto& a : accessors)
    {
        if (unpackedBufferViews.find(a.bufferViewId) == unpackedBufferViews.end())
        {
            // update acessors with new bufferview IDs, the above check may not be needed
            auto updatedAccessor = a;
            updatedAccessor.bufferViewId = bufferViewIndex[a.bufferViewId];
            gltfDoc.accessors.Append(std::move(updatedAccessor));
        }
    }

    bool changes = false;
    const auto meshes = glbDoc.meshes.Elements();
    std::vector<Mesh> updatedMeshs;
    for (auto updatedMesh : meshes)
    {
        for (auto& primitive : updatedMesh.primitives)
        {
            if (primitive.HasExtension<KHR::MeshPrimitives::DracoMeshCompression>())
            {
                auto& draco = primitive.GetExtension<KHR::MeshPrimitives::DracoMeshCompression>();
                draco.bufferViewId = bufferViewIndex[draco.bufferViewId];
                changes = true;
            }
        }
        updatedMeshs.emplace_back(updatedMesh);
    }

    if (changes)
    {
        for (const auto& mesh : updatedMeshs)
        {
            gltfDoc.meshes.Replace(mesh);
        }
    }

    return gltfDoc;
}

void GLBToGLTF::UnpackGLB(const std::string& glbPath, const std::string& outDirectory, const std::string& gltfName)
{
    // read glb file into json
    auto glbStream = std::make_shared<std::ifstream>(glbPath, std::ios::binary);
    auto streamReader = std::make_shared<StreamMock>();
    GLBResourceReader reader(streamReader, glbStream);

    // get original json
    auto json = reader.GetJson();
    auto doc = Deserialize(json, KHR::GetKHRExtensionDeserializer());

    // create new modified json
    std::unordered_set<std::string> unpackedBufferViews;
    auto gltfDoc = GLBToGLTF::CreateGLTFDocument(doc, gltfName, unpackedBufferViews);

    // serialize and write new gltf json
    auto gltfJson = Serialize(gltfDoc, KHR::GetKHRExtensionSerializer());
    std::ofstream outputStream(outDirectory + gltfName + "." + GLTF_EXTENSION);
    outputStream << gltfJson;
    outputStream.flush();

    // write images
    size_t bufferOffset = GetGLBBufferChunkOffset(glbStream.get());
    for (auto image : GLBToGLTF::GetImagesData(glbStream.get(), doc, gltfName, bufferOffset))
    {
        std::ofstream out(outDirectory + image.first, std::ios::binary);
        out.write(&image.second[0], image.second.size());
    }

    for (auto ext : GetExtensionsData(glbStream.get(), doc, gltfName, bufferOffset))
    {
        std::ofstream out(outDirectory + ext.first, std::ios::binary);
        out.write(&ext.second[0], ext.second.size());
    }

    // get new buffer size and write new buffer
    if (gltfDoc.buffers.Size() != 0)
    {
        size_t newBufferSize = gltfDoc.buffers[0].byteLength;
        auto binFileData = GLBToGLTF::SaveBin(glbStream.get(), doc, bufferOffset, newBufferSize, unpackedBufferViews);
        std::ofstream out(outDirectory + gltfName + "." + BUFFER_EXTENSION, std::ios::binary);
        out.write(&binFileData[0], binFileData.size());
    }
}


================================================
FILE: glTF-Toolkit/src/GLTFLODUtils.cpp
================================================
// 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 "GLTFTextureCompressionUtils.h"
#include "GLTFTexturePackingUtils.h"
#include "GLTFLODUtils.h"

#include "GLTFSDK/GLTF.h"
#include "GLTFSDK/Constants.h"
#include "GLTFSDK/Deserialize.h"
#include "GLTFSDK/RapidJsonUtils.h"
#include "GLTFSDK/ExtensionsKHR.h"

#include <algorithm>
#include <iostream>
#include <set>
#include <codecvt>
#include <filesystem>

using namespace Microsoft::glTF;
using namespace Microsoft::glTF::Toolkit;

const char* Microsoft::glTF::Toolkit::EXTENSION_MSFT_LOD = "MSFT_lod";
const char* Microsoft::glTF::Toolkit::MSFT_LOD_IDS_KEY = "ids";

namespace
{
    inline void AddIndexOffset(std::string& id, size_t offset)
    {
        // an empty id string indicates that the id is not inuse and therefore should not be updated
        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))
        {
            if (json[textureId].HasMember(MSFT_PACKING_INDEX_KEY))
            {
                auto index = json[textureId][MSFT_PACKING_INDEX_KEY].GetInt();
                json[textureId][MSFT_PACKING_INDEX_KEY] = index + offset;
            }
        }
    }

    std::vector<std::string> ParseExtensionMSFTLod(const Node& node)
    {
        std::vector<std::string> lodIds;

        auto lodExtension = node.extensions.find(Toolkit::EXTENSION_MSFT_LOD);
        if (lodExtension != node.extensions.end())
        {
            auto json = RapidJsonUtils::CreateDocumentFromString(lodExtension->second);

            auto idIt = json.FindMember(Toolkit::MSFT_LOD_IDS_KEY);
            if (idIt != json.MemberEnd())
            {
                for (rapidjson::Value::ConstValueIterator ait = idIt->value.Begin(); ait != idIt->value.End(); ++ait)
                {
                    lodIds.push_back(std::to_string(ait->GetInt()));
                }
            }
        }

        return lodIds;
    }

    template <typename T>
    std::string SerializeExtensionMSFTLod(const T&, const std::vector<std::string>& lods, const Document& document)
    {
        // Omit MSFT_lod entirely if no LODs are available
        if (lods.empty())
        {
            return std::string();
        }

        rapidjson::Document doc(rapidjson::kObjectType);
        rapidjson::Document::AllocatorType& a = doc.GetAllocator();

        std::vector<size_t> lodIndices;
        lodIndices.reserve(lods.size());

        if (std::is_same<T, Material>())
        {
            for (const auto& lodId : lods)
            {
                lodIndices.push_back(ToKnownSizeType(document.materials.GetIndex(lodId)));
            }
        }
        else if (std::is_same<T, Node>())
        {
            for (const auto& lodId : lods)
            {
                lodIndices.push_back(ToKnownSizeType(document.nodes.GetIndex(lodId)));
            }
        }
        else
        {
            throw GLTFException("LODs can only be applied to materials or nodes.");
        }

        doc.AddMember(RapidJsonUtils::ToStringValue(Toolkit::MSFT_LOD_IDS_KEY, a), RapidJsonUtils::ToJsonArray(lodIndices, a), a);

        rapidjson::StringBuffer stringBuffer;
        rapidjson::Writer<rapidjson::StringBuffer> writer(stringBuffer);
        doc.Accept(writer);

        return stringBuffer.GetString();
    }

    Document AddGLTFNodeLOD(const Document& primary, LODMap& primaryLods, const Document& lod, const std::wstring& relativePath = L"", bool sharedMaterials = false)
    {
        Microsoft::glTF::Document gltfLod(primary);

        auto primaryScenes = primary.scenes.Elements();
        auto lodScenes = lod.scenes.Elements();

        size_t MaxLODLevel = 0;

        // Both GLTF must have equivalent number and order of scenes and root nodes per scene otherwise merge will not be possible
        bool sceneNodeMatch = false;
        if (primaryScenes.size() == lodScenes.size())
        {
            for (size_t sceneIdx = 0; sceneIdx < primaryScenes.size(); sceneIdx++)
            {

                if ((primaryScenes[sceneIdx].nodes.size() == lodScenes[sceneIdx].nodes.size()) &&
                    (lodScenes[sceneIdx].nodes.size() == 1 ||
                        std::equal(primaryScenes[sceneIdx].nodes.begin(), primaryScenes[sceneIdx].nodes.end(), lodScenes[sceneIdx].nodes.begin()))
                    )
                {
                    sceneNodeMatch = true;
                    auto primaryRootNode = gltfLod.nodes.Get(primaryScenes[sceneIdx].nodes[0]);
                    MaxLODLevel = std::max(MaxLODLevel, primaryLods.at(primaryRootNode.id)->size());
                }
                else
                {
                    sceneNodeMatch = false;
                    break;
                }
            }
        }

        MaxLODLevel++;

        if (!sceneNodeMatch || primaryScenes.empty())
        {
            // Mis-match or empty scene; either way cannot merge Lod in
            throw new std::runtime_error("Primary Scene either empty or does not match scene node count of LOD gltf");
        }

        std::string nodeLodLabel = "_lod" + std::to_string(MaxLODLevel);

        // lod merge is performed from the lowest reference back upwards
        // e.g. buffers/samplers/extensions do not reference any other part of the gltf manifest    
        size_t buffersOffset = gltfLod.buffers.Size();
        size_t samplersOffset = sharedMaterials ? 0 : gltfLod.samplers.Size();
        {
            auto lodBuffers = lod.buffers.Elements();
            for (auto buffer : lodBuffers)
            {
                AddIndexOffset(buffer.id, buffersOffset);
                std::string relativePathUtf8 = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(relativePath);
                buffer.uri = relativePathUtf8 + buffer.uri;
                gltfLod.buffers.Append(std::move(buffer));
            }

            if (!sharedMaterials)
            {
                auto lodSamplers = lod.samplers.Elements();
                for (auto sampler : lodSamplers)
                {
                    AddIndexOffset(sampler.id, samplersOffset);
                    gltfLod.samplers.Append(std::move(sampler));
                }
            }

            for (const auto& extension : lod.extensionsUsed)
            {
                gltfLod.extensionsUsed.insert(extension);
            }
            // ensure that MSFT_LOD extension is specified as being used
            gltfLod.extensionsUsed.insert(Toolkit::EXTENSION_MSFT_LOD);
        }

        size_t accessorOffset = gltfLod.accessors.Size();
        size_t texturesOffset = gltfLod.textures.Size();
        {
            // Buffer Views depend upon Buffers
            size_t bufferViewsOffset = gltfLod.bufferViews.Size();
            auto lodBufferViews = lod.bufferViews.Elements();
            for (auto bufferView : lodBufferViews)
            {
                AddIndexOffset(bufferView.id, bufferViewsOffset);
                AddIndexOffset(bufferView.bufferId, buffersOffset);
                gltfLod.bufferViews.Append(std::move(bufferView));
            }

            // Accessors depend upon Buffer views        
            auto lodAccessors = lod.accessors.Elements();
            for (auto accessor : lodAccessors)
            {
                AddIndexOffset(accessor.id, accessorOffset);
                AddIndexOffset(accessor.bufferViewId, bufferViewsOffset);
                gltfLod.accessors.Append(std::move(accessor));
            }

            // Images depend upon Buffer views
            size_t imageOffset = sharedMaterials ? 0 : gltfLod.images.Size();
            if (!sharedMaterials)
            {
                auto lodImages = lod.images.Elements();
                for (auto image : lodImages)
                {

                    AddIndexOffset(image.id, imageOffset);
                    AddIndexOffset(image.bufferViewId, bufferViewsOffset);

                    std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
                    std::wstring uri = conv.from_bytes(image.uri);
                    if (std::experimental::filesystem::path(uri).is_relative()) {
                        // to be able to reference images with the same name, prefix with relative path
                        std::string relativePathUtf8 = conv.to_bytes(relativePath);
                        image.uri = relativePathUtf8 + image.uri;
                    }
                    gltfLod.images.Append(std::move(image));
                }

                // Textures depend upon Samplers and Images
                auto lodTextures = lod.textures.Elements();
                for (auto texture : lodTextures)
                {
                    AddIndexOffset(texture.id, texturesOffset);
                    AddIndexOffset(texture.samplerId, samplersOffset);
                    AddIndexOffset(texture.imageId, imageOffset);

                    // MSFT_texture_dds extension
                    auto ddsExtensionIt = texture.extensions.find(EXTENSION_MSFT_TEXTURE_DDS);
                    if (ddsExtensionIt != texture.extensions.end() && !ddsExtensionIt->second.empty())
                    {
                        rapidjson::Document ddsJson = RapidJsonUtils::CreateDocumentFromString(ddsExtensionIt->second);

                        if (ddsJson.HasMember("source"))
                        {
                            auto index = ddsJson["source"].GetInt();
                            ddsJson["source"] = index + imageOffset;
                        }

                        rapidjson::StringBuffer buffer;
                        rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
                        ddsJson.Accept(writer);

                        ddsExtensionIt->second = buffer.GetString();
                    }

                    gltfLod.textures.Append(std::move(texture));
                }
            }
        }

        // Material Merge
        // Note the extension KHR_materials_pbrSpecularGlossiness will be also updated
        // Materials depend upon textures
        size_t materialOffset = sharedMaterials ? 0 : gltfLod.materials.Size();
        if (!sharedMaterials)
        {
            auto lodMaterials = lod.materials.Elements();
            for (auto material : lodMaterials)
            {
                // post-fix with lod level indication; 
                // no functional reason other than making it easier to natively read gltf files with lods
                material.name += nodeLodLabel;
                AddIndexOffset(material.id, materialOffset);

                AddIndexOffset(material.normalTexture.textureId, texturesOffset);
                AddIndexOffset(material.occlusionTexture.textureId, texturesOffset);
                AddIndexOffset(material.emissiveTexture.textureId, texturesOffset);

                AddIndexOffset(material.metallicRoughness.baseColorTexture.textureId, texturesOffset);
                AddIndexOffset(material.metallicRoughness.metallicRoughnessTexture.textureId, texturesOffset);

                if (material.HasExtension<KHR::Materials::PBRSpecularGlossiness>())
                {
                    AddIndexOffset(material.GetExtension<KHR::Materials::PBRSpecularGlossiness>().diffuseTexture.textureId, texturesOffset);
                    AddIndexOffset(material.GetExtension<KHR::Materials::PBRSpecularGlossiness>().specularGlossinessTexture.textureId, texturesOffset);
                }

                // MSFT_packing_occlusionRoughnessMetallic packed textures
                auto ormExtensionIt = material.extensions.find(EXTENSION_MSFT_PACKING_ORM);
                if (ormExtensionIt != material.extensions.end() && !ormExtensionIt->second.empty())
                {
                    rapidjson::Document ormJson = RapidJsonUtils::CreateDocumentFromString(ormExtensionIt->second);

                    AddIndexOffsetPacked(ormJson, MSFT_PACKING_ORM_ORMTEXTURE_KEY, texturesOffset);
                    AddIndexOffsetPacked(ormJson, MSFT_PACKING_ORM_RMOTEXTURE_KEY, texturesOffset);
                    AddIndexOffsetPacked(ormJson, MSFT_PACKING_ORM_NORMALTEXTURE_KEY, texturesOffset);

                    rapidjson::StringBuffer buffer;
                    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
                    ormJson.Accept(writer);

                    ormExtensionIt->second = buffer.GetString();
                }

                // MSFT_packing_normalRoughnessMetallic packed texture
                auto nrmExtensionIt = material.extensions.find(EXTENSION_MSFT_PACKING_NRM);
                if (nrmExtensionIt != material.extensions.end() && !nrmExtensionIt->second.empty())
                {
                    rapidjson::Document nrmJson = RapidJsonUtils::CreateDocumentFromString(nrmExtensionIt->second);

                    AddIndexOffsetPacked(nrmJson, MSFT_PACKING_NRM_KEY, texturesOffset);
                    
                    rapidjson::StringBuffer buffer;
                    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
                    nrmJson.Accept(writer);

                    nrmExtensionIt->second = buffer.GetString();
                }

                gltfLod.materials.Append(std::move(material));
            }
        }

        // Meshs depend upon Accessors and Materials
        size_t meshOffset = gltfLod.meshes.Size();
        {
            auto lodMeshes = lod.meshes.Elements();
            for (auto mesh : lodMeshes)
            {
                // post-fix with lod level indication; 
                // no functional reason other than making it easier to natively read gltf files with lods
                mesh.name += nodeLodLabel;
                AddIndexOffset(mesh.id, meshOffset);

                for (auto &primitive : mesh.primitives)
                {
                    AddIndexOffset(primitive.indicesAccessorId, 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)
                    {
                        // lower quality LODs can have fewer images and textures than the highest LOD,
                        // so we need to find the correct material index for the same material from the highest LOD

                        const Material& localMaterial = lod.materials.Get(primitive.materialId);

                        // find merged material index for the given material index in this LOD
                        auto iter = std::find_if(gltfLod.materials.Elements().begin(),
                                gltfLod.materials.Elements().end(),
                                [localMaterial](const Material& globalMaterial) {
                                    // check that the materials are the same, noting that the texture and material ids will differ
                                    return localMaterial.name == globalMaterial.name &&
                                           localMaterial.alphaMode == globalMaterial.alphaMode &&
                                           localMaterial.alphaCutoff == globalMaterial.alphaCutoff &&
                                           localMaterial.emissiveFactor == globalMaterial.emissiveFactor &&
                                           localMaterial.doubleSided == globalMaterial.doubleSided &&
                                           localMaterial.metallicRoughness.baseColorFactor == globalMaterial.metallicRoughness.baseColorFactor &&
                                           localMaterial.metallicRoughness.metallicFactor == globalMaterial.metallicRoughness.metallicFactor &&
                                           localMaterial.occlusionTexture.strength == globalMaterial.occlusionTexture.strength &&
                                           localMaterial.HasExtension<KHR::Materials::PBRSpecularGlossiness>() == globalMaterial.HasExtension<KHR::Materials::PBRSpecularGlossiness>() && 
                                           (!localMaterial.HasExtension<KHR::Materials::PBRSpecularGlossiness>() ||
                                             (localMaterial.GetExtension<KHR::Materials::PBRSpecularGlossiness>().diffuseFactor == globalMaterial.GetExtension<KHR::Materials::PBRSpecularGlossiness>().diffuseFactor &&
                                              localMaterial.GetExtension<KHR::Materials::PBRSpecularGlossiness>().glossinessFactor == globalMaterial.GetExtension<KHR::Materials::PBRSpecularGlossiness>().glossinessFactor &&
                                              localMaterial.GetExtension<KHR::Materials::PBRSpecularGlossiness>().specularFactor == globalMaterial.GetExtension<KHR::Materials::PBRSpecularGlossiness>().specularFactor)
                                           );
                                }
                        );

                        if (iter != gltfLod.materials.Elements().end())
                        {
                            size_t newMaterialIndex = std::distance(gltfLod.materials.Elements().begin(), iter);
                            primitive.materialId = std::to_string(newMaterialIndex);
                        }
                        else
                        {
                            throw new std::runtime_error("Couldn't find the shared material in the highest LOD.");
                        }
                    }
                    else
                    {
                        AddIndexOffset(primitive.materialId, materialOffset);
                    }
                }

                gltfLod.meshes.Append(std::move(mesh));
            }
        }

        // Nodes depend upon Nodes and Meshes
        size_t nodeOffset = gltfLod.nodes.Size();
        // Skins depend upon Nodes
        size_t skinOffset = gltfLod.skins.Size();
        {
            auto nodes = lod.nodes.Elements();
            for (auto node : nodes)
            {
                // post-fix with lod level indication; 
                // no functional reason other than making it easier to natively read gltf files with lods
                node.name += nodeLodLabel;
                AddIndexOffset(node.id, nodeOffset);
                AddIndexOffset(node.meshId, meshOffset);
                if (!node.skinId.empty())
                {
                    AddIndexOffset(node.skinId, skinOffset);
                }

                for (auto &child : node.children)
                {
                    AddIndexOffset(child, nodeOffset);
                }

                gltfLod.nodes.Append(std::move(node));
            }
        }

        {
            auto skins = lod.skins.Elements();
            for (auto skin : skins)
            {
                // post-fix with lod level indication; 
                // no functional reason other than making it easier to natively read gltf files with lods
                skin.name += nodeLodLabel;
                AddIndexOffset(skin.id, skinOffset);
                AddIndexOffset(skin.skeletonId, nodeOffset);
                AddIndexOffset(skin.inverseBindMatricesAccessorId, accessorOffset);

                for (auto &jointId : skin.jointIds)
                {
                    AddIndexOffset(jointId, nodeOffset);
                }

                gltfLod.skins.Append(std::move(skin));
            }
        }

        // Animation channels depend upon Nodes and Accessors
        {
            for (size_t animationIndex = 0; animationIndex < gltfLod.animations.Size(); animationIndex++)
            {
                const auto &baseAnimation = gltfLod.animations[animationIndex];
                Animation newAnimation(baseAnimation);
                const auto &lodAnimation = lod.animations[animationIndex];

                size_t samplerOffset = baseAnimation.samplers.Size();
                for (const auto &sampler : lodAnimation.samplers.Elements())
                {
                    AnimationSampler newSampler(sampler);
                    AddIndexOffset(newSampler.id, samplerOffset);
                    AddIndexOffset(newSampler.inputAccessorId, accessorOffset);
                    AddIndexOffset(newSampler.outputAccessorId, accessorOffset);
                    newAnimation.samplers.Append(std::move(newSampler));
                }
                
                size_t channelOffset = baseAnimation.channels.Size();
                for (auto channel : lodAnimation.channels.Elements())
                {
                    AddIndexOffset(channel.id, channelOffset);
                    AddIndexOffset(channel.target.nodeId, nodeOffset);
                    AddIndexOffset(channel.samplerId, samplerOffset);

                    newAnimation.channels.Append(std::move(channel));
                }
                gltfLod.animations.Replace(newAnimation);
            }
        }

        // update the primary GLTF root nodes lod extension to reference the new lod root node
        // N.B. new lods are always added to the back
        for (size_t sceneIdx = 0; sceneIdx < primaryScenes.size(); sceneIdx++)
        {
            for (size_t rootNodeIdx = 0; rootNodeIdx < primaryScenes[sceneIdx].nodes.size(); rootNodeIdx++)
            {
                auto idx = primaryScenes[sceneIdx].nodes[rootNodeIdx];
                Node nodeWithLods(gltfLod.nodes.Get(idx));
                int lodRootIdx = std::stoi(lodScenes[sceneIdx].nodes[rootNodeIdx]) + static_cast<int>(nodeOffset);
                auto primaryNodeLod = primaryLods.at(nodeWithLods.id);
                primaryNodeLod->emplace_back(std::to_string(lodRootIdx));
            }
        }

        return gltfLod;
    }
}

LODMap GLTFLODUtils::ParseDocumentNodeLODs(const Document& doc)
{
    LODMap lodMap;

    for (auto node : doc.nodes.Elements())
    {
        lodMap.emplace(node.id, std::move(std::make_shared<std::vector<std::string>>(ParseExtensionMSFTLod(node))));
    }

    return lodMap;
}

Document GLTFLODUtils::MergeDocumentsAsLODs(const std::vector<Document>& docs, const std::vector<std::wstring>& relativePaths, const bool& sharedMaterials)
{
    if (docs.empty())
    {
        throw std::invalid_argument("MergeDocumentsAsLODs passed empty vector");
    }

    Document gltfPrimary(docs[0]);
    LODMap lods = ParseDocumentNodeLODs(gltfPrimary);

    for (size_t i = 1; i < docs.size(); i++)
    {
        gltfPrimary = AddGLTFNodeLOD(gltfPrimary, lods, docs[i], (relativePaths.size() == docs.size() - 1 ? relativePaths[i - 1] : L""), sharedMaterials);
    }

    for (auto lod : lods)
    {
        if (lod.second == nullptr || lod.second->size() == 0)
        {
            continue;
        }

        auto node = gltfPrimary.nodes.Get(lod.first);

        auto lodExtensionValue = SerializeExtensionMSFTLod<Node>(node, *lod.second, gltfPrimary);
        if (!lodExtensionValue.empty())
        {
            node.extensions.emplace(EXTENSION_MSFT_LOD, lodExtensionValue);
            gltfPrimary.nodes.Replace(node);
        }
    }

    return gltfPrimary;
}

Document GLTFLODUtils::MergeDocumentsAsLODs(const std::vector<Document>& docs, const std::vector<double>& screenCoveragePercentages, const std::vector<std::wstring>& relativePaths, const bool& sharedMaterials)
{
    Document merged = MergeDocumentsAsLODs(docs, relativePaths, sharedMaterials);

    if (screenCoveragePercentages.size() == 0)
    {
        return merged;
    }

    for (auto scene : merged.scenes.Elements())
    {
        for (auto rootNodeIndex : scene.nodes)
        {
            auto primaryRootNode = merged.nodes.Get(rootNodeIndex);

            rapidjson::Document extrasJson(rapidjson::kObjectType);
            if (!primaryRootNode.extras.empty())
            {
                extrasJson.Parse(primaryRootNode.extras.c_str());
            }
            rapidjson::Document::AllocatorType& allocator = extrasJson.GetAllocator();

            rapidjson::Value screenCoverageArray = RapidJsonUtils::ToJsonArray(screenCoveragePercentages, allocator);

            extrasJson.AddMember("MSFT_screencoverage", screenCoverageArray, allocator);

            rapidjson::StringBuffer buffer;
            rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
            extrasJson.Accept(writer);

            primaryRootNode.extras = buffer.GetString();

            merged.nodes.Replace(primaryRootNode);
        }
    }

    return merged;
}

uint32_t GLTFLODUtils::NumberOfNodeLODLevels(const Document& doc, const LODMap& lods)
{
    size_t maxLODLevel = 0;
    for (auto node : doc.nodes.Elements())
    {
        maxLODLevel = std::max(maxLODLevel, lods.at(node.id)->size());
    }

    return static_cast<uint32_t>(maxLODLevel);
}



================================================
FILE: glTF-Toolkit/src/GLTFMeshCompressionUtils.cpp
================================================
// 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<std::ostream> GetOutputStream(const std::string& filename) const override
    {
        return std::make_shared<std::ofstream>(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<typename T>
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<unsigned int>(accessor.count));
    auto attrActual = dracoMesh.attribute(attId);

    std::vector<T> values = reader.ReadBinaryData<T>(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<uint32_t>(accessor.count); ++i)
    {
        attrActual->SetAttributeValue(attrActual->mapped_index(i), &values[i.value() * numComponents]);
    }
    if (dracoMesh.num_points() == 0) 
    {
        dracoMesh.set_num_points(static_cast<unsigned int>(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<IStreamReader> streamReader,
    const Document & doc,
    CompressionOptions options,
    const Mesh & mesh,
    BufferBuilder* builder,
    std::unordered_set<std::string>& 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<KHR::MeshPrimitives::DracoMeshCompression>())
        {
            resultMesh.primitives.emplace_back(primitive);
            continue;
        }
        auto dracoExtension = std::make_unique<KHR::MeshPrimitives::DracoMeshCompression>();
        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<int8_t>(dracoMesh, attribute.first, doc, reader, attributeAccessor); break;
            case COMPONENT_UNSIGNED_BYTE:  attId = InitializePointAttribute<uint8_t>(dracoMesh, attribute.first, doc, reader, attributeAccessor); break;
            case COMPONENT_SHORT:          attId = InitializePointAttribute<int16_t>(dracoMesh, attribute.first, doc, reader, attributeAccessor); break;
            case COMPONENT_UNSIGNED_SHORT: attId = InitializePointAttribute<uint16_t>(dracoMesh, attribute.first, doc, reader, attributeAccessor); break;
            case COMPONENT_UNSIGNED_INT:   attId = InitializePointAttribute<uint32_t>(dracoMesh, attribute.first, doc, reader, attributeAccessor); break;
            case COMPONENT_FLOAT:          attId = InitializePointAttribute<float>(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<IStreamReader> streamReader, const Document & doc, CompressionOptions options, const std::string& outputDirectory)
{
    Document resultDocument(doc);

    auto writerStream = std::make_shared<FilepathStreamWriter>(outputDirectory);
    auto writer = std::make_unique<GLTFResourceWriter>(writerStream);
    writer->SetUriPrefix(PathConcat(outputDirectory, "MeshCompression"));
    std::unique_ptr<BufferBuilder> builder = std::make_unique<BufferBuilder>(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<std::string> 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;
}


================================================
FILE: glTF-Toolkit/src/GLTFSpecularGlossinessUtils.cpp
================================================
// 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;

void ConvertEntrySpecularGlossinessToMetallicRoughness(
    const XMVECTORF32& diffuseColor,
    const XMVECTORF32& specGloss,
    XMVECTORF32& diffuseOut,
    float& metallicOut,
    float& roughnessOut)
{
    SpecularGlossinessValue sg;
    sg.diffuse = Color3(diffuseColor[0], diffuseColor[1], diffuseColor[2]);
    sg.opacity = diffuseColor[3];
    sg.specular = Color3(specGloss[0], specGloss[1], specGloss[2]);
    sg.glossiness = specGloss[3];

    MetallicRoughnessValue mr = SGToMR(sg);

    roughnessOut = mr.roughness;
    metallicOut = mr.metallic;
    diffuseOut = { mr.base.r, mr.base.g, mr.base.b, mr.opacity };
}


void ConvertTextureSpecularGlossinessToMetallicRoughness(
    ScratchImage& out_metallicRoughnessTexture,
    ScratchImage& out_modulatedDiffuseTexture, 
    const std::unique_ptr<ScratchImage>& diffuseTexture, 
    const XMVECTORF32& diffuseFactor,
    const std::unique_ptr<ScratchImage>& 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<IStreamReader> streamReader, const Document & doc, const Material & material, const std::string& outputDirectory)
{
    if (!material.HasExtension<KHR::Materials::PBRSpecularGlossiness>())
    {
        return doc;
    }

    Document resultDoc(doc);
    Material resultMaterial(material);
    resultMaterial.RemoveExtension<KHR::Materials::PBRSpecularGlossiness>();

    const auto& specularGlossiness = material.GetExtension<KHR::Materials::PBRSpecularGlossiness>();

    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<ScratchImage> diffuseTexture;
    if (!specularGlossiness.diffuseTexture.textureId.empty())
    {
        try
        {
            diffuseTexture = std::make_unique<ScratchImage>(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<ScratchImage> specularGlossinessTexture;
    if (!specularGlossiness.specularGlossinessTexture.textureId.empty())
    {
        try
        {
            specularGlossinessTexture = std::make_unique<ScratchImage>(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<IStreamReader> 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;
}


================================================
FILE: glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp
================================================
// 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 "GLTFTexturePackingUtils.h"
#include "GLTFTextureCompressionUtils.h"
#include "DeviceResources.h"

// Usings for ComPtr
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;

// Usings for glTF
using namespace Microsoft::glTF;
using namespace Microsoft::glTF::Toolkit;

#include <DirectXTex.h>

const char* Microsoft::glTF::Toolkit::EXTENSION_MSFT_TEXTURE_DDS = "MSFT_texture_dds";

Document GLTFTextureCompressionUtils::CompressTextureAsDDS(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const Texture & texture, TextureCompression compression, const std::string& outputDirectory, size_t maxTextureSize, bool generateMipMaps, bool retainOriginalImage, bool treatAsLinear)
{
    Document outputDoc(doc);

    // Early return cases:
    // - No compression requested
    // - This texture doesn't have an image associated
    // - The texture already has a DDS extension
    if (compression == TextureCompression::None ||
        texture.imageId.empty() ||
        texture.extensions.find(EXTENSION_MSFT_TEXTURE_DDS) != texture.extensions.end())
    {
        // Return copy of document
        return outputDoc;
    }

    auto image = std::make_unique<DirectX::ScratchImage>(GLTFTextureUtils::LoadTexture(streamReader, doc, texture.id, treatAsLinear));

    // Resize up to a multiple of 4
    auto metadata = image->GetMetadata();
    auto resizedWidth = metadata.width;
    auto resizedHeight = metadata.height;

    if (maxTextureSize < resizedWidth || maxTextureSize < resizedHeight)
    {
        // Scale
        auto scaleFactor = static_cast<double>(maxTextureSize) / std::max(metadata.width, metadata.height);
        resizedWidth = static_cast<size_t>(std::llround(metadata.width * scaleFactor));
        resizedHeight = static_cast<size_t>(std::llround(metadata.height * scaleFactor));
    }

    if (resizedWidth % 4 != 0 || resizedHeight % 4 != 0)
    {
        static const std::function<size_t(size_t)> roundUpToMultipleOf4 = [](size_t input)
        {
            return input % 4 == 0 ? input : (input + 4) - (input % 4);
        };

        resizedWidth = roundUpToMultipleOf4(resizedWidth);
        resizedHeight = roundUpToMultipleOf4(resizedHeight);
    }

    if (resizedWidth != metadata.width || resizedHeight != metadata.height)
    {
        auto resized = std::make_unique<DirectX::ScratchImage>();
        if (FAILED(DirectX::Resize(image->GetImages(), image->GetImageCount(), image->GetMetadata(), resizedWidth, resizedHeight, DirectX::TEX_FILTER_SEPARATE_ALPHA, *resized)))
        {
            throw GLTFException("Failed to resize image.");
        }

        image = std::move(resized);
    }

    if (generateMipMaps)
    {
        auto mipChain = std::make_unique<DirectX::ScratchImage>();
        if (FAILED(DirectX::GenerateMipMaps(image->GetImages(), image->GetImageCount(), image->GetMetadata(), DirectX::TEX_FILTER_SEPARATE_ALPHA, 0, *mipChain)))
        {
            throw GLTFException("Failed to generate mip maps.");
        }

        image = std::move(mipChain);
    }

    CompressImage(*image, compression);

    // Save image to file
    std::string outputImagePath = "texture_" + texture.id;

    if (!generateMipMaps)
    {
        // The default is to have mips, so note on the texture when it doesn't
        outputImagePath += "_nomips";
    }

    switch (compression)
    {
    case TextureCompression::BC3:
        outputImagePath += "_BC3";
        break;
    case TextureCompression::BC5:
        outputImagePath += "_BC5";
        break;
    case TextureCompression::BC7:
    case TextureCompression::BC7_SRGB:
        outputImagePath += "_BC7";
        break;
    default:
        throw GLTFException("Invalid compression.");
        break;
    }

    outputImagePath += ".dds";
    std::wstring outputImagePathW(outputImagePath.begin(), outputImagePath.end());

    wchar_t outputImageFullPath[MAX_PATH];

    std::wstring outputDirectoryW(outputDirectory.begin(), outputDirectory.end());

    if (FAILED(::PathCchCombine(outputImageFullPath, ARRAYSIZE(outputImageFullPath), outputDirectoryW.c_str(), outputImagePathW.c_str())))
    {
        throw GLTFException("Failed to compose output file path.");
    }

    if (FAILED(SaveToDDSFile(image->GetImages(), image->GetImageCount(), image->GetMetadata(), DirectX::DDS_FLAGS::DDS_FLAGS_NONE, outputImageFullPath)))
    {
        throw GLTFException("Failed to save image as DDS.");
    }

    std::wstring outputImageFullPathW(outputImageFullPath);
    std::string outputImageFullPathA(outputImageFullPathW.begin(), outputImageFullPathW.end());

    // Add back to GLTF
    std::string ddsImageId(texture.imageId);

    Image ddsImage(doc.images.Get(texture.imageId));
    ddsImage.mimeType = "image/vnd-ms.dds";
    ddsImage.uri = outputImageFullPathA;

    if (retainOriginalImage)
    {
        ddsImage.id.clear();
        ddsImageId = outputDoc.images.Append(ddsImage, AppendIdPolicy::GenerateOnEmpty).id;
    }
    else
    {
        outputDoc.images.Replace(ddsImage);
    }

    Texture ddsTexture(texture);

    // Create the JSON for the DDS extension element
    rapidjson::Document ddsExtensionJson;
    ddsExtensionJson.SetObject();

    ddsExtensionJson.AddMember("source", rapidjson::Value(outputDoc.images.GetIndex(ddsImageId)), ddsExtensionJson.GetAllocator());

    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
    ddsExtensionJson.Accept(writer);

    ddsTexture.extensions.insert(std::pair<std::string, std::string>(EXTENSION_MSFT_TEXTURE_DDS, buffer.GetString()));

    outputDoc.textures.Replace(ddsTexture);

    outputDoc.extensionsUsed.insert(EXTENSION_MSFT_TEXTURE_DDS);

    if (!retainOriginalImage)
    {
        outputDoc.extensionsRequired.insert(EXTENSION_MSFT_TEXTURE_DDS);
    }

    return outputDoc;
}

Document GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(std::shared_ptr<IStreamReader> streamReader, const Document & doc, const std::string& outputDirectory, size_t maxTextureSize, bool retainOriginalImages)
{
    Document outputDoc(doc);

    for (auto material : doc.materials.Elements())
    {
        auto compressIfNotEmpty = [&outputDoc, &streamReader, &outputDirectory, maxTextureSize, retainOriginalImages](const std::string& textureId, TextureCompression compression, bool treatAsLinear = true)
        {
            if (!textureId.empty())
            {
                outputDoc = CompressTextureAsDDS(streamReader, outputDoc, outputDoc.textures.Get(textureId), compression, outputDirectory, maxTextureSize, true, retainOriginalImages, treatAsLinear);
            }
        };

        // Compress base and emissive texture as BC7
        compressIfNotEmpty(material.metallicRoughness.baseColorTexture.textureId, TextureCompression::BC7_SRGB, false);
        compressIfNotEmpty(material.emissiveTexture.textureId, TextureCompression::BC7_SRGB, false);

        // Get textures from the MSFT_packing_occlusionRoughnessMetallic extension
        if (material.extensions.find(EXTENSION_MSFT_PACKING_ORM) != material.extensions.end())
        {
            rapidjson::Document packingOrmContents;
            packingOrmContents.Parse(material.extensions[EXTENSION_MSFT_PACKING_ORM].c_str());

            // Compress packed textures as BC7
            if (packingOrmContents.HasMember(MSFT_PACKING_ORM_RMOT
Download .txt
gitextract_t5s8ddnm/

├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── WindowsMRAssetConverter/
│   ├── AssetType.cpp
│   ├── AssetType.h
│   ├── CommandLine.cpp
│   ├── CommandLine.h
│   ├── FileSystem.cpp
│   ├── FileSystem.h
│   ├── README.md
│   ├── WindowsMRAssetConverter.cpp
│   ├── WindowsMRAssetConverter.vcxproj
│   ├── WindowsMRAssetConverter.vcxproj.filters
│   ├── packages.config
│   ├── stdafx.cpp
│   ├── stdafx.h
│   └── targetver.h
├── glTF-Toolkit/
│   ├── glTF-Toolkit.vcxproj
│   ├── glTF-Toolkit.vcxproj.filters
│   ├── inc/
│   │   ├── AccessorUtils.h
│   │   ├── DeviceResources.h
│   │   ├── GLBtoGLTF.h
│   │   ├── GLTFLODUtils.h
│   │   ├── GLTFMeshCompressionUtils.h
│   │   ├── GLTFSDK.h
│   │   ├── GLTFSpecularGlossinessUtils.h
│   │   ├── GLTFTextureCompressionUtils.h
│   │   ├── GLTFTexturePackingUtils.h
│   │   ├── GLTFTextureUtils.h
│   │   ├── SerializeBinary.h
│   │   └── pch.h
│   ├── packages.config
│   └── src/
│       ├── DeviceResources.cpp
│       ├── GLBtoGLTF.cpp
│       ├── GLTFLODUtils.cpp
│       ├── GLTFMeshCompressionUtils.cpp
│       ├── GLTFSpecularGlossinessUtils.cpp
│       ├── GLTFTextureCompressionUtils.cpp
│       ├── GLTFTexturePackingUtils.cpp
│       ├── GLTFTextureUtils.cpp
│       ├── SerializeBinary.cpp
│       └── pch.cpp
├── glTF-Toolkit.Test/
│   ├── GLBSerializerTests.cpp
│   ├── GLBtoGLTFTests.cpp
│   ├── GLTFLODUtilsTests.cpp
│   ├── GLTFTextureCompressionUtilsTests.cpp
│   ├── GLTFTexturePackingUtilsTests.cpp
│   ├── Helpers/
│   │   ├── StreamMock.h
│   │   ├── TestUtils.h
│   │   └── WStringUtils.h
│   ├── Resources/
│   │   └── gltf/
│   │       ├── CubeAsset3D.gltf
│   │       ├── CubeWithLOD.gltf
│   │       ├── TextureTest/
│   │       │   └── TextureTest.gltf
│   │       ├── WaterBottle/
│   │       │   └── WaterBottle.gltf
│   │       └── WaterBottle_ORM/
│   │           ├── WaterBottle.gltf
│   │           ├── WaterBottle_WindowsMR.gltf
│   │           ├── WaterBottle_baseColor.DDS
│   │           ├── WaterBottle_emissive.DDS
│   │           ├── WaterBottle_normal.dds
│   │           └── WaterBottle_occlusionRoughnessMetallic.dds
│   ├── glTF-Toolkit.Test.vcxproj
│   ├── glTF-Toolkit.Test.vcxproj.filters
│   └── packages.config
├── glTF-Toolkit.UWP/
│   ├── GLTFSerialization.cpp
│   ├── GLTFSerialization.h
│   ├── GLTFStreams.h
│   ├── WindowsMRConversion.cpp
│   ├── WindowsMRConversion.h
│   ├── glTF-Toolkit.UWP.vcxproj
│   ├── glTF-Toolkit.UWP.vcxproj.filters
│   ├── packages.config
│   ├── pch.cpp
│   └── pch.h
├── glTF-Toolkit.UWP.Test/
│   ├── Assets/
│   │   └── 3DModels/
│   │       └── WaterBottle.glb
│   ├── Package.appxmanifest
│   ├── Properties/
│   │   ├── AssemblyInfo.cs
│   │   └── UnitTestApp.rd.xml
│   ├── UWPTest.cs
│   ├── UnitTestApp.xaml
│   ├── UnitTestApp.xaml.cs
│   └── glTF-Toolkit.UWP.Test.csproj
└── glTF-Toolkit.sln
Download .txt
SYMBOL INDEX (105 symbols across 38 files)

FILE: WindowsMRAssetConverter/AssetType.cpp
  function AssetType (line 10) | AssetType AssetTypeUtils::AssetTypeFromFilePath(const std::wstring& asse...

FILE: WindowsMRAssetConverter/AssetType.h
  function AssetType (line 9) | enum class AssetType

FILE: WindowsMRAssetConverter/CommandLine.cpp
  type CommandLineParsingState (line 38) | enum class CommandLineParsingState

FILE: WindowsMRAssetConverter/CommandLine.h
  function namespace (line 9) | namespace CommandLine

FILE: WindowsMRAssetConverter/FileSystem.h
  function namespace (line 7) | namespace FileSystem

FILE: WindowsMRAssetConverter/WindowsMRAssetConverter.cpp
  class GLTFStreamReader (line 26) | class GLTFStreamReader : public IStreamReader
    method GLTFStreamReader (line 29) | GLTFStreamReader(std::wstring uriBase) : m_uriBase(uriBase) {}
    method GetInputStream (line 32) | virtual std::shared_ptr<std::istream> GetInputStream(const std::string...
  class GLBStreamWriter (line 49) | class GLBStreamWriter : public Microsoft::glTF::IStreamWriter
    method GLBStreamWriter (line 52) | GLBStreamWriter(const std::wstring& filename) :
    method GetOutputStream (line 56) | std::shared_ptr<std::ostream> GetOutputStream(const std::string&) cons...
  function Document (line 65) | Document ProcessTextures(
  function Document (line 100) | Document LoadAndConvertDocumentForWindowsMR(
  function wmain (line 145) | int wmain(int argc, wchar_t *argv[])

FILE: glTF-Toolkit.Test/GLBSerializerTests.cpp
  type Microsoft::glTF::Toolkit::Test (line 24) | namespace Microsoft::glTF::Toolkit::Test
    class InMemoryStream (line 26) | class InMemoryStream : public Microsoft::glTF::IStreamWriter
      method InMemoryStream (line 29) | InMemoryStream(std::shared_ptr<std::stringstream> stream) :
      method GetOutputStream (line 33) | std::shared_ptr<std::ostream> GetOutputStream(const std::string&) const
    function TEST_CLASS (line 42) | TEST_CLASS(GLBSerializerTests)

FILE: glTF-Toolkit.Test/GLBtoGLTFTests.cpp
  type Microsoft::glTF::Toolkit::Test (line 17) | namespace Microsoft::glTF::Toolkit::Test
    function utf8Decode (line 19) | std::wstring utf8Decode(const std::string& s)
    function binBufferString (line 29) | std::string binBufferString(const std::vector<char>& vec)
    function Document (line 40) | Document setupGLBDocument1()
    function TEST_CLASS (line 87) | TEST_CLASS(GLBToGLTFTests)

FILE: glTF-Toolkit.Test/GLTFLODUtilsTests.cpp
  type Microsoft::glTF::Toolkit::Test (line 25) | namespace Microsoft::glTF::Toolkit::Test
    function TEST_CLASS (line 27) | TEST_CLASS(GLTFLODUtilsTests)

FILE: glTF-Toolkit.Test/GLTFTextureCompressionUtilsTests.cpp
  type Microsoft::glTF::Toolkit::Test (line 26) | namespace Microsoft::glTF::Toolkit::Test
    function TEST_CLASS (line 30) | TEST_CLASS(GLTFTextureCompressionUtilsTests)

FILE: glTF-Toolkit.Test/GLTFTexturePackingUtilsTests.cpp
  type Microsoft::glTF::Toolkit::Test (line 22) | namespace Microsoft::glTF::Toolkit::Test
    function TEST_CLASS (line 24) | TEST_CLASS(GLTFTexturePackingUtilsTests)

FILE: glTF-Toolkit.Test/Helpers/StreamMock.h
  function namespace (line 13) | namespace Microsoft::glTF::Toolkit::Test

FILE: glTF-Toolkit.Test/Helpers/TestUtils.h
  function namespace (line 25) | namespace Microsoft::glTF::Toolkit::Test
  function class (line 141) | class TestStreamReader : public IStreamReader

FILE: glTF-Toolkit.Test/Helpers/WStringUtils.h
  function namespace (line 11) | namespace Microsoft::glTF::Toolkit::Test

FILE: glTF-Toolkit.UWP.Test/UWPTest.cs
  class UWPTest (line 13) | [TestClass]
    method CopyFileToTempFolderAsync (line 16) | private async Task<StorageFile> CopyFileToTempFolderAsync(Uri uri)
    method CreateTemporaryOutputFolderAsync (line 23) | private IAsyncOperation<StorageFolder> CreateTemporaryOutputFolderAsyn...
    method CompareFilesAsync (line 28) | private async Task<bool> CompareFilesAsync(StorageFile file1, StorageF...
    method GLBDeserializeSerialize (line 36) | [TestMethod]
    method GLBConvertToWindowsMR (line 67) | [TestMethod]

FILE: glTF-Toolkit.UWP.Test/UnitTestApp.xaml.cs
  class App (line 23) | sealed partial class App : Application
    method App (line 29) | public App()
    method OnLaunched (line 40) | protected override void OnLaunched(LaunchActivatedEventArgs e)
    method OnNavigationFailed (line 83) | void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
    method OnSuspending (line 95) | private void OnSuspending(object sender, SuspendingEventArgs e)

FILE: glTF-Toolkit.UWP/GLTFSerialization.h
  function namespace (line 6) | namespace Microsoft::glTF::Toolkit::UWP

FILE: glTF-Toolkit.UWP/GLTFStreams.h
  function namespace (line 10) | namespace Microsoft::glTF::Toolkit::UWP

FILE: glTF-Toolkit.UWP/WindowsMRConversion.h
  function namespace (line 8) | namespace Microsoft::glTF::Toolkit::UWP

FILE: glTF-Toolkit/inc/AccessorUtils.h
  function namespace (line 10) | namespace Microsoft::glTF::Toolkit

FILE: glTF-Toolkit/inc/DeviceResources.h
  function namespace (line 6) | namespace DX

FILE: glTF-Toolkit/inc/GLBtoGLTF.h
  function namespace (line 8) | namespace Microsoft::glTF::Toolkit

FILE: glTF-Toolkit/inc/GLTFLODUtils.h
  function namespace (line 8) | namespace Microsoft::glTF::Toolkit

FILE: glTF-Toolkit/inc/GLTFMeshCompressionUtils.h
  function class (line 25) | class GLTFMeshCompressionUtils

FILE: glTF-Toolkit/inc/GLTFSpecularGlossinessUtils.h
  function namespace (line 8) | namespace Microsoft::glTF::Toolkit

FILE: glTF-Toolkit/inc/GLTFTextureCompressionUtils.h
  function namespace (line 8) | namespace DirectX
  function namespace (line 13) | namespace Microsoft::glTF::Toolkit

FILE: glTF-Toolkit/inc/GLTFTexturePackingUtils.h
  function namespace (line 8) | namespace Microsoft::glTF::Toolkit

FILE: glTF-Toolkit/inc/GLTFTextureUtils.h
  function namespace (line 9) | namespace Microsoft::glTF::Toolkit

FILE: glTF-Toolkit/inc/SerializeBinary.h
  function namespace (line 12) | namespace Microsoft::glTF::Toolkit

FILE: glTF-Toolkit/src/DeviceResources.cpp
  function SdkLayersAvailable (line 17) | inline bool SdkLayersAvailable()

FILE: glTF-Toolkit/src/GLBtoGLTF.cpp
  function GuessFileExtension (line 20) | std::string GuessFileExtension(const std::string& mimeType)
  class StreamMock (line 32) | class StreamMock : public IStreamReader
    method StreamMock (line 35) | StreamMock() : m_stream(std::make_shared<std::stringstream>(std::ios_b...
    method GetInputStream (line 39) | std::shared_ptr<std::istream> GetInputStream(const std::string&) const...
  function GetGLBBufferChunkOffset (line 48) | size_t GetGLBBufferChunkOffset(std::ifstream* input)
  function GetExtensionsData (line 181) | std::unordered_map<std::string, std::vector<char>> GetExtensionsData(std...
  function Document (line 246) | Document GLBToGLTF::CreateGLTFDocument(const Document& glbDoc, const std...

FILE: glTF-Toolkit/src/GLTFLODUtils.cpp
  function AddIndexOffset (line 30) | inline void AddIndexOffset(std::string& id, size_t offset)
  function AddIndexOffset (line 36) | inline void AddIndexOffset(MeshPrimitive& primitive, const char* attribu...
  function AddIndexOffsetPacked (line 46) | inline void AddIndexOffsetPacked(rapidjson::Value& json, const char* tex...
  function ParseExtensionMSFTLod (line 58) | std::vector<std::string> ParseExtensionMSFTLod(const Node& node)
  function SerializeExtensionMSFTLod (line 81) | std::string SerializeExtensionMSFTLod(const T&, const std::vector<std::s...
  function Document (line 123) | Document AddGLTFNodeLOD(const Document& primary, LODMap& primaryLods, co...
  function LODMap (line 504) | LODMap GLTFLODUtils::ParseDocumentNodeLODs(const Document& doc)
  function Document (line 516) | Document GLTFLODUtils::MergeDocumentsAsLODs(const std::vector<Document>&...
  function Document (line 551) | Document GLTFLODUtils::MergeDocumentsAsLODs(const std::vector<Document>&...

FILE: glTF-Toolkit/src/GLTFMeshCompressionUtils.cpp
  function PathConcat (line 24) | std::wstring PathConcat(const std::wstring& part1, const std::wstring& p...
  function PathConcat (line 37) | std::string PathConcat(const std::string& part1, const std::string& part2)
  class FilepathStreamWriter (line 46) | class FilepathStreamWriter : public IStreamWriter
    method FilepathStreamWriter (line 49) | FilepathStreamWriter(std::string uriBase) : m_uriBase(uriBase) {}
    method GetOutputStream (line 52) | virtual std::shared_ptr<std::ostream> GetOutputStream(const std::strin...
  function GetTypeFromAttributeName (line 60) | draco::GeometryAttribute::Type GetTypeFromAttributeName(const std::strin...
  function GetDataType (line 97) | draco::DataType GetDataType(const Accessor& accessor)
  function InitializePointAttribute (line 112) | int InitializePointAttribute(draco::Mesh& dracoMesh, const std::string& ...
  function SetEncoderOptions (line 146) | void SetEncoderOptions(draco::Encoder& encoder, const CompressionOptions...
  function Document (line 157) | Document GLTFMeshCompressionUtils::CompressMesh(
  function Document (line 261) | Document GLTFMeshCompressionUtils::CompressMeshes(std::shared_ptr<IStrea...

FILE: glTF-Toolkit/src/GLTFSpecularGlossinessUtils.cpp
  function ConvertEntrySpecularGlossinessToMetallicRoughness (line 17) | void ConvertEntrySpecularGlossinessToMetallicRoughness(
  function ConvertTextureSpecularGlossinessToMetallicRoughness (line 38) | void ConvertTextureSpecularGlossinessToMetallicRoughness(
  function Document (line 104) | Document GLTFSpecularGlossinessUtils::ConvertMaterial(std::shared_ptr<IS...
  function Document (line 232) | Document GLTFSpecularGlossinessUtils::ConvertMaterials(std::shared_ptr<I...

FILE: glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp
  function Document (line 23) | Document GLTFTextureCompressionUtils::CompressTextureAsDDS(std::shared_p...
  function Document (line 178) | Document GLTFTextureCompressionUtils::CompressAllTexturesForWindowsMR(st...

FILE: glTF-Toolkit/src/GLTFTexturePackingUtils.cpp
  function AddTextureToExtension (line 24) | void AddTextureToExtension(const std::string& imageId, TexturePacking pa...
  function Renormalize (line 51) | void Renormalize(std::unique_ptr<DirectX::ScratchImage> &normalImage, Di...
  function AdjustRoughness (line 87) | void AdjustRoughness(std::unique_ptr<DirectX::ScratchImage> &roughnessIm...
  function Document (line 172) | Document GLTFTexturePackingUtils::PackMaterialForWindowsMR(std::shared_p...
  function Document (line 445) | Document GLTFTexturePackingUtils::PackAllMaterialsForWindowsMR(std::shar...

FILE: glTF-Toolkit/src/GLTFTextureUtils.cpp
  function Document (line 105) | Document GLTFTextureUtils::RemoveRedundantTexturesAndImages(const Docume...

FILE: glTF-Toolkit/src/SerializeBinary.cpp
  function MimeTypeFromUri (line 22) | static std::string MimeTypeFromUri(const std::string& uri)
  function SaveAccessor (line 46) | void SaveAccessor(const Accessor& accessor, const std::vector<T> accesso...
  function vector_static_cast (line 61) | static std::vector<NewType> vector_static_cast(const std::vector<Origina...
  function ConvertAndSaveAccessor (line 75) | void ConvertAndSaveAccessor(const Accessor& accessor, const std::vector<...
  function SerializeAccessor (line 103) | void SerializeAccessor(const Accessor& accessor, const Document& doc, co...
  function SerializeAccessor (line 125) | void SerializeAccessor(const Accessor& accessor, const Document& doc, co...
Condensed preview — 84 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (436K chars).
[
  {
    "path": ".gitignore",
    "chars": 4832,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 333,
    "preview": "This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). F"
  },
  {
    "path": "LICENSE",
    "chars": 1162,
    "preview": "    MIT License\n\n    Copyright (c) Microsoft Corporation. All rights reserved.\n\n    Permission is hereby granted, free o"
  },
  {
    "path": "README.md",
    "chars": 4091,
    "preview": "# Microsoft glTF Toolkit\n\nThis project contains a collection of tools and libraries to modify and optimize glTF assets.\n"
  },
  {
    "path": "SECURITY.md",
    "chars": 2757,
    "preview": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.8 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products an"
  },
  {
    "path": "WindowsMRAssetConverter/AssetType.cpp",
    "chars": 938,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "WindowsMRAssetConverter/AssetType.h",
    "chars": 406,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "WindowsMRAssetConverter/CommandLine.cpp",
    "chars": 12563,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "WindowsMRAssetConverter/CommandLine.h",
    "chars": 1052,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "WindowsMRAssetConverter/FileSystem.cpp",
    "chars": 3693,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "WindowsMRAssetConverter/FileSystem.h",
    "chars": 573,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "WindowsMRAssetConverter/README.md",
    "chars": 7259,
    "preview": "# Windows Mixed Reality Asset Converter\r\n\r\nA command line tool to convert core GLTF 2.0 for use in the Windows Mixed Rea"
  },
  {
    "path": "WindowsMRAssetConverter/WindowsMRAssetConverter.cpp",
    "chars": 10856,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj",
    "chars": 13895,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.micros"
  },
  {
    "path": "WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj.filters",
    "chars": 1963,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
  },
  {
    "path": "WindowsMRAssetConverter/packages.config",
    "chars": 395,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<packages>\r\n  <package id=\"directxtex_desktop_2015\" version=\"2018.8.5.1\" target"
  },
  {
    "path": "WindowsMRAssetConverter/stdafx.cpp",
    "chars": 176,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "WindowsMRAssetConverter/stdafx.h",
    "chars": 911,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "WindowsMRAssetConverter/targetver.h",
    "chars": 471,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/glTF-Toolkit.vcxproj",
    "chars": 10693,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.micros"
  },
  {
    "path": "glTF-Toolkit/glTF-Toolkit.vcxproj.filters",
    "chars": 2699,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
  },
  {
    "path": "glTF-Toolkit/inc/AccessorUtils.h",
    "chars": 2229,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See LICENSE in the proje"
  },
  {
    "path": "glTF-Toolkit/inc/DeviceResources.h",
    "chars": 2225,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/inc/GLBtoGLTF.h",
    "chars": 3644,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/inc/GLTFLODUtils.h",
    "chars": 3668,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/inc/GLTFMeshCompressionUtils.h",
    "chars": 3034,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See LICENSE in the proje"
  },
  {
    "path": "glTF-Toolkit/inc/GLTFSDK.h",
    "chars": 617,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See LICENSE in the proje"
  },
  {
    "path": "glTF-Toolkit/inc/GLTFSpecularGlossinessUtils.h",
    "chars": 2084,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See LICENSE in the proje"
  },
  {
    "path": "glTF-Toolkit/inc/GLTFTextureCompressionUtils.h",
    "chars": 5589,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/inc/GLTFTexturePackingUtils.h",
    "chars": 3447,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/inc/GLTFTextureUtils.h",
    "chars": 2265,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See LICENSE in the proje"
  },
  {
    "path": "glTF-Toolkit/inc/SerializeBinary.h",
    "chars": 2094,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/inc/pch.h",
    "chars": 1151,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/packages.config",
    "chars": 395,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<packages>\r\n  <package id=\"directxtex_desktop_2015\" version=\"2018.8.5.1\" target"
  },
  {
    "path": "glTF-Toolkit/src/DeviceResources.cpp",
    "chars": 6698,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/src/GLBtoGLTF.cpp",
    "chars": 17142,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/src/GLTFLODUtils.cpp",
    "chars": 26516,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/src/GLTFMeshCompressionUtils.cpp",
    "chars": 12110,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See LICENSE in the proje"
  },
  {
    "path": "glTF-Toolkit/src/GLTFSpecularGlossinessUtils.cpp",
    "chars": 9932,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See LICENSE in the proje"
  },
  {
    "path": "glTF-Toolkit/src/GLTFTextureCompressionUtils.cpp",
    "chars": 11513,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/src/GLTFTexturePackingUtils.cpp",
    "chars": 20525,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/src/GLTFTextureUtils.cpp",
    "chars": 7552,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See LICENSE in the proje"
  },
  {
    "path": "glTF-Toolkit/src/SerializeBinary.cpp",
    "chars": 13441,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit/src/pch.cpp",
    "chars": 173,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.Test/GLBSerializerTests.cpp",
    "chars": 5663,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.Test/GLBtoGLTFTests.cpp",
    "chars": 11133,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.Test/GLTFLODUtilsTests.cpp",
    "chars": 14794,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.Test/GLTFTextureCompressionUtilsTests.cpp",
    "chars": 14183,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.Test/GLTFTexturePackingUtilsTests.cpp",
    "chars": 9794,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.Test/Helpers/StreamMock.h",
    "chars": 1014,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.Test/Helpers/TestUtils.h",
    "chars": 5757,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.Test/Helpers/WStringUtils.h",
    "chars": 820,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License. See LICENSE in the proje"
  },
  {
    "path": "glTF-Toolkit.Test/Resources/gltf/CubeAsset3D.gltf",
    "chars": 2778,
    "preview": "{\n\t\"asset\": {\n\t\t\"version\": \"2.0\",\n\t\t\"generator\": \"Microsoft GLTF Exporter 2.1.2-b21\"\n\t},\n\t\"accessors\": [{\n\t\t\t\"bufferView"
  },
  {
    "path": "glTF-Toolkit.Test/Resources/gltf/CubeWithLOD.gltf",
    "chars": 1822,
    "preview": "{\n\t\"asset\": {\n\t\t\"version\": \"2.0\",\n\t\t\"generator\": \"Microsoft GLTF Exporter 2.1.2-b21\"\n\t},\n\t\"accessors\": [{\n\t\t\t\"bufferView"
  },
  {
    "path": "glTF-Toolkit.Test/Resources/gltf/TextureTest/TextureTest.gltf",
    "chars": 181,
    "preview": "{\r\n  \"asset\" : {\r\n    \"version\" : \"2.0\"\r\n  },\r\n  \"textures\": [\r\n    {\r\n      \"sampler\": 0,\r\n      \"source\": 0\r\n    }\r\n  "
  },
  {
    "path": "glTF-Toolkit.Test/Resources/gltf/WaterBottle/WaterBottle.gltf",
    "chars": 3201,
    "preview": "{\n  \"accessors\": [\n    {\n      \"bufferView\": 0,\n      \"componentType\": 5126,\n      \"count\": 2549,\n      \"type\": \"VEC2\"\n "
  },
  {
    "path": "glTF-Toolkit.Test/Resources/gltf/WaterBottle_ORM/WaterBottle.gltf",
    "chars": 3563,
    "preview": "{\n  \"accessors\": [\n    {\n      \"bufferView\": 0,\n      \"componentType\": 5126,\n      \"count\": 2549,\n      \"type\": \"VEC2\"\n "
  },
  {
    "path": "glTF-Toolkit.Test/Resources/gltf/WaterBottle_ORM/WaterBottle_WindowsMR.gltf",
    "chars": 4373,
    "preview": "{\n  \"accessors\": [\n    {\n      \"bufferView\": 0,\n      \"componentType\": 5126,\n      \"count\": 2549,\n      \"type\": \"VEC2\"\n "
  },
  {
    "path": "glTF-Toolkit.Test/glTF-Toolkit.Test.vcxproj",
    "chars": 16117,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.micros"
  },
  {
    "path": "glTF-Toolkit.Test/glTF-Toolkit.Test.vcxproj.filters",
    "chars": 5497,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
  },
  {
    "path": "glTF-Toolkit.Test/packages.config",
    "chars": 395,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<packages>\r\n  <package id=\"directxtex_desktop_2015\" version=\"2018.8.5.1\" target"
  },
  {
    "path": "glTF-Toolkit.UWP/GLTFSerialization.cpp",
    "chars": 2782,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the pro"
  },
  {
    "path": "glTF-Toolkit.UWP/GLTFSerialization.h",
    "chars": 1661,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the pro"
  },
  {
    "path": "glTF-Toolkit.UWP/GLTFStreams.h",
    "chars": 1719,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.UWP/WindowsMRConversion.cpp",
    "chars": 6976,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.UWP/WindowsMRConversion.h",
    "chars": 1722,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the proj"
  },
  {
    "path": "glTF-Toolkit.UWP/glTF-Toolkit.UWP.vcxproj",
    "chars": 19830,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.micros"
  },
  {
    "path": "glTF-Toolkit.UWP/glTF-Toolkit.UWP.vcxproj.filters",
    "chars": 878,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbu"
  },
  {
    "path": "glTF-Toolkit.UWP/packages.config",
    "chars": 386,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<packages>\r\n  <package id=\"directxtex_uwp\" version=\"2018.8.5.1\" targetFramework"
  },
  {
    "path": "glTF-Toolkit.UWP/pch.cpp",
    "chars": 176,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the pro"
  },
  {
    "path": "glTF-Toolkit.UWP/pch.h",
    "chars": 594,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the pro"
  },
  {
    "path": "glTF-Toolkit.UWP.Test/Package.appxmanifest",
    "chars": 1660,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Package\n  xmlns=\"http://schemas.microsoft.com/appx/manifest/foundation/windows1"
  },
  {
    "path": "glTF-Toolkit.UWP.Test/Properties/AssemblyInfo.cs",
    "chars": 635,
    "preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n[assembly: Assemb"
  },
  {
    "path": "glTF-Toolkit.UWP.Test/Properties/UnitTestApp.rd.xml",
    "chars": 1231,
    "preview": "<!--\n    This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most\n    develope"
  },
  {
    "path": "glTF-Toolkit.UWP.Test/UWPTest.cs",
    "chars": 3986,
    "preview": "// Copyright (c) Microsoft Corporation. All rights reserved.\r\n// Licensed under the MIT License. See LICENSE in the pro"
  },
  {
    "path": "glTF-Toolkit.UWP.Test/UnitTestApp.xaml",
    "chars": 442,
    "preview": "<!-- \r\n    Copyright (c) Microsoft Corporation. All rights reserved.\r\n    Licensed under the MIT License. See LICENSE i"
  },
  {
    "path": "glTF-Toolkit.UWP.Test/UnitTestApp.xaml.cs",
    "chars": 3847,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.InteropService"
  },
  {
    "path": "glTF-Toolkit.UWP.Test/glTF-Toolkit.UWP.Test.csproj",
    "chars": 7924,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"15.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.micro"
  },
  {
    "path": "glTF-Toolkit.sln",
    "chars": 10718,
    "preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 15\r\nVisualStudioVersion = 15.0.27004.201"
  }
]

// ... and 5 more files (download for full content)

About this extraction

This page contains the full source code of the Microsoft/glTF-Toolkit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 84 files (17.0 MB), approximately 101.0k tokens, and a symbol index with 105 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!