Raycaster Not Detecting Certain Objects - Threejs - javascript

I have been trying to add a raycaster to my game for some time now. After testing a lot of situations, I have found that the raycaster IS functioning.
There is a scene that contains various enemy objects, and a floor. Same scene. A raycaster is supposed to be sending a ray out from the cursor to things on this scene. However, only the floor is detected as a collision, while all of the other objects cannot be detected.
This is currently being hosted here: http://aceverything.com/cskelly/project2/
Here are some relevant fragments:
function render() {
//console.log(object.position);
// LATER: IMPLEMENT NICER GUN ANIMATION
global_m60.position.x += ( mouseX - global_m60.position.x ) * .45;
if(-mouseY < 1){
global_m60.position.y += ( - mouseY - global_m60.position.y ) * .45;
}
else{
global_m60.position.y = 1;
}
for(i = 0; i < enemy_tanks.length ; ++i){
enemy_tanks[i].position.z += .01;
}
projector.unprojectVector( mouse, camera );
mouse.sub( camera.position );
mouse.normalize();
var raycaster = new THREE.Raycaster( camera.position, mouse );
var intersects = raycaster.intersectObjects( scene.children );
for( var i = 0; i < intersects.length; ++i ){
intersects[i].object.material.map = THREE.ImageUtils.loadTexture("models/red_checkerboard.jpg");
intersects[i].object.material.needsUpdate = true;
console.log("RAY STUFF HAPPENING");
}
camera.lookAt( scene.position );
renderer.clear();
renderer.render( scene, camera );
renderer.clearDepth();
renderer.render( sandbag_scene, camera );
renderer.clearDepth();
renderer.render( scene2, camera );
framecount += 1;
}
Me loading in the objects that are NOT being detected:
// ENEMIES
mtlLoader.setPath('models/');
for(i = 0; i < max_tanks; ++i){
mtlLoader.load( 'ultimate-tank.mtl', function( materials2 ) {
//materials2.needsUpdate = true;
materials2.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials2 );
//objLoader.setPath( 'obj/male02/' );
objLoader.load( 'models/ultimate-tank.obj', function ( object2 ) {
object2.position.x = (Math.random()*30)-15;
object2.position.y = -1;
object2.position.z = 220;
//object2.position.y = -95;
global_enemy_1 = object2;
global_enemy_1.rotateY(1.507);
//global_enemy_1.renderOrder = 1;
enemy_tanks.push(global_enemy_1);
scene.add( global_enemy_1 ); // THIS HAS TO BE DONE LIKE THIS.
//global_enemy_1.material.color.set( 0xff0000 ); THIS DOESNT WORK...
}, onProgress, onError );
});
}
The original intention was to change the material of objects the raycaster comes into contact with to visually show me that there was a collision. The material part is irrelevant. The part that matters is that:
var intersects = raycaster.intersectObjects( scene.children );
clearly isn't giving me the objects. What gives?

Related

I can't get the Perspective Camera to follow the player. Camera is stuck to the World origin

