Search code examples
openglvisual-c++glutfreeglutglu

How to add a scene panning using mouse drag event OpenGL( with perspective projection)?


Is there to have a scene panning (kind of like we get in gmaps) with simple mouse event ? Currently I two mouse events :

  1. Mousewheel to zoom by changing cRadius in glTranslatef

  2. LMB dragging for rotation by changing xrot and yrot in glRotatef.

    Below is the function:

float xpos = 0, ypos = 0, zpos = 0, xrot = 0, yrot = 0, zrot = 0, cRadius = 30.0f, lastx, lasty, lastz;

void mouseMovement(int x, int y)
{
    int diffx = x - lastx; 
    int diffy = y - lasty; 
    lastx = x;             
    lasty = y;  
    
    xrot += (float)diffy;  
    yrot += (float)diffx; 
}

and mouse button initialization function below

void mouseFunc(int button, int state, int x, int y) 
{
    lastx = x;
    lasty = y;
}

Can the pan function be enabled using the same logic that is being used for rotation, like replacing xrot and yrot with a different variable in glTranslatef (if not where should be the translation applied) ? Below is my display function along with reshape function. Am using glPerspective rather that glLookat

void display(void)
{
    glClearColor(0.0, 0.0, 0.0, 1.0);                   
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
    glLoadIdentity();
    glTranslatef(0.0f, 0.0f, -cRadius);
    glRotatef(xrot, 1.0, 0.0, 0.0);
    glRotatef(yrot, 0.0, 1.0, 0.0); 
    glBegin(GL_LINES);      
      ------------
      ------
    glTranslated(-xpos, 0.0f, zpos);
    glutSwapBuffers();              
    glEnd();
}

The mouse functions has been called in main loop

int OpenGL(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Window");
    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutMouseFunc(mouseFunc);
    glutMotionFunc(mouseMovement);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}

//Reshape

void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, (GLfloat)w / (GLfloat)h, 0.1, 500.0);
    glMatrixMode(GL_MODELVIEW);
}

Solution

  • At perspective projection panning depends on the depth of the object. (See Qmatrix4x4 translate does not take any effect.
    Luckily you know the dept of the geometry. It is defined in view space by cRadius.

    The relation between the projected area in view space and the Z coordinate of the view space is linear. It depends on the field of view angle and the aspect ratio.
    (See also Field of view + Aspect Ratio + View Matrix from Projection Matrix (HMD OST Calibration))

    A projected size in normalized device space can be transformed to a size in view space by:

    aspect = w / h
    tanFov = tan(fov_y * 2.0) * 2.0;
    
    size_x = ndx_size_x * z_eye * tanFov * aspect;
    size_y = ndx_size_y * z_eye * tanFov;
    

    Apply that to your code:

    #define _USE_MATH_DEFINES
    #include <math.h>
    
    float cRadius = 10.0f;
    float fov_y = 60.0f;
    float nearp = 0.1f;
    float farp = 500.0f;
    float width = 500.0f;
    float height = 500.0f;
    
    void mouseMovement(int x, int y)
    {
        int diffx = x - lastx; 
        int diffy = y - lasty; 
        lastx = x;
        lasty = y;
    
        float ndc_x = diffx * 2.0f / width;
        float ndc_y = diffy * 2.0f / height;
    
        float aspect = width / height;
        float fov_rad = fov_y * M_PI / 180.0;
        float tanFov = tan(fov_rad / 2.0);
    
        xtrans += ndc_x * cRadius * tanFov * aspect;
        ytrans -= ndc_y * cRadius * tanFov;
    }
    
    void mouseFunc( int button, int state, int x, int y )
    {
      lastx = x;
      lasty = y;
    }
    
    void reshape(int w, int h)
    {
        width  = (float)w;
        height = (float)h;
        glViewport(0, 0, (GLsizei)w, (GLsizei)h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(fov_y, width / height, nearp, farp);
        glMatrixMode(GL_MODELVIEW);
    }
    
    void display(void)
    {
        // [...]
    
        glLoadIdentity();
        glTranslatef(xtrans, ytrans, -cRadius);
        glRotatef(xrot, 1.0, 0.0, 0.0);
        glRotatef(yrot, 0.0, 1.0, 0.0);
        // [...]
        glEnd();
    
        glutSwapBuffers();
    }