Solved

2D object picking with OpenGL

Posted on 2004-08-13
8
1,313 Views
Last Modified: 2008-01-09
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;
}


 
0
Comment
Question by:adawyn
  • 6
  • 2
8 Comments
 

Author Comment

by:adawyn
ID: 11796190
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
0
 

Author Comment

by:adawyn
ID: 11796289
I will offer an additional 100 points if someone can tell me why the device context fails to release on deletion
0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 500 total points
ID: 11797557
The problem is not the points and not the amount of code.

Could you tell where you think your prog isn't working properly?  

>> It tells me I have selected an object when the object is about an inch below where I clicked.  

Where is the code that tells you any wrong object?
What OpenGl library are you using?
Is there a chance to get the whole project? What is your platform/compiler?

>> about an inch below where I clicked.

That sounds like mixing screen coordinates and client coordinates. The latter are relative to the active window and you may get screen coordinates by adding top-left corner of the client window.


>> why the device context fails to release on deletion

That's another problem, isn't it? Does ReleaseDC() fail?

I cannot see where OpenGL::init is called. But a (valid) call to GetDC() is needed if you want to call ReleaseDC, Also, the destructor might be a little late to release resources as destructing an object is the very last operation and releasing resources normally is done in the same fuction that is requesting the resource.


Regards, Alex
0
 

Author Comment

by:adawyn
ID: 11797906
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);

}

0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 

Author Comment

by:adawyn
ID: 11797940
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 :)
0
 

Author Comment

by:adawyn
ID: 11800402
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!
0
 

Author Comment

by:adawyn
ID: 11800421
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?
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 11800540
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
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now