tween camera movement around globe three.js and tween.js - three.js

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();
}

Related

Detecting object collision by dragging. Trying to use the raycaster intersectObjects method

I am simply trying to detect when the lower sphere (the draggable one) is intersecting with the upper ones. I'm sure there's something I do not understand, unfortunately, nothing is crossing my mind on what.
<script src='https://threejs.org/build/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/DragControls.js'></script>
<script>
window.onload = init;
// Global variables
var renderer, raycaster, mouse,
scene, camera, sphere1, sphere2,
sphere3, sphere4;
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
function init(){
// Get WebGL ready
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer = this.renderer;
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500);
camera.position.set(0, 0, 100);
camera.lookAt(0, 0, 0);
scene = new THREE.Scene();
// Get set
drawSpheres();
// Go
eventful();
animate();
};
function animate(){
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
function eventful(){ // Where all events happen
new THREE.DragControls([sphere1], camera, renderer.domElement);
window.addEventListener( 'mousemove', onMouseMove, false);
};
function drawSphere(){ // Sphere geometry
var geometry, material, sphere;
geometry = new THREE.SphereBufferGeometry(3, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2);
material = new THREE.MeshNormalMaterial();
sphere = new THREE.Mesh(geometry, material);
return sphere;
};
function drawSpheres(){ // Draw four corners for the quadrant
sphere1 = drawSphere(); sphere1.position.set(20, 0, 0);
sphere2 = drawSphere(); sphere2.position.set(15, 23, 0);
sphere3 = drawSphere(); sphere3.position.set(0, 22, 0);
sphere4 = drawSphere(); sphere4.position.set(-20, 20, 0);
scene.add(sphere1, sphere2, sphere3, sphere4);
};
function onMouseMove(event){ // Calculate mouse movements
// Pixel coordinates
mouse.x = event.clientX;
mouse.y = event.clientY;
raycasting(renderer, scene, camera);
};
function raycasting(renderer, scene, camera){
raycaster.setFromCamera(sphere1, camera); // Update the picking ray with the camera and mouse movements
intersects = raycaster.intersectObjects([sphere2, sphere3, sphere4]);
for(var i = 0; i < intersects.length; i++){
intersects[i].object.material.color.set(0xff0000);
console.log('Hit on: ', intersects[i]);
}
};
</script>
The only thing I can think of is my usage of the intersectObjects() method or the setFromCamera(), but I am not sure. I think this would make sense, since it is updated on mouse move. How would I say: "I want the draggable sphere to be the raycaster, as I move it, and detect collision"? Or something simpler to detect when things collide.
For instance, consider the following:
window.onload = init;
// Global variables
var renderer, raycaster, mouse,
scene, camera, sphere1, sphere2,
sphere3, sphere4;
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
console.log(raycaster);
function init(){
// Get WebGL ready
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer = this.renderer;
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500);
camera.position.set(0, 0, 100);
camera.lookAt(0, 0, 0);
scene = new THREE.Scene();
// Get set
drawSpheres();
// Go
eventful();
animate();
};
function animate(){
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
function eventful(){ // Where all events happen
new THREE.DragControls([sphere1], camera, renderer.domElement);
window.addEventListener( 'mousemove', onMouseMove, false);
};
function drawSphere(){ // Sphere geometry
var geometry, material, sphere;
geometry = new THREE.SphereBufferGeometry(3, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2);
material = new THREE.MeshNormalMaterial();
sphere = new THREE.Mesh(geometry, material);
return sphere;
};
function drawSpheres(){ // Draw four corners for the quadrant
sphere1 = drawSphere(); sphere1.position.set(20, 0, 0);
sphere2 = sphere1.clone(); sphere2.position.set(15, 23, 0);
sphere3 = sphere1.clone(); sphere3.position.set(0, 22, 0);
sphere4 = sphere1.clone(); sphere4.position.set(-20, 20, 0);
console.log(sphere1, sphere2, sphere3, sphere4);
scene.add(sphere1, sphere2, sphere3, sphere4);
};
function onMouseMove(event){ // Calculate mouse movements
// Normalized Coordinate System
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
raycasting(renderer, scene, camera);
};
function raycasting(renderer, scene, camera){
raycaster.setFromCamera(mouse, camera); // Update the picking ray with the camera and mouse movements
intersects = raycaster.intersectObjects([sphere2, sphere3, sphere4]);
for(var i = 0; i < intersects.length; i++){
console.log('Hit on: ', intersects[i].object.uuid);
}
};
In this example, the raycaster is the mouse. You'll see the 'hit' message on the console, every time there is a mouse hover the spheres I've specified in the intersectObjects() method.
if you aren't casting a ray from the mouse cursor, you don't need .setFromCamera().. you would just set up the ray manually.
You can use raycasting to check if one sphere hits another, or you can do a sphere->sphere intersection test like this..
var tmp = new THREE.Vector3();
function spheresCollide(centerA,radiusA,centerB,radiusB){
var sqdist = radiusA+radiusB;
sqdist*=sqdist;
tmp.copy(centerB).sub(centerA)
if(tmp.lengthSq()<sqdist)return true;
return false;
}
//centerA and centerB are the vector3 positions of your spheres.. radiusA and B are the sphere radii
To do a raycast, you'll need to do something like the following, for each sphere:
rayCaster.ray.origin.copy(sphereA.position);
rayCaster.ray.direction.copy(sphereB.position).sub(sphereA.position).normalize()
intersects = raycaster.intersectObjects([sphereB]);
for(var i = 0; i < intersects.length; i++){
tmp.copy(intersects[i].position).sub(sphereA.position);
if(tmp.length()<(radiusA+radiusB)){
intersects[i].object.material.color.set(0xff0000);
console.log('Hit on: ', intersects[i]);
}
}
It took me a while to get through this. There's something different about raycasting to a moving object. The idea behind ray casting is that a ray is being casted. For this example, the setFromCamera() method won't do, because the 'sphere' is supposed to be the object the ray(s) is(are) coming from.
<script src='https://threejs.org/build/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/DragControls.js'></script>
<script>
window.onload = init;
// Global variables
var renderer, raycaster,
scene, camera, sphere1, sphere2,
sphere3, sphere4, dragControls;
function init(){
// Get WebGL ready
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer = this.renderer;
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500);
camera.position.set(0, 0, 100);
camera.lookAt(0, 0, 0);
scene = new THREE.Scene();
// Get set
drawSpheres();
// Go
eventful();
animate();
};
function animate(){
requestAnimationFrame(animate);
renderer.render(scene, camera);
raycasting();
};
function eventful(){ // Where all events happen
dragControls = new THREE.DragControls([sphere1], camera, renderer.domElement);
dragControls.addEventListener('dragstart', onDragStart, false);
dragControls.addEventListener('dragend', onDragEnd, false);
};
function drawSphere(){ // Sphere geometry
var geometry, material, sphere;
geometry = new THREE.CubeGeometry(3,3,3,1,1,1);
material = new THREE.MeshBasicMaterial({
wireframe: true
});
sphere = new THREE.Mesh(geometry, material);
return sphere;
};
function drawSpheres(){ // Draw four corners for the quadrant
sphere1 = drawSphere(); sphere1.position.set(20, 0, 0);
sphere2 = sphere1.clone(); sphere2.position.set(15, 23, 0);
sphere3 = sphere1.clone(); sphere3.position.set(0, 22, 0);
sphere4 = sphere1.clone(); sphere4.position.set(-20, 20, 0);
console.log(sphere1, sphere2, sphere3, sphere4);
scene.add(sphere1, sphere2, sphere3, sphere4);
};
function onDragStart(event){
console.log('on drag start');
};
function onDragEnd(event){
console.log('on drag end');
};
function onMouseMove(event){ // Calculate mouse movements
// Normalized Coordinate System
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
};
//////////////////////////////
//////// RAYCASTING //////////
//////////////////////////////
function raycasting(){ // Blast rays like Cyclops or Superman, but only to measure proximity
var sphere1Origin, // The 3D position of sphere1 when the page loads
vIndex, // Vertex index
sphere1VLength, // The amount of vertices
dVector, // The directions the ray should be pointing to while it is moving
raycaster, // The ray casting from a given point
sphere1Origin = getUpdatedPosition();
sphere1VLength = sphere1.geometry.vertices.length;
for(vIndex = 0; vIndex < sphere1VLength; vIndex++){
dVector = bindRaysToVertices(sphere1, vIndex);
raycaster = raycast(sphere1Origin, dVector);
collided = detectCollision(raycaster, dVector).hasCollided;
if(collided){
console.log('Hit!');
}
}
};
function detectCollision(raycaster, dVector){ // Determines whether there is/are (a) collision(s)
var collisions, // Results of each collisions
collided; // True/False
collisions = raycaster.intersectObjects([sphere2, sphere3, sphere4]);
collided = collisions.length > 0 && collisions[0].distance < dVector.length();
return {
hasCollided: collided,
collisionsList: collisions
};
};
function bindRaysToVertices(sphere1, vIndex){ // Make the geometry blast rays in all directions, while moving
var lVertex, // The re-calculated (updated) vertices for the moving object
gVertex, // The complete representation of the re-calculated (updated) vertices
dVector; // The directions the ray should be pointing to while it is moving
lVertex = sphere1.geometry.vertices[vIndex].clone();
gVertex = lVertex.applyMatrix4(sphere1.matrix);
dVector = gVertex.sub(sphere1.position);
return dVector;
};
function getUpdatedPosition(){
var sphere1Origin, // The 3D position of sphere1 when the page loads
sphere1Origin = sphere1.position.clone();
return sphere1Origin;
};
function raycast(sphere1Origin, dVector){
// Make the sphere cast the ray, through its vertices,
// while moving, using a Normalized Coordinate System
return new THREE.Raycaster(sphere1Origin, toNCS(dVector));
};
function toNCS(dVector){ // To Normalize Coordinate System
return dVector.clone().normalize();
};
</script>
Following Stemkoski example, I've decided to use cubes as wireframes, and should there be a need to have a sphere, the cube should be within it. Otherwise it will be computationally expensive to have a sphere blasting rays like the Sun for proximity detection purposes.

