How to move the camera with the camera angle? - javascript

I made a simple threeJS 3d app in which you can look around and move with 4 keys. The problem is that it moves in the same direction no matter where you look to, obviously because I use:
camera.position.x += 0.1

You have to set the default displacement of (0.1, 0, 0) to a Vector3, then apply the camera's rotation (its quaternion) to that vector before adding it to its position. See the code below:
// Create camera
var camera = new THREE.PerspectiveCamera();
// Create displacement vector to re-use
var dispVector = new THREE.Vector3();
function onRightKey() {
dispVector.set(0.1, 0, 0); // Right key moves 0.1 on x axis
applyDisplacement();
}
function applyDisplacement() {
// We apply the camera's rotation to the displacement vector
dispVector.applyQuaternion(camera.quaternion);
// Move the camera position by the resulting displacement vector
camera.position.add(dispVector);
}
You can apply this same idea to the other keys: (-0.1, 0, 0) if you want to move left, or (0, 0.1, 0) if you want to move up, etc:
function onLeftKey() {
dispVector.set(-0.1, 0, 0); // Left key moves -0.1 on x axis
applyDisplacement();
}
function onUpKey() {
dispVector.set(0, 0.1, 0); // Left key moves 0.1 on y axis
applyDisplacement();
}

Related

How to rotate a ThreeJS Object3D object around its end?

Is it possible to rotate an Object3D object about its end? I found my object's center with const boundingBox = new THREE.Box3().setFromObject(this.object) to determine the center. Would rotating at a point mean translating the object to one point, rotate, then translate back?
Is it possible to rotate an Object3D object about its end?
Yes. By default, Object3D rotates about its center, but it's possible to change rotation point by "wrapping" object in parent (Object3D) and setting position for parent (this will be also a rotation point):
function changeRotationPoint(obj, scene, position) {
// create parent and add it to scene
const parent = new THREE.Object3D();
parent.add(obj);
if (obj.parent) {
obj.parent.add(parent);
} else {
// add to THREE scene if obj doesn't have a
// parent
scene.add(parent);
}
// position for rotation,
// for example (put your data): { x: 0.1, y: 0.1, z: 0 }
parent.position.set(position.x, position.y, position.z);
// correct position of obj, so only rotation point
// will be changed
const x = obj.position.x - position.x,
const y = obj.position.y - position.y,
const z = obj.position.z - position.z
obj.position.set(x, y, z);
}
// call function
changeRotationPoint(this.object, scene, { x: 0.1, y: 0.1, z: 0 });
// rotate this.object
this.object.parent.rotation.set(0.1, 0.2, 0.3);

Canvas: How do I rotate angle of shape with Event Handler?

