1 module glamour.util; 2 3 private { 4 import glamour.gl : GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, 5 GL_INT, GL_UNSIGNED_INT, GL_FLOAT, GL_DOUBLE, GLenum, 6 glGetError, GL_NO_ERROR, GL_INVALID_ENUM, GL_INVALID_VALUE, 7 GL_INVALID_OPERATION, GL_INVALID_FRAMEBUFFER_OPERATION, 8 GL_OUT_OF_MEMORY; 9 10 import std.traits : ReturnType; 11 12 debug { 13 import std.stdio : stderr; 14 import std.array : join; 15 import std.range : repeat; 16 import std..string : format; 17 } 18 } 19 20 21 debug { 22 static this() { 23 _error_callback = function void(GLenum error_code, string function_name, string args) { 24 stderr.writefln(`OpenGL function "%s(%s)" failed: "%s."`, 25 function_name, args, gl_error_string(error_code)); 26 }; 27 } 28 29 private void function(GLenum, string, string) _error_callback; 30 31 /// 32 void set_error_callback(void function(GLenum, string, string) cb) { 33 _error_callback = cb; 34 } 35 } else { 36 /// 37 void set_error_callback(void function(GLenum, string, string) cb) {} 38 } 39 40 /// checkgl checks in a debug build after every opengl call glGetError 41 /// and calls an error-callback which can be set with set_error_callback 42 /// a default is provided 43 ReturnType!func checkgl(alias func, Args...)(Args args) { 44 debug scope(success) { 45 GLenum error_code = glGetError(); 46 47 if(error_code != GL_NO_ERROR) { 48 _error_callback(error_code, func.stringof, format("%s".repeat(Args.length).join(", "), args)); 49 } 50 } 51 52 debug if(func is null) { 53 throw new Error("%s is null! OpenGL loaded? Required OpenGL version not supported?".format(func.stringof)); 54 } 55 56 return func(args); 57 } 58 59 /// Converts an OpenGL errorenum to a string 60 string gl_error_string(GLenum error) { 61 final switch(error) { 62 case GL_NO_ERROR: return "no error"; 63 case GL_INVALID_ENUM: return "invalid enum"; 64 case GL_INVALID_VALUE: return "invalid value"; 65 case GL_INVALID_OPERATION: return "invalid operation"; 66 //case GL_STACK_OVERFLOW: return "stack overflow"; 67 //case GL_STACK_UNDERFLOW: return "stack underflow"; 68 case GL_INVALID_FRAMEBUFFER_OPERATION: return "invalid framebuffer operation"; 69 case GL_OUT_OF_MEMORY: return "out of memory"; 70 } 71 assert(false, "invalid enum"); 72 } 73 74 75 /// D type to OpenGL enum 76 template type2glenum(T) { 77 static if(is(T == byte)) { 78 GLenum type2glenum = GL_BYTE; 79 } else static if(is(T == ubyte)) { 80 GLenum type2glenum = GL_UNSIGNED_BYTE; 81 } else static if(is(T == short)) { 82 GLenum type2glenum = GL_SHORT; 83 } else static if(is(T == ushort)) { 84 GLenum type2glenum = GL_UNSIGNED_SHORT; 85 } else static if(is(T == int)) { 86 GLenum type2glenum = GL_INT; 87 } else static if(is(T == uint)) { 88 GLenum type2glenum = GL_UNSIGNED_INT; 89 } else static if(is(T == float)) { 90 GLenum type2glenum = GL_FLOAT; 91 } else static if(is(T == double)) { 92 GLenum type2glenum = GL_DOUBLE; 93 } else { 94 static assert(false, T.stringof ~ " cannot be represented as GLenum"); 95 } 96 } 97 98 /// OpenGL enum to D type 99 template glenum2type(GLenum t) { 100 static if(t == GL_BYTE) { 101 alias byte glenum2type; 102 } else static if(t == GL_UNSIGNED_BYTE) { 103 alias ubyte glenum2type; 104 } else static if(t == GL_SHORT) { 105 alias short glenum2type; 106 } else static if(t == GL_UNSIGNED_SHORT) { 107 alias ushort glenum2type; 108 } else static if(t == GL_INT) { 109 alias int glenum2type; 110 } else static if(t == GL_UNSIGNED_INT) { 111 alias uint glenum2type; 112 } else static if(t == GL_FLOAT) { 113 alias float glenum2type; 114 } else static if(t == GL_DOUBLE) { 115 alias double glenum2type; 116 } else { 117 static assert(false, T.stringof ~ " cannot be represented as D-Type"); 118 } 119 } 120 121 unittest { 122 assert(GL_BYTE == type2glenum!byte); 123 assert(GL_UNSIGNED_BYTE == type2glenum!ubyte); 124 assert(GL_SHORT == type2glenum!short); 125 assert(GL_UNSIGNED_SHORT == type2glenum!ushort); 126 assert(GL_INT == type2glenum!int); 127 assert(GL_UNSIGNED_INT == type2glenum!uint); 128 assert(GL_FLOAT == type2glenum!float); 129 assert(GL_DOUBLE == type2glenum!double); 130 131 assert(is(byte : glenum2type!GL_BYTE)); 132 assert(is(ubyte : glenum2type!GL_UNSIGNED_BYTE)); 133 assert(is(short : glenum2type!GL_SHORT)); 134 assert(is(ushort : glenum2type!GL_UNSIGNED_SHORT)); 135 assert(is(int : glenum2type!GL_INT)); 136 assert(is(uint : glenum2type!GL_UNSIGNED_INT)); 137 assert(is(float : glenum2type!GL_FLOAT)); 138 assert(is(double : glenum2type!GL_DOUBLE)); 139 } 140 141 /// OpenGL enum to D type size 142 template glenum2sizect(GLenum t) { 143 static if(t == GL_BYTE) { 144 enum glenum2sizect = byte.sizeof; 145 } else static if(t == GL_UNSIGNED_BYTE) { 146 enum glenum2sizect = ubyte.sizeof; 147 } else static if(t == GL_SHORT) { 148 enum glenum2sizect = short.sizeof; 149 } else static if(t == GL_UNSIGNED_SHORT) { 150 enum glenum2sizect = ushort.sizeof; 151 } else static if(t == GL_INT) { 152 enum glenum2sizect = int.sizeof; 153 } else static if(t == GL_UNSIGNED_INT) { 154 enum glenum2sizect = uint.sizeof; 155 } else static if(t == GL_FLOAT) { 156 enum glenum2sizect = float.sizeof; 157 } else static if(t == GL_DOUBLE) { 158 enum glenum2sizect = double.sizeof; 159 } else { 160 static assert(false, T.stringof ~ " cannot be represented as D-Type"); 161 } 162 } 163 164 /// ditto 165 int glenum2size(GLenum t) { 166 switch(t) { 167 case GL_BYTE: return glenum2sizect!GL_BYTE; 168 case GL_UNSIGNED_BYTE: return glenum2sizect!GL_UNSIGNED_BYTE; 169 case GL_SHORT: return glenum2sizect!GL_SHORT; 170 case GL_UNSIGNED_SHORT: return glenum2sizect!GL_UNSIGNED_SHORT; 171 case GL_INT: return glenum2sizect!GL_INT; 172 case GL_UNSIGNED_INT: return glenum2sizect!GL_UNSIGNED_INT; 173 case GL_FLOAT: return glenum2sizect!GL_FLOAT; 174 case GL_DOUBLE: return glenum2sizect!GL_DOUBLE; 175 default: throw new Exception("Unknown GLenum"); 176 } 177 }