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 }