I'm trying to move a cube in three.js based on its rotation but not sure on how to go about it.
As of now I can rotate the cube's z-rotation with the A & D keys. And with the W key I would like it to move forward relative to its rotation.
From 2D I would so something along the lines of:
float angle = GradToRad(obj.rotation);
obj.x = obj.x + cos(angle) * velocity;
obj.y = obj.y + sin(angle) * velocity;
Here's an image of the current implementation.
How can I apply something similar in three.js?
Objects can be considered to be facing their positive-Z axis. So to move an object forward, relative to it's own coordinate system, you can use
Object3D.translateZ( distance );
three.js r.57
It might be easiest to express both rotation and translation in a single (homogenous projective) 4×4 matrix. The Object3D.matrix member in three.js already does that, although you might have to set matrixAutoUpdate to false to use that directly. Then you can move use the translate method to move the object in its own reference frame.
Your 2D method is exactly how I did it in three.js. For the Y position I'm using a terrain collision technique (which still needs work);
Related
I am trying to model a Rubik's Cube for a personal project, using Zdog for lightweight 3d graphics. Zdog uses a {x,y,z} vector to represent rotation - I believe this is essentially a Tait-Bryan angle.
To animate a rotation of the top, right, front, etc side, I attach the 9 blocks to an anchor in the center of the cube and rotate it 90 degrees in the desired direction. This works great, but when the animation is done I need to "save" the translation and rotation on the 9 blocks. Translation is relatively simple, but I'm stuck on rotation. I basically need a function like this:
function updateRotation(xyz, axis, angle) {
// xyz is a {x,y,z} vector
// axis is "x", "y", or "z"
// rotation is the angle of rotation in radians
}
that would apply the axis/angle rotation in world coordinates to the xyz vector in object coordinates. Originally I just had xyz[axis] += angle, but this only works when no other axis has any rotation. I then thought I could use a lookup table, and I think that's possible as I only use quarter turns, but constructing the table turns out to be harder than I thought.
I am starting to suspect I need to translate the xyz vector to some other representation (matrix? quaternion?) and apply the rotation there, but I'm not sure where to start. The frustrating thing is that my blocks are in the right position at the end of the animation - I'm just not sure how to apply the parent transform so that I can detach them from the parent without losing the rotation.
As far as I can tell, this can't be done with Euler angles alone (at least not in any easy way). My solution was to convert to quaternions, rotate, then convert back to Euler:
function updateRotation(obj, axis, rotation) {
const {x, y, z} = obj.rotate;
const q = new Quaternion()
.rotateZ(z)
.rotateY(y)
.rotateX(x);
const q2 = new Quaternion();
if (axis === 'x') {
q2.rotateX(rotation);
} else if (axis === 'y') {
q2.rotateY(rotation);
} else if (axis === 'z') {
q2.rotateZ(rotation);
}
q.multiply(q2, null);
const e = new Euler().fromQuaternion(q);
obj.rotate.x = e.x;
obj.rotate.y = e.y;
obj.rotate.z = e.z;
obj.normalizeRotate();
}
This uses the Euler and Quaternion classes from math.gl.
(It turned out Zdog actually uses ZYX Euler angles as far as I could tell, hence the order of rotations when creating the first quaternion.)
I have a mesh which is a circle geometry. I would like to animate it like in this example from two.js, a 2D library:
https://two.js.org/examples/physics.html
For now I look at this example and put the camera on the top of the shape but I'm sure there's a more simple way for my needs: https://threejs.org/examples/#webgl_gpgpu_water
Does anyone know how I can do that?
You simply need to shift the vertices positions according some sin() or cos() value according X and Y coordinates and a incremental phase (time) to animate.
your vertex shader could include something like this, where phase is incrementing with time (typically, a clock).
glPosition.x = vertex.x + sin((phase*frequency) + vertex.y) * amplitude;
glPosition.y = vertex.y + sin((phase*frequency) + vertex.x) * amplitude;
The basic concept is here, but you have to adapt the components yourself by testing the result. You probably should adjust frequency, amplitude, adding some more factors to add asymetry and randomness.
I've figured out from SO post How to get the canvas-relative position of an object that is in a group? how to get the co-ordinates. And I managed to do some experimentation to get the co-ordinates when the group is scaled.
x = object.left * object.group.scaleX + object.group.left +(object.group.width/2) * object.group.scaleX;
y = object.top * object.group.scaleY + object.group.top + (object.group.height/2) * object.group.scaleY;
How to get x & y when the group is also rotated. I've come across the fabric.util.rotatePoint, but don't really know how to use it. I wish there was a function which would return the co-ordinates of a object relative to canvas taking account of where the originX, originY, scaling, rotation etc. Please do help. Thanks for reading.
I solved the problem by keeping track of scaling and using the transformation matrix.
var matrix = object.calcTransformMatrix();
x = matrix[4] // translation in X
y = matrix[5] // translation in Y
The 5th and 6th values of the matrix is translation by definition but it also takes into account of the rotation, scaling for objects within the group. My intention to find the location of the object was to draw a line between two objects. I wanted to do it after the user resizes the canvas too.
I'm using a large array of objects built around a center point in a scene, and need to manipulate them all around their local axis. They are all facing the origin using a blank object and lookAt(), then I used this method to align the other axes correctly. Getting the initial rotation this way worked great, unfortunately when I try to rotate these objects on the fly with object.rotation.x = <amount>, it does not respect the local axis of the object.
The confusing part is, it's not even using the global axis, the axis it's using almost seems entirely arbitrary. I set up a JSFiddle to demonstrate this here. As you can see on line 129, looker.rotation.z works correctly, it rotates along the Z axis properly, but if it's changed to X or Y, it doesn't rotate along local or global axes. If anyone could demystify what is happening to cause this, that would be great.
What is happening is that you want to add some rotation to the current orientation, and setting the variable looker.rotation.z means other thing.
At the end, to calculate the rotation matrix of the looker, there will be something like (pseudocode: the functions are not these, but you get the idea):
this.matrix.multiply( makeXRotationMatrix(this.rotation.x) )
this.matrix.multiply( makeYRotationMatrix(this.rotation.y) )
this.matrix.multiply( makeZRotationMatrix(this.rotation.z) )
DrawGeometry(this.geom, this.matrix)
and composition of rotations are not intuitive. This is why it doesn't seem to follow any axis system.
If you want to apply a rotation in some axis to the existing matrix, it can be made with the functions rotateX (angle), rotateY (angle), rotateZ (angle), and rotateOnAxis (axis, angle). axis can be a THREE.Vector3.
Changing directly looker.rotation.z works because it is the nearest rotation to the geometry, and it will not be affected by the other rotations (remember that transformation matrices apply in inverse order, e.g. T*R*G is Rotating the Geometry, and then, Translating it).
Summary
In this case I suggest not to use the line:
looker.rotation.z += 0.05;
Use
looker.rotateZ (0.05);
or
looker.rotateX (0.05);
instead. Hope this helps :)
i'm using Three.js (without shaders, only with existing objects methods) in order to realize animations, but my question is very simple : i'm sure it's possible, but can you tell me (or help me) how should i combine several animations on a shape ? For example, rotating and translating a sphere.
When i'm doing :
three.sphere.rotation.y += 0.1;
three.sphere.translateZ += 1;
the sphere rotates but the translation vector is also rotating, so the translation has no effect.
I know a bit openGL and i already have used glPushMatrix and glPopMatrix functions, so do them exist in this framework ?
Cheers
Each three.js object3D has a position, rotation and scale; the rotation (always relative to its origin or "center") defines its own local axis coordinates (say, what the object sees as its own "front,up, right" directions) and when you call translateZ, the object is moved according to those local directions (not along the world -or parent- Z axis). If you want the later, do three.sphere.position.z += 1 instead.
The order of transformation is important. You get a different result if you translate first and then rotate than if you rotate first and then translate. Of course with a sphere it will be hard to see the rotation.