In three.j,s what exactly does Object3D's traverseVisible() method do? - three.js

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.

Related

How to do PerspectiveCamera panning with Left mouse button in Trackball controls (r87)?

By default, Camera panning is with right mouse button
Control.panSpeed = 1.5;
but I want it to work in left mouse button. How to set this?
This requires an easy modification to the trackball code.
If you look at TrackballControls.js, the very beginning details the keycode-correlated state machine (source code):
THREE.TrackballControls = function ( object, domElement ) {
var _this = this;
var STATE = {NONE: -1, ROTATE: 0, ZOOM: 1, PN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PANE: 4};
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
this.enabled = true;
this.screen = { left: 0, top: 0, width: 0, height: 0 };
Which is related to the keydown() function further down (source code):
function keydown( event ) {
if ( _this.enabled === false ) return;
window.removeEventListener( 'keydown', keydown );
_prevState = _state;
if ( _state !== STATE.NONE ) {
return;
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noRotate ){
_state = STATE.ROTATE;
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noZoom ){
_state = STATE.ZOOM;
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noPan ){
_state = STATE.PAN;
}
So, I haven't had a chance to test this, but you should be able to change PAN in line 11 from 2 (the keycode for Right Click) to 0 (Left Click) or any other keycode you want. Remember to change ROTATE as well to avoid the bug-prone directive of rotating and panning at the same time.
EDIT: per #TheJim01's helpful correction, the actual click event triggering actually happens in function mousedown( event ) on line 393 of the script. event.button corresponds to the respective number in the state machine. Sorry about the confusion.

Texture Dispose Not Working in Three JS release 73

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.

visibility of LOD DAE object in Three JS

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.

SVG ― click halts animation in Chromium and Safari

right now I have totally no idea what could cause this strange bug and I hope to able to describe it as good as possible, but the code has grown big and to post all of it wont result in a clear question.
1. Setup:
A Box2dWeb animation in the background, where the triggering of the Step() method is done by window.requestAnimationFrame following the game loop implementation here in the »That's it« section. Every time the »draw« method is called, the b2body's transformation is handed over to a <div> and to a <g>. The <div> sets up the desired animation and the <g> acts as monitor of the body and resides into an inline <svg>.
Clicks are globally caught and if the user clicks on the <div> or on the <path> the target gains focus what leads to applying forces on the b2body.
2. Bug:
All in all everything works as expected, the animation runs fluid for the <div> and its corresponding <g>, clicking on both, the focus state changes correctly, BUT in chromium and Safari the <g> stops showing up the animation, when the user clicked on the <g>.
Here is the code that is used to set the transformation from the b2Body to the <g>:
function draw(){
var str = this._sprite.transform;
var mtrs = this._transformItem.matrix;
mtrs.a = str.a1;
mtrs.c = str.a2;
mtrs.e = str.a3;
mtrs.b = str.b1;
mtrs.d = str.b2;
mtrs.f = str.b3;
this._transformItem.setMatrix( mtrs );
}
where this._transformItem points to <g>.transform.baseVal.getItem( 0 ) and this._sprite.transform to a transform matrix of custom typ.
Here is the code to catch the clicks:
function getClickTargets( target ){
var targets = { 'sprite' : null, 'link' : null };
while( target ){
if( target.nodeName.toLowerCase() === 'a' &&
targets.link == null ){
targets.link = target;
}
if( target.hasAttribute &&
target.hasAttribute( 'data-sprite-id' ) &&
targets.sprite == null ){
targets.sprite = this._stage.getSpriteById(
target.getAttribute( 'data-sprite-id' ) );
}
if( target.nodeName.toLowerCase() == 'path' ){
var attr = target.getAttributeNS( NS, 'monitor-for-sprite' );
if( attr != '' ){
targets.sprite = this._stage.getSpriteById( attr );
}
}
if( targets.link != null && target.sprite != null ){
break;
}
target = target.parentNode;
}
return targets;
};
In FF, IE9/10, Chrome and Opera everything runs as it should and the only thing that causes the misbehavior is that a »click« happens with a <path> as target. The matrices a correct because the animation keeps on running correct for the <div>.
What could cause this, where should I search, does anybody has an idea or had a similar bug?
EDIT:
Might this be caused by the fact that the events are caught and processed »asynchronously« to the overall running »update ticks« ?
Edit 2:
I noticed that the same problem occurs if I am starting the web inspector and watch the elements, but than all <g> elements freeze.
Edit 3:
I have got it running now, at least in chromium, but I guess Safari will do it also. I slightly changed the »draw« function to this:
function(){
var str = this._sprite.transform;
var mtrs = this._transformItem.matrix;
mtrs.a = str.a1;
mtrs.c = str.a2;
mtrs.e = str.a3;
mtrs.b = str.b1;
mtrs.d = str.b2;
mtrs.f = str.b3;
//this._transformItem.setMatrix( mtrs ); //old line
this._transformList.replaceItem(
this._transformList.createSVGTransformFromMatrix( mtrs ), 0 ); //new working line
}
If somebody knows why it only works with a new »SVGTransform« it would be nice to let me know. Otherwise I assume that this is a kind of bug.

select figure element in ckeditor - capture click on figure element (or any children)

i'm looking for a way to capture and selection a figure element (or any of its children)
ideally, i would like this to bypass the selections of imgs (including objectResizing on imgs) when they are within a figure tag.
i've played around with capturing the events using the currentInstance event. not sure if this is the best approach or whether there is another event. Obviously, i need some kind of event bubbling to know whether the event target (ie. the img) is within a figure element.
i have a sense of how to do this in javascript/jquery, but i'm looking for the proper ckeditor approach, since this is done in ckeditor with imgs and tables.
i have found one possible solution, which enables me to select the figure element, whether i click on one of its children or the figure itself. however, a double click triggers other events associated with the children, so i need to find a way to cancel those other events in this case
editor.on( 'selectionChange', function( evt )
{
if ( editor.readOnly )
return;
/*
* only select our figure elements
*/
var element = evt.data.path.lastElement && evt.data.path.lastElement.getAscendant( 'figure', true );
if ( element && element.getName() == 'figure' && element.getAttribute( 'data-media-id' ) && element.getChildCount() ) {
editor.getSelection().selectElement(element);
}
});
editor.on( 'doubleclick', function( evt )
{
var element = evt.data.element.getAscendant( 'figure', true );
if ( !element.isReadOnly() )
{
if ( element.is( 'figure' ) || element.getParent().is( 'figure' ) )
{
editor.getSelection().selectElement( element );
editor.execCommand('imagemanager', element);
}
}
});
turns out, i just need to pretend to open a dialog. my issue with the doubleclick is that i am not using ckeditor's dialogs (because i am opening a ajax window and building it myself). so this prevents other windows from opening
editor.on( 'doubleclick', function( evt )
{
var element = evt.data.element.getAscendant( 'figure', true );
if ( !element.isReadOnly() )
{
if ( element.is( 'figure' ) || element.getParent().is( 'figure' ) )
{
editor.getSelection().selectElement( element );
editor.execCommand('imagemanager', element);
// :HACK: pretend to open a dialog, cancels other dialogs from opening
evt.data.dialog = '';
}
}
});

Resources