After a lot of ups and downs :), rearranging the code like lego blocks, I finally managed to somehow implement async/await…
This code example looks interesting but turned out to be too difficult for me to implement
:
…
const [font, hdri, gltf] = await Promise.all([
new Promise((res, rej) => fontLoader.load(“/font.json”, res, rej)),
new Promise((res, rej) => rgbeLoader.load(“/warehouse.hdr”, res, rej)),
new Promise((res, rej) => gltfLoader.load(“/model.glb”, res, rej)),
])
> more…
However, these examples have become the most inspiring for me:
1 > more…
2 > more…
The hardest part for me was separating the objLoader and mtlLoader sequences into separate lines of code:
import * as THREE from 'three';
import { MTLLoader } from 'three/addons/loaders/MTLLoader.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
let camera, controls, scene, renderer, dirLight;
let AllModelGroup; // group of all loaded models
async function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color( '#ffffff' );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( -30, 50, 70 );
// controls
controls = new OrbitControls( camera, renderer.domElement );
controls.minDistance = 1;
controls.maxDistance = 100;
controls.target.set(15,10,0); // camera target
// lights
dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
dirLight.position.set(0, 50, 50);
dirLight.castShadow = true;
camera.add( dirLight );
scene.add( camera );
const ambientLight = new THREE.AmbientLight( 0xffffff, 1 );
scene.add( ambientLight );
await initModel();
await dispGroup(); // show structure of groups loaded *.obj
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
render();
}
function render() {
renderer.render( scene, camera );
}
init();
animate();
async function initModel () {
// list of *.obj models to load
let mod2load=[ 'box1', 'box2', 'box3'];
const mtlLoader = new MTLLoader();
const objLoader = new OBJLoader();
const LoadMaterials = async (loader, fname) => {
return await loader.loadAsync( fname+'.mtl');
}
// create an empty group of all *.obj models to load
// and attach it to the scene
AllModelGroup = new THREE.Group;
AllModelGroup.name = 'AllModelGroup';
scene.add( AllModelGroup );
for (let i = 0; i < mod2load.length; i++) {
let fname = mod2load[i], fpath='models/';
let MyObjGroup = await new THREE.Group; // the main group of one loaded model *.obj
let MyLineGroup = await new THREE.Group; // subgroup of hybrid edges
let MyAllLineGroup = await new THREE.Group; // subgroup of all edges
await mtlLoader.setPath( fpath );
const materials = await LoadMaterials(mtlLoader, fname);
await materials.preload();
objLoader.setMaterials( materials );
objLoader.setPath( fpath );
let mod3one = await objLoader.loadAsync(fname+'.obj');
// based on the loaded model *.obj, among others generate its edges
await mod3one.traverse(await function(node) {
if ( node.isMesh && node.material) {
node.material.side = THREE.DoubleSide;
let edges = new THREE.EdgesGeometry(node.geometry);
let line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial( { color: '#000000' } ) );
// assume the edges of the yellow model as hybrid,
// requiring showing together with the body of the model
// and join them to MyLineGroup
if (node.material.name =='yellow' ) {
MyLineGroup.add( line );
}else{
// edges of other materials join MyAllLineGroup,
// which is hidden by default
MyAllLineGroup.add( line );
}
}
}); // mod3one.traverse ------------------------------
// correct/complete data in sub/groups
MyObjGroup.name="MyObjGroup";
mod3one.name="ObjBody";
MyLineGroup.name="MyLineGroup";
MyAllLineGroup.name="MyAllLineGroup";
// remember the name of the file in the group
MyObjGroup.fname=fname;
mod3one.fname=fname;
MyLineGroup.fname=fname;
MyAllLineGroup.fname=fname;
// when the scene is loaded for the first time, hide the group of all edges
MyAllLineGroup.visible=false;
MyObjGroup.add( mod3one );
MyObjGroup.add( MyLineGroup );
MyObjGroup.add( MyAllLineGroup );
// append the group of the currently loaded model to the group of all models
AllModelGroup.add( MyObjGroup );
// in the console, show the groups of the currently loaded model *.obj
// console.log(fname);
// console.log(MyObjGroup);
// console.log(MyLineGroup);
// console.log(MyAllLineGroup);
// go to load next model *.obj
}//for
} // initModel
async function dispGroup () { // show structure of groups loaded *.obj
let x,y,z,q;
console.log(' ------ scene --------'); // OK
console.log(scene); // OK
console.log(' ------ AllModelGroup --------'); // OK
x= scene.getObjectByName( "AllModelGroup" );
console.log(x);
for (let i = 0; i < x.children.length; i++) {
console.log("=== "+x.children[i].fname+" ===");
y=x.children[i];//getObjectByName( "MyObjGroup" );
console.log( y.fname +" "+y.name );
z=y.children;
if (typeof z=='object' && z.length > 0 ){
for (let chil of z) {
console.log( chil.fname +" - "+chil.name );
// console.log( chil );
} // podgrupy AllModelGroup
}
} // for
} // dispGroup
I think I have achieved my intended goal:
- ordered obj models are loaded in the ordered order
- loaded obj models are placed in groups in the same order
- and it seems that the results are immediately available to be checked in the console - without any delay
Remaining issues to be resolved (someone help me ?):
- Can this current code be syntactically improved ?
- Is there too much await in it?
- Do I have to have the function LoadMaterials separately ?
- Can all this be achieved with the previous one
arrangement of nested objLoader and mtlLoader ?
Then I had a loading progress bar in JS syntax:
Previous syntax:
let mtlLoader = new MTLLoader();
mtlLoader.setPath( fpath );
mtlLoader.load( fname+'.mtl', function ( materials ) {
materials.preload();
let objLoader = new OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( fpath );
objLoader.setPath( fpath );
objLoader.load( fname+'.obj', function ( mod3one ) {
...
},
onProgress //where to move this function ?
);
});
Previously, instead of grouping loaded *.obj models, I used layers to store *.obj models. But in addition, I stored the edges of the models on two more layers. Therefore, each loaded *.obj occupied 3 layers. With this method, I could load a maximum of 10 *.obj models into the scene.
After switching to grouping of loaded objects, I got rid of these limitations and at the same time developed the existing *.obj model control properties on the stage:
- controlling the visibility of individual models;
- controlling the model display mode (with or without edges, etc.);
I have been satisfied for a month of learning about ThreeJs. 
ThreeJs in conjunction with LightGallery promises to be a great tool for publishing architectural and construction models (exported from ArchiCAD to *.obj) for the purpose of e.g. quick workshop communication with other designers and construction contractors.