Link to home
Start Free TrialLog in
Avatar of adawyn
adawyn

asked on

2D object picking with OpenGL

I am working on a tool application that uses OpenGL to render a workspace area of my main form.  I am trying to add 2d picking to the workspace (as all objects are 2d and rendered along the z=0 plane)  I have written the following code, but it doesn't seem to be working properly.  It tells me I have selected an object when the object is about an inch below where I clicked.  

This is the forms wndproc code
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
            case WM_CREATE:
            break;
            case WM_CLOSE:
                  DestroyWindow(hwnd);
            break;
            case WM_COMMAND:
                  switch(LOWORD(wParam))
                  {
                        case IDC_MAIN_RENDER_VIEW:
                              switch(HIWORD(wParam))
                              {
                              case STN_CLICKED:
                                    POINT pt;
                                    GetCursorPos(&pt);

                                    maininterface.handle_mouse_click(pt.x, pt.y);
                              break;
                              }
                                break;
                case WM_DESTROY:
                       PostQuitMessage(0);
                break;
                default:
                       return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}




 

 

then the maininterface class does the following I adjust for 390 to the X because the static control is placed 390 right and 0 down on the main form.


void windowsinterface::handle_mouse_click(int x, int y)
{
      char valc[100]="";
      // adjust for window shift (from main)
      x = x - 390;
      if (x < 0)
            return;
      itoa(renderwindow.check_collision(x,y),valc, 100);
      MessageBox(NULL, valc, "test", MB_OK);
}



 

 

I am including all of my opengl class because I can't tell where the error actually is. It is kind of long, but bear with it.


#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>

struct line
{
      float x1;
      float y1;
      float x2;
      float y2;
      int ID;
      bool color;
      float r;
      float g;
      float b;
};

struct quad
{
      RECT box;
      int ID;
      float r;
      float g;
      float b;
};

class OpenGL
{
public:
      OpenGL()
      {
            m_RC=NULL;
            m_DC=NULL;
            m_hwnd=NULL;
            m_hinstance=NULL;
            m_clear_c=0;
            m_linecount=0;
            m_quadcount=0;
            lines=NULL;
            quads=NULL;
            m_width=0;
            m_height=0;
      };
      ~OpenGL();
      GLvoid resize_scene(GLsizei width, GLsizei height);
      int init(HWND hwnd, int width, int height);
      int drawscene(GLvoid);
      void render();
      void set_clear_color(int val);
      void add_line(float x1, float y1, float x2, float y2, int val, float r, float g, float b);
      int remove_line(int ID);
      void add_quad(quad toadd);
      int remove_quad(int ID);
      int check_collision(int x, int y);
      void clear_lines();
      void clear_quads();
private:
      HGLRC m_RC;
      HDC m_DC;
      HWND m_hwnd;
      HINSTANCE m_hinstance;
      int m_clear_c;
      int m_linecount;
      int m_quadcount;
      struct line* lines;
      struct quad* quads;
      int m_width;
      int m_height;
};

OpenGL::~OpenGL()
{
      if (m_RC)
      {
            if (!wglMakeCurrent(NULL, NULL))
            {
                  MessageBox(NULL, "DC and RC release Failed", "SHUTDOWN ERROR", MB_OK | MB_ICONEXCLAMATION);
            }
            if (!wglDeleteContext(m_RC))
            {
                  MessageBox(NULL, "Rendering Context Release Failed", "SHUTDOWN ERROR", MB_OK | MB_ICONEXCLAMATION);
            }

            m_RC = NULL;      
      }
      if (m_DC && (!ReleaseDC(m_hwnd, m_DC)))
      {
            MessageBox(NULL, "Device Context Release Failed", "SHUTDOWN ERROR", MB_OK | MB_ICONEXCLAMATION);
            m_DC=NULL;
      }
      if (m_hwnd)
      {
            m_hwnd=NULL;
      }
}


GLvoid OpenGL::resize_scene(GLsizei width, GLsizei height)
{
      if (height==0)
            height=1;
      glViewport(0, 0, width, height);
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(45.0f, (GLfloat)width/(GLfloat)height, 0.1f, 100.0f);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
}

