1 module xmain_d2d
is aliced
;
20 // ////////////////////////////////////////////////////////////////////////// //
25 // ////////////////////////////////////////////////////////////////////////// //
26 __gshared LevelMap map
;
29 // ////////////////////////////////////////////////////////////////////////// //
30 __gshared SimpleWindow sdwindow
;
33 public enum vlWidth
= 800;
34 public enum vlHeight
= 800;
35 __gshared
int scale
= 1;
36 __gshared
bool scanlines
= false;
39 // ////////////////////////////////////////////////////////////////////////// //
40 enum MaxLightSize
= 512;
43 // ////////////////////////////////////////////////////////////////////////// //
45 __gshared FBO
[MaxLightSize
+1] fboOccluders
, fboShadowMap
;
46 __gshared Shader shadToPolar
, shadBlur
;
48 __gshared FBO fboLevel
, fboOrigBack
;
49 __gshared Shader shadScanlines
, shadLiquid
;
52 // ////////////////////////////////////////////////////////////////////////// //
54 glEnable(GL_TEXTURE_2D
);
55 glDisable(GL_LIGHTING
);
58 glDisable(GL_DEPTH_TEST
);
61 shadScanlines
= new Shader("scanlines", loadTextFile("data/shaders/srscanlines.frag"));
62 shadLiquid
= new Shader("liquid", loadTextFile("data/shaders/srliquid.frag"));
64 shadLiquid
["iChannel0"] = 0;
65 //shadLiquid["iChannel1"] = 1;
69 shadToPolar
= new Shader("topolar", loadTextFile("data/shaders/srlight_topolar.frag"));
70 shadBlur
= new Shader("blur", loadTextFile("data/shaders/srlight_blur.frag"));
76 foreach (int sz
; 1..MaxLightSize
+1) {
77 fboOccluders
[sz
] = new FBO(sz
, sz
); // create occluders FBO
78 fboShadowMap
[sz
] = new FBO(sz
, 1); // create 1d shadowmap FBO
82 glMatrixMode(GL_MODELVIEW
);
89 shadLiquid["iResolution"] = SVec2F(map.texgl.ptr[map.Water].width, map.texgl.ptr[map.Water].height);
93 fboLevel
= new FBO(map
.width
*8, map
.height
*8, Texture
.Option
.Nearest
);
95 fboOrigBack
= new FBO(map
.width
*8, map
.height
*8, Texture
.Option
.Nearest
); // original background
99 orthoCamera(map.width*8, map.height*8);
100 drawAtXY(map.texgl.ptr[map.Back], 0, 0);
104 // build noise texture for liquid shaders
106 glActiveTexture(GL_TEXTURE0+1);
108 import std.random : uniform;
109 auto img = new TrueColorImage(64, 64);
110 foreach (int y; 0..img.width) {
111 foreach (int x; 0..img.height) {
112 ubyte b = cast(ubyte)uniform(0, 256);
113 img.imageData.colors[y*img.width+x] = Color(b, b, b, 255);
116 auto tex = new Texture(img);
121 glActiveTexture(GL_TEXTURE0
+0);
122 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
123 orthoCamera(vlWidth
, vlHeight
);
125 map
.initActors(); // this will precache sprites too
126 //map.doAllActorTicks();
128 { import core
.memory
: GC
; GC
.collect(); }
132 // ////////////////////////////////////////////////////////////////////////// //
133 void renderLight() (int lightX
, int lightY
, in auto ref SVec4F lcol
, int lightSize
) {
134 if (lightSize
< 2) return;
136 if (lightSize
> MaxLightSize
) lightSize
= MaxLightSize
;
137 // is this light visible?
138 if (lightX
<= -lightSize
/2 || lightY
<= -lightSize
/2 || lightX
-lightSize
/2 >= map
.width
*8 || lightY
-lightSize
/2 >= map
.height
*8) return;
140 // draw shadow casters to fboOccludersId, light should be in the center
142 fboOccluders
[lightSize
].exec({
143 glColor3f(0.0f, 0.0f, 0.0f);
144 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
145 glClear(GL_COLOR_BUFFER_BIT
);
146 orthoCamera(lightSize
, lightSize
);
147 drawAtXY(map
.texgl
.ptr
[map
.LightMask
], lightSize
/2-lightX
, lightSize
/2-lightY
);
150 // build 1d shadow map to fboShadowMapId
151 fboShadowMap
[lightSize
].exec({
153 glColor3f(0.0f, 0.0f, 0.0f);
154 shadToPolar
["lightTexSize"] = SVec2F(lightSize
, lightSize
);
155 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
156 glClear(GL_COLOR_BUFFER_BIT
);
157 orthoCamera(lightSize
, 1);
158 drawAtXY(fboOccluders
[lightSize
].tex
.tid
, 0, 0, lightSize
, 1);
165 shadBlur
["lightTexSize"] = SVec2F(lightSize
, lightSize
);
167 glBlendFunc(GL_SRC_ALPHA
, GL_ONE
);
169 glColor4f(lcol
.x
, lcol
.y
, lcol
.z
, lcol
.w
);
170 orthoCamera(map
.width
*8, map
.height
*8);
171 drawAtXY(fboShadowMap
[lightSize
].tex
.tid
, lightX
-lightSize
/2, lightY
-lightSize
/2, lightSize
, lightSize
, mirrorY
:true);
177 // ////////////////////////////////////////////////////////////////////////// //
178 __gshared
int lightX
= vlWidth
/2, lightY
= vlHeight
/2;
179 __gshared
int mapOfsX
, mapOfsY
;
180 __gshared
bool movement
= false;
183 void renderScene () {
184 enum BackIntens
= 0.05f;
188 glClearColor(BackIntens
, BackIntens
, BackIntens
, 1.0f);
189 glClear(GL_COLOR_BUFFER_BIT
);
190 //glColor3f(0.0f, 0.0f, 0.0f);
192 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
193 orthoCamera(map
.width
*8, map
.height
*8);
196 glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
197 //drawAtXY(map.texgl.ptr[map.Back], 0, 0);
200 __gshared
float iGlobalTime
= 0.0;
202 shadLiquid
["iGlobalTime"] = iGlobalTime
;
203 shadLiquid
["liquidColorMul"] = SVec4F(1.0f, 1.0f, 1.0f, 1.0f);
204 shadLiquid
["liquidColor"] = SVec4F(0.0f, 0.0f, 0.4f, 1.0f);
205 drawAtXY(map
.texgl
.ptr
[map
.Water
], 0, 0);
206 shadLiquid
["liquidColorMul"] = SVec4F(0.6f, 0.6f, 0.6f, 1.0f);
207 shadLiquid
["liquidColor"] = SVec4F(0.6f, 0.1f, 0.0f, 1.0f);
208 drawAtXY(map
.texgl
.ptr
[map
.Lava
], 0, 0);
209 shadLiquid
["liquidColorMul"] = SVec4F(1.0f, 1.0f, 1.0f, 1.0f);
210 shadLiquid
["liquidColor"] = SVec4F(0.1f, 0.4f, 0.0f, 1.0f);
211 drawAtXY(map
.texgl
.ptr
[map
.Acid
], 0, 0);
213 //drawAtXY(map.texgl.ptr[map.LightMask], 0, 0);
219 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
220 glClear(GL_COLOR_BUFFER_BIT
);
221 //glEnable(GL_BLEND);
222 //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
223 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
224 orthoCamera(map
.width
*8, map
.height
*8);
226 drawAtXY(map
.texgl
.ptr
[map
.Back
], 0, 0);
229 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
230 glColor3f(1.0f, 1.0f, 1.0f);
231 foreach (auto actor
; actors
) {
232 if (actor
.frame
is null || actor
.dead
) continue;
233 //conwriteln("(", actor.x, ", ", actor.y, ") ", actor.frame.tex.tid, " ", actor.frame.tex.width, "x", actor.frame.tex.height);
234 //drawAtXY(actor.frame.tex, actor.x/*-actor.frame.vga.sx*/, actor.y/*-actor.frame.vga.sy*/);
235 //int y = LevelMap.MapSize*8-actor.y-1/*-(actor.height-actor.frame.vga.sy)*/;
236 int y
= LevelMap
.MapSize
*8-actor
.y
+(actor
.frame
.vga
.sy
-actor
.frame
.vga
.height
);
237 drawAtXY(actor
.frame
.tex
, actor
.x
-actor
.frame
.vga
.sx
, y
);
243 glBlendFunc(GL_SRC_ALPHA
, GL_ONE
);
245 glActiveTexture(GL_TEXTURE0
+1);
246 glBindTexture(GL_TEXTURE_2D
, /*map.texgl.ptr[map.Back].tid*/fboOrigBack
.tex
.tid
);
247 //glBindTexture(GL_TEXTURE_2D, map.texgl.ptr[map.Back].tid);
248 glActiveTexture(GL_TEXTURE0
+0);
250 renderLight( 27, map
.height
*8-1-391-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
251 renderLight(542, map
.height
*8-1-424-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
252 renderLight(377, map
.height
*8-1-368-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
253 renderLight(147, map
.height
*8-1-288-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
254 renderLight( 71, map
.height
*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
255 renderLight(249, map
.height
*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
256 renderLight(426, map
.height
*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
257 renderLight(624, map
.height
*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
258 renderLight(549, map
.height
*8-1-298-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
259 renderLight( 74, map
.height
*8-1-304-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
261 renderLight(24*8+4, map
.height
*8-1-(24+18)*8-2, SVec4F(0.6f, 0.0f, 0.0f, 1.0f), 128);
262 foreach (immutable _
; 0..1) {
263 renderLight(lightX
, lightY
, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 256);
266 glActiveTexture(GL_TEXTURE0
+1);
267 glBindTexture(GL_TEXTURE_2D
, 0);
268 glActiveTexture(GL_TEXTURE0
+0);
273 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
274 glColor3f(1.0f, 1.0f, 1.0f);
275 orthoCamera(map
.width
*8, map
.height
*8);
276 drawAtXY(map
.texgl
.ptr
[map
.LightMask
], 0, 0);
278 drawAtXY(map
.texgl
.ptr
[map
.Front
], 0, 0);
280 foreach (auto actor
; actors
) {
281 if (actor
.frame
is null || actor
.dead
) continue;
282 //conwriteln("(", actor.x, ", ", actor.y, ") ", actor.frame.tex.tid, " ", actor.frame.tex.width, "x", actor.frame.tex.height);
283 //int y = LevelMap.MapSize*8-actor.y-1/*-(actor.height-actor.frame.vga.sy)*/;
284 //drawAtXY(actor.frame.tex, actor.x-actor.frame.vga.sx, y);
290 shadScanlines
["scanlines"] = scanlines
;
291 glClearColor(BackIntens
, BackIntens
, BackIntens
, 1.0f);
292 glClear(GL_COLOR_BUFFER_BIT
);
293 orthoCamera(vlWidth
, vlHeight
);
294 //orthoCamera(map.width*8*scale, map.height*8*scale);
295 //glMatrixMode(GL_MODELVIEW);
297 //glTranslatef(0.375, 0.375, 0); // to be pixel-perfect
298 drawAtXY(fboLevel
.tex
.tid
, -mapOfsX
, -mapOfsY
, map
.width
*8*scale
, map
.height
*8*scale
);
306 // ////////////////////////////////////////////////////////////////////////// //
308 shared int diedie
= 0;
311 void renderThread () {
313 MonoTime prevFrameStartTime
= MonoTime
.currTime
;
314 version(use_vsync
) {} else MonoTime ltt
= MonoTime
.currTime
;
315 MonoTime lasttime
= MonoTime
.currTime
;
317 enum MaxFPSFrames
= 16;
318 float frtimes
= 0.0f;
323 if (sdwindow
.closed
) break;
324 if (atomicLoad(diedie
) > 0) break;
326 time
= MonoTime
.currTime
;
329 // max 60 FPS; capped by vsync
330 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
331 if (!first
&& (time
-ltt
).total
!"msecs" < 16) {
332 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
333 import core
.sys
.posix
.signal
: timespec
;
334 import core
.sys
.posix
.time
: nanosleep
;
337 ts
.tv_nsec
= (16-cast(int)((time
-ltt
).total
!"msecs"))*1000*1000; // milli to nano
338 nanosleep(&ts
, null); // idc how much time was passed
339 time
= MonoTime
.currTime
;
346 auto frameTime
= cast(float)(time
-prevFrameStartTime
).total
!"msecs"/1000.0f;
347 prevFrameStartTime
= time
;
349 //{ import core.stdc.stdio; printf("frametime: %f\n", frameTime*1000.0f); }
350 //{ import core.stdc.stdio; printf("FPS: %d\n", cast(int)(1.0f/frameTime)); }
351 frtimes
+= frameTime
;
352 if (++framenum
>= MaxFPSFrames || frtimes
>= 3.0f) {
353 import std
.string
: format
;
354 int newFPS
= cast(int)(cast(float)MaxFPSFrames
/frtimes
+0.5);
355 if (newFPS
!= prevFPS
) {
356 sdwindow
.title
= "%s / FPS:%s".format("D2D", newFPS
);
366 scope(exit
) sdwindow
.mtUnlock();
367 ctset
= sdwindow
.setAsCurrentOpenGlContextNT
;
370 { import core
.stdc
.stdio
; printf(" FUUUU\n"); }
371 //glXMakeCurrent(sdwindow.display, 0, null);
372 import core
.sys
.posix
.signal
: timespec
;
373 import core
.sys
.posix
.time
: nanosleep
;
376 ts
.tv_nsec
= 16*1000*1000; // milli to nano
377 nanosleep(&ts
, null); // idc how much time was passed
378 time
= MonoTime
.currTime
;
383 map
.doAllActorTicks();
388 scope(exit
) sdwindow
.mtUnlock();
389 sdwindow
.swapOpenGlBuffers();
391 sdwindow
.releaseCurrentOpenGlContext();
396 } catch (Exception e
) {
397 import core
.stdc
.stdio
;
398 fprintf(stderr
, "FUUUUUUUUUUUUUUUUUUUUUUUUUU\n");
400 if (sdwindow
.closed
) break;
401 if (atomicLoad(diedie
) > 0) break;
402 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
403 import core
.sys
.posix
.signal
: timespec
;
404 import core
.sys
.posix
.time
: nanosleep
;
407 ts
.tv_nsec
= 100*1000*1000; // milli to nano
408 nanosleep(&ts
, null); // idc how much time was passed
411 atomicStore(diedie
, 2);
415 // ////////////////////////////////////////////////////////////////////////// //
416 void closeWindow () {
417 if (atomicLoad(diedie
) != 2) {
418 atomicStore(diedie
, 1);
419 sdwindow
.mtUnlock(); // 'cause we are locked
420 while (atomicLoad(diedie
) != 2) {}
421 sdwindow
.mtLock(); // restore lock
423 if (!sdwindow
.closed
) {
430 // ////////////////////////////////////////////////////////////////////////// //
431 __gshared Thread renderTid
;
435 static void setDP () {
439 import std
.file
: thisExePath
;
440 import std
.path
: dirName
;
441 setDataPath(thisExePath
.dirName
);
443 addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad");
444 //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad");
445 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad");
446 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad");
447 //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad");
448 //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad");
455 setOpenGLContextVersion(3, 2); // up to GLSL 150
456 //openGLContextCompatible = false;
458 map
= new LevelMap("maps/map01.d2m");
464 sdwindow
= new SimpleWindow(vlWidth
, vlHeight
, "D2D", OpenGlOptions
.yes
, Resizablity
.fixedSize
);
466 sdwindow
.visibleForTheFirstTime
= delegate () {
467 sdwindow
.setAsCurrentOpenGlContext(); // make this window active
468 glbindLoadFunctions();
471 import core.stdc.stdio;
472 printf("GL version: %s\n", glGetString(GL_VERSION));
474 glGetIntegerv(GL_MAJOR_VERSION, &h);
475 glGetIntegerv(GL_MINOR_VERSION, &l);
476 printf("version: %d.%d\n", h, l);
477 printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
479 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
481 printf("%d shader versions supported:\n", svcount);
482 foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n));
486 glGetIntegerv(GL_NUM_EXTENSIONS, &ecount);
488 printf("%d extensions supported:\n", ecount);
489 foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n));
494 // check if we have sufficient shader version here
498 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS
, &svcount
);
500 foreach (GLuint n
; 0..svcount
) {
501 import core
.stdc
.string
: strncmp
;
502 auto v
= glGetStringi(GL_SHADING_LANGUAGE_VERSION
, n
);
503 if (v
is null) continue;
504 if (strncmp(v
, "120", 3) != 0) continue;
505 if (v
[3] > ' ') continue;
510 if (!found
) assert(0, "can't find OpenGL GLSL 120");
512 auto adr
= glGetProcAddress("glTexParameterf");
513 if (adr
is null) assert(0);
516 sdwindow
.vsync
= false;
517 //sdwindow.useGLFinish = false;
519 //sdwindow.redrawOpenGlScene();
520 if (!sdwindow
.releaseCurrentOpenGlContext()) { import core
.stdc
.stdio
; printf("can't release OpenGL context(1)\n"); }
522 renderTid
= new Thread(&renderThread
);
527 //sdwindow.redrawOpenGlScene = delegate () { renderScene(); };
529 enum MSecsPerFrame
= 1000/30; /* 30 is FPS */
531 uint[8] frameTimes
= 1000;
532 enum { Left
, Right
, Up
, Down
}
533 bool[4] pressed
= false;
535 sdwindow
.eventLoop(MSecsPerFrame
,
537 if (sdwindow
.closed
) return;
538 if (pressed
[Left
]) mapOfsX
-= 8;
539 if (pressed
[Right
]) mapOfsX
+= 8;
540 if (pressed
[Up
]) mapOfsY
+= 8;
541 if (pressed
[Down
]) mapOfsY
-= 8;
542 import std
.math
: cos
, sin
;
543 __gshared
float itime
= 0.0;
546 mapOfsX
= cast(int)(800.0/2.0+cos(itime
)*220.0);
547 mapOfsY
= cast(int)(800.0/2.0+120.0+sin(itime
)*160.0);
549 if (scale
== 1) mapOfsX
= mapOfsY
= 0;
550 //sdwindow.redrawOpenGlSceneNow();
552 delegate (KeyEvent event
) {
553 if (sdwindow
.closed
) return;
554 if (event
.pressed
&& event
.key
== Key
.Escape
) { closeWindow(); return; }
556 case Key
.Left
: pressed
[Left
] = event
.pressed
; break;
557 case Key
.Right
: pressed
[Right
] = event
.pressed
; break;
558 case Key
.Up
: pressed
[Up
] = event
.pressed
; break;
559 case Key
.Down
: pressed
[Down
] = event
.pressed
; break;
563 delegate (MouseEvent event
) {
564 lightX
= event
.x
/scale
;
565 lightY
= vlHeight
/scale
-event
.y
/scale
;
566 lightX
+= mapOfsX
/scale
;
567 lightY
+= mapOfsY
/scale
;
569 delegate (dchar ch
) {
570 if (ch
== 'q') { closeWindow(); return; }
571 if (ch
== 's') scanlines
= !scanlines
;
572 if (ch
== '1') scale
= 1;
573 if (ch
== '2') scale
= 2;
574 if (ch
== ' ') movement
= !movement
;