texture.dispose() doesn't seem to work in r73 but works fine in r75. What should I do to get it up and running on r73.
It doesn't either work in r77.
When using texture.dispose() the dispose normally should call onTextureDispose :
function onTextureDispose( event ) {
var texture = event.target;
texture.removeEventListener( 'dispose', onTextureDispose );
deallocateTexture( texture );
_infoMemory.textures --;
}
but it does not. Dont know if its a bug or something but in order to deallocate the texture I had to do a material.map.dispose() while I was clearing the scene (so to prepare for other texture to be inserted in the scene).
For example:
var clearScene = function(){
stopAnimate();
if(scene && scene.children.length>0){
for( var i = scene.children.length - 1; i >= 0; i-- ) {
if( scene.children[i] instanceof THREE.Sprite ){
if( scene.children[i].material.map ) scene.children[i].material.map.dispose();
}
if( scene.children[i] instanceof THREE.Mesh ){
if( scene.children[i].material.map ) scene.children[i].material.map.dispose();
}
scene.children[i].material.dispose();
scene.remove(scene.children[i]);
scene.children.splice(i,1);
}
}
};
Note: by using just:
scene.children[i].material.dispose();
the Textures of a material don't get disposed according to the documentation. http://threejs.org/docs/#Reference/Materials/Material
Hope that helps you.
Related
Does it:
Merely select objects within the camera's view frustum
Or does it actually do a z-depth type visibility check and only select those that are fully/partly visible on the screen - for a particular view
You can see what it does in the source code by clicking on the link at the bottom of the Object3D documentation.
traverseVisible( callback ) {
if ( this.visible === false ) return;
callback( this );
const children = this.children;
for ( let i = 0, l = children.length; i < l; i ++ ) {
children[ i ].traverseVisible( callback );
}
}
It only performs a check to see if visible === false, nothing more. There's no frustum or occlusion checks.
I am using the code below to load and clone a GLTF model. In practice this works, however it is way too resource heavy and the model is around a 2mb. It has no textures and the materials are a single MeshPhongMaterial.
I understand two ways to optimise this are to merge them into one mesh instead of cloning, however following numerous attempts (such as iterating within the load function with a for loop), I haven't been able to successfully do this. The second being instancing, however from a number of examples I understand this requires a shader material to handle to the attributes.
I'm a little out of my depth here and would massively appreciate any assistance.
Many thanks!
loader.load('obj/floor/floor-base-details-base.gltf', (gltf) => {
floorBaseModel = gltf.scene;
floorBaseModel.traverse( function ( node ) {
if ( node.isMesh ) {
node.castShadow = true;
node.receiveShadow = true;
if ( node instanceof THREE.Mesh ) {
node.material = base_material;
}
}
});
for(var i = 0; i < 15; i++){
var floorBaseClone = floorBaseModel.clone();
offsetPos = (i+1)* -595;
floorBaseClone.position.set(0,0,offsetPos);
floorBaseGroup.add(floorBaseClone);
}
});
floorBaseGroup.scale.set(1,1,1);
floorBaseGroup.position.set(0,-15,425);
scene.add(floorBaseGroup);
I can’t seem to be able to clone an FBX model (FBX downloaded from Mixamo) while retaining animation keyframes.
Have attempted a number of approaches including using the cloneFbx gist (included in the example below); all to no avail. Even placing the entire FBXLoader() function inside a loop does not work as expected since only one of the models will animate at a time.
This issue has been partially addressed here, but I cannot seem to ‘copy’ the animation sequence as answer suggests.
Can anyone point out where I’m going wrong?
Here's a rough example of one of my tests:
Load fbx model and store animation:
var loader = new THREE.FBXLoader();
loader.load( 'models/Walking.fbx', function ( fbx ) {
clip = fbx.animations[ 0 ];
// createVehicle(fbx); // Works! Creates one animated model via FBX
// cloneFbx via: https://gist.github.com/kevincharm/bf12a2c673b43a3988f0f171a05794c1
for (var i = 0; i < 2; i++) {
const model = cloneFbx(fbx);
createVehicle(model);
}
});
Add mixers and actions based on stored clip, add model to scene:
function createVehicle(model){
model.mixer = new THREE.AnimationMixer( model );
mixers.push( model.mixer );
var action = model.mixer.clipAction( clip );
action.play();
model.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
});
const x = Math.random() * groundSize - groundSize/2;
const z = Math.random() * groundSize - groundSize/2;
model.position.set(x, 0, z);
const vehicle = new Vehicle(model, x, z);
vehicles.push(vehicle);
scene.add( model );
}
Animation cycle:
if ( mixers.length > 0 ) {
for ( var i = 0; i < mixers.length; i ++ ) {
mixers[ 0 ].update( clock.getDelta() );
}
}
Couldn’t figure out an elegant solution to this. Best I could come up with is creating a loop with the loading sequence inside of it; this is very slow (since the FBX has to be parsed each time).
The key here was having an animation mixer controlling the animated objects as a group as opposed to creating a mixer per animated object.
If anyone can figure out a better solution, I would be super keen to hear it (perhaps using the cloneFbx script properly).
Create mixer, load FBX:
// Create mixer to run animations
mixer = new THREE.AnimationMixer( scene );
// Load fbx
var loader = new THREE.FBXLoader();
for (var i = 0; i < 5; i++) {
loader.load( 'models/Walking.fbx', function ( fbx ) {
mixer.clipAction( fbx.animations[ 0 ], fbx )
.startAt( - Math.random() )
.play();
createVehicle(fbx);
});
}
Create class instances, add to scene:
function createVehicle(model){
const x = Math.random() * groundSize - groundSize/2;
const z = Math.random() * groundSize - groundSize/2;
model.position.set(x, 0, z);
const vehicle = new Vehicle(model, x, z);
vehicles.push(vehicle);
scene.add( model );
}
Draw cycle:
mixer.update( clock.getDelta() );
I found out that SkeletonUtils.clone() works good for me.
https://threejs.org/docs/index.html#examples/en/utils/SkeletonUtils.clone
i'm trying to use the THREE.LOD object in my ThreeJS scene. i've been inspired by http://threejs.org/examples/webgl_lod.html
But I wanted to push the idea further and use DAE model (using this loader : http://threejs.org/examples/webgl_loader_collada.html)
Problem is i can't switch the visibility of the lod level. First, i tried an automated one in my Render function (based on distance to camera, found in the example):
this.m_Scene.traverse( function ( object ) {
if ( object instanceof THREE.LOD ) {
object.update( that.m_Camera );
}
} );
As it wasn't working (All my lod were displayed at the same time). I try something more manual. and it appears the Object3D.visibility attribute isn't really used by the renderer, or not inherited by children.
As far as I understand, this attribute is for meshes. But i'm not sure it's checked at render time.
So this doesn't work as expected:
var LodTemporaryObject = new THREE.LOD();
function LoadLod1()
{
//TEST COLLADA LOADER
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load(Lod2Path, function ( collada ) {
dae = collada.scene;
dae.scale.x = dae.scale.y = dae.scale.z = 0.1;
dae.updateMatrix();
dae.visible = false; //THIS HAS NO EFFECT
LodTemporaryObject.addLevel(dae,100);
AddLodToScene(LodTemporaryObject ); //where the lod is added to the threeJS scene object
} );
}
so question : how do I actually set (in)visible any Object3D or subScene ?
EDIT: The answer below is outdated. Visibility is now inherited. See, for example, Show children of invisible parents.
three.js r.71
Visibility is not inherited by children with WebGLRenderer.
The work-around is to use a pattern like so:
object.traverse( function( child ) {
if ( child instanceof THREE.Mesh ) {
child.visible = false;
}
}
three.js r.64
Thx to WestLangley answer above, i came up with an recursive solution to my problem:
first, a recursive function to update visibility of children to match the parent's:
function SetChildrenVisible(parent)
{
if(parent instanceof THREE.Object3D)
{
for(var i = 0; i< parent.children.length; i ++)
{
parent.children[i].visible = parent.visible;
SetChildrenVisible(parent.children[i]);
}
}
}
then in my render loop:
this.m_Scene.traverse( function ( object ) {
if ( object instanceof THREE.LOD ) {
//save all lodLevel state before updating
var oldVisible =[]; object.visible;
for(var i = 0; i< object.children.length; i++)
{
oldVisible.push(object.children[i].visible)
}
//Update Lod
object.update( that.m_Camera );
//Check for changes and update accordingly
for(var i = 0; i< object.children.length; i++)
{
if(oldVisible[i] != object.children[i].visible )
{
SetChildrenVisible(object.children[i]);
}
}
}
} );
Goal is to only update object whose attribute changed.
First of all, thank you for this wonderfull work, i'm having a lot of fun working with three.js.
I tried to find answer about a recurent issue, .WebGLRenderingContext: GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 2
I'm making a website in webgl, i spend few week understanding all about three.js but i can't fix this issue.
I get this message on Chrome and firefox (latest) each time i try to load a canvas into a map, bumpmap and specmap.
All my mesh are loaded from obj files, by the way i rewrote OBJMTLLoader.js to be able to load more parameters from obj files and more.
here the code used to load image.
THREE.MTLLoader.loadTexture = function ( url, mapping, onLoad, onError ) {
var isCompressed = url.toLowerCase().endsWith( ".dds" );
var texture = null;
if ( isCompressed ) {
texture = THREE.ImageUtils.loadCompressedTexture( url, mapping, onLoad, onError );
} else {
var image = new Image();
texture = new THREE.Texture( image, mapping );
var loader = new THREE.ImageLoader();
loader.addEventListener( 'load', function ( event ) {
texture.image = THREE.MTLLoader.ensurePowerOfTwo_( event.content );
texture.needsUpdate = true;
if ( onLoad )
onLoad( texture );
} );
loader.addEventListener( 'error', function ( event ) {
if ( onError ) onError( event.message );
} );
loader.crossOrigin = this.crossOrigin;
loader.load( url, image );
}
return texture;
};
I'm pretty sure it is from this, because when i disable this function, no more warning.
Is it because the mesh has a texture with an empty image while loading datas ?
Is there any restriction on the dimensions of image ?
For now everything works fines, but i feel strange having those message in console.
Thanks
This error become because the Three.js buffers are outdated. When your add some textures (map,bumpMap ...) to a Mesh, you must recompose the buffers like this :
ob is THREE.Mesh, mt is a Material, tex is a texture.
tex.needsUpdate = true;
mt.map = tex;
ob.material = mt;
ob.geometry.buffersNeedUpdate = true;
ob.geometry.uvsNeedUpdate = true;
mt.needsUpdate = true;
That's all folks !
Hope it's help.
Regards.
Sayris