openglpickingtrackballray-picking

Trackball issue with Raypicking


I'm trying to develop a 3D raypicking in my OpenGL scene.

I have a working OBJ loader with a trackball.

char*      model_file = NULL;       /* name of the obect file */
GLuint     model_list = 0;      /* display list for object */
GLMmodel*  model;               /* glm model data structure */
GLfloat    scale;               /* original scale factor */
GLfloat    smoothing_angle = 90.0;  /* smoothing angle */
GLfloat    weld_distance = 0.00001; /* epsilon for welding vertices */
GLboolean  facet_normal = GL_FALSE; /* draw with facet normal? */
GLboolean  bounding_box = GL_FALSE; /* bounding box on? */
GLboolean  spheres = GL_FALSE;
GLboolean  performance = GL_FALSE;  /* performance counter on? */
GLboolean  stats = GL_FALSE;        /* statistics on? */
GLuint     material_mode = 0;       /* 0=none, 1=color, 2=material, 3=texture */
GLint      entries = 0;         /* entries in model menu */
GLdouble   pan_x = 0.0;
GLdouble   pan_y = 0.0;
GLdouble   pan_z = 0.0;
char texnames[1][64] = {"foto_rgb.ppm"};
//char texnames[1][64] = {"grid.ppm"};
GLint w,h;
GLubyte* texture;

void convert2dto3D(int mouseX, int mouseY){
    double matModelView[16], matProjection[16]; 
    int viewport[4]; 

    glGetDoublev( GL_MODELVIEW_MATRIX, matModelView ); 
    glGetDoublev( GL_PROJECTION_MATRIX, matProjection ); 
    glGetIntegerv( GL_VIEWPORT, viewport );


    double winX = (double)mouseX; 
    double winY = viewport[3] - (double)mouseY; 

    gluUnProject(winX, winY, 0.0, matModelView, matProjection, 
                 viewport, &m_start.x, &m_start.y, &m_start.z); 

    gluUnProject(winX, winY, 1.0, matModelView, matProjection, 
                 viewport, &m_end.x, &m_end.y, &m_end.z);

    //printf("Coordinata 2D %d %d\nCoordinata camera far: %f %f %f\n", mouseX, mouseY, m_end.x, m_end.y, m_end.z);
    //printf("Coordinate camera near: %f %f %f\n", m_start.x, m_start.y, m_start.z);
    //fflush(stdout);

    mouseClicked = GL_TRUE;

    Vector3d nearPoint(m_start.x, m_start.y, m_start.z);
    Vector3d farPoint(m_end.x, m_end.y, m_end.z);

    Vector3d ray = farPoint - nearPoint;

}

void line (void) {

    glLineWidth(10);
    //glPointSize(50.2);
    glColor3f(0.0f, 1.0f, 0.0f);      
    glBegin(GL_LINES);
        glVertex3f( m_start.x, m_start.y, m_start.z );
        glVertex3f( m_end.x, m_end.y, m_end.z );
    glEnd();
    glColor3f(1.0f,1.0f,1.0f);
    glLineWidth(1);

}


void lists(void){
    GLfloat ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat diffuse[] = { 0.8, 0.8, 0.8, 1.0 };
    GLfloat specular[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat shininess = 65.0;

    glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
    glMaterialf(GL_FRONT, GL_SHININESS, shininess);

    if (model_list)
        glDeleteLists(model_list, 1);

    glDisable(GL_TEXTURE_2D);
    /* generate a list */
    switch (material_mode)
    {
     case 0:
        if (facet_normal)
            model_list = glmList(model, GLM_FLAT);
        else
            model_list = glmList(model, GLM_SMOOTH);
     break;
     case 1:
        if (facet_normal)
            model_list = glmList(model, GLM_FLAT | GLM_COLOR);
        else
            model_list = glmList(model, GLM_SMOOTH | GLM_COLOR);
     break;
     case 2:
        if (facet_normal)
            model_list = glmList(model, GLM_FLAT | GLM_MATERIAL);
        else
            model_list = glmList(model, GLM_SMOOTH | GLM_MATERIAL);
     break;
     case 3:
        glEnable(GL_TEXTURE_2D);
        model_list = glmList(model, GLM_TEXTURE);
//        glDisable(GL_TEXTURE_2D);
     break;
    }
}

void init(void){
    gltbInit(GLUT_LEFT_BUTTON);

    /* read in the model */
    model = glmReadOBJ(model_file);
    scale = glmUnitize(model);
    glmFacetNormals(model);
    glmVertexNormals(model, smoothing_angle);

    if (model->nummaterials > 0)
        material_mode = 2;

    /* create new display lists */
    lists();

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    glEnable(GL_DEPTH_TEST);
}

void reshape(int width, int height){
    gltbReshape(width, height);

    glViewport(0, 0, width, height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)height / (GLfloat)width, 1.0, 128.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -3.0);
}

void display(void){
    static char s[256], t[32];
    static char* p;
    static int frames = 0;
    int i=0,j=0;

    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

    glTranslatef(pan_x, pan_y, 0.0);

    gltbMatrix();

    if(mouseClicked){
        line();
    }

    glCallList(model_list);

    glPopMatrix();

    glutSwapBuffers();
    glEnable(GL_LIGHTING);
}

static GLint      mouse_state;
static GLint      mouse_button;

void mouse(int button, int state, int x, int y){
    GLdouble model_project[4*4];
    GLdouble proj[4*4];
    GLint view[4];

    /* fix for two-button mice -- left mouse + shift = middle mouse */
    if (button == GLUT_LEFT_BUTTON && glutGetModifiers() & GLUT_ACTIVE_SHIFT)
        button = GLUT_MIDDLE_BUTTON;

    gltbMouse(button, state, x, y);

    mouse_state = state;
    mouse_button = button;

    if (state == GLUT_DOWN && button == GLUT_MIDDLE_BUTTON) {
        glGetDoublev(GL_MODELVIEW_MATRIX, model_project);
        glGetDoublev(GL_PROJECTION_MATRIX, proj);
        glGetIntegerv(GL_VIEWPORT, view);

        gluProject((GLdouble)x, (GLdouble)y, 0.0,
            model_project, proj, view,
            &pan_x, &pan_y, &pan_z);
        gluUnProject((GLdouble)x, (GLdouble)y, pan_z,
            model_project, proj, view,
            &pan_x, &pan_y, &pan_z);
        pan_y = -pan_y;
    }

    if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN && glutGetModifiers() & GLUT_ACTIVE_CTRL) {
        convert2dto3D(x,y);
    }
    else if(button == GLUT_WHEEL_UP){ // Wheel up
        glmScale(model, 1.25);
        lists();
    }
    else if(button == GLUT_WHEEL_DOWN){ // Wheel down
        glmScale(model, 0.8);
        lists();
    }

    glutPostRedisplay();
}

void motion(int x, int y){
    GLdouble model[4*4];
    GLdouble proj[4*4];
    GLint view[4];

    gltbMotion(x, y);


    glutPostRedisplay();
}

int main(int argc, char** argv){
    int buffering = GLUT_DOUBLE;
    struct dirent* direntp;
    DIR* dirp;
    int models;

    glutInitWindowSize(512, 512);
    glutInit(&argc, argv);

    while (--argc) {
        if (strcmp(argv[argc], "-sb") == 0)
            buffering = GLUT_SINGLE;
        else
            model_file = argv[argc];
    }

    if (!model_file) {
//        model_file = "data/dolphins.obj";
        model_file = "data/boeing_2.obj";
    }

    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | buffering);
    glutCreateWindow("Smooth");

    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);

/* Image data packed tightly. */
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    textures();

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
//    glEnable(GL_TEXTURE_2D);

    models = glutCreateMenu(menu);
    dirp = opendir(DATA_DIR);
    if (!dirp) {
        fprintf(stderr, "%s: can't open data directory.\n", argv[0]);
    } else {
        while ((direntp = readdir(dirp)) != NULL) {
            if (strstr(direntp->d_name, ".obj")) {
                entries++;
                glutAddMenuEntry(direntp->d_name, -entries);
            }
        }
        closedir(dirp);
    }


    init();

    glutMainLoop();
    return 0;
}

