Search code examples
c++opengl2dglutaffinetransform

2D basic transformation combination


I have a 2D transformation problem in OpenGL. My program draws two letters on screen and, by pressing assigned keys, letters have to move up (GLUT_KEY_UP) down (GLUT_KEY_DOWN) left (GLUT_KEY_LEFT) right (GLUT_KEY_RIGHT) and rotate CW (GLUT_KEY_HOME) and CCW (GLUT_KEY_END). (On keys GLUT_KEY_PAGE_UP and GLUT_KEY_PAGE_DOWN both letters rotate around their own centers simultaniously in oposite directions, pressing e.g. GLUT_KEY_UP after GLUT_KEY_PAGE_UP moves both letters in a correct direction).

I can't make the letters do the correct movement (up, down, left or right) after the objects had been rotated with GLUT_KEY_HOME or GLUT_KEY_END:

case GLUT_KEY_HOME: //rotate clockwise around the center of the model
    glMatrixMode(GL_MODELVIEW);
    setRotationMatrix(-angle, modelSizeX, modelSizeY);
    glMultMatrixf(rotationMatrix);  // multiply current Modelview Matrix by rotate-and-translate matrix
    display();
    break;

Function that 'sets' the rotation matrix inserts angle and rotation point into Identity matrix [4x4]:

void setRotationMatrix(float theta, float x0, float y0)
{
    theta = theta * (2 * acos(0.0)) / 180; // convert theta from degrees to radian
    rotationMatrix[0] = cos(theta);
    rotationMatrix[1] = sin(theta);
    rotationMatrix[4] = -sin(theta);
    rotationMatrix[5] = cos(theta);
    rotationMatrix[12] = ((x0 * 0.5f) * (1 - cos(theta)) + (y0 * 0.5f) * sin(theta));
    rotationMatrix[13] = ((y0 * 0.5f) * (1 - cos(theta)) - (x0 * 0.5f) * sin(theta));
}

After the objects have been rotated, if I press KEY_UP, they move not 'up' (in normal direction against x axis ) but in the direction normal to the horizontal plane of the object after rotation (slightly sideways).

case GLUT_KEY_UP:
    glMatrixMode(GL_MODELVIEW);
    glTranslated(0, delta, 0);
    display();
    break;

how do I make it move straightly to the 'north', meaning, not the 'north' of the object but the 'north' of my coordinate system?

#include <Windows.h>
#include <GL/glut.h> 
#include <vector>
#include <fstream>
#include <cmath>

using namespace std;

#define EscKey 27
#define PlusKey 43
#define MinusKey 45

struct Point
{
    int x, y;
};

double pi = 2 * acos(0.0);
void reshape(int w, int h);
void display();
void drawInitials();
void processNormalKeys(unsigned char key, int x, int y);
void processSpecialKeys(int key, int x, int y);
void setRotationMatrix(float theta, float x0, float y0);
void readFromFile();
void lineto(Point p);
void moveto(Point p);
void drawM();
void drawJ();
vector <Point> point;
vector <int>   code;
Point currentPoint;
int modelSizeX = 202; int modelSizeY = 89;
int letterMSizeX = 107; int letterJSizeX = 78;
int   delta = 5;        // movement size
float zoomRate = 0.1;   // scale-and-translate rate: 0.1 = resize by 10%
float angle = 1, rotaterR = 1 * pi / 180, rotater,  angleCount = 0.0; // saved rotation angle
GLfloat modelviewStateMatrix[16];


int windowSizeX, windowSizeY;


boolean separate = false;

float rotationMatrix[16] = { 1.0f, 0.0f,  0.0f,  0.0f,
                             0.0f, 1.0f,  0.0f,  0.0f,
                             0.0f, 0.0f,  1.0f,  0.0f,
                             0.0f, 0.0f,  0.0f,  1.0f };

