waf·fle |ˈwä-fəl| verb (used without an object) to speak or write equivocally: to waffle on an important issue.

Menu

Category Archives: Technical

The popular torrent search engine, Torrentz.eu does not provide direct links to download torrents. Normally, one would have to pick one of the available torrent websites at the top of the page in order to get a .torrent file or magnet link. However, I recently noticed that all the information needed to generate a .torrent file or magnet link is actually available on the page itself, namely the info hash and a list of trackers. As such I wrote some JavaScript to generate a magnet link.

The last line is optional and performs the equivalent of clicking on the link. To convert the torrent hash above the trackers list into a clickable link instead of automatically opening the magnet handler, replace window.location = magnet with:

For the sake of convenience, I have also made a bookmarklet that will automatically start a torrent download when clicked (assuming that you are currently on a Torrentz.eu torrent page). Drag this button onto your bookmarks toolbar and you will be good to go:

Welcome, class, to Part 3! Today we will be discussing indexed vertex buffers and using them to optimize tessellation.

Indexed Vertex Buffer Objects

Rationale

In complicated shapes one often encounters several primitives which share at least one vertex. This unfortunate situation is exacerbated by tessellation, sometimes dramatically. To demonstrate this I will use the below-pictured, slightly creepy, Eye of Providence graphic. I have chosen this mainly to please my sponsors, the Illuminati, but also because as a pointy object it is bound to have a ton of reused vertices after tessellation.

It’s looking at you…

When tessellated it looks as follows.

Possibly even creepier now…

The change may not look drastic, but it actually goes from having 82 vertices, untessellated, to having 204, post-tessellation. Luckily we can mitigate the effects of the almost 250% increase in vertex-count by using an indexed vertex buffer object.

Preparation

We will start with the skeleton of an OpenGL program that you should be quite familiar with by now, assuming you have read parts 1 and 2. This includes the defining of the display object, as well as setting up a rendering loop.

If all has gone according to plan, you should now be ready to set up the tessellator.

Storing the Indexes

final List<Integer> allIndices = new ArrayList<>();

As usual you should start by creating a list in which to store the tessellation results. This time, however, you will be storing indices, rather than actual vertices, so you should make it a list of integers, rather than a list of floating point numbers.

Although the Eye of Providence example shape does not ever require the combine method, it can’t hurt to have one. It would be very difficult to actually combine the vertices in question, seeing as we don’t have access to them from the tessellator. Instead I have elected to pick the index of the vertex with the highest weight and return that. Feel free to experiment with more sophisticated techniques for combining the vertices.

The triangle strip and fan conversion functions I provided in the previous tutorial also require some tweaking to work with indices. The only absolutely necessary change is to divide all the indexes and incrementors used by 6.

In the tessellation loop you should now store every vertex encountered into a list, and then pass the index of the vertex to the tessellator as the data parameter. It is worth noting that OpenGL is going to expect the index of the vertex, NOT the index of the first coordinate of the vertex. For example, if allVertices contained the two vertices {399, 590, 0, 0.3, 0.8, 1, 412, 582, 0, 0.3, 0.8, 1}, then the index of the second vertex (the one starting with 412) would be 1, not 6. This is why you need to divide the index by 6 before passing it to the tessellator.

This time, in addition to loading the vertices into a buffer, you should also load the indices into a buffer of their own. Bind the buffer to the GL_ELEMENT_ARRAY_BUFFER instead of the GL_ARRAY_BUFFER target; this tells OpenGL to use read this buffer for indices when a call to glDrawElements() is made.

It is finally time to render the shape to the screen. Notice that inside the rendering loop we call glDrawElements() instead of glDrawArrays(). The parameters are relatively self-explanatory. GL_UNSIGNED_INT tells OpenGL that our indices are stored as ints, rather than as shorts or bytes. If you are drawing something with relatively few indices (fewer than 32,767), it makes sense to save memory by using a ShortBuffer instead of an IntBuffer and to use GL_UNSIGNED_SHORT instead. The last parameter is offset which is set to 0, as usual.

If you followed my instructions carefully, you should now be able to run your program and see the Eye of Providence staring into your soul. If you do see it, close it quickly before you need to be institutionalized, otherwise check your code against Appendix B.

Keyboard Input and Camera Manipulation

There comes a time in the life of every data visualization application that the user wants to control the view in some way or another. In this twice-foretold section I will discuss listening for keyboard input, and panning and zooming the 2D orthographic camera.

Listening For Keyboard Input

It’s worth noting that the code in this section is specific to LWJGL, and may or may not apply to other programming languages or toolkits.

LWJGL automatically takes care of polling user input when you call Display.update(). The only thing left for you to do is decide what to do with that input. First though, you should import the necessary classes, functions, and constants.

import static org.lwjgl.input.Keyboard.*;

Now you can start checking if a key has been pressed in the last frame from the rendering loop.

if (Keyboard.isKeyDown(KEY_Q)) System.out.println("Q has been pressed");

If you open your program and hold down the Q key you should see the phrase “Q has been pressed” printed several times to the console.

Manipulating the Camera

OpenGL provides some functions such as glTranslate() and glRotate() which allow you to easily manipulate the object pictured on screen. Unfortunately it always does this relative to the origin, and does not make it easy to scale the speed. As such, it is much easy to simply update the view with calls to glOrtho() on every frame. Before the rendering loop begins, define some variables to keep track of the state of the view.

If all went according to plan you should be able to pan and zoom your display using the keys specified in the code.

Goodbye Stranger

This marks the end of the third (and most likely final) part of my tutorial on basic OpenGL in Java. I hope it was of assistance, and please contact me with questions or comments. Thank you for attention!