Update position of mesh after rotation of THREE.Group() - three.js

I use a pivot group to rotate my planegeometries instead of each one individually. After rotation of the pivot group-object, I want to find the new positions for each child/planegeometry-mesh to correspond to the positions that is relative to the actual world position.
How do I go about doing this?

The easy way
Like Craig mentioned, getWorldPosition is a function on Object3D (the base class of pretty much everything in the scene), which returns a new Vector3 of the object's world position.
var childPlaneWorldPosition = childPlane.getWorldPosition();
The harder way:
There are two methods for converting between local and world positions: localToWorld and worldToLocal.
These are also functions on Object3D, and take a Vector3. The vector is then (destructively) converted to the desired coordinate system. Just know that it's not smart enough to know if the vector you're giving it is already in the right coordinate system--you'll need to keep track of that.
So, to convert a child plane's position from local to world coordinates, you would do this:
// clone because localToWorld changes the vector passed to it
var childPlanePosition = childPlane.position.clone();
childPlane.parent.localToWorld(childPlanePosition);
Notice that localToWorld is called on childPlane's parent. This is because the childPlane is local to its parent, and therefore its position is local to its parent's coordinate system.
The hard(er to understand) way:
Each childPlane stores not only its local transformation matrix (childPlane.matrix), but also its world transformation matrix (childPlane.matrixWorld). You can, of course, get the world position directly from the matrixWorld property in one step.
var childWorldPosition = new THREE.Vector3(
childPlane.matrixWorld.elements[12],
childPlane.matrixWorld.elements[13],
childPlane.matrixWorld.elements[14]
);
Edit to answer some questions
"If I understand correctly, can I find the "real" position of the meshes in the pivot-group children-array?"
Yes. If you called:
pivotGroup.add(childPlane);
Then that childPlane will be listed in the pivotGroup.children array, which you could use to iterate over all of the childPlane objects
"And clone these to the position object for each meshes?"
If you want the planes to be in world coordinates (in the scene), but you used the above code to add them to the group, then they are no longer direct children of the scene. You would need to re-add them to the scene:
scene.add(childPlane);
And then apply their calculated world positions. That said, why not just leave them in the group?
(You didn't ask this one) "How would you leave the planes as direct children of the scene, but rotate them as a group?"
Well, you wouldn't. But three.js does this group rotation by multiplying matrices to come up with finalized world matrices for each plane. So you could do the same thing manually, by creating a rotation matrix, and applying it to all of your planes.
var rotMat = new THREE.Matrix4().makeRotationMatrix(x, y, z);
for(var i = 0; i < planesArray.length; ++i){ // I guess this would loop over your 3D array
planesArray.applyMatrix(rotMat);
}

Use plane_mesh.getWorldPosition()

Related

Flip 3D object around point/line

I'm making a 3D monster maker. I recently added a feature to flip parts along the x and y axes, this works perfectly fine on its own, however, I also have a feature that allows users to combine parts (sets flags, doesn't combine mesh), this means that simply flipping the individual objects won't flip the "shape" of the combined object. I have had two ideas of how to do this which didn't work and I'll list them below. I have access to the origin of the objects and the centre of mass of all instances that are combined - the 0, 0, 0 point on a theoretical number plane
In these examples we're flipping across the y axis, the axis plane is X = width, Y = height, Z = depth
Attempt #1 - Simply flipping the individual object's X scale, getting the X distance from the centreMass and taking that from the centreMass for position, this works when the direction of the object is (0, 0, 1) and the right (1, 0, 0) or (-1, 0, 0), in any other direction X isn't the exact "left/right" of the object. Here's a video to clarify: https://youtu.be/QXdEF4ScP10
code:
modelInstance[i].scale.x *= -1;
modelInstance[i].basePosition.set(centre.x - modelInstance[i].distFromCentre.x, modelInstance[I].basePosition.y, modelInstance[I].basePosition.z);
modelInstance[i].transform.set(modelInstance[i].basePosition, modelInstance[i].baseRotation, modelInstance[i].scale);
Attempt #2 - Rotate the objects Y180° around the centreMass and then flip their z value. As far as I understand, this is a solution, but I don't think I can do this. The way to rotate an object around a point AFAIK involves transforming the matrix to the point, rotating it, and then translating it back which I can't use. Due to the ability to rotate, join, flip, and scale objects I keep the rotation, position, and scale completely separate because issues with scaling/rotating and movement occur. I have a Vector3 for the position, a matrix for the rotation, and a Vector3 for the scale, whenever I change any of these I use object.transform.set(position, matrix.getRotation(), scale); So when I attempt to do this method (translating rotation matrix to point etc) the objects individually flip but remain in the same place, translating the objects transform matrix has weird results and doesn't work. Video of both variations: https://youtu.be/5xzTAHA1vCU
code:
modelInstance[i].scale.z *= -1;
modelInstance[i].baseRotationMatrix.translate(modelInstance[i].distFromCentre).rotate(Vector3.Y, 180).translate( modelInstance[i].distFromCentre.scl(-1));
modelInstance[i].transform.set(modelInstance[i].basePosition, modelInstance[i].baseRotation, modelInstance[i].scale);
Ok, since no one else has helped I'll give you some code that you can either use directly or use to help you alter your code so that it is done in a similar way.
First of all, I tend to just deal with matrices and pass them to shaders as projection matrices, ie. I don't really know what modelInstance[i] is, is it an actor (I never use them), or some other libgdx class? Whatever it is, if you do use this code to generate your matrices, you should be able to overwrite your modelInstance[i] matrix at the end of it. If not, maybe it'll give you pointers on how to alter your code.
First, rotate or flip your object with out any translation. Don't translate or scale first, because when you rotate you'll also rotate the translation you've performed. I use this function to generate a rotation matrix, it rotates around the y axis first, which I think is way better then other rotation orders. Alternatively you could create an identity matrix and use the libgdx rotation functions on it to create a similar matrix.
public static void setYxzRotationMatrix(double xRotation, double yRotation, double zRotation, Matrix4 matrix)
{
// yxz - y rotation performed first
float c1=(float)Math.cos(yRotation);
float c2=(float)Math.cos(xRotation);
float c3=(float)Math.cos(zRotation);
float s1=(float)Math.sin(yRotation);
float s2=(float)Math.sin(xRotation);
float s3=(float)Math.sin(zRotation);
matrix.val[0]= -c1*c3 - s1*s2*s3; matrix.val[1]=c2*s3; matrix.val[2]=c1*s2*s3-c3*s1; matrix.val[3]=0;
matrix.val[4]= -c3*s1*s2 + c1*s3; matrix.val[5]=c2*c3; matrix.val[6]=c1*c3*s2+s1*s3; matrix.val[7]=0;
matrix.val[8]= -c2*s1; matrix.val[9]=-s2; matrix.val[10]=c1*c2; matrix.val[11]=0;
matrix.val[12]=0; matrix.val[13]=0; matrix.val[14]=0; matrix.val[15]=1.0f;
}
I use the above function to rotate my object to the correct orientation, I then translate it to the correct location, then multiply it by the cameras matrix and scale as the final operation. This will definitely work if you can do it that way, but I just pass my final matrix to the shader. I'm not sure how you use your matrices. If you want to flip the model using the scale, you should try it immediately after the rotation matrix has been created. I'd recommend getting it working without flipping with scale first, so you can test both matrix.scl() and matrix.scale() as the final step. Off hand, I'm not sure which scale function you'll need.
Matrix4 matrix1;
setYxzRotationMatrix(xRotationInRadians, yRotationInRadians, zRotationInRadians,matrix1);
//matrix1 will rotate your model to the correct orientation, around the origin.
//here is where you may wish to use matrix1.scl(-1,1,1) or matrix1.scale(-1,1,1).
//get anchor position here if required - see notes later
//now translate to the correct location, I alter the matrix directly so I know exactly
what is going on. I think matrix1.trn(x, y, z) would do the same.
matrix1.val[12]=x;
matrix1.val[13]=y;
matrix1.val[14]=z;
//Combine with your camera, this may be part of your stage or scene, but I don't use
//these, so can't help.
Matrix4 matrix2;
//set matrix2 to an identity matrix, multiply it by the cameras projection matrix, then
//finally with your rotation/flip/transform matrix1 you've created.
matrix2.idt().mul(yourCamera.combined).mul(matrix1);
matrix2.scale(-1,1,1); //flipping like this will work, but may screw up any anchor
//position if you calculated one earlier.
//matrix2 is the final projection matrix for your model. ie. you just pass that matrix
to a shader and it should be used to multiply with each vertex position vector to create
the fragment positions.
Hopefully you'll be able to adapt the above to your needs. I suggest trying one operation at a time and making sure your next operation doesn't screw up what you've already done.
The above code assumes you know where you want to translate the model to, that is you know where the center is going to be. If you have an anchor point, lets say -3 units in the x direction, you need to find out where that anchor point has been moved to after the rotation and maybe flip. You can do that by multiplying a vector with matrix1, I'd suggest before any translation to the correct location.
Vector3 anchor=new vector3(-3,0,0);
anchor.mul(matrix1); //after this operation anchor is now set to the correct location
//for the new rotation and flipping of the model. This offset should
//be applied to your translation if your anchor point is not at 0,0,0
//of the model.
This can all be a bit of a pain, particularly if you don't like matrices. It doesn't help that everything is done in a different way to what you've tried so far, but this is the method I use to display all the 3D models in my game and will work if you can adapt it to your code. Hopefully it'll help someone anyway.

