Search code examples
c++openglcoordinate-transformation

OpenGL movement object and rotate in same axis


I are trying add an object(triangle) in OpenGL, it move using glTranslatef() and rotate with glRotatef(), it is ok!

I add one matrix of the grid in background and want hold position of the object(triangle) in center and rotate only grid background, ok works!

However, when "walking" the object(triangle) using keys(up and down) it not rotate in same axis of the grid background.

it seems that it is distancing the rotate when move!

Code example:

#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <cmath>
#include <vector>
#include <GL/glew.h>
#include <GL/glut.h>


#define DEG_TO_RADIANS 0.017453292519943295769236907684886f 
#define GL_WIN_SIZE_X 800
#define GL_WIN_SIZE_Y 600
#define ANGLE_INITIAL 90.0f

using namespace std;

GLint idWin=0;

double rotate_value=ANGLE_INITIAL;
float posX = 0.0f, posY = 0.0f, angle = 0.0f;    
float velocity = 0.1f;

float zoom_value=-5.0f;
float zoom_steps=0.5f;


float x_offset=0.0f;
float y_offset=0.0f;


/* Store points way traveled */
struct vertex {
  float x, y, u, v, r, g, b;
};

std::vector<vertex> vertices;
bool start = false;
GLuint vboId;



void keyPress(int key, int xpos, int ypos)
{

  if (key == GLUT_KEY_UP) {    
    posX +=  (cos( rotate_value * DEG_TO_RADIANS )) * velocity;
    posY += -(sin( rotate_value * DEG_TO_RADIANS )) * velocity;
  }
  else if (key == GLUT_KEY_DOWN) {
    posX -=  (cos( rotate_value * DEG_TO_RADIANS ))  * velocity;
    posY -= -(sin( rotate_value * DEG_TO_RADIANS )) * velocity;
  }
  else if (key == GLUT_KEY_RIGHT) {
    if (rotate_value == 360) { rotate_value=0.0f; }
    else { rotate_value+=0.5f; }
  }
  else if (key == GLUT_KEY_LEFT) {
    if (rotate_value == 0) { rotate_value=360; }
    else {rotate_value-=0.5f; }

  }

  {
    static float posXOld=0;
    static float posYOld=0;

    if (posX == posXOld && posY == posYOld) { cout << "Some position..." << endl; }
    else {
      vertex temp = {-posX, -posY, 1, 0, 1, 0, 0};
      vertices.push_back(temp);
    }
    posXOld = posX;
    posYOld = posY;
  }

  glutPostRedisplay();
}

void initRendering()
{
  glClearColor(0.3f, 0.4f, 0.65f, zoom_value);    
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(-GL_WIN_SIZE_X, GL_WIN_SIZE_X, -GL_WIN_SIZE_Y, GL_WIN_SIZE_Y);
}


void handleResize(int w, int h) {
  glViewport(0, 0, w, h);

  glMatrixMode(GL_PROJECTION);

  glLoadIdentity(); //Reset the camera
  gluPerspective(45.0,                  //The camera angle
      (double)w / (double)h, 
      1.0,                   
      200.0);                
}

static void drawline(float x1, float y1, float x2, float y2)
{
  glLineWidth(2.0);
  glBegin (GL_LINES);
  glVertex3f(x1, y1, zoom_value);
  glVertex3f(x2, y2, zoom_value);
  glEnd();
}


static void drawGrid()
{
  float size_w = GL_WIN_SIZE_X;
  float size_h = GL_WIN_SIZE_Y;
  float size_offset = 1.0;

  glColor3f(1.0, 0.5, 1.0);
  glLoadIdentity();
  glPushMatrix();

  glTranslatef(0.0f, posY, zoom_value); 
  glRotatef(rotate_value, 0.0f, 0.0f, 1.0f);   //Z

  glLineWidth(1.5);
  for (float x1=-size_w; x1<size_w; x1 += size_offset)
  {
    drawline(size_w, x1, -size_w, x1);
  }
  for (float y1=-size_h; y1<size_h; y1 += size_offset)
  {
    drawline(y1,  size_h,  y1, -size_h);
  }

  glPopMatrix();
  glutSwapBuffers();
}

static void drawCursor()
{
  glColor3d(0.5, 0.1, 0.0);
  glLoadIdentity();

  glTranslatef(-posX, -posY, 0.0f); 

  glBegin(GL_TRIANGLES);
  glVertex3f( posX+0.0f,     posY+0.5f,    zoom_value);
  glVertex3f( posX+0.5f,     posY+(-0.5f), zoom_value);
  glVertex3f( posX+(-0.5f),  posY+(-0.5f), zoom_value);
  glEnd();

  glutSwapBuffers();
}


