3
votes

I am building a modeling program and I'd like to do transformations on objects in their own space and then assign that single object to a group to rotate around another axis which the group rotates around. However, I'd also like to be able to do transformations in the object's own space when it's combined.

Manipulating the individual object, I pick the object's center.

glm::mat4 transform;
transform = glm::translate(transform, - obj.meshCenter);
glm::mat4 transform1;
transform1 = glm::translate(transform1, obj.meshCenter);
obj.rotation =  transform1*obj.thisRot*transform;

I then send this off to the shader,

glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(obj.translation*obj.rotation*objscale);

I would now like to rotate this object around another axis, say an axis of (5,0,0) of 45 degrees.

I now have:

glm::mat4 groupR;
groupR = glm::rotate(groupR,glm::degrees(45.0f),glm::vec3(5,0,0));
obj.groupRotation = groupR;
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, 
glm::value_ptr(obj.groupRotation*obj.translation*obj.rotation*objscale)

I've now moved the object from it's local space to the Group space. I'm having a bit of difficulty now operating tranformations in the object's own space when combined with the Group's rotation. I've had limited success when I set the groupR axis to (0,1,0) like so:

///Translating object in its own space///
glm::mat4 R = obj.groupRotation;
obj.translation = glm::inverse(R) * obj.translate * R;

the problem here is that this will only translate the object correctly in it's own space if the axis of rotation of R (Group's rotation) is equal to (0,1,0):

///Rotating object in its own space///
glm::mat4 R = obj.groupRotation;
obj.rotation = glm::inverse(R) * obj.rot * R;

Again, the rotations are incorrect. I'm thinking that maybe I have to undo the groupR's axis translation? and then re-apply it somewhere?

2
what I'm attempting to do is undo the groupRotation so that my local transform calculations are correct. The inverse above will return rotations and translations (haven't attempted scaling yet) back to local space. The hangup is if the groupRotation's axis is not located at the origin. I'm going to try and insert an inverse translation of the axis location into the matrix multiplications above. I'm just not sure in which order it should be or if that will even work. However I'm going to give it a good go in the morning.Tom Tom
Can't you just modify obj.thisRot directly and recalculate the combined transform? This would be much less error-prone. Btw, I wouldn't count on glm::value_ptr() to return a valid address for a temporary object that is deleted right after the call to glm::value_ptr().Nico Schertler

2 Answers

7
votes

Let's assume we have an object that is moved, rotated and scaled, and we define a transformation matrix as follows:

glm::mat4 objTrans ...; // translation 
glm::mat4 objRot ...;   // roation 
glm::mat4 objScale ...; // scaling

glm::mat4 objMat = objTrans * objRot * objScale;

And we have rotation matrix that we want to run on the object. In this case we have rotation around the Z-axis:

foat angle ...; // rotation angle

glm::mat4 rotMat = glm::rotate( angle, glm::vec3( 0.0, 0.0, 1.0 ) ); 

We have several rotations we can do with this information. First we want to rotate the object on its local axis:

enter image description here

glm::mat4 modelMat = objMat * rotMat;

A Rotation around the worlds origin can be performed like this:

enter image description here

glm::mat4 modelMat = rotMat * objMat;

In order to rotate around the origin of the object in the world coordinate system, we must eliminate the rotation of the object:

enter image description here

glm::mat4 modelMat = objMat * (glm::inverse(objRot) * rotMat * objRot);

A Rotation around the worlds origin in relation to the object you have to do the opposite:

enter image description here

glm::mat4 modelMat = (objRot * rotMat * glm::inverse(objRot)) * objMat;

If you have a complete transformations matrix for an object and you do not know the rotation part, then it can be easily determined.

Note that a transformation matrix usually looks like this:

( X-axis.x, X-axis.y, X-axis.z, 0 )
( Y-axis.x, Y-axis.y, Y-axis.z, 0 )
( Z-axis.x, Z-axis.y, Z-axis.z, 0 )
( trans.x,  trans.y,  trans.z,  1 )

To generate a rotation only matrix you have to extract the normalized axis vectors:

glm::mat4 a ...; // any matrix
glm::vec3 x = glm::normalize( a[0][0], a[0][1], a[0][2] );
glm::vec3 y = glm::normalize( a[1][0], a[1][1], a[1][2] );
glm::vec3 z = glm::normalize( a[2][0], a[2][1], a[2][2] );

glm::mat4 r;
r[0][0] = x[0]; r[0][1] = x[1]; r[0][2] = x[2]; r[0][3] = 0.0f;
r[1][0] = y[0]; r[1][1] = y[1]; r[1][2] = y[2]; r[0][3] = 0.0f;
r[2][0] = z[0]; r[2][1] = z[1]; r[2][2] = z[2]; r[0][3] = 0.0f;
r[3][0] = 0.0f; r[3][1] = 0.0f; r[3][2] = 0.0f; r[0][3] = 1.0f; 
2
votes

Here is a partial answer to the behavior I want and the setup I used. This seems to be what I need to do to get the correct transforms in object space while apart of a group rotation. Here I have a model composed of 7 different individual meshes that is rotated around the origin of (0,5,0) on the y Axis, this is just an arbitrary rotation I choose for testing.

 for (int i = 0; i < models.at(currentSelectedPointer.x)->meshes.size()i++)
    {
    glm::mat4 rotMat;
    rotMat = glm::translate(rotMat, glm::vec3(5, 0, 0));
    rotMat = glm::rotate(rotMat, f, glm::vec3(0, 1.0, 0.0));
    rotMat = glm::translate(rotMat, glm::vec3(-5, 0, 0));
    models.at(currentSelectedPointer.x)->meshes.at(i).groupRotation = rotMat;
    }

all the meshes are now rotating around (0,5,0) as a group, not at (0,5,0), on the Y axis.

to do the correct rotation transform on a single object in it's own object space, I have to undo the location of the groupRotation's origin (Sorry for the messy code, but I did it in steps like this to keep everything seperated and easily disectable). Also the individual object has an identity matrix for both it's translation and it's scale.

     //These will be used to shift the groupRotation origin back to the 
     // origin in order to rotate around the object's origin.

     glm::mat4 gotoGroupAxis;
     gotoGroupAxis= glm::translate(gotoGroupAxis, glm::vec3(5, 0, 0));
     glm::mat4 goBack ;
     goBack = glm::translate(goBack , glm::vec3(-5, 0, 0));

     ////////Group rotation and it's inverse        
     glm::mat4 tempGroupRot = goBack *obj.groupRotation*gotoGroupAxis;
     glm::mat4 tempGroupRotInverse= glm::inverse(tempGroupRot);

     //thisRot and lastRot are matrix variables I use to accumulate and 
     //save rotations       
     obj.thisRot = tempGroupRotInverse* 
     glm::toMat4(currentRotation)*tempGroupRot * 
     obj.lastRot;

     //now I translate the object's rotation origin to it's center.

     glm::mat4 transform = glm::translate(transform, -obj.meshCenter);
     glm::mat4 transform1 = glm::translate(transform1, obj.meshCenter);
     //Finally I rotate the object in it's own space.
     obj.rotation =  transform1*obj.thisRot*transform;

Update:

 //Translation works as well with
 obj.finalTranslation= tempGroupRotInverse* 
 obj.translation * tempGroupRot ;

This is only a partial answer because I'm going to be doing transforms on an object level and group level and I'm almost certain that something will go wrong down the line that hasn't been taken into account by the answer I've posted.