1 module xmain_d2d
is aliced
;
26 // ////////////////////////////////////////////////////////////////////////// //
31 // ////////////////////////////////////////////////////////////////////////// //
32 __gshared SimpleWindow sdwindow
;
35 public enum vlWidth
= 800;
36 public enum vlHeight
= 800;
37 __gshared
int scale
= 1;
38 __gshared
bool scanlines
= false;
41 // ////////////////////////////////////////////////////////////////////////// //
43 __gshared
ubyte[] prevFrameActorsData
;
44 __gshared
uint[65536] prevFrameActorOfs
; // uint.max-1: dead; uint.max: last
45 __gshared MonoTime lastthink
= MonoTime
.zero
; // for interpolator
46 __gshared MonoTime nextthink
= MonoTime
.zero
;
47 __gshared
bool frameInterpolation
= true;
48 __gshared
bool doLighting
= true;
51 // ////////////////////////////////////////////////////////////////////////// //
52 enum MaxLightRadius
= 512;
55 // ////////////////////////////////////////////////////////////////////////// //
57 __gshared FBO
[MaxLightRadius
+1] fboOccluders
, fboShadowMap
;
58 __gshared Shader shadToPolar
, shadBlur
, shadBlurOcc
;
60 __gshared FBO fboLevel
, fboLevelLight
, fboOrigBack
;
61 __gshared Shader shadScanlines
;
62 __gshared Shader shadLiquidDistort
;
65 // ////////////////////////////////////////////////////////////////////////// //
67 glEnable(GL_TEXTURE_2D
);
68 glDisable(GL_LIGHTING
);
71 glDisable(GL_DEPTH_TEST
);
74 shadScanlines
= new Shader("scanlines", loadTextFile("data/shaders/srscanlines.frag"));
76 shadLiquidDistort
= new Shader("liquid_distort", loadTextFile("data/shaders/srliquid_distort.frag"));
77 shadLiquidDistort
.exec((Shader shad
) { shad
["tex0"] = 0; });
80 shadToPolar
= new Shader("light_topolar", loadTextFile("data/shaders/srlight_topolar.frag"));
81 shadBlur
= new Shader("light_blur", loadTextFile("data/shaders/srlight_blur.frag"));
82 shadBlur
.exec((Shader shad
) {
87 shadBlurOcc
= new Shader("light_blur_occ", loadTextFile("data/shaders/srlight_blur_occ.frag"));
88 shadBlurOcc
.exec((Shader shad
) {
94 foreach (int sz
; 2..MaxLightRadius
+1) {
95 fboOccluders
[sz
] = new FBO(sz
*2, sz
*2, Texture
.Option
.Clamp
, Texture
.Option
.Linear
); // create occluders FBO
96 fboShadowMap
[sz
] = new FBO(sz
*2, 1, Texture
.Option
.Clamp
); // create 1d shadowmap FBO
100 glMatrixMode(GL_MODELVIEW
);
105 fboLevel
= new FBO(map
.width
*8, map
.height
*8, Texture
.Option
.Nearest
); // final level render will be here
106 fboLevelLight
= new FBO(map
.width
*8, map
.height
*8, Texture
.Option
.Nearest
); // level lights will be rendered here
107 //fboForeground = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // level foreground
108 fboOrigBack
= new FBO(map
.width
*8, map
.height
*8, Texture
.Option
.Nearest
); // background+foreground
110 shadBlur
.exec((Shader shad
) { shad
["mapPixSize"] = SVec2F(map
.width
*8, map
.height
*8); });
111 shadBlurOcc
.exec((Shader shad
) { shad
["mapPixSize"] = SVec2F(map
.width
*8, map
.height
*8); });
113 glActiveTexture(GL_TEXTURE0
+0);
114 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
115 orthoCamera(vlWidth
, vlHeight
);
119 Actor
.resetStorage();
122 // save first snapshot
123 prevFrameActorsData
= new ubyte[](Actor
.actorSize
*65536); // ~15-20 megabytes
124 prevFrameActorOfs
[] = uint.max
; // just for fun
125 Actor
.saveSnapshot(prevFrameActorsData
[], prevFrameActorOfs
.ptr
);
127 { import core
.memory
: GC
; GC
.collect(); }
131 // ////////////////////////////////////////////////////////////////////////// //
132 void renderLight() (int lightX
, int lightY
, in auto ref SVec4F lcol
, int lightRadius
) {
133 if (lightRadius
< 2) return;
134 if (lightRadius
> MaxLightRadius
) lightRadius
= MaxLightRadius
;
135 int lightSize
= lightRadius
*2;
136 // is this light visible?
137 if (lightX
<= -lightRadius || lightY
<= -lightRadius || lightX
-lightRadius
>= map
.width
*8 || lightY
-lightRadius
>= map
.height
*8) return;
139 // draw shadow casters to fboOccludersId, light should be in the center
142 fboOccluders
[lightRadius
].exec({
143 //glDisable(GL_BLEND);
144 glColor3f(0.0f, 0.0f, 0.0f);
145 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
146 glClear(GL_COLOR_BUFFER_BIT
);
147 orthoCamera(lightSize
, lightSize
);
148 drawAtXY(map
.texgl
.ptr
[map
.LightMask
], lightRadius
-lightX
, lightRadius
-lightY
);
151 // build 1d shadow map to fboShadowMapId
152 fboShadowMap
[lightRadius
].exec({
153 shadToPolar
.exec((Shader shad
) {
154 //glDisable(GL_BLEND);
155 glColor3f(0.0f, 0.0f, 0.0f);
156 shad
["lightTexSize"] = SVec2F(lightSize
, lightSize
);
157 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
158 glClear(GL_COLOR_BUFFER_BIT
);
159 orthoCamera(lightSize
, 1);
160 drawAtXY(fboOccluders
[lightRadius
].tex
.tid
, 0, 0, lightSize
, 1);
164 // build light texture for blending
165 fboOccluders
[lightRadius
].exec({
166 // no need to clear it, shader will take care of that
167 //glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
168 //glClear(GL_COLOR_BUFFER_BIT);
169 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
170 //glDisable(GL_BLEND);
171 shadBlur
.exec((Shader shad
) {
172 shad
["lightTexSize"] = SVec2F(lightSize
, lightSize
);
173 shad
["lightColor"] = SVec4F(lcol
.x
, lcol
.y
, lcol
.z
, lcol
.w
);
174 shad
["lightPos"] = SVec2F(lightX
, lightY
);
175 orthoCamera(lightSize
, lightSize
);
176 drawAtXY(fboShadowMap
[lightRadius
].tex
.tid
, 0, 0, lightSize
, lightSize
);
180 // blend light texture
183 glBlendFunc(GL_SRC_ALPHA
, GL_ONE
);
184 orthoCamera(map
.width
*8, map
.height
*8);
185 drawAtXY(fboOccluders
[lightRadius
].tex
, lightX
-lightRadius
, lightY
-lightRadius
, mirrorY
:true);
186 // and blend it again, somewhat bigger, with the shader that will touch only occluders
188 shadBlurOcc
.exec((Shader shad
) {
189 shad
["lightTexSize"] = SVec2F(lightSize
, lightSize
);
190 shad
["lightColor"] = SVec4F(lcol
.x
, lcol
.y
, lcol
.z
, lcol
.w
);
191 shad
["lightPos"] = SVec2F(lightX
, lightY
);
192 drawAtXY(fboOccluders
[lightRadius
].tex
.tid
, lightX
-lightRadius
-szmore
, lightY
-lightRadius
-szmore
, lightSize
+szmore
*2, lightSize
+szmore
*2, mirrorY
:true);
198 // ////////////////////////////////////////////////////////////////////////// //
201 enum Phase
{ FadeIn
, Stay
, FadeOut
}
210 private import core
.sync
.mutex
: Mutex
;
212 __gshared Message
[128] messages
;
213 __gshared
uint messagesUsed
= 0;
214 __gshared Mutex messageLock
;
216 shared static this () { messageLock
= new Mutex(); }
219 void addMessage (const(char)[] msgtext
, int pauseMsecs
=3000, bool noreplace
=false) {
221 scope(exit
) messageLock
.unlock();
222 if (msgtext
.length
== 0) return;
224 if (pauseMsecs
<= 50) return;
225 if (messagesUsed
== messages
.length
) {
226 // remove top message
227 foreach (immutable cidx
; 1..messagesUsed
) messages
.ptr
[cidx
-1] = messages
.ptr
[cidx
];
228 messages
.ptr
[0].alpha
= 255;
232 if (!noreplace
&& messagesUsed
== 1) {
233 switch (messages
.ptr
[0].phase
) {
234 case Message
.Phase
.FadeIn
:
235 messages
.ptr
[0].phase
= Message
.Phase
.FadeOut
;
237 case Message
.Phase
.Stay
:
238 messages
.ptr
[0].phase
= Message
.Phase
.FadeOut
;
239 messages
.ptr
[0].alpha
= 255;
244 auto msg
= messages
.ptr
+messagesUsed
;
246 msg
.phase
= Message
.Phase
.FadeIn
;
248 msg
.pauseMsecs
= pauseMsecs
;
250 if (msgtext
.length
> msg
.text
.length
) {
251 msg
.text
= msgtext
[0..msg
.text
.length
];
252 msg
.textlen
= msg
.text
.length
;
254 msg
.text
[0..msgtext
.length
] = msgtext
[];
255 msg
.textlen
= msgtext
.length
;
260 void doMessages (MonoTime curtime
) {
262 scope(exit
) messageLock
.unlock();
264 if (messagesUsed
== 0) return;
266 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
272 final switch (msg
.phase
) {
273 case Message
.Phase
.FadeIn
:
274 if ((msg
.alpha
+= 10) >= 255) {
275 msg
.phase
= Message
.Phase
.Stay
;
276 msg
.removeTime
= curtime
+dur
!"msecs"(msg
.pauseMsecs
);
277 goto case; // to stay
279 glColor4f(1.0f, 1.0f, 1.0f, msg
.alpha
/255.0f);
281 case Message
.Phase
.Stay
:
282 if (msg
.removeTime
<= curtime
) {
284 msg
.phase
= Message
.Phase
.FadeOut
;
285 goto case; // to fade
287 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
289 case Message
.Phase
.FadeOut
:
290 if ((msg
.alpha
-= 10) <= 0) {
291 if (--messagesUsed
== 0) return;
292 // remove this message
293 foreach (immutable cidx
; 1..messagesUsed
+1) messages
.ptr
[cidx
-1] = messages
.ptr
[cidx
];
296 glColor4f(1.0f, 1.0f, 1.0f, msg
.alpha
/255.0f);
300 smDrawText(10, 10, msg
.text
[0..msg
.textlen
]);
304 // ////////////////////////////////////////////////////////////////////////// //
305 __gshared
int lightX
= vlWidth
/2, lightY
= vlHeight
/2;
306 __gshared
int mapOfsX
, mapOfsY
;
307 __gshared
bool movement
= false;
308 __gshared
float iLiquidTime
= 0.0;
311 void renderScene (MonoTime curtime
) {
312 //enum BackIntens = 0.05f;
313 enum BackIntens
= 0.0f;
315 float atob
= (curtime
> lastthink ?
cast(float)((curtime
-lastthink
).total
!"msecs")/cast(float)((nextthink
-lastthink
).total
!"msecs") : 1.0f);
316 //{ import core.stdc.stdio; printf("atob=%f\n", cast(double)atob); }
319 int framelen = cast(int)((nextthink-lastthink).total!"msecs");
320 int curfp = cast(int)((curtime-lastthink).total!"msecs");
321 { import core.stdc.stdio; printf("framelen=%d; curfp=%d\n", framelen, curfp); }
327 // build background layer
329 //glDisable(GL_BLEND);
330 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
331 glClear(GL_COLOR_BUFFER_BIT
);
332 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
333 orthoCamera(map
.width
*8, map
.height
*8);
335 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
338 drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8);
339 drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2, map.MapSize*8, map.MapSize*8);
340 drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8);
341 drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2, map.MapSize*8, map.MapSize*8);
343 drawAtXY(map
.skytexgl
.tid
, 0, 0, map
.MapSize
*8, map
.MapSize
*8);
345 drawAtXY(map
.texgl
.ptr
[map
.Back
], 0, 0);
346 // draw distorted liquid areas
347 shadLiquidDistort
.exec((Shader shad
) {
348 shad
["iDistortTime"] = iLiquidTime
;
349 drawAtXY(map
.texgl
.ptr
[map
.AllLiquids
], 0, 0);
351 // monsters, items; we'll do linear interpolation here
352 // we need this for saved frame anyway, so let's play dirty and speed up the things
353 uint fxofs
= Actor
.fields
["x"].ofs
;
354 uint fyofs
= Actor
.fields
["y"].ofs
;
355 uint fzAnimstateofs
= Actor
.fields
["zAnimstate"].ofs
;
356 uint fdirofs
= Actor
.fields
["dir"].ofs
;
357 uint fzAnimidxofs
= Actor
.fields
["zAnimidx"].ofs
;
358 uint fclasstypeofs
= Actor
.fields
["classtype"].ofs
;
359 uint fclassnameofs
= Actor
.fields
["classname"].ofs
;
360 glColor3f(1.0f, 1.0f, 1.0f);
361 Actor
.forEach((ActorId me
) {
362 // `act` is always valid here
363 auto aptr
= me
.data
.ptr
;
364 auto ctstr
= StrId(*cast(uint*)(aptr
+fclasstypeofs
));
365 auto cnstr
= StrId(*cast(uint*)(aptr
+fclassnameofs
));
366 if (auto adef
= findActorDef(ctstr
, cnstr
)) {
367 ctstr
= StrId(*cast(uint*)(aptr
+fzAnimstateofs
));
368 if (auto isp
= adef
.animSpr(ctstr
, *cast(uint*)(aptr
+fdirofs
), *cast(int*)(aptr
+fzAnimidxofs
))) {
369 auto ofs
= prevFrameActorOfs
.ptr
[me
.id
&0xffff];
370 if (frameInterpolation
&& ofs
< uint.max
-1 && Actor
.isSameSavedActor(me
.id
, prevFrameActorsData
.ptr
, ofs
)) {
371 import core
.stdc
.math
: roundf
;
372 auto xptr
= prevFrameActorsData
.ptr
+ofs
;
373 int ox
= *cast(int*)(xptr
+fxofs
);
374 int nx
= *cast(int*)(aptr
+fxofs
);
375 int oy
= *cast(int*)(xptr
+fyofs
);
376 int ny
= *cast(int*)(aptr
+fyofs
);
377 int x
= cast(int)(ox
+roundf((nx
-ox
)*atob
));
378 int y
= cast(int)(oy
+roundf((ny
-oy
)*atob
));
379 //conwriteln("actor ", me.id, "; o=(", ox, ",", oy, "); n=(", nx, ",", ny, "); p=(", x, ",", y, ")");
380 drawAtXY(isp
.tex
, x
-isp
.vga
.sx
, y
-isp
.vga
.sy
);
382 int nx
= *cast(int*)(aptr
+fxofs
)-isp
.vga
.sx
;
383 int ny
= *cast(int*)(aptr
+fyofs
)-isp
.vga
.sy
;
384 //conwriteln("actor ", me.id, "; nx=", nx, "; ny=", ny);
385 drawAtXY(isp
.tex
, nx
, ny
);
388 conwriteln("no animation for actor ", me
.id
, " (", me
.classtype
!string
, ":", me
.classname
!string
, ")");
391 conwriteln("not found actor ", me
.id
, " (", me
.classtype
!string
, ":", me
.classname
!string
, ")");
394 if (auto adef = findActorDef(me.classtype!StrId, me.classname!StrId)) {
395 if (auto isp = adef.animSpr(me.zAnimstate!StrId, me.dir!uint, me.zAnimidx!int)) {
396 int y = me.y!int-isp.vga.sy;
397 drawAtXY(isp.tex, me.x!int-isp.vga.sx, y);
398 //conwriteln("found animation for actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")", " (", adef.fullname, ")");
400 conwriteln("no animation for actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")");
403 conwriteln("not found actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")");
407 // do liquid coloring
408 drawAtXY(map
.texgl
.ptr
[map
.LiquidMask
], 0, 0);
409 // foreground -- hide secrets, draw lifts and such
410 drawAtXY(map
.texgl
.ptr
[map
.Front
], 0, 0);
413 if (auto img = fftest) {
414 //conwriteln("img.sx=", img.sx, "; img.sy=", img.sy, "; ", img.width, "x", img.height);
415 drawAtXY(img.tex, 50-img.sx, 50-img.sy);
425 glClearColor(BackIntens
, BackIntens
, BackIntens
, 1.0f);
426 //glClearColor(0.15f, 0.15f, 0.15f, 1.0f);
427 ////glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
428 glClear(GL_COLOR_BUFFER_BIT
);
431 // texture 1 is background
432 glActiveTexture(GL_TEXTURE0
+1);
433 glBindTexture(GL_TEXTURE_2D
, fboOrigBack
.tex
.tid
);
434 // texture 2 is occluders
435 glActiveTexture(GL_TEXTURE0
+2);
436 glBindTexture(GL_TEXTURE_2D
, map
.texgl
.ptr
[map
.LightMask
].tid
);
437 // done texture assign
438 glActiveTexture(GL_TEXTURE0
+0);
442 renderLight( 27, 391-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
443 renderLight(542, 424-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
444 renderLight(377, 368-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
445 renderLight(147, 288-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
446 renderLight( 71, 200-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
447 renderLight(249, 200-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
448 renderLight(426, 200-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
449 renderLight(624, 200-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
450 renderLight(549, 298-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
451 renderLight( 74, 304-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
453 renderLight(24*8+4, (24+18)*8-2+LYOfs
, SVec4F(0.6f, 0.0f, 0.0f, 1.0f), 128);
455 foreach (immutable _
; 0..1) {
456 renderLight(lightX
, lightY
, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 96);
459 glActiveTexture(GL_TEXTURE0
+1);
460 glBindTexture(GL_TEXTURE_2D
, 0);
461 glActiveTexture(GL_TEXTURE0
+2);
462 glBindTexture(GL_TEXTURE_2D
, 0);
463 glActiveTexture(GL_TEXTURE0
+0);
468 shadScanlines.exec((Shader shad) {
469 shad["scanlines"] = scanlines;
470 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
471 glClear(GL_COLOR_BUFFER_BIT);
472 orthoCamera(vlWidth, vlHeight);
473 //orthoCamera(map.width*8*scale, map.height*8*scale);
474 //glMatrixMode(GL_MODELVIEW);
476 //glTranslatef(0.375, 0.375, 0); // to be pixel-perfect
477 // somehow, FBO objects are mirrored; wtf?!
478 drawAtXY(fboLevel.tex.tid, -mapOfsX, -mapOfsY, map.width*8*scale, map.height*8*scale, mirrorY:true);
485 smDrawText(map.width*8/2, map.height*8/2, "Testing...");
490 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
495 //auto img = smfont.ptr[0x39];
498 //conwriteln("img.sx=", img.sx, "; img.sy=", img.sy, "; ", img.width, "x", img.height);
499 drawAtXY(img.tex, 10-img.sx, 10-img.sy);
504 orthoCamera(vlWidth
, vlHeight
);
506 drawAtXY(fboLevelLight
.tex
.tid
, -mapOfsX
, -mapOfsY
, map
.width
*8*scale
, map
.height
*8*scale
, mirrorY
:true);
508 drawAtXY(fboOrigBack
.tex
.tid
, -mapOfsX
, -mapOfsY
, map
.width
*8*scale
, map
.height
*8*scale
, mirrorY
:true);
515 // ////////////////////////////////////////////////////////////////////////// //
516 // returns time slept
517 int sleepAtMaxMsecs (int msecs
) {
519 import core
.sys
.posix
.signal
: timespec
;
520 import core
.sys
.posix
.time
: nanosleep
;
521 timespec ts
= void, tpassed
= void;
523 ts
.tv_nsec
= msecs
*1000*1000+(500*1000); // milli to nano
524 nanosleep(&ts
, &tpassed
);
525 return (ts
.tv_nsec
-tpassed
.tv_nsec
)/(1000*1000);
532 // ////////////////////////////////////////////////////////////////////////// //
534 shared int diedie
= 0;
536 enum D2DFrameTime
= 55; // milliseconds
537 enum MinFrameTime
= 1000/60; // ~60 FPS
539 void renderThread () {
541 version(use_vsync
) {} else MonoTime ltt
= MonoTime
.currTime
;
543 MonoTime curtime
= MonoTime
.currTime
;
545 lastthink
= curtime
; // for interpolator
546 nextthink
= curtime
+dur
!"msecs"(D2DFrameTime
);
547 MonoTime nextvframe
= curtime
;
549 enum MaxFPSFrames
= 16;
550 float frtimes
= 0.0f;
553 int hushFrames
= 6; // ignore first `hushFrames` frames overtime
554 MonoTime prevFrameStartTime
= curtime
;
556 bool vframeWasLost
= false;
558 // "D2D frames"; curtime should be set; return `true` if frame was processed; will fix `curtime`
559 bool doThinkFrame () {
560 if (curtime
>= nextthink
) {
562 while (nextthink
<= curtime
) nextthink
+= dur
!"msecs"(D2DFrameTime
);
563 // save snapshot for interpolator
564 Actor
.saveSnapshot(prevFrameActorsData
[], prevFrameActorOfs
.ptr
);
568 auto tm
= MonoTime
.currTime
;
569 int thinkTime
= cast(int)((tm
-curtime
).total
!"msecs");
570 if (thinkTime
> 9) { import core
.stdc
.stdio
; printf("spent on thinking: %d msecs\n", thinkTime
); }
578 // "video frames"; curtime should be set; return `true` if frame was processed; will fix `curtime`
581 __gshared
bool prevLost
= false;
582 bool doCheckTime
= vframeWasLost
;
585 { import core
.stdc
.stdio
; printf("frame was lost!\n"); }
592 enum doCheckTime
= true;
595 if (curtime
< nextvframe
) return false;
596 version(use_vsync
) {} else {
597 if (curtime
> nextvframe
) {
598 auto overtime
= cast(int)((curtime
-nextvframe
).total
!"msecs");
599 if (overtime
> 2500) {
603 { import core
.stdc
.stdio
; printf(" spent whole %d msecs\n", overtime
); }
609 while (nextvframe
<= curtime
) nextvframe
+= dur
!"msecs"(MinFrameTime
);
613 scope(exit
) sdwindow
.mtUnlock();
614 ctset
= sdwindow
.setAsCurrentOpenGlContextNT
;
616 // if we can't set context, pretend that videoframe was processed; this should solve problem with vsync and invisible window
619 iLiquidTime
= cast(float)((curtime
-MonoTime
.zero
).total
!"msecs"%10000000)/18.0f*0.04f;
620 renderScene(curtime
);
622 scope(exit
) sdwindow
.mtUnlock();
623 sdwindow
.swapOpenGlBuffers();
625 sdwindow
.releaseCurrentOpenGlContext();
626 vframeWasLost
= false;
628 vframeWasLost
= true;
629 { import core
.stdc
.stdio
; printf("xframe was lost!\n"); }
631 curtime
= MonoTime
.currTime
;
636 if (sdwindow
.closed
) break;
637 if (atomicLoad(diedie
) > 0) break;
639 curtime
= MonoTime
.currTime
;
640 auto fstime
= curtime
;
642 doThinkFrame(); // this will fix curtime if necessary
644 if (!vframeWasLost
) {
646 auto frameTime
= cast(float)(curtime
-prevFrameStartTime
).total
!"msecs"/1000.0f;
647 prevFrameStartTime
= curtime
;
648 frtimes
+= frameTime
;
649 if (++framenum
>= MaxFPSFrames || frtimes
>= 3.0f) {
650 import std
.string
: format
;
651 int newFPS
= cast(int)(cast(float)MaxFPSFrames
/frtimes
+0.5);
652 if (newFPS
!= prevFPS
) {
653 sdwindow
.title
= "%s / FPS:%s".format("D2D", newFPS
);
662 curtime
= MonoTime
.currTime
;
664 // now sleep until next "video" or "think" frame
665 if (nextthink
> curtime
&& nextvframe
> curtime
) {
667 immutable nextVideoFrameSleep
= cast(int)((nextvframe
-curtime
).total
!"msecs");
668 immutable nextThinkFrameSleep
= cast(int)((nextthink
-curtime
).total
!"msecs");
669 immutable sleepTime
= (nextVideoFrameSleep
< nextThinkFrameSleep ? nextVideoFrameSleep
: nextThinkFrameSleep
);
670 sleepAtMaxMsecs(sleepTime
);
671 //curtime = MonoTime.currTime;
674 } catch (Exception e
) {
675 import core
.stdc
.stdio
;
676 fprintf(stderr
, "FUUUUUUUUUUUUUUUUUUUUUUUUUU\n");
678 if (sdwindow
.closed
) break;
679 if (atomicLoad(diedie
) > 0) break;
680 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
681 sleepAtMaxMsecs(100);
684 atomicStore(diedie
, 2);
688 // ////////////////////////////////////////////////////////////////////////// //
689 void closeWindow () {
690 if (atomicLoad(diedie
) != 2) {
691 atomicStore(diedie
, 1);
692 while (atomicLoad(diedie
) != 2) {}
694 if (!sdwindow
.closed
) {
701 // ////////////////////////////////////////////////////////////////////////// //
702 __gshared Thread renderTid
;
705 void main (string
[] args
) {
706 FuncPool
.dumpCode
= false;
707 FuncPool
.dumpCodeSize
= false;
708 dacsDumpSemantic
= false;
710 //version(rdmd) { dacsOptimize = 0; }
711 bool compileOnly
= false;
713 for (usize idx
= 1; idx
< args
.length
; ++idx
) {
715 if (args
[idx
] == "--dump-code") FuncPool
.dumpCode
= true;
716 else if (args
[idx
] == "--dump-code-size") FuncPool
.dumpCodeSize
= true;
717 else if (args
[idx
] == "--dump-semantic") dacsDumpSemantic
= true;
718 else if (args
[idx
] == "--dump-all") { FuncPool
.dumpCode
= true; FuncPool
.dumpCodeSize
= true; dacsDumpSemantic
= true; }
719 else if (args
[idx
] == "--compile") compileOnly
= true;
720 else if (args
[idx
] == "--compile-only") compileOnly
= true;
721 else if (args
[idx
] == "--messages") ++dacsMessages
;
722 else if (args
[idx
] == "--no-copro") dacsOptimizeNoCoPro
= true;
723 else if (args
[idx
] == "--no-deadass") dacsOptimizeNoDeadAss
= true;
724 else if (args
[idx
] == "--no-purekill") dacsOptimizeNoPureKill
= true;
725 else if (args
[idx
].length
> 2 && args
[idx
][0..2] == "-O") {
726 import std
.conv
: to
;
727 ubyte olevel
= to
!ubyte(args
[idx
][2..$]);
728 dacsOptimize
= olevel
;
732 foreach (immutable c
; idx
+1..args
.length
) args
.ptr
[c
-1] = args
.ptr
[c
];
738 static void setDP () {
742 import std
.file
: thisExePath
;
743 import std
.path
: dirName
;
744 setDataPath(thisExePath
.dirName
);
746 addPK3(getDataPath
~"base.pk3"); loadWadScripts();
747 //addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad"); loadWadScripts();
748 //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad"); loadWadScripts();
749 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad"); loadWadScripts();
750 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad"); loadWadScripts();
751 //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad"); loadWadScripts();
752 //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad"); loadWadScripts();
753 scriptLoadingComplete();
758 if (compileOnly
) return;
764 setOpenGLContextVersion(3, 2); // up to GLSL 150
765 //openGLContextCompatible = false;
767 map
= new LevelMap("maps/map01.d2m");
771 map
.getThingPos(1/*ThingId.Player1*/, &mapOfsX
, &mapOfsY
);
773 mapOfsX
= (mapOfsX
*2)-vlWidth
/2;
774 if (mapOfsX
+vlWidth
> map
.width
*16) mapOfsX
= map
.width
*16-vlWidth
;
775 if (mapOfsX
< 0) mapOfsX
= 0;
776 mapOfsY
= (mapOfsY
*2)-vlHeight
/2;
777 if (mapOfsY
+vlHeight
> map
.height
*16) mapOfsY
= map
.height
*16-vlHeight
;
778 if (mapOfsY
< 0) mapOfsY
= 0;
781 sdwindow
= new SimpleWindow(vlWidth
, vlHeight
, "D2D", OpenGlOptions
.yes
, Resizablity
.fixedSize
);
783 sdwindow
.visibleForTheFirstTime
= delegate () {
784 sdwindow
.setAsCurrentOpenGlContext(); // make this window active
785 glbindLoadFunctions();
788 import core.stdc.stdio;
789 printf("GL version: %s\n", glGetString(GL_VERSION));
791 glGetIntegerv(GL_MAJOR_VERSION, &h);
792 glGetIntegerv(GL_MINOR_VERSION, &l);
793 printf("version: %d.%d\n", h, l);
794 printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
796 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
798 printf("%d shader versions supported:\n", svcount);
799 foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n));
803 glGetIntegerv(GL_NUM_EXTENSIONS, &ecount);
805 printf("%d extensions supported:\n", ecount);
806 foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n));
811 // check if we have sufficient shader version here
815 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS
, &svcount
);
817 foreach (GLuint n
; 0..svcount
) {
818 import core
.stdc
.string
: strncmp
;
819 auto v
= glGetStringi(GL_SHADING_LANGUAGE_VERSION
, n
);
820 if (v
is null) continue;
821 if (strncmp(v
, "120", 3) != 0) continue;
822 if (v
[3] > ' ') continue;
827 if (!found
) assert(0, "can't find OpenGL GLSL 120");
829 auto adr
= glGetProcAddress("glTexParameterf");
830 if (adr
is null) assert(0);
834 sdwindow
.vsync
= true;
836 sdwindow
.vsync
= false;
838 //sdwindow.useGLFinish = false;
840 //sdwindow.redrawOpenGlScene();
841 if (!sdwindow
.releaseCurrentOpenGlContext()) { import core
.stdc
.stdio
; printf("can't release OpenGL context(1)\n"); }
843 renderTid
= new Thread(&renderThread
);
848 //sdwindow.redrawOpenGlScene = delegate () { renderScene(); };
850 enum MSecsPerFrame
= 1000/30; /* 30 is FPS */
852 uint[8] frameTimes
= 1000;
853 enum { Left
, Right
, Up
, Down
}
854 bool[4] pressed
= false;
855 bool altMove
= false;
857 sdwindow
.eventLoop(MSecsPerFrame
,
859 if (sdwindow
.closed
) return;
860 if (pressed
[Left
]) mapOfsX
-= 8;
861 if (pressed
[Right
]) mapOfsX
+= 8;
862 if (pressed
[Up
]) mapOfsY
-= 8;
863 if (pressed
[Down
]) mapOfsY
+= 8;
864 import std
.math
: cos
, sin
;
865 __gshared
float itime
= 0.0;
868 mapOfsX
= cast(int)(800.0/2.0+cos(itime
)*220.0);
869 mapOfsY
= cast(int)(800.0/2.0+120.0+sin(itime
)*160.0);
871 if (scale
== 1) mapOfsX
= mapOfsY
= 0;
872 //sdwindow.redrawOpenGlSceneNow();
874 delegate (KeyEvent event
) {
875 if (sdwindow
.closed
) return;
876 if (event
.pressed
&& event
.key
== Key
.Escape
) { closeWindow(); return; }
878 case Key
.X
: if (event
.pressed
) altMove
= !altMove
; break;
879 case Key
.Left
: if (altMove
) pressed
[Left
] = event
.pressed
; break;
880 case Key
.Right
: if (altMove
) pressed
[Right
] = event
.pressed
; break;
881 case Key
.Up
: if (altMove
) pressed
[Up
] = event
.pressed
; break;
882 case Key
.Down
: if (altMove
) pressed
[Down
] = event
.pressed
; break;
887 case Key
.Left
: case Key
.Pad4
: plrKeyUpDown(0, PLK_LEFT
, event
.pressed
); break;
888 case Key
.Right
: case Key
.Pad6
: plrKeyUpDown(0, PLK_RIGHT
, event
.pressed
); break;
889 case Key
.Up
: case Key
.Pad8
: plrKeyUpDown(0, PLK_UP
, event
.pressed
); break;
890 case Key
.Down
: case Key
.Pad2
: plrKeyUpDown(0, PLK_DOWN
, event
.pressed
); break;
891 case Key
.Alt
: plrKeyUpDown(0, PLK_JUMP
, event
.pressed
); break;
892 case Key
.Ctrl
: plrKeyUpDown(0, PLK_FIRE
, event
.pressed
); break;
893 case Key
.Shift
: plrKeyUpDown(0, PLK_USE
, event
.pressed
); break;
898 delegate (MouseEvent event
) {
899 lightX
= event
.x
/scale
;
900 lightY
= event
.y
/scale
;
901 lightX
+= mapOfsX
/scale
;
902 lightY
+= mapOfsY
/scale
;
904 delegate (dchar ch
) {
905 if (ch
== 'q') { closeWindow(); return; }
906 if (ch
== 's') scanlines
= !scanlines
;
907 if (ch
== '1') scale
= 1;
908 if (ch
== '2') scale
= 2;
910 frameInterpolation
= !frameInterpolation
;
911 if (frameInterpolation
) addMessage("Interpolation: ON"); else addMessage("Interpolation: OFF");
914 doLighting
= !doLighting
;
915 if (doLighting
) addMessage("Lighting: ON"); else addMessage("Lighting: OFF");
917 if (ch
== ' ') movement
= !movement
;
920 } catch (Exception e
) {
921 import std
.stdio
: stderr
;
922 stderr
.writeln("FUUUUUUUUUUUU\n", e
.toString
);