Three.js SceneUtils has no traverseHierarchy!!! where is it? - three.js

I had not updated the three.js library in my system. Today, I updated the three.js file. But I encountered this error: "Uncaught TypeError: Object # has no method 'traverseHierarchy' ".
I had used it for detecting intersection:
Online code is here: https://dl.dropbox.com/u/44791710/test/simple2.html
function Intersect(event) {
event.preventDefault();
var mousex = (event.clientX / window.innerWidth) * 2 - 1;
var mousey = -(event.clientY / window.innerHeight) * 2 + 1;
var vector = new THREE.Vector3(mousex, mousey, 1);
var toIntersect = [];
THREE.SceneUtils.traverseHierarchy(scene, function (child) {
if (child instanceof THREE.Mesh) {
toIntersect.push(child);
}
});
var projector = new THREE.Projector();
projector.unprojectVector(vector, camera);
var ray = new THREE.Raycaster(camera.position,vector.sub(camera.position).normalize());
var intersects = ray.intersectObjects(toIntersect);
if (intersects.length > 0) {
for (var j = 0; j < intersects.length ; j++){
target = intersects[j].object;
console.log('Intersects at ' + mouse.x + '/' + mouse.y + ':');
for(var i = 0, m = intersects.length; i<m; i++){
console.log(intersects[i].object.id, intersects[i]);
}
}
}
}
what kind of changes I need to do?
Thanks.

