I have a mesh which is a child of an Object3D, which is a child of the scene.
The Object3D is rotated, let's say 90 degrees along X axis.
The child has no rotation applied,and keeps Y -up and Z forward orientation in its local space.
Being a noob to THREE.js API, my question is: What is the most efficient way (in terms of caclulation) to keep the child's rotation aligned with the world's Y?
That is, if its parent has been rotated 90 degs along X axis,then the local rotation of its child should be updated so that its up vector still points towards global Y axis?
Can I get world's quaternion of the object to get the difference between local and global rotation and then adjust locally?
Well,the easiest trick I found is to inverse parent's quaternion and apply it to the child,so that its local rotation stays the same in the world space:
var inv = mesh.parent.quaternion.clone();
inv.conjugate();
mesh.quaternion.copy(inv);
You want your child mesh to remain right-side-up and aligned with the coordinate axes -- even if the mesh's parent is rotated.
There are several ways to do that. What is best, depends on your use case, or personal preference.
Your proposed solution can be implemented without cloning like so:
mesh.quaternion.copy( mesh.parent.quaternion ).conjugate();
Another approach is to define an onBeforeRender() method that modifies the world matrix of the mesh:
var func = function() {
var v = new THREE.Vector3();
return function func() {
v.setFromMatrixPosition( this.matrixWorld );
this.matrixWorld.makeTranslation( v.x, v.y, v.z );
// or you could do this, instead
//this.quaternion.copy( this.parent.quaternion ).conjugate();
//this.updateMatrixWorld();
};
}();
mesh.onBeforeRender = func;
three.js r.94
Related
I'm creating an app where a person (right now I'm using a cone-shape) is standing on some surface (right now I'm using a cylinder laid lengthwise) and I'd like their feet to orient toward some point (right now it's the center of the cylinder).
(edit: I just realized that my Z axis in this photo is pointing in the wrong direction; it should be pointing towards the camera, but the question remains unchanged.)
Here is a version of the code similar to what I'm trying to accomplish. https://codepen.io/liamcorbett/pen/YMWayJ (Use arrow keys to move the cone)
//...
person = CreatePerson();
person.mesh.up = new THREE.Vector3(0, 0, 1);
//
// ...
//
function updateObj(obj, aboutObj=false){
let mesh = obj.mesh;
if (aboutObj) {
mesh.lookAt(
aboutObj.mesh.position.x,
aboutObj.mesh.position.y,
mesh.position.z)
};
}
//
// ...
//
function animate() {
// ...
updateObj(person);
// ...
}
The code above gives me something similar to what I'm looking for, but the issue is that lookAt() seems to always point the local Positive Z-axis in some direction, and I'd much prefer that it point the local Negative Y-axis instead.
I'd prefer to not change the x,y,z axes of the model itself, as I feel that's going to be a pain to deal with when I'm applying other logic to the person object.
Is there a way to change which axis lookAt() uses? Or am I going to have to roll my own lookAt() function? Thanks ~
Is there a way to change which axis lookAt() uses?
No, the default local forward vector for 3D objects (excluding cameras) is (0, 0, 1). Unlike other engines, three.js does not allow to configure the forward vector, only the up vector. But this is not really helpful in your case.
You can try to transform the geometry in order to achieve a similar effect.
If you don't want to do this for some reasons and you still want to use Object3D.lookAt(), you have to compute a different target vector (so not the cylinder's center).
Even if the forward vector of the lookAt method can't be changed (as #Mugen87 said), you can still adjust the local rotation afterwards by knowing in advance the difference between the forward Z axis used, and the axis you consider your mesh to be "upward" (ex: a person standing up on the Y axis).
Basically, in your case, just add this line after the lookAt method :
mesh.rotateOnAxis( new THREE.Vector3(1,0,0), Math.PI * -0.5 );
And the cone will look up :)
Trying to rotate an object around any axis.
For example like a door hinge (on edge of object) or planet around the sun (outside of object).
The problem seems to be defining the axis. The below unit vector results in axis remaining on object's origin (centre) therefor identical to standard rotation:
object2.rotateOnAxis(new THREE.Vector3(1,0,0), 0.01);
// same as
object1.rotation.x += 0.01;
See code example: JSFiddle
EDIT: Looking for a way that one can rotate around a pivot without using nested children. Rotating a child's parent provides an easy way to manipulate the child's pivot point, but modifying the pivot point is not viable.
Example below, if you wanted to rotate the cube in a figure 8 motion, it would be achievable with this method by changing the parent. But one would have to assure that the new parent's position and orientation is precisely configured to make the child seamlessly jump between parents, and complex motions that do not repeat or loop would be very complicated. Instead, I would like to (and I will paraphrase the question title) rotate an object on a specific axis without using object nesting anywhere in the scene, including outside of the object's mesh.
See code example: JSFiddle with pivots
If you want to rotate an object around an arbitrary line in world space, you can use the following method. The line is specified by a 3D point and a direction vector (axis).
THREE.Object3D.prototype.rotateAroundWorldAxis = function() {
// rotate object around axis in world space (the axis passes through point)
// axis is assumed to be normalized
// assumes object does not have a rotated parent
var q = new THREE.Quaternion();
return function rotateAroundWorldAxis( point, axis, angle ) {
q.setFromAxisAngle( axis, angle );
this.applyQuaternion( q );
this.position.sub( point );
this.position.applyQuaternion( q );
this.position.add( point );
return this;
}
}();
three.js r.85
I found several different supposedly working ways to rotate an object around the world axis but all of them kept rotating the object around it's own axis instead of rotating it around the world's. So, what's the proper way to rotate an object around the world's axis?
If I wasn't clear about the problem here's a drawing that represents my problem
Edit: Sorry I should have specificated in the question as well that I'm using Three.js
Edit2:
//camera is the object I want to rotate
//I also found a method that uses Euler but the result was far from the expected
/**Method 1**/
//Found this somewhere here in so
var rotateAroundWorldAxis = function(object, axis, radians) {
var rotWorldMatrix;
rotWorldMatrix = new THREE.Matrix4();
rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
rotWorldMatrix.multiply(object.matrix);
object.matrix = rotWorldMatrix;
object.rotation.setFromRotationMatrix(object.matrix);
}
//this is how I call the method
rotateAroundWorldAxis(camera, new THREE.Vector3(0,1,0), movementX*-0.001);
rotateAroundWorldAxis(camera, new THREE.Vector3(1,0,0), movementY*-0.001);
/**Method 1**/
/**Method 2**/
//I've also tried this method
var q = new THREE.Quaternion();
q.setFromAxisAngle( new THREE.Vector3(0,1,0), movementX*-0.001 ); // axis must be normalized, angle in radians
camera.quaternion.multiplyQuaternions( q, camera.quaternion );
/**Method 2**/
//movementX and movementY are values provided by the mouse movement using the pointerlock.
//These values works as I've tried them using rotateOnAxis
Use quaternions.
var rotateAroundWorldAxis = function(object, axis, radians) {
var rotation = new THREE.Quaternion();
rotation.setFromAxisAngle ( axis, radians );
object.quaternion.multiply(rotation);
}
rotateAroundWorldAxis(myObject, THREE.Vector3(0,1,0), movementX*-0.001);
I believe your problem before with the quaternion method is multiplying the quaternions in the wrong order so you rotate around global the Y axis, then perform the local rotation, which ultimately rotates in the local Y axis.
I ended up using the FirstPersonControls class. I had to make a few changes to the class though
In Three.JS, I am capable of rotating an object about its origin. If I were to do this with a line, for instance, the line rotates, but the positions of its vertices are not updated with their new locations. Is there some way to apply the rotation matrix to the position of the vertices to find the new position of the point? Say I rotate a line with points at (0,0,0) and (0,100,100) by 45° on the x, 20° on the y, and 100° on the z. How would I go about finding the actual position of the vertices with respect to the entire scene.
Thanks
yes, 'entire scene' means world position.
THREE.Vector3() has a applyMatrix4() method,
you can do the same things that the shader does so in order to project a vertex into world space you would do this
yourPoint.applyMatrix4(yourObject.matrixWorld);
to project that into camera space you can apply this next
yourPoint.applyMatrix4(camera.matrixWorld);
to get an actual screen position in -1 to 1
yourPoint.applyMatrix4(camera.projectionMatrix);
you would access your point like this
var yourPoint = yourObject.geometry.vertices[0]; //first vertex
also, rather than doing this three times, you can just combine the matrices. Didnt test this, but something along the lines of this. Might go the other way:
var neededPVMmatrix = new THREE.Matrix4().multiplyMatrices(yourObject.matrixWorld, camera.matrixWorld);
neededPVMmatrix.multiplyMatrices(neededPVMmatrix, camera.projectionMatrix);
if you need a good tutorial on what this does under the hood i recommend this
Alteredq posted everything there is to know about three.js matrices here
edit
One thing to note though, if you want just the rotation, not the translation, you need to use the upper 3x3 portion which is the rotation matrix, of the models world matrix. This might be slightly more complicated. I forgot what three.js gives you, but i think the normalMatrix would do the trick, or perhaps you can convert your THREE.Vector3() to THREE.Vector4(), and set .w to 0, this will prevent any translation from being applied.
edit2
if you want to move the line point in your example, instead of applying it to the particle, apply it to
var yourVertexWorldPosition = new THREE.Vector3().clone(geo.vertices[1]); //this is your second line point, to whatever you set it in your init function
yourVertexWorldPosition.applyMatrix4();//this transforms the new vector into world space based on the matrix you provide (line.matrixWorld)
I wanted to rotate a sphere around the world x and y axes. I successfully accomplished that with this code:
// Update ball rotation.
var tempMat = new THREE.Matrix4();
tempMat.makeRotationAxis(new THREE.Vector3(0,1,0), stepX/ballRadius);
tempMat.multiplySelf(ballMesh.matrix);
ballMesh.matrix = tempMat;
tempMat = new THREE.Matrix4();
tempMat.makeRotationAxis(new THREE.Vector3(1,0,0), -stepY/ballRadius);
tempMat.multiplySelf(ballMesh.matrix);
ballMesh.matrix = tempMat;
ballMesh.rotation.getRotationFromMatrix(ballMesh.matrix);
When I scale the ballMesh in any way away from (1,1,1), however, the rotations go awry in a way that is difficult to describe. I've put up a jsfiddle example here:
http://jsfiddle.net/pxTTv/26/ (use the arrow keys to rotate)
If you change the scale (indicated in the jsfiddle code) back to (1,1,1), it works as I expect it to.
What is causing this, and how can I fix it?
You left the scale argument out of the call to vector.getRotationFromMatrix( matrix, scale ).
Also, it's best not to mess with the object matrix -- unless you really know what you are doing. Instead just set the object's rotation, position, and scale, and let the library update the matrix.
In your case you were overwriting the object's matrix, but by luck, it was being recomputed in the render call.
Here is a corrected fiddle: http://jsfiddle.net/pxTTv/27/