I'm new to THREE.js.
For the purpose of learning JavaScript and THREE.js I made this (kind of) Mini Game where player is able to explore space and different space objects (work in process).
Anyways, my problem is when the scene and objects are rendered, and you start moving your cuboid, camera doesn't move with it (actually it doesn't move away from the world origin) only rotates in place looking at the player's object.
This is the code:
import './style.css'
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// Scene object
const scene = new THREE.Scene();
const clock = new THREE.Clock();
var cameraObject, keys;
var temp = new THREE.Vector3;
var dir = new THREE.Vector3;
var a = new THREE.Vector3;
var b = new THREE.Vector3;
var distance = 0.3;
var velocity = 0.0;
var speed = 0.0;
// Camera object
const perspectiveCamera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 50, 1e7 )
perspectiveCamera.position.setZ(80);
cameraObject = new THREE.Object3D;
cameraObject.add(perspectiveCamera);
// perspectiveCamera.lookAt(scene.position)
// Renderer object
const renderer = new THREE.WebGLRenderer({
});
document.body.appendChild( renderer.domElement );
// Renderer
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.render( scene, perspectiveCamera )
// Light
// PointLight
const pointLight = new THREE.PointLight(0xffffff)
pointLight.position.set(20,20,20)
// AmbientLight
const ambientLight = new THREE.AmbientLight(0xffffff)
scene.add(pointLight, ambientLight)
// Helper classes
const lightHelper = new THREE.PointLightHelper(pointLight);
const axesHelper = new THREE.AxesHelper(5);
scene.add(lightHelper, axesHelper)
// Controls
const controls = new OrbitControls(perspectiveCamera, renderer.domElement);
controls.movementSpeed = 1000;
controls.domElement = renderer.domElement;
controls.rollSpeed = Math.PI / 24;
controls.autoForward = false;
controls.dragToLook = false;
let cube = new THREE.Group();
const cubeGeometry = new THREE.BoxGeometry(3,10,3)
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x238423 })
const cube1 = new THREE.Mesh( cubeGeometry, cubeMaterial );
cube.add(cube1)
scene.add(cube)
// Objects
// Stars
function addStar() {
const geometry = new THREE.SphereGeometry(0.25, 24, 24);
const material = new THREE.MeshStandardMaterial( { color: 0xffffff })
const star = new THREE.Mesh( geometry, material );
const [x, y, z] = Array(3).fill().map(() => THREE.MathUtils.randFloatSpread( 500 ));
star.position.set(x,y,z);
scene.add(star)
}
Array(1500).fill().forEach(addStar)
let keyState = {};
keys = {
a: false,
s: false,
d: false,
w: false,
q: false,
e: false,
space: false,
shiftleft: false,
};
document.addEventListener("keydown", function(e) {
console.log(e.code);
const key = e.code.replace('Key', '').toLowerCase();
if ( keys[ key ] !== undefined )
keys[ key ] = true;
});
document.body.addEventListener( 'keyup', function(e) {
const key = e.code.replace('Key', '').toLowerCase();
if ( keys[ key ] !== undefined )
keys[ key ] = false;
});
// Animate objects
function animate() {
const delta = clock.getDelta();
requestAnimationFrame( animate );
speed = 0.0;
if ( keys.w )
cube.rotateX(-0.03);
if ( keys.s )
cube.rotateX(0.03);
if ( keys.a )
cube.rotateZ(0.03);
if ( keys.d )
cube.rotateZ(-0.03);
if ( keys.q )
cube.rotateY(-0.06);
if ( keys.e )
cube.rotateY(0.06);
if ( keys.space )
speed = 0.9;
velocity += ( speed - velocity ) * .3;
cube.translateY( velocity );
if ( keys.shiftleft )
speed = 0.9*5;
velocity += ( speed - velocity ) * .3;
cube.translateY( velocity );
perspectiveCamera.lookAt( cube.position );
controls.update(delta);
renderer.render( scene, perspectiveCamera );
}
animate();
I'm actually trying to make some sort of Third Person Camera. In order to make the camera follow this little cuboid and rotate as the object rotates on the screen.
First attempt:
const idealOffset = new THREE.Vector3(0, 0, -80);
function animate(){
...
perspectiveCamera.position.set(0, 0, -80);
perspectiveCamera.applyQuaternion(cube.rotation);
perspectiveCamera.position.add(cube.position);
perspectiveCamera.lookAt(idealOffset);
...
}
Camera moves with the cube but after a few seconds turns back to look at the world origin.
Second attempt:
// Animate objects
requestAnimationFrame( function animate(milliseconds) {
const delta = clock.getDelta();
let moveDistance = 10*delta;
let rotateAngle = Math.PI/2*delta;
requestAnimationFrame( animate );
speed = 0.0;
if ( keys.w ){
cube.rotateX(-0.03);
}
if ( keys.s ){
cube.rotateX(0.03);
}
let rotation_matrix = new THREE.Matrix4().identity();
if ( keys.a ){
cube.rotateZ(rotateAngle);
}
if ( keys.d ){
cube.rotateZ(-rotateAngle);
}
if ( keys.q ){
cube.rotateY(-rotateAngle);
}
if ( keys.e ){
cube.rotateY(rotateAngle);
}
if ( keys.space ){
speed = 0.9;
velocity += ( speed - velocity ) * .3;
cube.translateY( velocity );
}
if ( keys.shiftleft ){
speed = 0.9*5;
velocity += ( speed - velocity ) * .3;
cube.translateY( velocity );
}
var relativeCameraOffset = new THREE.Vector3(0,-80,0);
var cameraOffset = relativeCameraOffset.applyMatrix4(cube.matrixWorld);
perspectiveCamera.position.x = cameraOffset.x;
perspectiveCamera.position.y = cameraOffset.y;
perspectiveCamera.position.z = cameraOffset.z;
perspectiveCamera.lookAt(cube.position);
renderer.render( scene, perspectiveCamera );
});
Camera does follow my cube but when I rotate it 180 DEG on x-axis it turns upside-down,it's fixed to the player and doesn't move smoothly and it looks directly at the player (back side of the cube).
This inspired me to read more about rotating/translating in 3d space, about Euler angles, quaternions, gimbal lock. I'm just not quite sure how to apply these scary quaternions to my project...
Any help, reference, whatever would be greatly appreciated.
Thanks in advance.
If you're using OrbitControls, just update the .target property to the position of the object you want it to focus on. When the cube moves, the camera will move with it. See here for more on the docs:
orbitControls.target = cube.position;
I'm not sure why you're doing this: cameraObject.add(perspectiveCamera);. This doesn't do anything if you don't make any changes to cameraObject.

