PDA

View Full Version : Rubberband rectangle and local coordinates with MFC context being removed



EricMar
06-08-2013, 02:41 PM
( quick why)
I have a GDI+ rubberband or sweep rectangle that I have been using with OPengl and a MFC application. BUT as some have you already know, if you are using OPengl and GDI+ (which I was/is) .. Win7 aero or Win8 aero Desktop Window Manager (DWM) gets VERY mad at you where the sweept rectangle flickers like crazy if Aero is on.

So to remove GDI+ I created a opengl rubber band rectangle XOR and front buffer, but that is having 2 issue's:

Issues:
1. I cannot get the sweep rectangle to track my mouse curser of the box select , keep in mind I have created a 3d environment. What it wants to do is draw it in client cooridinates inside of OpenGL instead of the client rect of the opengl view. I want it to behave like standard CAD zooming/selection where I can have the 3d environment rotated but it draws to the screen like a standard zoom/selection rectangle

see picture below
1051

2. The rubberband rectangle draws with the XOR and I cannot get it to draw just with a standard zoom / selection window color ie white



LButtonDown : Initialize Sweep rectangle
MouseMove : SweepingDrawRect
LButtonUp : De-initilize SweepRectangle

Code sample:

void CGLView::InitializeDrawRectangle( int linestyle)
{
if(m_bSweepRectInitilized) return;
//get client rectangle
CRect rectClient;
::GetClientRect(m_hWnd,&rectClient);

GLsizei w, h;
w = rectClient.Width();
h = rectClient.Height();


// set drawing mode to front-buffer, etc
glDrawBuffer(GL_FRONT);


glDisable(GL_DEPTH_TEST);
glLogicOp(GL_XOR);
glEnable(GL_COLOR_LOGIC_OP);

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_DEPTH_TEST);


// save the current projection matrix and set up a new convenient projection matrix


if (h==0)
h=1;
if(w==0)
w=1;


int viewport[4];

glGetIntegerv(GL_VIEWPORT, viewport);

glPushMatrix();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0.0, viewport[2], 0.0, viewport[3]);

glViewport(0,0,w,h);


// gluOrtho2D(0, rectClient.Width(), 0, rectClient.Height());*/


// gluOrtho(0, winWidth, 0, winHeight, -1, 1);
// gluOrtho2D(0, winWidth, 0, winHeight);
/* if (w <= h)
::glOrtho (-nRange, nRange, -(nRange*h/w), (nRange*h/w), -(nRange*10000.0f), (nRange*10000.0f));
else
::glOrtho (-(nRange*w/h), (nRange*w/h), -nRange, nRange, -(nRange*10000.0f), (nRange*10000.0f));*/



glMatrixMode (GL_MODELVIEW);
glLoadIdentity();

glDisable(GL_LIGHTING); //turn off lighting effects

// glOrtho(0, rectClient.Width(), 0, rectClient.Height(), -1, 1);
// gluOrtho2D (0.0, (GLdouble) winWidth, 0.0, (GLdouble) winHeight);
// glViewport(-1, -1, rectClient.Width() + 2, rectClient.Height() + 2);

// save the current model view matrix
m_bSweepRectInitilized=1;

}



void CGLView::SweepingDrawRect(HDC hDC, int x1, int y1, int x2, int y2)
{
wglMakeCurrent(m_hgldc, m_hrc);

glDrawBuffer(GL_FRONT);
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(GL_XOR);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_DEPTH_TEST);// make sure you do this to get rid of flicker

// Perform the logic rendering
glLineWidth(2.0);
if(x1 < x2)
{
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
}
else
{
glColor4f(0.0f, 0.2f, 1.0f, 0.5f);
}

// CRect rectClient;
// ::GetClientRect(m_hWnd,&rectClient);

int viewport[4];

glGetIntegerv(GL_VIEWPORT, viewport);

// OpenGL window coordinates are different from GDI's
glRectd(x1, viewport[3]-y1,x2, viewport[3]-y2);


glDisable(GL_COLOR_LOGIC_OP);

wglMakeCurrent(NULL, NULL);
glDrawBuffer(GL_BACK);

glFlush(); // must flush here
}

It works pretty good with the exception of the above..

tonyo_au
06-11-2013, 01:21 AM
gluOrtho2D(0.0, viewport[2], 0.0, viewport[3])
Why are you using the old viewport for the current projection? If you are drawing in screen coordinates wont you want the current screen size.

Also in OpenGL I would not be using XOR at all just redraw the view and then draw the selection rect last.

EricMar
06-11-2013, 08:28 AM
That is my issue, I was trying to draw to screen coordinates but I couldn't get that too work...
unless I'm missing something...

I assume that I can do a:

// glOrtho(0, rectClient.Width(), 0, rectClient.Height(), -1, 1);
// gluOrtho2D (0.0, (GLdouble) winWidth, 0.0, (GLdouble) winHeight);

but I couldn't get either of those to work...

So ultimately how do you set OpenGL to screen coordinate mode?

