forked from PixarAnimationStudios/OpenUSD
-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: adding httpResolver #20
Open
beersandrew
wants to merge
1
commit into
autodesk-forks:adsk/feature/wasm
Choose a base branch
from
beersandrew:feature/http-asset-resolver
base: adsk/feature/wasm
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+292
−3
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
set(PXR_PREFIX pxr/usd/plugin) | ||
set(PXR_PACKAGE httpResolver) | ||
|
||
pxr_library(${PXR_PACKAGE} | ||
LIBRARIES | ||
ar | ||
arch | ||
PUBLIC_CLASSES | ||
resolver | ||
CPPFILES | ||
resolver.cpp | ||
RESOURCE_FILES | ||
plugInfo.json | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"Plugins": [ | ||
{ | ||
"Info": { | ||
"Types": { | ||
"HttpResolver": { | ||
"bases": ["ArDefaultResolver"] | ||
} | ||
} | ||
}, | ||
"Name": "HttpResolver", | ||
"LibraryPath": "@PLUG_INFO_LIBRARY_PATH@", | ||
"ResourcePath": "@PLUG_INFO_RESOURCE_PATH@", | ||
"Root": "@PLUG_INFO_ROOT@", | ||
"Type": "library" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
// IMPORT THIRD-PARTY LIBRARIES | ||
#include <pxr/usd/ar/defaultResolver.h> | ||
#include <pxr/usd/ar/defineResolver.h> | ||
#include <iostream> | ||
#include <filesystem> | ||
#include <fstream> | ||
|
||
// IMPORT LOCAL LIBRARIES | ||
#include "resolver.h" | ||
|
||
PXR_NAMESPACE_OPEN_SCOPE | ||
|
||
AR_DEFINE_RESOLVER(HttpResolver, ArDefaultResolver); | ||
|
||
HttpResolver::HttpResolver() : ArDefaultResolver() {} | ||
HttpResolver::~HttpResolver() {} | ||
|
||
struct AssetData { | ||
int ptrToContent; | ||
int length; // Use int for compatibility with JavaScript's setValue; adjust if necessary | ||
}; | ||
|
||
EM_ASYNC_JS(void, fetch_asset, (const char* route, int dataPtr), { | ||
const routeString = UTF8ToString(route); | ||
const absoluteUrl = new URL(routeString); | ||
try { | ||
const response = await fetch(absoluteUrl); | ||
if (!response.ok) throw new Error('Fetch failed: ' + response.statusText); | ||
const buffer = await response.arrayBuffer(); | ||
const length = buffer.byteLength; | ||
const ptr = _malloc(length); | ||
HEAPU8.set(new Uint8Array(buffer), ptr); | ||
|
||
// Correctly set the pointer and length in the AssetData structure | ||
// Note: Assumes dataPtr is a pointer to the structure where the first member is an int pointer | ||
// to the content, and the second is an int for the length. | ||
Module.HEAP32[dataPtr >> 2] = ptr; // Set the pointer | ||
Module.HEAP32[(dataPtr >> 2) + 1] = length; // Set the length | ||
} catch (err) { | ||
console.error("Error in fetch_asset: ", err); | ||
Module.HEAP32[dataPtr >> 2] = 0; // Indicate failure with null pointer | ||
Module.HEAP32[(dataPtr >> 2) + 1] = 0; // and zero length | ||
} | ||
}); | ||
|
||
EM_JS(void, addToLoadedFiles, (const char* path), { | ||
if (typeof loadedFiles === 'undefined'){ | ||
var loadedFiles = []; | ||
} | ||
loadedFiles.push(UTF8ToString(path)); | ||
}); | ||
|
||
std::filesystem::path HttpResolver::FetchAndSaveAsset(const std::string& route, | ||
const std::string& filePath) const { | ||
try { | ||
std::filesystem::path dirPath = std::filesystem::path(filePath).parent_path(); | ||
|
||
// Attempt to create the directory (and any necessary parent directories) | ||
if (std::filesystem::create_directories(dirPath)) { | ||
if (verbose){ | ||
std::cout << "Directories created successfully: " << dirPath << std::endl; | ||
} | ||
} else { | ||
if (verbose){ | ||
std::cout << "Directories already exist or cannot be created.\n"; | ||
} | ||
} | ||
|
||
AssetData* data = new AssetData(); | ||
fetch_asset(route.c_str(), reinterpret_cast<int>(data)); | ||
char *assetContentCString = reinterpret_cast<char *>(data->ptrToContent); | ||
saveBinaryAssetContentToFile(assetContentCString, data->length, filePath); | ||
|
||
free(reinterpret_cast<void*>(data->ptrToContent)); | ||
delete data; | ||
|
||
} | ||
catch (const std::exception& e){ | ||
std::cout << "Error: " << e.what() << std::endl; | ||
return filePath; | ||
} | ||
|
||
addToLoadedFiles(filePath.c_str()); | ||
return filePath; | ||
} | ||
|
||
void HttpResolver::saveBinaryAssetContentToFile(const char* assetContent, size_t length, const std::string& filePath) const { | ||
|
||
std::ofstream outFile(filePath, std::ios::out | std::ios::binary); | ||
if (outFile) { | ||
// Write the binary content directly to the file | ||
outFile.write(assetContent, length); | ||
outFile.close(); | ||
if (verbose) { | ||
std::cout << "File written successfully." << std::endl; | ||
} | ||
} else { | ||
if (verbose) { | ||
std::cout << "Failed to open file for writing." << std::endl; | ||
} | ||
} | ||
} | ||
|
||
void HttpResolver::setBaseUrl(const std::string& url) const { | ||
baseUrl = url; | ||
} | ||
|
||
void HttpResolver::setBaseTempDir(const std::string& tempDir) const { | ||
baseTempDir = tempDir; | ||
} | ||
|
||
std::string combineUrl(const std::string& baseUrl, const std::string& relativePath) { | ||
// Step 1: Strip off the scheme | ||
auto schemeEnd = baseUrl.find(":/"); | ||
if (schemeEnd == std::string::npos) { | ||
return baseUrl + relativePath; | ||
} | ||
std::string scheme = baseUrl.substr(0, schemeEnd + 3); // Include "://" | ||
std::string basePath = baseUrl.substr(schemeEnd + 3); | ||
|
||
// Extract the domain | ||
auto pathStart = basePath.find('/'); | ||
std::string domain = basePath.substr(0, pathStart); | ||
std::string pathOnly = basePath.substr(pathStart); // Path without the domain | ||
|
||
// Step 2: Use filesystem::path for manipulation | ||
std::filesystem::path pathObj = pathOnly; | ||
pathObj = pathObj.remove_filename(); // Ensure we're manipulating the directory part | ||
pathObj /= relativePath; // Append the relative path | ||
pathObj = pathObj.lexically_normal(); // Normalize the path (resolve "..", ".", etc.) | ||
|
||
// Step 3: Recombine | ||
std::string combinedUrl = scheme + domain + pathObj.string(); | ||
|
||
return combinedUrl; | ||
} | ||
|
||
ArResolvedPath HttpResolver::_Resolve(const std::string& assetPath) const { | ||
if (verbose){ | ||
std::cout << "_Resolve: " << assetPath << std::endl; | ||
} | ||
std::string stringAssetPathCopy = assetPath; | ||
std::filesystem::path savedAssetFilePath = assetPath; | ||
if (std::filesystem::exists(assetPath)){ | ||
if (verbose) { | ||
std::cout << "Already Exists: " << assetPath << std::endl; | ||
} | ||
} | ||
else if (assetPath.find("http") != std::string::npos){ | ||
std::string githubName = "github.com"; | ||
std::string rawGithubName = "raw.githubusercontent.com"; | ||
std::string blob = "/blob"; | ||
|
||
size_t pos = stringAssetPathCopy.find(githubName); | ||
if (pos!= std::string::npos) { | ||
stringAssetPathCopy.replace(pos, githubName.length(), rawGithubName); | ||
} | ||
|
||
size_t pos_blob = stringAssetPathCopy.find(blob); | ||
if (pos_blob!= std::string::npos) { | ||
stringAssetPathCopy.erase(pos_blob, blob.length()); | ||
} | ||
|
||
std::filesystem::path fullHttpRouteAsPath = stringAssetPathCopy; | ||
std::filesystem::path rootHttpRouteAsPath = fullHttpRouteAsPath.parent_path(); | ||
|
||
auto finalBaseUrl = rootHttpRouteAsPath.generic_string() + "/"; | ||
if (verbose){ | ||
std::cout << "http PATH: " << stringAssetPathCopy << std::endl; | ||
std::cout << "finalBaseUrl: " << finalBaseUrl << std::endl; | ||
} | ||
|
||
setBaseUrl(finalBaseUrl); | ||
|
||
std::filesystem::path tempDir = std::filesystem::temp_directory_path(); | ||
// This path is chosen because if an asset is found with the path /../../../ it will go up the tmp directory structure | ||
// in the case of using /tmp/ then all relative paths greater than depth 1, will look the same. using 6 here is arbitrary, | ||
// is there a way to make this always work? | ||
setBaseTempDir(tempDir.generic_string() + "/1/1/1/1/1/1/"); | ||
auto filePath = baseTempDir + fullHttpRouteAsPath.filename().generic_string(); | ||
savedAssetFilePath = FetchAndSaveAsset(assetPath, | ||
filePath); | ||
} | ||
else if (!baseUrl.empty()){ | ||
std::filesystem::path systemPath = stringAssetPathCopy; | ||
std::filesystem::path relativePath = std::filesystem::relative(systemPath, baseTempDir); | ||
|
||
std::string route = combineUrl(baseUrl, relativePath); | ||
if (verbose){ | ||
std::cout << "Relative Path before: " << relativePath << std::endl; | ||
} | ||
|
||
savedAssetFilePath = FetchAndSaveAsset(route, systemPath); | ||
if (verbose){ | ||
std::cout << "Assumed to exist now, trying from baseUrl: " << systemPath << std::endl; | ||
} | ||
} | ||
else { | ||
return ArDefaultResolver::_Resolve(assetPath); | ||
} | ||
|
||
if (verbose){ | ||
std::cout << "ENDDD_Resolve: " << savedAssetFilePath << std::endl; | ||
} | ||
|
||
return ArResolvedPath(savedAssetFilePath); | ||
} | ||
|
||
std::shared_ptr<ArAsset> HttpResolver::_OpenAsset(const ArResolvedPath &resolvedPath) const { | ||
if (verbose){ | ||
std::cout << "_OpenAsset: " << resolvedPath.GetPathString() << std::endl; | ||
} | ||
|
||
return ArDefaultResolver::_OpenAsset(resolvedPath); | ||
} | ||
|
||
ArResolvedPath HttpResolver::_ResolveForNewAsset(const std::string &assetPath) const { | ||
if (verbose){ | ||
std::cout << "Resolve for new asset" << std::endl; | ||
} | ||
|
||
return ArDefaultResolver::_ResolveForNewAsset(assetPath); | ||
} | ||
|
||
PXR_NAMESPACE_CLOSE_SCOPE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#pragma once | ||
|
||
// IMPORT THIRD-PARTY LIBRARIES | ||
#include <pxr/usd/ar/defaultResolver.h> | ||
#include <pxr/usd/ar/defineResolver.h> | ||
#include <emscripten/fetch.h> | ||
#include <emscripten.h> | ||
|
||
PXR_NAMESPACE_OPEN_SCOPE | ||
|
||
class HttpResolver : public ArDefaultResolver { | ||
public: | ||
HttpResolver(); | ||
~HttpResolver(); | ||
|
||
ArResolvedPath _Resolve(const std::string& path) const override; | ||
std::shared_ptr<ArAsset> _OpenAsset(const ArResolvedPath &resolvedPath) const override; | ||
ArResolvedPath _ResolveForNewAsset(const std::string &assetPath) const override; | ||
std::filesystem::path FetchAndSaveAsset(const std::string& baseUrl, const std::string& filePath) const; | ||
void saveBinaryAssetContentToFile(const char* assetContent, size_t length, const std::string& filePath) const; | ||
void setBaseUrl(const std::string &url) const; | ||
void setBaseTempDir(const std::string &tempDir) const; | ||
private: | ||
mutable std::string baseUrl; | ||
bool verbose = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a bad way to debug / set flags? I wasn't able to understand how to properly set debug flags within wasm (something like TF_DEBUG). Is that possible? |
||
mutable std::string baseTempDir; | ||
}; | ||
|
||
PXR_NAMESPACE_CLOSE_SCOPE |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the right way to handle this? Originally I was saving the base asset in the base tmp dir (something like
/tmp/baseAsset.usda
). However, when assets reference other assets in parent folders (something like../../../../materials/red.usda
, it seems correct to make sure to save that asset in the virtual file system at the same relative place to the original base asset. Here I've arbitrarily created 6 folders called '1' just to allow for up to 6 directories above the root asset. Is there a good way to make this generic? Am I missing something?