some opengl cosmetix
[dd2d.git] / xmain_d2d.d
blob8fac1d53aa014020ac26a5aebd8090e057e52615
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 wadarc;
12 import iv.stream;
14 import d2dmap;
17 // ////////////////////////////////////////////////////////////////////////// //
18 import arsd.color;
19 import arsd.png;
22 // ////////////////////////////////////////////////////////////////////////// //
23 __gshared LevelMap map;
26 // ////////////////////////////////////////////////////////////////////////// //
27 __gshared SimpleWindow sdwindow;
30 public enum vlWidth = 800;
31 public enum vlHeight = 800;
32 __gshared int scale = 1;
33 __gshared bool scanlines = false;
36 // ////////////////////////////////////////////////////////////////////////// //
37 enum MaxLightSize = 512;
40 // ////////////////////////////////////////////////////////////////////////// //
41 // for light
42 __gshared FBO[MaxLightSize+1] fboOccluders, fboShadowMap;
43 __gshared Shader shadToPolar, shadBlur;
45 __gshared FBO fboLevel, fboOrigBack;
46 __gshared Shader shadScanlines, shadLiquid;
49 // ////////////////////////////////////////////////////////////////////////// //
50 void initOpenGL () {
51 glEnable(GL_TEXTURE_2D);
52 glDisable(GL_LIGHTING);
53 glDisable(GL_DITHER);
54 glDisable(GL_BLEND);
55 glDisable(GL_DEPTH_TEST);
57 // create shaders
58 shadScanlines = new Shader("scanlines", loadTextFile("data/shaders/srscanlines.frag"));
59 shadLiquid = new Shader("liquid", loadTextFile("data/shaders/srliquid.frag"));
60 shadLiquid.exec({
61 shadLiquid["iChannel0"] = 0;
62 //shadLiquid["iChannel1"] = 1;
63 });
65 // lights
66 shadToPolar = new Shader("topolar", loadTextFile("data/shaders/srlight_topolar.frag"));
67 shadBlur = new Shader("blur", loadTextFile("data/shaders/srlight_blur.frag"));
68 shadBlur.exec({
69 shadBlur["tex0"] = 0;
70 shadBlur["tex1"] = 1;
71 });
72 //TODO: this sux!
73 foreach (int sz; 1..MaxLightSize+1) {
74 fboOccluders[sz] = new FBO(sz, sz); // create occluders FBO
75 fboShadowMap[sz] = new FBO(sz, 1); // create 1d shadowmap FBO
78 // setup matrices
79 glMatrixMode(GL_MODELVIEW);
80 glLoadIdentity();
82 map.oglBuildMega();
85 shadLiquid.exec({
86 shadLiquid["iResolution"] = SVec2F(map.texgl.ptr[map.Water].width, map.texgl.ptr[map.Water].height);
87 });
90 fboLevel = new FBO(map.width*8, map.height*8, Texture.Option.Nearest);
92 fboOrigBack = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // original background
95 fboOrigBack.exec({
96 orthoCamera(map.width*8, map.height*8);
97 drawAtXY(map.texgl.ptr[map.Back], 0, 0);
98 });
101 // build noise texture for liquid shaders
103 glActiveTexture(GL_TEXTURE0+1);
105 import std.random : uniform;
106 auto img = new TrueColorImage(64, 64);
107 foreach (int y; 0..img.width) {
108 foreach (int x; 0..img.height) {
109 ubyte b = cast(ubyte)uniform(0, 256);
110 img.imageData.colors[y*img.width+x] = Color(b, b, b, 255);
113 auto tex = new Texture(img);
114 tex.activate;
118 glActiveTexture(GL_TEXTURE0+0);
119 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
120 orthoCamera(vlWidth, vlHeight);
124 // ////////////////////////////////////////////////////////////////////////// //
125 void renderLight() (int lightX, int lightY, in auto ref SVec4F lcol, int lightSize) {
126 if (lightSize < 2) return;
127 lightSize *= 2;
128 if (lightSize > MaxLightSize) lightSize = MaxLightSize;
129 // is this light visible?
130 if (lightX <= -lightSize/2 || lightY <= -lightSize/2 || lightX-lightSize/2 >= map.width*8 || lightY-lightSize/2 >= map.height*8) return;
132 // draw shadow casters to fboOccludersId, light should be in the center
133 glUseProgram(0);
134 fboOccluders[lightSize].exec({
135 glColor3f(0.0f, 0.0f, 0.0f);
136 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
137 glClear(GL_COLOR_BUFFER_BIT);
138 orthoCamera(lightSize, lightSize);
139 drawAtXY(map.texgl.ptr[map.LightMask], lightSize/2-lightX, lightSize/2-lightY);
142 // build 1d shadow map to fboShadowMapId
143 fboShadowMap[lightSize].exec({
144 shadToPolar.exec({
145 glColor3f(0.0f, 0.0f, 0.0f);
146 shadToPolar["lightTexSize"] = SVec2F(lightSize, lightSize);
147 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
148 glClear(GL_COLOR_BUFFER_BIT);
149 orthoCamera(lightSize, 1);
150 drawAtXY(fboOccluders[lightSize].tex.tid, 0, 0, lightSize, 1);
154 // blend light
155 fboLevel.exec({
156 shadBlur.exec({
157 shadBlur["lightTexSize"] = SVec2F(lightSize, lightSize);
158 glEnable(GL_BLEND);
159 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
160 // set light color
161 glColor4f(lcol.x, lcol.y, lcol.z, lcol.w);
162 orthoCamera(map.width*8, map.height*8);
163 drawAtXY(fboShadowMap[lightSize].tex.tid, lightX-lightSize/2, lightY-lightSize/2, lightSize, lightSize, mirrorY:true);
169 // ////////////////////////////////////////////////////////////////////////// //
170 __gshared int lightX = vlWidth/2, lightY = vlHeight/2;
171 __gshared int mapOfsX, mapOfsY;
172 __gshared bool movement = false;
175 void renderScene () {
176 enum BackIntens = 0.05f;
178 fboLevel.exec({
179 glDisable(GL_BLEND);
180 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
181 glClear(GL_COLOR_BUFFER_BIT);
182 //glColor3f(0.0f, 0.0f, 0.0f);
183 glEnable(GL_BLEND);
184 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
185 orthoCamera(map.width*8, map.height*8);
187 // draw background
188 glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
189 //drawAtXY(map.texgl.ptr[map.Back], 0, 0);
190 // water, acid, lava
191 shadLiquid.exec({
192 __gshared float iGlobalTime = 0.0;
193 iGlobalTime += 0.04;
194 shadLiquid["iGlobalTime"] = iGlobalTime;
195 shadLiquid["liquidColorMul"] = SVec4F(1.0f, 1.0f, 1.0f, 1.0f);
196 shadLiquid["liquidColor"] = SVec4F(0.0f, 0.0f, 0.4f, 1.0f);
197 drawAtXY(map.texgl.ptr[map.Water], 0, 0);
198 shadLiquid["liquidColorMul"] = SVec4F(0.6f, 0.6f, 0.6f, 1.0f);
199 shadLiquid["liquidColor"] = SVec4F(0.6f, 0.1f, 0.0f, 1.0f);
200 drawAtXY(map.texgl.ptr[map.Lava], 0, 0);
201 shadLiquid["liquidColorMul"] = SVec4F(1.0f, 1.0f, 1.0f, 1.0f);
202 shadLiquid["liquidColor"] = SVec4F(0.1f, 0.4f, 0.0f, 1.0f);
203 drawAtXY(map.texgl.ptr[map.Acid], 0, 0);
205 //drawAtXY(map.texgl.ptr[map.LightMask], 0, 0);
208 glUseProgram(0);
209 fboOrigBack.exec({
210 glDisable(GL_BLEND);
211 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
212 glClear(GL_COLOR_BUFFER_BIT);
213 //glEnable(GL_BLEND);
214 //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
215 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
216 orthoCamera(map.width*8, map.height*8);
217 // draw background
218 drawAtXY(map.texgl.ptr[map.Back], 0, 0);
219 // draw items
220 // draw monsters
223 // additive lights
224 glEnable(GL_BLEND);
225 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
227 glActiveTexture(GL_TEXTURE0+1);
228 glBindTexture(GL_TEXTURE_2D, /*map.texgl.ptr[map.Back].tid*/fboOrigBack.tex.tid);
229 //glBindTexture(GL_TEXTURE_2D, map.texgl.ptr[map.Back].tid);
230 glActiveTexture(GL_TEXTURE0+0);
232 renderLight( 27, map.height*8-1-391-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
233 renderLight(542, map.height*8-1-424-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
234 renderLight(377, map.height*8-1-368-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
235 renderLight(147, map.height*8-1-288-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
236 renderLight( 71, map.height*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
237 renderLight(249, map.height*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
238 renderLight(426, map.height*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
239 renderLight(624, map.height*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
240 renderLight(549, map.height*8-1-298-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
241 renderLight( 74, map.height*8-1-304-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
243 renderLight(24*8+4, map.height*8-1-(24+18)*8-2, SVec4F(0.6f, 0.0f, 0.0f, 1.0f), 128);
244 foreach (immutable _; 0..10) {
245 renderLight(lightX, lightY, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 256);
248 glActiveTexture(GL_TEXTURE0+1);
249 glBindTexture(GL_TEXTURE_2D, 0);
250 glActiveTexture(GL_TEXTURE0+0);
252 // restore blending
253 fboLevel.exec({
254 glEnable(GL_BLEND);
255 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
256 glColor3f(1.0f, 1.0f, 1.0f);
257 orthoCamera(map.width*8, map.height*8);
258 drawAtXY(map.texgl.ptr[map.LightMask], 0, 0);
259 // foreground
260 drawAtXY(map.texgl.ptr[map.Front], 0, 0);
263 // scanlines shader
264 shadScanlines.exec({
265 shadScanlines["scanlines"] = scanlines;
266 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
267 glClear(GL_COLOR_BUFFER_BIT);
268 orthoCamera(vlWidth, vlHeight);
269 //orthoCamera(map.width*8*scale, map.height*8*scale);
270 //glMatrixMode(GL_MODELVIEW);
271 //glLoadIdentity();
272 //glTranslatef(0.375, 0.375, 0); // to be pixel-perfect
273 drawAtXY(fboLevel.tex.tid, -mapOfsX, -mapOfsY, map.width*8*scale, map.height*8*scale);
274 //glLoadIdentity();
277 glUseProgram(0);
281 // ////////////////////////////////////////////////////////////////////////// //
282 // rendering thread
283 shared int diedie = 0;
286 void renderThread () {
287 try {
288 MonoTime prevFrameStartTime = MonoTime.currTime;
289 version(use_vsync) {} else MonoTime ltt = MonoTime.currTime;
290 MonoTime lasttime = MonoTime.currTime;
291 MonoTime time;
292 enum MaxFPSFrames = 16;
293 float frtimes = 0.0f;
294 int framenum = 0;
295 int prevFPS = -1;
296 bool first = true;
297 for (;;) {
298 if (sdwindow.closed) break;
299 if (atomicLoad(diedie) > 0) break;
301 time = MonoTime.currTime;
302 version(use_vsync) {
303 } else {
304 // max 60 FPS; capped by vsync
305 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
306 if (!first && (time-ltt).total!"msecs" < 16) {
307 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
308 import core.sys.posix.signal : timespec;
309 import core.sys.posix.time : nanosleep;
310 timespec ts = void;
311 ts.tv_sec = 0;
312 ts.tv_nsec = (16-cast(int)((time-ltt).total!"msecs"))*1000*1000; // milli to nano
313 nanosleep(&ts, null); // idc how much time was passed
314 time = MonoTime.currTime;
316 ltt = time;
317 first = false;
321 auto frameTime = cast(float)(time-prevFrameStartTime).total!"msecs"/1000.0f;
322 prevFrameStartTime = time;
324 //{ import core.stdc.stdio; printf("frametime: %f\n", frameTime*1000.0f); }
325 //{ import core.stdc.stdio; printf("FPS: %d\n", cast(int)(1.0f/frameTime)); }
326 frtimes += frameTime;
327 if (++framenum >= MaxFPSFrames || frtimes >= 3.0f) {
328 import std.string : format;
329 int newFPS = cast(int)(cast(float)MaxFPSFrames/frtimes+0.5);
330 if (newFPS != prevFPS) {
331 sdwindow.title = "%s / FPS:%s".format("D2D", newFPS);
332 prevFPS = newFPS;
334 framenum = 0;
335 frtimes = 0.0f;
338 bool ctset;
340 sdwindow.mtLock();
341 scope(exit) sdwindow.mtUnlock();
342 ctset = sdwindow.setAsCurrentOpenGlContextNT;
344 if (!ctset) {
345 { import core.stdc.stdio; printf(" FUUUU\n"); }
346 //glXMakeCurrent(sdwindow.display, 0, null);
347 import core.sys.posix.signal : timespec;
348 import core.sys.posix.time : nanosleep;
349 timespec ts = void;
350 ts.tv_sec = 0;
351 ts.tv_nsec = 16*1000*1000; // milli to nano
352 nanosleep(&ts, null); // idc how much time was passed
353 time = MonoTime.currTime;
354 } else {
355 renderScene();
357 sdwindow.mtLock();
358 scope(exit) sdwindow.mtUnlock();
359 sdwindow.swapOpenGlBuffers();
360 glFinish();
361 sdwindow.releaseCurrentOpenGlContext();
366 } catch (Exception e) {
367 import core.stdc.stdio;
368 fprintf(stderr, "FUUUUUUUUUUUUUUUUUUUUUUUUUU\n");
369 for (;;) {
370 if (sdwindow.closed) break;
371 if (atomicLoad(diedie) > 0) break;
372 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
373 import core.sys.posix.signal : timespec;
374 import core.sys.posix.time : nanosleep;
375 timespec ts = void;
376 ts.tv_sec = 0;
377 ts.tv_nsec = 100*1000*1000; // milli to nano
378 nanosleep(&ts, null); // idc how much time was passed
381 atomicStore(diedie, 2);
385 // ////////////////////////////////////////////////////////////////////////// //
386 void closeWindow () {
387 if (atomicLoad(diedie) != 2) {
388 atomicStore(diedie, 1);
389 sdwindow.mtUnlock(); // 'cause we are locked
390 while (atomicLoad(diedie) != 2) {}
391 sdwindow.mtLock(); // restore lock
393 if (!sdwindow.closed) {
394 flushGui();
395 sdwindow.close();
400 // ////////////////////////////////////////////////////////////////////////// //
401 __gshared Thread renderTid;
404 void main () {
405 static void setDP () {
406 version(rdmd) {
407 setDataPath("data");
408 } else {
409 import std.file : thisExePath;
410 import std.path : dirName;
411 setDataPath(thisExePath.dirName);
413 addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad");
414 //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad");
415 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad");
416 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad");
417 //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad");
418 //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad");
421 setOpenGLContextVersion(3, 2); // up to GLSL 150
422 //openGLContextCompatible = false;
424 setDP();
425 loadPalette();
427 map = new LevelMap("maps/map01.d2m");
428 mapOfsX = 8*56;
429 mapOfsY = 8*56;
430 scale = 2;
432 sdwindow = new SimpleWindow(vlWidth, vlHeight, "D2D", OpenGlOptions.yes, Resizablity.fixedSize);
434 sdwindow.visibleForTheFirstTime = delegate () {
435 sdwindow.setAsCurrentOpenGlContext(); // make this window active
436 glbindLoadFunctions();
439 import core.stdc.stdio;
440 printf("GL version: %s\n", glGetString(GL_VERSION));
441 GLint l, h;
442 glGetIntegerv(GL_MAJOR_VERSION, &h);
443 glGetIntegerv(GL_MINOR_VERSION, &l);
444 printf("version: %d.%d\n", h, l);
445 printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
446 GLint svcount;
447 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
448 if (svcount > 0) {
449 printf("%d shader versions supported:\n", svcount);
450 foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n));
453 GLint ecount;
454 glGetIntegerv(GL_NUM_EXTENSIONS, &ecount);
455 if (ecount > 0) {
456 printf("%d extensions supported:\n", ecount);
457 foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n));
462 // check if we have sufficient shader version here
464 bool found = false;
465 GLint svcount;
466 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
467 if (svcount > 0) {
468 foreach (GLuint n; 0..svcount) {
469 import core.stdc.string : strncmp;
470 auto v = glGetStringi(GL_SHADING_LANGUAGE_VERSION, n);
471 if (v is null) continue;
472 if (strncmp(v, "120", 3) != 0) continue;
473 if (v[3] > ' ') continue;
474 found = true;
475 break;
478 if (!found) assert(0, "can't find OpenGL GLSL 120");
480 auto adr = glGetProcAddress("glTexParameterf");
481 if (adr is null) assert(0);
484 sdwindow.vsync = false;
485 //sdwindow.useGLFinish = false;
486 initOpenGL();
487 //sdwindow.redrawOpenGlScene();
488 if (!sdwindow.releaseCurrentOpenGlContext()) { import core.stdc.stdio; printf("can't release OpenGL context(1)\n"); }
489 if (!renderTid) {
490 renderTid = new Thread(&renderThread);
491 renderTid.start();
495 //sdwindow.redrawOpenGlScene = delegate () { renderScene(); };
497 enum MSecsPerFrame = 1000/30; /* 30 is FPS */
499 uint[8] frameTimes = 1000;
500 enum { Left, Right, Up, Down }
501 bool[4] pressed = false;
503 sdwindow.eventLoop(MSecsPerFrame,
504 delegate () {
505 if (sdwindow.closed) return;
506 if (pressed[Left]) mapOfsX -= 8;
507 if (pressed[Right]) mapOfsX += 8;
508 if (pressed[Up]) mapOfsY += 8;
509 if (pressed[Down]) mapOfsY -= 8;
510 import std.math : cos, sin;
511 __gshared float itime = 0.0;
512 itime += 0.02;
513 if (movement) {
514 mapOfsX = cast(int)(800.0/2.0+cos(itime)*220.0);
515 mapOfsY = cast(int)(800.0/2.0+120.0+sin(itime)*160.0);
517 if (scale == 1) mapOfsX = mapOfsY = 0;
518 //sdwindow.redrawOpenGlSceneNow();
520 delegate (KeyEvent event) {
521 if (sdwindow.closed) return;
522 if (event.pressed && event.key == Key.Escape) { closeWindow(); return; }
523 switch (event.key) {
524 case Key.Left: pressed[Left] = event.pressed; break;
525 case Key.Right: pressed[Right] = event.pressed; break;
526 case Key.Up: pressed[Up] = event.pressed; break;
527 case Key.Down: pressed[Down] = event.pressed; break;
528 default:
531 delegate (MouseEvent event) {
532 lightX = event.x/scale;
533 lightY = vlHeight/scale-event.y/scale;
534 lightX += mapOfsX/scale;
535 lightY += mapOfsY/scale;
537 delegate (dchar ch) {
538 if (ch == 'q') { closeWindow(); return; }
539 if (ch == 's') scanlines = !scanlines;
540 if (ch == '1') scale = 1;
541 if (ch == '2') scale = 2;
542 if (ch == ' ') movement = !movement;
545 sdwindow.mtLock();
546 closeWindow();
547 sdwindow.mtUnlock();
548 flushGui();