I've looked for help of first player rotation on three.js for a while but most of the answers are outdated with functions which currently don't exist in the updated library.
I'm trying to make my code run so that the camera will rotate around it's own axis according to the position of the mouse on the screen.
The current rotation code is:
var scale = 10;
function viewKeys(){
document.addEventListener("mousemove", MouseMove, true);
function MouseMove(event) {
mouseX = event.clientX - divOffsetWidth;
mouseY = event.clientY - divOffsetHeight;
}
}
divOffset variables make the mouse positions read relative to the center of the HTML div.
function viewAnimate(){
camera.rotation.x = -((3/2)*(Math.PI*mouseY)) / (scale);
camera.rotation.y = -(2*(Math.PI*mouseX)) / (scale);
}
The viewKeys() function is called in the init() function and the viewAnimate() function is called within the animate() function.
Currently the code can rotate normally when the camera's position is (0,0,0) but if I move to a different position it looks as if the camera is rotating relative to the whole environment's axis.
I am aware that there are lots of control librarys for three.js but I would like to understand how to be able to rotate something on its own axis myself.
How do you suppose I change the rotation so that it works correctly?
If you want to rotate the camera yourself via the mouse, you will have to understand how Euler rotations work in three.js. See this answer.
One way to implement what you want is by using a pattern like so:
var scale = 1;
var mouseX = 0;
var mouseY = 0;
camera.rotation.order = "YXZ"; // this is not the default
document.addEventListener( "mousemove", mouseMove, false );
function mouseMove( event ) {
mouseX = - ( event.clientX / renderer.domElement.clientWidth ) * 2 + 1;
mouseY = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
camera.rotation.x = mouseY / scale;
camera.rotation.y = mouseX / scale;
}
I agree with you that experimenting with this would be a good learning experience.
three.js r.89
Related
I got this website with a bg that as you move you mouse around it lights up part of the bg depending on where your mouse it. I was wondering if there was a way to make that light spot bob around on its own. Here is a codepen with the JS: https://codepen.io/anon/pen/MGoxNr
I hope thats enough to see what its doing
Here is a live website using this: http://www.crimson-moon.com/
If you need to see it.
function onDocumentMouseMove(event) {
mouseX = ( event.clientX - windowHalfX ) * 10;
mouseY = ( event.clientY - windowHalfY ) * -10;
}
Remove onDocumentMouseMove and set some interval, where you set X, Y on circle with some radius. Set some incrementing varible, and depending on it, calculate X,Y position on circle.
x0, y0 - center of circle
r - circle radius
a - angle
x = x0 + r*cos(a)
y = y0 + r*sin(a)
changing angle x, y will change.
This one is bugging me quite a bit.
I'm trying to achieve rotation of a Cannon.Body based on the mouse input.
By using the (Cannon) Three FPS example to demonstrate, you can see what the issue is.
https://codepen.io/Raggar/pen/EggaZP
https://github.com/RaggarDK/Baby/blob/baby/pl.js
When you run the code and enable pointerlockcontrols by clicking on the "click to play" area and press W for 1 second to get the sphere into the view of the camera, you'll see that the sphere moves according to the WASD keys by applying velocity. If you move the mouse, the quaternion is applied to the Body, and the proper velocity is calculated.
Now turn 180 degrees, and the rotation on the X axis is now negated somehow.
When moving the mouse up, the sphere rotates down.
How would one fix such issue? Maybe I'm doing something wrong elsewhere, that might mess with the quaternion?
Maybe I should mention, in the playercontroller(pl.js), I'm applying the rotation to the sphereBody, instead of the yaw- and pitchObjects.
Relevant code from pl.js (Line 49):
var onMouseMove = function ( event ) {
if ( scope.enabled === false ) return;
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
cannonBody.rotation.y -= movementX * 0.002;
cannonBody.rotation.x -= movementY * 0.002;
cannonBody.rotation.x = Math.max( - PI_2, Math.min( PI_2, cannonBody.rotation.x ) );
//console.log(cannonBody.rotation);
};
And (Line 174):
euler.x = cannonBody.rotation.x;
euler.y = cannonBody.rotation.y;
euler.order = "XYZ";
quat.setFromEuler(euler);
inputVelocity.applyQuaternion(quat);
cannonBody.quaternion.copy(quat);
velocity.x = inputVelocity.x;
velocity.z = inputVelocity.z;
Inside the animate() function, codepen (Line 305):
testballMesh.position.copy(sphereBody.position);
testballMesh.quaternion.copy(sphereBody.quaternion);
The problem is the way you assign angles to and from the Quaternions. The quaternion x,y,z,w properties are not directly compatible with angles, so you need to convert.
This is how to set the angle around a given axis for a CANNON.Quaternion:
var axis = new CANNON.Vec3(1,0,0);
var angle = Math.PI / 3;
body.quaternion.setFromAxisAngle(axis, angle);
Extracting the Euler angles from quaternions is probably not be the best way to attack the second part of the problem. You could instead just store the rotation around X and Y axes when the user moves the mouse:
// Declare variables outside the mouse handler
var angleX=0, angleY=0;
// Inside the handler:
angleY -= movementX * 0.002;
angleX -= movementY * 0.002;
angleX = Math.max( - PI_2, Math.min( PI_2, angleX ) );
And then to get the rotation as a quaternion, use two quaternions separately (one for X angle and one for Y) and then combine them to one:
var quatX = new CANNON.Quaternion();
var quatY = new CANNON.Quaternion();
quatX.setFromAxisAngle(new CANNON.Vec3(1,0,0), angleX);
quatY.setFromAxisAngle(new CANNON.Vec3(0,1,0), angleY);
var quaternion = quatY.mult(quatX);
quaternion.normalize();
To apply the quaternion to your velocity vector:
var rotatedVelocity = quaternion.vmult(inputVelocity);
Pro tip: don't use Euler angles if you can avoid them. They usually cause more problems than they solve.
Three.js : rev 73
I'm using the following construct to find intersection of mouse click with objects in the 3d world (orthographic setup):
function onDocumentMouseDown(event) {
event.preventDefault();
console.log("Click");
var mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
var raycaster = new THREE.Raycaster();
// 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);
console.log("intersects: " + intersects.length);
for (var i = 0; i < intersects.length; i++) {
console.log(intersects[i].point);
}
}
However, the intersection is very inaccurate. Intersection is captured when I click near the top left of the box only.
jsFiddle: Can someone please help me understand why is this misbehaving ?
Also, if no object is being selected, I want to find out where is the click in 3d world relative to the box - left, right, below the box ? Can I use the ray itself to compute this ?
you have to get accurate mouse position for ray-casting :-
mouse.x = ( (event.clientX -renderer.domElement.offsetLeft) / renderer.domElement.width ) * 2 - 1;
mouse.y = -( (event.clientY - renderer.domElement.offsetTop) / renderer.domElement.height ) * 2 + 1;
you have to fire event listener when window resize
i update the fiddle again check it now .
add new function for window resize...
code is self explaintory ...hope you got it .
Check Update Fiddle now
updated fiddle :-
http://jsfiddle.net/gc1v0rza/5/
I have a simple function which the user can click and drag the camera. The camera moves with a slight smoothness, but i do not know a simple way to stop the camera once it has caught up to the mouse position.
I'm sure it's just a simple maths logic error, but currently the camera just keeps floating off forever.
This is my function:
function drag(evt,el){
clearInterval(timer);
if(evt.button == 2){ //right click and drag
mousePos = {};
mousePos.x = evt.offsetX / scale;
mousePos.y = evt.offsetY / scale;
function update(e){
var difx = mousePos.x - (e.offsetX/scale),
dify = mousePos.y - (e.offsetY/scale);
var targetX = camera.x + difx;
var targetY = camera.y + dify;
//update for next mouse movement
mousePos.x = e.offsetX / scale;
mousePos.y = e.offsetY / scale;
function smooth(){ // the problems lay here
if(camera.x != targetX){
camera.x += (difx * lerp);
}
if(camera.y != targetY){
camera.y += (dify * lerp);
}
}
timer = setInterval(smooth,16);
}
function clear(){
el.removeEventListener('mousemove', update, false);
this.removeEventListener('mouseup', clear, false);
}
el.addEventListener('mousemove',update,false);
document.body.addEventListener('mouseup',clear,false);
}}
I have the code in action here http://jsfiddle.net/bbb9q2c3/ if you click and drag then let go the box will keep moving because my current code does not seem to detect when the camera has reached it's target.
What can i do to solve this issue?
I fiddled with this a bit.
Here's what I changed:
In the draw function I removed translate, so that the camera coordinates match mouse coordinates. So camera with (0,0) is at the top left and not middle.
Use threshold to check if camera is near the mouse ath.abs(dify) > 1.
Added clearInterval to update function. It is less responsive now, so you may want to fiddle with that.
Set square to the middle position in the initialization.
I've created a near exact implementation of the Three.js Draggable Cubes example, except that my implementation ignores the z axis for movement in 2D. The example can be found here: http://threejs.org/examples/#webgl_interactive_draggablecubes
While I am able to click and move around my object, if I move the mouse too fast, the object will lag behind; the mouse pointer will move to it's location and then the object will follow the mouses path to the location. This issue is also noticeable in the Three.js example. Try dragging one of the cubes at anything beyond a moderate speed.
I've tried a few things to get the object to be directly under the mouse pointer but none have worked. The closest I think I might have come to a solution was by changing the mouse position update in the MouseMove event. However, it appears that the Three.js implementation of the code returns values between 1 and -1 rather than the screen X and Y coordinates, which leads me to wonder if the original implementation is causing the lag.
//Original Implementation - Works With Lag
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
console.log(mouse.x + "," + mouse.y); //Output: -0.9729166666666667,0.8596858638743455
//My Implementation - Does Not Work
mouse.x = event.clientX - (window.innerWidth / 2);
mouse.y = event.clientY - (window.innerHeight / 2);
console.log(mouse.x + "," + mouse.y); //Output: -934,-410.5
//Update Function
void Update()
{
var vector = new THREE.Vector3(mouse.x, mouse.y, 1);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObject(plane);
meshToMove.position.x = intersects[0].point.x;
meshToMove.position.y = intersects[0].point.y;
}
//Main Loop
function Loop()
{
Update();
Render();
requestAnimationFrame(Loop);
}
Q: How can I update the three.js Draggable Cubes example so that objects don't lag behind the mouse during a click and drag?