Reputation: 990
I have a large amount of textures and models to load into my project. I am trying to show a progress bars while everything is loading. I think the LoadingManager does just what I need as it tracks the progress of all loaded assets.
I'm using the JSONLoader and TextureLoader.
If anyone could show me how to do implement this in the example code would be awesome.
var camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 400;
scene = new THREE.Scene();
// Load Textures
var modeltexture1 = new THREE.TextureLoader().load('modeltexture1.jpg');
var modeltexture2 = new THREE.TextureLoader().load('modeltexture2.jpg');
var modeltexture3 = new THREE.TextureLoader().load('modeltexture3.jpg');
var modeltexture4 = new THREE.TextureLoader().load('modeltexture4.jpg');
// Materials
var material = {
"texture1": new THREE.MeshPhongMaterial({ map: modeltexture1 }),
"texture2": new THREE.MeshPhongMaterial({ map: modeltexture2 }),
"texture3": new THREE.MeshPhongMaterial({ map: modeltexture3 }),
"texture4": new THREE.MeshPhongMaterial({ map: modeltexture4 }),
}
// Lights
scene.add(new THREE.AmbientLight(0xcccccc));
pointLight = new THREE.PointLight(0xff4400, 5, 30);
pointLight.position.set(5, 0, 0);
scene.add(pointLight);
var loader = new THREE.JSONLoader();
// Model1
var model1 = new THREE.Object3D;
scene.add(model1);
loader.load("model1.js", function(geometry) {
var mainmodel1 = new THREE.Mesh(geometry, material["texture1"]);
model1.add(mainmodel1);
});
// Model2
var model2 = new THREE.Object3D;
scene.add(model2);
loader.load("model2.js", function(geometry) {
var mainmodel2 = new THREE.Mesh(geometry, material["texture2"]);
model2.add(mainmodel2);
});
// Model3
var model3 = new THREE.Object3D;
scene.add(model3);
loader.load("model3.js", function(geometry) {
var mainmodel3 = new THREE.Mesh(geometry, material["texture3"]);
model3.add(mainmodel3);
});
// Model4
var model4 = new THREE.Object3D;
scene.add(model4);
loader.load("model4.js", function(geometry) {
var mainmodel4 = new THREE.Mesh(geometry, material["texture4"]);
model4.add(mainmodel4);
});
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>
Upvotes: 21
Views: 27151
Reputation: 17350
##LoadingManager Update
as @2pha points out, the below is pointless as THREE already implements a loading Manager - the below will remain for posterity, but have a look at this:
https://threejs.org/docs/?q=loading#api/loaders/managers/LoadingManager
Easy-peasy:
var progress = document.createElement('div');
var progressBar = document.createElement('div');
progress.appendChild(progressBar);
document.body.appendChild(progress);
var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {
progressBar.style.width = (loaded / total * 100) + '%';
};
function addRandomPlaceHoldItImage(){
var r = Math.round(Math.random() * 4000);
new THREE.ImageLoader(manager).load('//picsum.photos/' + r + '/' + r);
}
for(var i = 0; i < 10; i++) addRandomPlaceHoldItImage();
div {
width: 200px;
height: 20px;
background: #000;
border: 2px solid #000;
}
div > div {
width: 200px;
height: 20px;
background: red;
border: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>
Something like the below should do your trick - mind that I have just quickly tested it and there is probably a couple of improvements to make (including using the progress
callback in THREE js). I haven't fully refined it either, but it gives you a general implementation of how to do this. I also haven't implemented an error handler, etc... Those are things that take a bit longer to make, but you get the general idea.
You add new types of loaders (that conform to the type + Loader
in THREE) and their sources, then initiating the load by simply calling load
.
Hope it helps.
(I also made a random function to get some random images from placehold.it
since using the same results over and over means that caching kills the server.)
function LoaderProgress(callback){
this.length = -1;
this.complete = 0;
this.domElement = document.createElement('div');
this.progress = document.createElement('div');
this.domElement.appendChild(this.progress);
this.domElement.className = 'loader-progress-wrapper';
this.progress.className = 'loader-progress-progress';
this.progress.style.width = '0%';
this.callback = callback || function(){};
}
LoaderProgress.prototype.add = function(type, src){
var result = this[++this.length] = {
loader: new THREE[type + 'Loader'],
complete: false,
source: src
};
return result;
}
LoaderProgress.prototype.load = function(){
this.complete = 0;
for(var i = 0; i < this.length; i++){
var current = this[i];
if(!current.complete){
current.loader.load(this[i].source, function(result){
current.complete = result;
this.complete++;
this.update();
}.bind(this));
}
}
return this;
}
LoaderProgress.prototype.update = function(a){
var progress = this.complete / this.length;
this.progress.style.width = (progress * 100) + '%';
if(progress === 1) this.callback(this);
console.log(progress);
return progress;
}
var loader = new LoaderProgress(function(){
// Execute code that needfs the loaded elements here
alert('All are loaded');
});
document.body.appendChild(loader.domElement);
function addRandomPlaceHoldItImage(){
var r = Math.round(Math.random() * 4000);
loader.add('Image', '//picsum.photos/' + r + '/' + r);
}
for(var i = 0; i < 10; i++)
addRandomPlaceHoldItImage();
loader.load();
.loader-progress-wrapper {
width: 200px;
height: 20px;
background: #000;
border: 2px solid #000;
}
.loader-progress-progress {
width: 200px;
height: 20px;
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>
Upvotes: 21
Reputation: 13223
Docs: http://threejs.org/docs/#Reference/Loaders/TextureLoader
Example: http://threejs.org/examples/#webgl_loader_obj
Code:
var sphereMaterial = new THREE.MeshBasicMaterial();
var onProgress = function ( xhr ) {
if ( xhr.lengthComputable ) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log( Math.round(percentComplete, 2) + '% downloaded' );
}
};
var loader = new THREE.TextureLoader();
var texture = loader.load("___4MB_IMAGE.JPG___", undefined, onProgress);
sphereMaterial.map = texture;
It solves a similar problem I had - 4MB texture that takes some time to load over the wire...
Upvotes: 5