light shader now takes into account map size in pixels
[dd2d.git] / xmain_d2d.d
blob0fb7652f5b7563996112ac5a9ca1896a463c2927
1 module xmain_d2d is aliced;
3 private:
4 import core.atomic;
5 import core.thread;
6 import core.time;
8 import iv.glbinds;
9 import glutils;
10 import console;
11 import wadarc;
13 import iv.stream;
15 import d2dmap;
16 import d2dadefs;
17 //import d2dactors;
18 import d2dgfx;
19 import dacs;
21 // `map` is there
22 import dengapi;
25 // ////////////////////////////////////////////////////////////////////////// //
26 import arsd.color;
27 import arsd.png;
30 // ////////////////////////////////////////////////////////////////////////// //
31 __gshared SimpleWindow sdwindow;
34 public enum vlWidth = 800;
35 public enum vlHeight = 800;
36 __gshared int scale = 1;
37 __gshared bool scanlines = false;
40 // ////////////////////////////////////////////////////////////////////////// //
41 enum MaxLightRadius = 512;
44 // ////////////////////////////////////////////////////////////////////////// //
45 // for light
46 __gshared FBO[MaxLightRadius+1] fboOccluders, fboShadowMap;
47 __gshared Shader shadToPolar, shadBlur;
49 __gshared FBO fboLevel, fboOrigBack;
50 __gshared Shader shadScanlines;
51 __gshared Shader shadLiquidDistort, shadLiquidColor;
54 // ////////////////////////////////////////////////////////////////////////// //
55 void initOpenGL () {
56 glEnable(GL_TEXTURE_2D);
57 glDisable(GL_LIGHTING);
58 glDisable(GL_DITHER);
59 glDisable(GL_BLEND);
60 glDisable(GL_DEPTH_TEST);
62 // create shaders
63 shadScanlines = new Shader("scanlines", loadTextFile("data/shaders/srscanlines.frag"));
65 shadLiquidDistort = new Shader("liquid_distort", loadTextFile("data/shaders/srliquid_distort.frag"));
66 shadLiquidDistort.exec({ shadLiquidDistort["tex0"] = 0; });
68 shadLiquidColor = new Shader("liquid_color", loadTextFile("data/shaders/srliquid_color.frag"));
69 shadLiquidColor.exec({ shadLiquidColor["tex0"] = 0; });
71 // lights
72 shadToPolar = new Shader("topolar", loadTextFile("data/shaders/srlight_topolar.frag"));
73 shadBlur = new Shader("blur", loadTextFile("data/shaders/srlight_blur.frag"));
74 shadBlur.exec({
75 shadBlur["tex0"] = 0;
76 shadBlur["tex1"] = 1;
77 });
78 //TODO: this sux!
79 foreach (int sz; 2..MaxLightRadius+1) {
80 fboOccluders[sz] = new FBO(sz*2, sz*2, Texture.Option.Clamp); // create occluders FBO
81 fboShadowMap[sz] = new FBO(sz*2, 1, Texture.Option.Clamp); // create 1d shadowmap FBO
84 // setup matrices
85 glMatrixMode(GL_MODELVIEW);
86 glLoadIdentity();
88 map.oglBuildMega();
90 fboLevel = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // final level render will be here
91 fboOrigBack = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // original background
93 shadBlur.exec({
94 shadBlur["mapPixSize"] = SVec2F(map.width*8, map.height*8);
95 });
97 // build noise texture for liquid shaders
99 glActiveTexture(GL_TEXTURE0+1);
101 import std.random : uniform;
102 auto img = new TrueColorImage(64, 64);
103 foreach (int y; 0..img.width) {
104 foreach (int x; 0..img.height) {
105 ubyte b = cast(ubyte)uniform(0, 256);
106 img.imageData.colors[y*img.width+x] = Color(b, b, b, 255);
109 auto tex = new Texture(img);
110 tex.activate;
114 glActiveTexture(GL_TEXTURE0+0);
115 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
116 orthoCamera(vlWidth, vlHeight);
118 Actor.resetStorage();
119 loadMapMonsters();
120 //map.initActors(); // this will precache sprites too
121 //map.doAllActorTicks();
123 { import core.memory : GC; GC.collect(); }
127 // ////////////////////////////////////////////////////////////////////////// //
128 void renderLight() (int lightX, int lightY, in auto ref SVec4F lcol, int lightRadius) {
129 if (lightRadius < 2) return;
130 if (lightRadius > MaxLightRadius) lightRadius = MaxLightRadius;
131 int lightSize = lightRadius*2;
132 // is this light visible?
133 if (lightX <= -lightRadius || lightY <= -lightRadius || lightX-lightRadius >= map.width*8 || lightY-lightRadius >= map.height*8) return;
135 // draw shadow casters to fboOccludersId, light should be in the center
136 glUseProgram(0);
137 glDisable(GL_BLEND);
138 fboOccluders[lightRadius].exec({
139 glColor3f(0.0f, 0.0f, 0.0f);
140 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
141 glClear(GL_COLOR_BUFFER_BIT);
142 orthoCamera(lightSize, lightSize);
143 drawAtXY(map.texgl.ptr[map.LightMask], lightRadius-lightX, lightRadius-lightY);
146 // build 1d shadow map to fboShadowMapId
147 fboShadowMap[lightRadius].exec({
148 shadToPolar.exec({
149 glColor3f(0.0f, 0.0f, 0.0f);
150 shadToPolar["lightTexSize"] = SVec2F(lightSize, lightSize);
151 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
152 glClear(GL_COLOR_BUFFER_BIT);
153 orthoCamera(lightSize, 1);
154 drawAtXY(fboOccluders[lightRadius].tex.tid, 0, 0, lightSize, 1);
158 // blend light
159 fboLevel.exec({
160 shadBlur.exec({
161 shadBlur["lightTexSize"] = SVec2F(lightSize, lightSize);
162 glEnable(GL_BLEND);
163 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
164 // set light color
165 glColor4f(lcol.x, lcol.y, lcol.z, lcol.w);
166 orthoCamera(map.width*8, map.height*8);
167 drawAtXY(fboShadowMap[lightRadius].tex.tid, lightX-lightRadius, lightY-lightRadius, lightSize, lightSize);
173 // ////////////////////////////////////////////////////////////////////////// //
174 __gshared int lightX = vlWidth/2, lightY = vlHeight/2;
175 __gshared int mapOfsX, mapOfsY;
176 __gshared bool movement = false;
179 void renderScene () {
180 enum BackIntens = 0.05f;
182 glUseProgram(0);
184 fboLevel.exec({
185 glDisable(GL_BLEND);
186 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
187 glClear(GL_COLOR_BUFFER_BIT);
190 // build background layer
191 glUseProgram(0);
192 fboOrigBack.exec({
193 glDisable(GL_BLEND);
194 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
195 glClear(GL_COLOR_BUFFER_BIT);
196 //glEnable(GL_BLEND);
197 //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
198 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
199 orthoCamera(map.width*8, map.height*8);
200 glEnable(GL_BLEND);
201 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
202 // draw sky
204 drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8);
205 drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2, map.MapSize*8, map.MapSize*8);
206 drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8);
207 drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2, map.MapSize*8, map.MapSize*8);
209 drawAtXY(map.skytexgl.tid, 0, 0, map.MapSize*8, map.MapSize*8);
210 // draw background
211 drawAtXY(map.texgl.ptr[map.Back], 0, 0);
212 // draw distorted liquid areas
213 shadLiquidDistort.exec({
214 __gshared float iGlobalTime = 0.0;
215 iGlobalTime += 0.04;
216 shadLiquidDistort["iGlobalTime"] = iGlobalTime;
217 shadLiquidDistort["liquidColorMul"] = SVec4F(1.0f, 1.0f, 1.0f, 1.0f);
218 drawAtXY(map.texgl.ptr[map.Water], 0, 0);
219 shadLiquidDistort["liquidColorMul"] = SVec4F(0.6f, 0.6f, 0.6f, 1.0f);
220 drawAtXY(map.texgl.ptr[map.Lava], 0, 0);
221 shadLiquidDistort["liquidColorMul"] = SVec4F(1.0f, 1.0f, 1.0f, 1.0f);
222 drawAtXY(map.texgl.ptr[map.Acid], 0, 0);
224 // monsters, items
225 glColor3f(1.0f, 1.0f, 1.0f);
227 foreach (auto actor; actors) {
228 if (actor.frame is null || actor.dead) continue;
229 //conwriteln("(", actor.x, ", ", actor.y, ") ", actor.frame.tex.tid, " ", actor.frame.tex.width, "x", actor.frame.tex.height);
230 //drawAtXY(actor.frame.tex, actor.x/*-actor.frame.vga.sx*/, actor.y/*-actor.frame.vga.sy*/);
231 //int y = LevelMap.MapSize*8-actor.y-1/*-(actor.height-actor.frame.vga.sy)*/;
232 //int y = LevelMap.MapSize*8-actor.y+(actor.frame.vga.sy-actor.frame.vga.height);
233 int y = actor.y-actor.frame.vga.sy;
234 drawAtXY(actor.frame.tex, actor.x-actor.frame.vga.sx, y);
237 Actor.forEach((ActorId me) {
238 // `act` is always valid here
239 if (auto adef = findActorDef(me.classtype!StrId, me.classname!StrId)) {
240 if (auto isp = adef.animSpr(me.zAnimstate!StrId, me.dir!uint, me.zAnimidx!int)) {
241 int y = me.y!int-isp.vga.sy;
242 drawAtXY(isp.tex, me.x!int-isp.vga.sx, y);
243 //conwriteln("found animation for actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")", " (", adef.fullname, ")");
244 } else {
245 conwriteln("no animation for actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")");
247 } else {
248 conwriteln("not found actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")");
253 // additive lights
254 glEnable(GL_BLEND);
255 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
257 glActiveTexture(GL_TEXTURE0+1);
258 glBindTexture(GL_TEXTURE_2D, /*map.texgl.ptr[map.Back].tid*/fboOrigBack.tex.tid);
259 //glBindTexture(GL_TEXTURE_2D, map.texgl.ptr[map.Back].tid);
260 glActiveTexture(GL_TEXTURE0+0);
262 enum LYOfs = 1;
264 renderLight( 27, 391-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
265 renderLight(542, 424-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
266 renderLight(377, 368-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
267 renderLight(147, 288-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
268 renderLight( 71, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
269 renderLight(249, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
270 renderLight(426, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
271 renderLight(624, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
272 renderLight(549, 298-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
273 renderLight( 74, 304-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
275 renderLight(24*8+4, (24+18)*8-2+LYOfs, SVec4F(0.6f, 0.0f, 0.0f, 1.0f), 128);
276 foreach (immutable _; 0..1) {
277 renderLight(lightX, lightY, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 96);
280 //glActiveTexture(GL_TEXTURE0+1);
281 //glBindTexture(GL_TEXTURE_2D, 0);
282 //glActiveTexture(GL_TEXTURE0+0);
284 // draw final opaque and full-lit parts
286 fboLevel.exec({
287 glEnable(GL_BLEND);
288 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
289 glColor3f(1.0f, 1.0f, 1.0f);
290 orthoCamera(map.width*8, map.height*8);
291 // various occluders
292 drawAtXY(map.texgl.ptr[map.LightMask], 0, 0);
293 // foreground -- hide secrets, draw lifts and such
294 drawAtXY(map.texgl.ptr[map.Front], 0, 0);
295 // now do liquid coloring
296 shadLiquidColor.exec({
297 // additive blend
298 //glEnable(GL_BLEND);
299 // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
300 //glBlendFunc(GL_SRC_ALPHA, GL_ONE);
301 //glDisable(GL_BLEND);
302 //glColor3f(1.0f, 1.0f, 1.0f);
303 shadLiquidColor["liquidColor"] = SVec4F(0.0f, 0.0f, 0.4f, 0.5f);
304 drawAtXY(map.texgl.ptr[map.Water], 0, 0);
305 shadLiquidColor["liquidColor"] = SVec4F(0.6f, 0.0f, 0.0f, 0.4f);
306 drawAtXY(map.texgl.ptr[map.Lava], 0, 0);
307 shadLiquidColor["liquidColor"] = SVec4F(0.1f, 0.4f, 0.0f, 0.5f);
308 drawAtXY(map.texgl.ptr[map.Acid], 0, 0);
313 // draw scaled level
314 shadScanlines.exec({
315 shadScanlines["scanlines"] = scanlines;
316 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
317 glClear(GL_COLOR_BUFFER_BIT);
318 orthoCamera(vlWidth, vlHeight);
319 //orthoCamera(map.width*8*scale, map.height*8*scale);
320 //glMatrixMode(GL_MODELVIEW);
321 //glLoadIdentity();
322 //glTranslatef(0.375, 0.375, 0); // to be pixel-perfect
323 // somehow, FBO objects are mirrored; wtf?!
324 drawAtXY(fboLevel.tex.tid, -mapOfsX, -mapOfsY, map.width*8*scale, map.height*8*scale, mirrorY:true);
325 //glLoadIdentity();
330 // ////////////////////////////////////////////////////////////////////////// //
331 // rendering thread
332 shared int diedie = 0;
334 enum D2DFrameTime = 55; // milliseconds
336 void renderThread () {
337 try {
338 MonoTime prevFrameStartTime = MonoTime.currTime;
339 version(use_vsync) {} else MonoTime ltt = MonoTime.currTime;
340 MonoTime lasttime = MonoTime.currTime;
341 MonoTime nextthink = MonoTime.currTime;
342 nextthink += dur!"msecs"(D2DFrameTime);
343 MonoTime fstime, fetime;
344 enum MaxFPSFrames = 16;
345 float frtimes = 0.0f;
346 int framenum = 0;
347 int prevFPS = -1;
348 int hushFrames = 6; // ignore first `hushFrames` frames overtime
349 for (;;) {
350 if (sdwindow.closed) break;
351 if (atomicLoad(diedie) > 0) break;
353 fstime = MonoTime.currTime;
355 // "D2D frames"
356 if (nextthink <= fstime) {
357 nextthink = fstime;
358 nextthink += dur!"msecs"(D2DFrameTime);
359 doActorsThink();
360 //{ import core.stdc.stdio; printf("spent on thinking: %d msecs\n", cast(int)((MonoTime.currTime-fstime).total!"msecs")); }
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 continue;
379 } else {
380 // render scene
381 renderScene();
382 sdwindow.mtLock();
383 scope(exit) sdwindow.mtUnlock();
384 sdwindow.swapOpenGlBuffers();
385 glFinish();
386 sdwindow.releaseCurrentOpenGlContext();
389 fetime = MonoTime.currTime;
390 // wait for 16 ms if we still have some time
391 version(use_vsync) {
392 } else {
393 // max 60 FPS; capped by vsync
394 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((fetime-fstime).total!"msecs")); }
395 if ((fetime-fstime).total!"msecs" < 16) {
396 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((fetime-fstime).total!"msecs")); }
397 import core.sys.posix.signal : timespec;
398 import core.sys.posix.time : nanosleep;
399 timespec ts = void;
400 ts.tv_sec = 0;
401 ts.tv_nsec = (16-cast(int)((fetime-fstime).total!"msecs"))*1000*1000; // milli to nano
402 nanosleep(&ts, null); // idc how much time was passed
403 fetime = MonoTime.currTime;
404 } else {
405 if (hushFrames) {
406 --hushFrames;
407 } else {
408 { import core.stdc.stdio; printf(" spent whole %d msecs\n", cast(int)((fetime-fstime).total!"msecs")); }
414 auto frameTime = cast(float)(fetime-prevFrameStartTime).total!"msecs"/1000.0f;
415 prevFrameStartTime = fetime;
416 frtimes += frameTime;
417 if (++framenum >= MaxFPSFrames || frtimes >= 3.0f) {
418 import std.string : format;
419 int newFPS = cast(int)(cast(float)MaxFPSFrames/frtimes+0.5);
420 if (newFPS != prevFPS) {
421 sdwindow.title = "%s / FPS:%s".format("D2D", newFPS);
422 prevFPS = newFPS;
424 framenum = 0;
425 frtimes = 0.0f;
429 } catch (Exception e) {
430 import core.stdc.stdio;
431 fprintf(stderr, "FUUUUUUUUUUUUUUUUUUUUUUUUUU\n");
432 for (;;) {
433 if (sdwindow.closed) break;
434 if (atomicLoad(diedie) > 0) break;
435 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
436 import core.sys.posix.signal : timespec;
437 import core.sys.posix.time : nanosleep;
438 timespec ts = void;
439 ts.tv_sec = 0;
440 ts.tv_nsec = 100*1000*1000; // milli to nano
441 nanosleep(&ts, null); // idc how much time was passed
444 atomicStore(diedie, 2);
448 // ////////////////////////////////////////////////////////////////////////// //
449 void closeWindow () {
450 if (atomicLoad(diedie) != 2) {
451 atomicStore(diedie, 1);
452 while (atomicLoad(diedie) != 2) {}
454 if (!sdwindow.closed) {
455 flushGui();
456 sdwindow.close();
461 // ////////////////////////////////////////////////////////////////////////// //
462 __gshared Thread renderTid;
465 void main (string[] args) {
466 FuncPool.dumpCode = false;
467 FuncPool.dumpCodeSize = false;
468 dacsDumpSemantic = false;
469 dacsOptimize = 9;
470 version(rdmd) { dacsOptimize = 0; }
471 bool compileOnly = false;
473 for (usize idx = 1; idx < args.length; ++idx) {
474 bool remove = true;
475 if (args[idx] == "--dump-code") FuncPool.dumpCode = true;
476 else if (args[idx] == "--dump-code-size") FuncPool.dumpCodeSize = true;
477 else if (args[idx] == "--dump-semantic") dacsDumpSemantic = true;
478 else if (args[idx] == "--compile") compileOnly = true;
479 else if (args[idx] == "--messages") ++dacsMessages;
480 else if (args[idx].length > 2 && args[idx][0..2] == "-O") {
481 import std.conv : to;
482 ubyte olevel = to!ubyte(args[idx][2..$]);
483 dacsOptimize = olevel;
485 else remove = false;
486 if (remove) {
487 foreach (immutable c; idx+1..args.length) args.ptr[c-1] = args.ptr[c];
488 args.length -= 1;
489 --idx; //hack
493 static void setDP () {
494 version(rdmd) {
495 setDataPath("data");
496 } else {
497 import std.file : thisExePath;
498 import std.path : dirName;
499 setDataPath(thisExePath.dirName);
501 addPK3(getDataPath~"base.pk3"); loadWadScripts();
502 //addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad"); loadWadScripts();
503 //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad"); loadWadScripts();
504 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad"); loadWadScripts();
505 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad"); loadWadScripts();
506 //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad"); loadWadScripts();
507 //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad"); loadWadScripts();
510 setDP();
512 if (compileOnly) return;
514 registerAPI();
515 loadPalette();
517 setOpenGLContextVersion(3, 2); // up to GLSL 150
518 //openGLContextCompatible = false;
520 map = new LevelMap("maps/map01.d2m");
522 //mapOfsX = 8*26;
523 //mapOfsY = 8*56;
524 map.getThingPos(1/*ThingId.Player1*/, &mapOfsX, &mapOfsY);
525 // fix viewport
526 mapOfsX = (mapOfsX*2)-vlWidth/2;
527 if (mapOfsX+vlWidth > map.width*16) mapOfsX = map.width*16-vlWidth;
528 if (mapOfsX < 0) mapOfsX = 0;
529 mapOfsY = (mapOfsY*2)-vlHeight/2;
530 if (mapOfsY+vlHeight > map.height*16) mapOfsY = map.height*16-vlHeight;
531 if (mapOfsY < 0) mapOfsY = 0;
532 scale = 2;
534 sdwindow = new SimpleWindow(vlWidth, vlHeight, "D2D", OpenGlOptions.yes, Resizablity.fixedSize);
536 sdwindow.visibleForTheFirstTime = delegate () {
537 sdwindow.setAsCurrentOpenGlContext(); // make this window active
538 glbindLoadFunctions();
541 import core.stdc.stdio;
542 printf("GL version: %s\n", glGetString(GL_VERSION));
543 GLint l, h;
544 glGetIntegerv(GL_MAJOR_VERSION, &h);
545 glGetIntegerv(GL_MINOR_VERSION, &l);
546 printf("version: %d.%d\n", h, l);
547 printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
548 GLint svcount;
549 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
550 if (svcount > 0) {
551 printf("%d shader versions supported:\n", svcount);
552 foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n));
555 GLint ecount;
556 glGetIntegerv(GL_NUM_EXTENSIONS, &ecount);
557 if (ecount > 0) {
558 printf("%d extensions supported:\n", ecount);
559 foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n));
564 // check if we have sufficient shader version here
566 bool found = false;
567 GLint svcount;
568 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
569 if (svcount > 0) {
570 foreach (GLuint n; 0..svcount) {
571 import core.stdc.string : strncmp;
572 auto v = glGetStringi(GL_SHADING_LANGUAGE_VERSION, n);
573 if (v is null) continue;
574 if (strncmp(v, "120", 3) != 0) continue;
575 if (v[3] > ' ') continue;
576 found = true;
577 break;
580 if (!found) assert(0, "can't find OpenGL GLSL 120");
582 auto adr = glGetProcAddress("glTexParameterf");
583 if (adr is null) assert(0);
586 sdwindow.vsync = false;
587 //sdwindow.useGLFinish = false;
588 initOpenGL();
589 //sdwindow.redrawOpenGlScene();
590 if (!sdwindow.releaseCurrentOpenGlContext()) { import core.stdc.stdio; printf("can't release OpenGL context(1)\n"); }
591 if (!renderTid) {
592 renderTid = new Thread(&renderThread);
593 renderTid.start();
597 //sdwindow.redrawOpenGlScene = delegate () { renderScene(); };
599 enum MSecsPerFrame = 1000/30; /* 30 is FPS */
601 uint[8] frameTimes = 1000;
602 enum { Left, Right, Up, Down }
603 bool[4] pressed = false;
605 sdwindow.eventLoop(MSecsPerFrame,
606 delegate () {
607 if (sdwindow.closed) return;
608 if (pressed[Left]) mapOfsX -= 8;
609 if (pressed[Right]) mapOfsX += 8;
610 if (pressed[Up]) mapOfsY -= 8;
611 if (pressed[Down]) mapOfsY += 8;
612 import std.math : cos, sin;
613 __gshared float itime = 0.0;
614 itime += 0.02;
615 if (movement) {
616 mapOfsX = cast(int)(800.0/2.0+cos(itime)*220.0);
617 mapOfsY = cast(int)(800.0/2.0+120.0+sin(itime)*160.0);
619 if (scale == 1) mapOfsX = mapOfsY = 0;
620 //sdwindow.redrawOpenGlSceneNow();
622 delegate (KeyEvent event) {
623 if (sdwindow.closed) return;
624 if (event.pressed && event.key == Key.Escape) { closeWindow(); return; }
625 switch (event.key) {
626 case Key.Left: pressed[Left] = event.pressed; break;
627 case Key.Right: pressed[Right] = event.pressed; break;
628 case Key.Up: pressed[Up] = event.pressed; break;
629 case Key.Down: pressed[Down] = event.pressed; break;
630 default:
633 delegate (MouseEvent event) {
634 lightX = event.x/scale;
635 lightY = event.y/scale;
636 lightX += mapOfsX/scale;
637 lightY += mapOfsY/scale;
639 delegate (dchar ch) {
640 if (ch == 'q') { closeWindow(); return; }
641 if (ch == 's') scanlines = !scanlines;
642 if (ch == '1') scale = 1;
643 if (ch == '2') scale = 2;
644 if (ch == ' ') movement = !movement;
647 closeWindow();
648 flushGui();