I've seen a couple of questions on stack overflow about this but none resolve my issue.
For my first project in Three.Js I'm trying to create a tree and have the camera zoom in on specific nodes. I have the following code:
var selectedObject = fourthParent;
camera.target = selectedObject;
var tween = new TWEEN.Tween(camera.position).to({
x: selectedObject.position.x,
y: selectedObject.position.y,
z: 5
},1000).easing(TWEEN.Easing.Linear.None).onUpdate(function () {
camera.lookAt(camera.target);
}).onComplete(function () {
camera.lookAt(selectedObject.position);
}).start();
var tween = new TWEEN.Tween(camera.target).to({
x: selectedObject.position.x,
y: selectedObject.position.y,
z: 0
}).easing(TWEEN.Easing.Linear.None).onUpdate(function () {
}).onComplete(function () {
camera.lookAt(selectedObject.position);
}).start();
This moves the camera correctly but right at the end of the animation it changes the camera angle. I'm not changing the angle in my code so I have no idea why it is changing. Can anyone help?
Thanks,
Joe
Here is the working code:
updateControls = false;
var selectedObject = fourthParent;
camera.target = selectedObject;
var tween = new TWEEN.Tween(camera.position).to({
x: selectedObject.position.x,
y: selectedObject.position.y,
z: 20
},1000).easing(TWEEN.Easing.Linear.None).onComplete(function () {
controls.target0.set(selectedObject.position.x, selectedObject.position.y, selectedObject.position.z);
controls.position0.set(selectedObject.position.x, selectedObject.position.y, 20);
controls.reset();
updateControls = true;
}).start();
var tween = new TWEEN.Tween(camera.target).to({
x: selectedObject.position.x,
y: selectedObject.position.y,
z: 20
}).easing(TWEEN.Easing.Linear.None).start();
// Render loop
var render = function () {
requestAnimationFrame(render);
TWEEN.update();
renderer.render(scene, camera);
if (updateControls) {
controls.update();
}
};
By calling camera.lookAt() you're changing the camera angle.
You should just remove every instance of that code.
Related
I'm trying to implement a simple turn-around-and-move feature with Three.js. On mouse click, the object is supposed to first turn around and then move to the clicked location.
Codepen
The rotation is achieved with raycasting and lookAt(). It works by itself and it always works on the first click. If you remove the translation, it works continuously. The issue occurs when rotation and translation are implemented together. If you click a second time, after the object has moved to the previous clicked location, it doesn't rotate as expected. Depending on the mouse location it can flip to the other side without rotating at all.
Clarification: When you click the first time, notice how the object slowly and steadily turns around to face that direction? But the second time, after the object has moved, the rotation is quicker and/or flimsier or it simply flips over and there is no rotation at all. It depends on where you click in relation to the object.
I believe the issue stems from trying to implement lookAt while being located at the current lookAt location? If I stop the translation half way, the next rotation will work better. But of course I need it to go all the way.
I'm somewhat lost on how to proceed with this issue. Any help would be appreciated.
/*** Setup scene ***/
let width = 800
let height = 600
let scene
let renderer
let worldAxis
let box
let angle
let boxAxes
scene = new THREE.Scene()
worldAxis = new THREE.AxesHelper(200);
scene.add(worldAxis);
// Setup renderer
renderer = new THREE.WebGLRenderer({alpha: true, antialias: true})
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(width, height)
document.body.appendChild(renderer.domElement)
// Setup camera
const camera = new THREE.OrthographicCamera(
width / - 2, // left
width / 2, // right
height / 2, // top
height / - 2, // bottom
0, // near
1000 ); // far
camera.position.set(0, 0, 500)
camera.updateProjectionMatrix()
// Setup box
let geometry = new THREE.BoxGeometry( 15, 15, 15 );
let material = new THREE.MeshBasicMaterial( { color: "grey" } );
box = new THREE.Mesh( geometry, material );
box.position.set(100, 150, 0)
box.lookAt(getPointOfIntersection(new THREE.Vector2(0, 0)))
addAngle()
boxAxes = new THREE.AxesHelper(50);
box.add(boxAxes)
scene.add(box)
renderer.render(scene, camera);
/*** Setup animation ***/
let animate = false
let currentlyObservedPoint = new THREE.Vector2();
let rotationIncrement = {}
let translationIncrement = {}
let frameCount = 0
document.addEventListener('click', (event) => {
let mousePosForRotate = getMousePos(event.clientX, event.clientY)
rotationIncrement.x = (mousePosForRotate.x - currentlyObservedPoint.x)/100
rotationIncrement.y = (mousePosForRotate.y - currentlyObservedPoint.y)/100
let mousePosForTranslate = getMousePosForTranslate(event)
translationIncrement.x = (mousePosForTranslate.x - box.position.x)/100
translationIncrement.y = (mousePosForTranslate.y - box.position.y)/100
animate = true
})
function animationLoop() {
if (animate === true) {
if (frameCount < 100) {
rotate()
} else if (frameCount < 200) {
translate()
} else {
animate = false
frameCount = 0
}
frameCount++
renderer.render(scene, camera)
}
requestAnimationFrame(animationLoop)
}
function rotate() {
currentlyObservedPoint.x += rotationIncrement.x
currentlyObservedPoint.y += rotationIncrement.y
let pointOfIntersection = getPointOfIntersection(currentlyObservedPoint)
box.lookAt(pointOfIntersection)
addAngle()
}
function translate() {
box.position.x += translationIncrement.x
box.position.y += translationIncrement.y
}
function getMousePos(x, y) {
let mousePos = new THREE.Vector3(
(x / width) * 2 - 1,
- (y / height) * 2 + 1,
0)
return mousePos
}
function getMousePosForTranslate(event) {
let rect = event.target.getBoundingClientRect();
let mousePos = { x: event.clientX - rect.top, y: event.clientY - rect.left }
let vec = getMousePos(mousePos.x, mousePos.y)
vec.unproject(camera);
vec.sub(camera.position).normalize();
let distance = - camera.position.z / vec.z;
let pos = new THREE.Vector3(0, 0, 0);
pos.copy(camera.position).add(vec.multiplyScalar(distance));
return pos
}
function getPointOfIntersection(mousePos) {
let plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
let pointOfIntersection = new THREE.Vector3()
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mousePos, camera)
raycaster.ray.intersectPlane(plane, pointOfIntersection)
return pointOfIntersection
}
function addAngle() {
let angle = box.rotation.x - 32
box.rotation.x = angle
}
animationLoop()
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.min.js'></script>
i have 600 planes being added to a scene in random x,y,z positions, each plane is clickable. when clicked i animate to the selected plane. All works but i am struggling for the camera to face the selected plane / or make sure the plane is centred in the view. I have tried getting the direction vector of the clicked item but not sure how make sure the camera is always a set distance away. here is the function and below a link to the test. Any ideas? Many thanks
http://adigitalengagement.co.uk/webauth_stickies/plane/
function toObj(obj) {
var lookAtVector = new THREE.Vector3(0, 0, 1);
lookAtVector.applyQuaternion(obj.quaternion);
console.log(lookAtVector);
var rotateTween = new TWEEN.Tween(controls.target)
.to({
x: obj.position.x,
y: obj.position.y,
z: obj.position.z
}, 4000)
.interpolation(TWEEN.Interpolation.CatmullRom)
.easing(TWEEN.Easing.Quintic.InOut)
.start();
var goTween = new TWEEN.Tween(camera.position)
.to({
x: obj.position.x,
y: obj.position.y,
z: obj.position.z + 10
}, 4000)
.interpolation(TWEEN.Interpolation.CatmullRom)
.easing(TWEEN.Easing.Quintic.InOut);
goTween.start();
goTween.onComplete(function() {
console.log('done!');
});
}
I'm sure there can be a better solution, but this one can be a starting point based on this SO answer.
I've changed your toObj() function and added a global variable:
var lookAtVector = new THREE.Vector3(0,0,0);
...
function toObj(obj) {
var normalMatrix = new THREE.Matrix3().getNormalMatrix( obj.matrixWorld );
var worldNormal = new THREE.Vector3(0,0,1).applyMatrix3( normalMatrix ).normalize();
var camPosition = new THREE.Vector3().copy(obj.position).add(worldNormal.multiplyScalar(100));
var rotateTween = new TWEEN.Tween(lookAtVector)
.to({
x: obj.position.x,
y: obj.position.y,
z: obj.position.z
}, 4000)
.easing(TWEEN.Easing.Quadratic.InOut)
.onUpdate(function(){
camera.lookAt(lookAtVector);
})
.onComplete(function(){
lookAtVector.copy(obj.position);
})
.start();
var goTween = new TWEEN.Tween(camera.position)
.to(camPosition, 4000)
.easing(TWEEN.Easing.Quadratic.InOut)
.start();
}
jsfiddle example
I got a question about camera object. I'm trying to make a transition between cameras and I got it partially working. Camera is moving well but it does not rotate correct. I suppose my code does not calculate lookAtVector right but I cannot find information how to do it correct.
Here is the code I'm using:
var new_position = new_camera.position.clone();
var new_rotation = new_camera.rotation.clone();
var new_quaternion = new_camera.quaternion.clone();
camera.rotation.clone(new_rotation);
camera.quaternion.clone(new_quaternion);
newlookAtVector = new THREE.Vector3(0, 0, -1);
newlookAtVector.applyEuler(new_rotation, new_camera.eulerOrder);
new TWEEN.Tween( camera.position ).to( {
x: new_position.x,
y: new_position.y,
z: new_position.z}, 600 ).onUpdate(function () {
camera.lookAt(newlookAtVector);
}).onComplete(function () {
camera.lookAt(newlookAtVector);
}).easing( TWEEN.Easing.Sinusoidal.Out).start();
Thank you!
Trying to get a camera to smoothly rotate around a globe to a new position when a button is pressed. I'be done proof of position with the following to check the coordinates are OK
camera.position.set(posX,posY,posZ);
camera.lookAt(new THREE.Vector3(0,0,0));
However when I do the following to try to get it to tween nothing moves. Seems the .onupdate isn't being called and I can't figure out what I've done wrong
var from = {
x : camera.position.x,
y : camera.position.y,
z : camera.position.z
};
var to = {
x : posX,
y : posY,
z : posZ
};
var tween = new TWEEN.Tween(from)
.to(to,600)
.easing(TWEEN.Easing.Linear.None)
.onUpdate(function () {
camera.position.set(this.x, this.y, this.z);
camera.lookAt(new THREE.Vector3(0,0,0));
})
.onComplete(function () {
camera.lookAt(new THREE.Vector3(0,0,0));
})
.start();
Any help appreciated
Did you add TWEEN.update() in your animate function? Because your code does work. See fiddle. http://jsfiddle.net/Komsomol/r4nctoxy/
function animate() {
TWEEN.update();
requestAnimationFrame(animate);
renderer.render(scene, camera);
controls.update();
}
I have a scene which renders a board with several objects. Clicking on one of the objects and by use of object picking I can get the object and animate the cameras position to sit in an 'overhead' view of the object. Then using the camera.lookAt method I can force the camera to look directly at the object.
I am noticing a quick jump at the start of my animation when calling camera.lookAt within the onUpdate method as it initially has a large distance to rotate to look at the object selected. Each subsequent call to camera.lookAt is tiny in comparison and is animated nicely.
// Position the camera to fit
var tween = new TWEEN.Tween(camera.position).to({
x: selectedObject.position.x,
y: selectedObject.position.y,
z: 1
}).easing(TWEEN.Easing.Quadratic.InOut).onUpdate(function() {
camera.lookAt(selectedObject.position);
}).onComplete(function() {
camera.lookAt(selectedObject.position);
}).start();
Is there any way to animate the method or will I have to manually transform the matrix values of the camera to look at my selected object?
Here is a fiddle with an example. It uses WebGLRenderer so please use a suitable browser.
http://jsfiddle.net/fungus1487/SMLwa/
Thanks for any help.
One thing you can do is tween both the camera position and the camera target (which you have to define).
var tween = new TWEEN.Tween( camera.position )
.to( {
x: selectedObject.position.x,
y: selectedObject.position.y,
z: 1
} )
.easing( TWEEN.Easing.Linear.None ).onUpdate( function () {
camera.lookAt( camera.target );
} )
.onComplete( function () {
camera.lookAt( selectedObject.position );
} )
.start();
var tween = new TWEEN.Tween( camera.target )
.to( {
x: selectedObject.position.x,
y: selectedObject.position.y,
z: 0
} )
.easing( TWEEN.Easing.Linear.None )
.onUpdate( function () {
} )
.onComplete( function () {
camera.lookAt( selectedObject.position );
} )
.start();
It's a little tricky, because the tween's need to run exactly concurrently, and they don't...
This is the reason for the camera.lookAt() call in the second tween.
WestLangley's answer works but seems oddly lengthy. I used the following with a pre-defined position(xyz) and target(xyz). Using perspective camera and trackball controls.
new TWEEN.Tween( camera.position ).to( {
x: position.x,
y: position.y,
z: position.z}, 600 )
.easing( TWEEN.Easing.Sinusoidal.EaseInOut).start();
new TWEEN.Tween( controls.target ).to( {
x: target.x,
y: target.y,
z: target.z}, 600 )
.easing( TWEEN.Easing.Sinusoidal.EaseInOut).start();