Skip to content

Instantly share code, notes, and snippets.

@gordonnl gordonnl/preload.js
Last active Aug 29, 2015

Embed
What would you like to do?
Preload module
/*
*
* Preload.js is a require module used to asynchronously load external assets.
* Upon completion a custom event is triggered, passing the loaded assets.
*
* Requires threejs, jquery and pubsub
*
* Can load:
* images: returns an htmlElement - ready to clone and drop or use directly.
* geometries: buffer geometries require an 'bfr' prefix in the filename,
* objects/scenes require an 'obj' prefix in the filename. Returns a
* three js object.
* shaders: returns a string.
* buffers: completely loaded and decoded, ready to be used for analysis/other.
* streams: loaded as an html media element - only canplay event guaranteed.
*
* Use:
*
* preloader({
* id: 'customName',
* update: true, // if true, update event fired after load of each file
* images: {
* imgName: 'src',
* },
* geometries: {
* geoName: 'src',
* bufferGeoName: 'src-including-bfr-prefix',
* objName: 'src-including-obj-prefix',
* sceneName: 'src-including-obj-prefix',
* },
* shaders: {
* shaderName: 'src',
* },
* buffers: {
* bufferName: 'src',
* },
* streams: {
* streamName: 'src',
* },
* });
*
* $.subscribe('preload-customName', function(e, assets) {
* console.log(assets.images.imgName);
* });
*
* $.subscribe('preload-update-customName', function(e, progress) {
* console.log(Math.round(progress * 100), 'percent loaded');
* });
*
*/
define([
'three',
'jquery',
'pubsub',
],
function () {
return function(input) {
var output = {};
var total = 0;
var loaded = 0;
var _name;
var isWebAudio = 'webkitAudioContext' in window || 'AudioContext' in window;
var tally = function() {
loaded++;
if (input.update) $.publish(input.id ? 'preload-update-' + input.id : 'preload-update', [loaded / total]);
if (loaded == total) {
// Once loaded, fire a custom event with all of the loaded assets
$.publish(input.id ? 'preload-' + input.id : 'preload', [output]);
}
};
// Preload images, if any, populate output.images with final images
if (input.images) {
output.images = {};
var loadImage = function(name, src) {
total++;
var img = new Image();
img.src = src;
img.onload = function() {
output.images[name] = img;
tally();
};
img.onerror = function(e) {
console.error('error loading image', name, e);
tally();
};
};
for (_name in input.images) {
loadImage(_name, input.images[_name]);
}
}
// Preload geometries, if any, populate output.geometries with final geometries
if (input.geometries) {
output.geometries = {};
var jsonLoader = new THREE.JSONLoader();
var objectLoader = new THREE.ObjectLoader();
var bufferGeoloader = new THREE.BufferGeometryLoader();
var loadGeometry = function(name, src) {
total++;
// If src has 'bfr' use buffer loader, else, json. e.g 'assets/models/bfrGeoTrack.js'
if (src.indexOf('bfr') !== -1) {
bufferGeoloader.load(
src,
function(geometry) {
output.geometries[name] = geometry;
tally();
},
function() {},
function(xhr) {
console.error('error loading geometry', name, xhr);
tally();
}
);
} else if (src.indexOf('obj') !== -1) {
objectLoader.load(
src,
function(geometry) {
output.geometries[name] = geometry;
tally();
},
function() {},
function(xhr) {
console.error('error loading scene', name, xhr);
tally();
}
);
} else {
// JSON loader has no error catch :(
jsonLoader.load(
src,
function(geometry) {
output.geometries[name] = geometry;
tally();
}
);
}
};
for (_name in input.geometries) {
loadGeometry(_name, input.geometries[_name]);
}
}
// Preload shaders, if any, populate output.shaders with loaded shaders
if (input.shaders) {
output.shaders = {};
var loadShader = function(name, src) {
total++;
var request = new XMLHttpRequest();
request.open('GET', src);
request.onload = function() {
output.shaders[name] = this.responseText;
tally();
};
request.onerror = function() {
console.error('error loading shader', name, this);
};
request.send();
};
for (_name in input.shaders) {
loadShader(_name, input.shaders[_name]);
}
}
// Preload buffers files, if any, populate output.buffers with loaded shaders
if (input.buffers) {
output.buffers = {};
// if no webaudio, don't charge the audio files
if (!isWebAudio) {
for (_name in input.buffers) {
output.buffers[_name] = 'no webAudio';
tally();
}
return;
}
var context = new AudioContext();
var loadBuffer = function(name, src) {
total++;
var request = new XMLHttpRequest();
request.open('GET', src, true);
request.responseType = 'arraybuffer';
request.onload = function() {
context.decodeAudioData(
request.response,
function(buffer) {
if (!buffer) {
console.error('error decoding buffer', name, this);
return;
}
output.buffers[name] = buffer;
tally();
},
function() {
console.error('error decoding buffer', name, this);
}
);
};
request.onerror = function() {
console.error('error loading buffer', name, this);
};
request.send();
};
for (_name in input.buffers) {
loadBuffer(_name, input.buffers[_name]);
}
}
// Preload stream audio files, if any, populate output.streams with loaded shaders
if (input.streams) {
output.streams = {};
// if no webaudio, don't charge the audio files
if (!isWebAudio) {
for (_name in input.buffers) {
output.buffers[_name] = 'no webAudio';
tally();
}
return;
}
var loadStream = function(name, src) {
total++;
var element = new Audio();
element.addEventListener('canplay', function() {
output.streams[name] = element;
tally();
});
element.addEventListener('error', function() {
console.error('error loading stream', name, this);
});
element.src = src;
};
for (_name in input.streams) {
loadStream(_name, input.streams[_name]);
}
}
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.