Creating lines that can be modified from ends

So I'd like to create some lines that can be modified from points that are connecting them.
An example of initial state
First one has been moved down, second one up and third one right and down.
On the implementation side I currently have two meshes. First one is stretched out so that it would cover the distance from its starting point to the next point and second one marks the starting point.
var meshLine = new THREE.Mesh(boxGeometry, material);
meshLine.position.set(x,y,z);
meshLine.scale(1,1,distancetonextpoint);
var meshPoint = new THREE.Mesh(sphereGeometry, material);
meshPoint.position.set(x,y,z);
meshPoint.scale(2,2,2);
What I want from it is that when the user drags the circular point other lines would stretch or change their position accordingly to the one being dragged.
Is there some more reasonable solution for this as I feel mine is not quite good and clean. I'd have to do quite heavy lifting to get the movement done.
I've also looked at this example which looks visually very nice but could not integrate it to my system.
You mean you need to edit the object's geometry by dragging their vertices (here a line).
Objects'vertices can't be dragged theirselves, so you need to loop through the geometry and create little spheres at each vertex position ;
You set a raycaster to pick those spheres, as in the examples ;
Your screen is 2D so to drag objects in 3D you need a surface perpendicular to the screen, that intersects the sphere position. For this you set an invisible plane at the vertex position and make it look at the camera ;
Once you can correctly drag the spheres, you tell the corresponding vertices on the object (your lines) they must keep the same position as their spheres ;
End with geometry.verticesNeedUpdate=true.
And you have your new geometry
For code details on picking objects look at the official picking objects'example draggable cubes
This example shows how to use it for editing objects
Comment if you need more explanations

