forked from DFHack/dfhack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrenderer_light.hpp
More file actions
388 lines (354 loc) · 11.3 KB
/
renderer_light.hpp
File metadata and controls
388 lines (354 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/*
* TODO - important!
* Rendermax needs testing for thread related bugs
* due to the move from tinythread to std::thread and other
* standard library counterparts.
*
* It may also need improvements in regards to thread safety.
*/
#pragma once
#include <condition_variable>
#include <memory>
#include <mutex>
#include <stack>
#include <thread>
#include <tuple>
#include <unordered_map>
#include "renderer_opengl.hpp"
#include "Types.h"
// we are not using boost so let's cheat:
template <class T>
inline void hash_combine(std::size_t & seed, const T & v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
namespace std
{
template<typename S, typename T> struct hash<pair<S, T>>
{
inline size_t operator()(const pair<S, T> & v) const
{
size_t seed = 0;
::hash_combine(seed, v.first);
::hash_combine(seed, v.second);
return seed;
}
};
template<typename S, typename T,typename V> struct hash<tuple<S, T, V>>
{
inline size_t operator()(const tuple<S, T, V> & v) const
{
size_t seed = 0;
::hash_combine(seed,get<0>(v));
::hash_combine(seed,get<1>(v));
::hash_combine(seed,get<2>(v));
return seed;
}
};
}
// now we can hash pairs and tuples
#include "modules/MapCache.h"
bool isInRect(const df::coord2d& pos,const DFHack::rect2d& rect);
struct renderer_light : public renderer_wrap {
private:
float light_adaptation;
rgbf adapt_to_light(const rgbf& light)
{
const float influence=0.0001f;
float intensity=(light.r+light.g+light.b)/3.0;
light_adaptation=intensity*influence+light_adaptation*(1-influence);
float delta=light_adaptation-intensity;
rgbf ret;
ret.r=light.r-delta;
ret.g=light.g-delta;
ret.b=light.b-delta;
return ret;
//if light_adaptation/intensity~1 then draw 1,1,1 (i.e. totally adapted)
/*
1. adapted -> 1,1,1 (full bright everything okay) delta=0 multiplier=?
2. light adapted, real=dark -> darker delta>0 multiplier<1
3. dark adapted, real=light -> lighter delta<0 multiplier>1
*/
//if light_adaptation/intensity!=0 then draw
}
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;
rgbf light=lightGrid[tile];//for light adaptation: rgbf light=adapt_to_light(lightGrid[tile]);
for (int i = 0; i < 6; i++) { //oh how sse would do wonders here, or shaders...
*(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)
{
std::lock_guard<std::mutex> guard{dataMutex};
lightGrid.resize(w*h,rgbf(1,1,1));
}
void reinitLightGrid()
{
reinitLightGrid(df::global::gps->dimy,df::global::gps->dimx);
}
public:
std::mutex dataMutex;
std::vector<rgbf> lightGrid;
renderer_light(renderer* parent):renderer_wrap(parent),light_adaptation(1)
{
reinitLightGrid();
}
virtual void update_tile(int32_t x, int32_t y) {
renderer_wrap::update_tile(x,y);
std::lock_guard<std::mutex> guard{dataMutex};
colorizeTile(x,y);
};
virtual void update_all() {
renderer_wrap::update_all();
std::lock_guard<std::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();
}
virtual void set_fullscreen()
{
renderer_wrap::set_fullscreen();
reinitLightGrid();
}
virtual void zoom(df::zoom_commands z)
{
renderer_wrap::zoom(z);
reinitLightGrid();
}
};
class lightingEngine
{
public:
lightingEngine(renderer_light* target):myRenderer(target){}
virtual ~lightingEngine(){}
virtual void reinit()=0;
virtual void calculate()=0;
virtual void updateWindow()=0;
virtual void preRender()=0;
virtual void loadSettings()=0;
virtual void clear()=0;
virtual void setHour(float h)=0;
virtual void debug(bool enable)=0;
protected:
renderer_light* myRenderer;
};
struct lightSource
{
rgbf power;
int radius;
bool flicker;
lightSource():power(0,0,0),radius(0),flicker(false)
{
}
lightSource(rgbf power,int radius);
float powerSquared()const
{
return power.r*power.r+power.g*power.g+power.b*power.b;
}
void combine(const lightSource& other);
};
struct matLightDef
{
bool isTransparent;
rgbf transparency;
bool isEmiting;
bool flicker;
rgbf emitColor;
int radius;
matLightDef():isTransparent(false),transparency(0,0,0),isEmiting(false),emitColor(0,0,0),radius(0){}
matLightDef(rgbf transparency,rgbf emit,int rad):isTransparent(true),transparency(transparency),
isEmiting(true),emitColor(emit),radius(rad){}
matLightDef(rgbf emit,int rad):isTransparent(false),transparency(0,0,0),isEmiting(true),emitColor(emit),radius(rad){}
matLightDef(rgbf transparency):isTransparent(true),transparency(transparency),isEmiting(false){}
lightSource makeSource(float size=1) const
{
if(size>0.999 && size<1.001)
return lightSource(emitColor,radius);
else
return lightSource(emitColor*size,radius*size);//todo check if this is sane
}
};
struct buildingLightDef
{
matLightDef light;
bool poweredOnly;
bool useMaterial;
float thickness;
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
{
lightingEngineViewscreen *parent;
public:
DFHack::rect2d viewPort;
std::vector<std::unique_ptr<lightThread> > threadPool;
std::vector<lightSource>& lights;
std::mutex occlusionMutex;
std::condition_variable occlusionDone; //all threads wait for occlusion to finish
bool occlusionReady;
std::mutex unprocessedMutex;
std::stack<DFHack::rect2d> unprocessed; //stack of parts of map where lighting is not finished
std::vector<rgbf>& occlusion;
int& num_diffusion;
std::mutex writeLock; //mutex for lightMap
std::vector<rgbf>& lightMap;
std::condition_variable writesDone;
int writeCount;
lightThreadDispatch(lightingEngineViewscreen* p);
~lightThreadDispatch();
void signalDoneOcclusion();
void shutdown();
void waitForWrites();
int getW();
int getH();
void start(int count);
};
class lightThread
{
std::vector<rgbf> canvas;
lightThreadDispatch& dispatch;
DFHack::rect2d myRect;
void work(); //main light calculation function
void combine(); //combine existing canvas into global lightmap
public:
std::thread *myThread;
std::atomic<bool> isDone;
lightThread(lightThreadDispatch& dispatch);
~lightThread();
void run();
private:
void doLight(int x,int y);
void doRay(const rgbf& power,int cx,int cy,int tx,int ty,int num_diffuse);
rgbf lightUpCell(rgbf power,int dx,int dy,int tx,int ty);
};
class lightingEngineViewscreen:public lightingEngine
{
public:
lightingEngineViewscreen(renderer_light* target);
~lightingEngineViewscreen();
void reinit();
void calculate();
void updateWindow();
void preRender();
void loadSettings();
void clear();
void debug(bool enable){doDebug=enable;};
private:
void fixAdvMode(int mode);
df::coord2d worldToViewportCoord(const df::coord2d& in,const DFHack::rect2d& r,const df::coord2d& window2d) ;
void doSun(const lightSource& sky,MapExtras::MapCache& map);
void doOcupancyAndLights();
rgbf propogateSun(MapExtras::Block* b, int x,int y,const rgbf& in,bool lastLevel);
void doRay(std::vector<rgbf> & target, rgbf power,int cx,int cy,int tx,int ty);
void doFovs();
void doLight(std::vector<rgbf> & target, int index);
rgbf lightUpCell(std::vector<rgbf> & target, rgbf power,int dx,int dy,int tx,int ty);
bool addLight(int tileId,const lightSource& light);
void addOclusion(int tileId,const rgbf& c,float thickness);
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.
bool applyMaterial(int tileId,int matType,int matIndex,float size=1,float thickness = 1,const matLightDef* def=NULL);
size_t inline getIndex(int x,int y)
{
return x*h+y;
}
df::coord2d inline getCoords(int index)
{
return df::coord2d(index/h, index%h);
}
//maps
std::vector<rgbf> lightMap;
std::vector<rgbf> ocupancy;
std::vector<lightSource> lights;
//Threading stuff
int num_diffuse; //under same lock as ocupancy
lightThreadDispatch threading;
//misc
void setHour(float h){dayHour=h;};
int getW()const {return w;}
int getH()const {return h;}
public:
void lightWorkerThread(void * arg);
private:
rgbf getSkyColor(float v);
bool doDebug;
//settings
float daySpeed;
float dayHour; //<0 to cycle
std::vector<rgbf> dayColors; // a gradient of colors, first to 0, last to 24
///set up sane settings if setting file does not exist.
void defaultSettings();
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;
matLightDef matAmbience;
matLightDef matCursor;
matLightDef matWall;
matLightDef matWater;
matLightDef matCitizen;
float levelDim;
int adv_mode;
//materials
std::unordered_map<std::pair<int,int>,matLightDef> matDefs;
//buildings
std::unordered_map<std::tuple<int,int,int>,buildingLightDef> buildingDefs;
//creatures
std::unordered_map<std::pair<int,int>,creatureLightDef> creatureDefs;
//items
std::unordered_map<std::pair<int,int>,itemLightDef> itemDefs;
int w,h;
DFHack::rect2d mapPort;
friend class lightThreadDispatch;
};
rgbf blend(const rgbf& a,const rgbf& b);
rgbf blendMax(const rgbf& a,const rgbf& b);