Favivon - Correção

This commit is contained in:
2026-05-30 19:59:39 -03:00
parent 76ddaa815d
commit d7dfd221f0
32859 changed files with 5459654 additions and 404 deletions
+98
View File
@@ -0,0 +1,98 @@
import { AnimationClip } from '../animation/AnimationClip.js';
import { FileLoader } from './FileLoader.js';
import { Loader } from './Loader.js';
import { error } from '../utils.js';
/**
* Class for loading animation clips in the JSON format. The files are internally
* loaded via {@link FileLoader}.
*
* ```js
* const loader = new THREE.AnimationLoader();
* const animations = await loader.loadAsync( 'animations/animation.js' );
* ```
*
* @augments Loader
*/
class AnimationLoader extends Loader {
/**
* Constructs a new animation loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
}
/**
* Starts loading from the given URL and pass the loaded animations as an array
* holding instances of {@link AnimationClip} to the `onLoad()` callback.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(Array<AnimationClip>)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Executed while the loading is in progress.
* @param {onErrorCallback} onError - Executed when errors occur.
*/
load( url, onLoad, onProgress, onError ) {
const scope = this;
const loader = new FileLoader( this.manager );
loader.setPath( this.path );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, function ( text ) {
try {
onLoad( scope.parse( JSON.parse( text ) ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
error( e );
}
scope.manager.itemError( url );
}
}, onProgress, onError );
}
/**
* Parses the given JSON object and returns an array of animation clips.
*
* @param {Object} json - The serialized animation clips.
* @return {Array<AnimationClip>} The parsed animation clips.
*/
parse( json ) {
const animations = [];
for ( let i = 0; i < json.length; i ++ ) {
const clip = AnimationClip.parse( json[ i ] );
animations.push( clip );
}
return animations;
}
}
export { AnimationLoader };
+98
View File
@@ -0,0 +1,98 @@
import { AudioContext } from '../audio/AudioContext.js';
import { FileLoader } from './FileLoader.js';
import { Loader } from './Loader.js';
import { error } from '../utils.js';
/**
* Class for loading audio buffers. Audios are internally
* loaded via {@link FileLoader}.
*
* ```js
* const audioListener = new THREE.AudioListener();
* const ambientSound = new THREE.Audio( audioListener );
*
* const loader = new THREE.AudioLoader();
* const audioBuffer = await loader.loadAsync( 'audio/ambient_ocean.ogg' );
*
* ambientSound.setBuffer( audioBuffer );
* ambientSound.play();
* ```
*
* @augments Loader
*/
class AudioLoader extends Loader {
/**
* Constructs a new audio loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
}
/**
* Starts loading from the given URL and passes the loaded audio buffer
* to the `onLoad()` callback.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(AudioBuffer)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Executed while the loading is in progress.
* @param {onErrorCallback} onError - Executed when errors occur.
*/
load( url, onLoad, onProgress, onError ) {
const scope = this;
const loader = new FileLoader( this.manager );
loader.setResponseType( 'arraybuffer' );
loader.setPath( this.path );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, function ( buffer ) {
try {
// Create a copy of the buffer. The `decodeAudioData` method
// detaches the buffer when complete, preventing reuse.
const bufferCopy = buffer.slice( 0 );
const context = AudioContext.getContext();
context.decodeAudioData( bufferCopy, function ( audioBuffer ) {
onLoad( audioBuffer );
} ).catch( handleError );
} catch ( e ) {
handleError( e );
}
}, onProgress, onError );
function handleError( e ) {
if ( onError ) {
onError( e );
} else {
error( e );
}
scope.manager.itemError( url );
}
}
}
export { AudioLoader };
+242
View File
@@ -0,0 +1,242 @@
import { Sphere } from '../math/Sphere.js';
import { BufferAttribute } from '../core/BufferAttribute.js';
import { BufferGeometry } from '../core/BufferGeometry.js';
import { FileLoader } from './FileLoader.js';
import { Loader } from './Loader.js';
import { InstancedBufferGeometry } from '../core/InstancedBufferGeometry.js';
import { InstancedBufferAttribute } from '../core/InstancedBufferAttribute.js';
import { InterleavedBufferAttribute } from '../core/InterleavedBufferAttribute.js';
import { InterleavedBuffer } from '../core/InterleavedBuffer.js';
import { getTypedArray, error } from '../utils.js';
/**
* Class for loading geometries. The files are internally
* loaded via {@link FileLoader}.
*
* ```js
* const loader = new THREE.BufferGeometryLoader();
* const geometry = await loader.loadAsync( 'models/json/pressure.json' );
*
* const material = new THREE.MeshBasicMaterial( { color: 0xF5F5F5 } );
* const object = new THREE.Mesh( geometry, material );
* scene.add( object );
* ```
*
* @augments Loader
*/
class BufferGeometryLoader extends Loader {
/**
* Constructs a new geometry loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
}
/**
* Starts loading from the given URL and pass the loaded geometry to the `onLoad()` callback.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(BufferGeometry)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Executed while the loading is in progress.
* @param {onErrorCallback} onError - Executed when errors occur.
*/
load( url, onLoad, onProgress, onError ) {
const scope = this;
const loader = new FileLoader( scope.manager );
loader.setPath( scope.path );
loader.setRequestHeader( scope.requestHeader );
loader.setWithCredentials( scope.withCredentials );
loader.load( url, function ( text ) {
try {
onLoad( scope.parse( JSON.parse( text ) ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
error( e );
}
scope.manager.itemError( url );
}
}, onProgress, onError );
}
/**
* Parses the given JSON object and returns a geometry.
*
* @param {Object} json - The serialized geometry.
* @return {BufferGeometry} The parsed geometry.
*/
parse( json ) {
const interleavedBufferMap = {};
const arrayBufferMap = {};
function getInterleavedBuffer( json, uuid ) {
if ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ];
const interleavedBuffers = json.interleavedBuffers;
const interleavedBuffer = interleavedBuffers[ uuid ];
const buffer = getArrayBuffer( json, interleavedBuffer.buffer );
const array = getTypedArray( interleavedBuffer.type, buffer );
const ib = new InterleavedBuffer( array, interleavedBuffer.stride );
ib.uuid = interleavedBuffer.uuid;
interleavedBufferMap[ uuid ] = ib;
return ib;
}
function getArrayBuffer( json, uuid ) {
if ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ];
const arrayBuffers = json.arrayBuffers;
const arrayBuffer = arrayBuffers[ uuid ];
const ab = new Uint32Array( arrayBuffer ).buffer;
arrayBufferMap[ uuid ] = ab;
return ab;
}
const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry();
const index = json.data.index;
if ( index !== undefined ) {
const typedArray = getTypedArray( index.type, index.array );
geometry.setIndex( new BufferAttribute( typedArray, 1 ) );
}
const attributes = json.data.attributes;
for ( const key in attributes ) {
const attribute = attributes[ key ];
let bufferAttribute;
if ( attribute.isInterleavedBufferAttribute ) {
const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data );
bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized );
} else {
const typedArray = getTypedArray( attribute.type, attribute.array );
const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute;
bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized );
}
if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name;
if ( attribute.usage !== undefined ) bufferAttribute.setUsage( attribute.usage );
geometry.setAttribute( key, bufferAttribute );
}
const morphAttributes = json.data.morphAttributes;
if ( morphAttributes ) {
for ( const key in morphAttributes ) {
const attributeArray = morphAttributes[ key ];
const array = [];
for ( let i = 0, il = attributeArray.length; i < il; i ++ ) {
const attribute = attributeArray[ i ];
let bufferAttribute;
if ( attribute.isInterleavedBufferAttribute ) {
const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data );
bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized );
} else {
const typedArray = getTypedArray( attribute.type, attribute.array );
bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized );
}
if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name;
array.push( bufferAttribute );
}
geometry.morphAttributes[ key ] = array;
}
}
const morphTargetsRelative = json.data.morphTargetsRelative;
if ( morphTargetsRelative ) {
geometry.morphTargetsRelative = true;
}
const groups = json.data.groups || json.data.drawcalls || json.data.offsets;
if ( groups !== undefined ) {
for ( let i = 0, n = groups.length; i !== n; ++ i ) {
const group = groups[ i ];
geometry.addGroup( group.start, group.count, group.materialIndex );
}
}
const boundingSphere = json.data.boundingSphere;
if ( boundingSphere !== undefined ) {
geometry.boundingSphere = new Sphere().fromJSON( boundingSphere );
}
if ( json.name ) geometry.name = json.name;
if ( json.userData ) geometry.userData = json.userData;
return geometry;
}
}
export { BufferGeometryLoader };
+87
View File
@@ -0,0 +1,87 @@
/**
* @class
* @classdesc A simple caching system, used internally by {@link FileLoader}.
* To enable caching across all loaders that use {@link FileLoader}, add `THREE.Cache.enabled = true.` once in your app.
* @hideconstructor
*/
const Cache = {
/**
* Whether caching is enabled or not.
*
* @static
* @type {boolean}
* @default false
*/
enabled: false,
/**
* A dictionary that holds cached files.
*
* @static
* @type {Object<string,Object>}
*/
files: {},
/**
* Adds a cache entry with a key to reference the file. If this key already
* holds a file, it is overwritten.
*
* @static
* @param {string} key - The key to reference the cached file.
* @param {Object} file - The file to be cached.
*/
add: function ( key, file ) {
if ( this.enabled === false ) return;
// log( 'Cache', 'Adding key:', key );
this.files[ key ] = file;
},
/**
* Gets the cached value for the given key.
*
* @static
* @param {string} key - The key to reference the cached file.
* @return {Object|undefined} The cached file. If the key does not exist `undefined` is returned.
*/
get: function ( key ) {
if ( this.enabled === false ) return;
// log( 'Cache', 'Checking key:', key );
return this.files[ key ];
},
/**
* Removes the cached file associated with the given key.
*
* @static
* @param {string} key - The key to reference the cached file.
*/
remove: function ( key ) {
delete this.files[ key ];
},
/**
* Remove all values from the cache.
*
* @static
*/
clear: function () {
this.files = {};
}
};
export { Cache };
+167
View File
@@ -0,0 +1,167 @@
import { LinearFilter } from '../constants.js';
import { FileLoader } from './FileLoader.js';
import { CompressedTexture } from '../textures/CompressedTexture.js';
import { Loader } from './Loader.js';
/**
* Abstract base class for loading compressed texture formats S3TC, ASTC or ETC.
* Textures are internally loaded via {@link FileLoader}.
*
* Derived classes have to implement the `parse()` method which holds the parsing
* for the respective format.
*
* @abstract
* @augments Loader
*/
class CompressedTextureLoader extends Loader {
/**
* Constructs a new compressed texture loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
}
/**
* Starts loading from the given URL and passes the loaded compressed texture
* to the `onLoad()` callback. The method also returns a new texture object which can
* directly be used for material creation. If you do it this way, the texture
* may pop up in your scene once the respective loading process is finished.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(CompressedTexture)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Executed while the loading is in progress.
* @param {onErrorCallback} onError - Executed when errors occur.
* @return {CompressedTexture} The compressed texture.
*/
load( url, onLoad, onProgress, onError ) {
const scope = this;
const images = [];
const texture = new CompressedTexture();
const loader = new FileLoader( this.manager );
loader.setPath( this.path );
loader.setResponseType( 'arraybuffer' );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( scope.withCredentials );
let loaded = 0;
function loadTexture( i ) {
loader.load( url[ i ], function ( buffer ) {
const texDatas = scope.parse( buffer, true );
images[ i ] = {
width: texDatas.width,
height: texDatas.height,
format: texDatas.format,
mipmaps: texDatas.mipmaps
};
loaded += 1;
if ( loaded === 6 ) {
if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter;
texture.image = images;
texture.format = texDatas.format;
texture.needsUpdate = true;
if ( onLoad ) onLoad( texture );
}
}, onProgress, onError );
}
if ( Array.isArray( url ) ) {
for ( let i = 0, il = url.length; i < il; ++ i ) {
loadTexture( i );
}
} else {
// compressed cubemap texture stored in a single DDS file
loader.load( url, function ( buffer ) {
const texDatas = scope.parse( buffer, true );
if ( texDatas.isCubemap ) {
const faces = texDatas.mipmaps.length / texDatas.mipmapCount;
for ( let f = 0; f < faces; f ++ ) {
images[ f ] = { mipmaps: [] };
for ( let i = 0; i < texDatas.mipmapCount; i ++ ) {
images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );
images[ f ].format = texDatas.format;
images[ f ].width = texDatas.width;
images[ f ].height = texDatas.height;
}
}
texture.image = images;
} else {
texture.image.width = texDatas.width;
texture.image.height = texDatas.height;
texture.mipmaps = texDatas.mipmaps;
}
if ( texDatas.mipmapCount === 1 ) {
texture.minFilter = LinearFilter;
}
texture.format = texDatas.format;
texture.needsUpdate = true;
if ( onLoad ) onLoad( texture );
}, onProgress, onError );
}
return texture;
}
}
/**
* Represents the result object type of the `parse()` method.
*
* @typedef {Object} CompressedTextureLoader~TexData
* @property {number} width - The width of the base mip.
* @property {number} height - The width of the base mip.
* @property {boolean} isCubemap - Whether the data represent a cubemap or not.
* @property {number} mipmapCount - The mipmap count.
* @property {Array<{data:TypedArray,width:number,height:number}>} mipmaps - An array holding the mipmaps.
* Each entry holds the data and the dimensions for each level.
* @property {number} format - The texture format.
**/
export { CompressedTextureLoader };
+103
View File
@@ -0,0 +1,103 @@
import { ImageLoader } from './ImageLoader.js';
import { CubeTexture } from '../textures/CubeTexture.js';
import { Loader } from './Loader.js';
import { SRGBColorSpace } from '../constants.js';
/**
* Class for loading cube textures. Images are internally loaded via {@link ImageLoader}.
*
* The loader returns an instance of {@link CubeTexture} and expects the cube map to
* be defined as six separate images representing the sides of a cube. Other cube map definitions
* like vertical and horizontal cross, column and row layouts are not supported.
*
* Note that, by convention, cube maps are specified in a coordinate system
* in which positive-x is to the right when looking up the positive-z axis --
* in other words, using a left-handed coordinate system. Since three.js uses
* a right-handed coordinate system, environment maps used in three.js will
* have pos-x and neg-x swapped.
*
* The loaded cube texture is in sRGB color space. Meaning {@link Texture#colorSpace}
* is set to `SRGBColorSpace` by default.
*
* ```js
* const loader = new THREE.CubeTextureLoader().setPath( 'textures/cubeMaps/' );
* const cubeTexture = await loader.loadAsync( [
* 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png'
* ] );
* scene.background = cubeTexture;
* ```
*
* @augments Loader
*/
class CubeTextureLoader extends Loader {
/**
* Constructs a new cube texture loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
}
/**
* Starts loading from the given URL and pass the fully loaded cube texture
* to the `onLoad()` callback. The method also returns a new cube texture object which can
* directly be used for material creation. If you do it this way, the cube texture
* may pop up in your scene once the respective loading process is finished.
*
* @param {Array<string>} urls - Array of 6 URLs to images, one for each side of the
* cube texture. The urls should be specified in the following order: pos-x,
* neg-x, pos-y, neg-y, pos-z, neg-z. An array of data URIs are allowed as well.
* @param {function(CubeTexture)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Unsupported in this loader.
* @param {onErrorCallback} onError - Executed when errors occur.
* @return {CubeTexture} The cube texture.
*/
load( urls, onLoad, onProgress, onError ) {
const texture = new CubeTexture();
texture.colorSpace = SRGBColorSpace;
const loader = new ImageLoader( this.manager );
loader.setCrossOrigin( this.crossOrigin );
loader.setPath( this.path );
let loaded = 0;
function loadTexture( i ) {
loader.load( urls[ i ], function ( image ) {
texture.images[ i ] = image;
loaded ++;
if ( loaded === 6 ) {
texture.needsUpdate = true;
if ( onLoad ) onLoad( texture );
}
}, undefined, onError );
}
for ( let i = 0; i < urls.length; ++ i ) {
loadTexture( i );
}
return texture;
}
}
export { CubeTextureLoader };
+172
View File
@@ -0,0 +1,172 @@
import { LinearFilter, LinearMipmapLinearFilter, ClampToEdgeWrapping } from '../constants.js';
import { FileLoader } from './FileLoader.js';
import { DataTexture } from '../textures/DataTexture.js';
import { Loader } from './Loader.js';
/**
* Abstract base class for loading binary texture formats RGBE, EXR or TGA.
* Textures are internally loaded via {@link FileLoader}.
*
* Derived classes have to implement the `parse()` method which holds the parsing
* for the respective format.
*
* @abstract
* @augments Loader
*/
class DataTextureLoader extends Loader {
/**
* Constructs a new data texture loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
}
/**
* Starts loading from the given URL and passes the loaded data texture
* to the `onLoad()` callback. The method also returns a new texture object which can
* directly be used for material creation. If you do it this way, the texture
* may pop up in your scene once the respective loading process is finished.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(DataTexture)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Executed while the loading is in progress.
* @param {onErrorCallback} onError - Executed when errors occur.
* @return {DataTexture} The data texture.
*/
load( url, onLoad, onProgress, onError ) {
const scope = this;
const texture = new DataTexture();
const loader = new FileLoader( this.manager );
loader.setResponseType( 'arraybuffer' );
loader.setRequestHeader( this.requestHeader );
loader.setPath( this.path );
loader.setWithCredentials( scope.withCredentials );
loader.load( url, function ( buffer ) {
let texData;
try {
texData = scope.parse( buffer );
} catch ( error ) {
if ( onError !== undefined ) {
onError( error );
} else {
error( error );
return;
}
}
if ( texData.image !== undefined ) {
texture.image = texData.image;
} else if ( texData.data !== undefined ) {
texture.image.width = texData.width;
texture.image.height = texData.height;
texture.image.data = texData.data;
}
texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping;
texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping;
texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter;
texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter;
texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1;
if ( texData.colorSpace !== undefined ) {
texture.colorSpace = texData.colorSpace;
}
if ( texData.flipY !== undefined ) {
texture.flipY = texData.flipY;
}
if ( texData.format !== undefined ) {
texture.format = texData.format;
}
if ( texData.type !== undefined ) {
texture.type = texData.type;
}
if ( texData.mipmaps !== undefined ) {
texture.mipmaps = texData.mipmaps;
texture.minFilter = LinearMipmapLinearFilter; // presumably...
}
if ( texData.mipmapCount === 1 ) {
texture.minFilter = LinearFilter;
}
if ( texData.generateMipmaps !== undefined ) {
texture.generateMipmaps = texData.generateMipmaps;
}
texture.needsUpdate = true;
if ( onLoad ) onLoad( texture, texData );
}, onProgress, onError );
return texture;
}
}
/**
* Represents the result object type of the `parse()` method.
*
* @typedef {Object} DataTextureLoader~TexData
* @property {Object} [image] - An object holding width, height and the texture data.
* @property {number} [width] - The width of the base mip.
* @property {number} [height] - The width of the base mip.
* @property {TypedArray} [data] - The texture data.
* @property {number} [format] - The texture format.
* @property {number} [type] - The texture type.
* @property {boolean} [flipY] - If set to `true`, the texture is flipped along the vertical axis when uploaded to the GPU.
* @property {number} [wrapS=ClampToEdgeWrapping] - The wrapS value.
* @property {number} [wrapT=ClampToEdgeWrapping] - The wrapT value.
* @property {number} [anisotropy=1] - The anisotropy value.
* @property {boolean} [generateMipmaps] - Whether to generate mipmaps or not.
* @property {string} [colorSpace] - The color space.
* @property {number} [magFilter] - The mag filter.
* @property {number} [minFilter] - The min filter.
* @property {Array<Object>} [mipmaps] - The mipmaps.
**/
export { DataTextureLoader };
+368
View File
@@ -0,0 +1,368 @@
import { Cache } from './Cache.js';
import { Loader } from './Loader.js';
import { warn } from '../utils.js';
const loading = {};
class HttpError extends Error {
constructor( message, response ) {
super( message );
this.response = response;
}
}
/**
* A low level class for loading resources with the Fetch API, used internally by
* most loaders. It can also be used directly to load any file type that does
* not have a loader.
*
* This loader supports caching. If you want to use it, add `THREE.Cache.enabled = true;`
* once to your application.
*
* ```js
* const loader = new THREE.FileLoader();
* const data = await loader.loadAsync( 'example.txt' );
* ```
*
* @augments Loader
*/
class FileLoader extends Loader {
/**
* Constructs a new file loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
/**
* The expected mime type. Valid values can be found
* [here](hhttps://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString#mimetype)
*
* @type {string}
*/
this.mimeType = '';
/**
* The expected response type.
*
* @type {('arraybuffer'|'blob'|'document'|'json'|'')}
* @default ''
*/
this.responseType = '';
/**
* Used for aborting requests.
*
* @private
* @type {AbortController}
*/
this._abortController = new AbortController();
}
/**
* Starts loading from the given URL and pass the loaded response to the `onLoad()` callback.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(any)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} [onProgress] - Executed while the loading is in progress.
* @param {onErrorCallback} [onError] - Executed when errors occur.
* @return {any|undefined} The cached resource if available.
*/
load( url, onLoad, onProgress, onError ) {
if ( url === undefined ) url = '';
if ( this.path !== undefined ) url = this.path + url;
url = this.manager.resolveURL( url );
const cached = Cache.get( `file:${url}` );
if ( cached !== undefined ) {
this.manager.itemStart( url );
setTimeout( () => {
if ( onLoad ) onLoad( cached );
this.manager.itemEnd( url );
}, 0 );
return cached;
}
// Check if request is duplicate
if ( loading[ url ] !== undefined ) {
loading[ url ].push( {
onLoad: onLoad,
onProgress: onProgress,
onError: onError
} );
return;
}
// Initialise array for duplicate requests
loading[ url ] = [];
loading[ url ].push( {
onLoad: onLoad,
onProgress: onProgress,
onError: onError,
} );
// create request
const req = new Request( url, {
headers: new Headers( this.requestHeader ),
credentials: this.withCredentials ? 'include' : 'same-origin',
signal: ( typeof AbortSignal.any === 'function' ) ? AbortSignal.any( [ this._abortController.signal, this.manager.abortController.signal ] ) : this._abortController.signal
} );
// record states ( avoid data race )
const mimeType = this.mimeType;
const responseType = this.responseType;
// start the fetch
fetch( req )
.then( response => {
if ( response.status === 200 || response.status === 0 ) {
// Some browsers return HTTP Status 0 when using non-http protocol
// e.g. 'file://' or 'data://'. Handle as success.
if ( response.status === 0 ) {
warn( 'FileLoader: HTTP Status 0 received.' );
}
// Workaround: Checking if response.body === undefined for Alipay browser #23548
if ( typeof ReadableStream === 'undefined' || response.body === undefined || response.body.getReader === undefined ) {
return response;
}
const callbacks = loading[ url ];
const reader = response.body.getReader();
// Nginx needs X-File-Size check
// https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content
const contentLength = response.headers.get( 'X-File-Size' ) || response.headers.get( 'Content-Length' );
const total = contentLength ? parseInt( contentLength ) : 0;
const lengthComputable = total !== 0;
let loaded = 0;
// periodically read data into the new stream tracking while download progress
const stream = new ReadableStream( {
start( controller ) {
readData();
function readData() {
reader.read().then( ( { done, value } ) => {
if ( done ) {
controller.close();
} else {
loaded += value.byteLength;
const event = new ProgressEvent( 'progress', { lengthComputable, loaded, total } );
for ( let i = 0, il = callbacks.length; i < il; i ++ ) {
const callback = callbacks[ i ];
if ( callback.onProgress ) callback.onProgress( event );
}
controller.enqueue( value );
readData();
}
}, ( e ) => {
controller.error( e );
} );
}
}
} );
return new Response( stream );
} else {
throw new HttpError( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`, response );
}
} )
.then( response => {
switch ( responseType ) {
case 'arraybuffer':
return response.arrayBuffer();
case 'blob':
return response.blob();
case 'document':
return response.text()
.then( text => {
const parser = new DOMParser();
return parser.parseFromString( text, mimeType );
} );
case 'json':
return response.json();
default:
if ( mimeType === '' ) {
return response.text();
} else {
// sniff encoding
const re = /charset="?([^;"\s]*)"?/i;
const exec = re.exec( mimeType );
const label = exec && exec[ 1 ] ? exec[ 1 ].toLowerCase() : undefined;
const decoder = new TextDecoder( label );
return response.arrayBuffer().then( ab => decoder.decode( ab ) );
}
}
} )
.then( data => {
// Add to cache only on HTTP success, so that we do not cache
// error response bodies as proper responses to requests.
Cache.add( `file:${url}`, data );
const callbacks = loading[ url ];
delete loading[ url ];
for ( let i = 0, il = callbacks.length; i < il; i ++ ) {
const callback = callbacks[ i ];
if ( callback.onLoad ) callback.onLoad( data );
}
} )
.catch( err => {
// Abort errors and other errors are handled the same
const callbacks = loading[ url ];
if ( callbacks === undefined ) {
// When onLoad was called and url was deleted in `loading`
this.manager.itemError( url );
throw err;
}
delete loading[ url ];
for ( let i = 0, il = callbacks.length; i < il; i ++ ) {
const callback = callbacks[ i ];
if ( callback.onError ) callback.onError( err );
}
this.manager.itemError( url );
} )
.finally( () => {
this.manager.itemEnd( url );
} );
this.manager.itemStart( url );
}
/**
* Sets the expected response type.
*
* @param {('arraybuffer'|'blob'|'document'|'json'|'')} value - The response type.
* @return {FileLoader} A reference to this file loader.
*/
setResponseType( value ) {
this.responseType = value;
return this;
}
/**
* Sets the expected mime type of the loaded file.
*
* @param {string} value - The mime type.
* @return {FileLoader} A reference to this file loader.
*/
setMimeType( value ) {
this.mimeType = value;
return this;
}
/**
* Aborts ongoing fetch requests.
*
* @return {FileLoader} A reference to this instance.
*/
abort() {
this._abortController.abort();
this._abortController = new AbortController();
return this;
}
}
export { FileLoader };
+220
View File
@@ -0,0 +1,220 @@
import { Cache } from './Cache.js';
import { Loader } from './Loader.js';
import { warn } from '../utils.js';
const _errorMap = new WeakMap();
/**
* A loader for loading images as an [ImageBitmap](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap).
* An `ImageBitmap` provides an asynchronous and resource efficient pathway to prepare
* textures for rendering.
*
* Note that {@link Texture#flipY} and {@link Texture#premultiplyAlpha} are ignored with image bitmaps.
* They needs these configuration on bitmap creation unlike regular images need them on uploading to GPU.
*
* You need to set the equivalent options via {@link ImageBitmapLoader#setOptions} instead.
*
* Also note that unlike {@link FileLoader}, this loader avoids multiple concurrent requests to the same URL only if `Cache` is enabled.
*
* ```js
* const loader = new THREE.ImageBitmapLoader();
* loader.setOptions( { imageOrientation: 'flipY' } ); // set options if needed
* const imageBitmap = await loader.loadAsync( 'image.png' );
*
* const texture = new THREE.Texture( imageBitmap );
* texture.needsUpdate = true;
* ```
*
* @augments Loader
*/
class ImageBitmapLoader extends Loader {
/**
* Constructs a new image bitmap loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isImageBitmapLoader = true;
if ( typeof createImageBitmap === 'undefined' ) {
warn( 'ImageBitmapLoader: createImageBitmap() not supported.' );
}
if ( typeof fetch === 'undefined' ) {
warn( 'ImageBitmapLoader: fetch() not supported.' );
}
/**
* Represents the loader options.
*
* @type {Object}
* @default {premultiplyAlpha:'none'}
*/
this.options = { premultiplyAlpha: 'none' };
/**
* Used for aborting requests.
*
* @private
* @type {AbortController}
*/
this._abortController = new AbortController();
}
/**
* Sets the given loader options. The structure of the object must match the `options` parameter of
* [createImageBitmap](https://developer.mozilla.org/en-US/docs/Web/API/Window/createImageBitmap).
*
* @param {Object} options - The loader options to set.
* @return {ImageBitmapLoader} A reference to this image bitmap loader.
*/
setOptions( options ) {
this.options = options;
return this;
}
/**
* Starts loading from the given URL and pass the loaded image bitmap to the `onLoad()` callback.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(ImageBitmap)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Unsupported in this loader.
* @param {onErrorCallback} onError - Executed when errors occur.
* @return {ImageBitmap|undefined} The image bitmap.
*/
load( url, onLoad, onProgress, onError ) {
if ( url === undefined ) url = '';
if ( this.path !== undefined ) url = this.path + url;
url = this.manager.resolveURL( url );
const scope = this;
const cached = Cache.get( `image-bitmap:${url}` );
if ( cached !== undefined ) {
scope.manager.itemStart( url );
// If cached is a promise, wait for it to resolve
if ( cached.then ) {
cached.then( imageBitmap => {
// check if there is an error for the cached promise
if ( _errorMap.has( cached ) === true ) {
if ( onError ) onError( _errorMap.get( cached ) );
scope.manager.itemError( url );
scope.manager.itemEnd( url );
} else {
if ( onLoad ) onLoad( imageBitmap );
scope.manager.itemEnd( url );
return imageBitmap;
}
} );
return;
}
// If cached is not a promise (i.e., it's already an imageBitmap)
setTimeout( function () {
if ( onLoad ) onLoad( cached );
scope.manager.itemEnd( url );
}, 0 );
return cached;
}
const fetchOptions = {};
fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include';
fetchOptions.headers = this.requestHeader;
fetchOptions.signal = ( typeof AbortSignal.any === 'function' ) ? AbortSignal.any( [ this._abortController.signal, this.manager.abortController.signal ] ) : this._abortController.signal;
const promise = fetch( url, fetchOptions ).then( function ( res ) {
return res.blob();
} ).then( function ( blob ) {
return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) );
} ).then( function ( imageBitmap ) {
Cache.add( `image-bitmap:${url}`, imageBitmap );
if ( onLoad ) onLoad( imageBitmap );
scope.manager.itemEnd( url );
return imageBitmap;
} ).catch( function ( e ) {
if ( onError ) onError( e );
_errorMap.set( promise, e );
Cache.remove( `image-bitmap:${url}` );
scope.manager.itemError( url );
scope.manager.itemEnd( url );
} );
Cache.add( `image-bitmap:${url}`, promise );
scope.manager.itemStart( url );
}
/**
* Aborts ongoing fetch requests.
*
* @return {ImageBitmapLoader} A reference to this instance.
*/
abort() {
this._abortController.abort();
this._abortController = new AbortController();
return this;
}
}
export { ImageBitmapLoader };
+168
View File
@@ -0,0 +1,168 @@
import { Cache } from './Cache.js';
import { Loader } from './Loader.js';
import { createElementNS } from '../utils.js';
const _loading = new WeakMap();
/**
* A loader for loading images. The class loads images with the HTML `Image` API.
*
* ```js
* const loader = new THREE.ImageLoader();
* const image = await loader.loadAsync( 'image.png' );
* ```
* Please note that `ImageLoader` has dropped support for progress
* events in `r84`. For an `ImageLoader` that supports progress events, see
* [this thread](https://github.com/mrdoob/three.js/issues/10439#issuecomment-275785639).
*
* @augments Loader
*/
class ImageLoader extends Loader {
/**
* Constructs a new image loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
}
/**
* Starts loading from the given URL and passes the loaded image
* to the `onLoad()` callback. The method also returns a new `Image` object which can
* directly be used for texture creation. If you do it this way, the texture
* may pop up in your scene once the respective loading process is finished.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(Image)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Unsupported in this loader.
* @param {onErrorCallback} onError - Executed when errors occur.
* @return {Image} The image.
*/
load( url, onLoad, onProgress, onError ) {
if ( this.path !== undefined ) url = this.path + url;
url = this.manager.resolveURL( url );
const scope = this;
const cached = Cache.get( `image:${url}` );
if ( cached !== undefined ) {
if ( cached.complete === true ) {
scope.manager.itemStart( url );
setTimeout( function () {
if ( onLoad ) onLoad( cached );
scope.manager.itemEnd( url );
}, 0 );
} else {
let arr = _loading.get( cached );
if ( arr === undefined ) {
arr = [];
_loading.set( cached, arr );
}
arr.push( { onLoad, onError } );
}
return cached;
}
const image = createElementNS( 'img' );
function onImageLoad() {
removeEventListeners();
if ( onLoad ) onLoad( this );
//
const callbacks = _loading.get( this ) || [];
for ( let i = 0; i < callbacks.length; i ++ ) {
const callback = callbacks[ i ];
if ( callback.onLoad ) callback.onLoad( this );
}
_loading.delete( this );
scope.manager.itemEnd( url );
}
function onImageError( event ) {
removeEventListeners();
if ( onError ) onError( event );
Cache.remove( `image:${url}` );
//
const callbacks = _loading.get( this ) || [];
for ( let i = 0; i < callbacks.length; i ++ ) {
const callback = callbacks[ i ];
if ( callback.onError ) callback.onError( event );
}
_loading.delete( this );
scope.manager.itemError( url );
scope.manager.itemEnd( url );
}
function removeEventListeners() {
image.removeEventListener( 'load', onImageLoad, false );
image.removeEventListener( 'error', onImageError, false );
}
image.addEventListener( 'load', onImageLoad, false );
image.addEventListener( 'error', onImageError, false );
if ( url.slice( 0, 5 ) !== 'data:' ) {
if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;
}
Cache.add( `image:${url}`, image );
scope.manager.itemStart( url );
image.src = url;
return image;
}
}
export { ImageLoader };
+216
View File
@@ -0,0 +1,216 @@
import { DefaultLoadingManager } from './LoadingManager.js';
/**
* Abstract base class for loaders.
*
* @abstract
*/
class Loader {
/**
* Constructs a new loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
/**
* The loading manager.
*
* @type {LoadingManager}
* @default DefaultLoadingManager
*/
this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
/**
* The crossOrigin string to implement CORS for loading the url from a
* different domain that allows CORS.
*
* @type {string}
* @default 'anonymous'
*/
this.crossOrigin = 'anonymous';
/**
* Whether the XMLHttpRequest uses credentials.
*
* @type {boolean}
* @default false
*/
this.withCredentials = false;
/**
* The base path from which the asset will be loaded.
*
* @type {string}
*/
this.path = '';
/**
* The base path from which additional resources like textures will be loaded.
*
* @type {string}
*/
this.resourcePath = '';
/**
* The [request header](https://developer.mozilla.org/en-US/docs/Glossary/Request_header)
* used in HTTP request.
*
* @type {Object<string, any>}
*/
this.requestHeader = {};
}
/**
* This method needs to be implemented by all concrete loaders. It holds the
* logic for loading assets from the backend.
*
* @abstract
* @param {string} url - The path/URL of the file to be loaded.
* @param {Function} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} [onProgress] - Executed while the loading is in progress.
* @param {onErrorCallback} [onError] - Executed when errors occur.
*/
load( /* url, onLoad, onProgress, onError */ ) {}
/**
* A async version of {@link Loader#load}.
*
* @param {string} url - The path/URL of the file to be loaded.
* @param {onProgressCallback} [onProgress] - Executed while the loading is in progress.
* @return {Promise} A Promise that resolves when the asset has been loaded.
*/
loadAsync( url, onProgress ) {
const scope = this;
return new Promise( function ( resolve, reject ) {
scope.load( url, resolve, onProgress, reject );
} );
}
/**
* This method needs to be implemented by all concrete loaders. It holds the
* logic for parsing the asset into three.js entities.
*
* @abstract
* @param {any} data - The data to parse.
*/
parse( /* data */ ) {}
/**
* Sets the `crossOrigin` String to implement CORS for loading the URL
* from a different domain that allows CORS.
*
* @param {string} crossOrigin - The `crossOrigin` value.
* @return {Loader} A reference to this instance.
*/
setCrossOrigin( crossOrigin ) {
this.crossOrigin = crossOrigin;
return this;
}
/**
* Whether the XMLHttpRequest uses credentials such as cookies, authorization
* headers or TLS client certificates, see [XMLHttpRequest.withCredentials](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials).
*
* Note: This setting has no effect if you are loading files locally or from the same domain.
*
* @param {boolean} value - The `withCredentials` value.
* @return {Loader} A reference to this instance.
*/
setWithCredentials( value ) {
this.withCredentials = value;
return this;
}
/**
* Sets the base path for the asset.
*
* @param {string} path - The base path.
* @return {Loader} A reference to this instance.
*/
setPath( path ) {
this.path = path;
return this;
}
/**
* Sets the base path for dependent resources like textures.
*
* @param {string} resourcePath - The resource path.
* @return {Loader} A reference to this instance.
*/
setResourcePath( resourcePath ) {
this.resourcePath = resourcePath;
return this;
}
/**
* Sets the given request header.
*
* @param {Object} requestHeader - A [request header](https://developer.mozilla.org/en-US/docs/Glossary/Request_header)
* for configuring the HTTP request.
* @return {Loader} A reference to this instance.
*/
setRequestHeader( requestHeader ) {
this.requestHeader = requestHeader;
return this;
}
/**
* This method can be implemented in loaders for aborting ongoing requests.
*
* @abstract
* @return {Loader} A reference to this instance.
*/
abort() {
return this;
}
}
/**
* Callback for onProgress in loaders.
*
* @callback onProgressCallback
* @param {ProgressEvent} event - An instance of `ProgressEvent` that represents the current loading status.
*/
/**
* Callback for onError in loaders.
*
* @callback onErrorCallback
* @param {Error} error - The error which occurred during the loading process.
*/
/**
* The default material name that is used by loaders
* when creating materials for loaded 3D objects.
*
* Note: Not all loaders might honor this setting.
*
* @static
* @type {string}
* @default '__DEFAULT'
*/
Loader.DEFAULT_MATERIAL_NAME = '__DEFAULT';
export { Loader };
+59
View File
@@ -0,0 +1,59 @@
/**
* A class with loader utility functions.
*/
class LoaderUtils {
/**
* Extracts the base URL from the given URL.
*
* @param {string} url -The URL to extract the base URL from.
* @return {string} The extracted base URL.
*/
static extractUrlBase( url ) {
const index = url.lastIndexOf( '/' );
if ( index === - 1 ) return './';
return url.slice( 0, index + 1 );
}
/**
* Resolves relative URLs against the given path. Absolute paths, data urls,
* and blob URLs will be returned as is. Invalid URLs will return an empty
* string.
*
* @param {string} url -The URL to resolve.
* @param {string} path - The base path for relative URLs to be resolved against.
* @return {string} The resolved URL.
*/
static resolveURL( url, path ) {
// Invalid URL
if ( typeof url !== 'string' || url === '' ) return '';
// Host Relative URL
if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) {
path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' );
}
// Absolute URL http://,https://,//
if ( /^(https?:)?\/\//i.test( url ) ) return url;
// Data URI
if ( /^data:.*,.*$/i.test( url ) ) return url;
// Blob URL
if ( /^blob:.*$/i.test( url ) ) return url;
// Relative URL
return path + url;
}
}
export { LoaderUtils };
+329
View File
@@ -0,0 +1,329 @@
/**
* Handles and keeps track of loaded and pending data. A default global
* instance of this class is created and used by loaders if not supplied
* manually.
*
* In general that should be sufficient, however there are times when it can
* be useful to have separate loaders - for example if you want to show
* separate loading bars for objects and textures.
*
* ```js
* const manager = new THREE.LoadingManager();
* manager.onLoad = () => console.log( 'Loading complete!' );
*
* const loader1 = new OBJLoader( manager );
* const loader2 = new ColladaLoader( manager );
* ```
*/
class LoadingManager {
/**
* Constructs a new loading manager.
*
* @param {Function} [onLoad] - Executes when all items have been loaded.
* @param {Function} [onProgress] - Executes when single items have been loaded.
* @param {Function} [onError] - Executes when an error occurs.
*/
constructor( onLoad, onProgress, onError ) {
const scope = this;
let isLoading = false;
let itemsLoaded = 0;
let itemsTotal = 0;
let urlModifier = undefined;
const handlers = [];
// Refer to #5689 for the reason why we don't set .onStart
// in the constructor
/**
* Executes when an item starts loading.
*
* @type {Function|undefined}
* @default undefined
*/
this.onStart = undefined;
/**
* Executes when all items have been loaded.
*
* @type {Function|undefined}
* @default undefined
*/
this.onLoad = onLoad;
/**
* Executes when single items have been loaded.
*
* @type {Function|undefined}
* @default undefined
*/
this.onProgress = onProgress;
/**
* Executes when an error occurs.
*
* @type {Function|undefined}
* @default undefined
*/
this.onError = onError;
/**
* Used for aborting ongoing requests in loaders using this manager.
*
* @private
* @type {AbortController | null}
*/
this._abortController = null;
/**
* This should be called by any loader using the manager when the loader
* starts loading an item.
*
* @param {string} url - The URL to load.
*/
this.itemStart = function ( url ) {
itemsTotal ++;
if ( isLoading === false ) {
if ( scope.onStart !== undefined ) {
scope.onStart( url, itemsLoaded, itemsTotal );
}
}
isLoading = true;
};
/**
* This should be called by any loader using the manager when the loader
* ended loading an item.
*
* @param {string} url - The URL of the loaded item.
*/
this.itemEnd = function ( url ) {
itemsLoaded ++;
if ( scope.onProgress !== undefined ) {
scope.onProgress( url, itemsLoaded, itemsTotal );
}
if ( itemsLoaded === itemsTotal ) {
isLoading = false;
if ( scope.onLoad !== undefined ) {
scope.onLoad();
}
}
};
/**
* This should be called by any loader using the manager when the loader
* encounters an error when loading an item.
*
* @param {string} url - The URL of the item that produces an error.
*/
this.itemError = function ( url ) {
if ( scope.onError !== undefined ) {
scope.onError( url );
}
};
/**
* Given a URL, uses the URL modifier callback (if any) and returns a
* resolved URL. If no URL modifier is set, returns the original URL.
*
* @param {string} url - The URL to load.
* @return {string} The resolved URL.
*/
this.resolveURL = function ( url ) {
if ( urlModifier ) {
return urlModifier( url );
}
return url;
};
/**
* If provided, the callback will be passed each resource URL before a
* request is sent. The callback may return the original URL, or a new URL to
* override loading behavior. This behavior can be used to load assets from
* .ZIP files, drag-and-drop APIs, and Data URIs.
*
* ```js
* const blobs = {'fish.gltf': blob1, 'diffuse.png': blob2, 'normal.png': blob3};
*
* const manager = new THREE.LoadingManager();
*
* // Initialize loading manager with URL callback.
* const objectURLs = [];
* manager.setURLModifier( ( url ) => {
*
* url = URL.createObjectURL( blobs[ url ] );
* objectURLs.push( url );
* return url;
*
* } );
*
* // Load as usual, then revoke the blob URLs.
* const loader = new GLTFLoader( manager );
* loader.load( 'fish.gltf', (gltf) => {
*
* scene.add( gltf.scene );
* objectURLs.forEach( ( url ) => URL.revokeObjectURL( url ) );
*
* } );
* ```
*
* @param {function(string):string} transform - URL modifier callback. Called with an URL and must return a resolved URL.
* @return {LoadingManager} A reference to this loading manager.
*/
this.setURLModifier = function ( transform ) {
urlModifier = transform;
return this;
};
/**
* Registers a loader with the given regular expression. Can be used to
* define what loader should be used in order to load specific files. A
* typical use case is to overwrite the default loader for textures.
*
* ```js
* // add handler for TGA textures
* manager.addHandler( /\.tga$/i, new TGALoader() );
* ```
*
* @param {string} regex - A regular expression.
* @param {Loader} loader - A loader that should handle matched cases.
* @return {LoadingManager} A reference to this loading manager.
*/
this.addHandler = function ( regex, loader ) {
handlers.push( regex, loader );
return this;
};
/**
* Removes the loader for the given regular expression.
*
* @param {string} regex - A regular expression.
* @return {LoadingManager} A reference to this loading manager.
*/
this.removeHandler = function ( regex ) {
const index = handlers.indexOf( regex );
if ( index !== - 1 ) {
handlers.splice( index, 2 );
}
return this;
};
/**
* Can be used to retrieve the registered loader for the given file path.
*
* @param {string} file - The file path.
* @return {?Loader} The registered loader. Returns `null` if no loader was found.
*/
this.getHandler = function ( file ) {
for ( let i = 0, l = handlers.length; i < l; i += 2 ) {
const regex = handlers[ i ];
const loader = handlers[ i + 1 ];
if ( regex.global ) regex.lastIndex = 0; // see #17920
if ( regex.test( file ) ) {
return loader;
}
}
return null;
};
/**
* Can be used to abort ongoing loading requests in loaders using this manager.
* The abort only works if the loaders implement {@link Loader#abort} and `AbortSignal.any()`
* is supported in the browser.
*
* @return {LoadingManager} A reference to this loading manager.
*/
this.abort = function () {
this.abortController.abort();
this._abortController = null;
return this;
};
}
// TODO: Revert this back to a single member variable once this issue has been fixed
// https://github.com/cloudflare/workerd/issues/3657
/**
* Used for aborting ongoing requests in loaders using this manager.
*
* @type {AbortController}
*/
get abortController() {
if ( ! this._abortController ) {
this._abortController = new AbortController();
}
return this._abortController;
}
}
/**
* The global default loading manager.
*
* @constant
* @type {LoadingManager}
*/
const DefaultLoadingManager = /*@__PURE__*/ new LoadingManager();
export { DefaultLoadingManager, LoadingManager };
+439
View File
@@ -0,0 +1,439 @@
import { Color } from '../math/Color.js';
import { Vector2 } from '../math/Vector2.js';
import { Vector3 } from '../math/Vector3.js';
import { Vector4 } from '../math/Vector4.js';
import { Matrix3 } from '../math/Matrix3.js';
import { Matrix4 } from '../math/Matrix4.js';
import { FileLoader } from './FileLoader.js';
import { Loader } from './Loader.js';
import {
ShadowMaterial,
SpriteMaterial,
RawShaderMaterial,
ShaderMaterial,
PointsMaterial,
MeshPhysicalMaterial,
MeshStandardMaterial,
MeshPhongMaterial,
MeshToonMaterial,
MeshNormalMaterial,
MeshLambertMaterial,
MeshDepthMaterial,
MeshDistanceMaterial,
MeshBasicMaterial,
MeshMatcapMaterial,
LineDashedMaterial,
LineBasicMaterial,
Material,
} from '../materials/Materials.js';
import { error, warn } from '../utils.js';
/**
* Class for loading materials. The files are internally
* loaded via {@link FileLoader}.
*
* ```js
* const loader = new THREE.MaterialLoader();
* const material = await loader.loadAsync( 'material.json' );
* ```
* This loader does not support node materials. Use {@link NodeMaterialLoader} instead.
*
* @augments Loader
*/
class MaterialLoader extends Loader {
/**
* Constructs a new material loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
/**
* A dictionary holding textures used by the material.
*
* @type {Object<string,Texture>}
*/
this.textures = {};
}
/**
* Starts loading from the given URL and pass the loaded material to the `onLoad()` callback.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(Material)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Executed while the loading is in progress.
* @param {onErrorCallback} onError - Executed when errors occur.
*/
load( url, onLoad, onProgress, onError ) {
const scope = this;
const loader = new FileLoader( scope.manager );
loader.setPath( scope.path );
loader.setRequestHeader( scope.requestHeader );
loader.setWithCredentials( scope.withCredentials );
loader.load( url, function ( text ) {
try {
onLoad( scope.parse( JSON.parse( text ) ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
error( e );
}
scope.manager.itemError( url );
}
}, onProgress, onError );
}
/**
* Parses the given JSON object and returns a material.
*
* @param {Object} json - The serialized material.
* @return {Material} The parsed material.
*/
parse( json ) {
const textures = this.textures;
function getTexture( name ) {
if ( textures[ name ] === undefined ) {
warn( 'MaterialLoader: Undefined texture', name );
}
return textures[ name ];
}
const material = this.createMaterialFromType( json.type );
if ( json.uuid !== undefined ) material.uuid = json.uuid;
if ( json.name !== undefined ) material.name = json.name;
if ( json.color !== undefined && material.color !== undefined ) material.color.setHex( json.color );
if ( json.roughness !== undefined ) material.roughness = json.roughness;
if ( json.metalness !== undefined ) material.metalness = json.metalness;
if ( json.sheen !== undefined ) material.sheen = json.sheen;
if ( json.sheenColor !== undefined ) material.sheenColor = new Color().setHex( json.sheenColor );
if ( json.sheenRoughness !== undefined ) material.sheenRoughness = json.sheenRoughness;
if ( json.emissive !== undefined && material.emissive !== undefined ) material.emissive.setHex( json.emissive );
if ( json.specular !== undefined && material.specular !== undefined ) material.specular.setHex( json.specular );
if ( json.specularIntensity !== undefined ) material.specularIntensity = json.specularIntensity;
if ( json.specularColor !== undefined && material.specularColor !== undefined ) material.specularColor.setHex( json.specularColor );
if ( json.shininess !== undefined ) material.shininess = json.shininess;
if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat;
if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness;
if ( json.dispersion !== undefined ) material.dispersion = json.dispersion;
if ( json.iridescence !== undefined ) material.iridescence = json.iridescence;
if ( json.iridescenceIOR !== undefined ) material.iridescenceIOR = json.iridescenceIOR;
if ( json.iridescenceThicknessRange !== undefined ) material.iridescenceThicknessRange = json.iridescenceThicknessRange;
if ( json.transmission !== undefined ) material.transmission = json.transmission;
if ( json.thickness !== undefined ) material.thickness = json.thickness;
if ( json.attenuationDistance !== undefined ) material.attenuationDistance = json.attenuationDistance;
if ( json.attenuationColor !== undefined && material.attenuationColor !== undefined ) material.attenuationColor.setHex( json.attenuationColor );
if ( json.anisotropy !== undefined ) material.anisotropy = json.anisotropy;
if ( json.anisotropyRotation !== undefined ) material.anisotropyRotation = json.anisotropyRotation;
if ( json.fog !== undefined ) material.fog = json.fog;
if ( json.flatShading !== undefined ) material.flatShading = json.flatShading;
if ( json.blending !== undefined ) material.blending = json.blending;
if ( json.combine !== undefined ) material.combine = json.combine;
if ( json.side !== undefined ) material.side = json.side;
if ( json.shadowSide !== undefined ) material.shadowSide = json.shadowSide;
if ( json.opacity !== undefined ) material.opacity = json.opacity;
if ( json.transparent !== undefined ) material.transparent = json.transparent;
if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;
if ( json.alphaHash !== undefined ) material.alphaHash = json.alphaHash;
if ( json.depthFunc !== undefined ) material.depthFunc = json.depthFunc;
if ( json.depthTest !== undefined ) material.depthTest = json.depthTest;
if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;
if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite;
if ( json.blendSrc !== undefined ) material.blendSrc = json.blendSrc;
if ( json.blendDst !== undefined ) material.blendDst = json.blendDst;
if ( json.blendEquation !== undefined ) material.blendEquation = json.blendEquation;
if ( json.blendSrcAlpha !== undefined ) material.blendSrcAlpha = json.blendSrcAlpha;
if ( json.blendDstAlpha !== undefined ) material.blendDstAlpha = json.blendDstAlpha;
if ( json.blendEquationAlpha !== undefined ) material.blendEquationAlpha = json.blendEquationAlpha;
if ( json.blendColor !== undefined && material.blendColor !== undefined ) material.blendColor.setHex( json.blendColor );
if ( json.blendAlpha !== undefined ) material.blendAlpha = json.blendAlpha;
if ( json.stencilWriteMask !== undefined ) material.stencilWriteMask = json.stencilWriteMask;
if ( json.stencilFunc !== undefined ) material.stencilFunc = json.stencilFunc;
if ( json.stencilRef !== undefined ) material.stencilRef = json.stencilRef;
if ( json.stencilFuncMask !== undefined ) material.stencilFuncMask = json.stencilFuncMask;
if ( json.stencilFail !== undefined ) material.stencilFail = json.stencilFail;
if ( json.stencilZFail !== undefined ) material.stencilZFail = json.stencilZFail;
if ( json.stencilZPass !== undefined ) material.stencilZPass = json.stencilZPass;
if ( json.stencilWrite !== undefined ) material.stencilWrite = json.stencilWrite;
if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;
if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;
if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;
if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;
if ( json.rotation !== undefined ) material.rotation = json.rotation;
if ( json.linewidth !== undefined ) material.linewidth = json.linewidth;
if ( json.dashSize !== undefined ) material.dashSize = json.dashSize;
if ( json.gapSize !== undefined ) material.gapSize = json.gapSize;
if ( json.scale !== undefined ) material.scale = json.scale;
if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset;
if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor;
if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits;
if ( json.dithering !== undefined ) material.dithering = json.dithering;
if ( json.alphaToCoverage !== undefined ) material.alphaToCoverage = json.alphaToCoverage;
if ( json.premultipliedAlpha !== undefined ) material.premultipliedAlpha = json.premultipliedAlpha;
if ( json.forceSinglePass !== undefined ) material.forceSinglePass = json.forceSinglePass;
if ( json.allowOverride !== undefined ) material.allowOverride = json.allowOverride;
if ( json.visible !== undefined ) material.visible = json.visible;
if ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped;
if ( json.userData !== undefined ) material.userData = json.userData;
if ( json.vertexColors !== undefined ) {
if ( typeof json.vertexColors === 'number' ) {
material.vertexColors = ( json.vertexColors > 0 ) ? true : false;
} else {
material.vertexColors = json.vertexColors;
}
}
// Shader Material
if ( json.uniforms !== undefined ) {
for ( const name in json.uniforms ) {
const uniform = json.uniforms[ name ];
material.uniforms[ name ] = {};
switch ( uniform.type ) {
case 't':
material.uniforms[ name ].value = getTexture( uniform.value );
break;
case 'c':
material.uniforms[ name ].value = new Color().setHex( uniform.value );
break;
case 'v2':
material.uniforms[ name ].value = new Vector2().fromArray( uniform.value );
break;
case 'v3':
material.uniforms[ name ].value = new Vector3().fromArray( uniform.value );
break;
case 'v4':
material.uniforms[ name ].value = new Vector4().fromArray( uniform.value );
break;
case 'm3':
material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value );
break;
case 'm4':
material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value );
break;
default:
material.uniforms[ name ].value = uniform.value;
}
}
}
if ( json.defines !== undefined ) material.defines = json.defines;
if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;
if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;
if ( json.glslVersion !== undefined ) material.glslVersion = json.glslVersion;
if ( json.extensions !== undefined ) {
for ( const key in json.extensions ) {
material.extensions[ key ] = json.extensions[ key ];
}
}
if ( json.lights !== undefined ) material.lights = json.lights;
if ( json.clipping !== undefined ) material.clipping = json.clipping;
// for PointsMaterial
if ( json.size !== undefined ) material.size = json.size;
if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;
// maps
if ( json.map !== undefined ) material.map = getTexture( json.map );
if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap );
if ( json.alphaMap !== undefined ) material.alphaMap = getTexture( json.alphaMap );
if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap );
if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;
if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap );
if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType;
if ( json.normalScale !== undefined ) {
let normalScale = json.normalScale;
if ( Array.isArray( normalScale ) === false ) {
// Blender exporter used to export a scalar. See #7459
normalScale = [ normalScale, normalScale ];
}
material.normalScale = new Vector2().fromArray( normalScale );
}
if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap );
if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;
if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;
if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap );
if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap );
if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap );
if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;
if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );
if ( json.specularIntensityMap !== undefined ) material.specularIntensityMap = getTexture( json.specularIntensityMap );
if ( json.specularColorMap !== undefined ) material.specularColorMap = getTexture( json.specularColorMap );
if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );
if ( json.envMapRotation !== undefined ) material.envMapRotation.fromArray( json.envMapRotation );
if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity;
if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity;
if ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio;
if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap );
if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;
if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap );
if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;
if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap );
if ( json.clearcoatMap !== undefined ) material.clearcoatMap = getTexture( json.clearcoatMap );
if ( json.clearcoatRoughnessMap !== undefined ) material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap );
if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap );
if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale );
if ( json.iridescenceMap !== undefined ) material.iridescenceMap = getTexture( json.iridescenceMap );
if ( json.iridescenceThicknessMap !== undefined ) material.iridescenceThicknessMap = getTexture( json.iridescenceThicknessMap );
if ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap );
if ( json.thicknessMap !== undefined ) material.thicknessMap = getTexture( json.thicknessMap );
if ( json.anisotropyMap !== undefined ) material.anisotropyMap = getTexture( json.anisotropyMap );
if ( json.sheenColorMap !== undefined ) material.sheenColorMap = getTexture( json.sheenColorMap );
if ( json.sheenRoughnessMap !== undefined ) material.sheenRoughnessMap = getTexture( json.sheenRoughnessMap );
return material;
}
/**
* Textures are not embedded in the material JSON so they have
* to be injected before the loading process starts.
*
* @param {Object} value - A dictionary holding textures for material properties.
* @return {MaterialLoader} A reference to this material loader.
*/
setTextures( value ) {
this.textures = value;
return this;
}
/**
* Creates a material for the given type.
*
* @param {string} type - The material type.
* @return {Material} The new material.
*/
createMaterialFromType( type ) {
return MaterialLoader.createMaterialFromType( type );
}
/**
* Creates a material for the given type.
*
* @static
* @param {string} type - The material type.
* @return {Material} The new material.
*/
static createMaterialFromType( type ) {
const materialLib = {
ShadowMaterial,
SpriteMaterial,
RawShaderMaterial,
ShaderMaterial,
PointsMaterial,
MeshPhysicalMaterial,
MeshStandardMaterial,
MeshPhongMaterial,
MeshToonMaterial,
MeshNormalMaterial,
MeshLambertMaterial,
MeshDepthMaterial,
MeshDistanceMaterial,
MeshBasicMaterial,
MeshMatcapMaterial,
LineDashedMaterial,
LineBasicMaterial,
Material
};
return new materialLib[ type ]();
}
}
export { MaterialLoader };
+1266
View File
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
import { ImageLoader } from './ImageLoader.js';
import { Texture } from '../textures/Texture.js';
import { Loader } from './Loader.js';
/**
* Class for loading textures. Images are internally
* loaded via {@link ImageLoader}.
*
* ```js
* const loader = new THREE.TextureLoader();
* const texture = await loader.loadAsync( 'textures/land_ocean_ice_cloud_2048.jpg' );
*
* const material = new THREE.MeshBasicMaterial( { map:texture } );
* ```
* Please note that `TextureLoader` has dropped support for progress
* events in `r84`. For a `TextureLoader` that supports progress events, see
* [this thread](https://github.com/mrdoob/three.js/issues/10439#issuecomment-293260145).
*
* @augments Loader
*/
class TextureLoader extends Loader {
/**
* Constructs a new texture loader.
*
* @param {LoadingManager} [manager] - The loading manager.
*/
constructor( manager ) {
super( manager );
}
/**
* Starts loading from the given URL and pass the fully loaded texture
* to the `onLoad()` callback. The method also returns a new texture object which can
* directly be used for material creation. If you do it this way, the texture
* may pop up in your scene once the respective loading process is finished.
*
* @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
* @param {function(Texture)} onLoad - Executed when the loading process has been finished.
* @param {onProgressCallback} onProgress - Unsupported in this loader.
* @param {onErrorCallback} onError - Executed when errors occur.
* @return {Texture} The texture.
*/
load( url, onLoad, onProgress, onError ) {
const texture = new Texture();
const loader = new ImageLoader( this.manager );
loader.setCrossOrigin( this.crossOrigin );
loader.setPath( this.path );
loader.load( url, function ( image ) {
texture.image = image;
texture.needsUpdate = true;
if ( onLoad !== undefined ) {
onLoad( texture );
}
}, onProgress, onError );
return texture;
}
}
export { TextureLoader };
+194
View File
@@ -0,0 +1,194 @@
import { float } from '../../nodes/tsl/TSLBase.js';
import { Loader } from '../Loader.js';
import { FileLoader } from '../../loaders/FileLoader.js';
import { error } from '../../utils.js';
/**
* A loader for loading node objects in the three.js JSON Object/Scene format.
*
* @augments Loader
*/
class NodeLoader extends Loader {
/**
* Constructs a new node loader.
*
* @param {LoadingManager} [manager] - A reference to a loading manager.
*/
constructor( manager ) {
super( manager );
/**
* Represents a dictionary of textures.
*
* @type {Object<string,Texture>}
*/
this.textures = {};
/**
* Represents a dictionary of node types.
*
* @type {Object<string,Node.constructor>}
*/
this.nodes = {};
}
/**
* Loads the node definitions from the given URL.
*
* @param {string} url - The path/URL of the file to be loaded.
* @param {Function} onLoad - Will be called when load completes.
* @param {Function} onProgress - Will be called while load progresses.
* @param {Function} onError - Will be called when errors are thrown during the loading process.
*/
load( url, onLoad, onProgress, onError ) {
const loader = new FileLoader( this.manager );
loader.setPath( this.path );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, ( text ) => {
try {
onLoad( this.parse( JSON.parse( text ) ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
error( e );
}
this.manager.itemError( url );
}
}, onProgress, onError );
}
/**
* Parse the node dependencies for the loaded node.
*
* @param {Array<Object>} [json] - The JSON definition
* @return {Object<string,Node>} A dictionary with node dependencies.
*/
parseNodes( json ) {
const nodes = {};
if ( json !== undefined ) {
for ( const nodeJSON of json ) {
const { uuid, type } = nodeJSON;
nodes[ uuid ] = this.createNodeFromType( type );
nodes[ uuid ].uuid = uuid;
}
const meta = { nodes, textures: this.textures };
for ( const nodeJSON of json ) {
nodeJSON.meta = meta;
const node = nodes[ nodeJSON.uuid ];
node.deserialize( nodeJSON );
delete nodeJSON.meta;
}
}
return nodes;
}
/**
* Parses the node from the given JSON.
*
* @param {Object} json - The JSON definition
* @param {string} json.type - The node type.
* @param {string} json.uuid - The node UUID.
* @param {Array<Object>} [json.nodes] - The node dependencies.
* @param {Object} [json.meta] - The meta data.
* @return {Node} The parsed node.
*/
parse( json ) {
const node = this.createNodeFromType( json.type );
node.uuid = json.uuid;
const nodes = this.parseNodes( json.nodes );
const meta = { nodes, textures: this.textures };
json.meta = meta;
node.deserialize( json );
delete json.meta;
return node;
}
/**
* Defines the dictionary of textures.
*
* @param {Object<string,Texture>} value - The texture library defines as `<uuid,texture>`.
* @return {NodeLoader} A reference to this loader.
*/
setTextures( value ) {
this.textures = value;
return this;
}
/**
* Defines the dictionary of node types.
*
* @param {Object<string,Node.constructor>} value - The node library defined as `<classname,class>`.
* @return {NodeLoader} A reference to this loader.
*/
setNodes( value ) {
this.nodes = value;
return this;
}
/**
* Creates a node object from the given type.
*
* @param {string} type - The node type.
* @return {Node} The created node instance.
*/
createNodeFromType( type ) {
if ( this.nodes[ type ] === undefined ) {
error( 'NodeLoader: Node type not found:', type );
return float();
}
return new this.nodes[ type ]();
}
}
export default NodeLoader;
+108
View File
@@ -0,0 +1,108 @@
import { MaterialLoader } from '../../loaders/MaterialLoader.js';
/**
* A special type of material loader for loading node materials.
*
* @augments MaterialLoader
*/
class NodeMaterialLoader extends MaterialLoader {
/**
* Constructs a new node material loader.
*
* @param {LoadingManager} [manager] - A reference to a loading manager.
*/
constructor( manager ) {
super( manager );
/**
* Represents a dictionary of node types.
*
* @type {Object<string,Node.constructor>}
*/
this.nodes = {};
/**
* Represents a dictionary of node material types.
*
* @type {Object<string,NodeMaterial.constructor>}
*/
this.nodeMaterials = {};
}
/**
* Parses the node material from the given JSON.
*
* @param {Object} json - The JSON definition
* @return {NodeMaterial}. The parsed material.
*/
parse( json ) {
const material = super.parse( json );
const nodes = this.nodes;
const inputNodes = json.inputNodes;
for ( const property in inputNodes ) {
const uuid = inputNodes[ property ];
material[ property ] = nodes[ uuid ];
}
return material;
}
/**
* Defines the dictionary of node types.
*
* @param {Object<string,Node.constructor>} value - The node library defined as `<classname,class>`.
* @return {NodeLoader} A reference to this loader.
*/
setNodes( value ) {
this.nodes = value;
return this;
}
/**
* Defines the dictionary of node material types.
*
* @param {Object<string,NodeMaterial.constructor>} value - The node material library defined as `<classname,class>`.
* @return {NodeLoader} A reference to this loader.
*/
setNodeMaterials( value ) {
this.nodeMaterials = value;
return this;
}
/**
* Creates a node material from the given type.
*
* @param {string} type - The node material type.
* @return {Node} The created node material instance.
*/
createMaterialFromType( type ) {
const materialClass = this.nodeMaterials[ type ];
if ( materialClass !== undefined ) {
return new materialClass();
}
return super.createMaterialFromType( type );
}
}
export default NodeMaterialLoader;
+151
View File
@@ -0,0 +1,151 @@
import NodeLoader from './NodeLoader.js';
import NodeMaterialLoader from './NodeMaterialLoader.js';
import { ObjectLoader } from '../../loaders/ObjectLoader.js';
/**
* A special type of object loader for loading 3D objects using
* node materials.
*
* @augments ObjectLoader
*/
class NodeObjectLoader extends ObjectLoader {
/**
* Constructs a new node object loader.
*
* @param {LoadingManager} [manager] - A reference to a loading manager.
*/
constructor( manager ) {
super( manager );
/**
* Represents a dictionary of node types.
*
* @type {Object<string,Node.constructor>}
*/
this.nodes = {};
/**
* Represents a dictionary of node material types.
*
* @type {Object<string,NodeMaterial.constructor>}
*/
this.nodeMaterials = {};
/**
* A reference to hold the `nodes` JSON property.
*
* @private
* @type {?Object[]}
*/
this._nodesJSON = null;
}
/**
* Defines the dictionary of node types.
*
* @param {Object<string,Node.constructor>} value - The node library defined as `<classname,class>`.
* @return {NodeObjectLoader} A reference to this loader.
*/
setNodes( value ) {
this.nodes = value;
return this;
}
/**
* Defines the dictionary of node material types.
*
* @param {Object<string,NodeMaterial.constructor>} value - The node material library defined as `<classname,class>`.
* @return {NodeObjectLoader} A reference to this loader.
*/
setNodeMaterials( value ) {
this.nodeMaterials = value;
return this;
}
/**
* Parses the node objects from the given JSON.
*
* @param {Object} json - The JSON definition
* @param {Function} onLoad - The onLoad callback function.
* @return {Object3D}. The parsed 3D object.
*/
parse( json, onLoad ) {
this._nodesJSON = json.nodes;
const data = super.parse( json, onLoad );
this._nodesJSON = null; // dispose
return data;
}
/**
* Parses the node objects from the given JSON and textures.
*
* @param {Object[]} json - The JSON definition
* @param {Object<string,Texture>} textures - The texture library.
* @return {Object<string,Node>}. The parsed nodes.
*/
parseNodes( json, textures ) {
if ( json !== undefined ) {
const loader = new NodeLoader();
loader.setNodes( this.nodes );
loader.setTextures( textures );
return loader.parseNodes( json );
}
return {};
}
/**
* Parses the node objects from the given JSON and textures.
*
* @param {Object} json - The JSON definition
* @param {Object<string,Texture>} textures - The texture library.
* @return {Object<string,NodeMaterial>}. The parsed materials.
*/
parseMaterials( json, textures ) {
const materials = {};
if ( json !== undefined ) {
const nodes = this.parseNodes( this._nodesJSON, textures );
const loader = new NodeMaterialLoader();
loader.setTextures( textures );
loader.setNodes( nodes );
loader.setNodeMaterials( this.nodeMaterials );
for ( let i = 0, l = json.length; i < l; i ++ ) {
const data = json[ i ];
materials[ data.uuid ] = loader.parse( data );
}
}
return materials;
}
}
export default NodeObjectLoader;