|
|
|
It's been quite a while since I actually worked on TreeWars. Various things have distracted me, including some other programming projects. But I actually made some progress way back in July, before I shelved the project temporarily. So, let's talk about circles.<br/><br/>OpenGL gives us a few different ways to draw things, which I've talked about before. When we were using the fixed-pipeline functions (glBegin(), glEnd(), etc), I could draw a circle the same way I drew it in SDL: draw a bunch of same-sized rectangles, shifting the coordinates around a central point so that they overlap. Do enough of them (using small enough increments), and it makes a very smooth-looking circle. I never did this in OpenGL, but the SDL code looked like this:<br/><br/>[sourcecode language="cpp" gutter="false"]<br/>void DrawUtils::draw_circle_filled(SDL_Surface* dest, Sint16 int_x, Sint16 int_y, Uint16 int_r, Uint32 colour)<br/>{<br/> float x = static_cast<float> (int_x);<br/> float y = static_cast<float> (int_y);<br/> float r = static_cast<float> (int_r);<br/><br/> SDL_Rect pen;<br/> float i;<br/><br/> for (i=0; i < 6.28318531; i += 0.0034906585)<br/> {<br/> pen.x = static_cast<int> (x + cos(i) * r);<br/> pen.y = static_cast<int> (y + sin(i) * r);<br/> int w = static_cast<int> (x - pen.x);<br/> int h = static_cast<int> (y - pen.y);<br/><br/> if (w == 0) pen.w = 1;<br/> else if (w < 0)<br/> {<br/> pen.x = x;<br/> pen.w = abs(w);<br/> }<br/> else pen.w = w;<br/><br/> if (h == 0) pen.h = 1;<br/> else if (h < 0)<br/> {<br/> pen.y = y;<br/> pen.h = abs(h);<br/> }<br/> else pen.h = h;<br/> if (pen.x >= dest->clip_rect.x &&<br/> pen.y >= dest->clip_rect.y &&<br/> pen.x + pen.w <= dest->clip_rect.w &&<br/> pen.y + pen.h <= dest->clip_rect.h)<br/> SDL_FillRect(dest, &pen,<br/> SDL_MapRGBA(dest->format,<br/> (colour >> 16) & 0xff,<br/> (colour >> 8) & 0xff,<br/> colour & 0xff, 1));<br/> }<br/>}<br/>[/sourcecode]<br/><br/>I was pretty proud of this code when I wrote it. The magic is at the top of the <code>for</code> loop: from 0 to 2π, it increments a tiny bit and finds a new rectangle that has one vertex at the center of the circle, and the opposing vertex at some point along the circle. It does this 1800 times per circle, which isn't terribly efficient, but it got the job done.<br/><br/>With OpenGL and the shader pipeline, we *could* still do that. We could do the following in a loop, 1800 times:<br/><br/>[sourcecode lang="cpp" gutter="false"]<br/> GLushort Quad::rect_elements[] = {0, 1, 2, 3};<br/> GLfloat buffer_data[] = {x1f, y1f, x2f, y1f, x1f, y2f, x2f, y2f};<br/><br/> GLuint vertex_buffer;<br/> glGenBuffers(1, &vertex_buffer);<br/> glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);<br/> glBufferData(GL_ARRAY_BUFFER, sizeof(buffer_data), buffer_data, GL_STATIC_DRAW);<br/><br/> GLuint element_buffer;<br/> glGenBuffers(1, &element_buffer);<br/> glBindBuffer(GL_ELEMENT_BUFFER, element_buffer);<br/> glBufferData(GL_ELEMENT_BUFFER, sizeof(rect_elements), rect_elements, GL_STATIC_DRAW);<br/><br/> glUseProgram(program); // the shader program, created earlier<br/> glUniform4f(shader->uniforms.colour,<br/> GLUtils::convert_colour((colour >> 16) & 0xff),<br/> GLUtils::convert_colour((colour >> 8) & 0xff),<br/> GLUtils::convert_colour(colour & 0xff), 1.0);<br/><br/> // Put the vertices in an attribute<br/> glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);<br/> glVertexAttribPointer(shader->attributes.position, 2, GL_FLOAT,<br/> GL_FALSE, sizeof(GLfloat)*2, (void*)0);<br/>
|