Here trackball's functions:

     /*
     *  Simple trackball-like motion adapted (ripped off) from projtex.c
     *  (written by David Yu and David Blythe).  See the SIGGRAPH '96
     *  Advanced OpenGL course notes.
     */


    #include <math.h>
    #include <stdio.h>
    #include <assert.h>
    #include <GL/glut.h>
    #include "gltb.h"


    #define GLTB_TIME_EPSILON  10


    static GLuint    gltb_lasttime;
    static GLfloat   gltb_lastposition[3];

    static GLfloat   gltb_angle = 0.0;
    static GLfloat   gltb_axis[3];
    static GLfloat   gltb_transform[4][4];

    static GLuint    gltb_width;
    static GLuint    gltb_height;

    static GLint     gltb_button = -1;
    static GLboolean gltb_tracking = GL_FALSE;
    static GLboolean gltb_animate = GL_TRUE;


    static void
    _gltbPointToVector(int x, int y, int width, int height, float v[3])
    {
      float d, a;

      /* project x, y onto a hemi-sphere centered within width, height. */
      v[0] = (2.0 * x - width) / width;
      v[1] = (height - 2.0 * y) / height;
      d = sqrt(v[0] * v[0] + v[1] * v[1]);
      v[2] = cos((3.14159265 / 2.0) * ((d < 1.0) ? d : 1.0));
      a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
      v[0] *= a;
      v[1] *= a;
      v[2] *= a;
    }

    static void
    _gltbAnimate(void)
    {
      glutPostRedisplay();
    }

    void
    _gltbStartMotion(int x, int y, int button, int time)
    {
      assert(gltb_button != -1);

      gltb_tracking = GL_TRUE;
      gltb_lasttime = time;
      _gltbPointToVector(x, y, gltb_width, gltb_height, gltb_lastposition);
    }

    void
    _gltbStopMotion(int button, unsigned time)
    {
      assert(gltb_button != -1);

      gltb_tracking = GL_FALSE;

      if (time - gltb_lasttime < GLTB_TIME_EPSILON && gltb_animate) {
          glutIdleFunc(_gltbAnimate);
      } else {
        gltb_angle = 0;
        if (gltb_animate)
          glutIdleFunc(0);
      }
    }

    void
    gltbAnimate(GLboolean animate)
    {
      gltb_animate = animate;
    }

    void
    gltbInit(GLuint button)
    {
      gltb_button = button;
      gltb_angle = 0.0;

      /* put the identity in the trackball transform */
      glPushMatrix();
      glLoadIdentity();
      glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)gltb_transform);
      glPopMatrix();
    }

    void
    gltbMatrix(void)
    {
      assert(gltb_button != -1);

      glPushMatrix();
      glLoadIdentity();
      glRotatef(gltb_angle, gltb_axis[0], gltb_axis[1], gltb_axis[2]);
      glMultMatrixf((GLfloat*)gltb_transform);
      glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)gltb_transform);
      glPopMatrix();

      glMultMatrixf((GLfloat*)gltb_transform);
    }

    void
    gltbReshape(int width, int height)
    {
      assert(gltb_button != -1);

      gltb_width  = width;
      gltb_height = height;
    }

    void
    gltbMouse(int button, int state, int x, int y)
    {
      assert(gltb_button != -1);

      if (state == GLUT_DOWN && button == gltb_button)
        _gltbStartMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME));
      else if (state == GLUT_UP && button == gltb_button)
        _gltbStopMotion(button, glutGet(GLUT_ELAPSED_TIME));
    }

    void
    gltbMotion(int x, int y)
    {
      GLfloat current_position[3], dx, dy, dz;

      assert(gltb_button != -1);

      if (gltb_tracking == GL_FALSE)
        return;

      _gltbPointToVector(x, y, gltb_width, gltb_height, current_position);

      /* calculate the angle to rotate by (directly proportional to the
         length of the mouse movement) */
      dx = current_position[0] - gltb_lastposition[0];
      dy = current_position[1] - gltb_lastposition[1];
      dz = current_position[2] - gltb_lastposition[2];
      gltb_angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz);

      /* calculate the axis of rotation (cross product) */
      gltb_axis[0] = gltb_lastposition[1] * current_position[2] - 
                   gltb_lastposition[2] * current_position[1];
      gltb_axis[1] = gltb_lastposition[2] * current_position[0] - 
                   gltb_lastposition[0] * current_position[2];
      gltb_axis[2] = gltb_lastposition[0] * current_position[1] - 
                   gltb_lastposition[1] * current_position[0];

      /* XXX - constrain motion */
      gltb_axis[2] = 0;

      /* reset for next time */
      gltb_lasttime = glutGet(GLUT_ELAPSED_TIME);
      gltb_lastposition[0] = current_position[0];
      gltb_lastposition[1] = current_position[1];
      gltb_lastposition[2] = current_position[2];

      /* remember to draw new position */
      glutPostRedisplay();
    }

If I cast a ray (CTRL + Left_click) at the beginning It works with correct 3D far_point and near_point If I rotate the object (use Trackball), the ray doesn't use real eye/far points and I can't figure out why. I've tried with GluLookAt instead of glRotatef, but I can't figure out how to move camera with mouse like this trackball does.


Solution

  • Solved: The problem was the pushMatrix()/popMatrix() at beginning/end in display().