-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
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
Loader Improvements #4248
Comments
It gets used in the examples. Altough I agree it needs some improvements. It would be best if the manager had a simple default loading DOM. |
Sounds good to me 👍 |
How crazy would it be to look at using promises for loaders and the loading manager? I have a bunch of code going down this path and I'm wondering if it's worth pursuing. I'm not sure about dependencies and getting something that could be a self-contained promise implementation for use inside the three.js codebase. Promises seem to make things work out nice at the end of it. The end user would then have an onLoad callback, and a promise that could be used to interact with. |
I've yet to understand promises. Do you know of a page doing a good explanation. At the moment, I don't understand the difference between callbacks and promises. |
http://www.html5rocks.com/en/tutorials/es6/promises/ Essentially you can pass promises around, and you don't really have to care specifically when they are resolved. So you can load external shaders, images, models, etc, then use a promise to determine when to start the execution of your game logic or whatever else, only when the promised results have been resolved. It also helps get rid of callback hell and makes code easier to read since callbacks don't have to be crazily nested. It also makes error handling happen in a much more logical order. A bunch of examples using Q: https://github.com/kriskowal/q/wiki/Examples-Gallery |
Yeah, I think you're right about it being too opinionated a topic. I wrote some code going down this path, and left it for awhile. I'll just use the q-wrap in my own stuff. That looks like a handy tool. It seems like it would be trivial to implement. |
TIL: Promises. Thanks for sharing at least. I see the appeal but because of the work I do I have to think about backwards compatibility and that companies I deal with, or their clients, may not have the most updated browsers (in fact they are normally at least a year behind) so I would be concerned about depending on a native implementation of Promises being present. |
|
Isn't too hard to switch to promises in the calling code, using the existing callback system. atm we use https://github.com/taylorhakes/promise-polyfill as a promise polyfill as its tiny Wrap our texture loads in promises, with caching and cpu-side dumping post gpu load something like this: var texturePromises = {};
THREE.ImageUtils.crossOrigin = "anonymous";
function SetTextureAsync( path, wrapping, uniform ) {
var promise;
var hashPath = path + '|' + wrapping;
if ( texturePromises[hashPath] ) {
promise = texturePromises[hashPath];
} else {
promise = LoadTextureAsync( path );
texturePromises[hashPath] = promise;
}
return promise.then( updateLoadStatus )
.then( function ( texture ) {
texture.wrapS = texture.wrapT = wrapping;
uniform.value = texture;
} );
}
function SetCompressedTextureAsync( path, wrapping, uniform ) {
var promise;
var hashPath = path + '|' + wrapping;
if ( texturePromises[hashPath] ) {
promise = texturePromises[hashPath];
} else {
promise = LoadCompressedTextureAsync( path );
texturePromises[hashPath] = promise;
}
return promise.then( updateLoadStatus )
.then( function ( texture ) {
texture.wrapS = texture.wrapT = wrapping;
uniform.value = texture;
} );
}
var LoadTextureAsync = ( function () {
function ReleaseTexture( texture ) {
texture.image = null;
if ( texture.mipmaps ) {
texture.mipmaps = null;
}
texture.onUpdate = undefined;
}
return function LoadTextureAsync( texturePath ) {
return new Promise( function ( resolve, reject ) {
var texture = THREE.ImageUtils.loadTexture( pathTextures + texturePath, undefined, resolve, reject );
console.log( "* Loading " + texturePath );
texture.onUpdate = ReleaseTexture;
} );
}
} )();
var LoadCompressedTextureAsync = ( function () {
var ddsLoader = new THREE.DDSLoader();
function ReleaseTexture( texture ) {
if ( texture.mipmaps ) {
texture.image = null;
texture.mipmaps = null;
}
texture.onUpdate = undefined;
}
return function LoadCompressedTextureAsync( texturePath ) {
return new Promise( function ( resolve, reject ) {
var texture = ddsLoader.load( pathTextures + texturePath + '.dds' + ext, resolve, undefined, reject );
console.log( "* Loading " + texturePath + '.dds' + ext );
texture.onUpdate = ReleaseTexture;
} );
}
} )(); Have an item that holds all the start up promises: var startupResources = []; Create a bunch of texture uniforms and load the promises into the var textures = {};
function downloadTextures() {
textures["tileTexture"] = { type: "t", value: null };
startupResources.push( SetCompressedTextureAsync( "terrain_r",
THREE.ClampToEdgeWrapping, textures["tileTexture"] ) );
...
} Have an
function animate() {
if ( loadStage === loadStages.Running ) {
render();
} else if ( loadStage === loadStages.Loading ) {
tickLoader();
requestAnimationFrame( animate );
}
} In the We then use World.init = function init() {
if ( setupRenderer() !== true ) return false;
downloadExtraData();
downloadShaders();
downloadTextures();
animate();
startWorker();
Promise.all( startupResources )
.then( function () {
needsResize = true;
startupResources = texturePromises = undefined;
document.getElementById( "loadingText" ).style.display = "none";
document.getElementById( "progressBar" ).style.display = "none";
document.getElementById( "logoLoading" ).classList.add( "disappear" );
document.getElementById("topbar").classList.remove( "off" );
setTimeout( function () {
document.getElementById( "logoLoading" ).style.display = "none";
}, 3000 );
console.info( "All loaded" );
SetUpModels();
SetupUIEvents();
loadStage = loadStages.Running;
} )
.catch( function ( e ) {
console.error( "Error", e );
loadStage = loadStages.Errored;
} );
return true;
} |
If promises are too opinionated, perhaps loaders could at least use the common Node.js callback style i.e. Callback example: var loader = new THREE.ImageLoader();
loader.load('texture.png', function(error, image) {
if (error) {
// Do something with the error.
} else {
// Do something with the image.
}
}); Promisified example: var util = require('util');
var loader = new THREE.ImageLoader();
util.promisify(loader.load)('texture.png')
.then(function(image) {
// Do something with the image.
})
.catch(function(error) {
// Do something with the error.
}); |
Is there a spec for that? |
The closest I've found to a spec is this. And it is explained in the Node.js documentation here. It's just a Node.js convention, and pretty much every library that provides promisification (including ones that aren't tied to Node.js and work in the browser) follows this convention. You can read more about it here. |
I've been promisifying the current loaders for quite a while now without issues. I don't think that they need to be refactored for this. |
Hi all, One thing that springs to my mind regarding Promises, is that they allow for ES6 async / await style coding, which can help to simplify highly asynchronous code in many cases. |
THREE.Loader no longer has any DOM/status management code, so I think this issue can be closed? If we want to do anything different with promises vs callbacks that could be opened in a new issue. |
Yeah, sounds good. Let's close this one. |
I'm taking a crack at improving some of the loaders, making them a little more consistent in their usage, and then writing out the docs. I'll post anything I need feedback on here.
First thing:
THREE.Loader
has anaddStatusElement
method, which creates a DOM element that says "Loading ...". It doesn't add it to the DOM, update it, or do anything with it. I was thinking about removing that method as it doesn't appear to do anything, and is not used in the examples.It seems that if there is any DOM loading interface it should go in
THREE.LoadingManager
. In fact maybe a visual interface should be a separate class that is only in the examples.So my proposal is to remove that functionality, and if it gets put back in, put it into the examples. Thoughts?
The text was updated successfully, but these errors were encountered: