I've setup a scene with a camera which remains in a fixed point in the center of the scene. The user can pan the camera around by clicking (and holding) and dragging the left mouse button. When the user releases the left mouse button, the motion of the camera stops. My scene is based on one of the Three.js demos as see here
There are a bunch of event handlers which are used to create a target position for the camera to lookAt:
function onDocumentMouseDown( event ) {
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onDocumentMouseMove( event ) {
if ( isUserInteracting === true ) {
lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;
}
}
and in my update loop I have:
// lat and long have been calculated in the event handlers from the cursor click location
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = (500 * Math.sin( phi ) * Math.cos( theta ));
camera.target.y = (500 * Math.cos( phi ));
camera.target.z = (500 * Math.sin( phi ) * Math.sin( theta ));
camera.lookAt( camera.target );
Which works as expected. When the user clicks and drags the mouse, the camera rotates to follow. Now I am attempting to add some inertia to the movement so when the user releases the left mouse button the camera continues to rotate in the direction of rotation for a small amount of time.
I have tried tracking the change in position of the mouse position in my drag start and drag end events and try to calculate the direction of movement based on that, but it seems like a convoluted way of doing it
Any suggestions on how I could add inertia to the camera controls?
Related
I have a panoramic image loaded in threejs but it starts camera rotation from the logic below which is default in threejs
if ( isUserInteracting === false ) {
lon += 0.1;
}
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 100 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 100 * Math.cos( phi );
camera.target.z = 100 * Math.sin( phi ) * Math.sin( theta );
What I want to do is place the camera at a specific point which I am able to place using
camera.lookAt( -56.86954186163314, 0, -71.49481268065709 );
Now i want to start normal camera rotation from the above lookAt point. What I am currently doing is
camera.lookAt( -56.86954186163314 + camera.target.x, 0, -71.49481268065709 + camera.target.z);
Which is wrong I think.. PS (I am very weak in geometry, sin, cos).. Can any 1 please help me with this?? PS(I dont want to change camera.target.y It should be 0).. Thanks in advance..
This is best looked at from the perspective of vectors.
Take your lookAt position: that's a vector. You can make that vector spin around an axis using Vector3.applyAxisAngle. As you update the vector, make your camera look at it.
For your example, you want the camera to look at -56.86954186163314, 0, -71.49481268065709, and then spin 360° about the Y-axis (the camera position doesn't change, and the lookAt target doesn't change its Y value).
var lookVector = new THREE.Vector3();
// later...
lookVector.set(x, y, z); // -56.86954186163314, 0, -71.49481268065709
// down with your render function...
var axis = new THREE.Vector3(0, 1, 0);
function render(){
lookVector.applyAxisAngle(axis, 0.001); // or some other theta
camera.lookAt(lookVector);
renderer.render(scene, camera);
}
You'll need to track when your thetas add up to 360°, and perform logic to stop the rotation, but I'll leave that as an exercise for you.
So I am looking for a way to rotate around an object in Threejs but without holding the mouse button. I've got an example website here: http://www.dilladimension.com/ which uses Threejs. Been looking through forums and on the Threejs documentation but I can't figure out how to rotate around an object without holding down the mouse.
Any help is greatly appreciated!
If you are trying to get that kind of mouse interaction as shown here, you can :
//Listen to mouse mouve events
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX );
mouseY = ( event.clientY - windowHalfY );
}
//Update your camera position
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
Hope this helps.
What I'm trying to achieve is to make a specific mesh move towards a specific vector until it will eventually be stopped by the player.
So far I have managed to get the XY coordinates of the clicked canvas and project them in 3d using the following piece of code. Unfortunately I'm not sure what approach to take in order to get the direction towards the clicked position.
var vector = new THREE.Vector3();
vector.set(
( event.clientX / window.innerWidth ) * 2 - 1,
+ ( event.clientY / window.innerHeight ) * 2 + 1,
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 ) );
This assumes a target Vector3 and a maximum distance to be moved per frame of .01.
var vec1 = target.clone(); // target
vec1.sub(mesh.position); // target - position
var dist = Math.min(vec1.length(), .01); // assume .01 is maximum movement
if (dist > 0) {
vec1.setLength(dist); // this will be the movement
mesh.position.add(vec1); // this moves the messh
}
I've been struggling for a while with the concepts of quaternions and I think that it may have something to do with this particular challenge.
If you know three.js, you may be familiar with the equirectangular panorama video example. What I've been trying to do is to simply extract the camera's rotation at any given moment in a format that I understand (radians, degrees...) for each axis. In theory, shouldn't I be able to simply tap into the camera's rotation.x/y/z parameters to find those out? I'm getting strange values, though.
Check out this example:
http://www.spotted-triforce.com/other_files/three/test.html
I'm outputting the camera's xyz rotation values at the upper left corner and instead of expected values, the numbers bounce around between positive and negative values.
Now, this example doesn't use one of the control scripts that are available. Rather, it creates a vector to to calculate a camera target to look at. Here's what the camera code looks like:
function init() {
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3( 0, 0, 0 );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onDocumentMouseMove( event ) {
if ( isUserInteracting === true ) {
lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;
}
}
function onDocumentMouseUp( event ) {
isUserInteracting = false;
}
function update() {
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( camera.target );
}
Does anybody know what these strange values are that I'm getting and how I can extract proper rotation values that I can then use on another object to mirror the motion?
If you set
camera.rotation.order = "YXZ"
( the default is "XYZ" ) the Euler angles will make a lot more sense to you:
rotation.y will be the camera heading in radians
rotation.x will be the camera pitch in radians
rotation.z will be the camera roll in radians
The rotations will be applied in that order.
three.js r.70
I'm trying to use THREE.js and been looking at some examples, Voxel Painter exmaple
I'm trying to get it so that every time you click to create a new cube the roll over mesh will always move on top of the cube just pasted rather than being at the point of intersecting of the current mouse position...
All of the source code can be viewed from the link but I believe what I'm trying to do has something to do with this...
You click the mouse to add a Voxel, when onMouseDown() function is active it will check if current mouse position is intersecting with the plane and if CTRL button has been pressed for either a new cube or delete a cube.
function onDocumentMouseDown( event ) {
event.preventDefault();
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
intersector = getRealIntersector( intersects );
// delete cube
if ( isCtrlDown ) {
if ( intersector.object != plane ) {
scene.remove( intersector.object );
}
}
// create cube
else {
intersector = getRealIntersector( intersects);
setVoxelPosition( intersector );
var voxel = new THREE.Mesh( cubeGeo, cubeMaterial );
voxel.position.copy( voxelPosition );
voxel.matrixAutoUpdate = false;
voxel.updateMatrix();
scene.add( voxel );
}
}
}
When creating a new cube I believe THREE.js grabs the current point where the mouse intersects intersector = getRealIntersector( intersects); and then sets the new Voxel position with the function setVoxelPosition( intersector ); with the intersect point being passed in.
This is the setVoxelPosition function
function setVoxelPosition( intersector ) {
normalMatrix.getNormalMatrix( intersector.object.matrixWorld );
tmpVec.copy( intersector.face.normal );
tmpVec.applyMatrix3( normalMatrix ).normalize();
voxelPosition.addVectors( intersector.point, tmpVec );
voxelPosition.x = Math.floor( voxelPosition.x / 50 ) * 50 + 25;
voxelPosition.y = Math.floor( voxelPosition.y / 50 ) * 50 + 25;
voxelPosition.z = Math.floor( voxelPosition.z / 50 ) * 50 + 25;
}
and the render loop
function render() {
if ( isShiftDown )
theta += mouse2D.x * 1.5;
raycaster = projector.pickingRay( mouse2D.clone(), camera )
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
intersector = getRealIntersector( intersects );
if ( intersector ) {
setVoxelPosition( intersector );
rollOverMesh.position = voxelPosition;
}
}
camera.position.x = 1400 * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = 1400 * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
I have tried to pass in different values into setVoxelPosition( intersector ) but I can't seem to get it right..
Could someone please point me in the right direction?
Thanks.
There are several ways of doing this. I'm not going to sugar coat this answer because frankly, reverse engineering this code will do you some good. I will say, having worked with this voxel code myself, it's important you understand what's happening when you click the mouse button and create a new voxel box.
You're correct in understanding that, this function is in fact taking the current mouse position and creating the box. When the click is complete and the box has been made, the process starts over so the program again looks to where the mouse is and places the Ghost box. In this case the Ghost box is not on top of the previously made box, so you'd have to move the mouse up manually a few pixels to get it there.
Rather than fool around with the setVoxelPosition function directly, I'd recommend you 'temporarly change the x,y,z position of the ghost in relation to the matrix intersect mouse position of your computer. Upon a successful click, increase the matrixIntersect.x .y .z properties of this matrixIntersects object, increasing these values only a little so you get the 'box-on-top' effect you want directly after a click. Remember to change them back when the users mouse moves off the object otherwise the Ghost box will no longer be directly under the mouse and things can get messy fast if these properties grow after every click.