ThreeJS: rotate on y axis to face camera - javascript

I want my object to rotate on the Y axis to always face the camera (as if a human was turning around in circles), as the camera moves.
This is what I have tried so far:
var render = function() {
animationFrameId = window.requestAnimationFrame( render);
obj.lookAt(camera.position);
obj.rotation.set(0, obj.rotation.y, 0);
}
But i am missing some simple math or trig function, because when i rotate, it eventually 'jumps' and the object appears to face the wrong direction

If you use a .lookAt the object look to the camera in all directions, you have to calculate the angle on Y plane between camera and mesh like this.
var render = function() {
animationFrameId = window.requestAnimationFrame( render);
obj.rotation.y = Math.atan2( ( camera.position.x - obj.position.x ), ( camera.position.z - obj.position.z ) );
}

Related

Circular radius intersection (like a circular cursor brush) - Three js

I'd like to be able to catch the faces of an object in the radius of a circular cursor (like in painting/photoshop).
I'll show you what is it for https://jsfiddle.net/Shaggisu/w7ufmutr/9/
I'd want be able to selct not only the single face that in the moment intersects with the mouse point but all faces that might be in the circular radius, I tried to uplod some image for that cursor but cant really make it work with external files in jsfiddle.
My question is, if is there some standard method of achieving multiple selection/intersection in a specified radius or should I devised some code that would for example reiterate on suroundin faces around the mouse point in specific moment.
I'm still quite new to three.js so I would ask for some directions to go with it, and especialy if there are some solid ways to achieve, any tip would be helpful too.
var brushTexture = THREE.ImageUtils.loadTexture( '/cur_circle.png' );
var brushMaterial = new THREE.SpriteMaterial( { map: brushTexture, useScreenCoordinates: true, alignment: THREE.SpriteAlignment.center } );
brushSprite = new THREE.Sprite( brushMaterial );
brushSprite.scale.set( 32, 32, 1.0 );
brushSprite.position.set( 50, 50, 0 );
scene.add( brushSprite );
//////////////////////////////////////////////////////////////////////
// initialize object to perform world/screen calculations
projector = new THREE.Projector();
// when the mouse moves, call the given function
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
}
function onDocumentMouseDown( event )
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
console.log("Click.");
// update the mouse variable
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// find intersections
// create a Ray with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
// create an array containing all objects in the scene with which the ray intersects
var intersects = ray.intersectObjects( targetList );
// if there is one (or more) intersections
if ( intersects.length > 0 )
{
controls.enabled = false; // stops camera rotation
console.log("Hit # " + toString( intersects[0].point ) );
// change the color of the closest face.
intersects[ 0 ].face.color.setRGB( 0.8 * Math.random() + 0.2, 0, 0 );
intersects[ 0 ].object.geometry.colorsNeedUpdate = true;
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
}
}
function onDocumentMouseMove( event){
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
brushSprite.position.set( event.clientX, event.clientY, 0);
// find intersections
// create a Ray with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
// create an array containing all objects in the scene with which the ray intersects
var intersects = ray.intersectObjects( targetList );
// if there is one (or more) intersections
if ( intersects.length > 0 )
{
console.log("Hit # " + toString( intersects[0].point ) );
// change the color of the closest face.
intersects[ 0 ].face.color.setRGB( 0.8 * Math.random() + 0.2, 0, 0 );
intersects[ 0 ].object.geometry.colorsNeedUpdate = true;
}
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
}
function onDocumentMouseUp( event){
event.preventDefault();
document.removeEventListener( "mousemove", onDocumentMouseMove);
controls.enabled = true;
}
The code is modified version of the stemkoskis github that I used for practice.
I have already extended it somewhat for camera management in intersection events and continous selection, but the selection of multiple faces in a radius is what interests me now the most.
You can do this in javascript, modifying vertex color, like you do it in your sample but you will be quickly limited by the number of polygon.
That said, consider your brush like a cone, which start from the Ray.origin and extend in Ray.direction. The radius of the cone is driven by the radius of your brush.
Iterate over each vertices.
For each, get the minimum distance between the vertex to the Ray line.
Get the radius of the brush/cone based on the distance between this vertex and the Ray.origin
If the minimum distance is inferior to the cone radius, your vertex is "in". You can also handle the distance to create a smooth brush.
It should looks like this, it kind of pseudo code, you may need to adapt to ThreeJs Math lib:
// Important, Ray origin and direction must be defined in the same space a vertices positions
// You may need to transform ray origin and direction in object local space.
// get the length of Ray.direction
// may be useless if 'direction' is normalized
var rayDirLenSq = ray.direction.length();
rayDirLenSq *= rayDirLenSq;
var brushRadius = 10.0;
for( var i=0;i< vertices.length;i++){
// get the vertex
var v = vertices[i];
var vdir = v.sub( ray.origin );
var dot = vdir.dot( ray.direction ) / rayDirLenSq;
if( dot < 0 ){
// handle vertices behind the camera if needed
}
// v2 : projection of the vertex onto ray line
var v2 = ray.direction.clone().multiplyScalar( dot );
// v3 : projection -> vertex
var v3 = vdir.subtract( v2 )
// dist is the distance between the vertex and the ray line
var dist = v3.length()
// 0 when vertex is at the brush border
// 1 when vertex is in the brush center
var paintingFactor = Math.max(0.0, 1.0 - dist/brushRadius )
}
Depending of what you want, you can store the painting factor of each vertices to get a mean factor per faces. Or you can modify vertex color of each vertices independantly to get gradients on your faces...
I didn't test the code, it may contain some mistakes :)
A more advanced method
You could also use a texture to paint on. You will get rid of vertex (and javascript) limitations. You will be able to paint with textured brushes, and have detail inside a triangle (no more vertex color).
The principle :
You need UVs datas and a texture + FBO for each of your meshes.
In a prepass, for each mesh, render it to it's Fbo in it's uvs coords
gl_Position = vec4( UVs*2.0-1.0, 0.0, 1.0 );
Provide the worldSpace vertex position to fragment shader, so you can access the world space position of each pixel of the object texture.
vVertexPosition = modelMatrix * aPosition;
With vVertexPosition in your fragment shader, you can then use the same method as the javascript one to get the brushFactor of each pixels of your mesh.
You can even project this world space pixel position in a custom projection matrix based on the Ray to get the uvs coordinate of the pixel in a brush texture, and paint with textured brush.