How to change rotation direction in three.js

I'm new to three.js and having trouble finding documentation on how to change rotation direction. The current rotation is set so the sphere looks like it's spinning towards the viewer on a screen but I want it to rotate in a clockwise direction, so it looks like it's spinning from a side view if a viewer is looking at it.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, 500 / 400, 1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(500, 400);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.SphereGeometry(3, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh(geometry);
scene.add(cube);
camera.position.z = 10;
var render = function () {
requestAnimationFrame(render);
cube.rotation.x -= 0.10;
cube.rotation.y += 0.00;
renderer.render(scene, camera);
};
render();
http://jsfiddle.net/SF9tX/1968/
If I understand your question correctly, then rotation around different directions (or axis') can be achieved by incrementing different components of the objects rotation vector.
So for example, if you want to rotate the cube around the y-axis (ie to create a "spinning globe" effect) you can increment the .y component of the .rotation vector, as follows:
var render = function () {
requestAnimationFrame(render);
// This was casuing rotation around about the x-axis
// cube.rotation.x -= 0.10;
// Add this to cause rotation around the z-axis,
cube.rotation.z += 0.10;
renderer.render(scene, camera);
};
Ah I figured it out. It's as simple as replacing the cube.rotation.x to cube.rotation.z, so:
camera.position.z = 10;
var render = function () {
requestAnimationFrame(render);
cube.rotation.z -= 0.10;
cube.rotation.y += 0.00;
renderer.render(scene, camera);
};

touch controls: repeat action until touchend

I am trying to add touch controls to a three.js scene. I want to move the camera in whatever direction the user touches. It works great using the keyboard because you can press and hold the button and the camera moves continuously. But when I try the same thing using touchstart, you have to keep tapping the screen over and over to move, you can't just hold your finger down like on a keyboard or mouse.
I looked at touchmove, but if you just tap and hold without moving, there are no new touches.
Is there something similar to holding down the keyboard or mousekey using touch events?
There is no builtin callback for a touch event which fires repeatedly like the keyboard. You can, however, simply track the start and end of the touch and then call the move method at a set interval.
First, subscribe to the correct events and set a bool to track the state:
var isTouching = false;
window.addEventListener("touchstart", () => isTouching = true);
window.addEventListener("touchend", () => isTouching = false);
In Three.js you will most likely already have a render loop (e.g. a function called "animate"). Check the state variable at every iteration and apply the movement each time. You may need to also factor in deltaTime (the duration of the last frame), to make movement framerate independent.
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;
if (isTouching) {
console.log("move camera");
}
renderer.render(scene, camera);
}
Here is a snippet which shows the basic approach. Click and hold in the left or right half of the output window to move the camera.
var camera, scene, renderer, mesh, material, clock;
init();
animate();
var isTouching = false;
var mousePositionX;
window.addEventListener("mousedown", (e) => {
isTouching = true;
mousePositionX = e.clientX;
});
window.addEventListener("mouseup", (e) => isTouching = false);
function init() {
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
clock = new THREE.Clock();
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 400;
scene = new THREE.Scene();
material = new THREE.MeshPhongMaterial();
var geometry = new THREE.BoxGeometry(200, 200, 200);
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var light = new THREE.AmbientLight(0x404040);
scene.add(light);
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
window.addEventListener('resize', onWindowResize, false);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;
let deltaTime = clock.getDelta();
if (isTouching) {
let speed = 200; // px per second
let movement = speed * deltaTime;
if (mousePositionX > window.innerWidth / 2) {
camera.translateX(-movement);
} else {
camera.translateX(movement);
}
}
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
body {
padding: 0;
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.min.js"></script>

Three.js - Faces missing after Blender import

I tried to import a JSON exported file of my 3D model and import it in Three.js but it seems some faces are missing.
I am not sure if it was an export problem because when I rotate it, the left face exists but the right face does not, vice versa.
Here is my original model in Blender:
var scene, camera, renderer;
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var SPEED = 0.01;
function init() {
scene = new THREE.Scene();
initMesh();
initCamera();
initLights();
initRenderer();
document.body.appendChild(renderer.domElement);
}
function initCamera() {
camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
camera.position.set(0, 3.5, 5);
camera.lookAt(scene.position);
}
function initRenderer() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
}
function initLights() {
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 0, 1, 0 );
scene.add( directionalLight );
}
var mesh = null;
function initMesh() {
var loader = new THREE.JSONLoader();
loader.load('./model.json', function(geometry, materials) {
mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.75;
mesh.translation = THREE.GeometryUtils.center(geometry);
mesh.position.x = -5;
scene.add(mesh);
});
}
function rotateMesh() {
if (!mesh) {
return;
}
mesh.rotation.y -= SPEED;
}
function render() {
requestAnimationFrame(render);
rotateMesh();
renderer.render(scene, camera);
}
init();
render();
Hope you can help me with this problem.
Thanks in advance!
I would suspect your problem has to do with the face-normals pointing in the wrong direction. To check if this is the case you could try to set all materials to double-sided:
materials.forEach(function(mat) {
mat.side = THREE.DoubleSide;
});
With Double-Sided mode, the faces are drawn regardless of the normals direction, so you should see all faces if enabled.
Or you could use the THREE.FaceNormalsHelper to have a look at the normals yourself.
scene.add(new THREE.FaceNormalsHelper(mesh, 2, 0x00ff00, 1));
This will render arrows for all faces indicating the normal-direction.
If the normals are wrong you can fix this in blender by selecting all affected faces and using the command Mesh>Faces>Flip Normals from the menu or in the Tools-Panel on the right-hand side in the "Shading/UV"-Tab. Sometimes just selecting all faces and running "Recalculate Normals" from the Tools will work as well.
Blender also has a display-mode for face-normals in the right hand menu in the "Mesh Display"-Section.

