www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - "This is madness!" Red Book OpenGL Example 1-3, done using templates

reply downs <default_357-line yahoo.de> writes:
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
parent reply downs <default_357-line yahoo.de> writes:
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];

Of course, it should be (unsigned!(ElemType)?"u"~ElemType.stringof[1]:ElemType.stringof[0]); Sorry. --downs
Jul 31 2007
parent Don Clugston <dac nospam.com.au> writes:
downs wrote:
 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];

Of course, it should be (unsigned!(ElemType)?"u"~ElemType.stringof[1]:ElemType.stringof[0]); Sorry. --downs

Still doesn't compile with DMD 1.020. So basically you're generating the GL headers using metaprogramming? Crazy stuff. <g>
Aug 02 2007