Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Translation after rotation

I'm using OpenGL ES 2.0 for Android. I'm translating and rotating a model using the touch screen. My translations are only in the (x, y) plane, and my rotation is only about the z-axis. Imagine looking directly down at a map on a table and moving to various coordinates on the map, and being able to rotate the map around the point you are looking at.

The problem is that after I rotate, my subsequent translations are to longer matched to the motions of the pointer on the screen, the axes are different.

Everything I've tried gives me one of two behaviors One is equivalent to:

Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, Xposition, Yposition, 0.0f);
Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);

This allows me to translate as expected (up/down on the screen moves the model up and down, left/right moves model left and right), regardless of rotation. The problem is that the rotation is about the center of the object, and I need the rotation to be about the point that I am looking at, which is different than the center of the object.

The other behavior I can get is equivalent to:

Matrix.setIdentityM(mModelMatrix, 0);
Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);
Matrix.translateM(mModelMatrix, 0, Xposition, Yposition, 0.0f);

This gives me the rotation that I want, always about the point I am looking at. The problem is that after a rotation, the translations are wrong. Left/right on the screen translates the object at a different angle, along the rotated axes.

I need some way to get both behaviors at the same time. It needs to rotate about the point I am looking at, and translate in the direction that the finger moves on the screen.

Is this even possible? Am I basically trying to reconcile quantum mechanics with Newtonian physics, and doomed to failure?

I don't want to list all of the tricks that I've tried, because I want to consider all possibilities with a fresh perspective.

EDIT: I'm still completely stuck on this.

I have an object that starts at (0, 0, 0) in world coordinates. My view is looking down the z-axis at the object, and I want to limit translation to the x/y plane. I also want to rotate the object about the z-axis only. The center of the rotation must always be the center of the screen.

I am controlling the translation with the touch screen so I need the object to the same way the finger moves, regardless of how it it rotated.

As soon as I rotate, then all of my translations start happening on the rotated coordinate system, which means the object does not move with the pointer on the screen. I've tried to do a second translation as Hugh Fisher recommended, but I can't figure out how to calculate the second translation. Is there another way?

like image 215
Jason Powell Avatar asked Nov 02 '22 03:11

Jason Powell


2 Answers

I had the same problem. However i was using C# with OpenGL (SharpGL) and using a rotation Matrix.

Translation after rotation was required to keep rotation point at center of screen. As a CAD type application does. Problem was mouse translations are not always parallel to screen after rotations.

I found a fix here.

(Xposition, Yposition) = (Xposition, Yposition) + mRotation.transposed() * (XIncr, YIncr)

or

NewTranslationPosition = oldTranslationPosition + rotationMatrix.Transposed * UserTranslationIncrement.

Many many thanks to reto.koradi (at OpenGL)!

So I roughly coded in 3D like:

double gXposition = 0;
double gYposition = 0;
double gZposition = 0;

double gXincr = 0;
double gYincr = 0;
double gZincr = 0;

float[] rotMatrix = new float[16]; //Rotational matrix

private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e)
{

  OpenGL gl = openGLControl.OpenGL;

  gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);

  gl.LoadIdentity();
  gl.MultMatrix(rotMatrix); //This is my rotation, using a rotation matrix
  gl.Translate(gXposition, gYposition, gZposition); //translate second to keep rotation at center of screen

  DrawCube(ref  gl);

}

private void buttonTransLeft_Click(object sender, EventArgs e)
{
        double tX = -0.1;
        double tY = 0;
        double tZ = 0;

        TransposeRotMatrixFindPoint(ref tX, ref tY, ref tZ);

        gXposition = gXposition + tX;
        gYposition = gYposition + tY;
        gZposition = gZposition + tZ;

 }

 private void buttonTransRight_Click(object sender, EventArgs e)
 {

        double tX = 0.1;
        double tY = 0;
        double tZ = 0;

        TransposeRotMatrixFindPoint(ref tX, ref tY, ref tZ);


        gXposition = gXposition + tX;
        gYposition = gYposition + tY;
        gZposition = gZposition + tZ;

  }

public void TransposeRotMatrixFindPoint(ref double x, ref double y, ref double z)
    {
        //Multiply [x,y,z] by Transpose Rotation matrix to generate new [x,y,z]
        double Xt = 0; //Tempoary variable
        double Yt = 0; //Tempoary variable
        Xt = (x * rotMatrix[0, 0]) + (y * rotMatrix[0, 1]) + (z * rotMatrix[0, 2]);
        Yt = (x * rotMatrix[1, 0]) + (y * rotMatrix[1, 1]) + (z * rotMatrix[1, 2]);
        z = (x * rotMatrix[2, 0]) + (y * rotMatrix[2, 1]) + (z * rotMatrix[2, 2]);

        //or try this 
        //Xt = (x * rotMatrix[0, 0]) + (y * rotMatrix[1, 0]) + (z * rotMatrix[2, 0]);
        //Yt = (x * rotMatrix[0, 1]) + (y * rotMatrix[1, 1]) + (z * rotMatrix[2, 1]);
        //z = (x * rotMatrix[0, 2]) + (y * rotMatrix[1, 2]) + (z * rotMatrix[2, 2]);


        x = Xt;
        y = Yt;
    }
like image 83
AORD Avatar answered Nov 08 '22 05:11

AORD


This is an old post, but I'm posting the solution that worked best for me for posterity.

The solution was to keep a separate model matrix that accumulates transformations as they occur, and multiply each transformation by this matrix in the onDrawFrame() method.

//Initialize the model matrix for the current transformation
Matrix.setIdentityM(mModelMatrixCurrent, 0);
//Apply the current transformations
Matrix.translateM(mModelMatrixCurrent, 0, cameraX, cameraY, cameraZ);
Matrix.rotateM(mModelMatrixCurrent, 0, mAngle, 0.0f, 0.0f, 1.0f);
//Multiply the accumulated transformations by the current transformations
Matrix.multiplyMM(mTempMatrix, 0, mModelMatrixCurrent, 0, mModelMatrixAccumulated, 0);
System.arraycopy(mTempMatrix, 0, mModelMatrixAccumulated, 0, 16);

Then the accumulated matrix is used to position the object.

like image 20
Jason Powell Avatar answered Nov 08 '22 06:11

Jason Powell