I'm trying to make a pinball game and wondering the logic of the pinball flipper motion.
On the even handler of a right arrow, I want to move my rectangular piece several degrees up.
ctx.rotate(-(20*Math.PI/180));
ctx.beginPath();
ctx.rect(this.rposX , this.rposY, this.width, this.height);
ctx.fillStyle = 'red';
ctx.fill();
ctx.closePath();
ctx.rotate(20*Math.PI/180);
if (rPressed) {
this.flipRight(ctx);
}
Is what I have. How should I try to flip the flipper. Sometimes, I rotate it but that rotates all the objects.
Rotating a rendering
To do the flippers you create a function that draws a flipper at the origin (0,0) and along the xAxis left to right.
So from your rectangle example rather than draw the rectangle where the flipper is, draw it so that the canvas coords 0,0 is at the point of rotation. You will move it via the transform to the correct position.
ctx.strokeRect(-10,-10,100,20); // 0,0 is point of rotation
You position the flipper by moving its center point setTransform I use setTransform as it save having to use save and restore.
// x,y is the position you want the flipper to be.
ctx.setTransform(1,0,0,1,x,y); // sets position of flipper by moving point of rotation
And then rotate with
ctx.rotate(angle); // angle is in radians
The just draw the flipper
ctx.strokeRect(-10,-10,100,20); // 0,0 is point of rotation
Keyboard events
To animate I draw the flippers 60 times a second. I have two event listeners listen to keydown and keyup events. When a key is down I set the flipper flag on to true and when key up to false. I don't do any other processing in the key events.
See demo for more details on key event listeners
Animating the flippers
In the animation loop I call the flipper update function. It checks if the flipper is on or off and then moves the flipper depending on its state. This function is called once for every animation frame.
Example
I have not done a flipper or pinball game in a long time so I had a bit of fun and created some working flippers.
The function you want is called drawFlipper it has comments. The whole lot is animated using requestAnimationFrame
// get the canvas context
const ctx = canvas.getContext("2d");
// defines the flipper
const flipper = {
on : false, // on when key down
size : 20, // radius of flipper base
pos : 0.1,
shapeAng : Math.PI * 0.4, // approx angle to pointy end that line starts at the big arc
point : 5, // radius of pointy end
length : 100, // length of flipper
action : 0, // action is the rotational position
actionDest : 0, // is current destination where flipper should be
actionV : 0.0, // is movement towards destination
// next two numbers v must be 0 < v < 1
actionAccel : 0.7, // relative acceleration of flipper for action. bigger number faster flipper
actionDrag : 0.61, // amount of drag on flipper action. Smaller number more drag
actionExtent : 1, // max travel of flipper
actionBumperDamp : 0.8, // Amount of dampening flipper stop has
update(){
if(this.on){
this.actionDest = this.actionExtent;
}else{
this.actionDest = 0; // home position
}
this.actionV += (this.actionDest - this.action) * this.actionAccel;
this.actionV *= this.actionDrag
this.action += this.actionV
if(this.action > this.actionExtent){
this.action = this.actionExtent;
this.actionV *= this.actionBumperDamp;
this.actionV -= this.actionV;
}
}
}
// keyboard listeners
document.addEventListener("keydown",(e)=>{
flipper.actionDrag = Number(dragIn.value);
flipper.actionAccel = Number(accelIn.value);
flipper.on = true
});
document.addEventListener("keyup",(e)=>{ flipper.on = false});
window.focus(); // get the keyboards attention
// draws a flipper
// x,y is the location of the flipper base
// start angle is the home angle of the flipper
// flipperTravelDirection is flipper travel direction when active
function drawFlipper(x,y,startAng,flipperTravelDirection){
// set the position
ctx.setTransform(1,0,0,1,x,y);
// set the angle of the flipper plus action position
ctx.rotate(startAng + flipper.action * flipperTravelDirection);
// draw the flipper. the transform sets position and rotation
// so just have to render the flipper around its own center 0,0
ctx.beginPath();
ctx.arc(0, 0, flipper.size,flipper.shapeAng, Math.PI * 2 - flipper.shapeAng);
ctx.lineTo(flipper.length, - flipper.point);
ctx.arc(flipper.length,0,flipper.point,-Math.PI / 2,Math.PI /2)
ctx.closePath();
ctx.stroke();
}
var W,H; // canvas width and height
// draws the flippers. This function would do all rendering of the
// game animations
function mainLoop(time){
// resize if needed
if(canvas.width !== innerWidth || canvas.height !== innerHeight){ // resize canvas if window size has changed
W = canvas.width = innerWidth;
H = canvas.height = innerHeight;
}
// clear canvas
ctx.setTransform(1,0,0,1,0,0); // set default transform
ctx.clearRect(0,0,W,H); // clear the canvas
// update flipper actions
flipper.update();
// render the flippers left and right
drawFlipper(flipper.size * 2, H / 2, Math.PI * 0.25,-1);
drawFlipper(W - flipper.size * 2, H / 2, Math.PI * 0.75,1);
// get next animation loop
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
canvas {
position : absolute;
top : 0px;
left : 0px;
z-index : -10;
}
body {font-family : arial;}
Press any key to move flippers. Move sliders to change flipper acction.<br>
Left slider is acceleration, right is drag. Some settings make them flip out, change setting and hit key to fix<br>
Accel<input id="accelIn" type="range" min = 0.01 max = 0.99 step = 0.01 value = 0.5></input>
Drag<input id="dragIn" type="range" min = 0.01 max = 0.99 step = 0.01 value = 0.5></input>
<canvas id=canvas></canvas>

Moving the camera in the direction it's facing, with ThreeJS

Say I have a camera placed at:
{
x: 10,
y: 30,
z: 20
}
It's rotation is:
{
x: 0.1,
y: 0.2,
z: 0.3
}
I want to move the camera 1 unit from it's current position in the direction it's facing.
How do I do calculate the cameras new position?
I'm using ThreeJS.
The THREE.Camera class extends the THREE.Object3D class.
You can get the camera direction with the getWorldDirection method from the base class:
It returns a vector representing the direction in which the camera is looking, in world space.
When you have this direction vector you can use it to move the camera in the desired direction:
var direction = new THREE.Vector3();
camera.getWorldDirection( direction );
To move only 1 in that direction you can simply do:
camera.position.add( direction );
If you want to move more you could for example use the multiplyScalar method from the THREE.Vector3 class and multiply with the desired distance.
distance = 10;
camera.position.add( direction.multiplyScalar(distance) );
The camera looks down its local negative-z axis. So to move the camera in the direction it is facing, use this method:
camera.translateZ( - distance );
This is the most-efficient method.
three.js r.78

Three.js cylinder rotation in 3d plane

Ive been having the linewidth problem (something to do with ANGLE on window). I have resorted to using cylinders between 2 points (in 3D space). I have already solved the issue on getting the length of the cylinder based on the 2 points-3D distance formula.
I have been having trouble however getting the angle. I want the cylinder to rotate so that the angle found will make it so that the cylinder connects the two points.
Essensially I am trying to find a way to find the angle between (x1,y1,z1) and (x2,y2,z2). And having it modify a cylinder (cylinder.rotation.x, cylinder.rotation.y, and cylinder.rotation.z).
You can use a transformation matrix to do that. Here's some example code:
function createCylinderFromEnds( material, radiusTop, radiusBottom, top, bottom, segmentsWidth, openEnded)
{
// defaults
segmentsWidth = (segmentsWidth === undefined) ? 32 : segmentsWidth;
openEnded = (openEnded === undefined) ? false : openEnded;
// Dummy settings, replace with proper code:
var length = 100;
var cylAxis = new THREE.Vector3(100,100,-100);
var center = new THREE.Vector3(-100,100,100);
////////////////////
var cylGeom = new THREE.CylinderGeometry( radiusTop, radiusBottom, length, segmentsWidth, 1, openEnded );
var cyl = new THREE.Mesh( cylGeom, material );
// pass in the cylinder itself, its desired axis, and the place to move the center.
makeLengthAngleAxisTransform( cyl, cylAxis, center );
return cyl;
}
// Transform cylinder to align with given axis and then move to center
function makeLengthAngleAxisTransform( cyl, cylAxis, center )
{
cyl.matrixAutoUpdate = false;
// From left to right using frames: translate, then rotate; TR.
// So translate is first.
cyl.matrix.makeTranslation( center.x, center.y, center.z );
// take cross product of cylAxis and up vector to get axis of rotation
var yAxis = new THREE.Vector3(0,1,0);
// Needed later for dot product, just do it now;
// a little lazy, should really copy it to a local Vector3.
cylAxis.normalize();
var rotationAxis = new THREE.Vector3();
rotationAxis.crossVectors( cylAxis, yAxis );
if ( rotationAxis.length() < 0.000001 )
{
// Special case: if rotationAxis is just about zero, set to X axis,
// so that the angle can be given as 0 or PI. This works ONLY
// because we know one of the two axes is +Y.
rotationAxis.set( 1, 0, 0 );
}
rotationAxis.normalize();
// take dot product of cylAxis and up vector to get cosine of angle of rotation
var theta = -Math.acos( cylAxis.dot( yAxis ) );
//cyl.matrix.makeRotationAxis( rotationAxis, theta );
var rotMatrix = new THREE.Matrix4();
rotMatrix.makeRotationAxis( rotationAxis, theta );
cyl.matrix.multiply( rotMatrix );
}
I didn't write this. Find the full working sample here.
It's part of Chapter 5: Matrices from this awesome free Interactive 3D Graphics course taught using three.js.
I warmly recommend it. If you didn't have a chance to play with transformations you might want to start with Chapter 4.
As a side note. You can also cheat a bit and use Matrix4's lookAt() to solve the rotation, offset the translation so the pivot is at the tip of the cylinder, then apply the matrix to the cylinder.

Rotation anchor point in Three.js

I am defining a cone that I need to be able to rotate around its top point (the point where the cone thickness is the smallest one). I couldn't find yet the way to set the point around which the rotation to happen.
var coneGeometry = new THREE.CylinderGeometry(1000, 0, width, 50, 50, false);
var cone = new THREE.Mesh(coneGeometry, material);
cone.position.x = x;
cone.position.y = y + width / 2;
cone.position.z = z;
// I want this rotation to happen around the point given by the (x, y, z) location
cone.rotation.x = dip;
The cone geometry is centered at the origin. What you need to do is translate the cone geometry right after it is created so the tip of the cone is at the origin.
coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, coneHeight/2, 0 ) );
(The sign of the translation changes depending on which end of the cone is the small end.)
Now, when you rotate the cone, it will rotate around the tip. The tip will also be located at the position you set.
EDIT: You can now do this, instead:
coneGeometry.translate( 0, coneHeight/2, 0 ); // three.js r.72

Categories

Resources