PDA

View Full Version : Problems drawing 2D rects o 3D NSOpenGLView



Duncan Champney
03-18-2008, 07:39 PM
My app uses an NSOpenGLView to draw complex height maps as a shaded polygon mesh.

I've written code to save my 3D view to disk as a JPEG or TIFF image. I use FBOs to do the save so I can generate the output at higher resolution than the current view.

I want the user to be able to draw a "crop rectangle" on top of the 3D image so that the file save saves a cropped portion of the current view. This will also let the user create output at an arbitrary aspect ratio.

Based on a discussion in the beginner's board (I am an OpenGL neophyte) I'm using XOR logic operations to draw to the screen.

Here's what I do. The user selects "crop mode." In crop mode, when i get a mousedown, I do some setup to draw directly to the front buffer, switch to orthographic projection, with the coordinates matching the view coordinates. I then go into a while loop, drawing the selection rectangle as the user drags the mouse around, and un-drawing the old selection rect before drawing the new one.

The selection rectangles are drawing with colors that tell me the color values are being XORed. However, the call to erase the old rectangle before drawing a new one isn't working.

Interestingly, if I draw my rectangle twice in a row, the two draw calls DO cancel out. However, If I draw the rectangle once, do a glFlush(), and wait for the mouse to move before re-drawing it, it doesn't cancel the old drawing.

Am I getting into trouble because my app is set up for double-buffered drawing, and OS X plays games with buffers in order to handle all the OpenGL activity to draw all the rest of the windowing system?

Here is an extract of the code I'm using. First, the routine to set up for 2D XORed drawing: (called once in my mousedown method)

- (void) ortho_setup
{
GLsizei w,h;
NSRect rectView = [self bounds]; //Get bounds of OpenGL view

w = rectView.size.width;
h = rectView.size.height;
[[self openGLContext] makeCurrentContext];
glViewport(0, 0, w, h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, 0, h, 1, -1);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();

glDisable(GL_LIGHTING); //turn off lighting effects

glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(GL_XOR);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}


Then the code to draw the rectangles:


-(void) drawSelectionRect: (NSRect) theSelectionRect
{
if (!NSEqualRects(theSelectionRect, NSZeroRect) )
{
GLint x1 = theSelectionRect.origin.x;
GLint y1 = theSelectionRect.origin.y;
GLint x2 = theSelectionRect.origin.x +
theSelectionRect.size.width;
GLint y2 = theSelectionRect.origin.y +
theSelectionRect.size.height;
glRecti(x1, y1, x2, y2);
//glRecti(x1, y1, x2, y2);//Double call DOES cancel out.
glFlush ();
}
}

OneSadCookie
03-19-2008, 01:13 PM
Why not avoid the logic ops entirely, draw your 3D scene to an FBO or PBuffer, and draw that and the selection rectangle to the screen however you like?

Your back buffer won't be preserved between frames unless you have kCGLPFABackingStore in your pixel format.

You say you're rendering to the front buffer, but expecting that rendering still to be present in the next frame? That doesn't sound likely.

Duncan Champney
03-19-2008, 01:29 PM
Why not avoid the logic ops entirely, draw your 3D scene to an FBO or PBuffer, and draw that and the selection rectangle to the screen however you like?

Your back buffer won't be preserved between frames unless you have kCGLPFABackingStore in your pixel format.

You say you're rendering to the front buffer, but expecting that rendering still to be present in the next frame? That doesn't sound likely.

OneSadCookie,

I would like to use an FBO, but from what I've read I can't rely on them being available. My app is targeted for any Mac running OS X 10.4. I already have the code special-cased so that file save of my 3D view supports saving a 3D image larger than the screen if the FBO extension is available, or just a subset of the current viewport if not.

Now I'm adding UI to let the user select a portion of the viewport to save. If FBOs are supported, the user will be able to save the selected portion at a larger size (and my app will re-render that portion into the FBO at the increased size.) If FBOs are not available, I'll just copy the selected rectangle out of the front buffer.

As far a relying on the front buffer: During "crop mode" I'm turning off UI items that let the user change my 3D view. If the user changes the size of the window or does something else that invalidates the front buffer, I'll just scale the selection rectangle to the new viewport size if needed, then re-draw the selection rectangle on the new view contents.

Is the front buffer for my viewport changed by the system? I thought it was like an NSImage, where it's a view into my rendered contents?

I'm still trying to get my head around how all this works.

Should I turn on the backing store when I set up my app's pixelformat? If I do, what implications does that have for my app? would I need to change anything in my existing code?

Duncan Champney
03-20-2008, 12:46 PM
Folks,

I sent a private message to a member on the beginner board with a sample Glut project that had the same problem. It turns out that I had to disable depth testing in my 2D orthographic drawing. With depth testing turned off, the second time I draw a rectangle, it has the same depth as the first rectangle, so it is not shown.

When I turn off depth testing as part of the setup for my 2D selection rectangles in the test project, it works!

For other newbies like me, the call is

glDisable(GL_DEPTH_TEST
//2D drawing goes here.
glEnable(GL_DEPTH_TEST);


My full setup routine for 2D drawing (on top of a viewport set up for 3D perspective drawing) is:

- (void) ortho_setup
{
GLsizei w,h;
NSRect rectView = [self bounds]; //Get the bounds of the OpenGL view

w = rectView.size.width;
h = rectView.size.height;
[[self openGLContext] makeCurrentContext];
glViewport(0, 0, w, h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, 0, h, 1, -1);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();

glDisable(GL_LIGHTING); //turn off lighting effects
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(GL_XOR);
glDisable(GL_DEPTH_TEST);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

//Save the current buffer target
glGetIntegerv(GL_DRAW_BUFFER, &last_write_buffer);
glDrawBuffer(GL_FRONT); //set the drawbuffer to the front readbuffer.
}