three.js - get object name with mouse click - javascript

I had loaded 3 external model with the name into my scene using json loader and now i want to get the name of the model/object by clicking it.
Below is the that i had used to load the model
var object_material = new THREE.MeshBasicMaterial({
color: 0xd6d6d6,
side: THREE.DoubleSide
});
var loader = new THREE.JSONLoader();
loader.load("models/"+file,
function(geometry, object_material)
{
var object = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(object_material));
model = new THREE.Object3D();
model.id=file;
model.name='sample'+file;
model.userData.id='sampledata'+file;
model.add(object);
model.position.set(obj_x,obj_y,obj_z);
model.mirroredLoop = true;
model.castShadow = true;
model.receiveShadow = true;
scene.add(model);
}
);
Below is my mouse down function
function onMouseDown(event) {
event.preventDefault();
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, 1 );
projector.unprojectVector( vector, camera );
var pLocal = new THREE.Vector3(0, 0, -1);
var pWorld = pLocal.applyMatrix4(camera.matrixWorld);
var ray = new THREE.Raycaster(pWorld, vector.sub(pWorld).normalize());
// Get meshes from all objects
var getMeshes = function(children) {
var meshes = [];
for (var i = 0; i < children.length; i++)
{
if (children[i].children.length > 0) {
meshes = meshes.concat(getMeshes(children[i].children));
} else if (children[i] instanceof THREE.Mesh) {
meshes.push(children[i]);
}
}
return meshes;
};
function attributeValues(o)
{
var out = [];
for (var key in o) {
if (!o.hasOwnProperty(key))
continue;
out.push(o[key]);
}
return out;
}
var objects = attributeValues(this.o3dByEntityId);
var meshes = getMeshes(objects);
var intersects = ray.intersectObjects(meshes);
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( scene.children );
console.log(scene);
// this console displays all the objects under children - THREE.Object3D - name as name: "sample513.js"
if ( intersects.length > 0 )
{
// but for the clickedObject - the length is > 0 and name is empty
var clickedObject = intersects[0].object;
console.log(clickedObject.parent.userData.id); // return as undefined
if ( INTERSECTED != intersects[ 0 ].object )
{
INTERSECTED= intersects[ 0 ].object;
name = INTERSECTED.name;
}
} else {
console.log('intersects.length is 0');
}
}
Even-though i had provided the model name in the userData , i am not able to retrieve it . can any one guide me how to retrieve the name of the object when it is clicked

Try to make through this example. Look at messages in the console.
<script src="js/controls/EventsControls.js"></script>
EventsControls = new EventsControls( camera, renderer.domElement );
EventsControls.attachEvent( 'onclick', function() {
console.log( 'this.focused.name: ' + this.focused.name );
});
// if use drag and drop
EventsControls.attachEvent( 'dragAndDrop', function () {
this.container.style.cursor = 'move';
this.focused.position.y = this.previous.y;
});
EventsControls.attachEvent( 'mouseOut', function () {
this.container.style.cursor = 'auto';
});
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load( "models/Tux.js", addModelToScene );
function addModelToScene( geometry, materials ) {
var material = new THREE.MeshFaceMaterial( materials );
model = new THREE.Mesh( geometry, material );
model.scale.set( 10, 10, 10 ); model.name = 'Tux';
model.rotation.x = -Math.PI/2;
model.position.set( 175, 45, 125 );
scene.add( model );
EventsControls.attach( model );
}

The parent of the clickedObject is probably undefined. Perhaps you can console log the clickedObject and see which path you need to access the id.

Related

RAYCAST wont pick dynamically created lines on 3D canvas created with three.js

