develop
Japa 2013-06-23 22:05:02 +05:30
commit 140371a0c7
6 changed files with 485 additions and 18 deletions

@ -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)

@ -0,0 +1,267 @@
#include "renderer_light.hpp"
#include <functional>
#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_pos<area_pos || df::global::ui->main.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<void(int,int)> 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<bool(int,int,int,int)> setPixel)
{
int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
int err = dx+dy, e2; /* error value e_xy */
int rdx=0;
int rdy=0;
for(;;){ /* loop */
if(!setPixel(rdx,rdy,x0,y0))
return;
if (x0==x1 && y0==y1) break;
e2 = 2*err;
rdx=rdy=0;
if (e2 >= 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<lights.size();i++)
{
lightSource& csource=lights[i];
plotCircle(csource.pos.x,csource.pos.y,csource.radius,std::bind(&lightingEngineViewscreen::doRay,this,csource.power,csource.pos.x,csource.pos.y,_1,_2));
}
}
void lightingEngineViewscreen::calculate()
{
rect2d vp=getMapViewport();
const lightCell dim(levelDim,levelDim,levelDim);
lightMap.assign(lightMap.size(),lightCell(1,1,1));
for(int i=vp.first.x;i<vp.second.x;i++)
for(int j=vp.first.y;j<vp.second.y;j++)
{
lightMap[getIndex(i,j)]=dim;
}
doOcupancyAndLights();
doFovs();
//for each lightsource in viewscreen+x do light
}
void lightingEngineViewscreen::updateWindow()
{
tthread::lock_guard<tthread::fast_mutex> 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;x<window_x+vpW;x++)
for(int y=window_y;y<window_y+vpH;y++)
{
int wx=x-window_x+vp.first.x;
int wy=y-window_y+vp.first.y;
lightCell& curCell=ocupancy[getIndex(wx,wy)];
curCell=lightCell(0.8f,0.8f,0.8f);
df::tiletype* type = Maps::getTileType(x,y,window_z);
if(!type)
continue;
df::tiletype_shape shape = ENUM_ATTR(tiletype,shape,*type);
df::tile_designation* d=Maps::getTileDesignation(x,y,window_z);
df::tile_designation* d2=Maps::getTileDesignation(x,y,window_z-1);
df::tile_occupancy* o=Maps::getTileOccupancy(x,y,window_z);
if(!o || !d )
continue;
if(shape==df::tiletype_shape::BROOK_BED || shape==df::tiletype_shape::WALL || shape==df::tiletype_shape::TREE || o->bits.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;i<block->flows.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);
}
}
}
}
}

@ -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<old_opengl*>(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<tthread::fast_mutex> guard(dataMutex);
lightGrid.resize(w*h);
}
void reinitLightGrid()
{
reinitLightGrid(df::global::gps->dimy,df::global::gps->dimx);
}
public:
tthread::fast_mutex dataMutex;
std::vector<lightCell> 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<tthread::fast_mutex> guard(dataMutex);
colorizeTile(x,y);
};
virtual void update_all() {
renderer_wrap::update_all();
tthread::lock_guard<tthread::fast_mutex> 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<lightCell> lightMap;
std::vector<lightCell> ocupancy;
std::vector<lightSource> lights;
int w,h;
DFHack::rect2d mapPort;
};
#endif

@ -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;j<y+h;j++)
{
int index=i*df::global::gps->dimy + 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);
}
};
};
#endif

@ -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 <string> & parameters);
DFHACK_PLUGIN("rendermax");
@ -34,6 +37,7 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
" rendermax trippy\n"
" rendermax truecolor red|green|blue|white\n"
" rendermax lua\n"
" rendermax light\n"
" rendermax disable\n"
));
return CR_OK;
@ -43,6 +47,12 @@ void removeOld()
if(current_mode!=MODE_DEFAULT)
delete df::global::enabler->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 <string> & 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 <string> & 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 <string> & 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;
}

@ -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