int OpenGL::init(HWND hwnd, int width, int height)
{
      m_hwnd = hwnd;
      m_width = width;
      m_height = height;
      glShadeModel(GL_SMOOTH);
      // set background color to black
      glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
      glClearDepth(1.0f);
      glEnable(GL_DEPTH_TEST);
      glDepthFunc(GL_LEQUAL);
      // set nice perspective calculations
      glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);


      // variable for pixle format
      GLuint PixelFormat;

      // set up pixle format descriptor
      static PIXELFORMATDESCRIPTOR pfd=
      {
            sizeof(PIXELFORMATDESCRIPTOR),
            1,
            PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_TYPE_RGBA,
            32, // number of bits
            0, 0, 0, 0, 0, 0,
            0,
            0,
            0,
            0, 0, 0, 0,
            16,
            0,
            0,
            PFD_MAIN_PLANE,
            0,
            0, 0, 0
      };

      // get the device context
      if (!(m_DC = GetDC(hwnd)))
      {
            MessageBox(NULL, "Can't Create a GL Device Context", "ERROR", MB_OK | MB_ICONEXCLAMATION);
            delete this;
            return 0;
      }

      // get the pixel format
      if (!(PixelFormat=ChoosePixelFormat(m_DC, &pfd)))
      {
            MessageBox(NULL, "Can't find a Suitable Pixel Format Check that 32 bit color is enabled", "ERROR", MB_OK | MB_ICONEXCLAMATION);
            delete this;
            return 0;
      }

      // set the pixel format
      if (!SetPixelFormat(m_DC, PixelFormat, &pfd))
      {
            MessageBox(NULL, "Can't Set the Pixel Format", "ERROR", MB_OK | MB_ICONEXCLAMATION);
            delete this;
            return 0;
      }

      // get the render context
      if (!(m_RC=wglCreateContext(m_DC)))
      {
            MessageBox(NULL, "Can't Create a GL Rendering Context", "ERROR", MB_OK | MB_ICONEXCLAMATION);
            delete this;
            return 0;
      }

      // activate render context
      if (!(wglMakeCurrent(m_DC, m_RC)))
      {
            MessageBox(NULL, "Can't Activate GL Rendering Context", "ERROR", MB_OK | MB_ICONEXCLAMATION);
            delete this;
            return 0;
      }
      resize_scene(width, height);
      drawscene();

      return 1;
}

int OpenGL::drawscene(GLvoid)
{
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glLoadIdentity();
      glPushMatrix();
      gluLookAt(3,0,11, 3,0,0, 0,1,0);
      glInitNames();
      if (m_quadcount)
      {
            for (int i=0; i<m_quadcount+1;i++)
            {
                  glLoadName(quads[i].ID);
                  glBegin(GL_QUADS);
                        glColor3f(quads[i].r, quads[i].g, quads[i].b);
                        glVertex3d(quads[i].box.left, quads[i].box.top,0);
                        glVertex3d(quads[i].box.right, quads[i].box.top,0);
                        glVertex3d(quads[i].box.right, quads[i].box.bottom,0);
                        glVertex3d(quads[i].box.left, quads[i].box.bottom,0);
                  glEnd();
            }
      }
      if (m_linecount)
      {
            for (int i=0; i<m_linecount;i++)
            {
                  glLoadName(lines[i].ID);
                  glBegin(GL_LINES);
                        if (!lines[i].color && m_clear_c==0)
                              glColor3f(1.0f,1.0f,1.0f);
                        else if (!lines[i].color && m_clear_c==1)
                              glColor3f(0.0f,0.0f,0.0f);
                        else
                              glColor3f(lines[i].r, lines[i].g, lines[i].b);
                        if (lines[i].x1 < 0 || lines[i].x2 <0)
                              MessageBox(NULL, "x less than 0", "alert", MB_OK);
                        glVertex2d(lines[i].x1, lines[i].y1);
                        glVertex2d(lines[i].x2, lines[i].y2);
                  glEnd();
            }
      }
      glPopMatrix();
      glFlush();

      return 1;
}

void OpenGL::render()
{
      drawscene();
      SwapBuffers(m_DC);
}

void OpenGL::set_clear_color(int val)
{
      m_clear_c = val;
      if (val==0)
      {
            glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            glColor3f(1.0f, 1.0f, 1.0f);
      }
      else if (val==1)
      {
            glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
            glColor3f(0.0f, 0.0f, 0.0f);
      }
}            

void OpenGL::add_line(float x1, float y1, float x2, float y2, int val, float r=0, float g=0, float b=0)
{
      struct line* temp = lines;
      lines = new struct line[m_linecount+1];
      // fill up lines array with old entries and delete old lines array
      for (int i=0;i<m_linecount;i++)
      {
            lines[i] = temp[i];
      }
      delete temp;
      lines[m_linecount].ID = val;
      lines[m_linecount].x1 = x1;
      lines[m_linecount].y1 = y1;
      lines[m_linecount].x2 = x2;
      lines[m_linecount].y2 = y2;
      lines[m_linecount].r = r;
      lines[m_linecount].g = g;
      lines[m_linecount].b = b;
      if (r || g || b)
            lines[m_linecount].color = true;
      else
            lines[m_linecount].color = false;
      m_linecount++;
}