Why does this cube not rotate around its own axes?

I've got a cube which I would like to rotate around its own axes in 3D space by using keyboard input. The cube is still rotating around the world axes.
Here's my code:
var rotation_matrix_y, rotation_matrix_x, rotation_matrix_z;
var geometry = new THREE.CubeGeometry(1,1,1);
var material = new THREE.MeshBasicMaterial({color: 0x00ff00, wireframe: true});
var cube = new THREE.Mesh(geometry, material);
var axisHelper = new THREE.AxisHelper( 5 );
cube.add( axisHelper );
scene.add(cube);
cube.rotation.set(0, 0, 0);
cube.matrix.makeRotationFromEuler(cube.rotation);
var render = function () {
document.addEventListener("keydown", onKeyDown, false);
function onKeyDown(e) {
// W - up
if (e.keyCode == 87) {
rotation_matrix_x = new THREE.Matrix4().makeRotationX(.0001);
cube.applyMatrix(rotation_matrix_x);
}
// S - down
if (e.keyCode == 83) {
rotation_matrix_x = new THREE.Matrix4().makeRotationX(-0.0001);
cube.applyMatrix(rotation_matrix_x);
}
// D - right
if (e.keyCode == 68) {
rotation_matrix_y = new THREE.Matrix4().makeRotationY(-0.0001);
cube.applyMatrix(rotation_matrix_y);
}
// A - left
if (e.keyCode == 65) {
rotation_matrix_y = new THREE.Matrix4().makeRotationY(0.0001);
cube.applyMatrix(rotation_matrix_y);
}
requestAnimationFrame(render);
stats.update();
renderer.render(scene, camera);
};
There are a couple of questions similar to this one, but they all seem to be outdated and some of the methods have been deprecated.
An easy way to rotate an object on its own x-axis is like so:
object.rotateX( radians );
There is also object.rotateY( radians ); and object.rotateZ( radians );
three.js r.66

Resources