First person simulation with three.js using keyboard arrows - javascript

For my source, visit http://jsfiddle.net/fYtwf/
Background
I have a simple 3d simulation using three.js where the camera is surrounded in 3 dimensions by cubes. These cubes are to help visualise where the camera is looking until the view controls are coded and tested. I want to create a simple 3D application, where the camera is controlled via up, down, left and right keys. Just like moving your head
Issues
In my current application, when facing forward, and starting to look up, we are successful. However when we turn left 90 degrees, and we press the up arrow... The wrong thing happens. the camera increments the x axis, but because we're facing another direction, modifying the x axis ALONE is WRONG...
Now I'm assuming this is because some trigonometry is required to calculate the correct values for the z axis. However, my trig isn't brilliant.
Current
To get a better understanding of what i mean, please visit my jsfiddle : http://jsfiddle.net/fYtwf/
UP key ONLY increments X
DOWN key ONLY decrements X
LEFT key ONLY increments Y
RIGHT key ONLY decrements Y
Q key ONLY increments Z
W key ONLY decrements Z
( Q and W were only coded to try and help me understand. )
From my current understanding, when I press the UP key, X must increment and the Z axis must be modified based on what the current Y axis is. However I don't know the algorithm :(
So X and Z must be modified in the KEYUP code ( I think, please correct me if I am wrong )
// setRotateX, getRotateX, setRotateY and getRotateY are extended
// camera functions I wrote so I could work with degrees. Solution
// IS NOT required to use them, they just helped me
switch( key )
{
case KEYUP:
if ( camera.getRotateX() < 90 ){ // restrict so they cannot look overhead
camera.setRotateX( camera.getRotateX() + VIEW_INCREMENT );
}
break;
case KEYDOWN:
if ( camera.getRotateX() > -90 ){ // restrict so they cannot look under feet
camera.setRotateX( camera.getRotateX() - VIEW_INCREMENT );
}
break;
case KEYLEFT:
camera.setRotateY( camera.getRotateY() + VIEW_INCREMENT );
break;
case KEYRIGHT:
camera.setRotateY( camera.getRotateY() - VIEW_INCREMENT );
break;
}

There are a number of solutions to this problem, but since you only want the camera to rotate up, down, left, and right, the answer in this case is easy.
You just need to set the camera Euler order to "YXZ" like so:
camera.rotation.order = "YXZ"; // three.js r.65
If you do that, everything becomes very intuitive.
Here is an updated fiddle: http://jsfiddle.net/fYtwf/3/ (this demo is using r.54, however)
Once you change camera.rotation.z from it's default value of zero, things will become very confusing. So don't do that. :-)
three.js r.65

While this does not directly fix your code, I thought I'd mention that Three.js provides two ready-made controllers to navigate in FPS mode. They both use mouse for looking and can move, but should be rather simple to adapt to keyboard look and remove movement if needed. They are:
FirstPersonControls
PointerLockControls
I'd recommend the latter as a starting point because it's rather simple and the former confusingly has the looking code twice, probably as an artifact from old features.

Related

Rotation around a point causes weird glitches in THREE.js

