Skip to content

Commit

Permalink
Handle merges where GLTF resources are in different directories (#7)
Browse files Browse the repository at this point in the history
* Handle resources with the same name (prepend relative path).

* Fix linking on x86.

* Replace PathIsRelative with std::filesystem::path::is_relative.

This removes the shlwapi.lib dependency.

* Use default argument for relativePaths.
Add some documentation.
Revert the changes to the tests since passing relativePaths is no longer
required.

* Updated to follow changes in GLTFLODUtils::MergeDocumentsAsLODs.

* Implement GetRelativePathWithTrailingSeparator using std::filesystem.

* Remove shlwapi.lib from WindowsMRAssetConverter project.

* Remove redundant includes.

* Move the <filesystem> include into "filesystem.h".
  • Loading branch information
erikdahlstrom authored and robertos committed Feb 6, 2018
1 parent dc6d828 commit 9cc0960
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 19 deletions.
42 changes: 42 additions & 0 deletions WindowsMRAssetConverter/FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,48 @@
#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);
Expand Down
2 changes: 2 additions & 0 deletions WindowsMRAssetConverter/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// 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);
Expand Down
16 changes: 7 additions & 9 deletions WindowsMRAssetConverter/WindowsMRAssetConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ GLTFDocument LoadAndConvertDocumentForWindowsMR(
size_t maxTextureSize)
{
// Load the document
std::wstring inputFileName = PathFindFileName(inputFilePath.c_str());
std::experimental::filesystem::path inputFilePathFS(inputFilePath);
std::wstring inputFileName = inputFilePathFS.filename();
std::wcout << L"Loading input document: " << inputFileName << L"..." << std::endl;

if (inputAssetType == AssetType::GLB)
Expand All @@ -86,11 +87,8 @@ GLTFDocument LoadAndConvertDocumentForWindowsMR(
std::string inputFilePathA(inputFilePath.begin(), inputFilePath.end());
std::string tempDirectoryA(tempDirectory.begin(), tempDirectory.end());

wchar_t *inputFileNameRaw = &inputFileName[0];
PathRemoveExtension(inputFileNameRaw);

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

GLBToGLTF::UnpackGLB(inputFilePathA, tempDirectoryA, inputGltfNameA);
Expand Down Expand Up @@ -154,6 +152,7 @@ int wmain(int argc, wchar_t *argv[])
std::wcout << L"Merging LODs..." << std::endl;

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

for (size_t i = 0; i < lodFilePaths.size(); i++)
Expand All @@ -163,12 +162,11 @@ int wmain(int argc, wchar_t *argv[])
auto subFolder = FileSystem::CreateSubFolder(tempDirectory, L"lod" + std::to_wstring(i + 1));

lodDocuments.push_back(LoadAndConvertDocumentForWindowsMR(lod, AssetTypeUtils::AssetTypeFromFilePath(lod), subFolder, maxTextureSize));

lodDocumentRelativePaths.push_back(FileSystem::GetRelativePathWithTrailingSeparator(FileSystem::GetBasePath(inputFilePath), FileSystem::GetBasePath(lod)));
}

// TODO: LOD assets can be in different places in disk, so the merged document will not have
// the right relative paths to resources. We must either compute the correct relative paths or embed
// all resources as base64 in the source document, otherwise the export to GLB will fail.
document = GLTFLODUtils::MergeDocumentsAsLODs(lodDocuments, screenCoveragePercentages);
document = GLTFLODUtils::MergeDocumentsAsLODs(lodDocuments, screenCoveragePercentages, lodDocumentRelativePaths);
}

// 4. Make sure there's a default scene
Expand Down
4 changes: 2 additions & 2 deletions WindowsMRAssetConverter/WindowsMRAssetConverter.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>d3d11.lib;dxgi.lib;pathcch.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>d3d11.lib;dxgi.lib;pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
Expand Down Expand Up @@ -148,7 +148,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>d3d11.lib;dxgi.lib;pathcch.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>d3d11.lib;dxgi.lib;pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
Expand Down
1 change: 0 additions & 1 deletion glTF-Toolkit.Test/GLTFLODUtilsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ namespace Microsoft::glTF::Toolkit::Test
std::vector<GLTFDocument> docs;
docs.push_back(doc);
docs.push_back(doc);

auto newlodgltfDoc = GLTFLODUtils::MergeDocumentsAsLODs(docs);

// Serialize GLTFDocument back to json
Expand Down
8 changes: 6 additions & 2 deletions glTF-Toolkit/inc/GLTFLODUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ namespace Microsoft::glTF::Toolkit
/// </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>
static GLTFDocument MergeDocumentsAsLODs(const std::vector<GLTFDocument>& docs);
/// <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 GLTFDocument MergeDocumentsAsLODs(const std::vector<GLTFDocument>& docs, const std::vector<std::wstring>& relativePaths = std::vector<std::wstring>());

/// <summary>
/// Inserts each LOD GLTFDocument as a node LOD (at the root level) of the specified primary GLTF asset.
Expand All @@ -40,7 +42,9 @@ namespace Microsoft::glTF::Toolkit
/// <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>
static GLTFDocument MergeDocumentsAsLODs(const std::vector<GLTFDocument>& docs, const std::vector<double>& screenCoveragePercentages);
/// <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 GLTFDocument MergeDocumentsAsLODs(const std::vector<GLTFDocument>& docs, const std::vector<double>& screenCoveragePercentages, const std::vector<std::wstring>& relativePaths = std::vector<std::wstring>());

/// <summary>
/// Determines the highest number of Node LODs for a given glTF asset.
Expand Down
22 changes: 17 additions & 5 deletions glTF-Toolkit/src/GLTFLODUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include <algorithm>
#include <iostream>
#include <set>
#include <codecvt>
#include <filesystem>

using namespace Microsoft::glTF;
using namespace Microsoft::glTF::Toolkit;
Expand Down Expand Up @@ -108,7 +110,7 @@ namespace
return stringBuffer.GetString();
}

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

Expand Down Expand Up @@ -160,6 +162,8 @@ namespace
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));
}

Expand Down Expand Up @@ -207,6 +211,14 @@ namespace
{
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));
}

Expand Down Expand Up @@ -363,7 +375,7 @@ LODMap GLTFLODUtils::ParseDocumentNodeLODs(const GLTFDocument& doc)
return lodMap;
}

GLTFDocument GLTFLODUtils::MergeDocumentsAsLODs(const std::vector<GLTFDocument>& docs)
GLTFDocument GLTFLODUtils::MergeDocumentsAsLODs(const std::vector<GLTFDocument>& docs, const std::vector<std::wstring>& relativePaths)
{
if (docs.empty())
{
Expand All @@ -375,7 +387,7 @@ GLTFDocument GLTFLODUtils::MergeDocumentsAsLODs(const std::vector<GLTFDocument>&

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

for (auto lod : lods)
Expand All @@ -398,9 +410,9 @@ GLTFDocument GLTFLODUtils::MergeDocumentsAsLODs(const std::vector<GLTFDocument>&
return gltfPrimary;
}

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

if (screenCoveragePercentages.size() == 0)
{
Expand Down

0 comments on commit 9cc0960

Please sign in to comment.