module saurus.opengl; import derelict.opengl.gl; import derelict.opengl.glu; import saurus.sdl; import saurus.drawing; private import saurus.algorithm; private import saurus.math.util; private abstract class GLObject { protected GLuint name; protected bool finalized; private static GLObject[GLObject] objects; this(GLuint name) { this.name = name; finalized = false; objects[this] = this; } invariant { if (finalized) { assert(name == 0); } else { assert(name != 0); } } static void finalizeAll() { foreach (GLObject object; objects) { if (!object.finalized) { object.finalize(); assert(object.finalized == true); } delete objects[object]; } } abstract void finalize(); } static this() { DerelictGL_Load(); DerelictGLU_Load(); } static ~this() { GLObject.finalizeAll(); } class Texture : GLObject { private Point size_; this(Surface surface) { // Generate the name. GLuint name; glGenTextures(1, &name); super(name); // Track the size. size_ = surface.size.dup(); // Is the size not a power of two? if (!isPowerOf2(size.x)) { throw new Error("Surface width is not a power of 2."); } if (!isPowerOf2(size.y)) { throw new Error("Surface height is not a power of 2."); } // What format do we want our surface in? Uint32 rmask; Uint32 gmask; Uint32 bmask; Uint32 amask; int bpp; int components; GLenum glFormat; if ((surface.get().format.Amask) || (surface.get().flags & SDL_SRCCOLORKEY)) { rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; bpp = 32; components = 4; glFormat = GL_RGBA; } else { rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0x00000000; bpp = 24; components = 3; glFormat = GL_RGB; } // Do we need to convert the surface to the right format? if (1) { // Convert. surface = surface.convert(bpp, rmask, gmask, bmask, amask); } // Flip the surface. int pitch = surface.pitch; // Lock the surface. ubyte* topLine = surface.lock(); try { // Flip each pair of rows. ubyte* bottomLine = topLine + (pitch * (size_.y - 1)); while (topLine < bottomLine) { // Flip two rows. ubyte* topLineEnd = topLine + pitch; Swap!(ubyte).swap(topLine, topLineEnd, bottomLine); // Next! topLine = topLineEnd; bottomLine -= pitch; } } finally { // Unlock the surface. surface.unlock(); } // Copy the pixels to the texture. bind(); ubyte* pixels = surface.lock(); try { glTexImage2D( GL_TEXTURE_2D, 0, // Level components, size_.x, size_.y, 0, // Border glFormat, GL_UNSIGNED_BYTE, pixels); } finally { surface.unlock(); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } void finalize() { glDeleteTextures(1, &name); name = 0; finalized = true; } Point size() { return size_; } void bind() { glBindTexture(GL_TEXTURE_2D, name); } static void unbind() { glBindTexture(GL_TEXTURE_2D, 0); } } private class VAVertexBuffer(Vertex) : VertexBuffer!(Vertex).VertexBuffer { private Vertex[] array; this(size_t length) { this.array = new Vertex[length]; } void bind() { glEnableClientState(GL_VERTEX_ARRAY); } void unbind() { glDisableClientState(GL_VERTEX_ARRAY); } } static void swapBuffers() { SDL_GL_SwapBuffers(); }