Problem with glScissors

Started by
20 comments, last by mldaalder 18 years, 4 months ago
Hello everyone, I'm having a slight problem with glScissors, I'm trying to make a basis for my GUI for a template project I'm trying to make. I'm using glScissors to clip the child GUI nodes to fit in their parents. This is result of the code below: This is how it looks like without glScissors enabled: Free Image Hosting at www.ImageShack.us And this is how it should look like: Free Image Hosting at www.ImageShack.us What you see here is that the red rectangle is the root node (for testing I've made it smaller, but normally this would be screen sized and with the colour: 0,0,0,0). The green quad is the first child of the root it has the same dimensions (0.5:0.05) and is offsetted the same as the root (except that it is relative to the root instead of the screen). The blue quad is the grand child of root, it has the same size, but it is offsetted by -0.15:-0.15 so it should disappear partly at the left and top. Last but not least, here is my Node render source:
void GUINode::Render()
{
  // Draw current GUI
  glTranslated(x,y,0);
  glBegin(GL_QUADS);

  glColor4d(r,g,b,a);

  glVertex2d(w,0);
  glVertex2d(0,0);
  glVertex2d(0,h);
  glVertex2d(w,h);

  glEnd();

  // Retrieve viewport and scissor size
  GLint* viewport = new GLint[4];
  GLint* scissor  = new GLint[4];
  glGetIntegerv(GL_VIEWPORT, viewport);
  glGetIntegerv(GL_SCISSOR_BOX, scissor);

  // Calculate new scissor size (if it would be the root node)
  GLint nX,nY,nW,nH;
  nX = (GLint)(scalar(viewport[2])*x)+scissor[0];
  nY = (GLint)(scalar(viewport[3])*y)+scissor[1];
  nW = (GLint)(scalar(viewport[2])*w);
  nH = (GLint)(scalar(viewport[3])*h);

  std::cout<<"Requested:"<<nX<<":"<<nY<<":"<<nW<<":"<<nH<<std::endl;
  std::cout<<"Got:      "<<(scissor[0] > nX ? scissor[0] : nX)<<":"<<
                           (scissor[1] > nY ? scissor[1] : nY)<<":"<<
                           (scissor[2] < nW ? scissor[2] : nW)<<":"<<
                           (scissor[3] < nH ? scissor[3] : nH)<<std::endl;

  // Make the scissor size always smaller
  /* This means that the X and Y values can only get bigger
   * And that the W and H values can only get smaller*/
  glScissor((scissor[0] > nX ? scissor[0] : nX),
            (scissor[1] > nY ? scissor[1] : nY),
            (scissor[2] < nW ? scissor[2] : nW),
            (scissor[3] < nH ? scissor[3] : nH));

  // Go through the child nodes and render them into the visible space of this node
  BOOST_FOREACH(boost::shared_ptr<GUINode>& node, children)
    node->Render();
  // Reset translation for the siblings
  glTranslated(-x,-y,0);
  // Reset scissor for the siblings
  glScissor(scissor[0],scissor[1],
            scissor[2],scissor[3]);
  delete[] viewport;
  delete[] scissor;
}
This is the code that starts the rendering:
void GUIManager::Render()
{
  glPushAttrib(GL_ALL_ATTRIB_BITS);

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  glOrtho(0.0f,1.0f,1.0f,0.0f,-1.0f,1.0f);


  glDisable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glEnable(GL_SCISSOR_TEST);

  GLint* viewport = new GLint[4];
  glGetIntegerv(GL_VIEWPORT, viewport);
  glScissor(viewport[0],viewport[1],
            viewport[2],viewport[3]);
  delete[] viewport;
  std::cout<<"Starting"<<std::endl;

  root->Render();

  glMatrixMode(GL_PROJECTION);
  glPopMatrix();

  glMatrixMode(GL_MODELVIEW);
  glPopMatrix();

  glPopAttrib();
}
Here is root a GUINode to which all the other nodes are attached to. I'm staring the problem in the face and I can't find it... I just hope that a fresh pair of eyes will spot the error. Thank you for your time.
Advertisement
Just a guess, but nY seems to be calculated wrong. y=0 is the top of your screen because you flipped the view vertically in glOrtho. Scissor[1] is the bottom of your scissor region though. So try negating y.

I think if you flipped that red box horizontally long the center line you would see the green box is clipped along it's top edge. I would guess the logic for setting the scissor, i.e. "?:", is also wrong and causing the clipping on the blue box to be offset. Correcting nY may correct that though.
Keys to success: Ability, ambition and opportunity.
My coordinate system is set correctly (ie. as I wanted it).

The top left corner is 0:0, this is the same as in the clip region, right?
Or is it that the lower left is 0:0 when it comes to clip regions?
I always thought it was the top left, since all the other window coordinate things that I have done is this as well (same screen coordinates, I mean hardware wise here).


I've tried what you suggested, and negating nY is making it worse.

But deducting scissor[1] from nY made the blue box clip correctly.
Just not the green box.


My ?: is correct, as far as I saw it, since the clip region should only get smaller.

The X value of the clip region should either get larger or stay the same.
The Y value of the clip region should either get larger or stay the same.
The W value of the clip region should either get smaller or stay the same.
The H value of the clip region should either get smaller or stay the same.

This results for the X value:
if clipregion.x > nX then  Use clipregion.xelse  Use nXif clipregion.y > nY then  Use clipregion.yelse  Use nYif clipregion.w < nW then  Use clipregion.welse  Use nWif clipregion.h < nh then  Use clipregion.helse  Use nH

Or am I missing here something?


I'll try and see if glScissor is looking from the lower left instead of the (by me) expected upper left.
I use the top left corner as my origin, and I use glScissor to clip my gui to good effect.

I start work in 2d from that origin with:
void		OpenGL::_orthoStart( void ){	glDisable(GL_DEPTH_TEST);							// Disables Depth Testing	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix	glPushMatrix();										// Store The Projection Matrix	glLoadIdentity();									// Reset The Projection Matrix	int vPort[4];	glGetIntegerv(GL_VIEWPORT, vPort);	glOrtho(0, vPort[2], vPort[3], 0, -1, 1);	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix	glPushMatrix();										// Store The Modelview Matrix	glLoadIdentity();									// Reset The Modelview Matrix}


And I change back with:
void		OpenGL::_orthoEnd( void ){	glPopAttrib();										// Pops The Display List Bits	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix	glPopMatrix();										// Restore The Old Projection Matrix	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix	glPopMatrix();										// Restore The Old Projection Matrix	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing}


I set the scissor rect with:
void	OpenGL::setClipRect( int x, int y, int w, int h ){	m_dwClipX = x;	m_dwClipY = y;	m_dwClipW = w;	m_dwClipH = h;	int j = m_iHeight - y - h;	glScissor( x, j, w, h );}


Where x, y, w, h are the position and dimension of the clip rect, relative to the top left, and m_iHeight is the height of the OpenGL window. glScissor's arguements are specified relative to the bottom left corner if you read the documentation.

I'd also like to point out that using new/delete in your source for getting the viewport/scissor rect is probably not a very good idea.

Regarding your second post about the clip rect only getting smaller, you are right, it should. I accomplish this by having a stack of rects attached to the render. It starts off each frame with a rect in it the size of the OpenGL window. Each window that is clipped adds a rect to the stack. Adding a rect to this stack automatically adjusts the rect being added to fit inside what is ever at the top. This is all done before I get to a gui windows on_paint call. The gui rendering code loops through all windows, for each one it gets the windows dimensions, pushs a rect onto the clipping stack, sets the scissor rect to what is on the top of the clipping stack, and calls the windows paint method. The windows paint method calls the windows on_paint method which actually draw the window. The paint method then loops through all the child windows of that window, getting their dimensions, pushing a clip rect etc etc.
No, window coordinates are relative to the bottom left of the screen.
Keys to success: Ability, ambition and opportunity.
Thank you all for your help, and it is nearly correct.

There appears to be a special case where it doesn't work correctly, that is when it a child node is needs to be translated with a negative value on the y axis (the blue rectangle).

Free Image Hosting at www.ImageShack.us

Here is my current code:
void GUINode::Render(){  // Draw current GUI  glTranslated(x,y,0);  // Render the component, doing it this way ensures us that the end user won't "forget" to do certain things  this->RenderComponent();  // Retrieve viewport and scissor size  GLint* viewport = new GLint[4];  GLint* scissor  = new GLint[4];  glGetIntegerv(GL_VIEWPORT, viewport);  glGetIntegerv(GL_SCISSOR_BOX, scissor);  // Calculate new scissor size (if it would be the root node)  GLint nX,nY,nW,nH;  nX = (GLint)(scalar(viewport[2])*x)+scissor[0];  nY = (GLint)(scalar(viewport[3])*y);//+scissor[1];// The +scissor[1] isn't making any diffrence?  nW = (GLint)(scalar(viewport[2])*w);  nH = (GLint)(scalar(viewport[3])*h);  GLint nYtoUse = viewport[3] - nY - nH;  //std::cout<<nYtoUse<<":"<<scissor[1]<<std::endl;  //std::cout<<nH<<":"<<scissor[3]<<std::endl;  /*std::cout<<"Requested:"<<nX<<":"<<nY<<":"<<nW<<":"<<nH<<std::endl;  std::cout<<"Got:      "<<(scissor[0] > nX ? scissor[0] : nX)<<":"<<                           (scissor[1] > nY ? scissor[1] : nY)<<":"<<                           (scissor[2] < nW ? scissor[2] : nW)<<":"<<                           (scissor[3] < nH ? scissor[3] : nH)<<std::endl;*/  // Make the scissor size always smaller  /* This means that the X and Y values can only get bigger   * And that the W and H values can only get smaller*/  glScissor((scissor[0] > nX ? scissor[0] : nX),            (scissor[1] > nYtoUse ? scissor[1] : nYtoUse),//nY),            (scissor[2] < nW ? scissor[2] : nW),            (scissor[3] < nH ? scissor[3] : nH));  // Go through the child nodes and render them into the visible space of this node  BOOST_FOREACH(boost::shared_ptr<GUINode>& node, children)    node->Render();  // Reset translation for the siblings  glTranslated(-x,-y,0);  // Reset scissor for the siblings  glScissor(scissor[0],scissor[1],            scissor[2],scissor[3]);  delete[] viewport;  delete[] scissor;}


There are some things I find odd, such as that when I calculate nY I can leave out the +scissor[1] if I would want to, it doesn't make a diffrence, at all.

I must have overread the part of starting at the lower left corner, or it was just late when I read it.:P

@Bezben: Now I am curious, why is it better not to use new and delete to get the values? It asks for a pointer, which I give.
I do know that arrays are also pointers, I just like to know what I allocate and deallocate and when I do it.
So are there any specific reasons to use GLint scissor[4] instead of GLint* scissor = new GLint[4]?
The +scissor[1] makes no difference because it is probably zero at that point.

A cursorary glance at your code suggests this:

void GUINode::Render(){  // Draw current GUI  glTranslated(x,y,0);  // Render the component, doing it this way ensures us that the end user won't "forget" to do certain things  this->RenderComponent();  // Retrieve viewport and scissor size  GLint viewport[4];  GLint scissor[4];  glGetIntegerv(GL_VIEWPORT, viewport);  glGetIntegerv(GL_SCISSOR_BOX, scissor);  GLint nY = viewport[3] - y - h;  //std::cout<<nYtoUse<<":"<<scissor[1]<<std::endl;  //std::cout<<nH<<":"<<scissor[3]<<std::endl;  /*std::cout<<"Requested:"<<nX<<":"<<nY<<":"<<nW<<":"<<nH<<std::endl;  std::cout<<"Got:      "<<(scissor[0] > nX ? scissor[0] : nX)<<":"<<                           (scissor[1] > nY ? scissor[1] : nY)<<":"<<                           (scissor[2] < nW ? scissor[2] : nW)<<":"<<                           (scissor[3] < nH ? scissor[3] : nH)<<std::endl;*/  // Make the scissor size always smaller  /* This means that the X and Y values can only get bigger   * And that the W and H values can only get smaller*/  glScissor((scissor[0] > x ? scissor[0] : x),            (scissor[1] > nY ? scissor[1] : nY),//nY),            (scissor[2] < w ? scissor[2] : w),            (scissor[3] < h ? scissor[3] : h));  // Go through the child nodes and render them into the visible space of this node  BOOST_FOREACH(boost::shared_ptr<GUINode>& node, children)    node->Render();  // Reset translation for the siblings  glTranslated(-x,-y,0);  // Reset scissor for the siblings  glScissor(scissor[0],scissor[1],            scissor[2],scissor[3]);}


I wasn't really sure was the nX = (GLint)(scalar(viewport[2])*x)+scissor[0]; bits were for...


[EDIT]
Using new and delete like that will cause a performance hit as you allocate and free memory. I would imagine it'll fragment memory too. Basically, GLint scissor[4] is higher performance.
Those bits are there because my coordinate system goes from [0..1] in floats, so I need to convert them to window coordinates first, that's what the scalar(viewport[3])*y is for.

scalar is a typedef for float btw, it's just there to allow me to switch to double if I would have to.


The strange thing about the +scissor[1] not making an effect is because it isn't making an effect for _every_ node.

Originally I came to the conclusion to I would have to use that to offset my clip space relative to the previous clip space since I don't store any parent information.
I assume nx, ny, nw and nh are all correct values?

You made me spot a bug in my code I have to fix now :P
Quote:Original post by Bezben
I assume nx, ny, nw and nh are all correct values?

Unless there is something wrong with my logic of:
x = floating value and I multiply it with the value of viewports' width (viewport[2]) to get the screen offset of that GUI node.
Same for the others, except the vertically oriented values are mulitplied with the viewport[3].

Currently, I think it has something to do with the nH not being calculated correctly.

I'll have to do it by hand and see if this is the case.
Or I might be able to visualize the clip rectangles.

Quote:You made me spot a bug in my code I have to fix now :P

Glad to be of help.:P


This topic is closed to new replies.

Advertisement