proper ogl funcion loader
[dd2d.git] / glutils.d
blob78446c05c4580990f53edcee3b29cae46dcc6e8f
1 module glutils;
2 private:
3 import glbinds;
4 import arsd.color;
5 import arsd.png;
8 // ////////////////////////////////////////////////////////////////////////// //
9 /*
10 __gshared GLuint glLastUsedTexture = 0;
12 public void useTexture (GLuint tid) {
13 pragma(inline, true);
14 if (glLastUsedTexture != tid) {
15 glLastUsedTexture = tid;
16 glBindTexture(GL_TEXTURE_2D, tid);
20 public void useTexture (Texture tex) { pragma(inline, true); useTexture(tex !is null ? tex.tid : 0); }
24 // ////////////////////////////////////////////////////////////////////////// //
25 public final class Texture {
26 GLuint tid;
27 int width, height;
29 // default: repeat, linear
30 enum Option : int {
31 Repeat,
32 Clamp,
33 ClampBorder,
34 Linear,
35 Nearest,
36 UByte,
37 Float, // create floating point texture
40 this (string fname, in Option[] opts...) { loadPng(fname, opts); }
41 this (int w, int h, in Option[] opts...) { createEmpty(w, h, opts); }
42 this (TrueColorImage aimg, Option[] opts...) { createFromImage(aimg, opts); }
43 ~this () { clear(); }
46 void clear () {
47 if (tid) {
48 //useTexture(tid);
49 glBindTexture(GL_TEXTURE_2D, tid);
50 glDeleteTextures(1, &tid);
51 //useTexture(0);
52 glBindTexture(GL_TEXTURE_2D, 0);
53 tid = 0;
54 width = 0;
55 height = 0;
59 private static void processOpt (ref GLuint wrapOpt, ref GLuint filterOpt, ref GLuint ttype, Option[] opts...) {
60 foreach (immutable opt; opts) {
61 switch (opt) with (Option) {
62 case Repeat: wrapOpt = GL_REPEAT; break;
63 case Clamp: wrapOpt = GL_CLAMP_TO_EDGE; break;
64 case ClampBorder: wrapOpt = GL_CLAMP_TO_BORDER; break;
65 case Linear: filterOpt = GL_LINEAR; break;
66 case Nearest: filterOpt = GL_NEAREST; break;
67 case UByte: ttype = GL_UNSIGNED_BYTE; break;
68 case Float: ttype = GL_FLOAT; break;
69 default:
74 void createEmpty (int w, int h, in Option[] opts...) {
75 import core.stdc.stdlib : malloc, free;
76 assert(w > 0);
77 assert(h > 0);
78 clear();
80 GLuint wrapOpt = GL_REPEAT;
81 GLuint filterOpt = GL_LINEAR;
82 GLuint ttype = GL_UNSIGNED_BYTE;
83 processOpt(wrapOpt, filterOpt, ttype);
85 glGenTextures(1, &tid);
86 //useTexture(tid);
87 glBindTexture(GL_TEXTURE_2D, tid);
88 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapOpt);
89 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapOpt);
90 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterOpt);
91 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterOpt);
92 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
93 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
94 GLfloat[4] bclr = 0.0;
95 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
96 ubyte* ptr = null;
97 scope(exit) if (ptr !is null) free(ptr);
99 import core.stdc.string : memset;
100 ptr = cast(ubyte*)malloc(w*h*4*(ttype == GL_UNSIGNED_BYTE ? 1 : 4));
101 memset(ptr, 0, w*h*4);
103 glTexImage2D(GL_TEXTURE_2D, 0, (ttype == GL_FLOAT ? GL_RGBA16F : GL_RGBA), w, h, 0, GL_BGRA, ttype, ptr);
104 width = w;
105 height = h;
108 void createFromImage (TrueColorImage img, in Option[] opts...) {
109 scope(failure) clear;
110 createEmpty(img.width, img.height, opts);
111 Color[] cl = new Color[](img.width*img.height);
112 foreach (int y; 0..img.height) {
113 int iy = img.height-y-1;
114 cl[y*img.width..(y+1)*img.width] = img.imageData.colors[iy*img.width..(iy+1)*img.width];
116 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.width, img.height, GL_RGBA, GL_UNSIGNED_BYTE, /*img.imageData.bytes.ptr*/cl.ptr);
119 void loadPng (string fname, in Option[] opts...) {
120 scope(failure) clear;
121 auto img = readPng(fname).getAsTrueColorImage;
122 createFromImage(img, opts);
125 void activate () { if (tid) glBindTexture(GL_TEXTURE_2D, tid); }
126 void deactivate () { if (tid) glBindTexture(GL_TEXTURE_2D, 0); }
130 // ////////////////////////////////////////////////////////////////////////// //
131 public final class TextureCube {
132 GLuint tid;
133 int width, height;
135 this (string fname, in Texture.Option[] opts...) { loadPng(fname, opts); }
136 this (int w, int h, in Texture.Option[] opts...) { createEmpty(w, h, opts); }
137 ~this () { clear(); }
140 void clear () {
141 if (tid) {
142 glBindTexture(GL_TEXTURE_CUBE_MAP, tid);
143 glDeleteTextures(1, &tid);
144 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
145 tid = 0;
146 width = 0;
147 height = 0;
151 void createEmpty (int w, int h, in Texture.Option[] opts...) {
152 import core.stdc.stdlib : malloc, free;
153 assert(w > 0);
154 assert(h > 0);
156 GLuint wrapOpt = GL_CLAMP_TO_EDGE;
157 GLuint filterOpt = GL_LINEAR;
158 GLuint ttype = GL_FLOAT;
159 Texture.processOpt(wrapOpt, filterOpt, ttype);
161 glGenTextures(1, &tid);
162 glBindTexture(GL_TEXTURE_CUBE_MAP, tid);
163 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, wrapOpt);
164 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, wrapOpt);
165 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, wrapOpt);
166 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, filterOpt);
167 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, filterOpt);
168 GLfloat[4] bclr = 0.0;
169 glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
171 ubyte* ptr = null;
172 scope(exit) if (ptr !is null) free(ptr);
174 import core.stdc.string : memset;
175 ptr = cast(ubyte*)malloc(w*h*4*4);
176 memset(ptr, 0, w*h*4);
178 foreach (int idx; 0..6) {
179 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+idx, 0, (ttype == GL_FLOAT ? GL_RGBA16F : GL_RGBA), w, h, 0, GL_BGRA, GL_FLOAT, ptr);
181 width = w;
182 height = h;
185 void loadPng (string fname, in Texture.Option[] opts...) {
186 clear();
187 TrueColorImage[6] img;
188 foreach (int idx; 0..6) {
189 import std.string : format;
190 img[idx] = readPng(fname.format(idx)).getAsTrueColorImage;
191 if (idx > 0) {
192 if (img[idx].width != img[idx-1].width || img[idx].height != img[idx-1].height) {
193 assert(0, "cubemap fucked: "~fname);
197 createEmpty(img[0].width, img[0].height, opts);
198 foreach (int idx; 0..6) {
199 glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+idx, 0, 0, 0, img[idx].width, img[idx].height, GL_RGBA, GL_UNSIGNED_BYTE, img[idx].imageData.bytes.ptr);
203 void activate () { if (tid) glBindTexture(GL_TEXTURE_CUBE_MAP, tid); }
204 void deactivate () { if (tid) glBindTexture(GL_TEXTURE_CUBE_MAP, 0); }
208 // ////////////////////////////////////////////////////////////////////////// //
209 public final class FBO {
210 int width;
211 int height;
212 GLuint fbo;
213 Texture tex;
215 this (Texture atex) { createWithTexture(atex); }
217 this (int wdt, int hgt, Texture.Option[] opts...) {
218 createWithTexture(new Texture(wdt, hgt, opts));
221 void activate () { if (fbo) glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); }
222 void deactivate () { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); }
224 private:
225 void createWithTexture (Texture atex) {
226 assert(atex !is null && atex.tid);
228 tex = atex;
229 glGenFramebuffersEXT(1, &fbo);
230 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
231 // attach 2D texture to this FBO
232 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex.tid, 0);
235 glGenRenderbuffersEXT(1, &fboDepthId);
236 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthId);
237 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, LightSize, LightSize);
238 // attach depth buffer to FBO
239 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fboDepthId);
243 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
244 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) assert(0, "framebuffer fucked!");
246 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
248 width = tex.width;
249 height = tex.height;
254 // ////////////////////////////////////////////////////////////////////////// //
255 public struct SVec2I { int x, y; }
256 public struct SVec3I { int x, y, z; }
257 public struct SVec4I { int x, y, z, w; }
259 public struct SVec2F { float x, y; }
260 public struct SVec3F { float x, y, z; }
261 public struct SVec4F { float x, y, z, w; }
264 public final class Shader {
265 string shaderName;
266 GLuint prg = 0;
267 GLint[string] vars;
269 this (string ashaderName, const(char)[] src) {
270 shaderName = ashaderName;
271 if (src.length > int.max) {
272 import core.stdc.stdio : printf;
273 printf("shader '%.*s' code too long!", cast(uint)ashaderName.length, ashaderName.ptr);
274 assert(0);
276 auto shaderId = glCreateShader(GL_FRAGMENT_SHADER);
277 auto sptr = src.ptr;
278 GLint slen = cast(int)src.length;
279 glShaderSource(shaderId, 1, &sptr, &slen);
280 glCompileShader(shaderId);
281 GLint success = 0;
282 glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
283 if (!success) {
284 import core.stdc.stdio : printf;
285 import core.stdc.stdlib : malloc;
286 GLint logSize = 0;
287 glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &logSize);
288 auto logStrZ = cast(GLchar*)malloc(logSize);
289 glGetShaderInfoLog(shaderId, logSize, null, logStrZ);
290 printf("shader '%.*s' compilation fucked!\n%s\n", cast(uint)ashaderName.length, ashaderName.ptr, logStrZ);
291 assert(0);
293 prg = glCreateProgram();
294 glAttachShader(prg, shaderId);
295 glLinkProgram(prg);
298 GLint varId(NT) (NT vname) if (is(NT == char[]) || is(NT == const(char)[]) || is(NT == immutable(char)[])) {
299 GLint id = -1;
300 if (vname.length > 0 && vname.length <= 128) {
301 if (auto vi = vname in vars) {
302 id = *vi;
303 } else {
304 char[129] buf = void;
305 buf[0..vname.length] = vname[];
306 buf[vname.length] = 0;
307 id = glGetUniformLocation(prg, buf.ptr);
308 //{ import core.stdc.stdio; printf("[%.*s.%s]=%i\n", cast(uint)shaderName.length, shaderName.ptr, buf.ptr, id); }
309 static if (is(NT == immutable(char)[])) {
310 vars[vname.idup] = id;
311 } else {
312 vars[vname.idup] = id;
314 if (id < 0) {
315 import core.stdc.stdio : printf;
316 printf("shader '%.*s': unknown variable '%.*s'\n", cast(uint)shaderName.length, shaderName.ptr, cast(uint)vname.length, vname.ptr);
320 return id;
323 // get unified var id
324 GLint opIndex(NT) (NT vname) if (is(NT == char[]) || is(NT == const(char)[]) || is(NT == immutable(char)[])) {
325 auto id = varId(vname);
326 if (id < 0) {
327 import core.stdc.stdio : printf;
328 printf("shader '%.*s': unknown variable '%.*s'\n", cast(uint)shaderName.length, shaderName.ptr, cast(uint)vname.length, vname.ptr);
329 assert(0);
331 return id;
334 private import std.traits;
335 void opIndexAssign(T, NT) (in auto ref T v, NT vname)
336 if (((isIntegral!T && T.sizeof <= 4) || (isFloatingPoint!T && T.sizeof == float.sizeof) || isBoolean!T ||
337 is(T : SVec2I) || is(T : SVec3I) || is(T : SVec4I) ||
338 is(T : SVec2F) || is(T : SVec3F) || is(T : SVec4F)) &&
339 (is(NT == char[]) || is(NT == const(char)[]) || is(NT == immutable(char)[])))
341 auto id = varId(vname);
342 if (id < 0) return;
343 //{ import core.stdc.stdio; printf("setting '%.*s' (%d)\n", cast(uint)vname.length, vname.ptr, id); }
344 static if (isIntegral!T || isBoolean!T) glUniform1i(id, cast(int)v);
345 else static if (isFloatingPoint!T) glUniform1f(id, cast(float)v);
346 else static if (is(SVec2I : T)) glUniform2i(id, cast(int)v.x, cast(int)v.y);
347 else static if (is(SVec3I : T)) glUniform3i(id, cast(int)v.x, cast(int)v.y, cast(int)v.z);
348 else static if (is(SVec4I : T)) glUniform4i(id, cast(int)v.x, cast(int)v.y, cast(int)v.z, cast(int)v.w);
349 else static if (is(SVec2F : T)) glUniform2f(id, cast(float)v.x, cast(float)v.y);
350 else static if (is(SVec3F : T)) glUniform3f(id, cast(float)v.x, cast(float)v.y, cast(float)v.z);
351 else static if (is(SVec4F : T)) glUniform4f(id, cast(float)v.x, cast(float)v.y, cast(float)v.z, cast(float)v.w);
352 else static assert(0, "wtf?!");
355 void activate () { if (prg) glUseProgram(prg); }
356 void deactivate () { glUseProgram(0); }
360 // ////////////////////////////////////////////////////////////////////////// //
361 //private import std.traits;
363 public:
364 void exec(TO, TG) (TO obj, TG dg) if (is(typeof(() { dg(); })) && is(typeof(() { obj.activate(); obj.deactivate(); }))) {
365 obj.activate();
366 scope(exit) obj.deactivate();
367 dg();
371 // ////////////////////////////////////////////////////////////////////////// //
372 void orthoCamera (int wdt, int hgt) {
373 glMatrixMode(GL_PROJECTION); // for ortho camera
374 glLoadIdentity();
375 // left, right, bottom, top, near, far
376 glOrtho(0, wdt, 0, hgt, -1, 1);
377 glViewport(0, 0, wdt, hgt);
379 //glTranslatef(-cx, -cy, 0.0f);
382 // origin is texture left top
383 void drawAtXY (GLuint tid, int x, int y, int w, int h, bool mirrorX=false, bool mirrorY=false) {
384 if (!tid || w < 1 || h < 1) return;
385 w += x;
386 h += y;
387 if (mirrorX) { int tmp = x; x = w; w = tmp; }
388 if (mirrorY) { int tmp = y; y = h; h = tmp; }
389 glBindTexture(GL_TEXTURE_2D, tid);
390 glBegin(GL_QUADS);
391 glTexCoord2f(0.0f, 0.0f); glVertex2i(x, y); // bottom-left
392 glTexCoord2f(1.0f, 0.0f); glVertex2i(w, y); // bottom-right
393 glTexCoord2f(1.0f, 1.0f); glVertex2i(w, h); // top-right
394 glTexCoord2f(0.0f, 1.0f); glVertex2i(x, h); // top-left
395 glEnd();
399 // origin is texture center
400 void drawAtXYC (Texture tex, int x, int y, bool mirrorX=false, bool mirrorY=false) {
401 if (tex is null || !tex.tid) return;
402 x -= tex.width/2;
403 y -= tex.height/2;
404 drawAtXY(tex.tid, x, y, tex.width, tex.height, mirrorX, mirrorY);
408 // origin is texture left top
409 void drawAtXY (Texture tex, int x, int y, bool mirrorX=false, bool mirrorY=false) {
410 if (tex is null || !tex.tid) return;
411 drawAtXY(tex.tid, x, y, tex.width, tex.height, mirrorX, mirrorY);