Multiple clipping planes

Started by
2 comments, last by XTAL256 15 years, 10 months ago
I am making a game using OpenGL (Visual C++, Windows) and i need to use multiple clipping planes. I use glScissor to clip a region of text within a GUI component (i.e. a label or textbox) and also to clip components within a container (i.e. window). How do i do both of these at the same time? Is it possible? Here is a screenshot showing how i have my GUI: EDIT: oh, i should mention how i am currently attempting to do it. See, i create an OpenGL display list for each GUI component (button, label, etc) and call that dl when i want to draw the component. I do this mainly because i only need to create the image when and if the component changes position or size. So i thought i could clip the text on a TextBox when i create the display list for it. And then when i draw the TextBox on the container in my screenshot, the image created by the dl would be clipped to the container. But that doesn't quite work. Here is some of my code:

//// TextField.cpp ///////////////////

/* Updates the component's image when one of it's properties changes */
void TextField::update() {
    // Create display lists to draw the image
    createComponentDL(dlImg,img,x,y,width,height,divLeft,divRight);

    const int textSize = (int)(height*TEXT_FIELD_SIZE);
    const int textY = (int)(height/2.0f - textSize/2.0f);

    // Clip text to component edge
    clipRect(getAbsX()+TEXT_FIELD_PADDING_X, getAbsY(),
             width-(2*TEXT_FIELD_PADDING_X), height);
    buildList(dlText);
    printText(x+TEXT_FIELD_PADDING_X, y+textY, text, font, textSize);
    endList();
    disableClip();

    hasChanged = false;
}

void TextField::draw() {
    if (hasChanged) update();  // Update image if need be
    callList(dlImg);
    callList(dlText);

    int cxPos = x + getCharCoord(cursorPos);
    int sxPos = x + getCharCoord(selectPos);
    if (cxPos > width) cxPos = width;
    if (sxPos > width) sxPos = width;
    if (cursorPos == selectPos)
        drawLine(cxPos, y + TEXT_FIELD_PADDING_Y,
                 cxPos, y + height - TEXT_FIELD_PADDING_Y); // Draw cursor
    else {
        setLogicOp(GL_INVERT);
        if (cursorPos > selectPos)     // Draw selection
            fillRect(sxPos, y + TEXT_FIELD_PADDING_Y,
                     cxPos-sxPos, height-(2*TEXT_FIELD_PADDING_Y));
        else
            fillRect(cxPos, y + TEXT_FIELD_PADDING_Y,
                     sxPos-cxPos, height-(2*TEXT_FIELD_PADDING_Y));
        setLogicOp(GL_COPY);
    }
}

//// Container.cpp ////////////

void Container::draw() {
    glPushMatrix();
    // Clip to container bounds.
    clipRect(getAbsX(), getAbsY()+2, width, height);
    glTranslated(x + cX, y + cY, 0);   // Draw relative to container pos
    for (int i = 0; i < numComponents(); i++) {
        components->draw();         //### Draw each component in this container ###
    }
    disableClip();
    glPopMatrix();

    if (scrollBar) {
        glPushMatrix();
        glTranslated(x, y, 0);
        scrollBar->draw();
        glPopMatrix();
    }
}

/* Draws the currently active Window/Panel and any other visible */
/* components (i.e. a Panel behind another Panel)                */
void Container::drawScreen() {
    for (int i = MAX_SCREENS; i >= 0; i--) { // Draw first one last
        if (Component::visible)
            Component::visible->draw();  //### This draws the window shown in the screenshot ###
    }
}

//// Graphics.h /////////////
inline void clipRect(int x, int y, int width, int height) {
    glScissor(x, y, width, height);
    glEnable(GL_SCISSOR_TEST);
}
inline void disableClip() { glDisable(GL_SCISSOR_TEST); }

The textboxes (TextFields) in my screenshot are components in the Container, they are drawn in Container::draw(). I currently draw the screen in the WinMain loop (until i find a better way of doing it):

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int wndState) {
    MSG msg;
    // Init stuff
    ...
    loadGUIData();
    createUIScreens();

    while(!appQuit) {
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
            if (msg.message==WM_QUIT) {
                appQuit = true;
            }
            else {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        else {
            clearScreen();
            Container::drawScreen();  // Draw the screen
            SwapBuffers(hdc);
            Sleep(20);
        }
    }
    ...
}

I haven't provided the code for the rest of my GUI (window code and stuff) but comments with ### explain what happens. [Edited by - XTAL256 on June 20, 2008 7:50:52 PM]
[Window Detective] - Windows UI spy utility for programmers
Advertisement
Can anyone help me? Even if it's just a link to a tutorial online or something.
thanks
[Window Detective] - Windows UI spy utility for programmers
i think youre over complicating things, overworrying but anyways
one possible method would be to use the stencil test
eg clear the stencil to 0, and set the window to 1, and the box to 2.
and then set it to only draw if the current stencil value of the pixel is > 0

also surely u only need to clip to one region at once? ie glScissor should be enuf
Quote:Original post by zedz
i think youre over complicating things, overworrying but anyways...

I don't see any other way of doing it. Like i mentioned, i am clipping each component when i create a display list for them and then clipping when the display list is drawn (so i am only using glScissor once at a time, sort of) but that doesn't work. I will read up on how to do it the way you mentioned but i would really like to know how i would do it a simpler way.
btw, i am sort of a n00b at OpenGL which is why i am "overworrying" :)

[Edited by - XTAL256 on June 23, 2008 7:59:59 PM]
[Window Detective] - Windows UI spy utility for programmers

This topic is closed to new replies.

Advertisement