diff --git a/scripts/devel/light.lua b/scripts/devel/light.lua new file mode 100644 index 000000000..4152876ae --- /dev/null +++ b/scripts/devel/light.lua @@ -0,0 +1,238 @@ +-- an experimental lighting engine for df +local gui = require 'gui' +local guidm = require 'gui.dwarfmode' +local render = require 'plugins.rendermax' + +local levelDim=0.05 +local tile_attrs = df.tiletype.attrs + +local args={...} + +function setCell(x,y,cell) + cell=cell or {} + cell.fm=cell.fm or {r=1,g=1,b=1} + cell.bm=cell.bm or {r=1,g=1,b=1} + cell.fo=cell.fo or {r=0,g=0,b=0} + cell.bo=cell.bo or {r=0,g=0,b=0} + render.setCell(x,y,cell) +end +function getCursorPos() + local g_cursor=df.global.cursor + if g_cursor.x ~= -30000 then + return copyall(g_cursor) + end +end +function falloff(color,sqDist,maxdist) + local v1=1/(sqDist/maxdist+1) + local v2=v1-1/(1+maxdist*maxdist) + local v=v2/(1-1/(1+maxdist*maxdist)) + return {r=v*color.r,g=v*color.g,b=v*color.b} +end +function blend(c1,c2) +return {r=math.max(c1.r,c2.r), + g=math.max(c1.g,c2.g), + b=math.max(c1.b,c2.b)} +end +LightOverlay=defclass(LightOverlay,guidm.DwarfOverlay) +LightOverlay.ATTRS { + lightMap={}, + dynamic=true +} +function LightOverlay.init(args) +end + +function lightPassable(shape) + if shape==df.tiletype_shape.WALL or + shape==df.tiletype_shape.BROOK_BED or + shape==df.tiletype_shape.TREE then + return false + else + return true + end +end +function LightOverlay:placeLightFov(pos,radius,color,f,rays) + f=f or falloff + local raycount=rays or 25 + local vp=self:getViewport() + local map = self.df_layout.map + local off=math.random(0,math.pi) + local done={} + for d=0,math.pi*2,math.pi*2/raycount do + local dx,dy + dx=math.cos(d+off) + dy=math.sin(d+off) + local cx=0 + local cy=0 + + for dt=0,radius,0.01 do + if math.abs(math.floor(dt*dx)-cx)>0 or math.abs(math.floor(dt*dy)-cy)> 0then + local x=cx+pos.x + local y=cy+pos.y + + if x>0 and y>0 and x<=map.width and y<=map.height and not done[tile] then + local tile=x+y*map.width + done[tile]=true + local ncol=f(color,dt*dt,radius) + local ocol=self.lightMap[tile] or {r=0,g=0,b=0} + ncol=blend(ncol,ocol) + self.lightMap[tile]=ncol + + + if --(ncol.r==ocol.r and ncol.g==ocol.g and ncol.b==ocol.b) or + not self.ocupancy[tile] then + break + end + end + cx=math.floor(dt*dx) + cy=math.floor(dt*dy) + end + end + end +end +function LightOverlay:placeLight(pos,radius,color,f) + f=f or falloff + local vp=self:getViewport() + local map = self.df_layout.map + + for i=-radius,radius do + for j=-radius,radius do + local x=pos.x+i+1 + local y=pos.y+j+1 + if x>0 and y>0 and x<=map.width and y<=map.height then + local tile=x+y*map.width + local ncol=f(color,(i*i+j*j),radius) + local ocol=self.lightMap[tile] or {r=0,g=0,b=0} + self.lightMap[tile]=blend(ncol,ocol) + end + end + end +end +function LightOverlay:calculateLightLava() + local vp=self:getViewport() + local map = self.df_layout.map + for i=map.x1,map.x2 do + for j=map.y1,map.y2 do + local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} + local pos2={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z-1} + local t1=dfhack.maps.getTileFlags(pos) + local shape=tile_attrs[dfhack.maps.getTileType(pos)].shape + local t2=dfhack.maps.getTileFlags(pos2) + 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) + end + end + end +end +function LightOverlay:calculateLightSun() + local vp=self:getViewport() + local map = self.df_layout.map + for i=map.x1,map.x2+1 do + for j=map.y1,map.y2+1 do + local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} + + local t1=dfhack.maps.getTileFlags(pos) + + if (t1 and t1.outside ) then + + self:placeLightFov({x=i,y=j},7,{r=1,g=1,b=1},nil,3) + end + end + end +end +function LightOverlay:calculateLightCursor() + local c=getCursorPos() + + if c then + + local vp=self:getViewport() + local pos=vp:tileToScreen(c) + --self:placeLight(pos,11,{r=0.96,g=0.84,b=0.03}) + self:placeLightFov({x=pos.x+1,y=pos.y+1},11,{r=0.96,g=0.84,b=0.03}) + + end +end +function LightOverlay:buildOcupancy() + self.ocupancy={} + local vp=self:getViewport() + local map = self.df_layout.map + for i=map.x1,map.x2+1 do + for j=map.y1,map.y2+1 do + local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} + local tile=i+j*map.width + local tt=dfhack.maps.getTileType(pos) + + if tt then + local shape=tile_attrs[tt].shape + self.ocupancy[tile]=lightPassable(shape) + end + end + end + +end +function LightOverlay:makeLightMap() + self.lightMap={} + self:buildOcupancy() + self:calculateLightCursor() + self:calculateLightLava() + self:calculateLightSun() +end +function LightOverlay:onIdle() + self._native.parent:logic() + if self.dynamic then + self:makeLightMap() + end +end +function LightOverlay:render(dc) + + + self:renderParent() + local vp=self:getViewport() + local map = self.df_layout.map + + self.lightMap=self.lightMap or {} + render.lockGrids() + df.global.gps.force_full_display_count=df.global.gps.force_full_display_count+1 + render.resetGrids() + for i=map.x1,map.x2 do + for j=map.y1,map.y2 do + local v=self.lightMap[i+j*map.width] + if v then + setCell(i,j,{fm=v,bm=v}) + else + local dimRgb={r=levelDim,g=levelDim,b=levelDim} + setCell(i,j,{fm=dimRgb,bm=dimRgb}) + end + end + end + render.unlockGrids() + +end +function LightOverlay:onDismiss() + render.lockGrids() + render.resetGrids() + render.unlockGrids() + df.global.gps.force_full_display_count=df.global.gps.force_full_display_count+1 +end +function LightOverlay:onInput(keys) + if keys.LEAVESCREEN then + self:dismiss() + else + self:sendInputToParent(keys) + + if keys.CHANGETAB then + self:updateLayout() + end + if keys.STRING_A126 and not self.dynamic then + self:makeLightMap() + end + end +end +if not render.isEnabled() then + qerror("Lua rendermode not enabled!") +end +local dyn=true +if #args>0 and args[1]=="static" then dyn=false end +local lview = LightOverlay{ dynamic=dyn} +lview:show() \ No newline at end of file