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 egfx
.backgl
is aliced
;
21 import arsd
.simpledisplay
;
25 //import iv.glbinds : glTexParameterfv; // rdmd hack
30 // ////////////////////////////////////////////////////////////////////////// //
31 /*public*/ enum GLTexType
= GL_BGRA
;
32 //public enum GLTexType = GL_RGBA;
35 // ////////////////////////////////////////////////////////////////////////// //
36 public __gshared
uint vglTexId
; // OpenGL texture id
37 public __gshared
uint vArrowTextureId
= 0;
40 // ////////////////////////////////////////////////////////////////////////// //
41 shared static this () {
42 import core
.stdc
.stdlib
: malloc
;
43 vglTexBuf
= cast(uint*)malloc((VBufWidth
*VBufHeight
+4)*4);
44 if (vglTexBuf
is null) assert(0, "out of memory!");
45 vglTexBuf
[0..VBufWidth
*VBufHeight
+4] = 0;
49 // ////////////////////////////////////////////////////////////////////////// //
50 public void vglCreateArrowTexture () {
53 enum wrapOpt
= GL_REPEAT
;
54 enum filterOpt
= GL_NEAREST
; //GL_LINEAR;
55 enum ttype
= GL_UNSIGNED_BYTE
;
57 if (vArrowTextureId
) glDeleteTextures(1, &vArrowTextureId
);
59 glGenTextures(1, &vArrowTextureId
);
60 if (vArrowTextureId
== 0) assert(0, "can't create arrow texture");
62 //GLint gltextbinding;
63 //glGetIntegerv(GL_TEXTURE_BINDING_2D, &gltextbinding);
64 //scope(exit) glBindTexture(GL_TEXTURE_2D, gltextbinding);
66 glBindTexture(GL_TEXTURE_2D
, vArrowTextureId
);
67 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrapOpt
);
68 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrapOpt
);
69 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, filterOpt
);
70 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, filterOpt
);
71 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
72 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
74 //GLfloat[4] bclr = 0.0;
75 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
77 uint[16*8] pmap
= 0x00_000000U;
78 // sprite,sprite,mask,mask
79 static immutable ushort[$] spx
= [
80 0b11111111_10000000, 0b00000000_01111111,
81 0b01000000_10000000, 0b10000000_01111111,
82 0b00100000_10000000, 0b11000000_01111111,
83 0b00010000_01100000, 0b11100000_00011111,
84 0b00001001_10011000, 0b11110000_00000111,
85 0b00000110_01100110, 0b11111001_10000001,
86 0b00000000_00011001, 0b11111111_11100000,
87 0b00000000_00000110, 0b11111111_11111001,
90 foreach (immutable dy
; 0..8) {
91 ushort spr
= spx
[dy
*2+0];
92 ushort msk
= spx
[dy
*2+1];
93 foreach (immutable dx
; 0..16) {
94 if ((msk
&0x8000) == 0) {
95 pmap
[dy
*16+dx
] = (spr
&0x8000 ?
0xff_ffffffU
: 0xff_000000U);
101 //pmap = 0xff_ff0000U;
104 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, 16, 8, 0, GLTexType
, GL_UNSIGNED_BYTE
, pmap
.ptr
);
108 // ////////////////////////////////////////////////////////////////////////// //
109 public void vglBlitArrow (int px
, int py
) {
110 if (vArrowTextureId
!= 0) {
111 glMatrixMode(GL_PROJECTION
); // for ortho camera
113 // left, right, bottom, top, near, far
114 //glViewport(0, 0, w*vbufEffScale, h*vbufEffScale);
115 //glOrtho(0, w, h, 0, -1, 1); // top-to-bottom
116 glViewport(0, 0, VBufWidth
*vbufEffScale
, VBufHeight
*vbufEffScale
);
117 glOrtho(0, VBufWidth
, VBufHeight
, 0, -1, 1); // top-to-bottom
118 glMatrixMode(GL_MODELVIEW
);
121 glEnable(GL_TEXTURE_2D
);
122 glDisable(GL_LIGHTING
);
123 glDisable(GL_DITHER
);
124 glDisable(GL_DEPTH_TEST
);
127 //glBlendFunc(GL_SRC_ALPHA, GL_ONE);
128 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
129 //glColor4f(1, 1, 1, 1);
130 glBindTexture(GL_TEXTURE_2D
, vArrowTextureId
);
131 //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
133 glTexCoord2f(0.0f, 0.0f); glVertex2i(px
, py
); // top-left
134 glTexCoord2f(1.0f, 0.0f); glVertex2i(px
+16, py
); // top-right
135 glTexCoord2f(1.0f, 1.0f); glVertex2i(px
+16, py
+8); // bottom-right
136 glTexCoord2f(0.0f, 1.0f); glVertex2i(px
, py
+8); // bottom-left
142 // ////////////////////////////////////////////////////////////////////////// //
143 // resize buffer, reinitialize OpenGL texture
144 public void vglResizeBuffer (int wdt
, int hgt
, int ascale
=1) {
147 if (wdt
< 1) wdt
= 1;
148 if (hgt
< 1) hgt
= 1;
150 if (wdt
> 8192) wdt
= 8192;
151 if (hgt
> 8192) hgt
= 8192;
153 bool sizeChanged
= (wdt
!= VBufWidth || hgt
!= VBufHeight
);
157 if (vglTexBuf
is null || sizeChanged
) {
158 import core
.stdc
.stdlib
: realloc
;
159 vglTexBuf
= cast(uint*)realloc(vglTexBuf
, (VBufWidth
*VBufHeight
+4)*vglTexBuf
[0].sizeof
);
160 if (vglTexBuf
is null) assert(0, "out of memory!");
161 vglTexBuf
[0..VBufWidth
*VBufHeight
+4] = 0;
164 if (vglTexId
== 0 || sizeChanged
) {
165 enum wrapOpt
= GL_REPEAT
;
166 enum filterOpt
= GL_NEAREST
; //GL_LINEAR;
167 enum ttype
= GL_UNSIGNED_BYTE
;
169 if (vglTexId
) glDeleteTextures(1, &vglTexId
);
171 glGenTextures(1, &vglTexId
);
172 if (vglTexId
== 0) assert(0, "can't create OpenGL texture");
174 //GLint gltextbinding;
175 //glGetIntegerv(GL_TEXTURE_BINDING_2D, &gltextbinding);
176 //scope(exit) glBindTexture(GL_TEXTURE_2D, gltextbinding);
178 glBindTexture(GL_TEXTURE_2D
, vglTexId
);
179 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrapOpt
);
180 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrapOpt
);
181 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, filterOpt
);
182 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, filterOpt
);
183 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
184 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
186 //GLfloat[4] bclr = 0.0;
187 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
189 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, VBufWidth
, VBufHeight
, 0, GLTexType
, GL_UNSIGNED_BYTE
, vglTexBuf
);
192 if (ascale
< 1) ascale
= 1;
193 if (ascale
> 32) ascale
= 32;
194 vbufEffScale
= cast(ubyte)ascale
;
198 // ////////////////////////////////////////////////////////////////////////// //
199 public void vglUpdateTexture () {
201 glBindTexture(GL_TEXTURE_2D
, vglTexId
);
202 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0/*x*/, 0/*y*/, VBufWidth
, VBufHeight
, GLTexType
, GL_UNSIGNED_BYTE
, vglTexBuf
);
203 //glBindTexture(GL_TEXTURE_2D, 0);
208 // ////////////////////////////////////////////////////////////////////////// //
209 public void vglBlitTexture () {
211 glMatrixMode(GL_PROJECTION
); // for ortho camera
213 // left, right, bottom, top, near, far
214 //glViewport(0, 0, w*vbufEffScale, h*vbufEffScale);
215 //glOrtho(0, w, h, 0, -1, 1); // top-to-bottom
216 glViewport(0, 0, VBufWidth
*vbufEffScale
, VBufHeight
*vbufEffScale
);
217 glOrtho(0, VBufWidth
, VBufHeight
, 0, -1, 1); // top-to-bottom
218 glMatrixMode(GL_MODELVIEW
);
221 glEnable(GL_TEXTURE_2D
);
222 glDisable(GL_LIGHTING
);
223 glDisable(GL_DITHER
);
224 //glDisable(GL_BLEND);
225 glDisable(GL_DEPTH_TEST
);
226 //glEnable(GL_BLEND);
227 //glBlendFunc(GL_SRC_ALPHA, GL_ONE);
228 //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
230 //glDisable(GL_STENCIL_TEST);
233 immutable w
= VBufWidth
;
234 immutable h
= VBufHeight
;
235 //conwriteln("w=", w, "; h=", h, "; scale=", vbufEffScale);
237 glColor4f(1, 1, 1, 1);
238 glBindTexture(GL_TEXTURE_2D
, vglTexId
);
239 //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
241 glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); // top-left
242 glTexCoord2f(1.0f, 0.0f); glVertex2i(w
, 0); // top-right
243 glTexCoord2f(1.0f, 1.0f); glVertex2i(w
, h
); // bottom-right
244 glTexCoord2f(0.0f, 1.0f); glVertex2i(0, h
); // bottom-left
251 // ////////////////////////////////////////////////////////////////////////// //
252 struct EGfxWindowData
{
257 ubyte vbufEffScaleSave
;
258 GxRect gxClipRectSave
;
261 if (vglTexIdSave
!= 0) {
262 glDeleteTextures(1, &vglTexIdSave
);
265 if (vglTexBufSave
!is null) {
266 import core
.stdc
.stdlib
: free
;
268 vglTexBufSave
= null;
270 VBufWidthSave
= VBufHeightSave
= 0;
271 vbufEffScaleSave
= 1;
274 static auto setup (SimpleWindow w
) {
275 static struct WDRest
{
280 @property bool valid () const pure nothrow @safe @nogc { pragma(inline
, true); return (w
!is null); }
281 @property SimpleWindow
sw () pure nothrow @safe @nogc { pragma(inline
, true); return w
; }
282 @property EGfxWindowData
* wd () { pragma(inline
, true); return (w
is null ?
null : w
in wdata
); }
283 this (SimpleWindow aw
) {
284 if (aw
!is null && !aw
.closed
) {
285 if (auto sw
= aw
in wdata
) {
289 //conwriteln("saving old context");
292 EGfxWindowData
* rd
= (oldcw
!is null ? oldcw
in wdata
: null);
295 savedData
.vglTexIdSave
= vglTexId
;
296 savedData
.VBufWidthSave
= VBufWidth
;
297 savedData
.VBufHeightSave
= VBufHeight
;
298 savedData
.vbufEffScaleSave
= vbufEffScale
;
299 savedData
.vglTexBufSave
= vglTexBuf
;
300 savedData
.gxClipRectSave
= gxClipRect
;
303 rd
.vglTexIdSave
= vglTexId
;
304 rd
.VBufWidthSave
= VBufWidth
;
305 rd
.VBufHeightSave
= VBufHeight
;
306 rd
.vbufEffScaleSave
= vbufEffScale
;
307 rd
.vglTexBufSave
= vglTexBuf
;
308 rd
.gxClipRectSave
= gxClipRect
;
311 vglTexId
= sw
.vglTexIdSave
;
312 VBufWidth
= sw
.VBufWidthSave
;
313 VBufHeight
= sw
.VBufHeightSave
;
314 vbufEffScale
= sw
.vbufEffScaleSave
;
315 vglTexBuf
= sw
.vglTexBufSave
;
318 vglResizeBuffer(w
.width
/vbufEffScale
, w
.height
/vbufEffScale
, vbufEffScale
);
320 //conwriteln("same context");
326 @disable this (this);
329 if (auto sw
= w
in wdata
) {
332 //conwriteln("restoring old context");
335 sw
.vglTexIdSave
= vglTexId
;
336 sw
.VBufWidthSave
= VBufWidth
;
337 sw
.VBufHeightSave
= VBufHeight
;
338 sw
.vbufEffScaleSave
= vbufEffScale
;
339 sw
.vglTexBufSave
= vglTexBuf
;
341 if (w
.closed
) { sw
.clear(); wdata
.remove(w
); }
343 EGfxWindowData
* rd
= (oldcw
!is null ? oldcw
in wdata
: null);
346 vglTexId
= savedData
.vglTexIdSave
;
347 VBufWidth
= savedData
.VBufWidthSave
;
348 VBufHeight
= savedData
.VBufHeightSave
;
349 vbufEffScale
= savedData
.vbufEffScaleSave
;
350 vglTexBuf
= savedData
.vglTexBufSave
;
351 gxClipRect
= savedData
.gxClipRectSave
;
354 vglTexId
= rd
.vglTexIdSave
;
355 VBufWidth
= rd
.VBufWidthSave
;
356 VBufHeight
= rd
.VBufHeightSave
;
357 vbufEffScale
= rd
.vbufEffScaleSave
;
358 vglTexBuf
= rd
.vglTexBufSave
;
361 //conwriteln("skip restoring same context");
371 // old event handlers
372 void delegate () redrawOpenGlSceneSave
;
373 void delegate (KeyEvent ke
) handleKeyEventSave
;
374 void delegate (dchar c
) handleCharEventSave
;
375 void delegate () handlePulseSave
;
376 void delegate (MouseEvent
) handleMouseEventSave
;
377 void delegate (int width
, int height
) windowResizedSave
;
380 __gshared EGfxWindowData
[SimpleWindow
] wdata
; // yes, this anchors
381 __gshared EGfxWindowData savedData
;
382 __gshared SimpleWindow curwin
;
385 // call this in `visibleForTheFirstTime()`, so it can capture handlers
386 public void vglRegisterWindow (SimpleWindow w
) {
387 if (w
is null || w
.closed
) return;
388 if (w
in wdata
) return;
392 w
.setAsCurrentOpenGlContext();
393 if (glconMainWindow
is w
) glconInit(w
.width
, w
.height
);
394 //vglResizeBuffer(sdwin.width, sdwin.height);
397 svd
.vglTexIdSave
= 0;
398 svd
.VBufWidthSave
= 0;
399 svd
.VBufHeightSave
= 0;
400 svd
.vglTexBufSave
= null;
401 svd
.vbufEffScaleSave
= vbufEffScale
;
403 // create texture and backbuffer
404 { auto save
= EGfxWindowData
.setup(w
); }
406 svd
.redrawOpenGlSceneSave
= w
.redrawOpenGlScene
;
407 w
.redrawOpenGlScene
= delegate () {
408 w
.setAsCurrentOpenGlContext();
409 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
411 auto save
= EGfxWindowData
.setup(w
);
412 if (auto sw
= save
.wd
) {
413 if (sw
.redrawOpenGlSceneSave
!is null) {
415 sw
.redrawOpenGlSceneSave();
416 w
.setAsCurrentOpenGlContext();
422 if (glconMainWindow
is w
&& !w
.closed
) {
423 //if (isConsoleVisible) conwriteln("VISIBLE");
424 glconResize(w
.width
, w
.height
);
429 svd
.handleKeyEventSave
= w
.handleKeyEvent
;
430 w
.handleKeyEvent
= delegate (KeyEvent event
) {
431 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
432 if (glconMainWindow
is w
&& glconKeyEvent(event
)) { glconPostScreenRepaint(); return; }
433 if (isQuitRequested
) w
.close();
435 auto save
= EGfxWindowData
.setup(w
);
436 if (auto sw
= save
.wd
) {
437 if (sw
.handleKeyEventSave
!is null) sw
.handleKeyEventSave(event
);
442 svd
.handleMouseEventSave
= w
.handleMouseEvent
;
443 w
.handleMouseEvent
= delegate (MouseEvent event
) {
444 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
445 if (isQuitRequested
) w
.close();
447 auto save
= EGfxWindowData
.setup(w
);
448 if (auto sw
= save
.wd
) {
449 event
.x
/= vbufEffScale
;
450 event
.y
/= vbufEffScale
;
451 if (sw
.handleMouseEventSave
!is null) sw
.handleMouseEventSave(event
);
456 svd
.handleCharEventSave
= w
.handleCharEvent
;
457 w
.handleCharEvent
= delegate (dchar ch
) {
458 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
459 if (glconMainWindow
is w
&& glconCharEvent(ch
)) { glconPostScreenRepaint(); return; }
460 if (isQuitRequested
) w
.close();
462 auto save
= EGfxWindowData
.setup(w
);
463 if (auto sw
= save
.wd
) {
464 if (sw
.handleCharEventSave
!is null) sw
.handleCharEventSave(ch
);
469 svd
.handlePulseSave
= w
.handlePulse
;
470 w
.handlePulse
= delegate () {
471 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
472 if (isQuitRequested
) w
.close();
474 auto save
= EGfxWindowData
.setup(w
);
475 if (auto sw
= save
.wd
) {
476 if (sw
.handlePulseSave
!is null) sw
.handlePulseSave();
481 svd
.windowResizedSave
= w
.windowResized
;
482 w
.windowResized
= delegate (int wdt
, int hgt
) {
483 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
485 auto save
= EGfxWindowData
.setup(w
);
486 if (auto sw
= save
.wd
) {
488 //w.setMinSize(wdt, hgt);
489 //w.setMaxSize(wdt, hgt);
490 if (glconMainWindow
is w
) glconResize(wdt
, hgt
);
491 vglResizeBuffer(wdt
/vbufEffScale
, hgt
/vbufEffScale
, vbufEffScale
);
492 if (sw
.windowResizedSave
!is null) sw
.windowResizedSave(wdt
, hgt
);
499 if (glconMainWindow
is w
) glconResize(w
.width
, w
.height
);
500 w
.redrawOpenGlSceneNow();
504 public void vglCloseWindow (SimpleWindow w
) {
505 if (w
is null) return;
506 auto sw
= w
in wdata
;
508 if (!w
.closed
) w
.close();
515 public ubyte vglWindowScale (SimpleWindow w
) {
516 if (w
is null || w
.closed
) return 1;
517 auto sw
= w
in wdata
;
518 if (sw
!is null) return sw
.vbufEffScaleSave
;
523 public ubyte vglScaleWindow (SimpleWindow w
, int newscale
) {
524 if (newscale
< 1) newscale
= 1;
525 if (newscale
> 32) newscale
= 32;
526 if (w
is null || w
.closed
) return cast(ubyte)newscale
;
527 auto sw
= w
in wdata
;
529 if (sw
.vbufEffScaleSave
!= newscale
) {
530 sw
.vbufEffScaleSave
= cast(ubyte)newscale
;
531 int wdt
= sw
.VBufWidthSave
*newscale
;
532 int hgt
= sw
.VBufHeightSave
*newscale
;
536 return cast(ubyte)newscale
;
540 public void vglPaintWith (SimpleWindow w
, scope void delegate () paintcb
) {
541 if (w
is null) return;
542 auto save
= EGfxWindowData
.setup(w
);
543 if (auto sw
= save
.wd
) {
545 if (paintcb
!is null) paintcb();
546 if (!w
.closed
) w
.redrawOpenGlSceneNow();