float plusKeyMatrix[16] = { 1.0f + zoomRate, 0.0f,  0.0f,  0.0f,
                             0.0f, 1.0f + zoomRate,  0.0f,  0.0f,
                             0.0f, 0.0f,  1.0f,  0.0f,
                           -(modelSizeX * zoomRate * 0.5f), -(modelSizeY * zoomRate * 0.5f),  0.0f,  1.0f };

float minusKeyMatrix[16] = { 1.0f - zoomRate, 0.0f,  0.0f,  0.0f,
                             0.0f, 1.0f - zoomRate,  0.0f,  0.0f,
                             0.0f, 0.0f,  1.0f,  0.0f,
                             modelSizeX * zoomRate * 0.5f,    modelSizeY * zoomRate * 0.5f,  0.0f,  1.0f };

void readFromFile()
{
    fstream f("initials_points_2.txt", ios::in);
    int pointNumber;
    Point p;
    f >> pointNumber;
    for (int i = 0; i < pointNumber;i++)
    {
        f >> p.x >> p.y;
        point.push_back(p);
    }
    int movesNumber, m;
    f >> movesNumber;
    for (int i = 0; i < movesNumber; i++)
    {
        f >> m; code.push_back(m);
    }
    f.close();
}
void moveto(Point p)
{
    currentPoint.x = p.x; currentPoint.y = p.y;
}
void lineto(Point p)
{
    glBegin(GL_LINES);
    glVertex2i(currentPoint.x, currentPoint.y);
    glVertex2i(p.x, p.y);
    glEnd();
    currentPoint.x = p.x; currentPoint.y = p.y;
}

void setRotationMatrix(float theta, float x0, float y0)
{
    theta = theta * (2 * acos(0.0)) / 180; // convert theta from degrees to radian
    rotationMatrix[0] = cos(theta);
    rotationMatrix[1] = sin(theta);
    rotationMatrix[4] = -sin(theta);
    rotationMatrix[5] = cos(theta);
    rotationMatrix[12] = ((x0 * 0.5f) * (1 - cos(theta)) + (y0 * 0.5f) * sin(theta));
    rotationMatrix[13] = ((y0 * 0.5f) * (1 - cos(theta)) - (x0 * 0.5f) * sin(theta));
}

void drawM()
{
    int i = 1;
    moveto(point[0]);
    while (code[i] > 0 && i < code.size())
    {
        lineto(point[code[i] - 1]);
        i++;
    }

    glBegin(GL_LINES);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(letterMSizeX, 0.0, 0.0);
    glVertex3f(letterMSizeX, 0.0, 0.0);
    glVertex3f(letterMSizeX, modelSizeY, 0.0);
    glVertex3f(letterMSizeX, modelSizeY, 0.0);
    glVertex3f(0.0, modelSizeY, 0.0);
    glVertex3f(0.0, modelSizeY, 0.0);
    glVertex3f(0.0, 0.0, 0.0);
    glEnd();

    glColor3f(0.0, 1.0, 0.0);
    glBegin(GL_LINES);
    glVertex2i(0, 0); glVertex2i(letterMSizeX, modelSizeY);
    glVertex2i(letterMSizeX, 0); glVertex2i(0, modelSizeY);
    glEnd();
}
void drawJ()
{
    glColor3f(1.0, 0.0, 0.0);
    int i = 14;
    moveto(point[-1 * (code[i]) - 1]);
    i++;
    while (i < code.size())
    {
        lineto(point[code[i] - 1]);
        i++;
    }

    glBegin(GL_LINES);
    glVertex3f((modelSizeX - letterJSizeX), 0.0, 0.0);
    glVertex3f((modelSizeX - letterJSizeX), modelSizeY, 0.0);
    glVertex3f((modelSizeX - letterJSizeX), modelSizeY, 0.0);
    glVertex3f(modelSizeX, modelSizeY, 0.0);
    glVertex3f(modelSizeX, modelSizeY, 0.0);
    glVertex3f(modelSizeX, 0.0, 0.0);
    glVertex3f(modelSizeX, 0.0, 0.0);
    glVertex3f((modelSizeX - letterJSizeX), 0.0, 0.0);
    glEnd();

    glColor3f(0.0, 1.0, 0.0);
    glBegin(GL_LINES);
    glVertex2i(letterMSizeX + 17, 0); glVertex2i(modelSizeX, modelSizeY);
    glVertex2i(letterMSizeX + 17, modelSizeY); glVertex2i(modelSizeX, 0);
    glEnd();


}