Rotating earth on its axis

I am trying to rotate earth about it's tilted axis in three js. I found this solution, and I am trying to use it on my code, but it doesn't do anything.
When I execute this code the planet just sits there and doesn't rotate at all. I don't really have a firm grasp of what a quaternion is or how it works, so I am not sure what is going wrong.
function rotateAroundAxis(object, axis, radians) {
var vector = axis.normalize();
var quaternion = new THREE.Quaternion().setFromAxisAngle(vector, radians);
object.rotation = new THREE.Euler().setFromQuaternion( quaternion );
}
earthAxis = new THREE.Vector3(Math.cos(23.4*Math.PI/180), Math.sin(23.4*Math.PI/180), 0);
function render() {
stats.update();
step += 0.02;
rotateAroundAxis(earth, earthAxis, step);
}
First, you need to tilt your sphere's geometry by 23.4 degrees by applying a transformation to it.
var radians = 23.4 * Math.PI / 180; // tilt in radians
mesh.geometry.applyMatrix( new THREE.Matrix4().makeRotationZ( - radians ) );
Then, to rotate your earth on its axis in object space, first normalize the axis you are rotating around.
earthAxis = new THREE.Vector3( Math.sin( radians ), Math.cos( radians ), 0 ).normalize();
Then in your render function, do this:
earth.rotateOnAxis( earthAxis, 0.01 ); // axis must be normalized
three.js r.69

How to rotate a THREE.PerspectiveCamera around on object

