digitalmars.D.learn - "This is madness!" Red Book OpenGL Example 1-3, done using templates
- downs (232/232) Jul 31 2007 This is a translation of the Red Book's OpenGL Example 1-3 into D, witho...
- downs (5/15) Jul 31 2007 Stupid me. I never tested this part for unsigneds.
- Don Clugston (4/20) Aug 02 2007 Still doesn't compile with DMD 1.020.
This is a translation of the Red Book's OpenGL Example 1-3 into D, without using any standard library functions or pre-existing OpenGL bindings. It makes extensive use of templates and contains a minimum of repeated information. I realize the whole heap is probably close to unreadable, but it helps to start from the bottom - main is almost clean, and to ignore the templated code for the beginning. Use in whole or part as you like, under any license - none of this is copyrightable anyway. template Tuple(T...) { alias T Tuple; } /// Import the NAMES as constants of type TYPE (must be :uint) into the namespace, /// starting with OFFSET and moving in NEXT jumps template _ConstEnums(TYPE, alias NEXT, uint OFFSET, NAMES...) { static if (!NAMES.length) const char[] _ConstEnums=""; else { static assert(is(typeof(NAMES[0]): char[]), "Invalid _ConstEnums parameter "~NAMES[0].stringof~" which is "~typeof(NAMES[0]).stringof~"."); const char[] _ConstEnums="const "~TYPE.stringof~" "~NAMES[0]~"="~OFFSET.stringof~"; " ~_ConstEnums!(TYPE, NEXT, NEXT(OFFSET), NAMES[1..$]); } } typedef uint Enum; /// additive increase typedef uint Bitfield; /// multiplicative increase uint Inc(uint v) { return v+1; } /// for Enum uint Dbl(uint v) { return v*2; } /// for Bitfield /// convenient wrapper template ConstEnums(TYPE, OFFS_AND_NAMES...) { static assert(OFFS_AND_NAMES.length>1); static assert(is(TYPE: uint)); static if(is(TYPE: Enum)) const char[] ConstEnums=_ConstEnums!(TYPE, Inc, OFFS_AND_NAMES); else { static assert(is(TYPE: Bitfield), "Don't know how to generate const-enum from TYPE "~TYPE.stringof); const char[] ConstEnums=_ConstEnums!(TYPE, Dbl, OFFS_AND_NAMES); } } /// Makes sure all U are of type T template AssertTypes(T, U...) { static if (U.length) { const char[] AssertTypes= "static assert(is("~typeof(U[0]).stringof~": "~T.stringof~"), \" Type assertion failed - cannot convert "~typeof(U[0]).stringof~" to "~T.stringof~"\"); " ~AssertTypes!(T, U[1..$]); } else const char[] AssertTypes=""; } /// all STRINGS[2..$] are prepended STRINGS[0] and appended STRINGS[1] template PrePost(STRINGS...) { mixin(AssertTypes!(char[], STRINGS)); static assert(STRINGS.length!<3, "Insufficient argument count for PrePost"); static if(STRINGS.length>3) alias Tuple!(STRINGS[0]~STRINGS[2]~STRINGS[1], PrePost!(STRINGS[0..2], STRINGS[3..$])) PrePost; else alias Tuple!(STRINGS[0]~STRINGS[2]~STRINGS[1]) PrePost; } template Prepend(STRINGS...) { alias PrePost!(STRINGS[0], "", STRINGS[1..$]) Prepend; } template Append(STRINGS...) { alias PrePost!("", STRINGS[0], STRINGS[1..$]) Append; } extern(C) { typedef Enum StringName; mixin(ConstEnums!(StringName, 0x1F00, Prepend!("GL_", "VENDOR", "RENDERER", "VERSION", "EXTENSIONS"))); typedef Enum ShadeModel; mixin(ConstEnums!(ShadeModel, 0x1D00, Prepend!("GL_", "FLAT", "SMOOTH"))); typedef Bitfield AttribMask; mixin(ConstEnums!(AttribMask, 1, PrePost!("GL_", "_BIT", "CURRENT", "POINT", "LINE", "POLYGON", "POLYGON_STIPPLE", "PIXEL_MODE", "LIGHTING", "FOG", Append!("_BUFFER", "DEPTH", "ACCUM", "STENCIL"), "VIEWPORT", "TRANSFORM", "ENABLE", "COLOR_BUFFER", "HINT", "EVAL", "LIST", "TEXTURE", "SCISSORS"))); const AttribMask GL_ALL_ATTRIB_BITS=0xFFFF_FFFF; typedef Enum MatrixMode; mixin(ConstEnums!(MatrixMode, 0x1700, Prepend!("GL_", "MODELVIEW", "PROJECTION", "TEXTURE"))); char *glGetString(StringName); Enum glGetError(); } class glException : Exception { this(char[] s) { super(s); } } T glCheck(T)(lazy T lv) { void checkError() { auto error=glGetError(); if (error) throw new glException(format("GL error: ", error)); } static if(is(T==void)) { lv(); checkError; } else { auto res=lv(); checkError; return res; } } extern(C) { void glClearColor(float red=0f, float green=0f, float blue=0f, float alpha=0f); void glShadeModel(ShadeModel mode); } void init() { glClearColor; glShadeModel(GL_FLAT); } extern(C) { void glPushMatrix(); void glPopMatrix(); } void glMatrix(void delegate() dg) { glPushMatrix(); dg(); glPopMatrix(); } /// Look up the full type name for the OpenGL type suffix typeID char[] glLookupType(char[] typeID) { foreach (type; ["byte", "double", "float", "int", "short", "ubyte", "uint", "ushort"]) { if(type[0..typeID.length]==typeID) return type; } assert(false, "Invalid type: "~typeID); } /// "type, type, type" .. length is count char[] paramList(int count, string type) { if (count>1) return type~", "~paramList(count-1, type); return type; } char[] glColorMixin(int params, char[] typeID, bool vector) { return "void glColor"~(params==3?"3":"4")~typeID~(vector?"v":"")~" ("~ (vector?glLookupType(typeID)~" *); ":paramList(params, glLookupType(typeID))~"); "); } /// Generate the mixin string for all the glColor variations char[] glColors() { char[] res; foreach (count; [3, 4]) foreach (name; ["b", "d", "f", "i", "s", "ub", "ui", "us"]) res~=glColorMixin(count, name, false)~glColorMixin(count, name, true); return res; } extern(C) mixin(glColors); /// all the glColor functions in a single concise mixin. /// It's unsigned if it starts with 'u'. Pure simplicity. template unsigned(T) { static if(T.stringof[0]=='u') const bool unsigned=true; else const bool unsigned=false; } /// A very generic glColor. Supports static arrays. import std.traits; void glColor(T...)(T t) { static if (T.length==1) { alias T[0] Thingie; const bool vector=true; alias typeof(t[0][0]) ElemType; } else { alias T Thingie; const bool vector=false; alias typeof(t[0]) ElemType; } const char[] count=Thingie.length.stringof; static assert(count=="3"||count=="4"); const char[] type=(unsigned!(ElemType)?"u":"")~ElemType.stringof[0]; static if(vector) mixin("glColor"~count~type~"v(t[0].ptr); "); else mixin("glColor"~count~type~"(t); "); } float spin=0f; alias Tuple!(1f) Xf; alias Tuple!(0f, 1f) Yf; alias Tuple!(0f, 0f, 1f) Zf; template pair(T...) { alias Tuple!(T, T) pair; } extern(C) { void glClear(AttribMask mask); void glRotatef(float angle, float x=1, float y=0, float z=0); void glRectf(pair!(pair!(float))); void glutSwapBuffers(); void display() { glClear(GL_COLOR_BUFFER_BIT); glCheck(glMatrix({ glRotatef(spin, Zf); glColor(1f, 1f, 1f); glRectf(pair!(-25f), pair!(25f)); })); glutSwapBuffers; } } extern(C) { void glutPostRedisplay(); void spinDisplay() { spin+=2f; if (spin>360) spin-=360; glutPostRedisplay; } } extern(C) { void glViewport(pair!(pair!(int))); void glMatrixMode(MatrixMode); void glLoadIdentity(); void glOrtho(pair!(double) leftright, pair!(double) bottomtop, pair!(double) nearfar); template plusminus(T...) { static if(T.length) alias Tuple!(T[0], -T[0], plusminus!(T[1..$])) plusminus; else alias Tuple!() plusminus; } void reshape(pair!(int) size) { glViewport(0, 0, size); glCheck({glMatrixMode=GL_PROJECTION; glLoadIdentity; }); glCheck(glOrtho(plusminus!(50f, 50f, 1f))); glCheck({glMatrixMode=GL_MODELVIEW; glLoadIdentity; }); } } extern(C) { typedef Enum MouseButton; mixin(ConstEnums!(MouseButton, 0, PrePost!("GLUT_", "_BUTTON", "LEFT", "MIDDLE", "RIGHT"))); typedef Enum MouseState; mixin(ConstEnums!(MouseState, 0, Prepend!("GLUT_", "DOWN", "UP"))); void glutIdleFunc(void function()); void mouse(MouseButton button, MouseState state, pair!(int) where) { switch(button) { case GLUT_LEFT_BUTTON: if(state==GLUT_DOWN) glutIdleFunc=&spinDisplay; break; case GLUT_MIDDLE_BUTTON: if(state==GLUT_DOWN) glutIdleFunc=null; break; default: break; } } } import std.string; extern(C) { void glutInit(int *argc, char **argv); void glutInitDisplayMode(uint mode); void glutInitWindowSize(pair!(int) size); void glutInitWindowPosition(pair!(int) pos); void glutDisplayFunc(void function()); void glutReshapeFunc(void function(pair!(int) size)); void glutMouseFunc(void function(MouseButton button, MouseState state, pair!(int) pos)); int glutCreateWindow(char *name); typedef Bitfield DisplayMode; mixin(ConstEnums!(DisplayMode, 1, Prepend!( "GLUT_", "INDEX", "DOUBLE", "ACCUM", "ALPHA", "DEPTH", "STENCIL", "SKIPTHISBOGUS", "MULTISAMPLE", "STEREO", "LUMINANCE" ))); const DisplayMode GLUT_RGB=0, GLUT_RGBA=0, GLUT_SINGLE=0; void glutMainLoop(); } int main(string[] args) { /// C-ify the args int len=args.length; char *[] ptrs; foreach (entry; args) ptrs~=toStringz(entry); glutInit(&len, ptrs.ptr); glutInitDisplayMode=GLUT_DOUBLE | GLUT_RGB; glutInitWindowSize=pair!(250); glutInitWindowPosition=pair!(100); glutCreateWindow("foo".ptr); init; glutDisplayFunc=&display; glutReshapeFunc=&reshape; glutMouseFunc=&mouse; glutMainLoop; return 0; }
Jul 31 2007
downs wrote:/// A very generic glColor. Supports static arrays. import std.traits; void glColor(T...)(T t) { static if (T.length==1) { alias T[0] Thingie; const bool vector=true; alias typeof(t[0][0]) ElemType; } else { alias T Thingie; const bool vector=false; alias typeof(t[0]) ElemType; } const char[] count=Thingie.length.stringof; static assert(count=="3"||count=="4"); const char[] type=(unsigned!(ElemType)?"u":"")~ElemType.stringof[0];Stupid me. I never tested this part for unsigneds. Of course, it should be (unsigned!(ElemType)?"u"~ElemType.stringof[1]:ElemType.stringof[0]); Sorry. --downs
Jul 31 2007
downs wrote:downs wrote:Still doesn't compile with DMD 1.020. So basically you're generating the GL headers using metaprogramming? Crazy stuff. <g>/// A very generic glColor. Supports static arrays. import std.traits; void glColor(T...)(T t) { static if (T.length==1) { alias T[0] Thingie; const bool vector=true; alias typeof(t[0][0]) ElemType; } else { alias T Thingie; const bool vector=false; alias typeof(t[0]) ElemType; } const char[] count=Thingie.length.stringof; static assert(count=="3"||count=="4"); const char[] type=(unsigned!(ElemType)?"u":"")~ElemType.stringof[0];Stupid me. I never tested this part for unsigneds. Of course, it should be (unsigned!(ElemType)?"u"~ElemType.stringof[1]:ElemType.stringof[0]); Sorry. --downs
Aug 02 2007