i dynamically create some dots on 3D canvas with constructor (using three.js and javascript), using set of x y z coordinates, then i dynamically create lines also with constructor, between that dots. So then, when i want to pick that lines, raycaster wont recognize them. I tried to make the same lines without constructor, simply onload of browser, and then it works just fine. Also, dot picking is working fine.
all dots and beams i push in to arrays called allJoints[] and allBeams[].
i tried to create lines with THREE.MeshLIne using new Float32Array, MeshLine(), setGeometry, MeshLineMaterial and THREE.Mesh, but same problem occurs.
//dots:
function Joint(jointX,jointY,jointZ,jointCounter) {
console.log("nacrtaj tacku");
this.jointX = jointX;
this.jointY = jointY;
this.jointZ = jointZ;
this.jointNo = jointCounter;
this.dot;
this.deleteState = true;
this.makeDot = function () {
let dotGeometry = new THREE.Geometry();
dotGeometry.vertices.push(new THREE.Vector3( this.jointX,
this.jointY, this.jointZ));
let dotMaterial = new THREE.PointsMaterial( { size: 8,
color:"#ffccff", sizeAttenuation: false} );
this.dot = new THREE.Points( dotGeometry, dotMaterial )
}
this.drawDot = function () {
scene.add( this.dot );
renderer.render( scene, camera );
}
this.makeDot();
this.drawDot();
}
//Beams:
function Beam(firstNodeNo,secondNodeNo,beamCounter) {
this.deleteState = true;
this.firstNodeNo = allJoints[firstNodeNo].jointNo;
this.secondNodeNo = allJoints[secondNodeNo].jointNo;
this.beam;
this.makeBeam = function () {
let material = new THREE.LineBasicMaterial( { color: 0x0000ff
} );
let geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3(
allJoints[firstNodeNo].jointX, allJoints[firstNodeNo].jointY,
allJoints[firstNodeNo].jointZ),
new THREE.Vector3( allJoints[secondNodeNo].jointX,
allJoints[secondNodeNo].jointY, allJoints[secondNodeNo].jointZ) );
this.beam = new THREE.Line( geometry, material );
}
this.drawBeam = function () {
scene.add( this.beam );
}
this.makeBeam();
this.drawBeam();
//raycaster:
class PickHelper {
constructor() {
this.raycaster = new THREE.Raycaster();
this.pickedObject0 = null;
this.pickedObjectSavedColor0 = 0;
}
pick(normalizedPosition, scene, camera) {
if (this.pickedObject0) {
this.pickedObject0.material.color.setHex(this.pickedObjectSavedColor0);
this.pickedObject0 = undefined;
}
this.raycaster.setFromCamera(normalizedPosition, camera);
const intersectedObjects =
this.raycaster.intersectObjects(scene.children, true);
if (intersectedObjects.length>0) {
console.log('intersectedObjects[ 0 ].object.geometry.type = ' +
intersectedObjects[ 0 ].object.geometry.type);
for (let i = 0; i < allJoints.length; i++) {
if (intersectedObjects[0].object === allJoints[i].dot) {
this.pickedObject0 = intersectedObjects[0].object;
this.pickedObjectSavedColor0 =
this.pickedObject0.material.color.getHex();
this.pickedObject0.material.color.setHex(0xFF0000);
}
}
for (let i = 0; i < allBeams.length; i++) {
if (intersectedObjects[0].object === allBeams[i].beam){
console.log('allBeams = ' + i);
}
}
}}}
//renderer:
function render() {if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
pickHelper.pick(pickPosition, scene, camera);
renderer.render(scene, camera);
requestAnimationFrame(render);
}

Three.js animation mixer not playing?

