From 4ab750e9f3cbe5cfc895c31553447e89484e2cbc Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 13:28:22 +0300 Subject: [PATCH 1/2] light.lua: more help, changed screen exit key to '`'. Made it recalculate only when needed. --- scripts/devel/light.lua | 68 +++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/scripts/devel/light.lua b/scripts/devel/light.lua index f3d39e4db..48324d797 100644 --- a/scripts/devel/light.lua +++ b/scripts/devel/light.lua @@ -1,4 +1,4 @@ --- an experimental lighting engine for df +-- an experimental lighting engine for df. param: "static" to not recalc when in game. press "~" to recalculate. "`" to exit local gui = require 'gui' local guidm = require 'gui.dwarfmode' local render = require 'plugins.rendermax' @@ -36,9 +36,12 @@ end LightOverlay=defclass(LightOverlay,guidm.DwarfOverlay) LightOverlay.ATTRS { lightMap={}, - dynamic=true + dynamic=true, + dirty=false, } -function LightOverlay.init(args) +function LightOverlay:init(args) + + self.tick=df.global.cur_year_tick_advmode end function lightPassable(shape) @@ -102,8 +105,14 @@ function line(x0, y0, x1, y1,plot) end end end -function LightOverlay:placeLightFov(pos,radius,color,f) - f=f or falloff +function LightOverlay:calculateFovs() + self.fovs=self.fovs or {} + self.precalc=self.precalc or {} + for k,v in ipairs(self.fovs) do + self:calculateFov(v.pos,v.radius,v.color) + end +end +function LightOverlay:calculateFov(pos,radius,color) local vp=self:getViewport() local map = self.df_layout.map local ray=function(tx,ty) @@ -115,9 +124,16 @@ function LightOverlay:placeLightFov(pos,radius,color,f) local dtsq=(lx-x)*(lx-x)+(ly-y)*(ly-y) local dt=math.sqrt(dtsq) local tile=x+y*map.width - + if self.precalc[tile] then + local tcol=blend(self.precalc[tile],power) + if tcol.r==self.precalc[tile].r and tcol.g==self.precalc[tile].g and self.precalc[tile].b==self.precalc[tile].b + and dtsq>0 then + return false + end + end local ocol=self.lightMap[tile] or {r=0,g=0,b=0} local ncol=blend(power,ocol) + self.lightMap[tile]=ncol local v=self.ocupancy[tile] if dtsq>0 then @@ -136,6 +152,17 @@ function LightOverlay:placeLightFov(pos,radius,color,f) end circle(pos.x,pos.y,radius,ray) end +function LightOverlay:placeLightFov(pos,radius,color) + local map = self.df_layout.map + local tile=pos.x+pos.y*map.width + local ocol=self.precalc[tile] or {r=0,g=0,b=0} + local ncol=blend(color,ocol) + self.precalc[tile]=ncol + local ocol=self.lightMap[tile] or {r=0,g=0,b=0} + local ncol=blend(color,ocol) + self.lightMap[tile]=ncol + table.insert(self.fovs,{pos=pos,radius=radius,color=color}) +end function LightOverlay:placeLightFov2(pos,radius,color,f,rays) f=f or falloff local raycount=rays or 25 @@ -208,7 +235,7 @@ function LightOverlay:calculateLightLava() if (t1 and t1.liquid_type and t1.flow_size>0) or (shape==df.tiletype_shape.EMPTY and t2 and t2.liquid_type and t2.flow_size>0) then --self:placeLight({x=i,y=j},5,{r=0.8,g=0.2,b=0.2}) - self:placeLightFov({x=i,y=j},5,{r=0.8,g=0.2,b=0.2},nil,5) + self:placeLightFov({x=i,y=j},5,{r=0.8,g=0.2,b=0.2},nil) end end end @@ -225,7 +252,7 @@ function LightOverlay:calculateLightSun() if (t1 and t1.outside ) then - self:placeLightFov({x=i,y=j},7,{r=1,g=1,b=1},nil,3) + self:placeLightFov({x=i,y=j},15,{r=1,g=1,b=1},nil) end end end @@ -267,22 +294,36 @@ function LightOverlay:buildOcupancy() end end end +function LightOverlay:changed() + if self.dirty or self.tick~=df.global.cur_year_tick_advmode then + self.dirty=false + self.tick=df.global.cur_year_tick_advmode + return true + end + return false +end function LightOverlay:makeLightMap() + if not self:changed() then + return + end + self.fovs={} + self.precalc={} self.lightMap={} + self:buildOcupancy() self:calculateLightCursor() self:calculateLightLava() self:calculateLightSun() + + self:calculateFovs() end function LightOverlay:onIdle() self._native.parent:logic() +end +function LightOverlay:render(dc) if self.dynamic then self:makeLightMap() end -end -function LightOverlay:render(dc) - - self:renderParent() local vp=self:getViewport() local map = self.df_layout.map @@ -313,7 +354,7 @@ function LightOverlay:onDismiss() end function LightOverlay:onInput(keys) - if keys.LEAVESCREEN then + if keys.STRING_A096 then self:dismiss() else self:sendInputToParent(keys) @@ -324,6 +365,7 @@ function LightOverlay:onInput(keys) if keys.STRING_A126 and not self.dynamic then self:makeLightMap() end + self.dirty=true end end if not render.isEnabled() then From 2c49c9e83c9ec81bb832a708facc1815b92f324a Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Jun 2013 19:25:42 +0300 Subject: [PATCH 2/2] C++ version of light engine. --- plugins/rendermax/CMakeLists.txt | 2 + plugins/rendermax/renderer_light.cpp | 267 ++++++++++++++++++++++++++ plugins/rendermax/renderer_light.hpp | 109 +++++++++++ plugins/rendermax/renderer_opengl.hpp | 11 +- plugins/rendermax/rendermax.cpp | 46 ++++- 5 files changed, 430 insertions(+), 5 deletions(-) create mode 100644 plugins/rendermax/renderer_light.cpp create mode 100644 plugins/rendermax/renderer_light.hpp diff --git a/plugins/rendermax/CMakeLists.txt b/plugins/rendermax/CMakeLists.txt index a7422a2cd..fb8cc7724 100644 --- a/plugins/rendermax/CMakeLists.txt +++ b/plugins/rendermax/CMakeLists.txt @@ -3,10 +3,12 @@ PROJECT (rendermax) # A list of source files SET(PROJECT_SRCS rendermax.cpp + renderer_light.cpp ) # A list of headers SET(PROJECT_HDRS renderer_opengl.hpp + renderer_light.hpp ) SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp new file mode 100644 index 000000000..7ff122a37 --- /dev/null +++ b/plugins/rendermax/renderer_light.cpp @@ -0,0 +1,267 @@ +#include "renderer_light.hpp" + +#include + +#include "Types.h" + +#include "modules/Gui.h" +#include "modules/Screen.h" +#include "modules/Maps.h" + +#include "df/graphic.h" +#include "df/viewscreen_dwarfmodest.h" +#include "df/flow_info.h" + +using df::global::gps; +using namespace DFHack; +using df::coord2d; + +const float levelDim=0.2f; + +rect2d getMapViewport() +{ + const int AREA_MAP_WIDTH = 23; + const int MENU_WIDTH = 30; + if(!gps || !df::viewscreen_dwarfmodest::_identity.is_instance(DFHack::Gui::getCurViewscreen())) + return mkrect_wh(0,0,0,0); + int w=gps->dimx; + int h=gps->dimy; + int view_height=h-2; + int area_x2 = w-AREA_MAP_WIDTH-2; + int menu_x2=w-MENU_WIDTH-2; + int menu_x1=area_x2-MENU_WIDTH-1; + int view_rb=w-1; + + int area_pos=*df::global::ui_area_map_width; + int menu_pos=*df::global::ui_menu_width; + if(area_pos<3) + { + view_rb=area_x2; + } + if (menu_posmain.mode!=0) + { + if (menu_pos >= area_pos) + menu_pos = area_pos-1; + int menu_x = menu_x2; + if(menu_pos < 2) menu_x = menu_x1; + view_rb = menu_x; + } + return mkrect_wh(1,1,view_rb,view_height+1); +} +lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target) +{ + reinit(); +} + +void lightingEngineViewscreen::reinit() +{ + if(!gps) + return; + w=gps->dimx; + h=gps->dimy; + size_t size=w*h; + lightMap.resize(size,lightCell(1,1,1)); + ocupancy.resize(size); +} +void plotCircle(int xm, int ym, int r,std::function setPixel) +{ + int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */ + do { + setPixel(xm-x, ym+y); /* I. Quadrant */ + setPixel(xm-y, ym-x); /* II. Quadrant */ + setPixel(xm+x, ym-y); /* III. Quadrant */ + setPixel(xm+y, ym+x); /* IV. Quadrant */ + r = err; + if (r <= y) err += ++y*2+1; /* e_xy+e_y < 0 */ + if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */ + } while (x < 0); +} +void plotLine(int x0, int y0, int x1, int y1,std::function setPixel) +{ + int dx = abs(x1-x0), sx = x0= dy) { err += dy; x0 += sx; rdx=sx;} /* e_xy+e_x > 0 */ + if (e2 <= dx) { err += dx; y0 += sy; rdy=sy;} /* e_xy+e_y < 0 */ + } +} +lightCell blend(lightCell a,lightCell b) +{ + return lightCell(std::max(a.r,b.r),std::max(a.g,b.g),std::max(a.b,b.b)); +} +bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx,int ty) +{ + + if(tx>=mapPort.first.x && ty>=mapPort.first.y && tx<=mapPort.second.x && ty<=mapPort.second.y) + { + size_t tile=getIndex(tx,ty); + float dsq=dx*dx+dy*dy; + lightCell& v=ocupancy[tile]; + bool wallhack=false; + bool outsidehack=false; + if(v.r+v.g+v.b==0) + wallhack=true; + if(v.r<0) + outsidehack=true; + if (dsq>0 && !wallhack && !outsidehack) + { + power.r=power.r*(pow(v.r,dsq)); + power.g=power.g*(pow(v.g,dsq)); + power.b=power.b*(pow(v.b,dsq)); + } + //float dt=sqrt(dsq); + lightCell oldCol=lightMap[tile]; + lightCell ncol=blend(power,oldCol); + lightMap[tile]=ncol; + + if(wallhack) + return false; + if(dsq>0 && outsidehack) + return false; + float pwsq=power.r*power.r+power.g*power.g+power.b*power.b; + return pwsq>levelDim*levelDim; + } + else + return false; +} +void lightingEngineViewscreen::doRay(lightCell power,int cx,int cy,int tx,int ty) +{ + using namespace std::placeholders; + lightCell curPower=power; + plotLine(cx,cy,tx,ty,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(curPower),_1,_2,_3,_4)); +} +void lightingEngineViewscreen::doFovs() +{ + mapPort=getMapViewport(); + using namespace std::placeholders; + for(size_t i=0;i guard(myRenderer->dataMutex); + if(lightMap.size()!=myRenderer->lightGrid.size()) + { + reinit(); + return; + } + std::swap(lightMap,myRenderer->lightGrid); + rect2d vp=getMapViewport(); + + //myRenderer->invalidateRect(vp.first.x,vp.first.y,vp.second.x-vp.first.x,vp.second.y-vp.first.y); + myRenderer->invalidate(); + //std::copy(lightMap.begin(),lightMap.end(),myRenderer->lightGrid.begin()); +} +void lightingEngineViewscreen::doOcupancyAndLights() +{ + lights.clear(); + rect2d vp=getMapViewport(); + + int window_x=*df::global::window_x; + int window_y=*df::global::window_y; + int window_z=*df::global::window_z; + int vpW=vp.second.x-vp.first.x; + int vpH=vp.second.y-vp.first.y; + for(int x=window_x;xbits.building) + { + curCell=lightCell(0,0,0); + } + else if(!d->bits.liquid_type && d->bits.flow_size>3 ) + { + curCell=lightCell(0.5f,0.5f,0.6f); + } + //todo constructions + + //lights + if((d->bits.liquid_type && d->bits.flow_size>0)|| (d2 && d2->bits.liquid_type && d2->bits.flow_size>0)) + { + lightSource lava={lightCell(0.8f,0.2f,0.2f),5,coord2d(wx,wy)}; + lights.push_back(lava); + } + if(d->bits.outside) + { + lightSource sun={lightCell(1,1,1),25,coord2d(wx,wy)}; + lights.push_back(sun); + curCell=lightCell(-1,-1,-1);//Marking as outside so no calculation is done on it + } + + } + + for(int blockx=window_x/16;blockx<(window_x+vpW)/16;blockx++) + for(int blocky=window_y/16;blocky<(window_x+vpW)/16;blocky++) + { + df::map_block* block=Maps::getBlock(blockx,blocky,window_z); + if(!block) + continue; + for(int i=0;iflows.size();i++) + { + df::flow_info* f=block->flows[i]; + if(f && f->density>0 && f->type==df::flow_type::Dragonfire || f->type==df::flow_type::Fire) + { + df::coord2d pos=f->pos; + int wx=pos.x-window_x+vp.first.x; + int wy=pos.y-window_y+vp.first.y; + if(wx>=vp.first.x && wy>=vp.first.y && wx<=vp.second.x && wy<=vp.second.y) + { + lightCell fireColor; + if(f->density>60) + { + fireColor=lightCell(0.98f,0.91f,0.30f); + } + else if(f->density>30) + { + fireColor=lightCell(0.93f,0.16f,0.16f); + } + else + { + fireColor=lightCell(0.64f,0.0f,0.0f); + } + lightSource fire={fireColor,f->density/5,coord2d(wx,wy)}; + lights.push_back(fire); + } + } + } + } +} \ No newline at end of file diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp new file mode 100644 index 000000000..d0279fd02 --- /dev/null +++ b/plugins/rendermax/renderer_light.hpp @@ -0,0 +1,109 @@ +#ifndef RENDERER_LIGHT_INCLUDED +#define RENDERER_LIGHT_INCLUDED +#include "renderer_opengl.hpp" +#include "Types.h" + +struct renderer_light : public renderer_wrap { +private: + void colorizeTile(int x,int y) + { + const int tile = x*(df::global::gps->dimy) + y; + old_opengl* p=reinterpret_cast(parent); + float *fg = p->fg + tile * 4 * 6; + float *bg = p->bg + tile * 4 * 6; + float *tex = p->tex + tile * 2 * 6; + lightCell light=lightGrid[tile]; + for (int i = 0; i < 6; i++) { + *(fg++) *= light.r; + *(fg++) *= light.g; + *(fg++) *= light.b; + *(fg++) = 1; + + *(bg++) *= light.r; + *(bg++) *= light.g; + *(bg++) *= light.b; + *(bg++) = 1; + } + } + void reinitLightGrid(int w,int h) + { + tthread::lock_guard guard(dataMutex); + lightGrid.resize(w*h); + } + void reinitLightGrid() + { + reinitLightGrid(df::global::gps->dimy,df::global::gps->dimx); + } +public: + tthread::fast_mutex dataMutex; + std::vector lightGrid; + renderer_light(renderer* parent):renderer_wrap(parent) + { + reinitLightGrid(); + } + virtual void update_tile(int32_t x, int32_t y) { + renderer_wrap::update_tile(x,y); + tthread::lock_guard guard(dataMutex); + colorizeTile(x,y); + }; + virtual void update_all() { + renderer_wrap::update_all(); + tthread::lock_guard guard(dataMutex); + for (int x = 0; x < df::global::gps->dimx; x++) + for (int y = 0; y < df::global::gps->dimy; y++) + colorizeTile(x,y); + }; + virtual void grid_resize(int32_t w, int32_t h) { + renderer_wrap::grid_resize(w,h); + reinitLightGrid(w,h); + }; + virtual void resize(int32_t w, int32_t h) { + renderer_wrap::resize(w,h); + reinitLightGrid(w,h); + } +}; +class lightingEngine +{ +public: + lightingEngine(renderer_light* target):myRenderer(target){} + + virtual void reinit()=0; + virtual void calculate()=0; + + virtual void updateWindow()=0; + +protected: + renderer_light* myRenderer; +}; +struct lightSource +{ + lightCell power; + int radius; + df::coord2d pos; +}; +class lightingEngineViewscreen:public lightingEngine +{ +public: + lightingEngineViewscreen(renderer_light* target); + + void reinit(); + void calculate(); + + void updateWindow(); + +private: + void doOcupancyAndLights(); + void doRay(lightCell power,int cx,int cy,int tx,int ty); + void doFovs(); + bool lightUpCell(lightCell& power,int dx,int dy,int tx,int ty); + size_t inline getIndex(int x,int y) + { + return x*h+y; + } + std::vector lightMap; + std::vector ocupancy; + std::vector lights; + int w,h; + DFHack::rect2d mapPort; +}; +#endif \ No newline at end of file diff --git a/plugins/rendermax/renderer_opengl.hpp b/plugins/rendermax/renderer_opengl.hpp index c6d9d2e63..6b9e5ebed 100644 --- a/plugins/rendermax/renderer_opengl.hpp +++ b/plugins/rendermax/renderer_opengl.hpp @@ -1,4 +1,7 @@ //original file from https://github.com/Baughn/Dwarf-Fortress--libgraphics- +#ifndef RENDERER_OPENGL_INCLUDED +#define RENDERER_OPENGL_INCLUDED + #include "tinythread.h" #include "fast_mutex.h" @@ -120,12 +123,13 @@ public: for(int j=y;jdimy + j; - screen_old[index*4]=screen[index*4]+1; + screen_old[index*4]=0; } }; void invalidate() { - invalidateRect(0,0,df::global::gps->dimx,df::global::gps->dimy); + //invalidateRect(0,0,df::global::gps->dimx,df::global::gps->dimy); + df::global::gps->force_full_display_count++; }; protected: renderer* parent; @@ -346,4 +350,5 @@ public: renderer_wrap::resize(w,h); reinitGrids(w,h); } -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index 190e5f421..0a61d1e4a 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -13,15 +13,18 @@ #include "df/enabler.h" #include "renderer_opengl.hpp" +#include "renderer_light.hpp" using namespace DFHack; using std::vector; using std::string; enum RENDERER_MODE { - MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR,MODE_LUA + MODE_DEFAULT,MODE_TRIPPY,MODE_TRUECOLOR,MODE_LUA,MODE_LIGHT }; RENDERER_MODE current_mode=MODE_DEFAULT; +lightingEngine *engine=NULL; + static command_result rendermax(color_ostream &out, vector & parameters); DFHACK_PLUGIN("rendermax"); @@ -34,6 +37,7 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector renderer; current_mode=MODE_DEFAULT; + if(current_mode==MODE_LIGHT) + { + if(engine) + delete engine; + engine=0; + } } void installNew(df::renderer* r,RENDERER_MODE newMode) { @@ -249,7 +259,7 @@ static command_result rendermax(color_ostream &out, vector & parameters { lightCell red(1,0,0),green(0,1,0),blue(0,0,1),white(1,1,1); lightCell cur=white; - lightCell dim(0.2,0.2,0.2); + lightCell dim(0.2f,0.2f,0.2f); string col=parameters[1]; if(col=="red") cur=red; @@ -293,6 +303,24 @@ static command_result rendermax(color_ostream &out, vector & parameters unlockGrids(); return CR_OK; } + else if(cmd=="light") + { + if(current_mode==MODE_LIGHT) + { + engine->calculate(); + engine->updateWindow(); + } + else + { + removeOld(); + renderer_light *myRender=new renderer_light(df::global::enabler->renderer); + installNew(myRender,MODE_LIGHT); + engine=new lightingEngineViewscreen(myRender); + engine->calculate(); + engine->updateWindow(); + } + return CR_OK; + } else if(cmd=="disable") { if(current_mode==MODE_DEFAULT) @@ -303,4 +331,18 @@ static command_result rendermax(color_ostream &out, vector & parameters return CR_OK; } return CR_WRONG_USAGE; +} +DFhackCExport command_result plugin_onupdate (color_ostream &out) +{ + if(engine) + { + engine->calculate(); + engine->updateWindow(); + } + return CR_OK; +} +DFhackCExport command_result plugin_shutdown(color_ostream &) +{ + removeOld(); + return CR_OK; } \ No newline at end of file