Wouldn't doing redrawing the entire view, work but if you have ton of stuff on the screen wouldn't that take to much time and cause flicker...?

tonyo_au
06-11-2013, 06:47 PM
first gluOrtho2D and glOrtho( are mutually exclusive since the both initialize the same matrix. gluOrtho2D is for drawing in 2D in screen coordinates. If your redraw is really slow you can capture the screen to a buffer and just blit that buffer instead of redrawing the whole screen but then you have a problem with pan and zoom (the same problem exists with the XOR method). If you use double buffering you won't have a flicker problem.

EricMar
06-11-2013, 07:38 PM
I added the gluOrtho2D(0, w, 0, h); to the initialize function

It does draw the rectangle it is some set value off of the cursor??? very odd... It seems to stay in the X+, Y+ zone
1055
It does redraw itself and erase itself but that doesn't track the cursor and if I rotate the screen off as well...

tonyo_au
06-11-2013, 11:10 PM
What is your viewport? And what coordinate system is the mouse cursor in?

GClements
06-12-2013, 07:34 AM
So ultimately how do you set OpenGL to screen coordinate mode?
There are three transformations involved: model-view, projection, viewport.
If you want to draw in "pixel coordinates", the usual approach is to set the model-view matrix to the identity matrix, and the projection matrix to either gluOrtho2D(0, w, 0, h) or gluOrtho2D(0, w, h, 0) depending upon whether you want the origin at the bottom-left or top-left. That basically sets the projection transformation to the inverse of the viewport transformation, so that the combination of the three is an identity transformation.


Wouldn't doing redrawing the entire view, work but if you have ton of stuff on the screen wouldn't that take to much time and cause flicker...?
It won't cause flicker provided that you have double-buffering enabled. Whether it takes too much time depends upon the complexity of the scene, the performance of the video hardware, and the efficiency of the code. If it's too slow, you can cache the rendered scene in a framebuffer object (provided that you're using OpenGL 3 or later, or have either the GL_EXT_framebuffer_object or GL_ARB_framebuffer_object extension; with older versions, you needed to use a platform-specific mechanism to get an off-screen rendering surface).

EricMar
06-12-2013, 09:34 AM
I assume by viewport you mean size ?

I just have it set to

CRect rectClient;
::GetClientRect(m_hWnd,&rectClient);

GLsizei w, h;
w = rectClient.Width();
h = rectClient.Height();

glViewport(0,0,w,h);
gluOrtho2D(0, w, 0, h);

The mouse coordinates come from The OnMouseMove from windows and I just pass in what the point is from
void CTestOpenGLView::OnMouseMove(UINT nFlags, CPoint point)

EricMar
06-12-2013, 09:39 AM
OK, I seemed to have it now tracking the mouse ONLY if I don't do nothing to the view, if pan the screen or if I rotate the screen then it again loses the mouse cursor...

GClements
06-12-2013, 03:03 PM
OK, I seemed to have it now tracking the mouse ONLY if I don't do nothing to the view, if pan the screen or if I rotate the screen then it again loses the mouse cursor...
Coordinates passed to glVertex() are transformed by both the model-view matrix and the projection matrix. If you want the coordinates to correspond to window pixel coordinates, the projection matrix needs to be set with gluOrtho2D() and the model-view matrix needs to be the identity matrix. E.g.:


draw_scene();
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, w, 0, h);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
draw_rectangle();

If you need to preserve the matrices, you need to push and pop both of them, but you're usually better off setting them both from scratch (i.e. starting with glLoadIdentity()) at the start of rendering.
Also, avoid using glGetIntegerv(GL_VIEWPORT, ...), as glGet() causes a pipeline stall. Instead, store the viewport dimensions whenever you call glViewport(), and use the stored values.

EricMar
06-13-2013, 06:33 PM
OK, after messing with this and thanks to the general idea of GClements

I killed my initialization of the Draw rectangle and put it directly into the on Mousemove command

I just pass in the mouse start and end point

I had to make the dc current so this code worked! Thanks guys!

void CGLView::SweepingDrawRect( int x1, int y1, int x2, int y2)
{

wglMakeCurrent(m_hgldc, m_hrc);

glDrawBuffer(GL_FRONT);
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(GL_XOR);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_DEPTH_TEST);

//save the projection matrix and the model matrix by putting no ones on stack...
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, winWidth, 0, winHeight);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

// Perform the logic rendering
glLineWidth(2.0);
if(x1 < x2)
{
glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
}
else
{
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
}


// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

glRecti(x1,winHeight-y1,x2,winHeight-y2);
// glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);



// restore the old modelview and projection matrices
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();

glDisable(GL_COLOR_LOGIC_OP);
wglMakeCurrent(NULL, NULL);

}

Then on DeleteDrawRect() <- called on LButtonUP
{
glDisable(GL_COLOR_LOGIC_OP);// just to make sure...


// restore all the drawing modes
glDrawBuffer(GL_BACK);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);

wglMakeCurrent(NULL, NULL);//just to make sure
}