make a scene three.js move

i am working on an the application to optimize packs in a truck.
i use three.js to show the 3D results.
below is the code i use.
i want to make it a moving scene , i want the boxes to show one by one move towards there position inside the truck
curent 3D result
i don't know how to do that, any idea please ?
var camera, controls, scene, renderer;
init();
//render(); // remove when using next line for animation loop (requestAnimationFrame)
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xcccccc );
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 400, 200, 0 );
// controls
controls = new THREE.MapControls( camera, renderer.domElement );
//controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop)
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.25;
controls.screenSpacePanning = false;
controls.minDistance = 1;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2;
// world
var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
geometry.translate( 0, 0.5, 0 );
console.log("this is the list we get " , result ) ;
for (Element in result)
{
if( result[Element].Object === "Box" || result[Element].Object === "BigBox" )
{
var material = new THREE.MeshPhongMaterial( { opacity: 0.25 , color: getRandomColor() , transparent: false } );
var material2 = new THREE.MeshPhongMaterial( { opacity: 0.25 , color: getRandomColor() , transparent: true } );
var mesh = new THREE.Mesh( geometry, material );
if(result[Element].Object === "BigBox")
{
mesh.opacity = 0.01
mesh = new THREE.Mesh( geometry, material2 );
}
mesh.opacity = 0.75
mesh.position.x = result[Element].CenterPoint.X ;
mesh.position.y = result[Element].CenterPoint.Y;
mesh.position.z = result[Element].CenterPoint.Z;
mesh.scale.x = result[Element].Dimentions.X;
mesh.scale.y = result[Element].Dimentions.Y;
mesh.scale.z = result[Element].Dimentions.Z;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
}
}
// lights
var light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
var light = new THREE.DirectionalLight( 0x002288 );
light.position.set( - 1, - 1, - 1 );
scene.add( light );
var light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
requestAnimationFrame( animate );
controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true
render();
}
function render() {
renderer.render( scene, camera );
}
var link = document.createElement( 'a' );
link.style.display = 'none';
document.body.appendChild( link ); // Firefox workaround, see #6594
function save( blob, filename ) {
link.href = URL.createObjectURL( blob );
link.download = filename;
link.click();
// URL.revokeObjectURL( url ); breaks Firefox...
}
function saveString( text, filename ) {
save( new Blob( [ text ], { type: 'text/plain' } ), filename );
}
function saveArrayBuffer( buffer, filename ) {
save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
}
So, if I understand you well, you have two tasks to do:
Move the camera
Show the boxes one by one
MOVE THE CAMERA
Create a global variable:
let previousTime = performance.now();
Then create an update() function and call it in your animate() function:
function update() {
const time = performance.now();
const deltaTime = time - previousTime; // this variable tells us, how much time has passed since the last frame (the last call of update() function)
previousTime = time;
moveCamera(deltaTime);
}
And the moveCamera() function:
function moveCamera(deltaTime) {
// you have to set this variable by your preferences
const SPEED = 10;
// This is the vector that determines the direction in which your camera moves.
// In this case the camera will move only along the X axis.
// Set it by your preferences depending on which way you want the camera to move.
const MoveCameraDirection = new THREE.Vector3(1,0,0);
// You have to multiply it by SPEED and by deltaTime
// Multiplying by deltaTime is to make the movement speed independent of the number of FPS
MoveCameraDirection.multiplyScalar( SPEED * deltaTime );
camera.position.add(MoveCameraDirection);
}
Camera movement is done.
SHOW THE BOXES ONE BY ONE
You need to have a handler to your boxes. So create a variable boxes and place your objects there:
var camera, controls, scene, renderer, boxes = [];
...
// before scene.add( mesh );
mesh.visible = false ; // set all boxes invisible at the begin
boxes.push(mesh);
Now, in your update() function, under moveCamera(deltaTime) you can call showBoxes().
The question is: when to show the boxes? Let's say we want to show the boxes, when the camera passes them.
Let's create this function:
function showBoxes() {
// I don't know which direction is your camera moving, so let's assume it's moving along the X axis (like in previos function). You can use any other axis as well.
// We want to show only the boxes that have **box.position.x < camera.position.x** and are not currenly displayed
const boxesToShow = boxes.filter( box => !box.visible && box.position.x < camera.position.x);
for( let i=0 ; i < boxesToShow.length ; i++ ) {
// we can do it just like this:
// boxesToShow[i].visible = true;
// but we don't want them to show all at once, so let's use a timer and display them one by one in 1 second intervals
setTimeout( () => boxesToShow[i].visible=true, i * 1000 );
}
}

