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 ); }
Related
I'm using version 68 of three.js.
I would like to click somewhere and get the X, Y, and Z coordinates. I followed the steps here, but they give me a Z value of 0: Mouse / Canvas X, Y to Three.js World X, Y, Z
Basically, if I have a mesh in the scene, and I click in the middle of it, I'm hoping to be able to calculate the same values as the position of that mesh. This is just an example. I know I could use raycasting and see if I collided with a mesh and then just check its position. However, I want this to work even if I didn't click a mesh.
Is this possible? Here is a jsfiddle: http://jsfiddle.net/j9ydgyL3/
In that jsfiddle, if I could manage to click in the center of that square, I'm hoping to calculate 10, 10, 10 for the X, Y, and Z values respectively because those are the coordinates of the square's position. Here are the two functions of concern:
function getMousePosition(clientX, clientY) {
var mouse2D = new THREE.Vector3();
var mouse3D = new THREE.Vector3();
mouse2D.x = (clientX / window.innerWidth) * 2 - 1;
mouse2D.y = -(clientY / window.innerHeight) * 2 + 1;
mouse2D.z = 0.5;
mouse3D = projector.unprojectVector(mouse2D.clone(), camera);
return mouse3D;
//var vector = new THREE.Vector3(
//( clientX / window.innerWidth ) * 2 - 1,
//- ( clientY / window.innerHeight ) * 2 + 1,
//0.5 );
//projector.unprojectVector( vector, camera );
//var dir = vector.sub( camera.position ).normalize();
//var distance = - camera.position.z / dir.z;
//var pos = camera.position.clone().add( dir.multiplyScalar( distance ) );
//return pos;
}
function onDocumentMouseUp(event) {
event.preventDefault();
var mouse3D = getMousePosition(event.clientX, event.clientY);
console.log(mouse3D.x + ' ' + mouse3D.y + ' ' + mouse3D.z);
}
I left some of the other code I tried commented out. Please note that this commented-out code didn't work in the jsfiddle website, maybe because they're still using version 54 of three.js. It works fine on my machine with version 68.
Edit: To clarify, I would like to be able to get the coordinates no matter where the mouse is. I just used a mesh in this example because it's easy to verify if it works by seeing if the calculated coordinates are the same as the mesh's. What I would really like is for it to work without using raycasting on a mesh. For example, we could have it always printing the calculated coordinates to the console every time the mouse moves, no matter what is in the scene.
You should use a THREE.Raycaster for this. When you set a list of intersectObjects you will be able to get an array of objects that intersected with the ray. So you can get the position from the 'clicked' object from returned list. Check the updated fiddle here.
I also changed your Three.js to version R68
For more advanced use of THREE.RayCaster check the examples at Threejs.org/examples like this example with interactive cubes.
Relevant code from the updated fiddle:
function getMousePosition(clientX, clientY) {
var mouse2D = new THREE.Vector3();
var mouse3D = new THREE.Vector3();
mouse2D.x = (clientX / window.innerWidth) * 2 - 1;
mouse2D.y = -(clientY / window.innerHeight) * 2 + 1;
mouse2D.z = 0.5;
mouse3D = projector.unprojectVector(mouse2D.clone(), camera);
return mouse3D;
var vector = new THREE.Vector3(
(clientX / window.innerWidth) * 2 - 1, -(clientY / window.innerHeight) * 2 + 1,
0.5);
projector.unprojectVector(vector, camera);
var dir = vector.sub(camera.position).normalize();
var distance = -camera.position.z / dir.z;
var pos = camera.position.clone().add(dir.multiplyScalar(distance));
return pos;
}
function onDocumentMouseUp(event) {
event.preventDefault();
var mouse3D = getMousePosition(event.clientX, event.clientY);
console.log(mouse3D.x + ' ' + mouse3D.y + ' ' + mouse3D.z);
var vector = new THREE.Vector3( mouse3D.x, mouse3D.y, 1 );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects(scene.children );
if(intersects.length > 0){
console.log(intersects[0].object.position);
}
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
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 );
}
I've a scene with tube geometry and ray intersection is working fine. Upon ray intersection I am showing a small red color sphere and a tooltip next to the cursor. Please find the image without header:
Now if I add a header with div element, the ray intersection is working but the issue is there is a distance between red sphere, tooltip and a mouse cursor. Please find the image with header:
Please find below the code of header, tool-tip div, sphere and collision detection function:
Header:
<div style="margin-top:10px;margin-left:3%;height:100px;width:70%">
<label style="color:#b0bb02;font-size:14pt">three.js</label>
<label style="color:#f5f7f9;font-size:14pt;font-weight:bold">Tube Geometry</label><br><br>
</div>
Tool-tip div:
textDiv = document.createElement( 'div' );
textDiv.style.position = 'absolute';
textDiv.style.top = '50px';
textDiv.style.margin = '8px';
textDiv.style.border = '1px solid blue';
textDiv.style.zIndex = '100';
textDiv.style.background = '#ffffff';
textDiv.style.display = 'block';
container.appendChild( textDiv );
Sphere geometry:
dot = new THREE.Mesh ( new THREE.SphereGeometry( 1, 12, 1 ), new THREE.MeshBasicMaterial( { color: 0xff0000 } ) );
Collision detection:
var intersects;
function detectCollision(event){
var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1,- ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
/*var mouse_x = ((event.pageX-event.target.offsetParent.offsetLeft) / renderer.domElement.width) * 2 - 1;
var mouse_y = - ((event.pageY-event.target.offsetParent.offsetTop) / renderer.domElement.height) * 2 + 1;
var vector = new THREE.Vector3(mouse_x, mouse_y, 0.5);*/
projector.unprojectVector( vector, camera );
var ray = new THREE.Raycaster( camera.position, vector.subSelf( camera.position ).normalize() );
intersects = ray.intersectObjects( objects );
var pnt=0; var clickedMD = 0; var clickedDegree; var clickedTVD;
if ( intersects.length > 0 ) {
dot.position.copy( intersects[0].point );
scene.add( dot );
var fi = intersects[0].faceIndex;
pnt = Math.round(fi/11);
clickedMD = pathObjColl[pnt].md;
clickedTVD = Math.round(pathObjColl[pnt].point.z);
clickedDegree = degrees[Math.round(fi%11)];
// tooltip
var elem = document.getElementsByTagName("canvas");
var canvas = elem[0];
var x = event.pageX - canvas.offsetLeft ;
var y = event.pageY - canvas.offsetTop ;
//console.log("X: "+x+" Y: "+y);
textDiv.style.top = y+"px";
textDiv.style.left = x+"px";
textDiv.innerHTML = " Degree: "+clickedDegree+"<br/> MD: "+clickedMD+" ft<br/> TVD: "+clickedTVD+" ft";
if(textDiv.style.display == 'none'){
textDiv.style.display = 'block';
}
}
else if(intersects.length == 0){
var textDivVis = textDiv.style.display;
console.log(textDivVis);
if(textDivVis == 'block'){
textDiv.style.display = 'none';
}
}
}
demo on jsfiddle
Why there is a distance between the mouse cursor, sphere geometry and a too-tip if I add header ?
Is the textDiv positioned absolutely? Maybe the header just throws off the layout of the page, and the tooltip.. Try this:
textDiv.style.position = "absolute";
EDIT:
Well, in fact it's the header that needs to be absolutely positioned.. Otherwise it will move the canvas and your mouse position in HTML does not match mouse position in the webgl canvas.
Alternatively, if you don't want the header to be on top of canvas, you can take your container position into account when calculating mouse position. For the Vector:
var vector = new THREE.Vector3(
( (event.pageX - container.offsetLeft) / window.innerWidth ) * 2 - 1,
- ( (event.pageY - container.offsetTop) / window.innerHeight ) * 2 + 1,
0.5 );
For the tooltip:
textDiv.style.top = (container.offsetTop + y)+"px";
textDiv.style.left = (container.offsetLeft + x)+"px";
Updated jsFiddle: http://jsfiddle.net/tje8y/
Is there any way to move an object forward in Three.js?
Maybe I should convert the rotation.x,y,z to a vector, and deal with it. But I'm beginner, and I don't have any idea how to do it.
Object3D has some handy methods for that.
object.translateZ( 10 );
Please use above answer of #mrdoob, creator of ThreeJS:
object.translateZ( delta );
===OLD ANSWER===
A tutorial that worked for older ThreeJS version:
http://www.aerotwist.com/tutorials/getting-started-with-three-js/
// set position of YOUR_OBJECT
YOUR_OBJECT.position.x = 10;
YOUR_OBJECT.position.y = 50;
YOUR_OBJECT.position.z = 130;
More options:
var STEP = 10;
var newCubeMatrix = cube.matrix;
newCubeMatrix.identity();
//newCubeMatrix.multiplySelf(THREE.Matrix4.rotationYMatrix(cube.rotation.y));
newCubeMatrix.multiplySelf(THREE.Matrix4.translationMatrix(cube.position.x, cube.position.y, cube.position.z + STEP));
cube.updateMatrix();
details posted here https://gamedev.stackexchange.com/questions/7490/translate-object-in-world-space-usings-its-local-rotation
The camera is a point in space.
"Forward" is another point in space.
so you can simply use the coordinates of a second point, and make the camera location closer to the "forward" location.
however, you may also need to turn left and right, which might involve polar coordinates.
adjust these values for your convenience:
var scene;
var camera;
var playerDirection = 0;//angles 0 - 2pi
var dVector;
var angularSpeed = 0.01;
var playerSpeed = 0.075;
var playerBackwardsSpeed = playerSpeed * 0.4;
this function will initialize the scene:
function init(){
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera.position.z = 5;
dVector = new THREE.Vector3( 0, 0, 0 ) ;
camera.lookAt( dVector );
animate();
}
movement of the player is stopped when the player presses the up key.
function key_up(event){
playerIsMovingForward = 0;
playerIsMovingBackwards = 0;
playerIsRotatingLeft = 0;
playerIsRotatingRight = 0;
playerGoesUp = 0;
playerGoesDown = 0;
}
when the player is moving, we update the position:
function updatePlayer(){
if(playerIsRotatingLeft){ // rotate left
playerDirection -= angularSpeed;
}
if(playerIsRotatingRight){ // rotate right
playerDirection += angularSpeed;
}
if(playerIsRotatingRight || playerIsRotatingLeft){
setPlayerDirection();
}
if(playerIsMovingForward){ // go forward
moveForward(playerSpeed);
}
if(playerIsMovingBackwards){ // go backwards
moveForward(-playerBackwardsSpeed);
}
}
We assume by forward you meant "using the WASD keys"
function key_down(event){
var W = 87;
var S = 83;
var A = 65;
var D = 68;
var minus = 189;
var plus = 187;
var k = event.keyCode;
console.log(k);
if(k == A){ // rotate left
playerIsRotatingLeft = 1;
}
if(k == D){ // rotate right
playerIsRotatingRight = 1;
}
if(k == W){ // go forward
playerIsMovingForward = 1;
}
if(k == S){ // go back
playerIsMovingBackwards = 1;
}
}
player will only move as fast as his browser.
so maybe adjust this code?
function animate() {
requestAnimationFrame( animate );
updatePlayer();
renderer.render( scene, camera );
}
this is the code that moves the camera into the position of the dVector object
and also repositions the direction vector (dVector), so that it is always forward from the camera.
function moveForward(speed){
var delta_x = speed * Math.cos(playerDirection);
var delta_z = speed * Math.sin(playerDirection);
var new_x = camera.position.x + delta_x;
var new_z = camera.position.z + delta_z;
camera.position.x = new_x;
camera.position.z = new_z;
var new_dx = dVector.x + delta_x;
var new_dz = dVector.z + delta_z;
dVector.x = new_dx;
dVector.z = new_dz;
camera.lookAt( dVector );
}
moving forward usually involves turning left and right, here is some code that does that, it also uses polar coordinates, to move the point in relation to the camera (which is the center of the "circle" by a given amount of degrees (in radians)
function setPlayerDirection(){
//direction changed.
var delta_x = playerSpeed * Math.cos(playerDirection);
var delta_z = playerSpeed * Math.sin(playerDirection);
var new_dx = camera.position.x + delta_x;
var new_dz = camera.position.z + delta_z;
dVector.x = new_dx;
dVector.z = new_dz;
console.log(dVector);
camera.lookAt( dVector );
}
animate();
I hope that helps.
Another option is to use Vector3's set method/function.
position.set(x, y, z);
I have a THREE.js scene where a lot of elements appear, and I need to detect what object the user is clicking on.
What I have done so far is the following. The camera does not move to much - it only changes the vertical position by a limited amount, always looking towards the same point. My approximate method is the following:
I take the coordinates if the click relative to the canvas
I translate them into horizontal and vertical coordinates in the webGL scene by means of a simple rescaling, and add a Z coordinate which is sufficiently far away.
I take a horizontal ray starting from the point above, constructed by THREE.Ray()
I use ray.intersectObjects() to find the first element along the ray.
This method approximately works, but it is sometimes a few pixels away from the actual point.
Is there a more reliable technique to find out the object where a user has clicked?
Depends on what kind of camera are you using.
1) PerspectiveCamera: is ok link that Mr.doob provides.
2) OrthographicCamera: is quite different:
var init = function() {
camera = new THREE.OrthographicCamera( SCREEN_WIDTH / - 2, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_HEIGHT / - 2, NEAR, FAR);
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
}
function onDocumentMouseDown( e ) {
e.preventDefault();
var mouseVector = new THREE.Vector3();
mouseVector.x = 2 * (e.clientX / SCREEN_WIDTH) - 1;
mouseVector.y = 1 - 2 * ( e.clientY / SCREEN_HEIGHT );
var raycaster = projector.pickingRay( mouseVector.clone(), camera );
var intersects = raycaster.intersectObject( TARGET );
for( var i = 0; i < intersects.length; i++ ) {
var intersection = intersects[ i ],
obj = intersection.object;
console.log("Intersected object", obj);
}
}
Check out this one:
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 5000);
var object; //your object
document.addEventListener('mousedown', onMouseDown, false);
function onMouseDown(e) {
var vectorMouse = new THREE.Vector3( //vector from camera to mouse
-(window.innerWidth/2-e.clientX)*2/window.innerWidth,
(window.innerHeight/2-e.clientY)*2/window.innerHeight,
-1/Math.tan(22.5*Math.PI/180)); //22.5 is half of camera frustum angle 45 degree
vectorMouse.applyQuaternion(camera.quaternion);
vectorMouse.normalize();
var vectorObject = new THREE.Vector3(); //vector from camera to object
vectorObject.set(object.x - camera.position.x,
object.y - camera.position.y,
object.z - camera.position.z);
vectorObject.normalize();
if (vectorMouse.angleTo(vectorObject)*180/Math.PI < 1) {
//mouse's position is near object's position
}
}
Checks for intersection of the mouse and any of the Cubes in 3d space and alters it's color. Maybe this help you.
I ran into problems trying to implement this for a canvas which does not take up the entire width and height of the screen. Here is the solution I found works quite well.
Initialize everything on an existing canvas:
var init = function() {
var canvas_model = document.getElementById('model')
var viewSize = 50 // Depending on object size, canvas size etc.
var camera = new THREE.OrthographicCamera(-canvas_model.clientWidth/viewSize, canvas_model.clientWidth/viewSize, canvas_model.clientHeight/viewSize, -canvas_model.clientHeight/viewSize, 0.01, 2000),
}
Add an event listener to the canvas:
canvas_model.addEventListener('click', function(event){
var bounds = canvas_model.getBoundingClientRect()
mouse.x = ( (event.clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
mouse.y = - ( (event.clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
// Do stuff
}
}, false)
Or for a 'touchstart' event, change the lines calculating the mouse.x and mouse.y into:
mouse.x = ( (event.touches[0].clientX - bounds.left) / canvas_model.clientWidth ) * 2 - 1;
mouse.y = - ( (event.touches[0].clientY - bounds.top) / canvas_model.clientHeight ) * 2 + 1;