Every element from my scene is made of a chain of 3 Object3Ds. The order parent-to-child is cellPivot -> modifier -> setup
setup's purpose is to permanently align a loaded object by resizing / giving some padding that must always be there. It is not supposed to be changed once set
modifier's purpose is to actually perform the real transformation on the object
cellPivot's purpose is to allow me to drag modifier into a cell grid
An example why all this is needed: let's say I have a vertical door in an orthographic perspective that I wanna fit in a 1x1 space, so I give some padding on the x-axis to align the door in the center, similar to the picture below where the orange block is the door
Since I want to move this in any cell in the map, I use cellPivot's position to decide where. I can't use right away modifier since sometimes I wanna rotate the model inside the cell, which requires to modify both position and rotation (since my models are not built around (0, 0, 0), but along +X and +Z)
I have succesfully managed to rotate these doors by rotating modifier around the center of the model (which acts as a pivot). Here's the functions that does the rotation:
three.Object3D.prototype.pivot = function(pivot, f) {
pivot = lib.VecToVector3(three, pivot); // just a conversion between libs
this.position.sub(pivot);
f(this);
this.position.add(pivot);
return this;
};
three.Object3D.prototype.pivotRotate = function(pivot, axis, theta, rotational = false, abs = false) {
if(abs)
theta -= this.rotation.y; /// not good, handles only y
this.pivot(pivot, () => this.position.applyAxisAngle(axis, theta));
if(rotational)
this.rotateOnAxis(axis, theta);
return this;
};
The line that rotates the door and works:
this.o3d.userData.modifier.pivotRotate(this.o3d.userData.center, new three.Vector3(0, 1, 0), this.rot, true);
I'm now trying to do something similar with the player too. I record what keys are pressed, I calculate the normal of the vector of desired direction (if I press W and D I'll get (1, 1), if I press just W I'll get (0, 1)), after which I use the following line to detect the angle at which the user wanna move:
Math.atan2(-normal[1], normal[0]);
I have already tested that the angle is correct. On top of that, the codebase before "rotating around a pivot" used the same code and it worked fine
Everytime there's actually a direction the user wanna go, I'll run the following line:
this.o3d.userData.modifier.pivotRotate(this.o3d.userData.center, new three.Vector3(0, 1, 0), Math.atan2(-normal[1], normal[0]), true, true);
If the user just keeps a key pressed, then abs will make sure that no visible rotation is made (since theta will be 0)
Here's the problem: everytime I press A, be it in combination with W or S or not, the character will rotate like insane. I put after the line from above the following code to see what's happening:
com.log(new three.Euler().setFromQuaternion(this.o3d.userData.setup.getWorldQuaternion(new three.Quaternion())));
I'm getting this:
As you can see, x and z are reaching -pi, and y bouces back and forth. This does not happen for any other combination that does not contain key A
Update after 2 days:
I have rewrote my function like this:
I got these in console while trying to move in the problematic positions:
As it can be seen in the first log, my target is at rotation 0 and is going for -2.35..., but rotAfterRot is showing weird results..: -pi and -.78...
This is the result of running this.rotateOnAxis(axis, theta). I have changed this exact line with this.rotation.y += theta. Now everything is working as it should be: no weird -pi and rotAfterRot.y is actually theta
My guess is that rotateOnAxis is also counting other features of the object, like position, but still can't figure how it spits that -pi

Move up and down using pointer lock controls THREE.js

I need to move up and down using three.js's pointerlockcontrols. I am looking for the same kind of movement as three.js's flycontrols, where the amount you move up and down is proportional to the direction you are looking in (ie. if your looking north-east, you move in the Y and Z directions at the same rate).
I tried:
if(keys[38] || keys[87]){
Controls.moveForward(playerSpeed);
Camera.position.y += Math.cos(Camera.rotation.y) * playerSpeed;
}
Where keys is just a object of all the keys and whether they are down, however this didn't give the appropriate behavior, as looking straight down also moved you forward a lot.
Has anyone ever accomplished or knows how to do this?

Need help for javascript and trigonometry

http://jsfiddle.net/techsin/CxFRC/15/ all chrome only click and drag, up and back arrow.
pastebin.com/gXS1J7zw
The Problem: I can't make c=$('.con') go/translate sideways. I have managed to do front and backward. But I cant workout the formula for sideways.
This is my first attempt. Use arrows keys, forward and backward works are in different function. But leftRight don't.
Code in question: you don't need to worry about rest of the code main logic for right left lies here. I have tried finding the perpendicular angle but it behaves opposite and then opposite of opposite.
First code that works and need no attention:
function forwBack(x){
az+= x*(Math.cos((Math.PI/180)*ry))*Math.cos((Math.PI/180)*rx)*speed;
ax-= x*(Math.sin((Math.PI/180)*ry))*Math.cos((Math.PI/180)*rx)*speed;
ay+= x*(Math.sin((Math.PI/180)*rx))*speed;
}
Now code that doesn't do what I want... translate perpendicular to vector.
function rightLeft(x){
az+= Math.sin(ry*(Math.PI/180))*(Math.cos((rx+90)*(Math.PI/180))*speed*x);
ax-= Math.cos(ry*(Math.PI/180))*(Math.cos((rx+90)*(Math.PI/180))*speed*x);
ay+= Math.sin((rx+90)*(Math.PI/180))*speed*x;
}
x is sign which determines left/right or forward/backward. Speed is hypotenuse here. Pi/180 for deg to radians. And 180/Pi for radians to deg. (rotation around x axis) rx and ry are defined in function ch..and represent rotation of main(.ma) container. rx is defined by vertical movement of mouse.
I don't need to do this, but I want to. I want to make 3d explorer like a hall.. Just for fun. I used to do this kind of stuff in flash like 9 years ago. I was a kid then.
MAIN problem is that sideways is not always -x or +x, it depends on how much outer container has been rotated(around xyz). So if m is rotated 90 around y axis only telling c to move x+ would make c actually look like it's going back or reducing in z.
After trying two days straight I can't fix this.
http://jsfiddle.net/techsin/hGkMj/1/ -- Checking out 3d
http://jsfiddle.net/techsin/hGkMj/6/ -- Checking out 3d
http://jsfiddle.net/techsin/9YjSC/3 I made this to get clear idea of what I was trying to do. Move mouse, & Use arrow keys. Need to click in the preview window.
I found the problem. In your function rightLeft, you are inverting x and y.
I rewrote the function and it is working fine:
function rightLeft(x){
var dAy = 0 ;
var dAz = Math.cos((ry+90)*(Math.PI/180))*speed*x;
var dAx = Math.sin((rx+90)*(Math.PI/180))*speed*x;
console.log("dAx = " + dAx + " dAz = " + dAz + " dAy = " + dAy);
ax-= dAx;
az+= dAz;
ay+= dAy;
}
Hope this helps!

