Hi again,
Sorry about the late response but I’m working on this project on and off. So what I’m trying to do, is to save a project from crashing due to loading multiple GLTF models.
The initial problem, which is now fixed due to using dracoLoader.setDecoderConfig({ type: 'js' });
, was that once I added many models to the scene, of the order of 30, the scene would load but some of the models (2 to 5), were missing from the scene. They would never load and the scene seemed as if the models disappeared. That problem is now fixed.
The new problem is that we want to be able to load up to say 80-90 GLTF models, each of which having a size of about ~1 Mb. We are able to load about 45 models with the current code, but then the browser crashes. The project has about 60 unique models, so in some of the scenes, we might be loading the same model multiple times. So a scene might contain for example 10 models of the kind A, 20 of the kind B, 30 of the kind C, and 20 of the kind D.
The way the program works right now is that we create an array called requestedObjects
, which is filled with the User’s input. The user selects the models he wants and their position in the scene.
So the requestedObjects
looks like this:
requestedObjects = [
{
'model': "modelA",
'folder': "modelA_folder",
'xPos3D': "142.00",
'yPos3D': "163.00",
'zPos': '0',
'rotation': 90
},
{
'model': "modelB",
'folder': "modelB_folder",
'xPos3D': '268',
'yPos3D': "163.00",
'zPos': '0',
'rotation': 90
}
]
Then we call this function:
const renderObjects = function () {
// render the objects from the array passed
requestedObjects.map((item, i) => {
console.log(`park width = ${parkWidth} & park height = ${parkHeight}`);
createGLTF(i, item.model, item.folder, item.xPos3D, item.yPos3D, item.zPos, item.rotation);
// getting dynamic position for the camera to see all items
if (Number(item.xPos3D) > cameraX) {
cameraX = Number(item.xPos3D)
}
if (Number(item.yPos3D) > cameraY) {
cameraY = Number(item.yPos3D)
}
// once all the objects are ready run the initial set up functions
if (i + 1 === requestedObjects.length) {
// update the preloader text for total number of objects
totalModels = requestedObjects.length
//document.querySelector('#jsTotalLoad').textContent = totalModels
container = document.querySelector('#scene-container');
scene = new THREE.Scene();
// change sky colour
scene.background = new THREE.Color(0xffffff);
// initial set up functions
createCamera((cameraX - 50), (cameraY - 50));
createControls();
createLights();
createRenderer();
createWater();
initSky();
if (gridHelp) {
displayFloorGrid();
}
// start the animation loop
renderer.setAnimationLoop(() => {
update();
render();
});
}
})
}
The functions initSky(), createWater()
, etc add effects to the scene and I don’t think they are causing an issue. I think the most relevant function is the one where the GLTF loading takes place:
// the function to create a new 3D object
function createGLTF(i, model, folder, xPos, yPos, zPos, rotation) {
const loadingManager = new THREE.LoadingManager( () => {
const loadingScreen = document.getElementById( 'loading-screen' );
loadingScreen.classList.add( 'fade-out' );
// optional: remove loader from DOM via event listener
loadingScreen.addEventListener( 'transitionend', onTransitionEnd );
}
);
loadingManager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
};
let loader = new THREE.GLTFLoader(loadingManager);
const dracoLoader = new THREE.DRACOLoader(loadingManager);
dracoLoader.setDecoderPath( 'js/vendor/draco/' );
dracoLoader.setDecoderConfig({ type: 'js' });
loader.setDRACOLoader( dracoLoader );
loader.load(
// resource URL
`model_lib/${folder}/${model}.gltf`,
// called when resource is loaded
function (object) {
const aabb = new THREE.Box3().setFromObject(object.scene);
const centerHere = aabb.getCenter(new THREE.Vector3());
object.scene.position.x += (object.scene.position.x - centerHere.x);
//object.position.y += ( object.position.y - centerHere.y );
object.scene.position.z += (object.scene.position.z - centerHere.z);
if (gridHelp) {
var boxHelp = new THREE.BoxHelper(object.scene, 0x000000);
object.scene.add(boxHelp);
}
// this little helper allows the park items to be centered in view
const xPosCenter = xPos - (parkWidth / 2)
const yPosCenter = yPos - (parkHeight / 2)
// note zPos & yPos are switched in 2D to 3D exchange
object.scene.position.set(xPosCenter, 0, yPosCenter);
const rotationRadian = -(Number(rotation) * Math.PI / 180).toString();
object.scene.rotation.set(0, rotationRadian, 0);
scene.add(object.scene);
},
// called when loading is in progresses
function (xhr) {
let contentLength = xhr.loaded;
// if an object is 100% loaded
if ((xhr.loaded / contentLength * 100) === 100) {
modelsLoaded++
// if all models are loaded
if (totalModels === modelsLoaded) {
setTimeout(function () {
}, 1500)
}
}
}
,
// called when loading has errors
function (error) {
console.log('An error happened');
}
);
}
Finally the objects are rendered:
function render() {
const time = performance.now() * 0.001;
water.material.uniforms['time'].value += 1.0 / 60.0;
renderer.render(scene, camera);
}
So the two main functions that do the job are createGLTF
and renderObjects
. I don’t understand why the system would crash. Even in the extreme case of using 100 models (of 1 Mb each), the scene shouldn’t crash I think.
One issue for the crashing might be the dispose()
of the DracoLoader
you mentioned. Another issue might be that I think that we are not reusing models that are already loaded. Is there a way to check that a certain model is already in the scene, clone or copy the existing model, and create a clone in which we set up new parameters for its position? Would that save memory?
I’m sorry about the long response, but I’m trying to kind of save this project without knowing much about three.js.