int OpenGL::remove_line(int ID)
{
      int remove_count=0, count=0;
      struct line* temp = lines;
      lines = new struct line[m_linecount-1];

      for (int i=0; i<m_linecount+1;i++)
      {
            if (!(lines[i].ID == ID))
            {
                  lines[count] = temp[i];
                  count++;
                  /*
                  for (int j=i;j<m_linecount+1;j++)
                  {
                        lines[j] = lines[j+1];
                  }
                  remove_count++;
                  m_linecount--;
                  if (m_linecount)
                        lines = (struct line*)realloc(lines, sizeof(line)*m_linecount);
                  */
            }
            else
                  remove_count++;
      }
      delete temp;
      return remove_count;
}

void OpenGL::add_quad(quad toadd)
{
      struct quad* temp=quads;
      quads = new struct quad[m_quadcount+1];
      for (int i=0;i<m_quadcount;i++)
            quads[i] = temp[i];
      //quads = (struct quad*)realloc(quads, sizeof(quad)*(m_quadcount+1));
      quads[m_quadcount].box = toadd.box;
      quads[m_quadcount].ID = toadd.ID;
      quads[m_quadcount].r = toadd.r;
      quads[m_quadcount].g = toadd.g;
      quads[m_quadcount].b = toadd.b;
      m_quadcount++;
}

int OpenGL::remove_quad(int ID)
{
      int remove_count=0, count=0;
      struct quad* temp=quads;
      quads = new struct quad[m_quadcount-1];
      for (int i=0;i<m_quadcount+1;i++)
      {
            if (!(quads[i].ID == ID))
            {
                  quads[count] = temp[i];
                  count++;
                  /*
                  for (int j=i;j<m_quadcount+1;j++)
                  {
                        quads[i] = quads[i+1];
                  }
                  remove_count++;
                  m_quadcount--;
                  if (m_quadcount)
                        quads = (struct quad*)realloc(quads, sizeof(quad)*m_quadcount);
                  */
            }
            else
                  remove_count++;
      }
      delete temp;
      return remove_count;
}
#include <stdio.h>

int OpenGL::check_collision(int x, int y)
{
      int objectcount=0;
      int viewportcoords[4]= {0};

      unsigned int selectbuffer[32] = {0};

      // set the select buffer
      glSelectBuffer(32, selectbuffer);

      // get the coordinates for the viewport
      glGetIntegerv(GL_VIEWPORT, viewportcoords);

      // switch to project matrix mode
      glMatrixMode(GL_PROJECTION);

      // push on a new matrix so I don't effect the back buffer
      glPushMatrix();
            // set the render mode so that what we render isn't actually drawn to screen
            glRenderMode(GL_SELECT);
            // reset the projection matrix
            glLoadIdentity();
            // get the projection matrix for the pick region
            gluPickMatrix(x, viewportcoords[3]-y, 2, 2, viewportcoords);
            // reset the aspect ratio
            gluPerspective(45.0f, (GLfloat)(viewportcoords[2]-viewportcoords[0])/(GLfloat)(viewportcoords[3]-viewportcoords[1]), 0.1f, 100.0f);
            // change to modelview mode
            glMatrixMode(GL_MODELVIEW);
            // render
            drawscene();
            // get the number of objects in the clicked region
            objectcount = glRenderMode(GL_RENDER);
            // change back to projection matrix
            glMatrixMode(GL_PROJECTION);
      // pop the projection matrix
      glPopMatrix();
      glMatrixMode(GL_MODELVIEW);
      return objectcount;
}

void OpenGL::clear_lines()
{
      struct line* temp=lines;
      lines = new line[0];
      delete temp;
      m_linecount = 0;
}

void OpenGL::clear_quads()
{
      struct quad* temp=quads;
      quads = new quad[0];
      delete temp;
      m_quadcount = 0;
}


 
Avatar of adawyn
adawyn

ASKER

I am adding an additional 500 points to this question because no one seems to want to read through all the code... I need a response as soon as possible
Avatar of adawyn

ASKER

I will offer an additional 100 points if someone can tell me why the device context fails to release on deletion
ASKER CERTIFIED SOLUTION
Avatar of itsmeandnobodyelse
itsmeandnobodyelse
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of adawyn

ASKER

Sorry, here are some more specifications.  It is written on WinXP pro using MVC++ 6.0, I am using whatever the included gl.h and glu.h files are (build 4, copyright ending in 1996 if this is helpful, I cannot locate a version number) they were included with MVC++ in the root\include\gl\ folder

I think the problem has something to do with matrixes being modified during one draw and not modified or modified differently on the redraw.  Unfortunately I cannot include the entire project because pieces of it are covered under an NDA.  I can include some more of the code flow though.  The only place where I am modifing the matrixes though is in drawscene and check_collision.  The following is some of the additional code that may help to make clear the program flow.

// Main entrance to application
#include "windowsinterface.h"

extern windowsinterface maininterface;