When to updateMatrixWorld to have localToWorld and worldToLocal properly working?

I'll explain the problem I'm facing, cause probably I'm doing something wrong.
I have some models in blender, these are exported to json and used in three.js. In these models there are some planes, which then in js are replaced on the flight with another mesh to enable a cloth simulation.
The models can rotate once in the scene, and these planes being children of the models will also rotate. Moreover, the original planes from blender could have some rotation applied.
However we want the wind to be global, so for each plane and each frame, a global (world) wind direction vector is cloned and then transformed into the local coordinates of each plane, so that cloth particles can be moved correctly.
This is accomplished simply with :
globalDir = new THREE.Vector3(0,0,1); // Wind from north
// ...
var localDir = plane.worldToLocal(globalDir.clone());
// use localDir vector for moving around vertices base don wind
This "works", meaning that all clothes children of a single model are aligned to the same global wind, but :
nothing else changing, only refreshing the page, given the same values for the globalDir vector, the wind direction is always different.
from model to model, the direction is different.
It seems to be all about how the world matrix gets updated relatively to the object hierarchy, on the order the models are loaded and added, and so on.
I've been trying to add and remove calls to updateMatrix and updateMatrixWorld everywhere around, I'm asking for guidelines about how the methods updateMatrix, updateMatrixWorld, localToWorld and worldToLocal are supposed to be used.

How to Set Plane Mesh to always lookAt camera without tilting

I'm trying to make a Plane to always face the camera or another moving object but I want the Plane to only rotate on 1 axis. How can I use the lookAt function to make it only rotate side ways without tilting to look up or down at the moving object?
thanks, I managed to solve it easily by just keeping the y position of the rotating object constant.
if(planex){
var yaw_control = controls.getYawObject();
pos = new THREE.Vector3( yaw_control.position.x, planex.position.y, yaw_control.position.z );
planex.lookAt(pos);
}
http://www.lighthouse3d.com/opengl/billboarding/index.php?billCyl
maybe this article of any help for you. You are looking for those cylindrical billboards i think but read up from the first page ;) You can modify the specific mesh matrix yourself, although i am not sure if this is the most efficient way. I also did this myself once.
Get the camera look vec:
three.js set and read camera look vector
Then get the camera upVec and afterwards get the cross prodcut of those = rightVec according to the article above.
using those vectors, you can fill in a new Three.Matrix4() like explained in the article and then replace the meshes matrix with the newly created one. As I said, i am not quite into the matrix stuff in three.js but this works but it is probably not that efficient.
For this to work you will have to deactive the meshes auto matrix update with
mesh.matrixAutoUpdate = false;

Three.js Get Camera lookAt Vector

I'm looking to translate the camera along its lookAt vector. Once I have this vector, I can do scalar translation along it, use that point to move the camera position in global coordinates and re-render. The trick is getting the arbitrary lookAt vector? I've looked at several other questions and solutions but they don't seem to work for me.
You can't get the lookAtVector from the camera itself, you can however create a new vector and apply the camera rotation to that.
var lookAtVector = new THREE.Vector3(0,0, -1);
lookAtVector.applyQuaternion(camera.quaternion);
The first choice should be cam.translateZ();
There is a second option as well. You can extract the lookAt vector from the matrix property of the camera object. You just need to take the elements corresponding to the local z-axis.
var lookAtVector = new THREE.Vector3(cam.matrix[8], cam.matrix[9], cam.matrix[10]);

Resources