-1

Please tell me if the question's vogue, I need the answe as soon as possible

for more information about the problem you can refer to this. I just didn't understand how to manage buffers properly.

A red rectangle drawn on 2D texture disappears right after being drawn

Being in the last stages of customizing the class COpenGLControl:
I have created two instances of the class in my MFC Dialog:
enter image description here
whenever the extent of zoom is changed in the bigger window, it is drawn as a red rectangle on the smaller window. which is always in full extent mode. In order to stablish such a relation between two instances, I have used the concept of user defined messages and send the message to the parent of the class.

The main trouble
based on the information above:

1- when I pan in the bigger window (mean I cause the user defined message be sent rapidly and m_oglWindow2.DrawRectangleOnTopOfTexture() be called rapidly I see a track of red rectangles shown but immediately disappeared in the smaller window

2- CPU-usage immediately get's high when panning from 3% to 25%

3- In other navigation tasks liked Fixed zoom in,Fixed zoom out,Pan and etc a red rectangle flashes and then immediately disappears, I mean it seems that the red rectangle is there just when the control is in the function m_oglWindow2.DrawRectangleOnTopOfTexture() but I want the rectangle be there until the next call off m_oglWindow2.DrawRectangleOnTopOfTexture()

4- making calls to glDrawBuffer(GL_FRONT_AND_BACK) and glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) causes the texture in the smaller window to get off and on even if the mouse is idle

I know the problem is most because of the lines glClear, glDrawBuffer, SwapBuffers in the following codes. But I don't know exactly how to solve

void COpenGLControl::OnTimer(UINT nIDEvent)
{
wglMakeCurrent(hdc,hrc);
switch (nIDEvent)
{
    case 1:
    {
        // Clear color and depth buffer bits
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Draw OpenGL scene
        oglDrawScene();

        // Swap buffers
        SwapBuffers(hdc);

        break;
    }

    default:
        break;
}

CWnd::OnTimer(nIDEvent);
wglMakeCurrent(NULL, NULL);
}  

void COpenGLControl::DrawRectangleOnTopOfTexture()
{
wglMakeCurrent(hdc, hrc);
//glDrawBuffer(GL_FRONT_AND_BACK);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushAttrib(GL_ENABLE_BIT|GL_CURRENT_BIT);
glDisable(target);
glColor3f(1.0f,0.0f,0.0f);
glBegin(GL_LINE_LOOP);
   glVertex2f(RectangleToDraw.at(0),RectangleToDraw.at(1));
   glVertex2f(RectangleToDraw.at(0),RectangleToDraw.at(3));
   glVertex2f(RectangleToDraw.at(2),RectangleToDraw.at(3));
   glVertex2f(RectangleToDraw.at(2),RectangleToDraw.at(1));
glEnd();
glPopAttrib();
SwapBuffers(hdc);
wglMakeCurrent(NULL, NULL);
}  

void COpenGLControl::OnDraw(CDC *pDC)
{
// TODO: Camera controls
wglMakeCurrent(hdc,hrc);
glLoadIdentity();
gluLookAt(0,0,1,0,0,0,0,1,0);
glTranslatef(m_fPosX, m_fPosY, 0.0f);
glScalef(m_fZoom,m_fZoom,1.0);
wglMakeCurrent(NULL, NULL);
}  

Remember:

OnDraw function is just called twice in the smaller window first when initializing the window and second when calling m_oglWindow2.ZoomToFullExtent() and then for each call of OnDraw in the bigger window, there's a call to the DrawRectangleOnTopOfTexture() in the smaller one but this function DrawRectangleOnTopOfTexture() is never called in the bigger window

It'll be favor of you if:

  • you correct my code

  • introduce me an excellent tutorial on how to use buffers in multiple drawings that can not be done in a single function or a single thread (An excellent tutorial about buffers eg.color buffers and etc in opengl

------------------------------------------------------------------------------------------ -----------------------------------------------------------------------------------------

I just added explanations below to provide further information about how's the class is working if it's needed. If you think it's bothering viewers just edit it to remove which of the parts that you feel is not required. But please do help me.

the oglInitialize sets initial parameters for the scene:

void COpenGLControl::oglInitialize(void)
{
// Initial Setup:
//
static PIXELFORMATDESCRIPTOR pfd =
{
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    32, // bit depth
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    24, // z-buffer depth
    8,0,PFD_MAIN_PLANE, 0, 0, 0, 0,
};

// Get device context only once.
hdc = GetDC()->m_hDC;
// Pixel format.
m_nPixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, m_nPixelFormat, &pfd);
// Create the OpenGL Rendering Context.
hrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hrc);
// Basic Setup:
//
// Set color to use when clearing the background.
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearDepth(1.0f);
// Turn on backface culling
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
// Turn on depth testing
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// Send draw request
OnDraw(NULL);
wglMakeCurrent(NULL, NULL);
}  

example of a navigation task:

PAN:

void COpenGLControl::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (WantToPan)
{
    if (m_fLastX < 0.0f && m_fLastY < 0.0f)
    {
        m_fLastX = (float)point.x;
        m_fLastY = (float)point.y;
    }
    int diffX = (int)(point.x - m_fLastX);
    int diffY = (int)(point.y - m_fLastY);
    m_fLastX = (float)point.x;
    m_fLastY = (float)point.y;
    if (nFlags & MK_MBUTTON)
    {
        m_fPosX += (float)0.05f*m_fZoomInverse*diffX;
        m_fPosY -= (float)0.05f*m_fZoomInverse*diffY;
    }
    if (WantToSetViewRectangle)
        setViewRectangle();
    OnDraw(NULL);
}
CWnd::OnMouseMove(nFlags, point);
}     

the most important part: before calling OnDraw in each of the navigation functions if the client-programmer has set WantToSetViewRectangle as true means that he wants the view rectangle for the window to be calculated and calls setViewRectangle() which is as follows. It sends a message to the parent in case of an update for ViewRectangle:

void COpenGLControl::setViewRectangle()
{
CWnd *pParentOfClass = CWnd::GetParent();
ViewRectangle.at(0) = -m_fPosX - oglWindowWidth*m_fZoomInverse/2;
ViewRectangle.at(1) = -m_fPosY - oglWindowHeight*m_fZoomInverse/2;
ViewRectangle.at(2) = -m_fPosX + oglWindowWidth*m_fZoomInverse/2;
ViewRectangle.at(3) = -m_fPosY + oglWindowHeight*m_fZoomInverse/2;
bool is_equal = ViewRectangle == LastViewRectangle;
if (!is_equal)
    pParentOfClass ->SendMessage(WM_RECTANGLECHANGED,0,0);
LastViewRectangle.at(0) = ViewRectangle.at(0);
LastViewRectangle.at(1) = ViewRectangle.at(1);
LastViewRectangle.at(2) = ViewRectangle.at(2);
LastViewRectangle.at(3) = ViewRectangle.at(3);
} 

this is how we use the class in the client code:

MyOpenGLTestDlg.h

two instances of class:

COpenGLControl m_oglWindow;
COpenGLControl m_oglWindow2;  

MyOpenGLTestDlg.cpp

apply texture on the windows and set both of them to full extent in the OnInitDlg

    m_oglWindow.pImage = m_files.pRasterData;
    m_oglWindow.setImageWidthHeightType(m_files.RasterXSize,m_files.RasterYSize,m_files.eType);
    m_oglWindow.m_unpTimer = m_oglWindow.SetTimer(1, 1, 0);  

    m_oglWindow2.pImage = m_files.pRasterData;
    m_oglWindow2.setImageWidthHeightType(m_files.RasterXSize,m_files.RasterYSize,m_files.eType);
    m_oglWindow2.m_unpTimer = m_oglWindow2.SetTimer(1, 20, 0);  
    m_oglWindow2.ZoomToFullExtent();
    m_oglWindow.ZoomToFullExtent();  

want pan, zoomtool and setViewRectangle be active for the bigger window but not for the smaller one:

m_oglWindow.WantToPan = true;
m_oglWindow.WantToUseZoomTool = true;
m_oglWindow.WantToSetViewRectangle = true;  

handling the user-defined message in the parent. exchange the ViewRectangle data to the smaller window and draw the red rectangle:

LRESULT CMyOpenGLTestDlg::OnRectangleChanged(WPARAM wParam,LPARAM lParam)
{
m_oglWindow2.RectangleToDraw = m_oglWindow.ViewRectangle;
m_oglWindow2.DrawRectangleOnTopOfTexture();
return 0;
}  

Here's the full customized class if you're interested in downloading it and fix my problem.

Community
  • 1
  • 1
Sepideh Abadpour
  • 2,550
  • 10
  • 52
  • 88
  • 2
    Is there any way you could post a little less code to go through? – thokra Aug 30 '13 at 14:45
  • well I said I'm sure the main trouble is in the code posted under the horizontal line. I just added them to say that for example when `OnDraw` is called and etc – Sepideh Abadpour Aug 30 '13 at 14:55

1 Answers1

0

The problem is that you're drawing on a timer and when your application receives a WM_PAINT message. MFC invokes your OnDraw (...) callback when it needs to repaint the window, you should move ALL of your drawing functionality into OnDraw (...) and call OnDraw (...) from your timer function.

