@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
115 lines • 4.54 kB
JavaScript
import { Loader, TextureLoader } from "three";
import { ObjectUtils } from "../engine_create_objects.js";
import { MODULES } from "../engine_modules.js";
// #region Utils
export var MaterialX;
(function (MaterialX) {
/**
* Utility function to load a MaterialX material from a URL. This can be used in your own code to load MaterialX materials outside of the glTF loading process. The URL should point to a MaterialX XML file.
*/
async function loadFromUrl(urlOrXML, opts) {
if (!urlOrXML)
throw new Error("URL or XML string is required to load a MaterialX material");
// Ensure the MaterialX module is loaded
const module = await MODULES.MaterialX.load();
// Check if the input is an XML string or a URL
// And fetch the XML content if it's a URL
const isXmlString = urlOrXML.trimStart().startsWith("<");
const xml = isXmlString ? urlOrXML : await fetch(urlOrXML).then(r => r.text()).catch(console.error);
if (!xml) {
console.warn("Failed to load MaterialX file from url", urlOrXML);
return null;
}
// For relative texture paths we might need to detect the base directory of the material file.
// We can only do this if we have a URL (not an XML string) and if the URL is not a data URL. In that case we can use the URL to determine the base path for textures.
// This can be used by the loader callback to resolve texture paths relative to the material file.
let dir = undefined;
if (opts?.url || !isXmlString) {
const parts = (opts?.url || urlOrXML).split('/');
parts.pop();
dir = parts.join('/');
}
const textureLoader = new TextureLoader();
return module.Experimental_API.createMaterialXMaterial(xml, opts?.materialNameOrIndex ?? 0, {
getTexture: async (url) => {
if (!url.startsWith("http") && !url.startsWith("data:") && !url.startsWith("blob:") && !url.startsWith("file:")) {
if (dir) {
url = dir + "/" + url;
}
}
return textureLoader.loadAsync(url).catch(e => {
console.warn(`Failed to load texture for MaterialX material ${url}`, e);
});
}
}, {
cacheKey: urlOrXML,
});
}
MaterialX.loadFromUrl = loadFromUrl;
})(MaterialX || (MaterialX = {}));
// #region Loader
export class MaterialXLoader extends Loader {
loadAsync(url, onProgress) {
return new Promise((resolve, reject) => {
this.load(url, resolve, onProgress, reject);
});
}
load(url, onLoad, onProgress, onError) {
onProgress?.({ type: "progress", loaded: 0, total: 0 });
MaterialX.loadFromUrl(url, {}).then(mat => {
if (mat) {
onLoad(this.onLoaded(mat));
}
else {
onError?.(new Error("Failed to load MaterialX material from url: " + url));
}
});
}
onLoaded(mat) {
const shaderball = ObjectUtils.createPrimitive("ShaderBall", { material: mat });
return shaderball;
}
}
// #region GLTF Extension
export class NEEDLE_materialx {
context;
loader;
url;
parser;
get name() {
return "materialx-loading-helper";
}
constructor(context, loader, url, parser) {
this.context = context;
this.loader = loader;
this.url = url;
this.parser = parser;
}
mtlxLoader;
async beforeRoot() {
const mtlxExtension = this.parser.json.extensions?.["NEEDLE_materials_mtlx"];
if (mtlxExtension) {
const module = await MODULES.MaterialX.load();
try {
this.mtlxLoader = new module.MaterialXLoader(this.parser, {
cacheKey: `${this.url}:materialx`,
parameters: {
precision: this.context.renderer?.capabilities.precision,
}
}, {
getFrame: () => this.context.time.frame,
getTime: () => this.context.time.time,
});
}
catch (error) {
console.error(error);
}
}
}
loadMaterial(index) {
if (this.mtlxLoader)
return this.mtlxLoader.loadMaterial(index);
return null;
}
}
//# sourceMappingURL=NEEDLE_materialx.js.map