void drawInitials()
{
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    setRotationMatrix(-rotater, letterMSizeX, modelSizeY);
    glMultMatrixf(rotationMatrix);  // multiply current Modelview Matrix by rotate-and-translate matrix
    drawM();
    glTranslatef(-letterMSizeX * 0.5, -modelSizeY * 0.5, 0);
    glPopMatrix();

    glPushMatrix();
    glTranslatef((modelSizeX - letterJSizeX), 0, 0);
    setRotationMatrix(rotater, letterJSizeX, modelSizeY);
    glMultMatrixf(rotationMatrix);  // multiply current Modelview Matrix by rotate-and-translate matrix
    glTranslatef(-(modelSizeX - letterJSizeX), 0, 0);
    drawJ();
    glPopMatrix();
}

void processNormalKeys(unsigned char key, int x, int y)
{
    switch (key)
    {
    case EscKey:
        exit(0);
        break;
    case PlusKey:                      // "zoom in" 
        glMatrixMode(GL_MODELVIEW);
        glMultMatrixf(plusKeyMatrix);  // multiply current Modelview Matrix by scale-and-translate matrix   
        display();
        break;
    case MinusKey:                     // "zoom out"    
        glMatrixMode(GL_MODELVIEW);
        glMultMatrixf(minusKeyMatrix); // multiply current Modelview Matrix by scale-and-translate matrix
        display();
        break;
    }
}

void processSpecialKeys(int key, int x, int y) {
    switch (key) {
    case GLUT_KEY_UP:
        glMatrixMode(GL_MODELVIEW);
        glTranslated(0, delta, 0);
        display();
        break;
    case GLUT_KEY_DOWN:
        glMatrixMode(GL_MODELVIEW);
        glTranslated(0, -delta, 0);
        display();
        break;
    case GLUT_KEY_LEFT:
        glMatrixMode(GL_MODELVIEW);
        glTranslated(-delta, 0, 0);
        display();
        break;
    case GLUT_KEY_RIGHT:
        glMatrixMode(GL_MODELVIEW);
        glTranslated(delta, 0, 0);
        display();
        break;
    case GLUT_KEY_HOME: //rotate clockwise around the center of the model
        glMatrixMode(GL_MODELVIEW);
        setRotationMatrix(-angle, modelSizeX, modelSizeY);
        glMultMatrixf(rotationMatrix);  // multiply current Modelview Matrix by rotate-and-translate matrix
        display();
        break;
    case GLUT_KEY_END: //rotate counterclockwise around the center of the model
        glMatrixMode(GL_MODELVIEW);
        setRotationMatrix(angle, modelSizeX, modelSizeY);
        glMultMatrixf(rotationMatrix);  // multiply current Modelview Matrix by rotate-and-translate matrix
        display();
        break;
    case GLUT_KEY_PAGE_UP:
        rotater -= rotaterR;
        if (rotater <= -2 * pi)
            rotater += 2 * pi;
        display();
        break;
    case GLUT_KEY_PAGE_DOWN:
        rotater += rotaterR;
        if (rotater >= 2 * pi)
            rotater -= 2 * pi;
        display();
        break;
    }
}

