Flip 3D object around point/line - matrix

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.

Related

Update position of mesh after rotation of THREE.Group()

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()

Inverse of Object3D.updateMatrix() in Three.js?

If I got it right: an Object3D has on one side the set of properties (position, scale, rotation) [*] and on the other side the matrix - this later contains esentially the same information, it's computed from the others in Object3D.updateMatrix() and is the one really used internally by the renderer computations. Instead, the set (position, scale, rotation) is the one that is usually manipulated by the programmer (translations, rotations...).
Sometimes, however, one manipulates directly the matrix, and, further, instead of setting matrixAutoUpdate=false and forgetting about (position, scale, rotation), one wants to keep the properties in sync, i.e. do what Object3D.updateMatrix() does but in the other direction. Is there some implemented/efficient/recommended procedure to accomplish this?
[*] Caveats:
1) if useQuaternion=true, the quaternion is used instead of rotation
2) rotation is complemented by eulerOrder
3) I'm not sure what role play up in this picture, I guess it's not important here.
The routine
Matrix4.decompose( position, quaternion, scale ) // revision 57dev
will compute the position, quaternion, and scale vectors from a matrix.
(In r.56 the arg names were unfortunately misnamed, and this has been fixed in r.57dev.)
If you want the rotation vector instead, you will have to compute it from the just-computed quaternion like so:
rotation.setEulerFromQuaternion( quaternion, eulerOrder );
The up vector is not relevant to your question.
You can call the applyMatrix(matrix) method on a 3D object. To see what it does, see here.
Either:
1) you can feed it an identity matrix, or
2) you could copy what the function does:
this.scale.getScaleFromMatrix( this.matrix );
var mat = new THREE.Matrix4().extractRotation( this.matrix );
this.rotation.setEulerFromRotationMatrix( mat, this.eulerOrder );
this.position.getPositionFromMatrix( this.matrix );
I'm not sure how quaternions are relevant if you do the above, I don't know the circumstances under which the useQuaternion flag is set to true. But in Object3D objects by default it's false. I have a feeling it uses quaternions only when calculating certain rotations so to avoid gimbal lock.
I think the following code taken from the lookAt() method would update the quaternion:
this.quaternion.copy( this.matrix.decompose()[ 1 ] );

glTranslatef() is moving both, my origin and my sprite

Here is the deal, I'm programming a 2D framework/game engine with opengl ES. I am using VBOs and an ortho projection to draw an arrangement of sprites throughout the screen (as part of the testing), and everything was going nice and smooth until I had to play with translations and rotations. The specific problem I am having is that when I apply a translation with glTranslatef() prior to the rotation, the function does not only move the sprite, but also my origin, messing up my whole transformation. I am 100% sure it is working this way, because I used glTranslatef() to move to the right and bottom the sprite half of the size of the screen (yes, my origin is in the top left) and then apply a constant rotation and the thing just keeps mooving in a circular path around the center of the screen (actually rotating, but not as I expect.
If you want some code, here we go:
gl.glTranslatef(-(x+width/2), -(y+height/2), -layer);
gl.glRotatef(angle, 0.0f, 0.0f, -1.0f);
gl.glTranslatef(x+width/2, y+height/2, layer);
In this fragment of code, x and y are the position of the sprite, height and width are the size of the sprite, angle the angle of rotation, and layer just a form of organizing the sprites into several layers, pretty straight forward, right?
Again, my problem is that glTranslatef(); is moving both, the sprite and the origin, am I doing something wrong or misunderstanding something about the translation?
Thanks in advance.
you might need to use glPushMatrix and glPopMatrix since anything you do after those translations and rotations will be affected by them
but what you are describing is actually how it works, if you use a translate, that sort of becomes your new origin because once you do a translate, everything after that is affected by that translate, thats why you need to push and pop, so that you can go, push -> translate object and/or rotate -> pop, and then you can go about with whatever other translations you need to do without having that previous translation affecting everything else
its a bit confusing at first but google around and you'll see how to use them properly
http://www.khronos.org/opengles/sdk/1.1/docs/man/glPushMatrix.xml
I think you misunderstood how matrices work in openGL. When you do a matrix operation such as glRotatef and glTranslatef the matrices are being multiplied, resulting in affecting the base vectors.. For instance, let's say we are only drawing a point that starts at (0,0,0). If you call translate(1,0,0) the point will be in (1,0,0), after that you call rotate(90, 0, 0, 1) and your point will be on the same place as before but rotated. Now the last call is translate(-1,0,0) and your point is at (1,-1,0) (and not where you started)!
And that is what you did in your "fragment of code". The thing is you did not specify what you really want to do and how do you define your verices is relative as well.. If you want something like a view with some image that you want to control in sense of changing the position and rotation, you might want to create a square vertex buffer with values from -1 to 1 in both dimensions (or (-width/2, -height/2) to (width/2, height/2)). In this case the base center of your object is in (0,0,0) and that is probably the point you want to rotate it around (or am I wrong here?). So when you want to define the position of the object with origin point, you will need to write translatef(x+width/2,y+height/2,..).
As for the whole process of drawing in this case: If you want the origin to be at (x,y,z), with a (width, height) and rotated by (angle) here is the sequence
glTranslatef(x,y,z)
glTranslatef(width/2,height/2,0)
glScalef(width/2,height/2, 1) //only if verices defined at (-1,1)
glRotatef(angle, 0, 0, 1)
Do note in this case that since you rotate the object around its center its origin will not be at (x,y,z) anymore.
In general I would suggest to stay away from glRotate, glTranslate and glScale if possible. They tend to make things very nasty. So another way is to construct a matrix directly from base vectors: With little math you can compute all 4 points of your "square view" based on parameters such as origin, width, height and rotation.. The 4 points being (A-origin), (B-lower left point), (C-lower right point), (D-upper right point) your base vectors are (B-A), (D-A) and normalized(dotProduct((B-A), (D-A))) this 3 vectors can be inserted int top left 3x3 matrix of the GL matrix (witch is 4x4 or float[16]) and they represent both, rotation and scale so all you need to add is the translation part (just google around a bit for this approach).