Zoom on mouse pointer in three.js

Using thingiview.js, Three.js and the trackballControls, I've put together a system in which I can upload an STL file, and then render it on the canvas. trackballControls are pretty great with some adjustment, but I'm having an issue:
I would like to zoom in at the point of the mouse cursor as opposed to the center of the grid/plane.
I've done a simple captureEvent to get the on screen coordinates of the mouse and track it, but I'm having issue on figuring out where to tap into the control scheme to do it.
I checked out the _zoomStart / _zoomEnd stuff (which confuses me a little as it goes off of "y", I assumed it would be "z"). But when trying to add a _zoomStart.x, it basically ignores it.
Now I may not be a guru, but I'm comfortable banging around usually.
I'd also like to make sure that when I pan, the zoom and rotate still bases off the center of the object, as opposed to the center of the grid/plane.
Have been searching for days through posts and examples, but not really finding any answers.
I'm sure I'm not looking in the right place/heading in the right direction. A helpful nudge (or better yet a swift kick) in the right direction would be truly appreciated.
EDIT
this.zoomCamera = function () {
var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
if ( factor !== 1.0 && factor > 0.0 ) {
_eye.multiplyScalar( factor );
if ( _this.staticMoving ) {
_zoomStart.copy( _zoomEnd );
} else {
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
}
}
};
I assume the above is where I would go in for the zoom alter. What I don't understand is it being set to _zoomStart.y. But taking it as it is, how would I implement x?
I mean, if _zoomStart and _zoomEnd are a Vector2, where in the above code does it define x?
confuzzled
Zooming in Trackballcontrols is not actually zooming (that would be setting the camera fov). Two objects are just getting moved around in the controls..., the other would be the camera (this.object), the other the point it's looking at (this.target). I have not played much with trackballcontrols, but I would hazard a guess it won't touch the target at all (so all movement and zooming will revolve around that).
You could try changing the target at onclick, something like:
mycontrols.target = new THREE.Vector3(newx, newy, newz);
You might need to update/reset some other Trackballcontrols internal variables, but it might also work just like that.
For getting the 3D x/y/z coordinates from the 2D x/y mouse coordinates, I suggest searching around for ray casting or object picking, should find plenty of examples.
The trick is that _zoomStart and _zoomEnd is created for touch zooming, and when you zoom using the mouse wheel you have to pass only one variable expressing: "how much to zoom". The programmer didn't create a new variable for it but used the _zoom###.y component.
So _zoomStart and _zoomEnd doesn't provide information about how the zooming will be executed, these variables contain the "instruction" only. Then the software converts it to the "zoompan" vector expressing the required movement of the camera in 3D.

The closer the x/y the more x/y velocity

I am writing a fairly simple script in JavaScript using the canvas. It draws a central node which pulls all of the surrounding nodes towards it. This works great, however I need each node to repel each other.
I am going to do this by increasing each nodes velocity away from each other so eventually they should level out and end up looking something like a flower. It needs to be enough force to stop them from hitting each other or sinking into the center node without flying off into the distance.
I just can not work out how I can have a higher number the closer they get.
So if two nodes where 10px away from each other it would add 5 in force to one of their x velocities. But if they where 1000px away from each other then it would add almost nothing to the force of one of the nodes.
Does anyone know of a mathematical equation I can use to work this kind of thing out, or maybe a nudge in the right direction?
TL;DR: Depending on how close two x values are, I need to increment the x velocity of one node so they move apart but eventually level out. It is just the maths I can not crack, I have pretty much all of the JavaScript done, including the implementation of velocity.
Thanks, and sorry it is a bit wordy.
You just need an inverse (or inverse square) relationship:
var increment = k / distance;
or:
var increment = k / (distance * distance);
You can determine k based on the actual values you want, for example, in the first case, if you wanted an increment of 5 for a distance of 10, you would set k = increment * distance = 50.
Look into the equations governing electrical point charges, have the velocity be based on the "force" each "charge" would feel based on its proximity.

Categories