1 /* DooM2D: Midnight on the Firing Line
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module xmain_d2d
is aliced
;
20 //version = dont_use_vsync;
49 // ////////////////////////////////////////////////////////////////////////// //
54 // ////////////////////////////////////////////////////////////////////////// //
55 public __gshared
bool cheatNoDoors
= false;
58 // ////////////////////////////////////////////////////////////////////////// //
59 __gshared SimpleWindow sdwindow
;
62 public enum vlWidth
= 800;
63 public enum vlHeight
= 800;
64 public __gshared
int scale
= 1;
67 // ////////////////////////////////////////////////////////////////////////// //
68 __gshared
bool scanlines
= false;
69 __gshared
bool doLighting
= true;
72 // ////////////////////////////////////////////////////////////////////////// //
74 __gshared
ubyte[] prevFrameActorsData
;
75 __gshared
uint[65536] prevFrameActorOfs
; // uint.max-1: dead; uint.max: last
76 __gshared MonoTime lastthink
= MonoTime
.zero
; // for interpolator
77 __gshared MonoTime nextthink
= MonoTime
.zero
;
78 __gshared
bool frameInterpolation
= true;
81 __gshared
int[2] mapViewPosX
, mapViewPosY
; // [0]: previous frame -- for interpolator
84 // this should be screen center
85 public void setMapViewPos (int x
, int y
) {
91 int swdt
= vlWidth
/scale
;
92 int shgt
= vlHeight
/scale
;
95 if (x
< 0) x
= 0; else if (x
>= map
.width
*8-swdt
) x
= map
.width
*8-swdt
-1;
96 if (y
< 0) y
= 0; else if (y
>= map
.height
*8-shgt
) y
= map
.height
*8-shgt
-1;
97 mapViewPosX
[1] = x
*scale
;
98 mapViewPosY
[1] = y
*scale
;
102 // ////////////////////////////////////////////////////////////////////////// //
104 struct AttachedLightInfo
{
111 __gshared AttachedLightInfo
[65536] attachedLights
;
112 __gshared
uint attachedLightCount
= 0;
115 // ////////////////////////////////////////////////////////////////////////// //
116 enum MaxLightRadius
= 512;
119 // ////////////////////////////////////////////////////////////////////////// //
121 __gshared FBO
[MaxLightRadius
+1] fboOccluders
, fboShadowMap
;
122 __gshared Shader shadToPolar
, shadBlur
, shadBlurOcc
;
124 __gshared FBO fboLevel
, fboLevelLight
, fboOrigBack
;
125 __gshared Shader shadScanlines
;
126 __gshared Shader shadLiquidDistort
;
129 // ////////////////////////////////////////////////////////////////////////// //
133 glEnable(GL_TEXTURE_2D
);
134 glDisable(GL_LIGHTING
);
135 glDisable(GL_DITHER
);
137 glDisable(GL_DEPTH_TEST
);
140 shadScanlines
= new Shader("scanlines", loadTextFile("data/shaders/srscanlines.frag"));
142 shadLiquidDistort
= new Shader("liquid_distort", loadTextFile("data/shaders/srliquid_distort.frag"));
143 shadLiquidDistort
.exec((Shader shad
) { shad
["tex0"] = 0; });
146 shadToPolar
= new Shader("light_topolar", loadTextFile("data/shaders/srlight_topolar.frag"));
148 shadBlur
= new Shader("light_blur", loadTextFile("data/shaders/srlight_blur.frag"));
149 shadBlur
.exec((Shader shad
) {
155 shadBlurOcc
= new Shader("light_blur_occ", loadTextFile("data/shaders/srlight_blur_occ.frag"));
156 shadBlurOcc
.exec((Shader shad
) {
163 foreach (int sz
; 2..MaxLightRadius
+1) {
164 fboOccluders
[sz
] = new FBO(sz
*2, sz
*2, Texture
.Option
.Clamp
, Texture
.Option
.Linear
); // create occluders FBO
165 fboShadowMap
[sz
] = new FBO(sz
*2, 1, Texture
.Option
.Clamp
); // create 1d shadowmap FBO
169 glMatrixMode(GL_MODELVIEW
);
175 fboLevel
= new FBO(map
.width
*8, map
.height
*8, Texture
.Option
.Nearest
); // final level render will be here
176 fboLevelLight
= new FBO(map
.width
*8, map
.height
*8, Texture
.Option
.Nearest
); // level lights will be rendered here
177 //fboForeground = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // level foreground
178 fboOrigBack
= new FBO(map
.width
*8, map
.height
*8, Texture
.Option
.Nearest
, Texture
.Option
.Depth
); // background+foreground
180 shadBlur
.exec((Shader shad
) { shad
["mapPixSize"] = SVec2F(map
.width
*8, map
.height
*8); });
181 shadBlurOcc
.exec((Shader shad
) { shad
["mapPixSize"] = SVec2F(map
.width
*8, map
.height
*8); });
183 glActiveTexture(GL_TEXTURE0
+0);
184 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
185 orthoCamera(vlWidth
, vlHeight
);
189 Actor
.resetStorage();
194 // save first snapshot
195 prevFrameActorsData
= new ubyte[](Actor
.actorSize
*65536); // ~15-20 megabytes
196 prevFrameActorOfs
[] = uint.max
; // just for fun
197 Actor
.saveSnapshot(prevFrameActorsData
[], prevFrameActorOfs
.ptr
);
198 mapViewPosX
[0] = mapViewPosX
[1];
199 mapViewPosY
[0] = mapViewPosY
[1];
201 { import core
.memory
: GC
; GC
.collect(); }
205 // ////////////////////////////////////////////////////////////////////////// //
207 __gshared
uint mapTilesChanged
= 0;
210 public void mapDirty (uint layermask
) { mapTilesChanged |
= layermask
; }
212 void rebuildMapMegaTextures () {
214 //mapTilesChanged = false;
215 //map.clearMegaTextures();
216 map
.oglBuildMega(mapTilesChanged
);
221 // ////////////////////////////////////////////////////////////////////////// //
222 __gshared
int vportX0
, vportY0
, vportX1
, vportY1
;
225 void renderLight() (int lightX
, int lightY
, in auto ref SVec4F lcol
, int lightRadius
) {
226 if (lightRadius
< 2) return;
227 if (lightRadius
> MaxLightRadius
) lightRadius
= MaxLightRadius
;
228 int lightSize
= lightRadius
*2;
229 // is this light visible?
230 if (lightX
<= -lightRadius || lightY
<= -lightRadius || lightX
-lightRadius
>= map
.width
*8 || lightY
-lightRadius
>= map
.height
*8) return;
232 // out of viewport -- do nothing
233 if (lightX
+lightRadius
< vportX0 || lightY
+lightRadius
< vportY0
) return;
234 if (lightX
-lightRadius
> vportX1 || lightY
-lightRadius
> vportY1
) return;
236 // draw shadow casters to fboOccludersId, light should be in the center
239 fboOccluders
[lightRadius
].exec({
240 //glDisable(GL_BLEND);
241 glColor3f(0.0f, 0.0f, 0.0f);
242 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
243 glClear(GL_COLOR_BUFFER_BIT
);
244 orthoCamera(lightSize
, lightSize
);
245 drawAtXY(map
.texgl
.ptr
[map
.LightMask
], lightRadius
-lightX
, lightRadius
-lightY
);
248 // build 1d shadow map to fboShadowMapId
249 fboShadowMap
[lightRadius
].exec({
250 shadToPolar
.exec((Shader shad
) {
251 //glDisable(GL_BLEND);
252 glColor3f(0.0f, 0.0f, 0.0f);
253 shad
["lightTexSize"] = SVec2F(lightSize
, lightSize
);
254 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
255 glClear(GL_COLOR_BUFFER_BIT
);
256 orthoCamera(lightSize
, 1);
257 drawAtXY(fboOccluders
[lightRadius
].tex
.tid
, 0, 0, lightSize
, 1);
261 // build light texture for blending
262 fboOccluders
[lightRadius
].exec({
263 // no need to clear it, shader will take care of that
264 //glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
265 //glClear(GL_COLOR_BUFFER_BIT);
266 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
267 //glDisable(GL_BLEND);
268 shadBlur
.exec((Shader shad
) {
269 shad
["lightTexSize"] = SVec2F(lightSize
, lightSize
);
270 shad
["lightColor"] = SVec4F(lcol
.x
, lcol
.y
, lcol
.z
, lcol
.w
);
271 shad
["lightPos"] = SVec2F(lightX
, lightY
);
272 orthoCamera(lightSize
, lightSize
);
273 drawAtXY(fboShadowMap
[lightRadius
].tex
.tid
, 0, 0, lightSize
, lightSize
);
277 // blend light texture
280 glBlendFunc(GL_SRC_ALPHA
, GL_ONE
);
281 orthoCamera(map
.width
*8, map
.height
*8);
282 drawAtXY(fboOccluders
[lightRadius
].tex
, lightX
-lightRadius
, lightY
-lightRadius
, mirrorY
:true);
283 // and blend it again, somewhat bigger, with the shader that will touch only occluders
285 shadBlurOcc
.exec((Shader shad
) {
286 shad
["lightTexSize"] = SVec2F(lightSize
, lightSize
);
287 shad
["lightColor"] = SVec4F(lcol
.x
, lcol
.y
, lcol
.z
, lcol
.w
);
288 shad
["lightPos"] = SVec2F(lightX
, lightY
);
289 drawAtXY(fboOccluders
[lightRadius
].tex
.tid
, lightX
-lightRadius
-szmore
, lightY
-lightRadius
-szmore
, lightSize
+szmore
*2, lightSize
+szmore
*2, mirrorY
:true);
295 // ////////////////////////////////////////////////////////////////////////// //
298 enum Phase
{ FadeIn
, Stay
, FadeOut
}
307 private import core
.sync
.mutex
: Mutex
;
309 __gshared Message
[128] messages
;
310 __gshared
uint messagesUsed
= 0;
311 __gshared Mutex messageLock
;
313 shared static this () { messageLock
= new Mutex(); }
316 public void addMessage (const(char)[] msgtext
, int pauseMsecs
=3000, bool noreplace
=false) {
318 scope(exit
) messageLock
.unlock();
319 if (msgtext
.length
== 0) return;
321 if (pauseMsecs
<= 50) return;
322 if (messagesUsed
== messages
.length
) {
323 // remove top message
324 foreach (immutable cidx
; 1..messagesUsed
) messages
.ptr
[cidx
-1] = messages
.ptr
[cidx
];
325 messages
.ptr
[0].alpha
= 255;
329 if (!noreplace
&& messagesUsed
== 1) {
330 switch (messages
.ptr
[0].phase
) {
331 case Message
.Phase
.FadeIn
:
332 messages
.ptr
[0].phase
= Message
.Phase
.FadeOut
;
334 case Message
.Phase
.Stay
:
335 messages
.ptr
[0].phase
= Message
.Phase
.FadeOut
;
336 messages
.ptr
[0].alpha
= 255;
341 auto msg
= messages
.ptr
+messagesUsed
;
343 msg
.phase
= Message
.Phase
.FadeIn
;
345 msg
.pauseMsecs
= pauseMsecs
;
347 if (msgtext
.length
> msg
.text
.length
) {
348 msg
.text
= msgtext
[0..msg
.text
.length
];
349 msg
.textlen
= msg
.text
.length
;
351 msg
.text
[0..msgtext
.length
] = msgtext
[];
352 msg
.textlen
= msgtext
.length
;
357 void doMessages (MonoTime curtime
) {
359 scope(exit
) messageLock
.unlock();
361 if (messagesUsed
== 0) return;
363 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
369 final switch (msg
.phase
) {
370 case Message
.Phase
.FadeIn
:
371 if ((msg
.alpha
+= 10) >= 255) {
372 msg
.phase
= Message
.Phase
.Stay
;
373 msg
.removeTime
= curtime
+dur
!"msecs"(msg
.pauseMsecs
);
374 goto case; // to stay
376 glColor4f(1.0f, 1.0f, 1.0f, msg
.alpha
/255.0f);
378 case Message
.Phase
.Stay
:
379 if (msg
.removeTime
<= curtime
) {
381 msg
.phase
= Message
.Phase
.FadeOut
;
382 goto case; // to fade
384 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
386 case Message
.Phase
.FadeOut
:
387 if ((msg
.alpha
-= 10) <= 0) {
388 if (--messagesUsed
== 0) return;
389 // remove this message
390 foreach (immutable cidx
; 1..messagesUsed
+1) messages
.ptr
[cidx
-1] = messages
.ptr
[cidx
];
393 glColor4f(1.0f, 1.0f, 1.0f, msg
.alpha
/255.0f);
397 smDrawText(10, 10, msg
.text
[0..msg
.textlen
]);
401 // ////////////////////////////////////////////////////////////////////////// //
402 mixin(Actor
.FieldGetMixin
!("classtype", StrId
)); // fget_classtype
403 mixin(Actor
.FieldGetMixin
!("classname", StrId
)); // fget_classname
404 mixin(Actor
.FieldGetMixin
!("x", int));
405 mixin(Actor
.FieldGetMixin
!("y", int));
406 mixin(Actor
.FieldGetMixin
!("flags", uint));
407 mixin(Actor
.FieldGetMixin
!("zAnimstate", StrId
));
408 mixin(Actor
.FieldGetMixin
!("zAnimidx", int));
409 mixin(Actor
.FieldGetMixin
!("dir", uint));
410 mixin(Actor
.FieldGetMixin
!("attLightXOfs", int));
411 mixin(Actor
.FieldGetMixin
!("attLightYOfs", int));
412 mixin(Actor
.FieldGetMixin
!("attLightRGBX", uint));
414 mixin(Actor
.FieldGetPtrMixin
!("classtype", StrId
)); // fget_classtype
415 mixin(Actor
.FieldGetPtrMixin
!("classname", StrId
)); // fget_classname
416 mixin(Actor
.FieldGetPtrMixin
!("x", int));
417 mixin(Actor
.FieldGetPtrMixin
!("y", int));
418 mixin(Actor
.FieldGetPtrMixin
!("flags", uint));
419 mixin(Actor
.FieldGetPtrMixin
!("zAnimstate", StrId
));
420 mixin(Actor
.FieldGetPtrMixin
!("zAnimidx", int));
421 mixin(Actor
.FieldGetPtrMixin
!("dir", uint));
422 mixin(Actor
.FieldGetPtrMixin
!("attLightXOfs", int));
423 mixin(Actor
.FieldGetPtrMixin
!("attLightYOfs", int));
424 mixin(Actor
.FieldGetPtrMixin
!("attLightRGBX", uint));
427 // ////////////////////////////////////////////////////////////////////////// //
428 __gshared
int lightX
= vlWidth
/2, lightY
= vlHeight
/2;
429 __gshared
int mapOfsX
, mapOfsY
;
430 __gshared
bool movement
= false;
431 __gshared
float iLiquidTime
= 0.0;
432 __gshared
bool altMove
= false;
435 void renderScene (MonoTime curtime
) {
436 //enum BackIntens = 0.05f;
437 enum BackIntens
= 0.0f;
441 float atob
= (curtime
> lastthink ?
cast(float)((curtime
-lastthink
).total
!"msecs")/cast(float)((nextthink
-lastthink
).total
!"msecs") : 1.0f);
442 //{ import core.stdc.stdio; printf("atob=%f\n", cast(double)atob); }
445 int framelen = cast(int)((nextthink-lastthink).total!"msecs");
446 int curfp = cast(int)((curtime-lastthink).total!"msecs");
447 { import core.stdc.stdio; printf("framelen=%d; curfp=%d\n", framelen, curfp); }
453 bool camCenter
= true;
454 if (altMove || movement || scale
== 1) {
459 vportX1
= map
.width
*8;
460 vportY1
= map
.height
*8;
463 if (frameInterpolation
) {
464 import core
.stdc
.math
: roundf
;
465 mofsx
= cast(int)(mapViewPosX
[0]+roundf((mapViewPosX
[1]-mapViewPosX
[0])*atob
));
466 mofsy
= cast(int)(mapViewPosY
[0]+roundf((mapViewPosY
[1]-mapViewPosY
[0])*atob
));
468 mofsx
= mapViewPosX
[1];
469 mofsy
= mapViewPosY
[1];
471 vportX0
= mofsx
/scale
;
472 vportY0
= mofsy
/scale
;
473 vportX1
= vportX0
+vlWidth
/scale
;
474 vportY1
= vportY0
+vlHeight
/scale
;
477 if (mapTilesChanged
!= 0) rebuildMapMegaTextures();
481 // build background layer
483 //glDisable(GL_BLEND);
484 //glClearDepth(1.0f);
485 //glDepthFunc(GL_LESS);
486 //glDepthFunc(GL_NEVER);
487 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
488 glClear(GL_COLOR_BUFFER_BIT
/*|GL_DEPTH_BUFFER_BIT*/);
489 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
490 orthoCamera(map
.width
*8, map
.height
*8);
493 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
496 drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8);
497 drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2, map.MapSize*8, map.MapSize*8);
498 drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8);
499 drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2, map.MapSize*8, map.MapSize*8);
501 drawAtXY(map
.skytexgl
.tid
, 0, 0, map
.MapSize
*8, map
.MapSize
*8);
503 drawAtXY(map
.texgl
.ptr
[map
.Back
], 0, 0);
504 // draw distorted liquid areas
505 shadLiquidDistort
.exec((Shader shad
) {
506 shad
["iDistortTime"] = iLiquidTime
;
507 drawAtXY(map
.texgl
.ptr
[map
.AllLiquids
], 0, 0);
509 // monsters, items; we'll do linear interpolation here
510 glColor3f(1.0f, 1.0f, 1.0f);
511 //glEnable(GL_DEPTH_TEST);
512 attachedLightCount
= 0;
514 // who cares about memory?!
515 // draw order: players, items, monsters, other
516 static struct DrawInfo
{
520 @disable this (this); // no copies
522 enum { Players
, Items
, Monsters
, Other
}
523 __gshared DrawInfo
[65536][4] drawlists
;
524 __gshared
uint[4] dlpos
;
528 Actor
.forEach((ActorId me
) {
529 if (auto adef
= findActorDef(me
)) {
531 switch (adef
.classtype
.get
) {
532 case "monster": dlnum
= (adef
.classname
.get
!= "Player" ? Monsters
: Players
); break;
533 case "item": dlnum
= Items
; break;
534 default: dlnum
= Other
; break;
536 int actorX
, actorY
; // current actor position
538 auto ofs
= prevFrameActorOfs
.ptr
[me
.id
&0xffff];
539 if (frameInterpolation
&& ofs
< uint.max
-1 && Actor
.isSameSavedActor(me
.id
, prevFrameActorsData
.ptr
, ofs
)) {
540 import core
.stdc
.math
: roundf
;
541 auto xptr
= prevFrameActorsData
.ptr
+ofs
;
542 int ox
= xptr
.fgetp_x
;
544 int oy
= xptr
.fgetp_y
;
546 actorX
= cast(int)(ox
+roundf((nx
-ox
)*atob
));
547 actorY
= cast(int)(oy
+roundf((ny
-oy
)*atob
));
548 //conwriteln("actor ", me.id, "; o=(", ox, ",", oy, "); n=(", nx, ",", ny, "); p=(", x, ",", y, ")");
555 if ((me
.fget_flags
&AF_NODRAW
) == 0) {
556 //auto dl = &drawlists[dlnum][dlpos.ptr[dlnum]];
557 auto dl = drawlists
.ptr
[dlnum
].ptr
+dlpos
.ptr
[dlnum
];
564 if (auto isp = adef.animSpr(me.fget_zAnimstate, me.fget_dir, me.fget_zAnimidx)) {
565 drawAtXY(isp.tex, actorX-isp.vga.sx, actorY-isp.vga.sy, adef.zz);
567 //conwriteln("no animation for actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")");
571 // process attached lights
572 if ((me
.fget_flags
&AF_NOLIGHT
) == 0) {
573 uint alr
= me
.fget_attLightRGBX
;
574 if ((alr
&0xff) >= 4) {
576 auto li
= attachedLights
.ptr
+attachedLightCount
;
577 ++attachedLightCount
;
578 li
.x
= actorX
+me
.fget_attLightXOfs
;
579 li
.y
= actorY
+me
.fget_attLightYOfs
;
580 li
.r
= ((alr
>>24)&0xff)/255.0f; // red or intensity
581 if ((alr
&0x00_ff_ff
_00U) == 0x00_00_01_00U) {
584 li
.g
= ((alr
>>16)&0xff)/255.0f;
585 li
.b
= ((alr
>>8)&0xff)/255.0f;
586 li
.uncolored
= false;
588 li
.radius
= (alr
&0xff);
589 if (li
.radius
> MaxLightRadius
) li
.radius
= MaxLightRadius
;
593 conwriteln("not found actor ", me
.id
, " (", me
.classtype
!string
, ":", me
.classname
!string
, ")");
597 foreach_reverse (uint dlnum
; 0..drawlists
.length
) {
598 auto dl = drawlists
.ptr
[dlnum
].ptr
;
599 if (dlnum
== Players
) dl += dlpos
.ptr
[dlnum
]-1;
600 foreach (uint idx
; 0..dlpos
.ptr
[dlnum
]) {
602 if (auto isp
= dl.adef
.animSpr(me
.fget_zAnimstate
, me
.fget_dir
, me
.fget_zAnimidx
)) {
603 drawAtXY(isp
.tex
, dl.actorX
-isp
.vga
.sx
, dl.actorY
-isp
.vga
.sy
);
605 if (dlnum
!= Players
) ++dl; else --dl;
608 if (camCenter
&& dlpos
.ptr
[Players
]) {
609 int tiltHeight
= /*getMapViewHeight()*/(vlHeight
/scale
)/4;
610 int vy
= players
.ptr
[0].looky
!int;
611 if (vy
< -tiltHeight
) vy
= -tiltHeight
; else if (vy
> tiltHeight
) vy
= tiltHeight
;
612 //setMapViewPos(me.x, me.y+vy);
613 auto dl = drawlists
.ptr
[Players
].ptr
;
614 //mofsx = dl.actorX*2;
615 //mofsy = dl.actorY*2;
616 int swdt
= vlWidth
/scale
;
617 int shgt
= vlHeight
/scale
;
618 int x
= dl.actorX
-swdt
/2;
619 int y
= (dl.actorY
+vy
)-shgt
/2;
620 if (x
< 0) x
= 0; else if (x
>= map
.width
*8-swdt
) x
= map
.width
*8-swdt
-1;
621 if (y
< 0) y
= 0; else if (y
>= map
.height
*8-shgt
) y
= map
.height
*8-shgt
-1;
624 vportX0
= mofsx
/scale
;
625 vportY0
= mofsy
/scale
;
626 vportX1
= vportX0
+vlWidth
/scale
;
627 vportY1
= vportY0
+vlHeight
/scale
;
629 //glDisable(GL_DEPTH_TEST);
631 drawAtXY(texParts
, 0, 0);
632 // do liquid coloring
633 drawAtXY(map
.texgl
.ptr
[map
.LiquidMask
], 0, 0);
634 // foreground -- hide secrets, draw lifts and such
635 drawAtXY(map
.texgl
.ptr
[map
.Front
], 0, 0);
643 glClearColor(BackIntens
, BackIntens
, BackIntens
, 1.0f);
644 //glClearColor(0.15f, 0.15f, 0.15f, 1.0f);
645 ////glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
646 glClear(GL_COLOR_BUFFER_BIT
);
649 // texture 1 is background
650 glActiveTexture(GL_TEXTURE0
+1);
651 glBindTexture(GL_TEXTURE_2D
, fboOrigBack
.tex
.tid
);
652 // texture 2 is occluders
653 glActiveTexture(GL_TEXTURE0
+2);
654 glBindTexture(GL_TEXTURE_2D
, map
.texgl
.ptr
[map
.LightMask
].tid
);
655 // done texture assign
656 glActiveTexture(GL_TEXTURE0
+0);
660 renderLight( 27, 391-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 100);
661 renderLight(542, 424-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 100);
662 renderLight(377, 368-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 32);
663 renderLight(147, 288-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 64);
664 renderLight( 71, 200-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 128);
665 renderLight(249, 200-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 128);
666 renderLight(426, 200-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 128);
667 renderLight(624, 200-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 128);
668 renderLight(549, 298-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 64);
669 renderLight( 74, 304-0+LYOfs
, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 32);
671 renderLight(24*8+4, (24+18)*8-2+LYOfs
, SVec4F(0.6f, 0.0f, 0.0f, 1.0f), 128);
673 renderLight(280, 330, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 64);
676 foreach (ref li
; attachedLights
[0..attachedLightCount
]) {
678 renderLight(li
.x
, li
.y
, SVec4F(0.0f, 0.0f, 0.0f, li
.r
), li
.radius
);
680 renderLight(li
.x
, li
.y
, SVec4F(li
.r
, li
.g
, li
.b
, 1.0f), li
.radius
);
684 foreach (immutable _
; 0..1) {
685 renderLight(lightX
, lightY
, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 96);
688 glActiveTexture(GL_TEXTURE0
+1);
689 glBindTexture(GL_TEXTURE_2D
, 0);
690 glActiveTexture(GL_TEXTURE0
+2);
691 glBindTexture(GL_TEXTURE_2D
, 0);
692 glActiveTexture(GL_TEXTURE0
+0);
697 shadScanlines.exec((Shader shad) {
698 shad["scanlines"] = scanlines;
699 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
700 glClear(GL_COLOR_BUFFER_BIT);
701 orthoCamera(vlWidth, vlHeight);
702 //orthoCamera(map.width*8*scale, map.height*8*scale);
703 //glMatrixMode(GL_MODELVIEW);
705 //glTranslatef(0.375, 0.375, 0); // to be pixel-perfect
706 // somehow, FBO objects are mirrored; wtf?!
707 drawAtXY(fboLevel.tex.tid, -mapOfsX, -mapOfsY, map.width*8*scale, map.height*8*scale, mirrorY:true);
714 smDrawText(map.width*8/2, map.height*8/2, "Testing...");
719 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
724 //auto img = smfont.ptr[0x39];
727 //conwriteln("img.sx=", img.sx, "; img.sy=", img.sy, "; ", img.width, "x", img.height);
728 drawAtXY(img.tex, 10-img.sx, 10-img.sy);
733 orthoCamera(vlWidth
, vlHeight
);
734 auto tex
= (doLighting ? fboLevelLight
.tex
.tid
: fboOrigBack
.tex
.tid
);
737 drawAtXY(tex
, -mofsx
, -mofsy
, map
.width
*8*scale
, map
.height
*8*scale
, mirrorY
:true);
743 // ////////////////////////////////////////////////////////////////////////// //
744 // returns time slept
745 int sleepAtMaxMsecs (int msecs
) {
747 import core
.sys
.posix
.signal
: timespec
;
748 import core
.sys
.posix
.time
: nanosleep
;
749 timespec ts
= void, tpassed
= void;
751 ts
.tv_nsec
= msecs
*1000*1000+(500*1000); // milli to nano
752 nanosleep(&ts
, &tpassed
);
753 return (ts
.tv_nsec
-tpassed
.tv_nsec
)/(1000*1000);
760 // ////////////////////////////////////////////////////////////////////////// //
762 shared int diedie
= 0;
764 enum D2DFrameTime
= 55; // milliseconds
765 enum MinFrameTime
= 1000/60; // ~60 FPS
767 void renderThread () {
769 MonoTime curtime
= MonoTime
.currTime
;
771 lastthink
= curtime
; // for interpolator
772 nextthink
= curtime
+dur
!"msecs"(D2DFrameTime
);
773 MonoTime nextvframe
= curtime
;
775 enum MaxFPSFrames
= 16;
776 float frtimes
= 0.0f;
779 int hushFrames
= 6; // ignore first `hushFrames` frames overtime
780 MonoTime prevFrameStartTime
= curtime
;
782 bool vframeWasLost
= false;
784 // "D2D frames"; curtime should be set; return `true` if frame was processed; will fix `curtime`
785 bool doThinkFrame () {
786 if (curtime
>= nextthink
) {
788 while (nextthink
<= curtime
) nextthink
+= dur
!"msecs"(D2DFrameTime
);
789 // save snapshot and other data for interpolator
790 Actor
.saveSnapshot(prevFrameActorsData
[], prevFrameActorOfs
.ptr
);
791 mapViewPosX
[0] = mapViewPosX
[1];
792 mapViewPosY
[0] = mapViewPosY
[1];
797 auto tm
= MonoTime
.currTime
;
798 int thinkTime
= cast(int)((tm
-curtime
).total
!"msecs");
799 if (thinkTime
> 9) { import core
.stdc
.stdio
; printf("spent on thinking: %d msecs\n", thinkTime
); }
807 // "video frames"; curtime should be set; return `true` if frame was processed; will fix `curtime`
809 version(dont_use_vsync
) {
811 enum doCheckTime
= true;
814 __gshared
bool prevLost
= false;
815 bool doCheckTime
= vframeWasLost
;
818 { import core
.stdc
.stdio
; printf("frame was lost!\n"); }
826 if (curtime
< nextvframe
) return false;
827 version(dont_use_vsync
) {
828 if (curtime
> nextvframe
) {
829 auto overtime
= cast(int)((curtime
-nextvframe
).total
!"msecs");
830 if (overtime
> 2500) {
834 { import core
.stdc
.stdio
; printf(" spent whole %d msecs\n", overtime
); }
840 while (nextvframe
<= curtime
) nextvframe
+= dur
!"msecs"(MinFrameTime
);
844 scope(exit
) sdwindow
.mtUnlock();
845 ctset
= sdwindow
.setAsCurrentOpenGlContextNT
;
847 // if we can't set context, pretend that videoframe was processed; this should solve problem with vsync and invisible window
850 iLiquidTime
= cast(float)((curtime
-MonoTime
.zero
).total
!"msecs"%10000000)/18.0f*0.04f;
851 renderScene(curtime
);
853 scope(exit
) sdwindow
.mtUnlock();
854 sdwindow
.swapOpenGlBuffers();
856 sdwindow
.releaseCurrentOpenGlContext();
857 vframeWasLost
= false;
859 vframeWasLost
= true;
860 { import core
.stdc
.stdio
; printf("xframe was lost!\n"); }
862 curtime
= MonoTime
.currTime
;
867 if (sdwindow
.closed
) break;
868 if (atomicLoad(diedie
) > 0) break;
870 curtime
= MonoTime
.currTime
;
871 auto fstime
= curtime
;
873 doThinkFrame(); // this will fix curtime if necessary
875 if (!vframeWasLost
) {
877 auto frameTime
= cast(float)(curtime
-prevFrameStartTime
).total
!"msecs"/1000.0f;
878 prevFrameStartTime
= curtime
;
879 frtimes
+= frameTime
;
880 if (++framenum
>= MaxFPSFrames || frtimes
>= 3.0f) {
881 import std
.string
: format
;
882 int newFPS
= cast(int)(cast(float)MaxFPSFrames
/frtimes
+0.5);
883 if (newFPS
!= prevFPS
) {
884 sdwindow
.title
= "%s / FPS:%s".format("D2D", newFPS
);
893 curtime
= MonoTime
.currTime
;
895 // now sleep until next "video" or "think" frame
896 if (nextthink
> curtime
&& nextvframe
> curtime
) {
898 immutable nextVideoFrameSleep
= cast(int)((nextvframe
-curtime
).total
!"msecs");
899 immutable nextThinkFrameSleep
= cast(int)((nextthink
-curtime
).total
!"msecs");
900 immutable sleepTime
= (nextVideoFrameSleep
< nextThinkFrameSleep ? nextVideoFrameSleep
: nextThinkFrameSleep
);
901 sleepAtMaxMsecs(sleepTime
);
902 //curtime = MonoTime.currTime;
905 } catch (Exception e
) {
906 import core
.stdc
.stdio
;
907 fprintf(stderr
, "FUUUUUUUUUUUUUUUUUUUUUUUUUU\n");
909 if (sdwindow
.closed
) break;
910 if (atomicLoad(diedie
) > 0) break;
911 sleepAtMaxMsecs(100);
914 atomicStore(diedie
, 2);
918 // ////////////////////////////////////////////////////////////////////////// //
919 void closeWindow () {
920 if (atomicLoad(diedie
) != 2) {
921 atomicStore(diedie
, 1);
922 while (atomicLoad(diedie
) != 2) {}
924 if (!sdwindow
.closed
) {
931 // ////////////////////////////////////////////////////////////////////////// //
932 __gshared Thread renderTid
;
935 void main (string
[] args
) {
936 FuncPool
.dumpCode
= false;
937 FuncPool
.dumpCodeSize
= false;
938 dacsDumpSemantic
= false;
940 //version(rdmd) { dacsOptimize = 0; }
941 bool compileOnly
= false;
943 for (usize idx
= 1; idx
< args
.length
; ++idx
) {
945 if (args
[idx
] == "--dump-code") FuncPool
.dumpCode
= true;
946 else if (args
[idx
] == "--dump-code-size") FuncPool
.dumpCodeSize
= true;
947 else if (args
[idx
] == "--dump-semantic") dacsDumpSemantic
= true;
948 else if (args
[idx
] == "--dump-all") { FuncPool
.dumpCode
= true; FuncPool
.dumpCodeSize
= true; dacsDumpSemantic
= true; }
949 else if (args
[idx
] == "--compile") compileOnly
= true;
950 else if (args
[idx
] == "--compile-only") compileOnly
= true;
951 else if (args
[idx
] == "--messages") ++dacsMessages
;
952 else if (args
[idx
] == "--no-copro") dacsOptimizeNoCoPro
= true;
953 else if (args
[idx
] == "--no-deadass") dacsOptimizeNoDeadAss
= true;
954 else if (args
[idx
] == "--no-purekill") dacsOptimizeNoPureKill
= true;
955 else if (args
[idx
].length
> 2 && args
[idx
][0..2] == "-O") {
956 import std
.conv
: to
;
957 ubyte olevel
= to
!ubyte(args
[idx
][2..$]);
958 dacsOptimize
= olevel
;
962 foreach (immutable c
; idx
+1..args
.length
) args
.ptr
[c
-1] = args
.ptr
[c
];
968 static void setDP () {
972 import std
.file
: thisExePath
;
973 import std
.path
: dirName
;
974 setDataPath(thisExePath
.dirName
);
976 addPK3(getDataPath
~"base.pk3"); loadWadScripts();
977 //addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad"); loadWadScripts();
978 //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad"); loadWadScripts();
979 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad"); loadWadScripts();
980 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad"); loadWadScripts();
981 //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad"); loadWadScripts();
982 //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad"); loadWadScripts();
983 scriptLoadingComplete();
988 if (compileOnly
) return;
994 setOpenGLContextVersion(3, 2); // up to GLSL 150
995 //openGLContextCompatible = false;
997 map
= new LevelMap("maps/map01.d2m");
998 ugInit(map
.width
*8, map
.height
*8);
1004 map
.getThingPos(1/*ThingId.Player1*/, &mapOfsX
, &mapOfsY
);
1007 mapOfsX = (mapOfsX*2)-vlWidth/2;
1008 if (mapOfsX+vlWidth > map.width*16) mapOfsX = map.width*16-vlWidth;
1009 if (mapOfsX < 0) mapOfsX = 0;
1010 mapOfsY = (mapOfsY*2)-vlHeight/2;
1011 if (mapOfsY+vlHeight > map.height*16) mapOfsY = map.height*16-vlHeight;
1012 if (mapOfsY < 0) mapOfsY = 0;
1014 setMapViewPos(mapOfsX
, mapOfsY
);
1015 mapOfsX
= mapViewPosX
[1];
1016 mapOfsY
= mapViewPosY
[1];
1018 sdwindow
= new SimpleWindow(vlWidth
, vlHeight
, "D2D", OpenGlOptions
.yes
, Resizablity
.fixedSize
);
1020 sdwindow
.visibleForTheFirstTime
= delegate () {
1021 sdwindow
.setAsCurrentOpenGlContext(); // make this window active
1022 glbindLoadFunctions();
1025 import core.stdc.stdio;
1026 printf("GL version: %s\n", glGetString(GL_VERSION));
1028 glGetIntegerv(GL_MAJOR_VERSION, &h);
1029 glGetIntegerv(GL_MINOR_VERSION, &l);
1030 printf("version: %d.%d\n", h, l);
1031 printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
1033 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
1035 printf("%d shader versions supported:\n", svcount);
1036 foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n));
1040 glGetIntegerv(GL_NUM_EXTENSIONS, &ecount);
1042 printf("%d extensions supported:\n", ecount);
1043 foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n));
1048 // check if we have sufficient shader version here
1052 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS
, &svcount
);
1054 foreach (GLuint n
; 0..svcount
) {
1055 import core
.stdc
.string
: strncmp
;
1056 auto v
= glGetStringi(GL_SHADING_LANGUAGE_VERSION
, n
);
1057 if (v
is null) continue;
1058 if (strncmp(v
, "120", 3) != 0) continue;
1059 if (v
[3] > ' ') continue;
1064 if (!found
) assert(0, "can't find OpenGL GLSL 120");
1066 auto adr
= glGetProcAddress("glTexParameterf");
1067 if (adr
is null) assert(0);
1070 version(dont_use_vsync
) {
1071 sdwindow
.vsync
= false;
1073 sdwindow
.vsync
= true;
1075 //sdwindow.useGLFinish = false;
1077 //sdwindow.redrawOpenGlScene();
1078 if (!sdwindow
.releaseCurrentOpenGlContext()) { import core
.stdc
.stdio
; printf("can't release OpenGL context(1)\n"); }
1080 renderTid
= new Thread(&renderThread
);
1085 //sdwindow.redrawOpenGlScene = delegate () { renderScene(); };
1087 enum MSecsPerFrame
= 1000/30; /* 30 is FPS */
1089 uint[8] frameTimes
= 1000;
1090 enum { Left
, Right
, Up
, Down
}
1091 bool[4] pressed
= false;
1093 sdwindow
.eventLoop(MSecsPerFrame
,
1095 if (sdwindow
.closed
) return;
1096 if (pressed
[Left
]) mapOfsX
-= 8;
1097 if (pressed
[Right
]) mapOfsX
+= 8;
1098 if (pressed
[Up
]) mapOfsY
-= 8;
1099 if (pressed
[Down
]) mapOfsY
+= 8;
1100 import std
.math
: cos
, sin
;
1101 __gshared
float itime
= 0.0;
1104 mapOfsX
= cast(int)(800.0/2.0+cos(itime
)*220.0);
1105 mapOfsY
= cast(int)(800.0/2.0+120.0+sin(itime
)*160.0);
1107 if (scale
== 1) mapOfsX
= mapOfsY
= 0;
1108 //sdwindow.redrawOpenGlSceneNow();
1110 delegate (KeyEvent event
) {
1111 if (sdwindow
.closed
) return;
1112 if (event
.pressed
&& event
.key
== Key
.Escape
) { closeWindow(); return; }
1113 switch (event
.key
) {
1114 case Key
.X
: if (event
.pressed
) altMove
= !altMove
; break;
1115 case Key
.Left
: if (altMove
) pressed
[Left
] = event
.pressed
; break;
1116 case Key
.Right
: if (altMove
) pressed
[Right
] = event
.pressed
; break;
1117 case Key
.Up
: if (altMove
) pressed
[Up
] = event
.pressed
; break;
1118 case Key
.Down
: if (altMove
) pressed
[Down
] = event
.pressed
; break;
1122 switch (event
.key
) {
1123 case Key
.Left
: case Key
.Pad4
: plrKeyUpDown(0, PLK_LEFT
, event
.pressed
); break;
1124 case Key
.Right
: case Key
.Pad6
: plrKeyUpDown(0, PLK_RIGHT
, event
.pressed
); break;
1125 case Key
.Up
: case Key
.Pad8
: plrKeyUpDown(0, PLK_UP
, event
.pressed
); break;
1126 case Key
.Down
: case Key
.Pad2
: plrKeyUpDown(0, PLK_DOWN
, event
.pressed
); break;
1127 case Key
.Alt
: plrKeyUpDown(0, PLK_JUMP
, event
.pressed
); break;
1128 case Key
.Ctrl
: plrKeyUpDown(0, PLK_FIRE
, event
.pressed
); break;
1129 case Key
.Shift
: plrKeyUpDown(0, PLK_USE
, event
.pressed
); break;
1134 delegate (MouseEvent event
) {
1135 lightX
= event
.x
/scale
;
1136 lightY
= event
.y
/scale
;
1137 lightX
+= mapOfsX
/scale
;
1138 lightY
+= mapOfsY
/scale
;
1140 delegate (dchar ch
) {
1141 if (ch
== 'q') { closeWindow(); return; }
1142 if (ch
== 's') scanlines
= !scanlines
;
1143 if (ch
== '1') scale
= 1;
1144 if (ch
== '2') scale
= 2;
1145 if (ch
== 'D') cheatNoDoors
= !cheatNoDoors
;
1147 frameInterpolation
= !frameInterpolation
;
1148 if (frameInterpolation
) addMessage("Interpolation: ON"); else addMessage("Interpolation: OFF");
1151 doLighting
= !doLighting
;
1152 if (doLighting
) addMessage("Lighting: ON"); else addMessage("Lighting: OFF");
1154 if (ch
== ' ') movement
= !movement
;
1157 } catch (Exception e
) {
1158 import std
.stdio
: stderr
;
1159 stderr
.writeln("FUUUUUUUUUUUU\n", e
.toString
);