8 // ////////////////////////////////////////////////////////////////////////// //
10 __gshared GLuint glLastUsedTexture = 0;
12 public void useTexture (GLuint tid) {
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
{
29 // default: repeat, linear
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
); }
49 glBindTexture(GL_TEXTURE_2D
, tid
);
50 glDeleteTextures(1, &tid
);
52 glBindTexture(GL_TEXTURE_2D
, 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;
74 void createEmpty (int w
, int h
, in Option
[] opts
...) {
75 import core
.stdc
.stdlib
: malloc
, free
;
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
);
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
);
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
);
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
{
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(); }
142 glBindTexture(GL_TEXTURE_CUBE_MAP
, tid
);
143 glDeleteTextures(1, &tid
);
144 glBindTexture(GL_TEXTURE_CUBE_MAP
, 0);
151 void createEmpty (int w
, int h
, in Texture
.Option
[] opts
...) {
152 import core
.stdc
.stdlib
: malloc
, free
;
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
);
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
);
185 void loadPng (string fname
, in Texture
.Option
[] opts
...) {
187 TrueColorImage
[6] img
;
188 foreach (int idx
; 0..6) {
189 import std
.string
: format
;
190 img
[idx
] = readPng(fname
.format(idx
)).getAsTrueColorImage
;
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
{
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); }
225 void createWithTexture (Texture atex
) {
226 assert(atex
!is null && atex
.tid
);
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);
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
{
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
);
276 auto shaderId
= glCreateShader(GL_FRAGMENT_SHADER
);
278 GLint slen
= cast(int)src
.length
;
279 glShaderSource(shaderId
, 1, &sptr
, &slen
);
280 glCompileShader(shaderId
);
282 glGetShaderiv(shaderId
, GL_COMPILE_STATUS
, &success
);
284 import core
.stdc
.stdio
: printf
;
285 import core
.stdc
.stdlib
: malloc
;
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
);
293 prg
= glCreateProgram();
294 glAttachShader(prg
, shaderId
);
298 GLint
varId(NT
) (NT vname
) if (is(NT
== char[]) ||
is(NT
== const(char)[]) ||
is(NT
== immutable(char)[])) {
300 if (vname
.length
> 0 && vname
.length
<= 128) {
301 if (auto vi
= vname
in vars
) {
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
;
312 vars
[vname
.idup
] = id
;
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
);
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
);
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
);
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
);
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;
364 void exec(TO
, TG
) (TO obj
, TG dg
) if (is(typeof(() { dg(); })) && is(typeof(() { obj
.activate(); obj
.deactivate(); }))) {
366 scope(exit
) obj
.deactivate();
371 // ////////////////////////////////////////////////////////////////////////// //
372 void orthoCamera (int wdt
, int hgt
) {
373 glMatrixMode(GL_PROJECTION
); // for ortho camera
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;
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
);
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
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;
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
);