OpenGL custom navigation/selection

I have a situation that I'm not realy sure how I can handle. I have an openGl object of about 20k vertices, and I need to offer the user the possibility to select any one of these vertices (let's say with a smallest margin of error possible). Now here is what I want to do in order to do this:
Next to the 3D canvas of the object, I also offer the user 3 'slices' done by the planes x=0; y=0 and z=0. Say for the simplest example for a sphere these would be 3 circles, correponding to 'cutting' out one of the dimensions. Now let's take the z=0 one for the purpose of the example. When the user clicks on a point say (x_circle, y_circle) i would like to get the actual point in the 3d representation where he clicked. The z would be 0 of course but I can't figure out a way to get the x and y. I can easily translate that (x_circle, y_circle) -> (x_screen, y_screen) which would have the same result as a click on the canvas at those coordinates, but I need to find a way to translate that into the (x, y, 0) coordinate in 3D view.
The same thing would need to be done with x=0, y=0 but I think if I can understand/implement a way for z=0 I can just apply more or less the same solution with an added rotation over something. If anyone can help with any examples/code or even math behind this it would help a lot because at the moment I'm not really sure how to proceed.
When the user clicks, you can render the vertices using GL.POINTS (with a certain size, if you like) to an off-screen buffer using a shader that renders each vertex' index into RGBA. Then you read back the pixel on the mouse position and see what index it is.

Java3D: rotating object in world coordinates?

I've been searching all evening but can't find the information I'm looking for, or even if it's possible, which is quite distressing ;)
I'm using Java3D and can't figure out how to rotate the camera in world space.
My left/right, and up/down rotation both happen on local space.
Meaning that if I move left and right, everything looks fine.
However if I look 90 degrees down, then look 90 degrees right, everything appears to be on its side.
Currently, I'm doing the following. This will result in the above effects:
TransformGroup cam = universe.getViewingPlatform().getViewPlatformTransform();
Transform3D trfcam = new Transform3D();
cam.getTransform(trfcam);
trfcam.mul(Camera.GetT3D()); //Gets a Transform3D containing how far to rotate left/right and how far to move left/right/forward/back
trfcam.mul(Camera.GetRot()); //Gets a t3d containing how far to rotate up/down
cam.setTransform(trfcam);
Alternatively, one thing I tried was rotating the root, but that rotates around 0, so if I ever move the camera away from 0, it goes bad.
Is there something available on the web that would talk me through how to achieve this kind of thing?
I've tried a lot of different things but just can't seem to get my head around it at all.
I'm familiar with the concept, as I've achieved it in Ogre3D, just not familiar with the law of the land in J3D.
Thanks in advance for replies :)
Store the ammound you have rotated around each axis (x and y), and when you try to rotate around the x axis for example, reverse the the rotation around y, do the rotate around x, then redo the rotation around y.
I'm not sure I understand your second question correctly. Since viewer and model transformations are dual, you can simulate camera moves by transforming the world itself. If you dont want to translate the x and y axis you are rotating around, just add another TransformGroup to the main TransformGroup you are using, and do the transforms in the new one.
Edit: The first solution is quite slow, so you can make a Transform3D out of the 3 transform you have to do:
Say you have rotated around the x axis (Translate3D xrot), and now you need to rotate around y:
Translate3D yrot = new Translate3D();
yrot.rotY(angle);
Translate3D temp = xot;
xrot.mul(yrot); // Dont forget the reverse order. xrot is the previous translate
xrot.mul(yrot); // xrot = xrot * yrot * xrot^-1
temp.transpose(); // Get the reverse transform of the old transform
xrot.mul(temp);
yrot = xrot; // Store it for future rotation around x axis
cam.setTransform(yrot);
It works similar for many transformations you make: reverse the previous done, do the transform, redo the old one. I hope it helps.

Resources