I am making this program where you can click on an object, zoom to it, then look at it from all angles by holding the right mouse button and dragging. I need the camera to be going around the object, not rotate the object with the camera looking at it. I honestly just have no idea how to math it out!
For testing there is already a game object with an xyz we have selected and are looking at
var g = new GameObject(500, 0, 0);//The game object with xyz
this.selected = g;//set selected to g
//Create and set the camera
this.camera = new THREE.PerspectiveCamera(45, w/h, 1, 10000);
this.camera.position.x = 0;
this.camera.position.y = 0;
this.camera.position.z = 0;
//set camera to look at the object which is 500 away in the x direction
this.camera.lookAt(new THREE.Vector3(this.selected.x, this.selected.y, this.selected.z));
So the radius between the camera and the object is 500 and while selected and rotating, the camera should always be 500 away.
I update the scene here:
Main.prototype.update = function(){
this.renderer.render(this.scene, this.camera);//scene is just some ambient lighting
//what to do when mouse right is held down
if(this.rightMouseDown){
//placeholder functionality, needs to rotate around object based on mouse movements
this.camera.position.x -= 5;
}
}
How do I rotate this camera around g with a radius of 500?!?!
As gaitat mentioned, trackball controls are the best place to start with many configurable parameters to make camera rotation/revolution easy. One enormous potential benefit of this method ( especially for your project ) is avoiding "gimbal lock" which is the source of much frustration when working with rotations. Here's a link that might help you with Trackball controls and Orbitcontrols:
Rotate camera in Three.js with mouse
Another option would be setting camera coordinates yourself in the animation loop which is actually quite simple:
var angle = 0;
var radius = 500;
function animate() {
...
// Use Math.cos and Math.sin to set camera X and Z values based on angle.
camera.position.x = radius * Math.cos( angle );
camera.position.z = radius * Math.sin( angle );
angle += 0.01;
...
}
Another option would be to connect the camera to a pivot object and just rotate the pivot:
var camera_pivot = new THREE.Object3D()
var Y_AXIS = new THREE.Vector3( 0, 1, 0 );
scene.add( camera_pivot );
camera_pivot.add( camera );
camera.position.set( 500, 0, 0 );
camera.lookAt( camera_pivot.position );
...
camera_pivot.rotateOnAxis( Y_AXIS, 0.01 ); // radians
If you pursue this option, be aware that the camera object is in "camera pivot space", and might be more challenging to manipulate further.

How to find the rotation of an object relative to the camera

I'm having trouble figuring out the angle of an object relative to the camera, I'm trying to code a spaceship with a camera following it. I have the camera following the ship but the rotation of the camera sometimes is a little off, here's my camera code:
var focalpoint = new THREE.Vector3(
actor.position.x,
actor.position.y,
actor.position.z + 14
);
//move camera closer to the object if it gets too far away
var calculatedDistance = distance(camera.position, actor.position);
var cameraTolerance = calculatedDistance - this.cameradistance.min;
var closingvelocity = cameraTolerance * 0.02;
if(calculatedDistance > this.cameradistance.max)cameravelocity.z = -closingvelocity;
if(calculatedDistance < this.cameradistance.min)cameravelocity.z = closingvelocity;
//slow down the camera
if(calculatedDistance < this.cameradistance.max && calculatedDistance > this.cameradistance.min){
cameravelocity.z = 0;
}
camera.translateX( cameravelocity.x );
camera.translateY( cameravelocity.y );
camera.translateZ( cameravelocity.z );
camera.lookAt(focalpoint);
camera.rotation.z = 0;
Now I need to limit the rotation of the spaceship (actor) so it doesn't start flying towards the camera, and to fix the camera flipping over problem. So I need to figure out how to find the rotation of the actor relative to the camera, I have absolutely no idea where to start calculating or even how.
found the answer, by inversing the target's rotation, then multiplying like so:
var rotationOffset = actor.quaternion.clone().inverse();
var rotation = camera.quaternion.clone().multiply( rotationOffset );

three.js: transforming coordinates from camera space to scene space

I'm trying to place a cube relative to the camera, rather than relative to the scene. The thing is, to place it in the scene (which I have to do make it show), I have to know the scene coordinates that correspond to the cubes camera space coordinates. I found this function "projectionMatrixInverse" in THREE.Camera. It has a nice function called "multiplyVector3" which I hoped would enable me to transform a vector (1,1,1) back to scene space like this:
var camera, myvec, multvec; // (and others)
camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, - 2000, 1000 );
camera.position.x = 200;
camera.position.y = 100;
camera.position.z = 200;
myvec = new THREE.Vector3(1,1,1);
console.log("myvec: ", myvec);
multvec = camera.projectionMatrixInverse.multiplyVector3(THREE.Vector3(1,1,1));
console.log("multvec: ", multvec);
the thing is, on the console i get:
myvec: Object { x=1, y=1, z=1}
TypeError: v is undefined
var vx = v.x, vy = v.y, vz = v.z;
multiplyVector3 simply doesn't accept my myvec, or says it's undefined, even though the console says it's an object. I don't get it.
The camera is located at the origin of it's coordinate system, and looks down it's negative-Z axis. A point directly in front of the camera has camera coordinates of the form ( 0, 0, z ), where z is a negative number.
You convert a point p
p = new THREE.Vector3(); // create once and reuse if you can
p.set( x, y, z );
from camera coordinates to world coordinates like so:
p.applyMatrix4( camera.matrixWorld );
camera.matrixWorld is by default updated every frame, but if need be, you can update it yourself by calling camera.updateMatrixWorld();
three.js r.95
This may also be what you're after:
scene.add( camera );
brick.position.set( 0, 0, -1 );
camera.add( brick );

Categories

Resources