downs
| 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;
}
|