0
\$\begingroup\$

I have created a menu class and now I'm working on getting it to render using OpenGL's VBO's. The menu is fairly simple, it has a number of buttons, the user can press up and down to highlight the desired button and then press ENTER to go to the selected buttons sub-menu which is just another instance of my menu. It should be noted that it doesn't need to have the same amount of buttons as the previous one. Pretty basic.

What I've come up so far is this:

//setting up the buffers and variables
int amountOfVerticesButtons = this.menu.getCurMenu().getNoButtons() * ShapeRenderUtils2D.VERTICES_RECT;
FloatBuffer vertexBufferButtons = BufferUtils.createFloatBuffer(amountOfVerticesButtons * VERTEX_SIZE);
FloatBuffer colorBufferButtons = RenderUtils.createColorBuffer(0.5f, 0f, 0.5f, amountOfVerticesButtons);

int amountOfVerticesBorders = this.menu.getCurMenu().getNoButtons() * ShapeRenderUtils2D.VERTICES_FRAME;
FloatBuffer vertexBufferBorders = BufferUtils.createFloatBuffer(amountOfVerticesBorders * VERTEX_SIZE);
FloatBuffer colorBufferBorders = RenderUtils.createColorBuffer(0.3f, 0f, 0.45f, amountOfVerticesBorders);

//filling up the buffers
for (int i = 0; i < MAX_BUTTONS_PER_PAGE && i < menu.getCurMenu().getNoButtons(); i++) {
    //buttons
    vertexBufferButtons.put(ShapeRenderUtils2D.getRectVertices(new Point(point.x, point.y + (i * (BUTTON_HEIGHT + BUTTON_SPACING))), MENU_WIDTH, BUTTON_HEIGHT));

    //button borders
    vertexBufferBorders.put(ShapeRenderUtils2D.getFrameRectangle(new Point(point.x, point.y + (i * (BUTTON_HEIGHT + BUTTON_SPACING))), MENU_WIDTH, BUTTON_HEIGHT));
}

vertexBufferButtons.flip();
buttons_handles = RenderUtils.createHandles(vertexBufferButtons, null, colorBufferButtons, amountOfVerticesButtons);

vertexBufferBorders.flip();
borders_handles = RenderUtils.createHandles(vertexBufferBorders, null, colorBufferBorders, amountOfVerticesBorders);

This code is run once in the constructor and it sets up the VBO for my buttons, and creates a color array with one color for each button.

My question is:

When the user presses ENTER, a new menu becomes active. The new menu doesn't have to have as many buttons as the previous one, so the old VBO is worthless, because it is of different size. I detect that the menu changes and what I basically do is to run the above code again, for the new menu, but at the end before I flip and create Handles I first do:

RenderUtils.deleteHandles(buttons_handles);
RenderUtils.deleteHandles(borders_handles);

Which removes the handles to the old VBO's. And then I create new ones just like I did the first time, but this time with correct size.

This seems very ineffective. Is there a better way to do it? Is there a better way to change an existing VBO's size?

EDIT: Please note that I've got everything else done with the menu. I only have problems with the VBO rendering. Also, there may be a very large amount of buttons, with many sub-menus that also contain large amounts of buttons, so creating a separate VBO for each case is not possible.

\$\endgroup\$
  • \$\begingroup\$ Still, if you use a setup similar to the one I've described in my reply, where each button is an immutable object that the menus can share, you can easily pack all vertexes in a single buffer and use something like glDrawElementsBaseVertex for rendering. \$\endgroup\$ – glampert Jul 10 '14 at 2:28
  • \$\begingroup\$ @glampert This method requires indices. I'm not using indices because i only have 2 out of 6 vertices that are duplicates. That is 6 * FLOAT without indices and 4 * FLOAT + 6 * INT with indices so they are ineffective. Also FYI, i can't place any data about how the buttons are rendered inside the Button class because I'm using MVC structure for my code. Also, if I use your method with VBOs, i would have to have possibly thousands of separate VBO's, which I tried to avoid. I would like all the buttons in the current menu inside one VBO. Maybe there is something that renders a part of VBO? \$\endgroup\$ – JamMaster Jul 10 '14 at 18:27
  • \$\begingroup\$ "Maybe there is something that renders a part of VBO": Sure, if drawing un-indexed, glDrawArrays should do. Just adjust the first parameter to point to the first vertex in the VBO you wish to draw. \$\endgroup\$ – glampert Jul 10 '14 at 20:30
  • \$\begingroup\$ @glampert Wow, that was the simpliest answer ever :D. It works out perfectly- if i create a VBO for maximum amount of buttons that are displayed at once and then just render a part of it by simply changing the count parameter, only desired amount is rendered. Thank you a lot! \$\endgroup\$ – JamMaster Jul 10 '14 at 21:23
  • \$\begingroup\$ Okay, good! Guess I completely misunderstood your question initially. I'll edit my reply to include this comment about the drawing. \$\endgroup\$ – glampert Jul 10 '14 at 22:38
1
\$\begingroup\$

By no means you need to recreate the buffers every time. Why don't you create all your buttons once and then draw them as needed? Buttons are immutable things.

Suppose you had a class Button, that incorporates all the stuff needed to draw a button, such as textures and the vertex buffer:

class Button {

    Texture texture;
    VertexBuffer buttonVerts;
    ...

    // draw this button using OpenGL.
    void draw(float x, float y);

    // load a file that describes this button's layout.
    // also loads texture and creates the vertex/index buffers.
    void init(string filename);
}

You only need to create your button "templates" once, and then every menu can share them. Supposing each button can be loaded from a file that describes its layout:

Button pushButton = new Button();
pushButton.init("push_btn.xml");

Button radioButton = new Button();
radioButton.init("radio_btn.xnl");

etc ...

That only have to be done once. Then you just distribute them to the menus:

// Menu with 2 push-buttons and a radio-button
Menu menu1 = new Menu();
menu1.addButton(pushButton);
menu1.addButton(pushButton);
menu1.addButton(radioButton);

And when the menu is drawn, all you will have to do is iterate the menu buttons and draw them:

function Menu.draw()
{
    // draw the menu background, etc...

    // now draw each button:
    for each Button btn in menuButtons
    {
        btn.draw(pos.x, pos.y);
    }
}

EDIT:

So, initially I misunderstood the difficulty the OP was having. The issue was how to pack all vertexes pertaining the buttons in a single VBO and draw them separately.

For indexed geometry, a good option would be to use glDrawElementsBaseVertex, which allows the specification of a vertex offset (basevertex) of where to start drawing.

For unindexed geometry (raw triangles and triangle-strips/fans), the approach is even simpler. glDrawArrays takes a first parameter which is the initial vertex to start drawing from. So the drawing of a button would become something in the lines of:

GLint firstBtnVertex = (vertsPerButton * buttonIndexInVBO);
glDrawArrays(GL_TRIANGLES, firstBtnVertex, vertsPerButton);
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.