void COpenGLControl::OnTimer(UINT nIDEvent)
{
  switch (nIDEvent)
  {
    case 1:
    {
      OnDraw (NULL);
      break;
    }

    default:
      break;
  }

  CWnd::OnTimer(nIDEvent);
}

void COpenGLControl::OnDraw(CDC *pDC)
{
  // TODO: Camera controls
  wglMakeCurrent(hdc,hrc);

  // Clear color and depth buffer bits
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glLoadIdentity ();
  gluLookAt      (0,0,1,0,0,0,0,1,0);
  glTranslatef   (m_fPosX, m_fPosY, 0.0f);
  glScalef       (m_fZoom,m_fZoom,1.0);

  // Draw OpenGL scene
  oglDrawScene();

  // Swap buffers
  SwapBuffers(hdc);

  wglMakeCurrent(NULL, NULL);
}

void COpenGLControl::DrawRectangleOnTopOfTexture()
{
  glPushAttrib(GL_ENABLE_BIT|GL_CURRENT_BIT);
  glDisable(target);
  glColor3f(1.0f,0.0f,0.0f);
  glBegin(GL_LINE_LOOP);
    glVertex2f(RectangleToDraw.at(0),RectangleToDraw.at(1));
    glVertex2f(RectangleToDraw.at(0),RectangleToDraw.at(3));
    glVertex2f(RectangleToDraw.at(2),RectangleToDraw.at(3));
    glVertex2f(RectangleToDraw.at(2),RectangleToDraw.at(1));
  glEnd();
  glPopAttrib();
}

And only ever make calls to wglMakeCurrent (...) within OnDraw (...). This function is really meant for situations where you're rendering into multiple render contexts, or drawing using multiple threads.

Andon M. Coleman
  • 42,359
  • 2
  • 81
  • 106
  • have you noticed that OnDraw is not automatically runned by MFC. Because it's not included in the MESSAGE_MAP and I've just created it manually also the code for WM_PAINT IS {ValidateRect(NULL)} – Sepideh Abadpour Aug 31 '13 at 03:08
  • also you mean there's no need to take the `DrawRectangleOnTopOfTexture()` into the OnTimer? the `DrawRectangleOnTopOfTexture()` is called through a user defined handler in the parent – Sepideh Abadpour Aug 31 '13 at 03:13
  • 1
    Draw everything all at once, is what I'm saying here. Clear the color/depth buffer { draw X, draw Y, draw Z } swap buffers. If you have to call multiple functions to draw everything you want, they should all happen inbetween the time you cleared the buffers and swapped them in `OnDraw (...)` and nothing but `OnDraw (...)` should be swapping buffers, or calling `wglMakeCurrent (...)` – Andon M. Coleman Aug 31 '13 at 03:17
  • but you haven't made `DrawRectangleOnTopOfTexture()` into the OnDraw and also OnDraw is not a drawing function it just makes some transfers on the model – Sepideh Abadpour Aug 31 '13 at 03:20
  • 1
    If you have a user-defined handler in the parent window, make it re-draw everything. Change some state and make it call your control's OnDraw (...) function. You cannot draw things partially in a double buffered system, you basically have to draw everything and ***THEN*** swap buffers. – Andon M. Coleman Aug 31 '13 at 03:20
  • well it's the thing that I would like to say don't you think it is better to call the both `oglDrawScene` and `DrawRectangleOnTopOfTexture()` in the OnTimer function and then call `OnTimer` everytime from the userdefined message handler in the parent and then leave the `OnDraw` as it was? – Sepideh Abadpour Aug 31 '13 at 03:25
  • I'll delete and mark the parts that helped me alot but last thing is about your point for `wglMakeCurrent` I have two ogl windows I learned [here](http://stackoverflow.com/questions/12227586/opengl-two-different-3d-rendering-picture-control-on-single-mfc-dialog-not-work) that I should use this in every function that has used commands with gl prefix. – Sepideh Abadpour Aug 31 '13 at 03:38
  • 1
    Right, `wglMakeCurrent (...)` is very important to call when you have two controls. But you really only need to call it when you start/finish rendering one, and not within the little functions that only do one or two things (like draw a rectangle over the texture). You also should only be swapping buffers in one location, at the very end of all of your drawing commands. – Andon M. Coleman Aug 31 '13 at 03:41
  • 1
    OK that's it. It works perfectly.thanks for your attention. I call both `oglDrawScene` and `DrawRectangleOnTopOfTextures` in `OnTimer` and call the `OnTimer` within the message handler. As you said **You cannot draw things partially in a double buffered system, you basically have to draw everything and THEN swap buffers** – Sepideh Abadpour Aug 31 '13 at 03:43