Hey!
I thought I’d take some time to explain the concept of destructible terrain. I’ve needed this for , and it took me quite some time to figure out a performant and flexible way of implementing it.
The main problem is that circles take quite a lot of vertices to draw. You really don’t want to just take a slab of terrain, and cut circles out of it, and then use that data to draw a texture on or calculate intersection against.
Square shapes are far less expensive; and that’s how my method got born. It’s a little tricky, so you might need to read over this a few times to get it straight.
The background
We want to be able to use any kind of background. Be it a solid color, a gradient, or as advanced as a moving image. That means that we can’t just paint a background, paint a terrain, and fill up holes in the terrain with a background color.
We start simply by drawing whatever background we need.
In the case of Gorillas, that’s a simple gradient for the sky and a few dots for stars. When you disable the special effects in Gorillas, it just draws a plain solid color.
Plain background:
GLubyte *colorBytes = (GLubyte *) &skyColor;
glClearColor(colorBytes[3] / (float)0xff, colorBytes[2] / (float)0xff, colorBytes[1] / (float)0xff, colorBytes[0] / (float)0xff);
glClear(GL_COLOR_BUFFER_BIT);
Gradient background:
glVertexPointer(2, GL_FLOAT, 0, vertices);
const GLubyte *fromColorBytes = (GLubyte *)&fromColor;
const GLubyte *toColorBytes = (GLubyte *)&toColor;
const GLubyte colors[4 * 4] = {
fromColorBytes[3], fromColorBytes[2], fromColorBytes[1], fromColorBytes[0],
fromColorBytes[3], fromColorBytes[2], fromColorBytes[1], fromColorBytes[0],
toColorBytes[3], toColorBytes[2], toColorBytes[1], toColorBytes[0],
toColorBytes[3], toColorBytes[2], toColorBytes[1], toColorBytes[0],
};
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
glEnableClientState(GL_COLOR_ARRAY);
// Draw.
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Untoggle state.
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
(This code assumes ’skyColor’, ‘fromColor’ and ‘toColor’ are ‘long’s that contain your color codes, such as 0×0000FFFF for bright blue, and ‘from’ and ‘to’ are structs with two floats which describe the background sky box)
See:
The holes
Say we’ve already got five explosion holes that should carve chunks out of our terrain. As explained above, the problem is that we need to see the background we just drew through those chunks.
The magic to make this happen is clever blending and toggling of OpenGL state.
We basically just draw a hole, which is a texture that is a white square with a transparent circle in the middle. Before we draw it, we tell OpenGL to NOT change any of the RGB components of our render buffer, but only touch the Opacity component. Then, we tell it to blend the destination (existing) components with the alpha values of the texture’s components. As a result, we get a destination in our render buffer that has identical RGB components as before (we didn’t touch those) but Opacity components as they are in the texture we drew. Which is to say, a transparent circle in the center and opaqueness around it. The render buffer doesn’t look any different, but where we want a hole, the pixels’ Opacity components are no longer opaque.
// Blend our transarent white with DST. If SRC, make DST transparent, hide original DST.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
[super draw];
// Reset blend & data source.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
(The [super draw] call just draws our hole texture where we need it to be)
See: