diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 1691cbb22..4509c463d 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -24,6 +24,9 @@ #include "df/building_floodgatest.h" #include "df/plant.h" #include "df/plant_raw.h" +#include "df/item.h" +#include "df/items_other_id.h" +#include "df/unit.h" #include @@ -219,11 +222,11 @@ void plotLineAA(int x0, int y0, int x1, int y1,rgbf power,const std::functioninvalidateRect(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()); } static size_t max_list_size = 100000; // Avoid iterating over huge lists @@ -324,7 +325,7 @@ void addPlant(const std::string& id,std::map& map,const lightSo map[nId]=v; } } -matLightDef* lightingEngineViewscreen::getMaterial(int matType,int matIndex) +matLightDef* lightingEngineViewscreen::getMaterialDef( int matType,int matIndex ) { auto it=matDefs.find(std::make_pair(matType,matIndex)); if(it!=matDefs.end()) @@ -332,7 +333,7 @@ matLightDef* lightingEngineViewscreen::getMaterial(int matType,int matIndex) else return NULL; } -buildingLightDef* lightingEngineViewscreen::getBuilding(df::building* bld) +buildingLightDef* lightingEngineViewscreen::getBuildingDef( df::building* bld ) { auto it=buildingDefs.find(std::make_tuple((int)bld->getType(),(int)bld->getSubtype(),(int)bld->getCustomType())); if(it!=buildingDefs.end()) @@ -340,6 +341,34 @@ buildingLightDef* lightingEngineViewscreen::getBuilding(df::building* bld) else return NULL; } +creatureLightDef* lightingEngineViewscreen::getCreatureDef(df::unit* u) +{ + auto it=creatureDefs.find(std::make_pair(int(u->race),int(u->caste))); + if(it!=creatureDefs.end()) + return &it->second; + else + { + auto it2=creatureDefs.find(std::make_pair(int(u->race),int(-1))); + if(it2!=creatureDefs.end()) + return &it2->second; + else + return NULL; + } +} +itemLightDef* lightingEngineViewscreen::getItemDef(df::item* it) +{ + auto iter=itemDefs.find(std::make_pair(int(it->getType()),int(it->getSubtype()))); + if(iter!=itemDefs.end()) + return &iter->second; + else + { + auto iter2=itemDefs.find(std::make_pair(int(it->getType()),int(-1))); + if(iter2!=itemDefs.end()) + return &iter2->second; + else + return NULL; + } +} void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,float size, float thickness) { if(mat.isTransparent) @@ -353,7 +382,7 @@ void lightingEngineViewscreen::applyMaterial(int tileId,const matLightDef& mat,f } bool lightingEngineViewscreen::applyMaterial(int tileId,int matType,int matIndex,float size,float thickness,const matLightDef* def) { - matLightDef* m=getMaterial(matType,matIndex); + matLightDef* m=getMaterialDef(matType,matIndex); if(m) { applyMaterial(tileId,*m,size,thickness); @@ -392,7 +421,7 @@ rgbf lightingEngineViewscreen::propogateSun(MapExtras::Block* b, int x,int y,con ret*=matIce.transparency.pow(1.0f/7.0f); } - lightDef=getMaterial(mat.mat_type,mat.mat_index); + lightDef=getMaterialDef(mat.mat_type,mat.mat_index); if(!lightDef || !lightDef->isTransparent) lightDef=&matWall; @@ -502,8 +531,6 @@ rgbf lightingEngineViewscreen::getSkyColor(float v) } void lightingEngineViewscreen::doOcupancyAndLights() { - // TODO better curve (+red dawn ?) - float daycol; if(dayHour<0) { @@ -515,11 +542,6 @@ void lightingEngineViewscreen::doOcupancyAndLights() rgbf sky_col=getSkyColor(daycol); lightSource sky(sky_col, -1);//auto calculate best size - - lightSource candle(rgbf(0.96f,0.84f,0.03f),5); - lightSource torch(rgbf(0.9f,0.75f,0.3f),8); - - //perfectly blocking material MapExtras::MapCache cache; doSun(sky,cache); @@ -543,7 +565,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() MapExtras::Block* bDown=cache.BlockAt(DFCoord(blockX,blockY,window_z-1)); if(!b) continue; //empty blocks fixed by sun propagation - + for(int block_x = 0; block_x < 16; block_x++) for(int block_y = 0; block_y < 16; block_y++) { @@ -573,7 +595,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() DFHack::t_matpair mat=b->staticMaterialAt(gpos); - matLightDef* lightDef=getMaterial(mat.mat_type,mat.mat_index); + matLightDef* lightDef=getMaterialDef(mat.mat_type,mat.mat_index); if(!lightDef || !lightDef->isTransparent) lightDef=&matWall; if(shape==df::tiletype_shape::BROOK_BED ) @@ -643,6 +665,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() } } } + //plants for(int i=0;iplants.size();i++) { @@ -665,7 +688,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(ev_type==df::block_square_event_type::material_spatter) { df::block_square_event_material_spatterst* spatter=static_cast(ev); - matLightDef* m=getMaterial(spatter->mat_type,spatter->mat_index); + matLightDef* m=getMaterialDef(spatter->mat_type,spatter->mat_index); if(!m) continue; if(!m->isEmiting) @@ -696,15 +719,47 @@ void lightingEngineViewscreen::doOcupancyAndLights() applyMaterial(tile,matCursor); } //citizen only emit light, if defined - if(matCitizen.isEmiting) + //or other creatures + if(matCitizen.isEmiting || creatureDefs.size()>0) for (int i=0;iunits.active.size();++i) { df::unit *u = df::global::world->units.active[i]; coord2d pos=worldToViewportCoord(coord2d(u->pos.x,u->pos.y),vp,window2d); if(u->pos.z==window_z && isInRect(pos,vp)) - if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) - addLight(getIndex(pos.x,pos.y),matCitizen.makeSource()); + { + if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) + addLight(getIndex(pos.x,pos.y),matCitizen.makeSource()); + creatureLightDef *def=getCreatureDef(u); + if(def && !u->flags1.bits.dead) + { + addLight(getIndex(pos.x,pos.y),def->light.makeSource()); + } + } + } + //items + if(itemDefs.size()>0) + { + std::vector& vec=df::global::world->items.other[items_other_id::IN_PLAY]; + for(size_t i=0;iequiped || mat->haul ||mat->inBuilding ||mat->inContainer) && curItem->flags.bits.in_inventory)|| //TODO split this up + (mat->onGround && curItem->flags.bits.on_ground) ) + { + if(mat->light.isEmiting) + addLight(getIndex(pos.x,pos.y),mat->light.makeSource()); + if(!mat->light.isTransparent) + addOclusion(getIndex(pos.x,pos.y),mat->light.transparency,1); + } + } + } } + //buildings for(size_t i = 0; i < df::global::world->buildings.all.size(); i++) { @@ -728,7 +783,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() else tile=getIndex(p2.x,p2.y); df::building_type type = bld->getType(); - buildingLightDef* def=getBuilding(bld); + buildingLightDef* def=getBuildingDef(bld); if(!def) continue; if(type==df::enums::building_type::Door) @@ -747,7 +802,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(def->useMaterial) { - matLightDef* mat=getMaterial(bld->mat_type,bld->mat_index); + matLightDef* mat=getMaterialDef(bld->mat_type,bld->mat_index); if(!mat)mat=&matWall; if(!def->poweredOnly || !bld->isUnpowered()) //not powered. Add occlusion only. { @@ -906,6 +961,72 @@ int lightingEngineViewscreen::parseSpecial(lua_State* L) return 0; } #undef LOAD_SPECIAL +int lightingEngineViewscreen::parseItems(lua_State* L) +{ + auto engine= (lightingEngineViewscreen*)lua_touserdata(L, 1); + engine->itemDefs.clear(); + Lua::StackUnwinder unwinder(L); + lua_getfield(L,2,"items"); + if(!lua_istable(L,-1)) + { + luaL_error(L,"Items table not found."); + return 0; + } + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if(!lua_istable(L,-1)) + luaL_error(L,"Broken item definitions."); + lua_getfield(L,-1,"type"); + int type=lua_tonumber(L,-1); + lua_pop(L,1); + lua_getfield(L,-1,"subtype"); + int subtype=luaL_optinteger(L,-1,-1); + lua_pop(L,1); + itemLightDef item; + lua_getfield(L,-1,"light"); + item.light=lua_parseMatDef(L); + GETLUAFLAG(item.haul,"hauling"); + GETLUAFLAG(item.equiped,"equiped"); + GETLUAFLAG(item.inBuilding,"inBuilding"); + GETLUAFLAG(item.inContainer,"contained"); + GETLUAFLAG(item.onGround,"onGround"); + GETLUAFLAG(item.useMaterial,"useMaterial"); + engine->itemDefs[std::make_pair(type,subtype)]=item; + lua_pop(L,2); + } + lua_pop(L,1); + return 0; +} +int lightingEngineViewscreen::parseCreatures(lua_State* L) +{ + auto engine= (lightingEngineViewscreen*)lua_touserdata(L, 1); + engine->creatureDefs.clear(); + Lua::StackUnwinder unwinder(L); + lua_getfield(L,2,"creatures"); + if(!lua_istable(L,-1)) + { + luaL_error(L,"Creatures table not found."); + return 0; + } + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if(!lua_istable(L,-1)) + luaL_error(L,"Broken creature definitions."); + lua_getfield(L,-1,"race"); + int race=lua_tonumber(L,-1); + lua_pop(L,1); + lua_getfield(L,-1,"caste"); + int caste=lua_tonumber(L,-1); + lua_pop(L,1); + creatureLightDef cr; + lua_getfield(L,-1,"light"); + cr.light=lua_parseMatDef(L); + engine->creatureDefs[std::make_pair(race,caste)]=cr; + lua_pop(L,2); + } + lua_pop(L,1); + return 0; +} int lightingEngineViewscreen::parseBuildings(lua_State* L) { auto engine= (lightingEngineViewscreen*)lua_touserdata(L, 1); @@ -1018,6 +1139,18 @@ void lightingEngineViewscreen::loadSettings() lua_pushvalue(s,env); Lua::SafeCall(out,s,2,0); out.print("%d buildings loaded\n",buildingDefs.size()); + + lua_pushcfunction(s, parseCreatures); + lua_pushlightuserdata(s, this); + lua_pushvalue(s,env); + Lua::SafeCall(out,s,2,0); + out.print("%d creatures loaded\n",creatureDefs.size()); + + lua_pushcfunction(s, parseItems); + lua_pushlightuserdata(s, this); + lua_pushvalue(s,env); + Lua::SafeCall(out,s,2,0); + out.print("%d items loaded\n",itemDefs.size()); } } @@ -1147,7 +1280,7 @@ rgbf lightThread::lightUpCell(rgbf power,int dx,int dy,int tx,int ty) else return rgbf(); } -void lightThread::doRay(rgbf power,int cx,int cy,int tx,int ty) +void lightThread::doRay(const rgbf& power,int cx,int cy,int tx,int ty) { using namespace std::placeholders; diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 5e768c27d..75fb5ab6c 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -166,6 +166,22 @@ struct buildingLightDef float size; buildingLightDef():poweredOnly(false),useMaterial(true),thickness(1.0f),size(1.0f){} }; +struct itemLightDef +{ + matLightDef light; + bool haul; + bool equiped; + bool onGround; + bool inBuilding; + bool inContainer; + bool useMaterial; + itemLightDef():haul(true),equiped(true),onGround(true),inBuilding(false),inContainer(false),useMaterial(true){} +}; +struct creatureLightDef +{ + matLightDef light; + +}; class lightThread; class lightingEngineViewscreen; class lightThreadDispatch @@ -215,7 +231,7 @@ public: void run(); private: void doLight(int x,int y); - void doRay(rgbf power,int cx,int cy,int tx,int ty); + void doRay(const rgbf& power,int cx,int cy,int tx,int ty); rgbf lightUpCell(rgbf power,int dx,int dy,int tx,int ty); }; class lightingEngineViewscreen:public lightingEngine @@ -247,9 +263,11 @@ private: bool addLight(int tileId,const lightSource& light); void addOclusion(int tileId,const rgbf& c,float thickness); - matLightDef* getMaterial(int matType,int matIndex); - buildingLightDef* getBuilding(df::building* bld); - + matLightDef* getMaterialDef(int matType,int matIndex); + buildingLightDef* getBuildingDef(df::building* bld); + creatureLightDef* getCreatureDef(df::unit* u); + itemLightDef* getItemDef(df::item* it); + //apply material to cell void applyMaterial(int tileId,const matLightDef& mat,float size=1, float thickness = 1); //try to find and apply material, if failed return false, and if def!=null then apply def. @@ -291,6 +309,8 @@ private: static int parseMaterials(lua_State* L); static int parseSpecial(lua_State* L); static int parseBuildings(lua_State* L); + static int parseItems(lua_State* L); + static int parseCreatures(lua_State* L); //special stuff matLightDef matLava; matLightDef matIce; @@ -304,10 +324,14 @@ private: std::unordered_map,matLightDef> matDefs; //buildings std::unordered_map,buildingLightDef> buildingDefs; + //creatures + std::unordered_map,creatureLightDef> creatureDefs; + //items + std::unordered_map,itemLightDef> itemDefs; int w,h; DFHack::rect2d mapPort; friend lightThreadDispatch; }; -rgbf blend(rgbf a,rgbf b); -rgbf blendMax(rgbf a,rgbf b); +rgbf blend(const rgbf& a,const rgbf& b); +rgbf blendMax(const rgbf& a,const rgbf& b); #endif diff --git a/plugins/rendermax/rendermax.lua b/plugins/rendermax/rendermax.lua index c5e7c21a7..24b5c7fc8 100644 --- a/plugins/rendermax/rendermax.lua +++ b/plugins/rendermax/rendermax.lua @@ -4,6 +4,8 @@ ret=ret[1] ret.materials={} ret.buildings={} ret.special={} +ret.items={} +ret.creatures={} for k,v in pairs(ret) do _ENV[k]=v end @@ -28,7 +30,7 @@ end function buildingLookUp(id) local tokens={} local lookup={ Workshop=df.workshop_type,Furnace=df.furnace_type,Trap=df.trap_type, - SiegeEngine=siegeengine_type} + SiegeEngine=df.siegeengine_type} for i in string.gmatch(id, "[^:]+") do table.insert(tokens,i) end @@ -44,21 +46,86 @@ function buildingLookUp(id) for k,v in pairs(df.global.world.raws.buildings.workshops) do if v.code==tokens[3] then ret.custom=v.id - break + return ret end end elseif ret.type==df.building_type.Furnace then for k,v in pairs(df.global.world.raws.buildings.furnaces) do if v.code==tokens[3] then ret.custom=v.id - break + return ret end end end end + qerror("Invalid custom building:"..tokens[3]) end return ret end +function itemLookup(id) + local ret={} + local tokens={} + for i in string.gmatch(id, "[^:]+") do + table.insert(tokens,i) + end + ret.type=df.item_type[tokens[1]] + ret.subtype=-1 + if tokens[2] then + for k,v in ipairs(df.global.world.raws.itemdefs.all) do --todo lookup correct itemdef + if v.id==tokens[2] then + ret.subtype=v.subtype + return ret + end + end + qerror("Failed item subtype lookup:"..tokens[2]) + end + return ret +end +function creatureLookup(id) + local ret={} + local tokens={} + for i in string.gmatch(id, "[^:]+") do + table.insert(tokens,i) + end + for k,v in ipairs(df.global.world.raws.creatures.all) do + if v.creature_id==tokens[1] then + ret.type=k + if tokens[2] then + for k,v in ipairs(v.caste) do + if v.caste_id==tokens[2] then + ret.subtype=k + break + end + end + if ret.subtype==nil then + qerror("caste "..tokens[2].." for "..tokens[1].." not found") + end + end + return ret + end + end + qerror("Failed to find race:"..tokens[1]) +end +-- add creature by id ("DWARF" or "DWARF:MALE") +-- supported flags: +function addCreature(id,transparency,emitance,radius,flags) + local crId=creatureLookup(id) + local mat=makeMaterialDef(transparency,emitance,radius,flags) + table.insert(creatures,{race=crId.type,caste=crId.subtype or -1, light=mat}) +end +-- add item by id ( "TOTEM" or "WEAPON:PICK" or "WEAPON" for all the weapon types) +-- supported flags: +-- hauling --active when hauled TODO::currently all mean same thing... +-- equiped --active while equiped TODO::currently all mean same thing... +-- inBuilding --active in building TODO::currently all mean same thing... +-- contained --active in container TODO::currently all mean same thing... +-- onGround --active on ground +-- useMaterial --uses material, but the defined things overwrite +function addItem(id,transparency,emitance,radius,flags) + local itemId=itemLookup(id) + local mat=makeMaterialDef(transparency,emitance,radius,flags) + table.insert(items,{["type"]=itemId.type,subtype=itemId.subtype,light=mat}) +end -- add building by id (string e.g. "Statue" or "Workshop:Masons", flags is a table of strings -- supported flags: -- useMaterial --uses material, but the defined things overwrite @@ -147,3 +214,9 @@ addBuilding("WindowGlass",nil,nil,0,{"useMaterial"}) addBuilding("WindowGem",nil,nil,0,{"useMaterial"}) addBuilding("Door",nil,nil,0,{"useMaterial"}) -- special case, only closed door obstruct/emit light addBuilding("Floodgate",nil,nil,0,{"useMaterial"}) -- special case, only closed door obstruct/emit light +--creatures +addCreature("ELEMENTMAN_MAGMA",{0.8,0.2,0.2},{0.8,0.2,0.2},5) +--items +addItem("GEM",nil,nil,{"useMaterial","onGround"}) +addItem("ROUGH",nil,nil,{"useMaterial","onGround"}) +addItem("SMALLGEM",nil,nil,{"useMaterial","onGround"}) \ No newline at end of file