void initVertexBuffer() {
  glGenBuffers(1, &vboId);
  glBindBuffer(GL_ARRAY_BUFFER, vboId);
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertex) * vertices.size(), &vertices[0], GL_STATIC_DRAW);
  glBindBuffer(GL_ARRAY_BUFFER, 0);
}


void display()
{
  glClear(GL_COLOR_BUFFER_BIT);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  drawGrid();

  drawCursor();
}


int main(int argc,char** argv)
{
  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(GL_WIN_SIZE_X, GL_WIN_SIZE_Y);

  idWin = glutCreateWindow("Tests OpenGL Objects in Scene");

  initRendering();

  glutDisplayFunc(display);
  glutSpecialFunc(keyPress);
  glutReshapeFunc(handleResize);

  glutMainLoop();

  return(EXIT_SUCCESS);
}

Suggestions are welcomed...


Solution

  • glutSwapBuffers Performs a buffer swap on the layer in use for the current window. You should do a single glutSwapBuffers call at the end of the rendering. Further it is sufficient to on glutPostRedisplay call in the main loop.

    This means remove all all glutPostRedisplay and glutPostRedisplay from your entire code, but change the display function somehow like this:

    void display()
    {
        glClear(GL_COLOR_BUFFER_BIT);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
    
        drawGrid();
        drawCursor();
    
        glutSwapBuffers();
        glutPostRedisplay();
    }
    



    If you want to rotat around the center of the vieport, the you have to swap the glTranslatef and the glRotatef operation:

    void drawGrid()
    {
    
        .....
    
        glRotatef(rotate_value, 0.0f, 0.0f, 1.0f);
        glTranslatef(0.0f, posY, zoom_value); 
    
        .....
    
    }
    


    If you want to move the cursor on grid in the Y-direction of the viewport, then you have to proper respect the angle of rotation:

    if (key == GLUT_KEY_UP) {  
        posX -= sin( rotate_value * DEG_TO_RADIANS ) * velocity;
        posY -= cos( rotate_value * DEG_TO_RADIANS ) * velocity;
    }
    else if (key == GLUT_KEY_DOWN) {
        posX += sin( rotate_value * DEG_TO_RADIANS ) * velocity;
        posY += cos( rotate_value * DEG_TO_RADIANS ) * velocity;
    }
    

    And you have to respect the posX in the translation of the grid:

    glRotatef(rotate_value, 0.0f, 0.0f, 1.0f);
    glTranslatef(posX, posY, zoom_value);
    

    Explanation:

    See the documentation of glTranslate:

    glTranslate produces a translation by x y z . The current matrix (see glMatrixMode) is multiplied by this translation matrix, with the product replacing the current matrix,

    and see the documentation of glRotate:

    glRotate produces a rotation of angle degrees around the vector x y z . The current matrix (see glMatrixMode) is multiplied by a rotation matrix with the product replacing the current matrix,


    Note, the translation matrix looks like this:

    Matrix4x4 translate;
    
    translate[0] : ( 1,  0,  0,  0 )
    translate[1] : ( 0,  1,  0,  0 )
    translate[2] : ( 0,  0,  1,  0 )
    translate[3] : ( tx, ty, tz, 1 )
    

    And the rotation matrix around Y-Axis looks like this:

    Matrix4x4  rotate;
    float      angle;
    
    rotate[0] : ( cos(angle),  sin(angle), 0, 0 )
    rotate[1] : ( -sin(angle), cos(angle), 0, 0 )
    rotate[2] : ( 0,           0,          1, 0 )
    rotate[3] : ( 0,           0,          0, 1 ) 
    

    A matrix multiplication works like this:

    Matrix4x4 A, B, C;
    
    // C = A * B
    for ( int k = 0; k < 4; ++ k )
        for ( int l = 0; l < 4; ++ l )
            C[k][l] = A[0][l] * B[k][0] + A[1][l] * B[k][1] + A[2][l] * B[k][2] +  A[3][l] * B[k][3];
    


    The result of translate * rotate is this:

    model[0] : ( cos(angle),  sin(angle), 0,  0 )
    model[1] : ( -sin(angle)  cos(angle), 0,  0 )
    model[2] : ( 0,           0,          0,  0 )
    model[3] : ( tx,          ty,         tz, 1 )
    

    enter image description here


    Note, the result of rotate * translate would be:

    model[0] : ( cos(angle),                     sin(angle),                    0,   0 )
    model[1] : ( -sin(angle),                    cos(angle),                    0,   0 )
    model[2] : ( 0,                              0,                             0    0 )
    model[3] : ( cos(angle)*tx - sin(angle)*tx,  sin(angle)*ty + cos(angle)*ty, tz,  1 )
    

    enter image description here