THREE.js - Rotate the camera just like trackball controls - javascript

I'm using Trackball controls in a scene and I want to implement a function to rotate the camera just like the way dragging the mouse in the canvas does it. How can I accomplish something like that? I've been looking the code of the Trackball control module, but I can't find something to start.
EDIT: I've been looking several pages, the THREE documentation and whatnot, but still can't reproduce the Trackball style rotation. I've been using quaternions too but they can't reproduce the behavior(or I'm missing something, most probably). Any help?
EDIT 2 : What I'm looking for is a way to do something like this:
function rotateCam(angle) { // code }
var angle = 0.01; //some value
rotateCam(angle);
$('#button').addEventListener('mousedown', function() { rotateCam(angle); } );
Where button is an HTML element representing a button.

I noticed that the Trackball controls, apart of rotate via quaternion, do a zoom to correct some distances. I tried to read the code, and got this:
function rotate(L) {
var vector = controls.target.clone();
var l = (new THREE.Vector3()).subVectors(camera.position, vector).length();
var up = camera.up.clone();
var quaternion = new THREE.Quaternion();
// Zoom correction
camera.translateZ(L - l);
quaternion.setFromAxisAngle(up, 0.015);
camera.position.applyQuaternion(quaternion);
camera.lookAt(vector);
renderer.render(scene, camera);
}
Works like a charm...hope someone find this useful too. ;)

function cameraRotate(distance, angle){
camera.position.x = distance * Math.cos( angle );
camera.position.z = distance * Math.sin( angle );
}
This would rotate your camera at the specified distance and angle around the scene. If you want a smooth rotation you could call this with a small angle increase from the animate-loop, depending on input for example.

I'm on the right way I think...I did this:
function rotate() {
if (this.showCase) {
var vector = controls.target.clone(); // controls is a TrackballControls
var up = camera.up.clone();
var quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle(up, 0.015);
camera.position.applyQuaternion(quaternion);
camera.lookAt(vector);
renderer.render(this.scene, this.camera);
}
},
Still, it doesn't look right right like the TrackballControls rotation.

Related

Weird rotation behavior with hammerjs and threejs

I want to rotate an object3D with hammerjs gestures.
Basically the rotation work, but with two issues I can't figure out.
The direction of the rotation changes randomly. It turns left. I stop and than move my fingers in the same diction again and suddenly instead of continuing to rotate left its going right. Happens sometimes but not always.
Once the rotation is started it only rotates to the same direction, despite me moving my fingers to different directions.
Here is how I handle the rotation:
public rotateObject3D (e: HammerInput): void {
if (rotationEnabled) {
const translation = new THREE.Vector3();
const rotation = new THREE.Quaternion();
const scale = new THREE.Vector3();
const rotateMatrix = new THREE.Matrix4();
const deltaRotationQuaternion = new THREE.Quaternion();
this._myObject.matrix.decompose(translation, rotation, scale);
this._deltaRot = (e.rotation * 0.01);
deltaRotationQuaternion.setFromEuler(new THREE.Euler(
0,
this._deltaRot * (Math.PI / 180),
0,
'XYZ'
));
deltaRotationQuaternion.multiplyQuaternions(deltaRotationQuaternion, rotation);
this._myObject.matrix = rotateMatrix.compose(translation, deltaRotationQuaternion, scale);
}
}
And this is the call of it:
this._hammerManager.on('rotate', (e) => {
this._arTools.rotateObject3D(e);
});
Is there anything I am missing?
It looks like you're using the absolute rotation value, instead of the change in rotation. For instance, consider the following scenario:
Rotation1: 12°
Rotation2: 10°
Rotation3: 5°
By multiplying your quaternions, your object is being rotated to 12°, then 22°, then finally 27°, even though you were turning back towards 0. This is because you're adding the new rotation to the last rotation on each event.
What you should do is save the previous rotation value, and subtract it from the new one, to get the rotation delta:
var previousRot = 0;
var newRot = 0;
rotateObject3D(e) {
newRot = e.rotation - previousRot;
// You could use newRot for your quaternion calculations
// but modifying .rotation.y is simpler
myObject.rotation.y += newRot
// Save value to be used on next event
previousRot = newRot;
}
With this method, the scenario above will give you the change in rotation. Your object will first rotate by +12°, then -2°, then -5°, for a more natural behavior.
Just make sure to reset previousRot = 0 on HammerJS' rotateend event so you don't use the value from previous gestures when a new one begins.

Three js Obj loader Rotation

Ive made a stick in the Three js editor, after loading it with the following code i can position it but not rotate it.
Its a stick made of 4 meshes, so i probably have to make a rotation point but i cant figure out how i get that to work.
The ideal situation :
x -------
The x represents the point where i want to rotate the dashes(stick) around
can anyone help me?
thx in advance
var loader = new THREE.ObjectLoader();
loader.load("scene.json", function ( obj )
{
stick =obj;
scene.add( stick );
stick.position.z = -9;
stick.position.y = .4;
stick.children[3].rotation.x(45);
});
If I understand aright, you have an object with 4 children that are meshes.
Object3D
-Mesh
-Mesh
-Mesh
-Mesh
First, I would translate all the children, and then rotate the parent object.
for(var i=0; i<stick.children.length; i++) {
stick.children[i].position.set(0, 0.4, -9); // or another offset
}
stick.rotateX( 45 * Math.PI / 180 ); // make sure to use Radians

three.js: jumpy animation (– due to float imprecision?)

I'm using three.js on a separate canvas on top of a openlayers map. the way I synchronize the view of the map and the three.js camera (which looks straight down onto the map) works fine, and looks like this:
function updateThreeCam(
group, // three.js group — has camera in it
view, // ol3 view
map // ol3 map
) {
// get visible part of map
const extent = getViewExtent(view, map);
const h = ol.extent.getHeight(extent);
const mapCenter = ol.extent.getCenter(extent);
// calculates how for away from the ground the camera has to
// be to match the currently visible part of the map
const camDist = h / (2 * Math.tan(constants.CAM_V_FOV_RAD / 2));
camera.updateProjectionMatrix(); // needed?
group.position.set(...mapCenter, camDist);
group.updateMatrixWorld();
}
however, when I am animating the flight of an object from in front of the camera down to the ground, the movement is not smooth: the vertical movement is jumping a lot. https://jsbin.com/qohutabofe/2/edit?js,output
this is the animation code:
// construct a spline to animate along
const waypoints = [
objStartPosition,
objEndPosition,
];
const pathSpline = new THREE.CatmullRomCurve3(waypoints);
// const pathSpline = new THREE.LineCurve3(...waypoints);
startTime = Date.now();
// [...]
function animate() {
const diff = Date.now() - startTime;
const t = diff / aniDuration;
// sample curve at `t`
const pos = pathSpline.getPointAt(t);
// update position
obj.position.copy(pos);
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
what is weird is that I don't really see the jumps in the sampled positions:
as you can see the map coordinates tend to be very big numbers. that's why I thought it might be a float precision issue. — dividing everything by 1000 indeed seems to help. there is just two new problems with that:
the foreshortening effect is way less drastic / not realistic anymore
panning does not work as expected anymore: the rectangle on the ground does not stay in its position
https://jsbin.com/bujepediro/1/edit?js,output

Object shrinks when rotated javascript

I have been developing a basic game engine just to learn the process and I have hit a issue with my rotation function.
It works fine except that the object shrinks and appears to invert.
Here is a jsfiddle that illustrates my point.
I think the problem would be in the rotation code its self but i'm not positive.
function Rotation(vec, rot){
if(Math.acos((vec.x + vec.y + vec.z -1)/2) === 0) { return vec; }
var qVec = new Quaternion(vec.x, vec.y, vec.z, 0);
qVec = Quaternions.multiply(qVec, rot);
qVec = Quaternions.multiply(qVec, rot.conjugate());
return new Vector3(qVec.x, qVec.y, qVec.z);
}
Couple things:
First, the rotation quaternion is not normalized, so it's inverse is not the same as its conjugate. Rotation by a quaternion is defined by:
Where q is the vector you're rotating around, p is the vector you're rotating, and p' is the final rotated vector.
So this is defined using the inverse of q, which is defined as conjugate(q) / magnitude(q)^2. In the case where q is normalized, magnitude(q)^2 == 1, so it's the same as just multiplying by the conjugate.
Also note the order of operations here. Quat multiplications are non-commutative so their order matters.
You can fix this by normalizing your rotation quaternion, and fixing the order of operations:
var qVec = new Quaternion(vec.x, vec.y, vec.z, 0);
qVec = Quaternions.multiply(rot.normalize(), qVec);
qVec = Quaternions.multiply(qVec, rot.conjugate());
return new Vector3(qVec.x, qVec.y, qVec.z);
Second, you want to define your rotation quat as normal to the plane you want to rotate around. In this case, you want to rotate around the x-y plane. The z-axis is normal to this plane, so we want to define the rotation vector along the z-axis:
function update(){
for(var i = 0; i < gameObjects.length; i++){
gameObjects[i].rotation = euler(new Vector3(0, 0, frames/100));
}
}
With these changes, I was able to get the box rotating correctly.
(In terms of why it was scaling up/down, I'm not 100% sure. Still trying to figure that out.)

Trying to workout how to use quaternions to rotate a camera that is moving along a path to look in new direction vector

I am trying to rotate the camera smoothly and without altering the y-vector of the camera direction, i can use look at, and it changes the camera direction in a flash, but this is not working for me, I would like a smooth transition as the direction of the camera changes. I have been reading up, and not understanding everything, but it seems to me that quaternions are the solution to this problem.
I have this.object (my camera) moving along a set path (this.spline.points). The location of the camera at any one time is (thisx,thisy, thisz)
I have cc[i] the direction vector for the direction I would like the camera to face (formerly I was using lookat(cc[i]) which changes the direction correctly, but too quickly/instantaneously)
Using info I have read, I have tried this below, and it just resulted in the screen going black at the point when the camera is due to move.
Could anyone please explain if I am on the right track, how to correct my code.
Thanks
var thisx = this.object.matrixWorld.getPosition().x.toPrecision(3);
var thisy = this.object.matrixWorld.getPosition().y.toPrecision(3);
var thisz = this.object.matrixWorld.getPosition().z.toPrecision(3);
var i = 0;
do {
var pathx = this.spline.points[i].x.toPrecision(3);
var pathz = this.spline.points[i].z.toPrecision(3);
if (thisx == pathx && thisz == pathz){
this.object.useQuaternion = true;
this.object.quaternion = new THREE.Quaternion(thisx, thisy, thisz, 1);
var newvect;
newvect.useQuaternion = true;
newvect.quaternion = new THREE.Quaternion(thisx+cc[i].x, thisy+cc[i].y, thisz+cc[i].z, 1);
var newQuaternion = new THREE.Quaternion();
THREE.Quaternion.slerp(this.object.quaternion, newvect.quaternion, newQuaternion, 0.5);
this.object.quaternion = newQuaternion;
//this.object.lookAt( cc[i]);
i = cc.length;
} else i++;
} while(i < cc.length);
There is no need to call this.object.useQuaternion = true. That is default behavior.
Also, this.object.quaternion contains the current rotation, so no need to generate that either.
You might want to try a different approach - construct the rotation matrix from the spline position, lookAt and up vectors, creating a path of quaternions as a preprocessing step:
var eye = this.spline.points[i].clone().normalize();
var center = cc[i].normalize();
var up = this.object.up.normalize();
var rotMatrix = new THREE.Matrix4().lookAt(eye, center, up);
You could then create the quaternions from the rotation matrix:
var quaternionAtSplineCoordinates = [];
quaternionAtSplineCoordinates.push(new THREE.Quaternion().setFromRotationMatrix(rotMatrix));
Once you have that path, you could apply the quaternion to the camera in your animation loop - provided you have a large enough number of samples. Otherwise, you could consider using slerp to generate the intermediate points.

Categories

Resources