Three.js THREE.Projector has been moved to

I understand there is no THREE.projector in version 71 (see the deprecated list), but I don't understand how to replace it, particularly in this code that detects which object has been clicked on:
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(objects);
if (intersects.length > 0) {
clicked = intersects[0];
console.log("my clicked object:", clicked);
}
There is now an easier pattern that works with both orthographic and perspective camera types:
var raycaster = new THREE.Raycaster(); // create once
var mouse = new THREE.Vector2(); // create once
...
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( objects, recursiveFlag );
three.js r.84
The THREE.JS raycaster documentation actually gives all the relevant information that is laid out in these answers as well as all the missing points that may be difficult to get your head around.
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray var intersects =
raycaster.intersectObjects( scene.children );
for ( var i = 0; i < intersects.length; i++ ) {
intersects[ i ].object.material.color.set( 0xff0000 );
}
renderer.render( scene, camera );
}
window.addEventListener( 'mousemove', onMouseMove, false );
window.requestAnimationFrame(render);`
Hope it helps.
You can use the latest recommended version as stated above.
To get your specific code working, replace:
projector.unprojectVector( vector, camera );
with
vector.unproject(camera);
I noted that all this that was said before is fine in a full window I think, but if you have other things besides a canvas on the page you need to get the click events target's offset and remove it.
e = event and mVec is a THREE.Vector2
let et = e.target, de = renderer.domElement;
let trueX = (e.pageX - et.offsetLeft);
let trueY = (e.pageY - et.offsetTop);
mVec.x = (((trueX / de.width) * 2) - 1);
mVec.y = (((trueY / de.height) * -2) + 1);
wup.raycaster.setFromCamera( mVec, camera );
[Then check for intersections, etc.]
Looks like you need to watch for dragging (mouse movements) too or releasing from a drag will trigger an unwanted click when using window.addEventListener( 'click', function(e) {<code>});
[You'll notice I put the negative sign where it's more logical as well.]
https://github.com/mrdoob/three.js/issues/5587
var vector = new THREE.Vector3();
var raycaster = new THREE.Raycaster();
var dir = new THREE.Vector3();
...
if ( camera instanceof THREE.OrthographicCamera ) {
vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, - 1 ); // z = - 1 important!
vector.unproject( camera );
dir.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
raycaster.set( vector, dir );
} else if ( camera instanceof THREE.PerspectiveCamera ) {
vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 ); // z = 0.5 important!
vector.unproject( camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
}
var intersects = raycaster.intersectObjects( objects, recursiveFlag );
Objects = array of objects of type Object3D to check for intersection with the ray. Can be everything in your scene, but might be inefficient if you have a lot of stuff there.
recursiveFlag = If true, it also checks all descendants. Otherwise it only checks intersection with the object. Default is true.
docs
I wanted to share my experiences with more details doing this since I feel the answers above do not really explain the details to get this going.
In my case I was using STL_Viewer, you can locate it here (https://www.viewstl.com/plugin/), it helps create a threeJS scene and supplies you with the camera and scene that you will need for this to work.
function mouse_click_normalize(clientX,clientY,offset_left,offset_top){
var mouse = new THREE.Vector2(); // create once
var c = document.getElementById("stl_contEye").childNodes[0];//get canvas element created in here
mouse.x = ( (event.clientX-offset_left) / (c.clientWidth) * 2) - 1;
mouse.y = - ( (event.clientY-offset_top) / (c.clientHeight) ) * 2 + 1;
console.log(`clientX=${clientX},clientY=${clientY},mouse.x=${mouse.x},mouse.y=${mouse.y}\nwidth=${c.clientWidth},height=${c.clientHeight},offset_left=${offset_left},offset_top=${offset_top}`);
return mouse;
}
Above is my function that will automatically normalize the x,y coordinates in the space that was clicked (between -1 and 1), as this is what the raycaster requires for it's setFromCamera function.
Because you may be clicking in a section of the screen with offsets I programmed it this way so it will handle no matter how it's positioned in the DOM. Just replace "stl_contEye" with the name of your div that will contain the rendered ThreeJS or STLViewer in my case.
function model_clicked(model_id, e, distance, click_type){
console.log(e);
var pos = new THREE.Vector3(); // create once and reuse
// var vec = new THREE.Vector3(); // create once and reuse
var camera = stl_viewer1.camera;
var scene = stl_viewer1.scene;
// vec.unproject( camera );
// vec.sub( camera.position ).normalize();
// //var distance2 = (distance - camera.position.z) / vec.z;
// pos.copy( camera.position ).add( vec.multiplyScalar( distance ) );
var mouse_coords_normalized = mouse_click_normalize(e.clientX,e.clientY,e.srcElement.offsetLeft,e.srcElement.offsetTop);
var raycaster = new THREE.Raycaster(); // create once
raycaster.setFromCamera( mouse_coords_normalized, camera );
//console.log(raycaster);
//console.log(scene);
var intersects = raycaster.intersectObjects( scene.children, true );
if(intersects.length > 0){
console.log(intersects);
console.log(intersects[0].point);
pos = intersects[0].point
}
}
By doing it this way, you will get the exact point in 3D space from where you clicked. The function model_clicked simply returns an event that holds the clientX or clientY, you just have to get this yourself somehow if you are not using STLViewers event for detecting a click. There are many examples above for this from the other answers.
I hope this helps someone trying to figure this out with or without stl_viewer
camera.shooting = Date.now()
document.getElementById("div5").addEventListener("mousedown", mousedown);
document.getElementById("div5").addEventListener("mouseup", mouseup);
document.getElementById("div5").addEventListener( 'mousemove', renderq);
function mousedown(event) {
camera.calculator = 1;
}
function mouseup(event) {
camera.calculator = 0;
}
function renderq(event){
if( ai2bcz.length > 0 && ai_key3.length > 0 ){
if( camera.calculator > 0 ){
camera.mouse = new THREE.Vector2();
var fleece = document.getElementById("div5").scrollHeight;
var fleeceb = document.getElementById("div5").scrollWidth;
var fees = (event.clientX / fleeceb) - 1;
var feesa = - (event.clientY / fleece) + 1;
camera.mouse.x = fees ; camera.mouse.y = feesa;
var sphereMaterialc = new THREE.MeshBasicMaterial({color: 0x0099FF});
var sphereGeoc = new THREE.SphereGeometry(5, 5, 5);
var spherec = new THREE.Mesh(sphereGeoc, sphereMaterialc);
spherec.position.set(camera.position.x, camera.position.y, camera.position.z);
spherec.raycaster = new THREE.Raycaster();
spherec.raycaster.setFromCamera( camera.mouse, camera);
spherec.owner = 'player';
spherec.health = 100;
bulletsc.push(spherec);
scene.add(spherec);
camera.lastshot = Date.now();
camera.shooting = Date.now();
}
}
}
function render() { const controlscamera = new FirstPersonControls(camera);
controlscamera.update(100);
if( ai2bcz.length > 0 && ai_key3.length > 0 ){
if( camera.calculator > 0 && camera.shooting + 25 < Date.now() ){
renderq(event)
camera.shooting = Date.now();
}}
if(bulletsc.length > 1){
for (var i = 0; i < bulletsc.length - 1; i++) {
var bu = bulletsc[i], pu = bu.position, du = bu.raycaster.ray.direction;
if(bu.owner == "player"){
var enemybulletspeeda = window.document.getElementById("bulletsplayerspeed").value;
bu.translateX(enemybulletspeeda * du.x);
bu.translateY(enemybulletspeeda * du.y);
bu.translateZ(enemybulletspeeda * du.z);
}
}}
renderer.render( scene, camera ); }

three.js can't select the objects with Raycaster

I am having the following function to get the selected object, so my function is here
function onMouseDown(event) {
console.log(event);
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// find intersections
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
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());
ray.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = ray.intersectObjects( scene.children );
//console.log(intersects);
console.log(scene.children);
if ( intersects.length > 0 ) {
var clickedObject = intersects[0].object;
console.log('objects '+intersects[ 0 ].object);
console.log('objects id'+intersects[ 0 ].object.id);
console.log('objects name'+intersects[ 0 ].object.name);
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
console.log('INTERSECTED '+INTERSECTED);
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xff0000 );
}
} else {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
}
}
but am getting empty array on intersects , i tried my level best but i can't find any solution here, if i console the scene.children i can see all the attached objects of the scene
on before this am adding the object into scene as like here
var loader = new THREE.JSONLoader();
loader.load("uploads/accessories/3d/code/3dfile_"+file+".js",
function(geometry, object_material)
{
var object = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(object_material));
model = new THREE.Object3D();
model.id="Myid"+file;
model.name="Myname"+file;
model.userData ={ URL: "http://myurl.com" };
model.add(object);
model.position.set(x,y,z);
model.scale.set(obj_width,obj_height,obj_rotation);
model.opacity =2;
model.rotation.y = 600;
model.duration = 12000;
model.mirroredLoop = true;
model.castShadow = true;
model.receiveShadow = true;
console.log(model);
var smodel=model;
scene.add(smodel);
}
);
now i want to get the added model by new THREE.Object3D(); in onMouseDown but i can't, any one have the same issue ?
You need to pass in the recursive flag to Raycaster.intersectObjects().
intersects = ray.intersectObjects( scene.children, true );
Also, do not overwrite Object3D.id. Set Object3D.userData.id instead.
three.js r.68
You are using a MeshFaceMaterial which doesn't have a hex to grab. You have to get the 'materials array' from the 'material' like so:
for(var p =0; p < INTERSECTED.material.materials.length; p++){
INTERSECTED.currentHex = INTERSECTED.material.materials[p].emissive.getHex();
}
This way you are not selecting the MeshFaceMaterial but the MeshLambertMaterial or MeshBasicMaterial that you are using for each face. If you are using images as your textures I would suggest checking out Three.js material texture and color for a quick answer to that question.
Hope this was helpful!
Raycaster is little bit tricky I have added explanation
A typical setup need to be done for raycaster to work as follows:
first initialize a variable to hold mouse position from which we need to emit a ray
var mouse = {
x: 0,
y: 0
};
//object intersected
var INTERSECTED;
// pool of objects which can be selected
var objects = [];
// ray caster initialization
var raycaster = new THREE.Raycaster();
We need to create a Object3D parent to hold all child meshes
let mainGroup = new THREE.Object3D();
// add into object pool
objects.push(mainGroup);
scene.add(mainGroup);
// add mesh to be select by ray caster in Object3D parent
mainGroup.add(meshToSelect);
Add an event listener to get mouse X,Y position
document.addEventListener("mousemove", this.onDocumentMouseMove, false);
onDocumentMouseMove = event => {
event.preventDefault();
if (event && typeof event !== undefined) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
};
Now use raycaster like this:
renderScene = () => {
// Emit ray
raycaster.setFromCamera(mouse, this.camera);
// all intersects
var intersects = raycaster.intersectObjects(objects, true);
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
//if new item selected swap color of new item
//& revert color of previous item
if (INTERSECTED)
INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
INTERSECTED = intersects[0].object;
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
INTERSECTED.material.color.setHex(0xff0000);
}
} else {
// None selected then revert color of previous item
if (INTERSECTED)
INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
INTERSECTED = null;
}
if (this.renderer) this.renderer.render(scene, this.camera);
};
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.draggable = false;
EventsControls.onclick = function() {
console.log( this.focused.name );
}
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 );
}

Changing texture and color on Three.js collada object

I recently got three.js example from the official site working with my collada objects (.dae) using the ColladaLoader.js.
Now my question is, how do i change the loaded collada object color attribute and add a custom texture?? I tried adding the texture with no luck yet.
Here is my code (slightly changed from the original example):
function load_model(el) {
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats;
var camera, scene, renderer, objects;
var particleLight, pointLight;
var dae, skin;
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load( '/site_media/models/model.dae', function ( collada ) {
dae = collada.scene;
skin = collada.skins[ 0 ];
dae.scale.x = dae.scale.y = dae.scale.z = 0.90;
dae.updateMatrix();
init(el);
animate();
} );
function init(el) {
container = document.createElement( 'div' );
el.append( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set( 2, 2, 3 );
scene = new THREE.Scene();
scene.add( dae );
particleLight = new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
scene.add( particleLight );
// Lights
scene.add( new THREE.AmbientLight( 0xcccccc ) );
var directionalLight = new THREE.DirectionalLight(/*Math.random() * 0xffffff*/0xeeeeee );
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
directionalLight.position.normalize();
scene.add( directionalLight );
// pointLight = new THREE.PointLight( 0xffffff, 4 );
// pointLight.position = particleLight.position;
// scene.add( pointLight );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth/2, window.innerHeight/2 );
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth/2, window.innerHeight/2 );
}
//
var t = 0;
var clock = new THREE.Clock();
function animate() {
var delta = clock.getDelta();
requestAnimationFrame( animate );
if ( t > 1 ) t = 0;
if ( skin ) {
// guess this can be done smarter...
// (Indeed, there are way more frames than needed and interpolation is not used at all
// could be something like - one morph per each skinning pose keyframe, or even less,
// animation could be resampled, morphing interpolation handles sparse keyframes quite well.
// Simple animation cycles like this look ok with 10-15 frames instead of 100 ;)
for ( var i = 0; i < skin.morphTargetInfluences.length; i++ ) {
skin.morphTargetInfluences[ i ] = 0;
}
skin.morphTargetInfluences[ Math.floor( t * 30 ) ] = 1;
t += delta;
}
render();
stats.update();
}
function render() {
var timer = Date.now() * 0.0005;
camera.position.x = Math.cos( timer ) * 10;
camera.position.y = 2;
camera.position.z = Math.sin( timer ) * 10;
camera.lookAt( scene.position );
particleLight.position.x = Math.sin( timer * 4 ) * 3009;
particleLight.position.y = Math.cos( timer * 5 ) * 4000;
particleLight.position.z = Math.cos( timer * 4 ) * 3009;
renderer.render( scene, camera );
}
}
You can override your collada scene materials recursively with this kind of function. It goes through the whole hierarchy and assigns a material.
var setMaterial = function(node, material) {
node.material = material;
if (node.children) {
for (var i = 0; i < node.children.length; i++) {
setMaterial(node.children[i], material);
}
}
}
Use it like setMaterial(dae, new THREE.MeshBasicMaterial({color: 0xff0000}));
You could probably adapt that to modify the existing material properties instead of assigning a new one, if needed.
After many problems, we wrote a small hack in ColladaLoader.js taking the idea from #gaitat
witch basically replaces the old path to the textures from the images, passing some new ones in an array, and using regular expressions to parse the xml for the .png or .jpg under images tag. Not sure if there is an easier way but since support was limited we had to come up with a fix somehow
function parse( doc, imageReplace, callBack, url ) {
COLLADA = doc;
callBack = callBack || readyCallbackFunc;
if ( url !== undefined ) {
var parts = url.split( '/' );
parts.pop();
baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
}
parseAsset();
setUpConversion();
images = parseLib( "//dae:library_images/dae:image", _Image, "image" );
for(var i in imageReplace) {
var iR = imageReplace[i];
for(var i in images) {
var image = images[i];
var patt=new RegExp('[a-zA-Z0-9\-\_]*\/'+iR.name,'g');
//if(image.id==iR.id)
if(patt.test(image.init_from))
image.init_from = iR.new_image;
}//for
}
materials = parseLib( "//dae:library_materials/dae:material", Material, "material" );
effects = parseLib( "//dae:library_effects/dae:effect", Effect, "effect" );
geometries = parseLib( "//dae:library_geometries/dae:geometry", Geometry, "geometry" );
cameras = parseLib( ".//dae:library_cameras/dae:camera", Camera, "camera" );
controllers = parseLib( "//dae:library_controllers/dae:controller", Controller, "controller" );
animations = parseLib( "//dae:library_animations/dae:animation", Animation, "animation" );
visualScenes = parseLib( ".//dae:library_visual_scenes/dae:visual_scene", VisualScene, "visual_scene" );
morphs = [];
skins = [];
daeScene = parseScene();
scene = new THREE.Object3D();
for ( var i = 0; i < daeScene.nodes.length; i ++ ) {
scene.add( createSceneGraph( daeScene.nodes[ i ] ) );
}
// unit conversion
scene.position.multiplyScalar(colladaUnit);
scene.scale.multiplyScalar(colladaUnit);
createAnimations();
var result = {
scene: scene,
morphs: morphs,
skins: skins,
animations: animData,
dae: {
images: images,
materials: materials,
cameras: cameras,
effects: effects,
geometries: geometries,
controllers: controllers,
animations: animations,
visualScenes: visualScenes,
scene: daeScene
}
};
if ( callBack ) {
callBack( result );
}
return result;
};
One thing you can do is modify your collada model (dae file) locate the texture reference there and change it to your liking.
if ( url !== undefined ) {
var parts = url.split( '/' );
parts.pop();
baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
}
parseAsset();
setUpConversion();
images = parseLib( "//dae:library_images/dae:image", _Image, "image" );
for(var i in imageReplace) {
var iR = imageReplace[i];
for(var i in images) {
var image = images[i];
var patt=new RegExp('[a-zA-Z0-9\-\_]*\/'+iR.name,'g');
//if(image.id==iR.id)
if(patt.test(image.init_from))
image.init_from = iR.new_image;
}//for
}

Categories

Resources