// WORK IN PROGRESS /** An add-on for simpledisplay.d, joystick.d, and simpleaudio.d that includes helper functions for writing games (and perhaps other multimedia programs). Usage example: --- class MyGame { /// Called when it is time to redraw the frame /// it will try for a particular FPS final void drawFrame() { } final void update(Duration deltaTime) { } final void fillAudioBuffer(short[] buffer) { } } void main() { auto game = new MyGame(); runGame(game, maxRedrawRate, maxUpdateRate); } --- It provides an audio thread, input scaffold, and helper functions. The MyGame handler is actually a template, so you don't have virtual function indirection and not all functions are required. The interfaces are just to help you get the signatures right, they don't work at runtime. */ module arsd.gamehelpers; public import arsd.color; public import simpledisplay; import std.math; /// The max rates are given in executions per second /// Redraw will never be called unless there has been at least one update void runGame(T)(T game, int maxRedrawRate, int maxUpdateRate) { } /// Simple class for putting a TrueColorImage in as an OpenGL texture. /// /// Doesn't do mipmapping btw. final class OpenGlTexture { private uint _tex; private int _width; private int _height; private float _texCoordWidth; private float _texCoordHeight; void bind() { glBindTexture(GL_TEXTURE_2D, _tex); } // For easy 2d drawing of it void draw(Point where, int width = 0, int height = 0, float rotation = 0.0, Color bg = Color.white) { glPushMatrix(); glTranslatef(where.x, where.y, 0); glRotatef(rotation, 0,0, 1); if(width == 0) width = this.originalImageWidth; if(height == 0) height = this.originalImageHeight; glColor4f(cast(float)bg.r/255.0, cast(float)bg.g/255.0, cast(float)bg.b/255.0, cast(float)bg.a / 255.0); glBindTexture(GL_TEXTURE_2D, _tex); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2i(0, 0); glTexCoord2f(texCoordWidth, 0); glVertex2i(width, 0); glTexCoord2f(texCoordWidth, texCoordHeight); glVertex2i(width, height); glTexCoord2f(0, texCoordHeight); glVertex2i(0, height); glEnd(); glBindTexture(GL_TEXTURE_2D, 0); // unbind the texture glPopMatrix(); } /// Use for glTexCoord2f float texCoordWidth() { return _texCoordWidth; } float texCoordHeight() { return _texCoordHeight; } /// ditto /// Returns the texture ID uint tex() { return _tex; } /// Returns the size of the image int originalImageWidth() { return _width; } int originalImageHeight() { return _height; } /// ditto /// Make a texture from an image. this(TrueColorImage from) { assert(from.width > 0 && from.height > 0); _width = from.width; _height = from.height; auto _texWidth = _width; auto _texHeight = _height; const(ubyte)[] data = from.imageData.bytes; // gotta round them to the nearest power of two which means padding the image if((_texWidth & (_texWidth - 1)) || (_texHeight & (_texHeight - 1))) { _texWidth = nextPowerOfTwo(_texWidth); _texHeight = nextPowerOfTwo(_texHeight); auto n = new ubyte[](_texWidth * _texHeight * 4); auto size = from.width * 4; auto advance = _texWidth * 4; int at = 0; int at2 = 0; foreach(y; 0 .. from.height) { n[at .. at + size] = from.imageData.bytes[at2 .. at2+ size]; at += advance; at2 += size; } data = n[]; // the rest of data will be initialized to zeros automatically which is fine. } glGenTextures(1, &_tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, _texWidth, // needs to be power of 2 _texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.ptr); assert(!glGetError()); _texCoordWidth = cast(float) _width / _texWidth; _texCoordHeight = cast(float) _height / _texHeight; } /// Generates from text. Requires stb_truetype.d /// pass a pointer to the TtfFont as the first arg (it is template cuz of lazy importing, not because it actually works with different types) this(T, FONT)(FONT* font, int size, in T[] text) if(is(T == char)) { assert(font !is null); int width, height; auto data = font.renderString(text, size, width, height); auto image = new TrueColorImage(width, height); int pos = 0; foreach(y; 0 .. height) foreach(x; 0 .. width) { image.imageData.bytes[pos++] = 255; image.imageData.bytes[pos++] = 255; image.imageData.bytes[pos++] = 255; image.imageData.bytes[pos++] = data[0]; data = data[1 .. $]; } assert(data.length == 0); this(image); } ~this() { glDeleteTextures(1, &_tex); } } // Some math helpers int nextPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } void crossProduct( float u1, float u2, float u3, float v1, float v2, float v3, out float s1, out float s2, out float s3) { s1 = u2 * v3 - u3 * v2; s2 = u3 * v1 - u1 * v3; s3 = u1 * v2 - u2 * v1; } void rotateAboutAxis( float theta, // in RADIANS float x, float y, float z, float u, float v, float w, out float xp, out float yp, out float zp) { xp = u * (u*x + v*y + w*z) * (1 - cos(theta)) + x * cos(theta) + (-w*y + v*z) * sin(theta); yp = v * (u*x + v*y + w*z) * (1 - cos(theta)) + y * cos(theta) + (w*x - u*z) * sin(theta); zp = w * (u*x + v*y + w*z) * (1 - cos(theta)) + z * cos(theta) + (-v*x + u*y) * sin(theta); }