Ok I have an fbx successfully loaded into Three.js exported from blender with all animation boxes checked. Ive tried all export settings from blender so that's not it.
This is how I bring the model in, and I am able to accurately determine whether the fbx has animations or not:
var geoFromScene = new THREE.Geometry();
var FBXLoader = require('three-fbx-loader');
var loader = new FBXLoader();
loader.load(string, function ( object ) {
object.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
geoFromScene = (new THREE.Geometry()).fromBufferGeometry(child.geometry);
}
} );
var theModel = new THREE.Mesh();
theModel.geometry = geoFromScene;
theModel.material = material;
theModel.position.set(5,5,-8);
//theModel.rotation.set(new THREE.Vector3( 0, MATH.pi/2, 0));
theModel.scale.set(0.1, 0.1, 0.1);
localThis.scene.add(theModel);
localThis.mixer = new THREE.AnimationMixer(theModel);
if(theModel.animations[0])
{
var action = localThis.mixer.clipAction(theModel.animations[0]);
action.play();
} else {
console.log("No animations");
}
} );
In update (which does work, because I can print the animation.time):
this.mixer.update(this.clock.getDelta());
And yet the model is just static. Whats wrong here?
UPDATE:
code from example copy pasted-
var geoFromScene = new THREE.Geometry();
var FBXLoader = require('three-fbx-loader');
var loader = new FBXLoader();
loader.load( string, function ( object ) {
localThis.mixer = new THREE.AnimationMixer( object );
var action = localThis.mixer.clipAction( object.animations[ 0 ] );
action.play();
object.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
} );
object.position.set(5,5,-8)
object.scale.set(0.1, 0.1, 0.1);
localThis.scene.add( object );
} );
in animate-
this.mixer.update(this.clock.getDelta());
All I get is the model armature it seems -
New approach:
var FBXLoader = require('wge-three-fbx-loader'); //https://www.npmjs.com/package/wge-three-fbx-loader
var loader = new FBXLoader();
loader.load( string, function ( object ) {

THREE.JS - Raycaster performance on OBJ+MTL for collision detection (FirstPersonControls.js)

This is the upload of OBJ+MTL(used as texture)
var ambientLight = new THREE.AmbientLight(0xf0f0f0, '.$dbData['light_drone'].');
scene.add(ambientLight);
var modelloOBJ;
var fileMTL = "./Uploads/'.$dbData['id'].'/Drone/";
console.log("fileMTL", fileMTL);
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setTexturePath(fileMTL);
mtlLoader.setPath(fileMTL);
mtlLoader.load("'.$dbData['file_drone'].'.mtl", function( materials ) {
materials.preload();
var loader = new THREE.OBJLoader();
loader.setMaterials(materials);
loader.load(fileMTL+"'.$dbData['file_drone'].'"+".obj", function ( modello ) {
modelloOBJ = modello;
modello.rotateOnWorldAxis(new THREE.Vector3(1,0,0), Math.PI * 3 / 2);
new THREE.Vector3(1,0,0);
scene.add(modello);
document.getElementById("ProgressCircle").style.visibility = "hidden";
modelloOBJ.boundingBox = (new THREE.Box3()).setFromObject(modello);
camera.lookAt(modelloOBJ.position); //
var vmax =modelloOBJ.boundingBox.max;
var vmin = modelloOBJ.boundingBox.min;
var centroX = ( vmax.x + vmin.x )/2;
var centroY = vmin.y;
var centroZ = ( vmax.z + vmin.z )/2;
},
And these are the raycaster(S)
function animate() {
var delta = clock.getDelta();
controls.update(delta);
var vector = new THREE.Vector3(0,-1,0);
vector = camera.localToWorld(vector);
vector.sub(camera.position);
var raycaster = new THREE.Raycaster(camera.position,vector);
var intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
console.log(intersects[0].distance);
if(intersects[0].distance<1.21){
camera.position.y=(camera.position.y+(1.21-intersects[0].distance));
}
if(intersects[0].distance>1.36&&intersects[0].distance<1.45){
camera.position.y=(camera.position.y-(intersects[0].distance-1.215));
}
}
var vector1 = new THREE.Vector3(0,0,-1);
vector1 = camera.localToWorld(vector1);
vector1.sub(camera.position);
var raycaster1 = new THREE.Raycaster(camera.position,vector1);
var intersects1 = raycaster1.intersectObjects(scene.children, true);
if (intersects1.length > 0) {
console.log(intersects1[0].distance);
if(intersects1[0].distance<0.3){
controls.moveForward=false;
controls.w=false;
}else{
controls.w=true;
}
THREE.Vector3(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z);
}else controls.w=true;
stats.update(renderer);
console.log(stats.domElement.firstChild.childNodes["0"].innerHTML);
requestAnimationFrame( animate );
renderer.render( scene, camera );
console.log(renderer.info.render.faces);
}
animate();
PROBLEM: When I add the raycasts to the code, the performances drop considerably. From 60 fps to 20 fps.
Another thing that I tried is this:
var l=1;
function animate() {
var delta = clock.getDelta();
controls.update(delta);
if (l%5==0)
{
var vector = new THREE.Vector3(0,-1,0);
vector = camera.localToWorld(vector);
vector.sub(camera.position);
var raycaster = new THREE.Raycaster(camera.position,vector);
var intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
console.log(intersects[0].distance);
if(intersects[0].distance<1.21){
camera.position.y=(camera.position.y+(1.21-intersects[0].distance));
}
if(intersects[0].distance>1.36&&intersects[0].distance<1.45){
camera.position.y=(camera.position.y-(intersects[0].distance-1.215));
}
}
var vector1 = new THREE.Vector3(0,0,-1);
vector1 = camera.localToWorld(vector1);
vector1.sub(camera.position);
var raycaster1 = new THREE.Raycaster(camera.position,vector1);
var intersects1 = raycaster1.intersectObjects(scene.children, true);
if (intersects1.length > 0) {
console.log(intersects1[0].distance);
if(intersects1[0].distance<0.3){
controls.moveForward=false;
controls.w=false;
}else{
controls.w=true;
}
THREE.Vector3(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z);
}else controls.w=true;
}
stats.update(renderer);
requestAnimationFrame( animate );
renderer.render( scene, camera );
console.log(renderer.info.render.faces);
l=l+1;
}
animate();
In this case they are performed once every five times the animate function is called.
The fps have gone from 20 to 35. But that's inefficient since I use the raycasters for collisions on walls and ground.
The size of the model is less than 20mb. Also, when raycaster isn't working, the model is able to stay on 60fps without any drop.
Remember that I need collision in the model.
Is there a way to improve the performance?
Maybe I should not use raycaster?
P.S. Controls are FirstPersonControls.js, a little bit modified by me.

enableAngularMotor in ammo js doesn't seem to function when changing object from BoxBufferGeometry to BufferGeometry

I'm creating a pinball game using three.js and ammo.js and am having issues rotating the flippers using enableAngularMotor when using bufferGeometry cloned from a loaded GLTF file, when the same code using a BoxBufferGeometry works ok.
The original test build I had a threejs BoxBufferGeometry block wired up to the hinge, and got that working. I have shifted the code to the loaded callback on the gltf loader in three.js and the geometry from the new nicer model clones ok, and can be read, but the same hinge wiring code doesn't seem to rotate the flipper.
var loader = new THREE.GLTFLoader();
let mass = ms;
loader.load(whichFile,function ( gltf ) {
gltf.animations; // Array<THREE.AnimationClip>
gltf.scene; // THREE.Scene
gltf.scenes; // Array<THREE.Scene>
gltf.cameras; // Array<THREE.Camera>
gltf.asset; // Object
gltf.scene.scale.set(Size[0],Size[1],Size[2]);
gltf.scene.position.set(Pos[0],Pos[1],Pos[2]);
gltf.asset.castShadow = true;
gltf.asset.receiveShadow = true;
gltf.scene.traverse(function (child) {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
let geometry = new THREE.Geometry().fromBufferGeometry( child.geometry );
let qCircX = new THREE.Mesh(geometry, child.material);
//scene.add(qCircX);
qCircX.scale.set(Size[0],Size[1],Size[2]);
qCircX.position.set(Pos[0],Pos[1],Pos[2]);
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
child.geometry = new THREE.BufferGeometry().fromGeometry( geometry );
let qCirc = new THREE.Mesh(child.geometry, child.material);
qCirc.scale.set(Size[0],Size[1],Size[2]);
qCirc.position.set(Pos[0],Pos[1],Pos[2]);
qCirc.castShadow = true;
qCirc.receiveShadow = true;
scene.add(qCirc);
let transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin( new Ammo.btVector3( Pos[0],Pos[1],Pos[2] ) );
let motionState = new Ammo.btDefaultMotionState( transform );
let vertices, face, triangles = [];
vertices = geometry.vertices;
vertices = generateVertexMesh(geometry, vertices, triangles);
let i, triangle, triangle_mesh = new Ammo.btTriangleMesh;
let _vec3_1 = new Ammo.btVector3(0,0,0);
let _vec3_2 = new Ammo.btVector3(0,0,0);
let _vec3_3 = new Ammo.btVector3(0,0,0);
for ( i = 0; i < triangles.length; i++ ) {
triangle = triangles[i];
_vec3_1.setX(triangle[0].x);
_vec3_1.setY(triangle[0].y);
_vec3_1.setZ(triangle[0].z);
_vec3_2.setX(triangle[1].x);
_vec3_2.setY(triangle[1].y);
_vec3_2.setZ(triangle[1].z);
_vec3_3.setX(triangle[2].x);
_vec3_3.setY(triangle[2].y);
_vec3_3.setZ(triangle[2].z);
triangle_mesh.addTriangle(
_vec3_1,
_vec3_2,
_vec3_3,
true
);
}
let colSurround = new Ammo.btBvhTriangleMeshShape( triangle_mesh, true, true );
let localInertia = new Ammo.btVector3( 0, 0, 0 );
colSurround.calculateLocalInertia( mass, localInertia );
let rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, colSurround, localInertia );
let body = new Ammo.btRigidBody( rbInfo );
body.setRestitution(rest);
qCirc.userData.physicsBody = body;
qCirc.name = objName;
collidableMeshList.push(qCirc);
if(addToScoreColliders){
scoreList.push(objName);
}
//console.log(ballArray);
if ( mass > 0 ) {
rigidBodies.push( qCirc );
//console.log(rigidBodies);
body.setActivationState( 4 );
}
physicsWorld.addRigidBody( body );
if(objName == "flipperL"){
let pos = {x:-16,y:0,z:27}, posBumper = {x:0,y:0,z:0}, scaleHinge = {x:1,y:1,z:1}, scaleBumper = {x:1,y:1,z:1}, quat = {x:0,y:0,z:0,w:1}, quatBumper = {x:0,y:0,z:0,w:1}, mass = 0, massBumper = 9;
// create the hinge and bumper objects
leftHinge = createPhysicsGeometry(0xff0000,pos,scaleHinge,quat,mass,"leftHinge",0,[0,0], true);
// Hinge constraint to move the bumper
let pivotA = new Ammo.btVector3( 0, 0.5, 0 ),pivotB = new Ammo.btVector3( -1.5, -3, 0 ),axis = new Ammo.btVector3( 0, 1, 0 );
leftHingeConstraint = new Ammo.btHingeConstraint( leftHinge.userData.physicsBody, qCirc.userData.physicsBody, pivotA, pivotB, axis, axis, true );
leftHingeConstraint.setLimit(0, Math.PI/2 * 0.5, 0.9, 0.3, 1);
physicsWorld.addConstraint( leftHingeConstraint, true );
leftHingeActive = true;
bumperGeoms[0] = qCirc;
}
if(objName == "flipperR"){
let pos = {x:16,y:0,z:27}, posBumper = {x:0,y:0,z:0}, scaleHinge = {x:1,y:1,z:1}, scaleBumper = {x:1,y:1,z:1}, quat = {x:0, y:0, z:0, w: 1},quatBumper = {x:0,y:0,z:0,w:1}, mass = 0, massBumper = 9;
// create the hinge and bumper objects
rightHinge = createPhysicsGeometry(0xff0000,pos,scaleHinge,quat,mass,"rightHinge",0,[0,0], true);
// Hinge constraint to move the bumper
let pivotA = new Ammo.btVector3( 0, 0.5, 0 ),pivotB = new Ammo.btVector3( 1.5, -3, 0 ),axis = new Ammo.btVector3( 0, 1, 0 );
//console.log(bumperArray);
rightHingeConstraint = new Ammo.btHingeConstraint( rightHinge.userData.physicsBody, qCirc.userData.physicsBody, pivotA, pivotB, axis, axis, true );
rightHingeConstraint.setLimit(-Math.PI/2 * 0.5, 0, 0.9, 0.3, 1);
physicsWorld.addConstraint( rightHingeConstraint, true );
rightHingeActive = true;
bumperGeoms[1] = qCirc;
}
}
});
},
// called while loading is progressing
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened : ' + error );
});
}```
When an area of the screen is pressed, the angular motor is enabled with a multiplier to turn the flipper, whereas now the flipper doesn't rotate. I don't get any errors logging to the console, and logging geometry to check it's loaded ok is fine.
Ok, so I took an alternate approach and make the gltf scene a child of the existing simple cuboid flipper.

How to use multiple texture use in `OBJLoader` three.js?

How to multiple textures material use in Three.js?.Here three textures using and one 3D sofa.obj format file. I tried a lot of time. Below my code. What do I mistake in my code?
var loader = new THREE.OBJLoader();
var textureLoader = new THREE.TextureLoader();
threeDTexture = new THREE.ImageUtils.loadTexture( 'models/Sofa/Texturses/paradis_beige.jpg' );
threeDTexture2 = new THREE.ImageUtils.loadTexture( 'models/Sofa/Texturses/1.jpg' );
threeDTexture3 = new THREE.ImageUtils.loadTexture( 'models/Sofa/Texturses/2.jpg' );
loader.load('models/Sofa/sofa.obj', function (object) {
var geo = object.children[0].geometry;
var mats = [threeDTexture, threeDTexture2,threeDTexture3];
objct = new THREE.Mesh(geo, mats);
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material = objct;
}
});
object.position.x = posX;
object.position.y = 0;
object.position.z = posZ;
var size = new THREE.Box3().setFromObject(object).getSize();
object.scale.set(width/size.x, height/size.y, depth/size.z);
scene1.add(object);
console.log(object);
console.log(size);
console.log(width/size.x, height/size.y, depth/size.z);
},
function ( xhr ) {
returnValue = ( xhr.loaded / xhr.total * 100 ) + '% loaded';
console.log(returnValue);
},
function ( error ) {
console.log( 'An error happened' );
}
);
break
first, the textureLoader declared on line-2 is not in use;
second, belowing mats is not an Material Array, you should new MeshBasicMaterial(...) to wrap a texture, see the document here.
var mats = [threeDTexture, threeDTexture2,threeDTexture3];
objct = new THREE.Mesh(geo, mats); // Mesh will not accept this mats param!

Categories