grouped collada models onload three.js - javascript

I am currently loading in a bunch of elements of a model and adding them to a group. I then want to be able to move the group as a whole unit after they load.
My issue is, how can I run code once I have ensured that all of my models have loaded?
I don't understand how to use the .onload function with the colladaLoader & its callback functions. Also, is it a bad idea to use a self executing function like I have here? I am not sure how else to go through a list and load in all the models.
My code is below. I have tried to use a hack, by using the counter "complete" but it doesn't work all of the time. Thanks!
for ( var i=0; i<object.asset.length; i++ ) {
loader = new THREE.ColladaLoader();
asset = furniture.asset[i];
(function(asset) {
loader.load(asset["path"], function(collada, materials) {
//this function scales & positions model
var mesh = daeAttributes(collada, object, asset, newMaterial);
var scene = get_scene();
group.add( mesh );
complete++;
//when all assets have been loaded, add the group to the scene
if (complete===object.asset.length-1) {
//want to move the group once all the models are loaded
group = moveModel(group, object);
scene.add( group );
render();
};
});
})(asset);
Update
So if I have a list >1, of unique objects to load in, it works. If there is only 1 object it doesn't load that one object.

If you put everything inside of this it should work:
<body onload="myFunction()">

Related

Three.JS instance can't be re-created

I'm trying to destroy (or destruct or dispose) an 'instance' of Three.JS using this:
Full example: https://jsfiddle.net/v7oLzs4m/58/
function kill() {
const rendererDomWas = renderer.domElement;
renderer.dispose();
controls.dispose();
renderer = scene = camera = controls = null;
document.body.removeChild( rendererDomWas );
shouldRender = false;
}
function animate() {
if(!shouldRender) {return} // no more requesting animation frames if 'shouldRender' is false
frameID = window.requestAnimationFrame( animate );
renderer.render( scene, camera );
}
(i.e. disposing, setting references to null, and stopping the draw loop from touching renderer while shouldRender is false)
It appears to work at first (The renderer content stops showing) but when I re-create the instance, it never comes back.
It's as if something is still... holding onto the GLContext which prevents it from being invoked again.
Why can't I re-create a new instance of Three.JS?
I'm not sure why this works (since I was already doing this in my Fiddle...)
But it turns out the secret ingredient (for me) to letting Three.JS get GC'd/disposed is this:
window.cancelAnimationFrame(frameID);
I suppose that this stops the render function from being stored in the hardware-level draw loop, which holds references to GLContext (and the chain)
The GC can't ever happen unless the 'loop is broken'
(Correct me if I'm wrong please)

I want to add a hotspot to certain parts of the obj extension model I have installed with three.js. how can I do it?

I can add a click event to the model I loaded with Three.js, but I can't add a click event to a particular part of the model I've uploaded. For example, I can make a click event on the car model I have installed, but what I want to do is exactly this: when I click on the wheel of the car I want to write 'wheel' to the console. When I click on the glass I want to write 'glass'.
How can I do it?
const objLoader = new THREE.OBJLoader2();
objLoader.loadMtl('bugatti.mtl', null, (materials) => {
objLoader.setMaterials(materials);
objLoader.load('bugatti.obj', (event) => {
const root = event.detail.loaderRootNode;
scene.add(root);
myModel = root;
const domEvents = new THREEx.DomEvents(camera, renderer.domElement)
domEvents.addEventListener(myModel, 'click', event => {
console.log("car");
});
It's best to solve this issue during the design phase and ensure that your model does consist of multiple independent meshes grouped together to a more complex object. In this way, it will be much easier to select single components in three.js (and any other 3D engine).

.3ds loader does not load materials and textures

I am trying to load quite complex .3ds model into three.js using TDSLoader exactly the same way as in three.js/examples: https://github.com/mrdoob/three.js/blob/master/examples/webgl_loader_3ds.html
but what I get looks like this Result when it is supposed to look like this Loaded using https://3dviewer.net/. I can see that this online viewer uses three.js so it is possible. I also know that TDSLoader loads textures but does not map them for some reason (if I move texture out of the folder I get an load resource error). Code looks like this:
var loader = new THREE.TextureLoader();
var normal = loader.load('textures/normal.png');
var loader = new THREE.TDSLoader();
loader.setPath('textures/');
loader.load('textures/CAT_336D.3ds', function (object) {
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material.normalMap = normal;
}
});
scene.add(object);
});
My model has 23 types of materials but the loader loads only 2 - black and yellow, you can see that glass, metal and others are missing. How do I map all textures and materials? Maybe it does not work because the model is too big? (over 160 meshes, around 30MB)

How to remove and dispose of all child geometry and meshes from an Object3D?

In this project I am working on I have several Collada models being displayed and then removed when not needed anymore. There seems to be a memory leak somewhere in the project and I am looking for ways to get it to run as smooth as possible as time is not on my sideā€¦
I feel like I don't remove the meshes the right way and that this might cause some of the memory leakage I have.
I load the objects with LoadObject(level_1_character, "Assets/Level_1_Character.dae"); for example, where level_1_character is a Object3D. This calls the following function:
function LoadObject(name, url) {
var mesh, geometry, material, animation;
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load(url, function(col) {
mesh = col;
geometry = col.scene;
name.name = url.toString();
name.add(geometry);
});
}
I add the objects to the scene depending on the level through scene.add(level_1_character); and then remove it by doing the following:
level_1_character.traverse(function(child){
if (child instanceof THREE.Mesh) {
child.material.dispose();
child.geometry.dispose();
}
});
I am not sure if this actually fully removes the object though. It seems as though the objects still are present in memory. Any idea what I'm doing wrong here?

How to assign to variable content of Three.js model file

When you export your model from Blender to Three.js you end up with a file with JSON data inside. I know two ways of loading this model from that file:
var loader = new THREE.JSONLoader()
var material = new THREE.MeshPhongMaterial({color: '#8080a0'})
1.
loader.load('tower.json', function (geometry) {
var mesh = new THREE.Mesh(geometry, material)
})
2. Edit tower.js file and add
var tower =
in first line. Then you can load it with:
var towerModel = loader.parse(tower)
var mesh = new THREE.Mesh(towerModel.geometry, material)
I like second solution more because using loader.load() function every time you want to create a mesh when you have thousands of meshes based on the same model is very slow and can easly kill your browser.
So my question is - can I get JSON data from tower.json file into a variable without editing the file manually? The best solution would be to get JSON data into a variable without making changes to the file.
I would go for option 1.
It sounds very strange to add a loader to a source file.
To keep things clean you should keep loaders and data sources (your model files) separated if you ask me.
Why would you reload the same model anyway. You should just store it in a variable and reuse it. It is a waste of CPU and memory to load the same geometry twice.
You can do easily something like this to optimize things. It is just a concept but I hope you get the idea:
// Storage for model files
myFiles = {
tower: 'tower.json',
car: 'car.json'
};
// Storage for loaded geometries
myModels = {
tower: null,
car: null
};
// Loader, loads only first time from file then from storage
var load = function( modelName ){
if( myModels[modelName] === null ){
loader.load(myFiles[modelName], function (geometry) {
myModels[modelName] = geometry;
});
}
var model = new THREE.Mesh(myModels[modelName], material)
scene.add( model );
}
You can rework it how you think is best.

Categories