void get_materials_from_file();

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
                 // disregard this it has nothing to do with the interface or opengl
      get_materials_from_file();

                // this is the entrance into the windows interface and setup of all forms and opengl
      maininterface.go();

      MSG Msg;

      while(GetMessage(&Msg, NULL, 0,0) > 0)
      {
            
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
      }
      return Msg.wParam;
}

int windowsinterface::go()
{
               // here all windows classes for forms are registered (used for defining all seperate wndprocs)
      register_classes();

      // Create main window m_hwnd contains hwnd of main window (class var)
      m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "WINFUSinputMAIN", "WINFUSinput", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1024,738, NULL, NULL, GetModuleHandle(NULL), NULL);
      if (!m_hwnd)
      {
            MessageBox(NULL, "Main Window Creation Failed", "Error", MB_OK | MB_ICONEXCLAMATION);
            return 1;
      }

                // here all other sub windows are created including statics, edits, comboboxes, listboxes and other forms
      setup_layout();

      // init OpenGL (renderwindow is my opengl class object, m_s_render_view.hwnd() returns the statics hwnd, and the calls to .width(-1) and .height(-1) return the windows width and height
      renderwindow.init(m_s_render_view.hwnd(), m_s_render_view.width(-1), m_s_render_view.height(-1));
      
                // 2 squares are drawn here
      setup_grid();

      ShowWindow(m_hwnd, 1);
      UpdateWindow(m_hwnd);

      return 0;
}


// here all objects are defined (I created custom classes for textboxes, statics etc, the init function just simplifies parameters and setups up the classes)   after the first boolean var the x,y coords are given followed by the width and height of the window.  m_s_render_view will be the window where OpenGL is initialized to
void windowsinterface::setup_layout()
{
      char * text = "Help";
      setup_system_font();
      // Main window interface setup
      // top buttons
      m_b_help.init("Help", m_hwnd, IDC_MAIN_HELP, true, 10, 10, 40, 20, false, true);
      m_b_material_colors.init("Material Colors Off", m_hwnd, IDC_MAIN_MATERIAL_COLOR, true, 90, 10, 120, 20, false, true);
      m_b_check_save_quit_run.init("Check/Save/Quit/Run", m_hwnd, IDC_MAIN_CHECK_SAVE_QUIT, true, 220, 10, 160, 20, false, true);
      m_b_zoom.init("Zoom", m_hwnd, IDC_MAIN_ZOOM, false, 10, 40, 40, 20, false, true);
      m_b_zoom_reset.init("Zoom Reset", m_hwnd, IDC_MAIN_ZOOM_RESET, false, 280, 40, 100, 20, false, true);
      // Render view was changed to no longer have client edge
      m_s_render_view.init(NULL, m_hwnd, IDC_MAIN_RENDER_VIEW , true, 390, 0, 620, 730, true, true);
}

// this just adds two boxes on the screen (which would later be filled with a combination of quads, and verticle/horizontal lines)

void windowsinterface::setup_grid()
{
      // for yz view
      renderwindow.add_line(0, 4, 3.5, 4, 0, 0, 0, 0);
      renderwindow.add_line(3.5, 4, 3.5, 0.5, 0, 0, 0, 0);
      renderwindow.add_line(3.5, 0.5, 0, 0.5, 0, 0, 0, 0);
      renderwindow.add_line(0, 0.5, 0, 4, 0, 0, 0, 0);

      // for x view
      renderwindow.add_line(0, -4, 3.5, -4, 0, 0, 0, 0);
      renderwindow.add_line(3.5, -4, 3.5, -0.5, 0, 0, 0, 0);
      renderwindow.add_line(3.5, -0.5, 0, -0.5, 0, 0, 0, 0);
      renderwindow.add_line(0, -0.5, 0, -4, 0, 0, 0, 0);

}

Avatar of adawyn

ASKER

I created a new function that is called right before the windows interface is destroyed that destroys the opengl objects outside of the destructor, and now I successfully release the device context.  100 points are yours, 1000 to go :)
Avatar of adawyn

ASKER

I found the screentoclient command and that seems to fix my problem if I pass it the hwnd of my render window and the point structure containing the coordinates from getcursorpos.  I am awarding you the full 1100 points.  Thanks for all your help!
Avatar of adawyn

ASKER

I can't seem to figure out how to add additional points to you (so far I accepted your answer for the 500) how do I give you the additional 600 points?
That's great that both suggestions have been helpful. ;-)

>> how do I give you the additional 600 points?

The maximum points on EE for one question is 500 points. So, it's ok. I got for that grade 'B' accepted answer 1500 expert points (grade 'A' would have been 2000 points).

If you want to give more points you may post another question named "points for xyz" and post a comment here with the link to new question. Normally, 'xyz' then gives an answer that you may accept (grade 'A' is appreciated).

Regards, Alex