thing y output fix
[dd2d.git] / xmain_d2d.d
blob3905735985aa961a9c334adbda55554138dc0be0
1 module xmain_d2d is aliced;
3 private:
4 import core.atomic;
5 import core.thread;
6 import core.time;
8 import glbinds;
9 import glutils;
10 import console;
11 import wadarc;
13 import iv.stream;
15 import d2dmap;
16 import d2dtpl;
17 import d2dactors;
20 // ////////////////////////////////////////////////////////////////////////// //
21 import arsd.color;
22 import arsd.png;
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 // ////////////////////////////////////////////////////////////////////////// //
44 // for light
45 __gshared FBO[MaxLightSize+1] fboOccluders, fboShadowMap;
46 __gshared Shader shadToPolar, shadBlur;
48 __gshared FBO fboLevel, fboOrigBack;
49 __gshared Shader shadScanlines, shadLiquid;
52 // ////////////////////////////////////////////////////////////////////////// //
53 void initOpenGL () {
54 glEnable(GL_TEXTURE_2D);
55 glDisable(GL_LIGHTING);
56 glDisable(GL_DITHER);
57 glDisable(GL_BLEND);
58 glDisable(GL_DEPTH_TEST);
60 // create shaders
61 shadScanlines = new Shader("scanlines", loadTextFile("data/shaders/srscanlines.frag"));
62 shadLiquid = new Shader("liquid", loadTextFile("data/shaders/srliquid.frag"));
63 shadLiquid.exec({
64 shadLiquid["iChannel0"] = 0;
65 //shadLiquid["iChannel1"] = 1;
66 });
68 // lights
69 shadToPolar = new Shader("topolar", loadTextFile("data/shaders/srlight_topolar.frag"));
70 shadBlur = new Shader("blur", loadTextFile("data/shaders/srlight_blur.frag"));
71 shadBlur.exec({
72 shadBlur["tex0"] = 0;
73 shadBlur["tex1"] = 1;
74 });
75 //TODO: this sux!
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
81 // setup matrices
82 glMatrixMode(GL_MODELVIEW);
83 glLoadIdentity();
85 map.oglBuildMega();
88 shadLiquid.exec({
89 shadLiquid["iResolution"] = SVec2F(map.texgl.ptr[map.Water].width, map.texgl.ptr[map.Water].height);
90 });
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
98 fboOrigBack.exec({
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);
117 tex.activate;
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;
135 lightSize *= 2;
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
141 glUseProgram(0);
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({
152 shadToPolar.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);
162 // blend light
163 fboLevel.exec({
164 shadBlur.exec({
165 shadBlur["lightTexSize"] = SVec2F(lightSize, lightSize);
166 glEnable(GL_BLEND);
167 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
168 // set light color
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;
186 fboLevel.exec({
187 glDisable(GL_BLEND);
188 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
189 glClear(GL_COLOR_BUFFER_BIT);
190 //glColor3f(0.0f, 0.0f, 0.0f);
191 glEnable(GL_BLEND);
192 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
193 orthoCamera(map.width*8, map.height*8);
195 // draw background
196 glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
197 //drawAtXY(map.texgl.ptr[map.Back], 0, 0);
198 // water, acid, lava
199 shadLiquid.exec({
200 __gshared float iGlobalTime = 0.0;
201 iGlobalTime += 0.04;
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);
216 glUseProgram(0);
217 fboOrigBack.exec({
218 glDisable(GL_BLEND);
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);
225 // draw background
226 drawAtXY(map.texgl.ptr[map.Back], 0, 0);
227 // monsters, items
228 glEnable(GL_BLEND);
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);
241 // additive lights
242 glEnable(GL_BLEND);
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);
270 // restore blending
271 fboLevel.exec({
272 glEnable(GL_BLEND);
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);
277 // foreground
278 drawAtXY(map.texgl.ptr[map.Front], 0, 0);
279 // monsters, items
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);
288 // scanlines shader
289 shadScanlines.exec({
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);
296 //glLoadIdentity();
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);
299 //glLoadIdentity();
302 glUseProgram(0);
306 // ////////////////////////////////////////////////////////////////////////// //
307 // rendering thread
308 shared int diedie = 0;
311 void renderThread () {
312 try {
313 MonoTime prevFrameStartTime = MonoTime.currTime;
314 version(use_vsync) {} else MonoTime ltt = MonoTime.currTime;
315 MonoTime lasttime = MonoTime.currTime;
316 MonoTime time;
317 enum MaxFPSFrames = 16;
318 float frtimes = 0.0f;
319 int framenum = 0;
320 int prevFPS = -1;
321 bool first = true;
322 for (;;) {
323 if (sdwindow.closed) break;
324 if (atomicLoad(diedie) > 0) break;
326 time = MonoTime.currTime;
327 version(use_vsync) {
328 } else {
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;
335 timespec ts = void;
336 ts.tv_sec = 0;
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;
341 ltt = time;
342 first = false;
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);
357 prevFPS = newFPS;
359 framenum = 0;
360 frtimes = 0.0f;
363 bool ctset;
365 sdwindow.mtLock();
366 scope(exit) sdwindow.mtUnlock();
367 ctset = sdwindow.setAsCurrentOpenGlContextNT;
369 if (!ctset) {
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;
374 timespec ts = void;
375 ts.tv_sec = 0;
376 ts.tv_nsec = 16*1000*1000; // milli to nano
377 nanosleep(&ts, null); // idc how much time was passed
378 time = MonoTime.currTime;
379 } else {
380 __gshared frp = 0;
381 if (--frp <= 0) {
382 frp = 3;
383 map.doAllActorTicks();
385 renderScene();
387 sdwindow.mtLock();
388 scope(exit) sdwindow.mtUnlock();
389 sdwindow.swapOpenGlBuffers();
390 glFinish();
391 sdwindow.releaseCurrentOpenGlContext();
396 } catch (Exception e) {
397 import core.stdc.stdio;
398 fprintf(stderr, "FUUUUUUUUUUUUUUUUUUUUUUUUUU\n");
399 for (;;) {
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;
405 timespec ts = void;
406 ts.tv_sec = 0;
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) {
424 flushGui();
425 sdwindow.close();
430 // ////////////////////////////////////////////////////////////////////////// //
431 __gshared Thread renderTid;
434 void main () {
435 static void setDP () {
436 version(rdmd) {
437 setDataPath("data");
438 } else {
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");
451 setDP();
452 loadTemplates();
453 loadPalette();
455 setOpenGLContextVersion(3, 2); // up to GLSL 150
456 //openGLContextCompatible = false;
458 map = new LevelMap("maps/map01.d2m");
460 mapOfsX = 8*26;
461 mapOfsY = 8*56;
462 scale = 2;
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));
473 GLint l, h;
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));
478 GLint svcount;
479 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
480 if (svcount > 0) {
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));
485 GLint ecount;
486 glGetIntegerv(GL_NUM_EXTENSIONS, &ecount);
487 if (ecount > 0) {
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
496 bool found = false;
497 GLint svcount;
498 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
499 if (svcount > 0) {
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;
506 found = true;
507 break;
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;
518 initOpenGL();
519 //sdwindow.redrawOpenGlScene();
520 if (!sdwindow.releaseCurrentOpenGlContext()) { import core.stdc.stdio; printf("can't release OpenGL context(1)\n"); }
521 if (!renderTid) {
522 renderTid = new Thread(&renderThread);
523 renderTid.start();
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,
536 delegate () {
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;
544 itime += 0.02;
545 if (movement) {
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; }
555 switch (event.key) {
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;
560 default:
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;
577 sdwindow.mtLock();
578 closeWindow();
579 sdwindow.mtUnlock();
580 flushGui();