From 26f7b0422224dfbb08420ef7c79b53cd30f4f469 Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Mon, 28 Mar 2016 16:23:31 +0300 Subject: [PATCH] rendering thread moved to it's own module; no more gshared hacks, all communications are thru messages now --- d2dmap.d | 2 + dengapi.d | 34 +- xmain_d2d.d => render.d | 692 +++++++++++----------- xmain_d2d.d | 1500 ++++++++++------------------------------------- 4 files changed, 676 insertions(+), 1552 deletions(-) copy xmain_d2d.d => render.d (76%) rewrite xmain_d2d.d (77%) diff --git a/d2dmap.d b/d2dmap.d index 7dd26a3..665d458 100644 --- a/d2dmap.d +++ b/d2dmap.d @@ -186,6 +186,8 @@ public: MapThing[] things; MapSwitch[] switches; + this () {} + this (string fname) { load(fname); } this (File fl) { load(fl); } diff --git a/dengapi.d b/dengapi.d index c5d482b..71ae951 100644 --- a/dengapi.d +++ b/dengapi.d @@ -33,6 +33,7 @@ import d2dgfx; import dacs; import d2dunigrid; +import render; // ////////////////////////////////////////////////////////////////////////// // @@ -359,7 +360,7 @@ void setupDAPI () { auto p = map.tiles.ptr[LevelMap.Type].ptr+y*map.width+x; if (*p != tid) { *p = cast(ubyte)tid; - import xmain_d2d : mapDirty; + import render : mapDirty; mapDirty((1<. */ -module xmain_d2d is aliced; +module render is aliced; //version = dont_use_vsync; //version = old_light; @@ -25,6 +25,8 @@ import core.atomic; import core.thread; import core.time; +import std.concurrency; + import iv.glbinds; import glutils; import console; @@ -57,12 +59,18 @@ public __gshared bool cheatNoDoors = false; // ////////////////////////////////////////////////////////////////////////// // -__gshared SimpleWindow sdwindow; +public __gshared SimpleWindow sdwindow; public enum vlWidth = 800; public enum vlHeight = 800; -public __gshared int scale = 1; +__gshared int scale = 2; + +public int getScale () nothrow @trusted @nogc { pragma(inline, true); return scale; } + + +// ////////////////////////////////////////////////////////////////////////// // +__gshared bool levelLoaded = false; // ////////////////////////////////////////////////////////////////////////// // @@ -128,7 +136,8 @@ __gshared Shader shadLiquidDistort; // ////////////////////////////////////////////////////////////////////////// // -void initOpenGL () { +// call once! +public void initOpenGL () { gloStackClear(); glEnable(GL_TEXTURE_2D); @@ -180,12 +189,28 @@ void initOpenGL () { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + loadSmFont(); + loadAllMonsterGraphics(); +} + + +// ////////////////////////////////////////////////////////////////////////// // +// should be called when OpenGL is initialized +void loadMap (string mapname) { + if (map !is null) map.clear(); + map = new LevelMap(mapname); + + ugInit(map.width*8, map.height*8); + map.oglBuildMega(); mapTilesChanged = 0; + if (fboLevel !is null) fboLevel.clear(); + if (fboLevelLight !is null) fboLevelLight.clear(); + if (fboOrigBack !is null) fboOrigBack.clear(); + fboLevel = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // final level render will be here fboLevelLight = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // level lights will be rendered here - //fboForeground = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // level foreground fboOrigBack = new FBO(map.width*8, map.height*8, Texture.Option.Nearest, Texture.Option.Depth); // background+foreground shadToPolar.exec((Shader shad) { shad["mapPixSize"] = SVec2F(map.width*8-1, map.height*8-1); }); @@ -196,8 +221,6 @@ void initOpenGL () { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); orthoCamera(vlWidth, vlHeight); - loadSmFont(); - Actor.resetStorage(); loadMapMonsters(); @@ -210,6 +233,8 @@ void initOpenGL () { mapViewPosX[0] = mapViewPosX[1]; mapViewPosY[0] = mapViewPosY[1]; + levelLoaded = true; + { import core.memory : GC; GC.collect(); } } @@ -219,8 +244,10 @@ void initOpenGL () { __gshared uint mapTilesChanged = 0; +//WARNING! this can be called only from DACS, so we don't have to sync it! public void mapDirty (uint layermask) { mapTilesChanged |= layermask; } + void rebuildMapMegaTextures () { //fbo.replaceTexture //mapTilesChanged = false; @@ -231,86 +258,6 @@ void rebuildMapMegaTextures () { // ////////////////////////////////////////////////////////////////////////// // -__gshared int vportX0, vportY0, vportX1, vportY1; - - -void renderLight() (int lightX, int lightY, in auto ref SVec4F lcol, int lightRadius) { - if (lightRadius < 2) return; - if (lightRadius > MaxLightRadius) lightRadius = MaxLightRadius; - int lightSize = lightRadius*2; - // is this light visible? - if (lightX <= -lightRadius || lightY <= -lightRadius || lightX-lightRadius >= map.width*8 || lightY-lightRadius >= map.height*8) return; - - // out of viewport -- do nothing - if (lightX+lightRadius < vportX0 || lightY+lightRadius < vportY0) return; - if (lightX-lightRadius > vportX1 || lightY-lightRadius > vportY1) return; - - if (lightX >= 0 && lightY >= 0 && lightX < map.width*8 && lightY < map.height*8 && - map.teximgs[map.LightMask].imageData.colors.ptr[lightY*(map.width*8)+lightX].a > 190) return; - - //glUseProgram(0); - glDisable(GL_BLEND); - - // draw shadow casters to fboOccludersId, light should be in the center - version(old_light) { - fboOccluders[lightRadius].exec({ - glColor4f(0.0f, 0.0f, 0.0f, 1.0f); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - orthoCamera(lightSize, lightSize); - drawAtXY(map.texgl.ptr[map.LightMask], lightRadius-lightX, lightRadius-lightY); - }); - } - // common color for all the following - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - // build 1d distance map to fboShadowMapId - fboDistMap[lightRadius].exec({ - shadToPolar.exec((Shader shad) { - shad["lightTexSize"] = SVec2F(lightSize, lightSize); - shad["lightPos"] = SVec2F(lightX, lightY); - // no need to clear it, shader will take care of that - orthoCamera(lightSize, 1); - version(old_light) { - drawAtXY(fboOccluders[lightRadius].tex.tid, 0, 0, lightSize, 1); - } else { - // it doesn't matter what we will draw here, so just draw filled rect - glRectf(0, 0, lightSize, 1); - } - }); - }); - - // build light texture for blending - fboOccluders[lightRadius].exec({ - // no need to clear it, shader will take care of that - shadBlur.exec((Shader shad) { - shad["lightTexSize"] = SVec2F(lightSize, lightSize); - shad["lightColor"] = SVec4F(lcol.x, lcol.y, lcol.z, lcol.w); - shad["lightPos"] = SVec2F(lightX, lightY); - orthoCamera(lightSize, lightSize); - drawAtXY(fboDistMap[lightRadius].tex.tid, 0, 0, lightSize, lightSize); - }); - }); - - // blend light texture - fboLevelLight.exec({ - glEnable(GL_BLEND); - //glDisable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - orthoCamera(map.width*8, map.height*8); - drawAtXY(fboOccluders[lightRadius].tex, lightX-lightRadius, lightY-lightRadius, mirrorY:true); - // and blend it again, with the shader that will touch only occluders - shadBlurOcc.exec((Shader shad) { - shad["lightTexSize"] = SVec2F(lightSize, lightSize); - shad["lightColor"] = SVec4F(lcol.x, lcol.y, lcol.z, lcol.w); - shad["lightPos"] = SVec2F(lightX, lightY); - drawAtXY(fboOccluders[lightRadius].tex.tid, lightX-lightRadius, lightY-lightRadius, lightSize, lightSize, mirrorY:true); - }); - }); -} - - -// ////////////////////////////////////////////////////////////////////////// // // messages struct Message { enum Phase { FadeIn, Stay, FadeOut } @@ -326,14 +273,14 @@ private import core.sync.mutex : Mutex; __gshared Message[128] messages; __gshared uint messagesUsed = 0; -__gshared Mutex messageLock; -shared static this () { messageLock = new Mutex(); } +//__gshared Mutex messageLock; +//shared static this () { messageLock = new Mutex(); } -public void addMessage (const(char)[] msgtext, int pauseMsecs=3000, bool noreplace=false) { - messageLock.lock(); - scope(exit) messageLock.unlock(); +void addMessage (const(char)[] msgtext, int pauseMsecs=3000, bool noreplace=false) { + //messageLock.lock(); + //scope(exit) messageLock.unlock(); if (msgtext.length == 0) return; conwriteln(msgtext); if (pauseMsecs <= 50) return; @@ -373,8 +320,8 @@ public void addMessage (const(char)[] msgtext, int pauseMsecs=3000, bool norepla void doMessages (MonoTime curtime) { - messageLock.lock(); - scope(exit) messageLock.unlock(); + //messageLock.lock(); + //scope(exit) messageLock.unlock(); if (messagesUsed == 0) return; glEnable(GL_BLEND); @@ -445,11 +392,91 @@ mixin(Actor.FieldGetPtrMixin!("y", int)); // ////////////////////////////////////////////////////////////////////////// // -__gshared int lightX = vlWidth/2, lightY = vlHeight/2; -__gshared int mapOfsX, mapOfsY; -__gshared bool movement = false; +__gshared int vportX0, vportY0, vportX1, vportY1; + + +void renderLight() (int lightX, int lightY, in auto ref SVec4F lcol, int lightRadius) { + if (lightRadius < 2) return; + if (lightRadius > MaxLightRadius) lightRadius = MaxLightRadius; + int lightSize = lightRadius*2; + // is this light visible? + if (lightX <= -lightRadius || lightY <= -lightRadius || lightX-lightRadius >= map.width*8 || lightY-lightRadius >= map.height*8) return; + + // out of viewport -- do nothing + if (lightX+lightRadius < vportX0 || lightY+lightRadius < vportY0) return; + if (lightX-lightRadius > vportX1 || lightY-lightRadius > vportY1) return; + + if (lightX >= 0 && lightY >= 0 && lightX < map.width*8 && lightY < map.height*8 && + map.teximgs[map.LightMask].imageData.colors.ptr[lightY*(map.width*8)+lightX].a > 190) return; + + //glUseProgram(0); + glDisable(GL_BLEND); + + // draw shadow casters to fboOccludersId, light should be in the center + version(old_light) { + fboOccluders[lightRadius].exec({ + glColor4f(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + orthoCamera(lightSize, lightSize); + drawAtXY(map.texgl.ptr[map.LightMask], lightRadius-lightX, lightRadius-lightY); + }); + } + // common color for all the following + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + // build 1d distance map to fboShadowMapId + fboDistMap[lightRadius].exec({ + shadToPolar.exec((Shader shad) { + shad["lightTexSize"] = SVec2F(lightSize, lightSize); + shad["lightPos"] = SVec2F(lightX, lightY); + // no need to clear it, shader will take care of that + orthoCamera(lightSize, 1); + version(old_light) { + drawAtXY(fboOccluders[lightRadius].tex.tid, 0, 0, lightSize, 1); + } else { + // it doesn't matter what we will draw here, so just draw filled rect + glRectf(0, 0, lightSize, 1); + } + }); + }); + + // build light texture for blending + fboOccluders[lightRadius].exec({ + // no need to clear it, shader will take care of that + shadBlur.exec((Shader shad) { + shad["lightTexSize"] = SVec2F(lightSize, lightSize); + shad["lightColor"] = SVec4F(lcol.x, lcol.y, lcol.z, lcol.w); + shad["lightPos"] = SVec2F(lightX, lightY); + orthoCamera(lightSize, lightSize); + drawAtXY(fboDistMap[lightRadius].tex.tid, 0, 0, lightSize, lightSize); + }); + }); + + // blend light texture + fboLevelLight.exec({ + glEnable(GL_BLEND); + //glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + orthoCamera(map.width*8, map.height*8); + drawAtXY(fboOccluders[lightRadius].tex, lightX-lightRadius, lightY-lightRadius, mirrorY:true); + // and blend it again, with the shader that will touch only occluders + shadBlurOcc.exec((Shader shad) { + shad["lightTexSize"] = SVec2F(lightSize, lightSize); + shad["lightColor"] = SVec4F(lcol.x, lcol.y, lcol.z, lcol.w); + shad["lightPos"] = SVec2F(lightX, lightY); + drawAtXY(fboOccluders[lightRadius].tex.tid, lightX-lightRadius, lightY-lightRadius, lightSize, lightSize, mirrorY:true); + }); + }); +} + + +// ////////////////////////////////////////////////////////////////////////// // +__gshared int testLightX = vlWidth/2, testLightY = vlHeight/2; +//__gshared int mapOfsX, mapOfsY; +//__gshared bool movement = false; __gshared float iLiquidTime = 0.0; -__gshared bool altMove = false; +//__gshared bool altMove = false; void renderScene (MonoTime curtime) { @@ -470,9 +497,11 @@ void renderScene (MonoTime curtime) { int mofsx, mofsy; bool camCenter = true; - if (altMove || movement || scale == 1) { + if (/*altMove || movement ||*/ scale == 1) { + /* mofsx = mapOfsX; mofsy = mapOfsY; + */ vportX0 = 0; vportY0 = 0; vportX1 = map.width*8; @@ -708,8 +737,12 @@ void renderScene (MonoTime curtime) { } } - foreach (immutable _; 0..1) { - renderLight(lightX, lightY, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 96); + { + int tlx = testLightX/scale+mofsx/scale; + int tly = testLightY/scale+mofsx/scale; + foreach (immutable _; 0..1) { + renderLight(tlx, tly, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 96); + } } glActiveTexture(GL_TEXTURE0+1); @@ -800,7 +833,8 @@ shared int diedie = 0; enum D2DFrameTime = 55; // milliseconds enum MinFrameTime = 1000/60; // ~60 FPS -void renderThread () { +public void renderThread (Tid starterTid) { + send(starterTid, 42); try { MonoTime curtime = MonoTime.currTime; @@ -817,18 +851,103 @@ void renderThread () { bool vframeWasLost = false; + void receiveMessages () { + for (;;) { + import core.time : Duration; + //conwriteln("rendering thread: waiting for messages..."); + auto got = receiveTimeout( + Duration.zero, // don't wait + (TMsgMessage msg) { + addMessage(msg.text[0..msg.textlen], msg.pauseMsecs, msg.noreplace); + }, + (TMsgLoadLevel msg) { + if (levelLoaded) { + conwriteln("ERROR: can't load new levels yet"); + return; + } + auto mn = msg.mapfile[0..msg.textlen].idup; + if (mn.length == 0) { + conwriteln("ERROR: can't load empty level!"); + } + conwriteln("loading map '", mn, "'"); + loadMap(mn); + }, + (TMsgIntOption msg) { + final switch (msg.type) with (TMsgIntOption.Type) { + case Scale: + if (msg.value >= 1 && msg.value <= 2) scale = msg.value; + break; + } + }, + (TMsgBoolOption msg) { + final switch (msg.type) with (TMsgBoolOption.Type) { + case Interpolation: + if (msg.toggle) msg.value = !frameInterpolation; + if (frameInterpolation != msg.value) { + frameInterpolation = msg.value; + } else { + msg.showMessage = false; + } + break; + case Lighting: + if (msg.toggle) msg.value = !doLighting; + if (doLighting != msg.value) { + doLighting = msg.value; + } else { + msg.showMessage = false; + } + break; + case CheatNoDoors: + if (msg.toggle) msg.value = !cheatNoDoors; + if (cheatNoDoors != msg.value) { + cheatNoDoors = msg.value; + } else { + msg.showMessage = false; + } + break; + } + if (msg.showMessage) { + char[128] mbuf; + uint msgpos; + void putStr(T) (T s) if (is(T : const(char)[])) { + foreach (char ch; s) { + if (msgpos >= mbuf.length) break; + mbuf.ptr[msgpos++] = ch; + } + } + putStr("Option \""); + { import std.conv : to; putStr(to!string(msg.type)); } + putStr("\": O"); + if (msg.value) putStr("N"); else putStr("FF"); + addMessage(mbuf[0..msgpos]); + } + }, + (Variant v) { + conwriteln("WARNING: unknown thread message received and ignored"); + }, + ); + if (!got) { + // no more messages + //conwriteln("rendering thread: no more messages"); + break; + } + } + } + // "D2D frames"; curtime should be set; return `true` if frame was processed; will fix `curtime` bool doThinkFrame () { if (curtime >= nextthink) { lastthink = curtime; while (nextthink <= curtime) nextthink += dur!"msecs"(D2DFrameTime); - // save snapshot and other data for interpolator - Actor.saveSnapshot(prevFrameActorsData[], prevFrameActorOfs.ptr); - mapViewPosX[0] = mapViewPosX[1]; - mapViewPosY[0] = mapViewPosY[1]; - // process actors - doActorsThink(); - dotThink(); + if (levelLoaded) { + // save snapshot and other data for interpolator + Actor.saveSnapshot(prevFrameActorsData[], prevFrameActorOfs.ptr); + mapViewPosX[0] = mapViewPosX[1]; + mapViewPosY[0] = mapViewPosY[1]; + // process actors + doActorsThink(); + dotThink(); + } // some timing auto tm = MonoTime.currTime; int thinkTime = cast(int)((tm-curtime).total!"msecs"); @@ -884,7 +1003,12 @@ void renderThread () { if (ctset) { // render scene iLiquidTime = cast(float)((curtime-MonoTime.zero).total!"msecs"%10000000)/18.0f*0.04f; - renderScene(curtime); + receiveMessages(); // here, 'cause we need active OpenGL context for some messages + if (levelLoaded) { + renderScene(curtime); + } else { + //renderLoading(curtime); + } sdwindow.mtLock(); scope(exit) sdwindow.mtUnlock(); sdwindow.swapOpenGlBuffers(); @@ -952,7 +1076,28 @@ void renderThread () { // ////////////////////////////////////////////////////////////////////////// // -void closeWindow () { +__gshared Tid renderTid; +shared bool renderThreadStarted = false; + + +public void startRenderThread () { + if (!cas(&renderThreadStarted, false, true)) { + assert(0, "render thread already started!"); + } + renderTid = spawn(&renderThread, thisTid); + setMaxMailboxSize(renderTid, 1024, OnCrowding.throwException); //FIXME + // wait for "i'm ready" signal + receive( + (int ok) { + if (ok != 42) assert(0, "wtf?!"); + }, + ); + conwriteln("rendering thread started"); +} + + +// ////////////////////////////////////////////////////////////////////////// // +public void closeWindow () { if (atomicLoad(diedie) != 2) { atomicStore(diedie, 1); while (atomicLoad(diedie) != 2) {} @@ -965,240 +1110,121 @@ void closeWindow () { // ////////////////////////////////////////////////////////////////////////// // -__gshared Thread renderTid; - - -void main (string[] args) { - FuncPool.dumpCode = false; - FuncPool.dumpCodeSize = false; - dacsDumpSemantic = false; - dacsOptimize = 9; - //version(rdmd) { dacsOptimize = 0; } - bool compileOnly = false; - bool doVBL = true; - - for (usize idx = 1; idx < args.length; ++idx) { - bool remove = true; - if (args[idx] == "--dump-code") FuncPool.dumpCode = true; - else if (args[idx] == "--dump-code-size") FuncPool.dumpCodeSize = true; - else if (args[idx] == "--dump-semantic") dacsDumpSemantic = true; - else if (args[idx] == "--dump-all") { FuncPool.dumpCode = true; FuncPool.dumpCodeSize = true; dacsDumpSemantic = true; } - else if (args[idx] == "--compile") compileOnly = true; - else if (args[idx] == "--compile-only") compileOnly = true; - else if (args[idx] == "--messages") ++dacsMessages; - else if (args[idx] == "--no-copro") dacsOptimizeNoCoPro = true; - else if (args[idx] == "--no-deadass") dacsOptimizeNoDeadAss = true; - else if (args[idx] == "--no-purekill") dacsOptimizeNoPureKill = true; - else if (args[idx] == "--no-vsync") doVBL = false; - else if (args[idx] == "--vsync") doVBL = true; - else if (args[idx].length > 2 && args[idx][0..2] == "-O") { - import std.conv : to; - ubyte olevel = to!ubyte(args[idx][2..$]); - dacsOptimize = olevel; - } - else remove = false; - if (remove) { - foreach (immutable c; idx+1..args.length) args.ptr[c-1] = args.ptr[c]; - args.length -= 1; - --idx; //hack - } - } +// thread messages - addInternalActorFields(); - static void setDP () { - version(rdmd) { - setDataPath("data"); - } else { - import std.file : thisExePath; - import std.path : dirName; - setDataPath(thisExePath.dirName); - } - addPK3(getDataPath~"base.pk3"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad"); loadWadScripts(); - scriptLoadingComplete(); - } +// ////////////////////////////////////////////////////////////////////////// // +struct TMsgTestLightMove { + int x, y; +} - setDP(); +public void postTestLightMove (int x, int y) { + if (!atomicLoad(renderThreadStarted)) return; + auto msg = TMsgTestLightMove(x, y); + send(renderTid, msg); +} - if (compileOnly) return; - try { - registerAPI(); - loadPalette(); +// ////////////////////////////////////////////////////////////////////////// // +struct TMsgMessage { + char[256] text; + uint textlen; + int pauseMsecs; + bool noreplace; +} - setOpenGLContextVersion(3, 2); // up to GLSL 150 - //openGLContextCompatible = false; +public void postAddMessage (const(char)[] msgtext, int pauseMsecs=3000, bool noreplace=false) { + if (!atomicLoad(renderThreadStarted)) return; + if (msgtext.length > TMsgMessage.text.length) msgtext = msgtext[0..TMsgMessage.text.length]; + TMsgMessage msg; + msg.textlen = cast(uint)msgtext.length; + if (msg.textlen) msg.text[0..msg.textlen] = msgtext[0..msg.textlen]; + msg.pauseMsecs = pauseMsecs; + msg.noreplace = noreplace; + send(renderTid, msg); +} - map = new LevelMap("maps/map01.d2m"); - ugInit(map.width*8, map.height*8); - scale = 2; +// ////////////////////////////////////////////////////////////////////////// // +struct TMsgLoadLevel { + char[1024] mapfile; + uint textlen; +} - //mapOfsX = 8*26; - //mapOfsY = 8*56; - map.getThingPos(1/*ThingId.Player1*/, &mapOfsX, &mapOfsY); - // fix viewport - /* - mapOfsX = (mapOfsX*2)-vlWidth/2; - if (mapOfsX+vlWidth > map.width*16) mapOfsX = map.width*16-vlWidth; - if (mapOfsX < 0) mapOfsX = 0; - mapOfsY = (mapOfsY*2)-vlHeight/2; - if (mapOfsY+vlHeight > map.height*16) mapOfsY = map.height*16-vlHeight; - if (mapOfsY < 0) mapOfsY = 0; - */ - setMapViewPos(mapOfsX, mapOfsY); - mapOfsX = mapViewPosX[1]; - mapOfsY = mapViewPosY[1]; +public void postLoadLevel (const(char)[] mapfile) { + if (!atomicLoad(renderThreadStarted)) return; + if (mapfile.length > TMsgLoadLevel.mapfile.length) mapfile = mapfile[0..TMsgLoadLevel.mapfile.length]; + TMsgLoadLevel msg; + msg.textlen = cast(uint)mapfile.length; + if (msg.textlen) msg.mapfile[0..msg.textlen] = mapfile[0..msg.textlen]; + send(renderTid, msg); +} - sdwindow = new SimpleWindow(vlWidth, vlHeight, "D2D", OpenGlOptions.yes, Resizablity.fixedSize); - sdwindow.visibleForTheFirstTime = delegate () { - sdwindow.setAsCurrentOpenGlContext(); // make this window active - glbindLoadFunctions(); - /+ - { - import core.stdc.stdio; - printf("GL version: %s\n", glGetString(GL_VERSION)); - GLint l, h; - glGetIntegerv(GL_MAJOR_VERSION, &h); - glGetIntegerv(GL_MINOR_VERSION, &l); - printf("version: %d.%d\n", h, l); - printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); - GLint svcount; - glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount); - if (svcount > 0) { - printf("%d shader versions supported:\n", svcount); - foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n)); - } - /* - GLint ecount; - glGetIntegerv(GL_NUM_EXTENSIONS, &ecount); - if (ecount > 0) { - printf("%d extensions supported:\n", ecount); - foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n)); - } - */ - } - +/ - // check if we have sufficient shader version here - { - bool found = false; - GLint svcount; - glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount); - if (svcount > 0) { - foreach (GLuint n; 0..svcount) { - import core.stdc.string : strncmp; - auto v = glGetStringi(GL_SHADING_LANGUAGE_VERSION, n); - if (v is null) continue; - if (strncmp(v, "120", 3) != 0) continue; - if (v[3] > ' ') continue; - found = true; - break; - } - } - if (!found) assert(0, "can't find OpenGL GLSL 120"); - { - auto adr = glGetProcAddress("glTexParameterf"); - if (adr is null) assert(0); - } - } - version(dont_use_vsync) { - sdwindow.vsync = false; - } else { - sdwindow.vsync = true; +// ////////////////////////////////////////////////////////////////////////// // +struct TMsgIntOption { + enum Type { + Scale, + } + Type type; + int value; + bool showMessage; +} + + +struct TMsgBoolOption { + enum Type { + Interpolation, + Lighting, + CheatNoDoors, + } + Type type; + bool value; + bool toggle; + bool showMessage; +} + + +bool strCaseEqu (const(char)[] s0, const(char)[] s1) { + if (s0.length != s1.length) return false; + foreach (immutable idx, char ch; s0) { + if (ch >= 'A' && ch <= 'Z') ch += 32; + char c1 = s1.ptr[idx]; + if (c1 >= 'A' && c1 <= 'Z') c1 += 32; + if (ch != c1) return false; + } + return true; +} + + +public bool postSetOption(T, ST) (ST name, T value, bool showMessage=false, bool toggle=false) if (is(ST : const(char)[]) && (is(T == int) || is(T == bool))) { + if (!atomicLoad(renderThreadStarted)) return false; + if (name.length == 0 || name.length > 127) return false; + static if (is(T == int)) { + TMsgIntOption msg; + foreach (string oname; __traits(allMembers, TMsgIntOption.Type)) { + if (strCaseEqu(oname, name)) { + msg.type = __traits(getMember, TMsgIntOption.Type, oname); + msg.value = value; + msg.showMessage = showMessage; + send(renderTid, msg); + return true; } - //sdwindow.useGLFinish = false; - initOpenGL(); - //sdwindow.redrawOpenGlScene(); - if (!sdwindow.releaseCurrentOpenGlContext()) { import core.stdc.stdio; printf("can't release OpenGL context(1)\n"); } - if (!renderTid) { - renderTid = new Thread(&renderThread); - renderTid.start(); + } + } else static if (is(T == bool)) { + TMsgBoolOption msg; + foreach (string oname; __traits(allMembers, TMsgBoolOption.Type)) { + if (strCaseEqu(oname, name)) { + msg.type = __traits(getMember, TMsgBoolOption.Type, oname); + msg.value = value; + msg.toggle = toggle; + msg.showMessage = showMessage; + send(renderTid, msg); + return true; } - }; - - //sdwindow.redrawOpenGlScene = delegate () { renderScene(); }; - - enum MSecsPerFrame = 1000/30; /* 30 is FPS */ - - uint[8] frameTimes = 1000; - enum { Left, Right, Up, Down } - bool[4] pressed = false; - - sdwindow.eventLoop(MSecsPerFrame, - delegate () { - if (sdwindow.closed) return; - if (pressed[Left]) mapOfsX -= 8; - if (pressed[Right]) mapOfsX += 8; - if (pressed[Up]) mapOfsY -= 8; - if (pressed[Down]) mapOfsY += 8; - import std.math : cos, sin; - __gshared float itime = 0.0; - itime += 0.02; - if (movement) { - mapOfsX = cast(int)(800.0/2.0+cos(itime)*220.0); - mapOfsY = cast(int)(800.0/2.0+120.0+sin(itime)*160.0); - } - if (scale == 1) mapOfsX = mapOfsY = 0; - //sdwindow.redrawOpenGlSceneNow(); - }, - delegate (KeyEvent event) { - if (sdwindow.closed) return; - if (event.pressed && event.key == Key.Escape) { closeWindow(); return; } - switch (event.key) { - case Key.X: if (event.pressed) altMove = !altMove; break; - case Key.Left: if (altMove) pressed[Left] = event.pressed; break; - case Key.Right: if (altMove) pressed[Right] = event.pressed; break; - case Key.Up: if (altMove) pressed[Up] = event.pressed; break; - case Key.Down: if (altMove) pressed[Down] = event.pressed; break; - default: - } - if (!altMove) { - switch (event.key) { - case Key.Left: case Key.Pad4: plrKeyUpDown(0, PLK_LEFT, event.pressed); break; - case Key.Right: case Key.Pad6: plrKeyUpDown(0, PLK_RIGHT, event.pressed); break; - case Key.Up: case Key.Pad8: plrKeyUpDown(0, PLK_UP, event.pressed); break; - case Key.Down: case Key.Pad2: plrKeyUpDown(0, PLK_DOWN, event.pressed); break; - case Key.Alt: plrKeyUpDown(0, PLK_JUMP, event.pressed); break; - case Key.Ctrl: plrKeyUpDown(0, PLK_FIRE, event.pressed); break; - case Key.Shift: plrKeyUpDown(0, PLK_USE, event.pressed); break; - default: - } - } - }, - delegate (MouseEvent event) { - lightX = event.x/scale; - lightY = event.y/scale; - lightX += mapOfsX/scale; - lightY += mapOfsY/scale; - }, - delegate (dchar ch) { - if (ch == 'q') { closeWindow(); return; } - if (ch == 's') scanlines = !scanlines; - if (ch == '1') scale = 1; - if (ch == '2') scale = 2; - if (ch == 'D') cheatNoDoors = !cheatNoDoors; - if (ch == 'i') { - frameInterpolation = !frameInterpolation; - if (frameInterpolation) addMessage("Interpolation: ON"); else addMessage("Interpolation: OFF"); - } - if (ch == 'l') { - doLighting = !doLighting; - if (doLighting) addMessage("Lighting: ON"); else addMessage("Lighting: OFF"); - } - if (ch == ' ') movement = !movement; - }, - ); - } catch (Exception e) { - import std.stdio : stderr; - stderr.writeln("FUUUUUUUUUUUU\n", e.toString); + } + } else { + static assert(0, "invalid option type '"~T.stringof~"'"); } - closeWindow(); - flushGui(); + return false; } diff --git a/xmain_d2d.d b/xmain_d2d.d dissimilarity index 77% index c6fe30a..02726a0 100644 --- a/xmain_d2d.d +++ b/xmain_d2d.d @@ -1,1204 +1,296 @@ -/* DooM2D: Midnight on the Firing Line - * coded by Ketmar // Invisible Vector - * Understanding is not required. Only obedience. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -module xmain_d2d is aliced; - -//version = dont_use_vsync; -//version = old_light; - -private: -import core.atomic; -import core.thread; -import core.time; - -import iv.glbinds; -import glutils; -import console; -import wadarc; - -import iv.stream; - -import d2dmap; -import d2dadefs; -//import d2dactors; -import d2dgfx; -import d2dfont; -import dacs; - -import d2dunigrid; - -// `map` is there -import dengapi; - -import d2dparts; - - -// ////////////////////////////////////////////////////////////////////////// // -import arsd.color; -import arsd.png; - - -// ////////////////////////////////////////////////////////////////////////// // -public __gshared bool cheatNoDoors = false; - - -// ////////////////////////////////////////////////////////////////////////// // -__gshared SimpleWindow sdwindow; - - -public enum vlWidth = 800; -public enum vlHeight = 800; -public __gshared int scale = 1; - - -// ////////////////////////////////////////////////////////////////////////// // -__gshared bool scanlines = false; -__gshared bool doLighting = true; - - -// ////////////////////////////////////////////////////////////////////////// // -// interpolation -__gshared ubyte[] prevFrameActorsData; -__gshared uint[65536] prevFrameActorOfs; // uint.max-1: dead; uint.max: last -__gshared MonoTime lastthink = MonoTime.zero; // for interpolator -__gshared MonoTime nextthink = MonoTime.zero; -__gshared bool frameInterpolation = true; - - -__gshared int[2] mapViewPosX, mapViewPosY; // [0]: previous frame -- for interpolator - - -// this should be screen center -public void setMapViewPos (int x, int y) { - if (map is null) { - mapViewPosX[] = 0; - mapViewPosY[] = 0; - return; - } - int swdt = vlWidth/scale; - int shgt = vlHeight/scale; - x -= swdt/2; - y -= shgt/2; - if (x < 0) x = 0; else if (x >= map.width*8-swdt) x = map.width*8-swdt-1; - if (y < 0) y = 0; else if (y >= map.height*8-shgt) y = map.height*8-shgt-1; - mapViewPosX[1] = x*scale; - mapViewPosY[1] = y*scale; -} - - -// ////////////////////////////////////////////////////////////////////////// // -// attached lights -struct AttachedLightInfo { - int x, y; - float r, g, b; - bool uncolored; - int radius; -} - -__gshared AttachedLightInfo[65536] attachedLights; -__gshared uint attachedLightCount = 0; - - -// ////////////////////////////////////////////////////////////////////////// // -enum MaxLightRadius = 512; - - -// ////////////////////////////////////////////////////////////////////////// // -// for light -__gshared FBO[MaxLightRadius+1] fboOccluders, fboDistMap; -__gshared Shader shadToPolar, shadBlur, shadBlurOcc; - -__gshared FBO fboLevel, fboLevelLight, fboOrigBack; -__gshared Shader shadScanlines; -__gshared Shader shadLiquidDistort; - - -// ////////////////////////////////////////////////////////////////////////// // -void initOpenGL () { - gloStackClear(); - - glEnable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); - glDisable(GL_DITHER); - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - - // create shaders - shadScanlines = new Shader("scanlines", loadTextFile("data/shaders/srscanlines.frag")); - - shadLiquidDistort = new Shader("liquid_distort", loadTextFile("data/shaders/srliquid_distort.frag")); - shadLiquidDistort.exec((Shader shad) { - shad["texLqMap"] = 0; - }); - - // lights - version(old_light) { - shadToPolar = new Shader("light_topolar", loadTextFile("data/shaders/srlight_topolar.frag")); - } else { - shadToPolar = new Shader("light_topolar", loadTextFile("data/shaders/srlight_topolar_new.frag")); - } - shadToPolar.exec((Shader shad) { - shad["texOcc"] = 0; - shad["texOccFull"] = 2; - }); - - shadBlur = new Shader("light_blur", loadTextFile("data/shaders/srlight_blur.frag")); - shadBlur.exec((Shader shad) { - shad["texDist"] = 0; - shad["texBg"] = 1; - shad["texOcc"] = 2; - }); - - shadBlurOcc = new Shader("light_blur_occ", loadTextFile("data/shaders/srlight_blur_occ.frag")); - shadBlurOcc.exec((Shader shad) { - shad["texLMap"] = 0; - shad["texBg"] = 1; - shad["texOcc"] = 2; - }); - - //TODO: this sux! - foreach (int sz; 2..MaxLightRadius+1) { - fboOccluders[sz] = new FBO(sz*2, sz*2, Texture.Option.Clamp, Texture.Option.Linear); // create occluders FBO - fboDistMap[sz] = new FBO(sz*2, 1, Texture.Option.Clamp); // create 1d distance map FBO - } - - // setup matrices - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - map.oglBuildMega(); - mapTilesChanged = 0; - - fboLevel = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // final level render will be here - fboLevelLight = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // level lights will be rendered here - //fboForeground = new FBO(map.width*8, map.height*8, Texture.Option.Nearest); // level foreground - fboOrigBack = new FBO(map.width*8, map.height*8, Texture.Option.Nearest, Texture.Option.Depth); // background+foreground - - shadToPolar.exec((Shader shad) { shad["mapPixSize"] = SVec2F(map.width*8-1, map.height*8-1); }); - shadBlur.exec((Shader shad) { shad["mapPixSize"] = SVec2F(map.width*8, map.height*8); }); - shadBlurOcc.exec((Shader shad) { shad["mapPixSize"] = SVec2F(map.width*8, map.height*8); }); - - glActiveTexture(GL_TEXTURE0+0); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - orthoCamera(vlWidth, vlHeight); - - loadSmFont(); - - Actor.resetStorage(); - loadMapMonsters(); - - dotInit(); - - // save first snapshot - prevFrameActorsData = new ubyte[](Actor.actorSize*65536); // ~15-20 megabytes - prevFrameActorOfs[] = uint.max; // just for fun - Actor.saveSnapshot(prevFrameActorsData[], prevFrameActorOfs.ptr); - mapViewPosX[0] = mapViewPosX[1]; - mapViewPosY[0] = mapViewPosY[1]; - - { import core.memory : GC; GC.collect(); } -} - - -// ////////////////////////////////////////////////////////////////////////// // -//FIXME: optimize! -__gshared uint mapTilesChanged = 0; - - -public void mapDirty (uint layermask) { mapTilesChanged |= layermask; } - -void rebuildMapMegaTextures () { - //fbo.replaceTexture - //mapTilesChanged = false; - //map.clearMegaTextures(); - map.oglBuildMega(mapTilesChanged); - mapTilesChanged = 0; -} - - -// ////////////////////////////////////////////////////////////////////////// // -__gshared int vportX0, vportY0, vportX1, vportY1; - - -void renderLight() (int lightX, int lightY, in auto ref SVec4F lcol, int lightRadius) { - if (lightRadius < 2) return; - if (lightRadius > MaxLightRadius) lightRadius = MaxLightRadius; - int lightSize = lightRadius*2; - // is this light visible? - if (lightX <= -lightRadius || lightY <= -lightRadius || lightX-lightRadius >= map.width*8 || lightY-lightRadius >= map.height*8) return; - - // out of viewport -- do nothing - if (lightX+lightRadius < vportX0 || lightY+lightRadius < vportY0) return; - if (lightX-lightRadius > vportX1 || lightY-lightRadius > vportY1) return; - - if (lightX >= 0 && lightY >= 0 && lightX < map.width*8 && lightY < map.height*8 && - map.teximgs[map.LightMask].imageData.colors.ptr[lightY*(map.width*8)+lightX].a > 190) return; - - //glUseProgram(0); - glDisable(GL_BLEND); - - // draw shadow casters to fboOccludersId, light should be in the center - version(old_light) { - fboOccluders[lightRadius].exec({ - glColor4f(0.0f, 0.0f, 0.0f, 1.0f); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - orthoCamera(lightSize, lightSize); - drawAtXY(map.texgl.ptr[map.LightMask], lightRadius-lightX, lightRadius-lightY); - }); - } - // common color for all the following - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - // build 1d distance map to fboShadowMapId - fboDistMap[lightRadius].exec({ - shadToPolar.exec((Shader shad) { - shad["lightTexSize"] = SVec2F(lightSize, lightSize); - shad["lightPos"] = SVec2F(lightX, lightY); - // no need to clear it, shader will take care of that - orthoCamera(lightSize, 1); - version(old_light) { - drawAtXY(fboOccluders[lightRadius].tex.tid, 0, 0, lightSize, 1); - } else { - // it doesn't matter what we will draw here, so just draw filled rect - glRectf(0, 0, lightSize, 1); - } - }); - }); - - // build light texture for blending - fboOccluders[lightRadius].exec({ - // no need to clear it, shader will take care of that - shadBlur.exec((Shader shad) { - shad["lightTexSize"] = SVec2F(lightSize, lightSize); - shad["lightColor"] = SVec4F(lcol.x, lcol.y, lcol.z, lcol.w); - shad["lightPos"] = SVec2F(lightX, lightY); - orthoCamera(lightSize, lightSize); - drawAtXY(fboDistMap[lightRadius].tex.tid, 0, 0, lightSize, lightSize); - }); - }); - - // blend light texture - fboLevelLight.exec({ - glEnable(GL_BLEND); - //glDisable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - orthoCamera(map.width*8, map.height*8); - drawAtXY(fboOccluders[lightRadius].tex, lightX-lightRadius, lightY-lightRadius, mirrorY:true); - // and blend it again, with the shader that will touch only occluders - shadBlurOcc.exec((Shader shad) { - shad["lightTexSize"] = SVec2F(lightSize, lightSize); - shad["lightColor"] = SVec4F(lcol.x, lcol.y, lcol.z, lcol.w); - shad["lightPos"] = SVec2F(lightX, lightY); - drawAtXY(fboOccluders[lightRadius].tex.tid, lightX-lightRadius, lightY-lightRadius, lightSize, lightSize, mirrorY:true); - }); - }); -} - - -// ////////////////////////////////////////////////////////////////////////// // -// messages -struct Message { - enum Phase { FadeIn, Stay, FadeOut } - Phase phase; - int alpha; - int pauseMsecs; - MonoTime removeTime; - char[256] text; - usize textlen; -} - -private import core.sync.mutex : Mutex; - -__gshared Message[128] messages; -__gshared uint messagesUsed = 0; -__gshared Mutex messageLock; - -shared static this () { messageLock = new Mutex(); } - - -public void addMessage (const(char)[] msgtext, int pauseMsecs=3000, bool noreplace=false) { - messageLock.lock(); - scope(exit) messageLock.unlock(); - if (msgtext.length == 0) return; - conwriteln(msgtext); - if (pauseMsecs <= 50) return; - if (messagesUsed == messages.length) { - // remove top message - foreach (immutable cidx; 1..messagesUsed) messages.ptr[cidx-1] = messages.ptr[cidx]; - messages.ptr[0].alpha = 255; - --messagesUsed; - } - // quick replace - if (!noreplace && messagesUsed == 1) { - switch (messages.ptr[0].phase) { - case Message.Phase.FadeIn: - messages.ptr[0].phase = Message.Phase.FadeOut; - break; - case Message.Phase.Stay: - messages.ptr[0].phase = Message.Phase.FadeOut; - messages.ptr[0].alpha = 255; - break; - default: - } - } - auto msg = messages.ptr+messagesUsed; - ++messagesUsed; - msg.phase = Message.Phase.FadeIn; - msg.alpha = 0; - msg.pauseMsecs = pauseMsecs; - // copy text - if (msgtext.length > msg.text.length) { - msg.text = msgtext[0..msg.text.length]; - msg.textlen = msg.text.length; - } else { - msg.text[0..msgtext.length] = msgtext[]; - msg.textlen = msgtext.length; - } -} - - -void doMessages (MonoTime curtime) { - messageLock.lock(); - scope(exit) messageLock.unlock(); - - if (messagesUsed == 0) return; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - Message* msg; - - again: - msg = messages.ptr; - final switch (msg.phase) { - case Message.Phase.FadeIn: - if ((msg.alpha += 10) >= 255) { - msg.phase = Message.Phase.Stay; - msg.removeTime = curtime+dur!"msecs"(msg.pauseMsecs); - goto case; // to stay - } - glColor4f(1.0f, 1.0f, 1.0f, msg.alpha/255.0f); - break; - case Message.Phase.Stay: - if (msg.removeTime <= curtime) { - msg.alpha = 255; - msg.phase = Message.Phase.FadeOut; - goto case; // to fade - } - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - break; - case Message.Phase.FadeOut: - if ((msg.alpha -= 10) <= 0) { - if (--messagesUsed == 0) return; - // remove this message - foreach (immutable cidx; 1..messagesUsed+1) messages.ptr[cidx-1] = messages.ptr[cidx]; - goto again; - } - glColor4f(1.0f, 1.0f, 1.0f, msg.alpha/255.0f); - break; - } - - smDrawText(10, 10, msg.text[0..msg.textlen]); -} - - -// ////////////////////////////////////////////////////////////////////////// // -//mixin(Actor.FieldPropMixin!("0drawlistpos", uint)); - -mixin(Actor.FieldGetMixin!("classtype", StrId)); // fget_classtype -mixin(Actor.FieldGetMixin!("classname", StrId)); // fget_classname -mixin(Actor.FieldGetMixin!("x", int)); -mixin(Actor.FieldGetMixin!("y", int)); -mixin(Actor.FieldGetMixin!("flags", uint)); -mixin(Actor.FieldGetMixin!("zAnimstate", StrId)); -mixin(Actor.FieldGetMixin!("zAnimidx", int)); -mixin(Actor.FieldGetMixin!("dir", uint)); -mixin(Actor.FieldGetMixin!("attLightXOfs", int)); -mixin(Actor.FieldGetMixin!("attLightYOfs", int)); -mixin(Actor.FieldGetMixin!("attLightRGBX", uint)); - -//mixin(Actor.FieldGetPtrMixin!("classtype", StrId)); // fget_classtype -//mixin(Actor.FieldGetPtrMixin!("classname", StrId)); // fget_classname -mixin(Actor.FieldGetPtrMixin!("x", int)); -mixin(Actor.FieldGetPtrMixin!("y", int)); -//mixin(Actor.FieldGetPtrMixin!("flags", uint)); -//mixin(Actor.FieldGetPtrMixin!("zAnimstate", StrId)); -//mixin(Actor.FieldGetPtrMixin!("zAnimidx", int)); -//mixin(Actor.FieldGetPtrMixin!("dir", uint)); -//mixin(Actor.FieldGetPtrMixin!("attLightXOfs", int)); -//mixin(Actor.FieldGetPtrMixin!("attLightYOfs", int)); -//mixin(Actor.FieldGetPtrMixin!("attLightRGBX", uint)); - - -// ////////////////////////////////////////////////////////////////////////// // -__gshared int lightX = vlWidth/2, lightY = vlHeight/2; -__gshared int mapOfsX, mapOfsY; -__gshared bool movement = false; -__gshared float iLiquidTime = 0.0; -__gshared bool altMove = false; - - -void renderScene (MonoTime curtime) { - //enum BackIntens = 0.05f; - enum BackIntens = 0.0f; - - gloStackClear(); - float atob = (curtime > lastthink ? cast(float)((curtime-lastthink).total!"msecs")/cast(float)((nextthink-lastthink).total!"msecs") : 1.0f); - //{ import core.stdc.stdio; printf("atob=%f\n", cast(double)atob); } - /* - { - int framelen = cast(int)((nextthink-lastthink).total!"msecs"); - int curfp = cast(int)((curtime-lastthink).total!"msecs"); - { import core.stdc.stdio; printf("framelen=%d; curfp=%d\n", framelen, curfp); } - } - */ - - int mofsx, mofsy; - - bool camCenter = true; - if (altMove || movement || scale == 1) { - mofsx = mapOfsX; - mofsy = mapOfsY; - vportX0 = 0; - vportY0 = 0; - vportX1 = map.width*8; - vportY1 = map.height*8; - camCenter = false; - } else { - if (frameInterpolation) { - import core.stdc.math : roundf; - mofsx = cast(int)(mapViewPosX[0]+roundf((mapViewPosX[1]-mapViewPosX[0])*atob)); - mofsy = cast(int)(mapViewPosY[0]+roundf((mapViewPosY[1]-mapViewPosY[0])*atob)); - } else { - mofsx = mapViewPosX[1]; - mofsy = mapViewPosY[1]; - } - vportX0 = mofsx/scale; - vportY0 = mofsy/scale; - vportX1 = vportX0+vlWidth/scale; - vportY1 = vportY0+vlHeight/scale; - } - - if (mapTilesChanged != 0) rebuildMapMegaTextures(); - - dotDraw(atob); - - // build background layer - fboOrigBack.exec({ - //glDisable(GL_BLEND); - //glClearDepth(1.0f); - //glDepthFunc(GL_LESS); - //glDepthFunc(GL_NEVER); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT/*|GL_DEPTH_BUFFER_BIT*/); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - orthoCamera(map.width*8, map.height*8); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // draw sky - /* - drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8); - drawAtXY(map.skytexgl.tid, mapOfsX/2-map.MapSize*8, mapOfsY/2, map.MapSize*8, map.MapSize*8); - drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2-map.MapSize*8, map.MapSize*8, map.MapSize*8); - drawAtXY(map.skytexgl.tid, mapOfsX/2, mapOfsY/2, map.MapSize*8, map.MapSize*8); - */ - drawAtXY(map.skytexgl.tid, 0, 0, map.MapSize*8, map.MapSize*8); - // draw background - drawAtXY(map.texgl.ptr[map.Back], 0, 0); - // draw distorted liquid areas - shadLiquidDistort.exec((Shader shad) { - shad["iDistortTime"] = iLiquidTime; - drawAtXY(map.texgl.ptr[map.AllLiquids], 0, 0); - }); - // monsters, items; we'll do linear interpolation here - glColor3f(1.0f, 1.0f, 1.0f); - //glEnable(GL_DEPTH_TEST); - attachedLightCount = 0; - - // who cares about memory?! - // draw order: players, items, monsters, other - static struct DrawInfo { - ActorDef adef; - ActorId aid; - int actorX, actorY; - @disable this (this); // no copies - } - enum { Players, Items, Monsters, Other } - __gshared DrawInfo[65536][4] drawlists; - __gshared uint[4] dlpos; - DrawInfo camchickdi; - - dlpos[] = 0; - ActorId camchick; - - Actor.forEach((ActorId me) { - //me.fprop_0drawlistpos = 0; - if (auto adef = findActorDef(me)) { - uint dlnum = Other; - switch (adef.classtype.get) { - case "monster": dlnum = (adef.classname.get != "Player" ? Monsters : Players); break; - case "item": dlnum = Items; break; - default: dlnum = Other; break; - } - int actorX, actorY; // current actor position - { - auto ofs = prevFrameActorOfs.ptr[me.id&0xffff]; - if (frameInterpolation && ofs < uint.max-1 && Actor.isSameSavedActor(me.id, prevFrameActorsData.ptr, ofs)) { - import core.stdc.math : roundf; - auto xptr = prevFrameActorsData.ptr+ofs; - int ox = xptr.fgetp_x; - int nx = me.fget_x; - int oy = xptr.fgetp_y; - int ny = me.fget_y; - actorX = cast(int)(ox+roundf((nx-ox)*atob)); - actorY = cast(int)(oy+roundf((ny-oy)*atob)); - //conwriteln("actor ", me.id, "; o=(", ox, ",", oy, "); n=(", nx, ",", ny, "); p=(", x, ",", y, ")"); - } else { - actorX = me.fget_x; - actorY = me.fget_y; - } - } - if (!camchick.valid && me.flags!uint&AF_CAMERACHICK) { - camchick = me; - camchickdi.adef = adef; - camchickdi.aid = me; - camchickdi.actorX = actorX; - camchickdi.actorY = actorY; - } - // draw sprite - if ((me.fget_flags&AF_NODRAW) == 0) { - //auto dl = &drawlists[dlnum][dlpos.ptr[dlnum]]; - //me.fprop_0drawlistpos = (dlpos.ptr[dlnum]&0xffff)|((dlnum&0xff)<<16); - auto dl = drawlists.ptr[dlnum].ptr+dlpos.ptr[dlnum]; - ++dlpos.ptr[dlnum]; - dl.adef = adef; - dl.aid = me; - dl.actorX = actorX; - dl.actorY = actorY; - /* - if (auto isp = adef.animSpr(me.fget_zAnimstate, me.fget_dir, me.fget_zAnimidx)) { - drawAtXY(isp.tex, actorX-isp.vga.sx, actorY-isp.vga.sy, adef.zz); - } else { - //conwriteln("no animation for actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")"); - } - */ - } - // process attached lights - if ((me.fget_flags&AF_NOLIGHT) == 0) { - uint alr = me.fget_attLightRGBX; - if ((alr&0xff) >= 4) { - // yep, add it - auto li = attachedLights.ptr+attachedLightCount; - ++attachedLightCount; - li.x = actorX+me.fget_attLightXOfs; - li.y = actorY+me.fget_attLightYOfs; - li.r = ((alr>>24)&0xff)/255.0f; // red or intensity - if ((alr&0x00_ff_ff_00U) == 0x00_00_01_00U) { - li.uncolored = true; - } else { - li.g = ((alr>>16)&0xff)/255.0f; - li.b = ((alr>>8)&0xff)/255.0f; - li.uncolored = false; - } - li.radius = (alr&0xff); - if (li.radius > MaxLightRadius) li.radius = MaxLightRadius; - } - } - } else { - conwriteln("not found actor ", me.id, " (", me.classtype!string, ":", me.classname!string, ")"); - } - }); - // draw actor lists - foreach_reverse (uint dlnum; 0..drawlists.length) { - auto dl = drawlists.ptr[dlnum].ptr; - if (dlnum == Players) dl += dlpos.ptr[dlnum]-1; - foreach (uint idx; 0..dlpos.ptr[dlnum]) { - auto me = dl.aid; - if (auto isp = dl.adef.animSpr(me.fget_zAnimstate, me.fget_dir, me.fget_zAnimidx)) { - //drawAtXY(isp.tex, dl.actorX-isp.vga.sx, dl.actorY-isp.vga.sy); - isp.drawAtXY(dl.actorX, dl.actorY); - } - if (dlnum != Players) ++dl; else --dl; - } - } - if (camCenter && camchick.valid) { - int tiltHeight = /*getMapViewHeight()*/(vlHeight/scale)/4; - int vy = camchick.looky!int; - if (vy < -tiltHeight) vy = -tiltHeight; else if (vy > tiltHeight) vy = tiltHeight; - int swdt = vlWidth/scale; - int shgt = vlHeight/scale; - int x = camchickdi.actorX-swdt/2; - int y = (camchickdi.actorY+vy)-shgt/2; - if (x < 0) x = 0; else if (x >= map.width*8-swdt) x = map.width*8-swdt-1; - if (y < 0) y = 0; else if (y >= map.height*8-shgt) y = map.height*8-shgt-1; - mofsx = x*2; - mofsy = y*2; - vportX0 = mofsx/scale; - vportY0 = mofsy/scale; - vportX1 = vportX0+vlWidth/scale; - vportY1 = vportY0+vlHeight/scale; - } - //glDisable(GL_DEPTH_TEST); - // draw dots - drawAtXY(texParts, 0, 0); - // do liquid coloring - drawAtXY(map.texgl.ptr[map.LiquidMask], 0, 0); - // foreground -- hide secrets, draw lifts and such - drawAtXY(map.texgl.ptr[map.Front], 0, 0); - }); - - - if (doLighting) { - // clear light layer - fboLevelLight.exec({ - glDisable(GL_BLEND); - glClearColor(BackIntens, BackIntens, BackIntens, 1.0f); - //glClearColor(0.15f, 0.15f, 0.15f, 1.0f); - ////glColor4f(1.0f, 1.0f, 1.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - }); - - // texture 1 is background - glActiveTexture(GL_TEXTURE0+1); - glBindTexture(GL_TEXTURE_2D, fboOrigBack.tex.tid); - // texture 2 is occluders - glActiveTexture(GL_TEXTURE0+2); - glBindTexture(GL_TEXTURE_2D, map.texgl.ptr[map.LightMask].tid); - // done texture assign - glActiveTexture(GL_TEXTURE0+0); - - enum LYOfs = 1; - - renderLight( 27, 391-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 100); - renderLight(542, 424-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 100); - renderLight(377, 368-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 32); - renderLight(147, 288-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 64); - renderLight( 71, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 128); - renderLight(249, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 128); - renderLight(426, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 128); - renderLight(624, 200-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 128); - renderLight(549, 298-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 64); - renderLight( 74, 304-0+LYOfs, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 32); - - renderLight(24*8+4, (24+18)*8-2+LYOfs, SVec4F(0.6f, 0.0f, 0.0f, 1.0f), 128); - - renderLight(280, 330, SVec4F(0.0f, 0.0f, 0.0f, 1.0f), 64); - - // attached lights - foreach (ref li; attachedLights[0..attachedLightCount]) { - if (li.uncolored) { - renderLight(li.x, li.y, SVec4F(0.0f, 0.0f, 0.0f, li.r), li.radius); - } else { - renderLight(li.x, li.y, SVec4F(li.r, li.g, li.b, 1.0f), li.radius); - } - } - - foreach (immutable _; 0..1) { - renderLight(lightX, lightY, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 96); - } - - glActiveTexture(GL_TEXTURE0+1); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0+2); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0+0); - - - // draw scaled level - /+ - shadScanlines.exec((Shader shad) { - shad["scanlines"] = scanlines; - glClearColor(BackIntens, BackIntens, BackIntens, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - orthoCamera(vlWidth, vlHeight); - //orthoCamera(map.width*8*scale, map.height*8*scale); - //glMatrixMode(GL_MODELVIEW); - //glLoadIdentity(); - //glTranslatef(0.375, 0.375, 0); // to be pixel-perfect - // somehow, FBO objects are mirrored; wtf?! - drawAtXY(fboLevel.tex.tid, -mapOfsX, -mapOfsY, map.width*8*scale, map.height*8*scale, mirrorY:true); - //glLoadIdentity(); - }); - +/ - - /* - fboLevelLight.exec({ - smDrawText(map.width*8/2, map.height*8/2, "Testing..."); - }); - */ - } - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - glDisable(GL_BLEND); - - /* - fboOrigBack.exec({ - //auto img = smfont.ptr[0x39]; - auto img = fftest; - if (img !is null) { - //conwriteln("img.sx=", img.sx, "; img.sy=", img.sy, "; ", img.width, "x", img.height); - drawAtXY(img.tex, 10-img.sx, 10-img.sy); - } - }); - */ - - - /+ - { // http://stackoverflow.com/questions/7207422/setting-up-opengl-multiple-render-targets - GLenum[1] buffers = [ GL_BACK_LEFT, /*GL_COLOR_ATTACHMENT0_EXT*/ ]; - //GLenum[1] buffers = [ GL_NONE, /*GL_COLOR_ATTACHMENT0_EXT*/ ]; - glDrawBuffers(1, buffers.ptr); - } - +/ - - orthoCamera(vlWidth, vlHeight); - auto tex = (doLighting ? fboLevelLight.tex.tid : fboOrigBack.tex.tid); - //mofsx &= ~1; - //mofsy &= ~1; - drawAtXY(tex, -mofsx, -mofsy, map.width*8*scale, map.height*8*scale, mirrorY:true); - - doMessages(curtime); -} - - -// ////////////////////////////////////////////////////////////////////////// // -// returns time slept -int sleepAtMaxMsecs (int msecs) { - if (msecs > 0) { - import core.sys.posix.signal : timespec; - import core.sys.posix.time : nanosleep; - timespec ts = void, tpassed = void; - ts.tv_sec = 0; - ts.tv_nsec = msecs*1000*1000+(500*1000); // milli to nano - nanosleep(&ts, &tpassed); - return (ts.tv_nsec-tpassed.tv_nsec)/(1000*1000); - } else { - return 0; - } -} - - -// ////////////////////////////////////////////////////////////////////////// // -// rendering thread -shared int diedie = 0; - -enum D2DFrameTime = 55; // milliseconds -enum MinFrameTime = 1000/60; // ~60 FPS - -void renderThread () { - try { - MonoTime curtime = MonoTime.currTime; - - lastthink = curtime; // for interpolator - nextthink = curtime+dur!"msecs"(D2DFrameTime); - MonoTime nextvframe = curtime; - - enum MaxFPSFrames = 16; - float frtimes = 0.0f; - int framenum = 0; - int prevFPS = -1; - int hushFrames = 6; // ignore first `hushFrames` frames overtime - MonoTime prevFrameStartTime = curtime; - - bool vframeWasLost = false; - - // "D2D frames"; curtime should be set; return `true` if frame was processed; will fix `curtime` - bool doThinkFrame () { - if (curtime >= nextthink) { - lastthink = curtime; - while (nextthink <= curtime) nextthink += dur!"msecs"(D2DFrameTime); - // save snapshot and other data for interpolator - Actor.saveSnapshot(prevFrameActorsData[], prevFrameActorOfs.ptr); - mapViewPosX[0] = mapViewPosX[1]; - mapViewPosY[0] = mapViewPosY[1]; - // process actors - doActorsThink(); - dotThink(); - // some timing - auto tm = MonoTime.currTime; - int thinkTime = cast(int)((tm-curtime).total!"msecs"); - if (thinkTime > 9) { import core.stdc.stdio; printf("spent on thinking: %d msecs\n", thinkTime); } - curtime = tm; - return true; - } else { - return false; - } - } - - // "video frames"; curtime should be set; return `true` if frame was processed; will fix `curtime` - bool doVFrame () { - version(dont_use_vsync) { - // timer - enum doCheckTime = true; - } else { - // vsync - __gshared bool prevLost = false; - bool doCheckTime = vframeWasLost; - if (vframeWasLost) { - if (!prevLost) { - { import core.stdc.stdio; printf("frame was lost!\n"); } - } - prevLost = true; - } else { - prevLost = false; - } - } - if (doCheckTime) { - if (curtime < nextvframe) return false; - version(dont_use_vsync) { - if (curtime > nextvframe) { - auto overtime = cast(int)((curtime-nextvframe).total!"msecs"); - if (overtime > 2500) { - if (hushFrames) { - --hushFrames; - } else { - { import core.stdc.stdio; printf(" spent whole %d msecs\n", overtime); } - } - } - } - } - } - while (nextvframe <= curtime) nextvframe += dur!"msecs"(MinFrameTime); - bool ctset = false; - { - sdwindow.mtLock(); - scope(exit) sdwindow.mtUnlock(); - ctset = sdwindow.setAsCurrentOpenGlContextNT; - } - // if we can't set context, pretend that videoframe was processed; this should solve problem with vsync and invisible window - if (ctset) { - // render scene - iLiquidTime = cast(float)((curtime-MonoTime.zero).total!"msecs"%10000000)/18.0f*0.04f; - renderScene(curtime); - sdwindow.mtLock(); - scope(exit) sdwindow.mtUnlock(); - sdwindow.swapOpenGlBuffers(); - glFinish(); - sdwindow.releaseCurrentOpenGlContext(); - vframeWasLost = false; - } else { - vframeWasLost = true; - { import core.stdc.stdio; printf("xframe was lost!\n"); } - } - curtime = MonoTime.currTime; - return true; - } - - for (;;) { - if (sdwindow.closed) break; - if (atomicLoad(diedie) > 0) break; - - curtime = MonoTime.currTime; - auto fstime = curtime; - - doThinkFrame(); // this will fix curtime if necessary - if (doVFrame()) { - if (!vframeWasLost) { - // fps - auto frameTime = cast(float)(curtime-prevFrameStartTime).total!"msecs"/1000.0f; - prevFrameStartTime = curtime; - frtimes += frameTime; - if (++framenum >= MaxFPSFrames || frtimes >= 3.0f) { - import std.string : format; - int newFPS = cast(int)(cast(float)MaxFPSFrames/frtimes+0.5); - if (newFPS != prevFPS) { - sdwindow.title = "%s / FPS:%s".format("D2D", newFPS); - prevFPS = newFPS; - } - framenum = 0; - frtimes = 0.0f; - } - } - } - - curtime = MonoTime.currTime; - - // now sleep until next "video" or "think" frame - if (nextthink > curtime && nextvframe > curtime) { - // let's decide - immutable nextVideoFrameSleep = cast(int)((nextvframe-curtime).total!"msecs"); - immutable nextThinkFrameSleep = cast(int)((nextthink-curtime).total!"msecs"); - immutable sleepTime = (nextVideoFrameSleep < nextThinkFrameSleep ? nextVideoFrameSleep : nextThinkFrameSleep); - sleepAtMaxMsecs(sleepTime); - //curtime = MonoTime.currTime; - } - } - } catch (Exception e) { - import core.stdc.stdio; - fprintf(stderr, "FUUUUUUUUUUUUUUUUUUUUUUUUUU\n"); - for (;;) { - if (sdwindow.closed) break; - if (atomicLoad(diedie) > 0) break; - sleepAtMaxMsecs(100); - } - } - atomicStore(diedie, 2); -} - - -// ////////////////////////////////////////////////////////////////////////// // -void closeWindow () { - if (atomicLoad(diedie) != 2) { - atomicStore(diedie, 1); - while (atomicLoad(diedie) != 2) {} - } - if (!sdwindow.closed) { - flushGui(); - sdwindow.close(); - } -} - - -// ////////////////////////////////////////////////////////////////////////// // -__gshared Thread renderTid; - - -void main (string[] args) { - FuncPool.dumpCode = false; - FuncPool.dumpCodeSize = false; - dacsDumpSemantic = false; - dacsOptimize = 9; - //version(rdmd) { dacsOptimize = 0; } - bool compileOnly = false; - bool doVBL = true; - - for (usize idx = 1; idx < args.length; ++idx) { - bool remove = true; - if (args[idx] == "--dump-code") FuncPool.dumpCode = true; - else if (args[idx] == "--dump-code-size") FuncPool.dumpCodeSize = true; - else if (args[idx] == "--dump-semantic") dacsDumpSemantic = true; - else if (args[idx] == "--dump-all") { FuncPool.dumpCode = true; FuncPool.dumpCodeSize = true; dacsDumpSemantic = true; } - else if (args[idx] == "--compile") compileOnly = true; - else if (args[idx] == "--compile-only") compileOnly = true; - else if (args[idx] == "--messages") ++dacsMessages; - else if (args[idx] == "--no-copro") dacsOptimizeNoCoPro = true; - else if (args[idx] == "--no-deadass") dacsOptimizeNoDeadAss = true; - else if (args[idx] == "--no-purekill") dacsOptimizeNoPureKill = true; - else if (args[idx] == "--no-vsync") doVBL = false; - else if (args[idx] == "--vsync") doVBL = true; - else if (args[idx].length > 2 && args[idx][0..2] == "-O") { - import std.conv : to; - ubyte olevel = to!ubyte(args[idx][2..$]); - dacsOptimize = olevel; - } - else remove = false; - if (remove) { - foreach (immutable c; idx+1..args.length) args.ptr[c-1] = args.ptr[c]; - args.length -= 1; - --idx; //hack - } - } - - addInternalActorFields(); - - static void setDP () { - version(rdmd) { - setDataPath("data"); - } else { - import std.file : thisExePath; - import std.path : dirName; - setDataPath(thisExePath.dirName); - } - addPK3(getDataPath~"base.pk3"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad"); loadWadScripts(); - //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad"); loadWadScripts(); - scriptLoadingComplete(); - } - - setDP(); - - if (compileOnly) return; - - try { - registerAPI(); - loadPalette(); - - setOpenGLContextVersion(3, 2); // up to GLSL 150 - //openGLContextCompatible = false; - - map = new LevelMap("maps/map01.d2m"); - ugInit(map.width*8, map.height*8); - - scale = 2; - - //mapOfsX = 8*26; - //mapOfsY = 8*56; - map.getThingPos(1/*ThingId.Player1*/, &mapOfsX, &mapOfsY); - // fix viewport - /* - mapOfsX = (mapOfsX*2)-vlWidth/2; - if (mapOfsX+vlWidth > map.width*16) mapOfsX = map.width*16-vlWidth; - if (mapOfsX < 0) mapOfsX = 0; - mapOfsY = (mapOfsY*2)-vlHeight/2; - if (mapOfsY+vlHeight > map.height*16) mapOfsY = map.height*16-vlHeight; - if (mapOfsY < 0) mapOfsY = 0; - */ - setMapViewPos(mapOfsX, mapOfsY); - mapOfsX = mapViewPosX[1]; - mapOfsY = mapViewPosY[1]; - - sdwindow = new SimpleWindow(vlWidth, vlHeight, "D2D", OpenGlOptions.yes, Resizablity.fixedSize); - - sdwindow.visibleForTheFirstTime = delegate () { - sdwindow.setAsCurrentOpenGlContext(); // make this window active - glbindLoadFunctions(); - /+ - { - import core.stdc.stdio; - printf("GL version: %s\n", glGetString(GL_VERSION)); - GLint l, h; - glGetIntegerv(GL_MAJOR_VERSION, &h); - glGetIntegerv(GL_MINOR_VERSION, &l); - printf("version: %d.%d\n", h, l); - printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); - GLint svcount; - glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount); - if (svcount > 0) { - printf("%d shader versions supported:\n", svcount); - foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n)); - } - /* - GLint ecount; - glGetIntegerv(GL_NUM_EXTENSIONS, &ecount); - if (ecount > 0) { - printf("%d extensions supported:\n", ecount); - foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n)); - } - */ - } - +/ - // check if we have sufficient shader version here - { - bool found = false; - GLint svcount; - glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount); - if (svcount > 0) { - foreach (GLuint n; 0..svcount) { - import core.stdc.string : strncmp; - auto v = glGetStringi(GL_SHADING_LANGUAGE_VERSION, n); - if (v is null) continue; - if (strncmp(v, "120", 3) != 0) continue; - if (v[3] > ' ') continue; - found = true; - break; - } - } - if (!found) assert(0, "can't find OpenGL GLSL 120"); - { - auto adr = glGetProcAddress("glTexParameterf"); - if (adr is null) assert(0); - } - } - version(dont_use_vsync) { - sdwindow.vsync = false; - } else { - sdwindow.vsync = true; - } - //sdwindow.useGLFinish = false; - initOpenGL(); - //sdwindow.redrawOpenGlScene(); - if (!sdwindow.releaseCurrentOpenGlContext()) { import core.stdc.stdio; printf("can't release OpenGL context(1)\n"); } - if (!renderTid) { - renderTid = new Thread(&renderThread); - renderTid.start(); - } - }; - - //sdwindow.redrawOpenGlScene = delegate () { renderScene(); }; - - enum MSecsPerFrame = 1000/30; /* 30 is FPS */ - - uint[8] frameTimes = 1000; - enum { Left, Right, Up, Down } - bool[4] pressed = false; - - sdwindow.eventLoop(MSecsPerFrame, - delegate () { - if (sdwindow.closed) return; - if (pressed[Left]) mapOfsX -= 8; - if (pressed[Right]) mapOfsX += 8; - if (pressed[Up]) mapOfsY -= 8; - if (pressed[Down]) mapOfsY += 8; - import std.math : cos, sin; - __gshared float itime = 0.0; - itime += 0.02; - if (movement) { - mapOfsX = cast(int)(800.0/2.0+cos(itime)*220.0); - mapOfsY = cast(int)(800.0/2.0+120.0+sin(itime)*160.0); - } - if (scale == 1) mapOfsX = mapOfsY = 0; - //sdwindow.redrawOpenGlSceneNow(); - }, - delegate (KeyEvent event) { - if (sdwindow.closed) return; - if (event.pressed && event.key == Key.Escape) { closeWindow(); return; } - switch (event.key) { - case Key.X: if (event.pressed) altMove = !altMove; break; - case Key.Left: if (altMove) pressed[Left] = event.pressed; break; - case Key.Right: if (altMove) pressed[Right] = event.pressed; break; - case Key.Up: if (altMove) pressed[Up] = event.pressed; break; - case Key.Down: if (altMove) pressed[Down] = event.pressed; break; - default: - } - if (!altMove) { - switch (event.key) { - case Key.Left: case Key.Pad4: plrKeyUpDown(0, PLK_LEFT, event.pressed); break; - case Key.Right: case Key.Pad6: plrKeyUpDown(0, PLK_RIGHT, event.pressed); break; - case Key.Up: case Key.Pad8: plrKeyUpDown(0, PLK_UP, event.pressed); break; - case Key.Down: case Key.Pad2: plrKeyUpDown(0, PLK_DOWN, event.pressed); break; - case Key.Alt: plrKeyUpDown(0, PLK_JUMP, event.pressed); break; - case Key.Ctrl: plrKeyUpDown(0, PLK_FIRE, event.pressed); break; - case Key.Shift: plrKeyUpDown(0, PLK_USE, event.pressed); break; - default: - } - } - }, - delegate (MouseEvent event) { - lightX = event.x/scale; - lightY = event.y/scale; - lightX += mapOfsX/scale; - lightY += mapOfsY/scale; - }, - delegate (dchar ch) { - if (ch == 'q') { closeWindow(); return; } - if (ch == 's') scanlines = !scanlines; - if (ch == '1') scale = 1; - if (ch == '2') scale = 2; - if (ch == 'D') cheatNoDoors = !cheatNoDoors; - if (ch == 'i') { - frameInterpolation = !frameInterpolation; - if (frameInterpolation) addMessage("Interpolation: ON"); else addMessage("Interpolation: OFF"); - } - if (ch == 'l') { - doLighting = !doLighting; - if (doLighting) addMessage("Lighting: ON"); else addMessage("Lighting: OFF"); - } - if (ch == ' ') movement = !movement; - }, - ); - } catch (Exception e) { - import std.stdio : stderr; - stderr.writeln("FUUUUUUUUUUUU\n", e.toString); - } - closeWindow(); - flushGui(); -} +/* DooM2D: Midnight on the Firing Line + * coded by Ketmar // Invisible Vector + * Understanding is not required. Only obedience. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +module xmain_d2d is aliced; + +private: +import core.atomic; +import core.thread; +import core.time; + +import std.concurrency; + +import iv.glbinds; +import glutils; +import console; +import wadarc; + +import iv.stream; + +import d2dmap; +import d2dadefs; +//import d2dactors; +import d2dgfx; +import d2dfont; +import dacs; + +import d2dunigrid; + +// `map` is there +import dengapi; + +import d2dparts; + +import render; + + +// ////////////////////////////////////////////////////////////////////////// // +import arsd.color; +import arsd.png; + + +// ////////////////////////////////////////////////////////////////////////// // +void main (string[] args) { + FuncPool.dumpCode = false; + FuncPool.dumpCodeSize = false; + dacsDumpSemantic = false; + dacsOptimize = 9; + //version(rdmd) { dacsOptimize = 0; } + bool compileOnly = false; + bool doVBL = true; + + for (usize idx = 1; idx < args.length; ++idx) { + bool remove = true; + if (args[idx] == "--dump-code") FuncPool.dumpCode = true; + else if (args[idx] == "--dump-code-size") FuncPool.dumpCodeSize = true; + else if (args[idx] == "--dump-semantic") dacsDumpSemantic = true; + else if (args[idx] == "--dump-all") { FuncPool.dumpCode = true; FuncPool.dumpCodeSize = true; dacsDumpSemantic = true; } + else if (args[idx] == "--compile") compileOnly = true; + else if (args[idx] == "--compile-only") compileOnly = true; + else if (args[idx] == "--messages") ++dacsMessages; + else if (args[idx] == "--no-copro") dacsOptimizeNoCoPro = true; + else if (args[idx] == "--no-deadass") dacsOptimizeNoDeadAss = true; + else if (args[idx] == "--no-purekill") dacsOptimizeNoPureKill = true; + else if (args[idx] == "--no-vsync") doVBL = false; + else if (args[idx] == "--vsync") doVBL = true; + else if (args[idx].length > 2 && args[idx][0..2] == "-O") { + import std.conv : to; + ubyte olevel = to!ubyte(args[idx][2..$]); + dacsOptimize = olevel; + } + else remove = false; + if (remove) { + foreach (immutable c; idx+1..args.length) args.ptr[c-1] = args.ptr[c]; + args.length -= 1; + --idx; //hack + } + } + + addInternalActorFields(); + + static void setDP () { + version(rdmd) { + setDataPath("data"); + } else { + import std.file : thisExePath; + import std.path : dirName; + setDataPath(thisExePath.dirName); + } + addPK3(getDataPath~"base.pk3"); loadWadScripts(); + //addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad"); loadWadScripts(); + //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad"); loadWadScripts(); + //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad"); loadWadScripts(); + //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad"); loadWadScripts(); + //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad"); loadWadScripts(); + //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad"); loadWadScripts(); + scriptLoadingComplete(); + } + + setDP(); + + if (compileOnly) return; + + try { + registerAPI(); + loadPalette(); + + setOpenGLContextVersion(3, 2); // up to GLSL 150 + //openGLContextCompatible = false; + + //map = new LevelMap("maps/map01.d2m"); + //ugInit(map.width*8, map.height*8); + + //scale = 2; + + /+ + //mapOfsX = 8*26; + //mapOfsY = 8*56; + map.getThingPos(1/*ThingId.Player1*/, &mapOfsX, &mapOfsY); + // fix viewport + /* + mapOfsX = (mapOfsX*2)-vlWidth/2; + if (mapOfsX+vlWidth > map.width*16) mapOfsX = map.width*16-vlWidth; + if (mapOfsX < 0) mapOfsX = 0; + mapOfsY = (mapOfsY*2)-vlHeight/2; + if (mapOfsY+vlHeight > map.height*16) mapOfsY = map.height*16-vlHeight; + if (mapOfsY < 0) mapOfsY = 0; + */ + setMapViewPos(mapOfsX, mapOfsY); + mapOfsX = mapViewPosX[1]; + mapOfsY = mapViewPosY[1]; + +/ + + sdwindow = new SimpleWindow(vlWidth, vlHeight, "D2D", OpenGlOptions.yes, Resizablity.fixedSize); + + sdwindow.visibleForTheFirstTime = delegate () { + sdwindow.setAsCurrentOpenGlContext(); // make this window active + glbindLoadFunctions(); + /+ + { + import core.stdc.stdio; + printf("GL version: %s\n", glGetString(GL_VERSION)); + GLint l, h; + glGetIntegerv(GL_MAJOR_VERSION, &h); + glGetIntegerv(GL_MINOR_VERSION, &l); + printf("version: %d.%d\n", h, l); + printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); + GLint svcount; + glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount); + if (svcount > 0) { + printf("%d shader versions supported:\n", svcount); + foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n)); + } + /* + GLint ecount; + glGetIntegerv(GL_NUM_EXTENSIONS, &ecount); + if (ecount > 0) { + printf("%d extensions supported:\n", ecount); + foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n)); + } + */ + } + +/ + // check if we have sufficient shader version here + { + bool found = false; + GLint svcount; + glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount); + if (svcount > 0) { + foreach (GLuint n; 0..svcount) { + import core.stdc.string : strncmp; + auto v = glGetStringi(GL_SHADING_LANGUAGE_VERSION, n); + if (v is null) continue; + if (strncmp(v, "120", 3) != 0) continue; + if (v[3] > ' ') continue; + found = true; + break; + } + } + if (!found) assert(0, "can't find OpenGL GLSL 120"); + { + auto adr = glGetProcAddress("glTexParameterf"); + if (adr is null) assert(0); + } + } + version(dont_use_vsync) { + sdwindow.vsync = false; + } else { + sdwindow.vsync = true; + } + //sdwindow.useGLFinish = false; + initOpenGL(); + //sdwindow.redrawOpenGlScene(); + if (!sdwindow.releaseCurrentOpenGlContext()) { import core.stdc.stdio; printf("can't release OpenGL context(1)\n"); } + /* + if (!renderTid) { + renderTid = new Thread(&renderThread); + renderTid.start(); + } + */ + startRenderThread(); + postLoadLevel("maps/map01.d2m"); + }; + + //sdwindow.redrawOpenGlScene = delegate () { renderScene(); }; + + enum MSecsPerFrame = 1000/30; /* 30 is FPS */ + + enum { Left, Right, Up, Down } + bool[4] pressed = false; + + sdwindow.eventLoop(MSecsPerFrame, + delegate () { + if (sdwindow.closed) return; + /* + if (pressed[Left]) mapOfsX -= 8; + if (pressed[Right]) mapOfsX += 8; + if (pressed[Up]) mapOfsY -= 8; + if (pressed[Down]) mapOfsY += 8; + import std.math : cos, sin; + __gshared float itime = 0.0; + itime += 0.02; + if (movement) { + mapOfsX = cast(int)(800.0/2.0+cos(itime)*220.0); + mapOfsY = cast(int)(800.0/2.0+120.0+sin(itime)*160.0); + } + if (scale == 1) mapOfsX = mapOfsY = 0; + */ + //sdwindow.redrawOpenGlSceneNow(); + }, + delegate (KeyEvent event) { + if (sdwindow.closed) return; + if (event.pressed && event.key == Key.Escape) { closeWindow(); return; } + /* + switch (event.key) { + case Key.X: if (event.pressed) altMove = !altMove; break; + case Key.Left: if (altMove) pressed[Left] = event.pressed; break; + case Key.Right: if (altMove) pressed[Right] = event.pressed; break; + case Key.Up: if (altMove) pressed[Up] = event.pressed; break; + case Key.Down: if (altMove) pressed[Down] = event.pressed; break; + default: + } + */ + if (true/*!altMove*/) { + switch (event.key) { + case Key.Left: case Key.Pad4: plrKeyUpDown(0, PLK_LEFT, event.pressed); break; + case Key.Right: case Key.Pad6: plrKeyUpDown(0, PLK_RIGHT, event.pressed); break; + case Key.Up: case Key.Pad8: plrKeyUpDown(0, PLK_UP, event.pressed); break; + case Key.Down: case Key.Pad2: plrKeyUpDown(0, PLK_DOWN, event.pressed); break; + case Key.Alt: plrKeyUpDown(0, PLK_JUMP, event.pressed); break; + case Key.Ctrl: plrKeyUpDown(0, PLK_FIRE, event.pressed); break; + case Key.Shift: plrKeyUpDown(0, PLK_USE, event.pressed); break; + default: + } + } + }, + delegate (MouseEvent event) { + /+ + testLightX = event.x/*/scale*/; + testLightY = event.y/*/scale*/; + //testLightX += mapOfsX/scale; + //testLightY += mapOfsY/scale; + +/ + postTestLightMove(event.x, event.y); + }, + delegate (dchar ch) { + if (ch == 'q') { closeWindow(); return; } + //if (ch == 's') scanlines = !scanlines; + if (ch == '1') postSetOption("scale", 1); + if (ch == '2') postSetOption("scale", 2); + if (ch == 'D') postSetOption("cheatNoDoors", true, showMessage:true, toggle:true); + if (ch == 'i') postSetOption("interpolation", true, showMessage:true, toggle:true); + if (ch == 'l') postSetOption("lighting", true, showMessage:true, toggle:true); + //if (ch == ' ') movement = !movement; + }, + ); + } catch (Exception e) { + import std.stdio : stderr; + stderr.writeln("FUUUUUUUUUUUU\n", e.toString); + } + closeWindow(); + flushGui(); +} -- 2.11.4.GIT