actor position interpolation
[dd2d.git] / xmain_d2d.d
blobf4127e5c65dc72bcdcb70f5f06094f3aed8b57bb
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 // interpolation
42 __gshared ubyte[] prevFrameActorsData;
43 __gshared uint[65536] prevFrameActorOfs; // uint.max-1: dead; uint.max: last
44 __gshared MonoTime lastthink = MonoTime.zero; // for interpolator
45 __gshared MonoTime nextthink = MonoTime.zero;
46 __gshared bool frameInterpolation = true;
49 // ////////////////////////////////////////////////////////////////////////// //
50 enum MaxLightRadius = 512;
53 // ////////////////////////////////////////////////////////////////////////// //
54 // for light
55 __gshared FBO[MaxLightRadius+1] fboOccluders, fboShadowMap;
56 __gshared Shader shadToPolar, shadBlur, shadBlurOcc;
58 __gshared FBO fboLevel, fboLevelLight, fboOrigBack;
59 __gshared Shader shadScanlines;
60 __gshared Shader shadLiquidDistort;
63 // ////////////////////////////////////////////////////////////////////////// //
64 void initOpenGL () {
65 glEnable(GL_TEXTURE_2D);
66 glDisable(GL_LIGHTING);
67 glDisable(GL_DITHER);
68 glDisable(GL_BLEND);
69 glDisable(GL_DEPTH_TEST);
71 // create shaders
72 shadScanlines = new Shader("scanlines", loadTextFile("data/shaders/srscanlines.frag"));
74 shadLiquidDistort = new Shader("liquid_distort", loadTextFile("data/shaders/srliquid_distort.frag"));
75 shadLiquidDistort.exec((Shader shad) { shad["tex0"] = 0; });
77 // lights
78 shadToPolar = new Shader("light_topolar", loadTextFile("data/shaders/srlight_topolar.frag"));
79 shadBlur = new Shader("light_blur", loadTextFile("data/shaders/srlight_blur.frag"));
80 shadBlur.exec((Shader shad) {
81 shad["tex0"] = 0;
82 shad["tex1"] = 1;
83 shad["tex2"] = 2;
84 });
85 shadBlurOcc = new Shader("light_blur_occ", loadTextFile("data/shaders/srlight_blur_occ.frag"));
86 shadBlurOcc.exec((Shader shad) {
87 shad["tex0"] = 0;
88 shad["tex1"] = 1;
89 shad["tex2"] = 2;
90 });
91 //TODO: this sux!
92 foreach (int sz; 2..MaxLightRadius+1) {
93 fboOccluders[sz] = new FBO(sz*2, sz*2, Texture.Option.Clamp, Texture.Option.Linear); // create occluders FBO
94 fboShadowMap[sz] = new FBO(sz*2, 1, Texture.Option.Clamp); // create 1d shadowmap FBO
97 // setup matrices
98 glMatrixMode(GL_MODELVIEW);
99 glLoadIdentity();
101 map.oglBuildMega();
103 fboLevel = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // final level render will be here
104 fboLevelLight = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // level lights will be rendered here
105 //fboForeground = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // level foreground
106 fboOrigBack = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // background+foreground
108 shadBlur.exec((Shader shad) { shad["mapPixSize"] = SVec2F(map.width*8, map.height*8); });
109 shadBlurOcc.exec((Shader shad) { shad["mapPixSize"] = SVec2F(map.width*8, map.height*8); });
111 glActiveTexture(GL_TEXTURE0+0);
112 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
113 orthoCamera(vlWidth, vlHeight);
115 Actor.resetStorage();
116 loadMapMonsters();
118 // save first snapshot
119 prevFrameActorsData = new ubyte[](Actor.actorSize*65536); // ~15-20 megabytes
120 prevFrameActorOfs[] = uint.max; // just for fun
121 Actor.saveSnapshot(prevFrameActorsData[], prevFrameActorOfs.ptr);
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 //glDisable(GL_BLEND);
140 glColor3f(0.0f, 0.0f, 0.0f);
141 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
142 glClear(GL_COLOR_BUFFER_BIT);
143 orthoCamera(lightSize, lightSize);
144 drawAtXY(map.texgl.ptr[map.LightMask], lightRadius-lightX, lightRadius-lightY);
147 // build 1d shadow map to fboShadowMapId
148 fboShadowMap[lightRadius].exec({
149 shadToPolar.exec((Shader shad) {
150 //glDisable(GL_BLEND);
151 glColor3f(0.0f, 0.0f, 0.0f);
152 shad["lightTexSize"] = SVec2F(lightSize, lightSize);
153 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
154 glClear(GL_COLOR_BUFFER_BIT);
155 orthoCamera(lightSize, 1);
156 drawAtXY(fboOccluders[lightRadius].tex.tid, 0, 0, lightSize, 1);
160 // build light texture for blending
161 fboOccluders[lightRadius].exec({
162 // no need to clear it, shader will take care of that
163 //glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
164 //glClear(GL_COLOR_BUFFER_BIT);
165 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
166 //glDisable(GL_BLEND);
167 shadBlur.exec((Shader shad) {
168 shad["lightTexSize"] = SVec2F(lightSize, lightSize);
169 shad["lightColor"] = SVec4F(lcol.x, lcol.y, lcol.z, lcol.w);
170 shad["lightPos"] = SVec2F(lightX, lightY);
171 orthoCamera(lightSize, lightSize);
172 drawAtXY(fboShadowMap[lightRadius].tex.tid, 0, 0, lightSize, lightSize);
176 // blend light texture
177 fboLevelLight.exec({
178 glEnable(GL_BLEND);
179 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
180 orthoCamera(map.width*8, map.height*8);
181 drawAtXY(fboOccluders[lightRadius].tex, lightX-lightRadius, lightY-lightRadius, mirrorY:true);
182 // and blend it again, somewhat bigger, with the shader that will touch only occluders
183 enum szmore = 0;
184 shadBlurOcc.exec((Shader shad) {
185 shad["lightTexSize"] = SVec2F(lightSize, lightSize);
186 shad["lightColor"] = SVec4F(lcol.x, lcol.y, lcol.z, lcol.w);
187 shad["lightPos"] = SVec2F(lightX, lightY);
188 drawAtXY(fboOccluders[lightRadius].tex.tid, lightX-lightRadius-szmore, lightY-lightRadius-szmore, lightSize+szmore*2, lightSize+szmore*2, mirrorY:true);
194 // ////////////////////////////////////////////////////////////////////////// //
195 __gshared int lightX = vlWidth/2, lightY = vlHeight/2;
196 __gshared int mapOfsX, mapOfsY;
197 __gshared bool movement = false;
198 __gshared float iLiquidTime = 0.0;
201 void renderScene (MonoTime curtime) {
202 //enum BackIntens = 0.05f;
203 enum BackIntens = 0.0f;
205 float atob = cast(float)((curtime-lastthink).total!"msecs")/cast(float)((nextthink-lastthink).total!"msecs");
206 //{ import core.stdc.stdio; printf("atob=%f\n", cast(double)atob); }
209 int framelen = cast(int)((nextthink-lastthink).total!"msecs");
210 int curfp = cast(int)((curtime-lastthink).total!"msecs");
211 { import core.stdc.stdio; printf("framelen=%d; curfp=%d\n", framelen, curfp); }
215 glUseProgram(0);
217 // build background layer
218 fboOrigBack.exec({
219 //glDisable(GL_BLEND);
220 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
221 glClear(GL_COLOR_BUFFER_BIT);
222 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
223 orthoCamera(map.width*8, map.height*8);
224 glEnable(GL_BLEND);
225 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
226 // draw sky
228 drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8);
229 drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2, map.MapSize*8, map.MapSize*8);
230 drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8);
231 drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2, map.MapSize*8, map.MapSize*8);
233 drawAtXY(map.skytexgl.tid, 0, 0, map.MapSize*8, map.MapSize*8);
234 // draw background
235 drawAtXY(map.texgl.ptr[map.Back], 0, 0);
236 // draw distorted liquid areas
237 shadLiquidDistort.exec((Shader shad) {
238 shad["iDistortTime"] = iLiquidTime;
239 drawAtXY(map.texgl.ptr[map.AllLiquids], 0, 0);
241 // monsters, items; we'll do linear interpolation here
242 // we need this for saved frame anyway, so let's play dirty and speed up the things
243 uint fxofs = Actor.fields["x"].ofs;
244 uint fyofs = Actor.fields["y"].ofs;
245 uint fzAnimstateofs = Actor.fields["zAnimstate"].ofs;
246 uint fdirofs = Actor.fields["dir"].ofs;
247 uint fzAnimidxofs = Actor.fields["zAnimidx"].ofs;
248 uint fclasstypeofs = Actor.fields["classtype"].ofs;
249 uint fclassnameofs = Actor.fields["classname"].ofs;
250 glColor3f(1.0f, 1.0f, 1.0f);
251 Actor.forEach((ActorId me) {
252 // `act` is always valid here
253 auto aptr = me.data.ptr;
254 auto ctstr = StrId(*cast(uint*)(aptr+fclasstypeofs));
255 auto cnstr = StrId(*cast(uint*)(aptr+fclassnameofs));
256 if (auto adef = findActorDef(ctstr, cnstr)) {
257 ctstr = StrId(*cast(uint*)(aptr+fzAnimstateofs));
258 if (auto isp = adef.animSpr(ctstr, *cast(uint*)(aptr+fdirofs), *cast(int*)(aptr+fzAnimidxofs))) {
259 auto ofs = prevFrameActorOfs.ptr[me.id&0xffff];
260 if (frameInterpolation && ofs < uint.max-1) {
261 import core.stdc.math : roundf;
262 auto xptr = prevFrameActorsData.ptr+ofs;
263 int ox = *cast(int*)(xptr+fxofs);
264 int nx = *cast(int*)(aptr+fxofs);
265 int oy = *cast(int*)(xptr+fyofs);
266 int ny = *cast(int*)(aptr+fyofs);
267 int x = cast(int)(ox+roundf((nx-ox)*atob));
268 int y = cast(int)(oy+roundf((ny-oy)*atob));
269 //conwriteln("actor ", me.id, "; o=(", ox, ",", oy, "); n=(", nx, ",", ny, "); p=(", x, ",", y, ")");
270 drawAtXY(isp.tex, x-isp.vga.sx, y-isp.vga.sy);
271 } else {
272 int nx = *cast(int*)(aptr+fxofs)-isp.vga.sx;
273 int ny = *cast(int*)(aptr+fyofs)-isp.vga.sy;
274 //conwriteln("actor ", me.id, "; nx=", nx, "; ny=", ny);
275 drawAtXY(isp.tex, nx, ny);
277 } else {
278 conwriteln("no animation for actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")");
280 } else {
281 conwriteln("not found actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")");
284 if (auto adef = findActorDef(me.classtype!StrId, me.classname!StrId)) {
285 if (auto isp = adef.animSpr(me.zAnimstate!StrId, me.dir!uint, me.zAnimidx!int)) {
286 int y = me.y!int-isp.vga.sy;
287 drawAtXY(isp.tex, me.x!int-isp.vga.sx, y);
288 //conwriteln("found animation for actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")", " (", adef.fullname, ")");
289 } else {
290 conwriteln("no animation for actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")");
292 } else {
293 conwriteln("not found actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")");
297 // do liquid coloring
298 drawAtXY(map.texgl.ptr[map.LiquidMask], 0, 0);
299 // foreground -- hide secrets, draw lifts and such
300 drawAtXY(map.texgl.ptr[map.Front], 0, 0);
304 // clear light layer
305 fboLevelLight.exec({
306 glDisable(GL_BLEND);
307 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
308 //glClearColor(0.15f, 0.15f, 0.15f, 1.0f);
309 ////glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
310 glClear(GL_COLOR_BUFFER_BIT);
313 // texture 1 is background
314 glActiveTexture(GL_TEXTURE0+1);
315 glBindTexture(GL_TEXTURE_2D, fboOrigBack.tex.tid);
316 // texture 2 is occluders
317 glActiveTexture(GL_TEXTURE0+2);
318 glBindTexture(GL_TEXTURE_2D, map.texgl.ptr[map.LightMask].tid);
319 // done texture assign
320 glActiveTexture(GL_TEXTURE0+0);
322 enum LYOfs = 1;
324 renderLight( 27, 391-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
325 renderLight(542, 424-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
326 renderLight(377, 368-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
327 renderLight(147, 288-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
328 renderLight( 71, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
329 renderLight(249, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
330 renderLight(426, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
331 renderLight(624, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
332 renderLight(549, 298-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
333 renderLight( 74, 304-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
335 renderLight(24*8+4, (24+18)*8-2+LYOfs, SVec4F(0.6f, 0.0f, 0.0f, 1.0f), 128);
337 foreach (immutable _; 0..1) {
338 renderLight(lightX, lightY, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 96);
341 glActiveTexture(GL_TEXTURE0+1);
342 glBindTexture(GL_TEXTURE_2D, 0);
343 glActiveTexture(GL_TEXTURE0+2);
344 glBindTexture(GL_TEXTURE_2D, 0);
345 glActiveTexture(GL_TEXTURE0+0);
348 // draw scaled level
350 shadScanlines.exec((Shader shad) {
351 shad["scanlines"] = scanlines;
352 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
353 glClear(GL_COLOR_BUFFER_BIT);
354 orthoCamera(vlWidth, vlHeight);
355 //orthoCamera(map.width*8*scale, map.height*8*scale);
356 //glMatrixMode(GL_MODELVIEW);
357 //glLoadIdentity();
358 //glTranslatef(0.375, 0.375, 0); // to be pixel-perfect
359 // somehow, FBO objects are mirrored; wtf?!
360 drawAtXY(fboLevel.tex.tid, -mapOfsX, -mapOfsY, map.width*8*scale, map.height*8*scale, mirrorY:true);
361 //glLoadIdentity();
364 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
365 glDisable(GL_BLEND);
366 orthoCamera(vlWidth, vlHeight);
367 drawAtXY(fboLevelLight.tex.tid, -mapOfsX, -mapOfsY, map.width*8*scale, map.height*8*scale, mirrorY:true);
371 // ////////////////////////////////////////////////////////////////////////// //
372 // rendering thread
373 shared int diedie = 0;
375 enum D2DFrameTime = 55; // milliseconds
377 void renderThread () {
378 try {
379 MonoTime prevFrameStartTime = MonoTime.currTime;
380 version(use_vsync) {} else MonoTime ltt = MonoTime.currTime;
381 MonoTime lasttime = MonoTime.currTime;
382 nextthink = MonoTime.currTime;
383 lastthink = nextthink; // for interpolator
384 nextthink += dur!"msecs"(D2DFrameTime);
385 MonoTime fstime, fetime;
386 enum MaxFPSFrames = 16;
387 float frtimes = 0.0f;
388 int framenum = 0;
389 int prevFPS = -1;
390 int hushFrames = 6; // ignore first `hushFrames` frames overtime
391 for (;;) {
392 if (sdwindow.closed) break;
393 if (atomicLoad(diedie) > 0) break;
395 fstime = MonoTime.currTime;
397 // "D2D frames"
398 if (nextthink <= fstime) {
399 lastthink = fstime;
400 nextthink = fstime;
401 nextthink += dur!"msecs"(D2DFrameTime);
402 // save snapshot for interpolator
403 Actor.saveSnapshot(prevFrameActorsData[], prevFrameActorOfs.ptr);
404 // process actors
405 doActorsThink();
406 // some timing
407 int thinkTime = cast(int)((MonoTime.currTime-fstime).total!"msecs");
408 if (thinkTime > 9) { import core.stdc.stdio; printf("spent on thinking: %d msecs\n", thinkTime); }
411 bool ctset;
413 sdwindow.mtLock();
414 scope(exit) sdwindow.mtUnlock();
415 ctset = sdwindow.setAsCurrentOpenGlContextNT;
417 if (!ctset) {
418 { import core.stdc.stdio; printf(" FUUUU\n"); }
419 //glXMakeCurrent(sdwindow.display, 0, null);
420 import core.sys.posix.signal : timespec;
421 import core.sys.posix.time : nanosleep;
422 timespec ts = void;
423 ts.tv_sec = 0;
424 ts.tv_nsec = 16*1000*1000; // milli to nano
425 nanosleep(&ts, null); // idc how much time was passed
426 continue;
427 } else {
428 // render scene
429 iLiquidTime = cast(float)((fstime-MonoTime.zero).total!"msecs"%10000000)/18.0f*0.04f;
430 renderScene(fstime);
431 sdwindow.mtLock();
432 scope(exit) sdwindow.mtUnlock();
433 sdwindow.swapOpenGlBuffers();
434 glFinish();
435 sdwindow.releaseCurrentOpenGlContext();
438 fetime = MonoTime.currTime;
439 // wait for 16 ms if we still have some time
440 version(use_vsync) {
441 } else {
442 // max 60 FPS; capped by vsync
443 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((fetime-fstime).total!"msecs")); }
444 if ((fetime-fstime).total!"msecs" < 16) {
445 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((fetime-fstime).total!"msecs")); }
446 import core.sys.posix.signal : timespec;
447 import core.sys.posix.time : nanosleep;
448 timespec ts = void;
449 ts.tv_sec = 0;
450 ts.tv_nsec = (16-cast(int)((fetime-fstime).total!"msecs"))*1000*1000; // milli to nano
451 nanosleep(&ts, null); // idc how much time was passed
452 fetime = MonoTime.currTime;
453 } else {
454 if (hushFrames) {
455 --hushFrames;
456 } else {
457 { import core.stdc.stdio; printf(" spent whole %d msecs\n", cast(int)((fetime-fstime).total!"msecs")); }
463 auto frameTime = cast(float)(fetime-prevFrameStartTime).total!"msecs"/1000.0f;
464 prevFrameStartTime = fetime;
465 frtimes += frameTime;
466 if (++framenum >= MaxFPSFrames || frtimes >= 3.0f) {
467 import std.string : format;
468 int newFPS = cast(int)(cast(float)MaxFPSFrames/frtimes+0.5);
469 if (newFPS != prevFPS) {
470 sdwindow.title = "%s / FPS:%s".format("D2D", newFPS);
471 prevFPS = newFPS;
473 framenum = 0;
474 frtimes = 0.0f;
478 } catch (Exception e) {
479 import core.stdc.stdio;
480 fprintf(stderr, "FUUUUUUUUUUUUUUUUUUUUUUUUUU\n");
481 for (;;) {
482 if (sdwindow.closed) break;
483 if (atomicLoad(diedie) > 0) break;
484 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
485 import core.sys.posix.signal : timespec;
486 import core.sys.posix.time : nanosleep;
487 timespec ts = void;
488 ts.tv_sec = 0;
489 ts.tv_nsec = 100*1000*1000; // milli to nano
490 nanosleep(&ts, null); // idc how much time was passed
493 atomicStore(diedie, 2);
497 // ////////////////////////////////////////////////////////////////////////// //
498 void closeWindow () {
499 if (atomicLoad(diedie) != 2) {
500 atomicStore(diedie, 1);
501 while (atomicLoad(diedie) != 2) {}
503 if (!sdwindow.closed) {
504 flushGui();
505 sdwindow.close();
510 // ////////////////////////////////////////////////////////////////////////// //
511 __gshared Thread renderTid;
514 void main (string[] args) {
515 FuncPool.dumpCode = false;
516 FuncPool.dumpCodeSize = false;
517 dacsDumpSemantic = false;
518 dacsOptimize = 9;
519 version(rdmd) { dacsOptimize = 0; }
520 bool compileOnly = false;
522 for (usize idx = 1; idx < args.length; ++idx) {
523 bool remove = true;
524 if (args[idx] == "--dump-code") FuncPool.dumpCode = true;
525 else if (args[idx] == "--dump-code-size") FuncPool.dumpCodeSize = true;
526 else if (args[idx] == "--dump-semantic") dacsDumpSemantic = true;
527 else if (args[idx] == "--dump-all") { FuncPool.dumpCode = true; FuncPool.dumpCodeSize = true; dacsDumpSemantic = true; }
528 else if (args[idx] == "--compile") compileOnly = true;
529 else if (args[idx] == "--compile-only") compileOnly = true;
530 else if (args[idx] == "--messages") ++dacsMessages;
531 else if (args[idx] == "--no-copro") dacsOptimizeNoCoPro = true;
532 else if (args[idx] == "--no-deadass") dacsOptimizeNoDeadAss = true;
533 else if (args[idx] == "--no-purekill") dacsOptimizeNoPureKill = true;
534 else if (args[idx].length > 2 && args[idx][0..2] == "-O") {
535 import std.conv : to;
536 ubyte olevel = to!ubyte(args[idx][2..$]);
537 dacsOptimize = olevel;
539 else remove = false;
540 if (remove) {
541 foreach (immutable c; idx+1..args.length) args.ptr[c-1] = args.ptr[c];
542 args.length -= 1;
543 --idx; //hack
547 static void setDP () {
548 version(rdmd) {
549 setDataPath("data");
550 } else {
551 import std.file : thisExePath;
552 import std.path : dirName;
553 setDataPath(thisExePath.dirName);
555 addPK3(getDataPath~"base.pk3"); loadWadScripts();
556 //addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad"); loadWadScripts();
557 //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad"); loadWadScripts();
558 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad"); loadWadScripts();
559 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad"); loadWadScripts();
560 //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad"); loadWadScripts();
561 //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad"); loadWadScripts();
562 scriptLoadingComplete();
565 setDP();
567 if (compileOnly) return;
569 try {
570 registerAPI();
571 loadPalette();
573 setOpenGLContextVersion(3, 2); // up to GLSL 150
574 //openGLContextCompatible = false;
576 map = new LevelMap("maps/map01.d2m");
578 //mapOfsX = 8*26;
579 //mapOfsY = 8*56;
580 map.getThingPos(1/*ThingId.Player1*/, &mapOfsX, &mapOfsY);
581 // fix viewport
582 mapOfsX = (mapOfsX*2)-vlWidth/2;
583 if (mapOfsX+vlWidth > map.width*16) mapOfsX = map.width*16-vlWidth;
584 if (mapOfsX < 0) mapOfsX = 0;
585 mapOfsY = (mapOfsY*2)-vlHeight/2;
586 if (mapOfsY+vlHeight > map.height*16) mapOfsY = map.height*16-vlHeight;
587 if (mapOfsY < 0) mapOfsY = 0;
588 scale = 2;
590 sdwindow = new SimpleWindow(vlWidth, vlHeight, "D2D", OpenGlOptions.yes, Resizablity.fixedSize);
592 sdwindow.visibleForTheFirstTime = delegate () {
593 sdwindow.setAsCurrentOpenGlContext(); // make this window active
594 glbindLoadFunctions();
597 import core.stdc.stdio;
598 printf("GL version: %s\n", glGetString(GL_VERSION));
599 GLint l, h;
600 glGetIntegerv(GL_MAJOR_VERSION, &h);
601 glGetIntegerv(GL_MINOR_VERSION, &l);
602 printf("version: %d.%d\n", h, l);
603 printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
604 GLint svcount;
605 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
606 if (svcount > 0) {
607 printf("%d shader versions supported:\n", svcount);
608 foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n));
611 GLint ecount;
612 glGetIntegerv(GL_NUM_EXTENSIONS, &ecount);
613 if (ecount > 0) {
614 printf("%d extensions supported:\n", ecount);
615 foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n));
620 // check if we have sufficient shader version here
622 bool found = false;
623 GLint svcount;
624 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
625 if (svcount > 0) {
626 foreach (GLuint n; 0..svcount) {
627 import core.stdc.string : strncmp;
628 auto v = glGetStringi(GL_SHADING_LANGUAGE_VERSION, n);
629 if (v is null) continue;
630 if (strncmp(v, "120", 3) != 0) continue;
631 if (v[3] > ' ') continue;
632 found = true;
633 break;
636 if (!found) assert(0, "can't find OpenGL GLSL 120");
638 auto adr = glGetProcAddress("glTexParameterf");
639 if (adr is null) assert(0);
642 sdwindow.vsync = false;
643 //sdwindow.useGLFinish = false;
644 initOpenGL();
645 //sdwindow.redrawOpenGlScene();
646 if (!sdwindow.releaseCurrentOpenGlContext()) { import core.stdc.stdio; printf("can't release OpenGL context(1)\n"); }
647 if (!renderTid) {
648 renderTid = new Thread(&renderThread);
649 renderTid.start();
653 //sdwindow.redrawOpenGlScene = delegate () { renderScene(); };
655 enum MSecsPerFrame = 1000/30; /* 30 is FPS */
657 uint[8] frameTimes = 1000;
658 enum { Left, Right, Up, Down }
659 bool[4] pressed = false;
660 bool altMove = false;
662 sdwindow.eventLoop(MSecsPerFrame,
663 delegate () {
664 if (sdwindow.closed) return;
665 if (pressed[Left]) mapOfsX -= 8;
666 if (pressed[Right]) mapOfsX += 8;
667 if (pressed[Up]) mapOfsY -= 8;
668 if (pressed[Down]) mapOfsY += 8;
669 import std.math : cos, sin;
670 __gshared float itime = 0.0;
671 itime += 0.02;
672 if (movement) {
673 mapOfsX = cast(int)(800.0/2.0+cos(itime)*220.0);
674 mapOfsY = cast(int)(800.0/2.0+120.0+sin(itime)*160.0);
676 if (scale == 1) mapOfsX = mapOfsY = 0;
677 //sdwindow.redrawOpenGlSceneNow();
679 delegate (KeyEvent event) {
680 if (sdwindow.closed) return;
681 if (event.pressed && event.key == Key.Escape) { closeWindow(); return; }
682 switch (event.key) {
683 case Key.X: if (event.pressed) altMove = !altMove; break;
684 case Key.Left: if (altMove) pressed[Left] = event.pressed; break;
685 case Key.Right: if (altMove) pressed[Right] = event.pressed; break;
686 case Key.Up: if (altMove) pressed[Up] = event.pressed; break;
687 case Key.Down: if (altMove) pressed[Down] = event.pressed; break;
688 default:
690 if (!altMove) {
691 switch (event.key) {
692 case Key.Left: case Key.Pad4: plrKeyUpDown(0, PLK_LEFT, event.pressed); break;
693 case Key.Right: case Key.Pad6: plrKeyUpDown(0, PLK_RIGHT, event.pressed); break;
694 case Key.Up: case Key.Pad8: plrKeyUpDown(0, PLK_UP, event.pressed); break;
695 case Key.Down: case Key.Pad2: plrKeyUpDown(0, PLK_DOWN, event.pressed); break;
696 case Key.Alt: plrKeyUpDown(0, PLK_JUMP, event.pressed); break;
697 case Key.Ctrl: plrKeyUpDown(0, PLK_FIRE, event.pressed); break;
698 case Key.Shift: plrKeyUpDown(0, PLK_USE, event.pressed); break;
699 default:
703 delegate (MouseEvent event) {
704 lightX = event.x/scale;
705 lightY = event.y/scale;
706 lightX += mapOfsX/scale;
707 lightY += mapOfsY/scale;
709 delegate (dchar ch) {
710 if (ch == 'q') { closeWindow(); return; }
711 if (ch == 's') scanlines = !scanlines;
712 if (ch == '1') scale = 1;
713 if (ch == '2') scale = 2;
714 if (ch == 'i') frameInterpolation = !frameInterpolation;
715 if (ch == ' ') movement = !movement;
718 } catch (Exception e) {
719 import std.stdio : stderr;
720 stderr.writeln("FUUUUUUUUUUUU\n", e.toString);
722 closeWindow();
723 flushGui();