cosmetix
[dd2d.git] / glutils.d
blob977bf650cdda2de4cea283890017616edc3c0558
1 /* DooM2D: Midnight on the Firing Line
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module glutils is aliced;
19 private:
20 import iv.glbinds;
21 import arsd.color;
22 import arsd.png;
24 import wadarc;
27 // ////////////////////////////////////////////////////////////////////////// //
28 __gshared bool glutilsShowShaderWarnings = false; // shut up!
31 __gshared GLuint glLastUsedTexture = 0;
33 public void useTexture (GLuint tid) {
34 pragma(inline, true);
35 if (glLastUsedTexture != tid) {
36 glLastUsedTexture = tid;
37 glBindTexture(GL_TEXTURE_2D, tid);
41 public void useTexture (Texture tex) { pragma(inline, true); useTexture(tex !is null ? tex.tid : 0); }
45 public TrueColorImage loadPngFile (string fname) {
46 auto fl = openFile(fname);
47 auto sz = fl.size;
48 if (sz < 4 || sz > 32*1024*1024) throw new Exception("invalid png file size: '"~fname~"'");
49 if (sz == 0) return null;
50 auto res = new ubyte[](cast(uint)sz);
51 if (fl.rawRead(res[]).length != res.length) throw new Exception("error reading png file '"~fname~"'");
52 return imageFromPng(readPng(res)).getAsTrueColorImage;
56 // ////////////////////////////////////////////////////////////////////////// //
57 class OpenGLObject {
58 abstract @property uint id () const pure nothrow @nogc;
60 final void activate () nothrow @nogc {
61 if (gloSP >= gloStack.length) assert(0, "glo stack overflow");
62 gloStack.ptr[gloSP++] = this;
63 activateObj();
66 final void deactivate () nothrow @nogc {
67 foreach_reverse (usize idx; 0..gloStack.length) {
68 if (gloStack.ptr[idx] is this) {
69 // find previous object of this type
70 foreach_reverse (usize pidx; 0..idx) {
71 if (typeid(gloStack.ptr[pidx]) is typeid(gloStack.ptr[idx])) {
72 gloStack.ptr[pidx].activateObj();
73 removeFromStack(pidx);
74 return;
77 deactivateObj();
78 removeFromStack(idx);
79 return;
82 assert(0, "trying to deactivate inactive object");
85 protected:
86 abstract void activateObj () nothrow @nogc;
87 abstract void deactivateObj () nothrow @nogc;
91 __gshared OpenGLObject[1024] gloStack;
92 __gshared uint gloSP = 0;
95 public void gloStackClear () nothrow @nogc {
96 glBindTexture(GL_TEXTURE_2D, 0);
97 //glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
98 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
99 glUseProgram(0);
103 void removeFromStack (usize idx) nothrow @nogc {
104 if (idx >= gloSP) return;
105 if (idx != gloSP-1) {
106 import core.stdc.string : memmove;
107 memmove(gloStack.ptr+idx, gloStack.ptr+idx+1, (gloSP-idx-1)*gloStack[0].sizeof);
109 --gloSP;
113 // ////////////////////////////////////////////////////////////////////////// //
114 public final class Texture : OpenGLObject {
115 GLuint tid;
116 int width, height;
118 override @property uint id () const pure nothrow @nogc => tid;
120 // default: repeat, linear
121 enum Option : int {
122 Repeat,
123 Clamp,
124 ClampBorder,
125 Linear,
126 Nearest,
127 UByte,
128 Float, // create floating point texture
129 Depth, // FBO: attach depth buffer
132 this (string fname, in Option[] opts...) { loadPng(fname, opts); }
133 this (int w, int h, in Option[] opts...) { createIntr(w, h, null, opts); }
134 this (TrueColorImage aimg, Option[] opts...) { createIntr(aimg.width, aimg.height, aimg, opts); }
135 ~this () { clear(); }
138 void clear () {
139 if (tid) {
140 //useTexture(tid);
141 glBindTexture(GL_TEXTURE_2D, tid);
142 glDeleteTextures(1, &tid);
143 //useTexture(0);
144 glBindTexture(GL_TEXTURE_2D, 0);
145 tid = 0;
146 width = 0;
147 height = 0;
151 private static void processOpt (GLuint* wrapOpt, GLuint* filterOpt, GLuint* ttype, in Option[] opts...) {
152 foreach (immutable opt; opts) {
153 switch (opt) with (Option) {
154 case Repeat: *wrapOpt = GL_REPEAT; break;
155 case Clamp: *wrapOpt = GL_CLAMP_TO_EDGE; break;
156 case ClampBorder: *wrapOpt = GL_CLAMP_TO_BORDER; break;
157 case Linear: *filterOpt = GL_LINEAR; break;
158 case Nearest: *filterOpt = GL_NEAREST; break;
159 case UByte: *ttype = GL_UNSIGNED_BYTE; break;
160 case Float: *ttype = GL_FLOAT; break;
161 default:
166 void createIntr (int w, int h, TrueColorImage img, in Option[] opts...) {
167 import core.stdc.stdlib : malloc, free;
168 assert(w > 0);
169 assert(h > 0);
170 clear();
172 GLuint wrapOpt = GL_REPEAT;
173 GLuint filterOpt = GL_LINEAR;
174 GLuint ttype = GL_UNSIGNED_BYTE;
175 processOpt(&wrapOpt, &filterOpt, &ttype, opts);
177 glGenTextures(1, &tid);
178 //useTexture(tid);
179 glBindTexture(GL_TEXTURE_2D, tid);
180 scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
181 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapOpt);
182 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapOpt);
183 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterOpt);
184 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterOpt);
185 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
186 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
187 GLfloat[4] bclr = 0.0;
188 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
189 if (img !is null && img.width == w && img.height == h) {
190 // create straight from image
191 glTexImage2D(GL_TEXTURE_2D, 0, (ttype == GL_FLOAT ? GL_RGBA16F : GL_RGBA), w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.imageData.bytes.ptr);
192 } else {
193 // create empty texture
194 ubyte* ptr = null;
195 scope(exit) if (ptr !is null) free(ptr);
197 import core.stdc.string : memset;
198 ptr = cast(ubyte*)malloc(w*h*4);
199 if (ptr !is null) memset(ptr, 0, w*h*4);
201 glTexImage2D(GL_TEXTURE_2D, 0, (ttype == GL_FLOAT ? GL_RGBA16F : GL_RGBA), w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
202 if (img !is null && img.width > 0 && img.height > 0) {
203 // setup from image
204 //TODO: dunno if it's ok to use images bigger than texture here
205 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.width, img.height, GL_RGBA, GL_UNSIGNED_BYTE, img.imageData.bytes.ptr);
206 // the following is ok too
207 //glBindTexture(GL_TEXTURE_2D, 0);
208 //glTextureSubImage2D(tid, 0, 0, 0, img.width, img.height, GL_RGBA, GL_UNSIGNED_BYTE, img.imageData.bytes.ptr);
211 width = w;
212 height = h;
215 void setFromImage (TrueColorImage img, int x=0, int y=0) {
216 if (img is null || !tid || img.height < 1 || img.width < 1) return;
217 if (x >= width || y >= height) return;
218 if (x+img.width <= 0 || y+img.height <= 0) return; //TODO: overflow
219 if (x >= 0 && y >= 0 && x+img.width <= width && y+img.height <= height) {
220 // easy case, just copy it
221 glTextureSubImage2D(tid, 0, x, y, img.width, img.height, GL_RGBA, GL_UNSIGNED_BYTE, img.imageData.bytes.ptr);
222 } else {
223 import core.stdc.stdlib : malloc, free;
224 import core.stdc.string : memset, memcpy;
225 // hard case, have to build the temp region
226 uint* src = cast(uint*)img.imageData.bytes.ptr;
227 // calc x skip and effective width
228 int rwdt = img.width;
229 if (x < 0) {
230 rwdt += x;
231 src -= x; // as `x` is negative here
232 x = 0;
234 if (x+rwdt > width) rwdt = width-x;
235 // calc y skip and effective height
236 int rhgt = img.height;
237 if (y < 0) {
238 rhgt += y;
239 src -= y*img.width; // as `y` is negative here
240 y = 0;
242 if (y+rhgt > height) rhgt = height-y;
243 assert(rwdt > 0 && rhgt > 0);
244 uint* ptr = null;
245 scope(exit) if (ptr !is null) free(ptr);
246 ptr = cast(uint*)malloc(rwdt*rhgt*4);
247 if (ptr is null) assert(0, "out of memory in `Texture.setFromImage()`");
248 // now copy
249 auto d = ptr;
250 foreach (immutable _; 0..rhgt) {
251 memcpy(d, src, rwdt*4);
252 src += img.width;
253 d += rwdt;
255 glTextureSubImage2D(tid, 0, x, y, rwdt, rhgt, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
259 void loadPng (string fname, in Option[] opts...) {
260 scope(failure) clear;
261 auto img = loadPngFile(fname);
262 createIntr(img.width, img.height, img, opts);
265 protected:
266 override void activateObj () nothrow @nogc { /*if (tid)*/ glBindTexture(GL_TEXTURE_2D, tid); }
267 override void deactivateObj () nothrow @nogc { /*if (tid)*/ glBindTexture(GL_TEXTURE_2D, 0); }
271 // ////////////////////////////////////////////////////////////////////////// //
273 public final class TextureCube : OpenGLObject {
274 GLuint tid;
275 int width, height;
277 override @property uint id () const pure nothrow @nogc => tid;
279 this (string fname, in Texture.Option[] opts...) { loadPng(fname, opts); }
280 this (int w, int h, in Texture.Option[] opts...) { createIntr(w, h, opts); }
281 ~this () { clear(); }
284 void clear () {
285 if (tid) {
286 glBindTexture(GL_TEXTURE_CUBE_MAP, tid);
287 glDeleteTextures(1, &tid);
288 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
289 tid = 0;
290 width = 0;
291 height = 0;
295 void createIntr (int w, int h, in Texture.Option[] opts...) {
296 import core.stdc.stdlib : malloc, free;
297 assert(w > 0);
298 assert(h > 0);
300 GLuint wrapOpt = GL_CLAMP_TO_EDGE;
301 GLuint filterOpt = GL_LINEAR;
302 GLuint ttype = GL_FLOAT;
303 Texture.processOpt(&wrapOpt, &filterOpt, &ttype, opts);
305 glGenTextures(1, &tid);
306 glBindTexture(GL_TEXTURE_CUBE_MAP, tid);
307 scope(exit) glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
308 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, wrapOpt);
309 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, wrapOpt);
310 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, wrapOpt);
311 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, filterOpt);
312 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, filterOpt);
313 GLfloat[4] bclr = 0.0;
314 glTexParameterfv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
316 ubyte* ptr = null;
317 scope(exit) if (ptr !is null) free(ptr);
319 import core.stdc.string : memset;
320 ptr = cast(ubyte*)malloc(w*h*4*4);
321 memset(ptr, 0, w*h*4);
323 foreach (int idx; 0..6) {
324 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+idx, 0, (ttype == GL_FLOAT ? GL_RGBA16F : GL_RGBA), w, h, 0, GL_BGRA, GL_FLOAT, ptr);
326 width = w;
327 height = h;
330 void loadPng (string fname, in Texture.Option[] opts...) {
331 clear();
332 TrueColorImage[6] img;
333 foreach (int idx; 0..6) {
334 import std.string : format;
335 img[idx] = loadPngFile(fname.format(idx));
336 if (idx > 0) {
337 if (img[idx].width != img[idx-1].width || img[idx].height != img[idx-1].height) {
338 assert(0, "cubemap fucked: "~fname);
342 createIntr(img[0].width, img[0].height, opts);
343 glBindTexture(GL_TEXTURE_CUBE_MAP, tid);
344 scope(exit) glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
345 foreach (int idx; 0..6) {
346 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);
350 void activate () { if (tid) glBindTexture(GL_TEXTURE_CUBE_MAP, tid); }
351 void deactivate () { if (tid) glBindTexture(GL_TEXTURE_CUBE_MAP, 0); }
356 // ////////////////////////////////////////////////////////////////////////// //
357 public final class FBO : OpenGLObject {
358 int width;
359 int height;
360 GLuint fbo;
361 Texture tex;
362 Texture texdepth;
363 //Texture.Option[] xopts;
365 override @property uint id () const pure nothrow @nogc => fbo;
367 this (Texture atex) { createWithTexture(atex); }
369 this (int wdt, int hgt, Texture.Option[] opts...) {
370 //xopts = opts.dup;
371 createWithTexture(new Texture(wdt, hgt, opts), opts);
374 ~this () {
375 //FIXME: this may be wrong, as texture may be already destroyed (and it's wrong too); we need refcount for textures
376 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0);
377 glDeleteFramebuffersEXT(1, &fbo);
378 fbo = 0;
381 void clear () {
382 if (fbo) {
383 // detach texture
384 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0);
385 glDeleteFramebuffersEXT(1, &fbo);
386 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
387 glDeleteFramebuffersEXT(1, &fbo);
388 fbo = 0;
390 if (tex !is null) tex.clear();
391 tex = null;
392 if (texdepth !is null) texdepth.clear();
393 texdepth = null;
396 protected:
397 override void activateObj () nothrow @nogc { /*if (fbo)*/ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); }
398 override void deactivateObj () nothrow @nogc { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); }
400 // this will deactivate current FBO!
402 void replaceTexture (Texture ntex) {
403 if (tex !is null) {
404 if (ntex !is null && ntex.tid == tex.tid) return;
405 } else {
406 if (ntex is null) return;
408 glGenFramebuffersEXT(1, &fbo);
409 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
410 scope(exit) glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
411 // detach texture
412 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0);
413 glDeleteFramebuffersEXT(1, &fbo);
414 fbo = 0;
415 tex = ntex;
416 // attach texture
417 if (tex !is null) {
418 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex.tid, 0);
420 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
421 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) assert(0, "framebuffer fucked!");
427 private:
428 void createWithTexture (Texture atex, Texture.Option[] opts...) {
429 assert(atex !is null && atex.tid);
431 tex = atex;
432 glGenFramebuffersEXT(1, &fbo);
433 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
434 scope(exit) glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
435 // attach 2D texture to this FBO
436 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex.tid, 0);
437 // GL_COLOR_ATTACHMENT0_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_STENCIL_ATTACHMENT_EXT
439 void createDepth () {
440 uint fboDepthId;
441 glGenRenderbuffersEXT(1, &fboDepthId);
442 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthId);
443 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT/*24*/, atex.width, atex.height);
444 // attach depth buffer to FBO
445 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fboDepthId);
446 // kill it (we don't need it anymore)
447 glDeleteRenderbuffersEXT(1, &fboDepthId);
451 foreach (Texture.Option opt; opts) {
452 if (opt == Texture.Option.Depth) {
453 createDepth();
454 break;
458 //createDepth();
461 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
462 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) assert(0, "framebuffer fucked!");
464 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
466 width = tex.width;
467 height = tex.height;
472 // ////////////////////////////////////////////////////////////////////////// //
473 public struct SVec2I { int x, y; }
474 public struct SVec3I { int x, y, z; }
475 public struct SVec4I { int x, y, z, w; }
477 public struct SVec2F { float x, y; }
478 public struct SVec3F { float x, y, z; }
479 public struct SVec4F { float x, y, z, w; }
482 public final class Shader : OpenGLObject {
483 string shaderName;
484 GLuint prg = 0;
485 GLint[string] vars;
487 override @property uint id () const pure nothrow @nogc => prg;
489 this (string ashaderName, const(char)[] src) {
490 shaderName = ashaderName;
491 if (src.length > int.max) {
492 import core.stdc.stdio : printf;
493 printf("shader '%.*s' code too long!", cast(uint)ashaderName.length, ashaderName.ptr);
494 assert(0);
496 auto shaderId = glCreateShader(GL_FRAGMENT_SHADER);
497 auto sptr = src.ptr;
498 GLint slen = cast(int)src.length;
499 glShaderSource(shaderId, 1, &sptr, &slen);
500 glCompileShader(shaderId);
501 GLint success = 0;
502 glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
503 if (!success || glutilsShowShaderWarnings) {
504 import core.stdc.stdio : printf;
505 import core.stdc.stdlib : malloc, free;
506 GLint logSize = 0;
507 glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &logSize);
508 if (logSize > 0) {
509 auto logStrZ = cast(GLchar*)malloc(logSize);
510 glGetShaderInfoLog(shaderId, logSize, null, logStrZ);
511 printf("shader '%.*s' compilation messages:\n%s\n", cast(uint)ashaderName.length, ashaderName.ptr, logStrZ);
512 free(logStrZ);
515 if (!success) assert(0);
516 prg = glCreateProgram();
517 glAttachShader(prg, shaderId);
518 glLinkProgram(prg);
521 GLint varId(NT) (NT vname) if (is(NT == char[]) || is(NT == const(char)[]) || is(NT == immutable(char)[])) {
522 GLint id = -1;
523 if (vname.length > 0 && vname.length <= 128) {
524 if (auto vi = vname in vars) {
525 id = *vi;
526 } else {
527 char[129] buf = void;
528 buf[0..vname.length] = vname[];
529 buf[vname.length] = 0;
530 id = glGetUniformLocation(prg, buf.ptr);
531 //{ import core.stdc.stdio; printf("[%.*s.%s]=%i\n", cast(uint)shaderName.length, shaderName.ptr, buf.ptr, id); }
532 static if (is(NT == immutable(char)[])) {
533 vars[vname.idup] = id;
534 } else {
535 vars[vname.idup] = id;
537 if (id < 0) {
538 import core.stdc.stdio : printf;
539 printf("shader '%.*s': unknown variable '%.*s'\n", cast(uint)shaderName.length, shaderName.ptr, cast(uint)vname.length, vname.ptr);
543 return id;
546 // get unified var id
547 GLint opIndex(NT) (NT vname) if (is(NT == char[]) || is(NT == const(char)[]) || is(NT == immutable(char)[])) {
548 auto id = varId(vname);
549 if (id < 0) {
550 import core.stdc.stdio : printf;
551 printf("shader '%.*s': unknown variable '%.*s'\n", cast(uint)shaderName.length, shaderName.ptr, cast(uint)vname.length, vname.ptr);
552 assert(0);
554 return id;
557 private import std.traits;
558 void opIndexAssign(T, NT) (in auto ref T v, NT vname)
559 if (((isIntegral!T && T.sizeof <= 4) || (isFloatingPoint!T && T.sizeof == float.sizeof) || isBoolean!T ||
560 is(T : SVec2I) || is(T : SVec3I) || is(T : SVec4I) ||
561 is(T : SVec2F) || is(T : SVec3F) || is(T : SVec4F)) &&
562 (is(NT == char[]) || is(NT == const(char)[]) || is(NT == immutable(char)[])))
564 auto id = varId(vname);
565 if (id < 0) return;
566 //{ import core.stdc.stdio; printf("setting '%.*s' (%d)\n", cast(uint)vname.length, vname.ptr, id); }
567 static if (isIntegral!T || isBoolean!T) glUniform1i(id, cast(int)v);
568 else static if (isFloatingPoint!T) glUniform1f(id, cast(float)v);
569 else static if (is(SVec2I : T)) glUniform2i(id, cast(int)v.x, cast(int)v.y);
570 else static if (is(SVec3I : T)) glUniform3i(id, cast(int)v.x, cast(int)v.y, cast(int)v.z);
571 else static if (is(SVec4I : T)) glUniform4i(id, cast(int)v.x, cast(int)v.y, cast(int)v.z, cast(int)v.w);
572 else static if (is(SVec2F : T)) glUniform2f(id, cast(float)v.x, cast(float)v.y);
573 else static if (is(SVec3F : T)) glUniform3f(id, cast(float)v.x, cast(float)v.y, cast(float)v.z);
574 else static if (is(SVec4F : T)) glUniform4f(id, cast(float)v.x, cast(float)v.y, cast(float)v.z, cast(float)v.w);
575 else static assert(0, "wtf?!");
578 protected:
579 override void activateObj () nothrow @nogc { /*if (prg)*/ glUseProgram(prg); }
580 override void deactivateObj () nothrow @nogc { glUseProgram(0); }
584 // ////////////////////////////////////////////////////////////////////////// //
585 //private import std.traits;
587 public:
588 void exec(TO) (TO obj, scope void delegate () dg) if (is(typeof(() { obj.activate(); obj.deactivate(); }))) {
589 obj.activate();
590 scope(exit) obj.deactivate();
591 dg();
594 void exec(TO, TG) (TO obj, scope TG dg) if (is(typeof((TO obj) { dg(obj); })) && is(typeof(() { obj.activate(); obj.deactivate(); }))) {
595 obj.activate();
596 scope(exit) obj.deactivate();
597 dg(obj);
601 // ////////////////////////////////////////////////////////////////////////// //
602 void orthoCamera (int wdt, int hgt) {
603 glMatrixMode(GL_PROJECTION); // for ortho camera
604 glLoadIdentity();
605 // left, right, bottom, top, near, far
606 //glOrtho(0, wdt, 0, hgt, -1, 1); // bottom-to-top
607 glOrtho(0, wdt, hgt, 0, -1, 1); // top-to-bottom
608 glViewport(0, 0, wdt, hgt);
610 //glTranslatef(-cx, -cy, 0.0f);
613 // origin is texture left top
614 void drawAtXY (GLuint tid, int x, int y, int w, int h, bool mirrorX=false, bool mirrorY=false) {
615 if (!tid || w < 1 || h < 1) return;
616 w += x;
617 h += y;
618 if (mirrorX) { int tmp = x; x = w; w = tmp; }
619 if (mirrorY) { int tmp = y; y = h; h = tmp; }
620 glBindTexture(GL_TEXTURE_2D, tid);
621 glBegin(GL_QUADS);
622 glTexCoord2f(0.0f, 0.0f); glVertex2i(x, y); // top-left
623 glTexCoord2f(1.0f, 0.0f); glVertex2i(w, y); // top-right
624 glTexCoord2f(1.0f, 1.0f); glVertex2i(w, h); // bottom-right
625 glTexCoord2f(0.0f, 1.0f); glVertex2i(x, h); // bottom-left
626 glEnd();
630 // origin is texture center
631 void drawAtXYC (Texture tex, int x, int y, bool mirrorX=false, bool mirrorY=false) {
632 if (tex is null || !tex.tid) return;
633 x -= tex.width/2;
634 y -= tex.height/2;
635 drawAtXY(tex.tid, x, y, tex.width, tex.height, mirrorX, mirrorY);
639 // origin is texture left top
640 void drawAtXY (Texture tex, int x, int y, bool mirrorX=false, bool mirrorY=false) {
641 if (tex is null || !tex.tid) return;
642 drawAtXY(tex.tid, x, y, tex.width, tex.height, mirrorX, mirrorY);