You should replace scene.traverse(scene, function (child) { by scene.traverse(function (child) { (I could not add a comment, so I created an answer instead).

As the Migration guide states, in r51 -> r52:
Replaced SceneUtils.traverseHierarchy with object.traverse.
object meaning any THREE.Object3D instance, e.g. THREE.Mesh or THREE.Scene. So instead of THREE.SceneUtils.traverseHierarchy, try scene.traverse.

Related

How to perform child displacement on 3D object, Three.js?

Here I have a simple example of an object being loaded using MTLLoader() and OBJLoader() in Three.js
var loader = new THREE.MTLLoader();
loader.load(<?php echo "'".$mPart."'"; ?>, function (materials) {
materials.preload();
var objLoaderOfficeChair = new THREE.OBJLoader();
objLoaderOfficeChair.setMaterials(materials);
objLoaderOfficeChair.load(<?php echo "'".$oPart."'"; ?>, function (object) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ){
x=document.getElementsByClassName("popup");
for(var i = 0; i < x.length; i++){
x[i].innerHTML += "<li class='mi_child'>"+child.name+"</li>";
var timer = performance.now();
geometry = child.geometry;
geometry.dynamic = true;
material = child.material;
mesh = new THREE.Mesh(geometry, material);
model = object.children[i].geometry;
model2 = object;
scene.add(mesh);
}
Now later in the anim() code, I call another action which will move the children the object loaded. Now I would like these items to be moved in all different directions.
var center = new THREE.Vector3(0, 0, 0);
var distanceToMove = 0.1;
if(params.explodeEnabled) {
for (var i = 0; i < model2.children.length; i++) {
model2.children[i].position.x = explodeValue + outlinePass.Explode * i;
model2.children[i].position.y = explodeValue + outlinePass.Explode * i - 0.02;
model2.children[i].position.z = explodeValue + outlinePass.Explode * i + 0.06;
}
}
But in this example all the items move together in the same direction as seen here.
More Info
You need to move each part along a different path from the center of the object. Save the old position of each part, and calculate its direction away from the center.
var explosionCenter = new THREE.Vector3(0, 0, 0);
for (var i = 0; i < model2.children.length; i++) {
var child = model2.children[i];
if (!child.geometry.boundingSphere) child.computeBoundingSphere();
child.oldPosition = child.oldPosition || geometry.boundingSphere.center.clone();
var direction = child.oldPosition.clone().sub(explosionCenter).normalize();
child.position.x = child.oldPosition.x + explodeValue + outlinePass.Explode * direction.x;
child.position.y = child.oldPosition.y + explodeValue + outlinePass.Explode * direction.y;
child.position.z = child.oldPosition.z + explodeValue + outlinePass.Explode * direction.z;
}
This way, each component will move away from the center depending on its initial position. You should use custom direction vectors for some of the big components to make sure they do not cover the small ones:
if (i == <index of big component>) {
direction = new THREE.Vector3(1, 1, 0); // Custom direction for a component
}

Uncaught TypeError: Cannot read property 'length' of null - Three.js

I've trying to draw the square wall by getting mouse clicks coordinates and extrude it.
I've picking up the mouse coordinates by clicking at the scene.
var onDocumentMouseDown = function ( event )
{
//update the mouse variable
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;
var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
vector.unproject( camera );
var dir = vector.sub( camera.position ).normalize();
var distance = - camera.position.z / dir.z;
var pos = camera.position.clone().add( dir.multiplyScalar( distance));
console.log('mouse_x ' + pos.x + ' mouse_y ' + pos.y);
if (clickCount <= 3){
coord[clickCount] = {'x' : pos.x, 'y' : pos.y};
clickCount ++;
} else {
//make new wall and stop function
newshape = new THREE.Shape();
shape.moveTo(coord['0'].x ,coord['0'].y);
shape.lineTo(coord['0'].x, coord['1'].y);
shape.lineTo(coord['2'].x, +coord['2'].y);
shape.lineTo(coord['3'].x, coord['3'].y);
shape.lineTo(coord['0'].x, coord['0'].y);
var newextrudeSettings = {
//*******/
};
}
And when I've recived four coordinates, three.js throw the error:
Uncaught TypeError: Cannot read property 'length' of null
at Object.triangulateShape (three.js:26140)
at ExtrudeGeometry.addShape (three.js:26330)
at ExtrudeGeometry.addShapeList (three.js:26235)
at new ExtrudeGeometry (three.js:26211)
at HTMLDocument.onDocumentMouseDown (script.js:116)
To find points of intersection I prefer to use THREE.Raycaster() (though I've never used THREE.Projector() for this purpose).
This is the result of my code:
I hope I got your conception. Thus, all the stuff you need is here:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var intersects;
var controlPoints = [];
var clickCount = 0;
function onMouseDown(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObjects(objects); // objects is an array which contains just the mesh of the plane
if (intersects.length > 0) {
if (clickCount <= 3) { // I took your idea of 4 clicks
controlPoints[clickCount] = intersects[0].point.clone(); // add a control point to the array
// visualization of a control point
var cp = new THREE.Mesh(new THREE.SphereGeometry(0.125, 16, 12), new THREE.MeshBasicMaterial({color: "red"}));
cp.position.copy(intersects[0].point);
scene.add(cp);
clickCount++;
} else { // on the fifth click we'll create our wall
shape = new THREE.Shape();
shape.moveTo(controlPoints[0].x, -controlPoints[0].z);
shape.lineTo(controlPoints[1].x, -controlPoints[1].z);
shape.lineTo(controlPoints[2].x, -controlPoints[2].z);
shape.lineTo(controlPoints[3].x, -controlPoints[3].z);
shape.lineTo(controlPoints[0].x, -controlPoints[0].z);
var extrudeSettings = {
steps: 1,
amount: 2,
bevelEnabled: false
};
var extrudeGeom = new THREE.ExtrudeGeometry(shape, extrudeSettings);
extrudeGeom.rotateX(-Math.PI / 2);
var wall = new THREE.Mesh(extrudeGeom, new THREE.MeshStandardMaterial({
color: "gray"
}));
scene.add(wall);
controlPoints = []; // we clear the array of control points
clickCount = 0; // and reset the counter of clicks
};
};
};
jsfiddle example. 4 clicks for setting control points, the fifth click creates a wall, and so on.

Picking Object3D loaded via OBJMTLLoader

When we load an Object3D with OBJMTLLoader, it is not possible to use raycaster to pick this object with mouse. Intersection array length is always 0. Any one knows the reason? Below is the code...
The loader routine
var loader2 = new THREE.OBJMTLLoader();
loader2.load('/assets/unwrap/masa/dogtasmasa.obj', '/assets/unwrap/masa/dogtasmasa.mtl', function (object) {
object.position.y = 1.5;
object.position.x = 0;
object.position.z = 2;
object.rotateX(-Math.PI / 2);
object.rotateZ(-Math.PI / 2);
object.scale.set(0.04, 0.04, 0.04);
object.castShadow = true;
scene.add(object);
});
and the picking
function onDocumentMouseDown(event) {
event.preventDefault();
SCREEN_WIDTH = window.innerWidth - 5;
SCREEN_HEIGHT = window.innerHeight - 5;
var vector = new THREE.Vector3((event.clientX / SCREEN_WIDTH) * 2 - 1, -(event.clientY / SCREEN_HEIGHT) * 2 + 1, 0.5);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
for (var i = 0; i < intersects.length; i++) {
var obj = intersects[i].object;
controls.enabled = false;
tControls.attach();
}
}
else {
controls.enabled = true;
tControls.detach();
}
}
The scene is the whole browser window. Any other mesh cerated via THREE types can be picked, but object3d not...
Thanks for all kinds of help
Add the recursive flag like so:
var intersects = raycaster.intersectObjects( objects, true );
three.js r.66

Three js How to move scene

I would like to move the container (which contains my 3d objects)
Could you tell me how?
I am using Three JS
THREE.DragControls = function(camera, scene, domElement) {
this.childs = [];
this.childOffsets = [];
if (scene instanceof THREE.Scene) {
childs = scene.children;
scene = scene.children;
}
var _projector = new THREE.Projector();
var mouse = new THREE.Vector3();
var offset = new THREE.Vector3();
var selected;
domElement.addEventListener('mousemove', onDocumentMouseMove, false);
domElement.addEventListener('mousedown', onDocumentMouseDown, false);
domElement.addEventListener('mouseup', onDocumentMouseUp, false);
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / domElement.width) * 2 - 1;
mouse.y = -(event.clientY / domElement.height) * 2 + 1;
var ray = _projector.pickingRay(mouse, camera);
if (selected) {
var targetPos = ray.ray.direction.clone().multiplyScalar(selected.distance).add(ray.ray.origin);
selected.object.position.copy(targetPos.sub(offset));
return;
}
var intersects = ray.intersectObjects(scene);
if (intersects.length > 0) {
domElement.style.cursor = 'pointer';
} else {
domElement.style.cursor = 'auto';
}
}
function onDocumentMouseDown(event) {
event.preventDefault();
mouse.x = (event.clientX / domElement.width) * 2 - 1;
mouse.y = -(event.clientY / domElement.height) * 2 + 1;
var ray = _projector.pickingRay(mouse, camera);
var intersects = ray.intersectObjects(scene);
if (intersects.length > 0) {
selected = intersects[0];
offset.copy(selected.point).sub(selected.object.position);
domElement.style.cursor = 'move';
}
}
function onDocumentMouseUp(event) {
event.preventDefault();
if (selected) {
selected = null;
}
domElement.style.cursor = 'auto';
}
}
If I understand correctly you would like to MOVE all 3D Objects within a scene?
The way I did that is to create a THREE.Group(). E.g.
var holder = new THREE.Group();
Then add all models/lights/etc to that holder group. Then add the holder to the scene.
var scene = new THREE.Scene();
holder.add(model);
scene.add(holder);
Then moving the holder would move all objects added to it.
I think you are asking for moving you're scene container which maybe a canvas or a dom element if that's the case you can apply drag and drop to your container which is rendering your scene.
Reference below :
https://www.google.com/url?sa=t&source=web&rct=j&url=https://konvajs.org/docs/drag_and_drop/Drag_and_Drop.html&ved=2ahUKEwjOscW6zpj1AhXSEXAKHb21Bz8QFnoECAYQAQ&usg=AOvVaw2aV1F114SqLcBDVL8BiN0Q

Threejs raycaster only works super close

I was following mrdoobs examples of how to use raycasting to handle clickable objects. I have also looked at all of the many many similar questions and have tried countless things.
The raycasting works... If I am at a distance of less than 1.
The Raycaster is set to near 0 and far infinity though (defaults).
I haven't seen any code examples where distance was set.
I am hoping for another pair of eyes.
// snippet
glow.clickables = [];
var cubeGeo = new THREE.CubeGeometry(2, 2, 2);
cubeGeo.computeFaceNormals();
var cube = new THREE.Mesh(cubeGeo, redmat);
cube.position.y = 10;
cube.position.x = 0;
cube.position.z = -12;
cube.overdraw = true;
glow.Vis.scene.add(cube);
glow.clickables.push(cube);
onclick_();
var onclick_ = function() {
$('#world').on('mousedown', function(e){
var mouseX = (event.offsetX / $('#world').width()) * 2 - 1;
var mouseY = - ( event.offsetY / $('#world').height()) * 2 + 1;
var vector = new THREE.Vector3(mouseX, mouseY, 0.1); //what should z be set to?
//console.info(vector); // A vector between -1,1 for both x and y. Z is whatever is set on the line above
projector.unprojectVector(vector, glow.Vis.camera);
var conts = glow.Vis.controls.getObject().position; // control 3dObject which the camera is added to.
var clickRay = new THREE.Raycaster(conts, vector.sub(conts).normalize());
var intersects = clickRay.intersectObjects(glow.clickables);
console.info(intersects.length);
if(intersects.length > 0) {
alert("Click detected!");
}
});
}
Setting the mouse position this way is more accurate.
var rect = renderer.domElement.getBoundingClientRect();
mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
For mouse detecting (far or near! no matter!), do this:
put this in your global:
var pointerDetectRay, projector, mouse2D;
put this in your init() function:
pointerDetectRay = new THREE.Raycaster();
pointerDetectRay.ray.direction.set(0, -1, 0);
projector = new THREE.Projector();
mouse2D = new THREE.Vector3(0, 0, 0);
put this in your render() loop function:
pointerDetectRay = projector.pickingRay(mouse2D.clone(), camera);
and it's your mouse event:
function onDocumentMouseMove(event) {
event.preventDefault();
mouse2D.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse2D.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
Now, every where that you want to detect the objects under your mouse pointer, use this:
var intersects = pointerDetectRay.intersectObjects(scene.children);
if (intersects.length > 0) {
var intersect = intersects[0];
// intersect is the object under your mouse!
// do what ever you want!
}
I use the following which, for me, works for larger distances:
var projector = new THREE.Projector();
function onDocumentMouseDown( event ) {
event.preventDefault();
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector(vector,camera);
var raycaster = new THREE.Raycaster(camera.position,vector.sub(camera.position).normalize() );
var intersects = raycaster.intersectObjects( [sphere,cylinder,cube] );
if ( intersects.length > 0 ) {
intersects[ 0 ].object.material.transparent=true;
intersects[ 0 ].object.material.opacity=0.1;
console.log(intersects[0]);
}
}
This just sets the first selected object to semitransparent. Complete example at: https://github.com/josdirksen/learning-threejs/blob/master/chapter-09/02-selecting-objects.html

Resources