int main(int argc, char* argv[])
{
    currentPoint.x = 0; currentPoint.y = 0;
    readFromFile();

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(800, 600);
    glutInitWindowPosition(100, 150);
    glutCreateWindow("OpenGL lab");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);

    glutKeyboardFunc(processNormalKeys);
    glutSpecialFunc(processSpecialKeys);

    glutMainLoop();
    return 0;
}
void reshape(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    windowSizeX = w;
    windowSizeY = h;
    gluOrtho2D(-modelSizeX, modelSizeX * 2, -modelSizeY, modelSizeY * 2); // World size
}
void display()
{
    glClearColor(1, 1, 1, 0);
    glClear(GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    glColor3f(0.0, 0.0, 1.0);
    glBegin(GL_LINES);
    glVertex2i(-windowSizeX, 0); glVertex2i(windowSizeX, 0);
    glVertex2i(0, -windowSizeY); glVertex2i(0, windowSizeY);
    glEnd();
    glPopMatrix();

    glColor3d(1, 0, 0);
    drawInitials();

    glPointSize(5);
    glBegin(GL_POINTS);
    glVertex3f(modelSizeX * 0.5, modelSizeY * 0.5, 0.0);
    glVertex3f(letterMSizeX * 0.5, modelSizeY * 0.5, 0.0);
    glVertex3f(letterJSizeX * 0.5 + letterMSizeX + 17, modelSizeY * 0.5, 0.0);
    glEnd();

    glFlush();
}

vertices file: initials_points_2.txt

I was trying to:

  • encapsulate _HOME/_END rotation with glPushMatrix()/glPopMatrix()
  • revert the rotation after the drawal of the object by multiplying MODELVIEW matrix with (-angle, same point)
  • was trying to drop glMultMatrix() and use glTranslate(), glRotate()

none of them had worked as I intended


Solution

  • I found out what I was doing wrong: buttons GLUT_KEY_HOME/GLUT_KEY_END should be performing rotations similar to those of GLUT_KEY_PAGE_UP/GLUT_KEY_PAGE_DOWN:

    • increase current rotation angle for the whole model (new variable to save rotation angle around the middle of the model);
    • call display method();

    Then, inside display() method:

    • push current matrix into stack;
    • rotate model by the angle;
    • another push matrix into stack
    • rotate letters by the rotater variable changed in PAGE_UP/PAGE_DOWN draw letters
    • pop matrix
    • pop matrix;

    Code on keys:

       case GLUT_KEY_HOME: //rotate clockwise around the center of the model
            glMatrixMode(GL_MODELVIEW);
            angle -= rotationRate;
            if (angle <= -2 * pi)
                angle += 2 * pi;
            display();
            break;
        case GLUT_KEY_END: //rotate counterclockwise around the center of the model
            glMatrixMode(GL_MODELVIEW);
            angle += rotationRate;
            if (angle >= 2 * pi)
                angle -= 2 * pi;
            display();
            break;
    

    edited drawInitials() method (called from display() method):

    void drawInitials()
    {
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        setRotationMatrix(angle, modelSizeX, modelSizeY); //set rotation matrix to rotate around the center of the model by current angle
        glMultMatrixf(rotationMatrix);  // multiply current Modelview Matrix by rotate-and-translate matrix
            glPushMatrix();
            setRotationMatrix(-rotater, letterMSizeX, modelSizeY); //set to rotate around the center of the letter M by current rotater Clockwise
            glMultMatrixf(rotationMatrix);  // multiply current Modelview Matrix by rotate-and-translate matrix
            drawM();
            glPopMatrix();
    
            glPushMatrix();
            glTranslatef((modelSizeX - letterJSizeX), 0, 0);  // move OX axis by the size of letter M + space between the letters 
            setRotationMatrix(rotater, letterJSizeX, modelSizeY);//set to rotate around the center of the letter J by current rotater Counter Clockwise
            glMultMatrixf(rotationMatrix);  // multiply current Modelview Matrix by rotate-and-translate matrix
            glTranslatef(-(modelSizeX - letterJSizeX), 0, 0); // return OX axis to its previous place
            drawJ();
            glPopMatrix();
        glPopMatrix();
    }
    

    In setRotationMatrix() remove conversion into radian;

    Add new variable rotationRate to use it as an incriment for both rotations:

    float angle; 
    // saved rotation angle around center of the model (radian) 
    float rotater;
    // rotater - saved rotation angle around center of the letter (radian)
    float rotationRate = 1 * pi / 180;  // rotation rate (radian), ex rotaterR