1 #include "engine.h"
2
3 #define MAXLIGHTMAPTASKS 4096
4 #define LIGHTMAPBUFSIZE (2*1024*1024)
5
6 struct lightmapinfo;
7 struct lightmaptask;
8
9 struct lightmapworker
10 {
11 uchar *buf;
12 int bufstart, bufused;
13 lightmapinfo *firstlightmap, *lastlightmap, *curlightmaps;
14 cube *c;
15 cubeext *ext;
16 uchar *colorbuf;
17 bvec *raybuf;
18 uchar *ambient, *blur;
19 vec *colordata, *raydata;
20 int type, bpp, w, h, orient, rotate;
21 VSlot *vslot;
22 Slot *slot;
23 vector<const extentity *> lights;
24 ShadowRayCache *shadowraycache;
25 BlendMapCache *blendmapcache;
26 bool needspace, doneworking;
27 SDL_cond *spacecond;
28 SDL_Thread *thread;
29
30 lightmapworker();
31 ~lightmapworker();
32
33 void reset();
34 bool setupthread();
35 void cleanupthread();
36
37 static int work(void *data);
38 };
39
40 struct lightmapinfo
41 {
42 lightmapinfo *next;
43 cube *c;
44 uchar *colorbuf;
45 bvec *raybuf;
46 bool packed;
47 int type, w, h, bpp, bufsize, surface, layers;
48 };
49
50 struct lightmaptask
51 {
52 ivec o;
53 int size, usefaces, progress;
54 cube *c;
55 cubeext *ext;
56 lightmapinfo *lightmaps;
57 lightmapworker *worker;
58 };
59
60 struct lightmapext
61 {
62 cube *c;
63 cubeext *ext;
64 };
65
66 static vector<lightmapworker *> lightmapworkers;
67 static vector<lightmaptask> lightmaptasks[2];
68 static vector<lightmapext> lightmapexts;
69 static int packidx = 0, allocidx = 0;
70 static SDL_mutex *lightlock = NULL, *tasklock = NULL;
71 static SDL_cond *fullcond = NULL, *emptycond = NULL;
72 static vector<const extentity *> sunlights;
73
74 int lightmapping = 0;
75
76 vector<LightMap> lightmaps;
77
78 int curlightprecision = 32;
79 VAR(IDF_WORLD, lightprecision, 1, 32, 2048);
80 VAR(IDF_WORLD, lightprecisionquick, 1, 2048, 2048);
81
82 VAR(IDF_WORLD, lighterror, 1, 8, 16);
83 VAR(IDF_WORLD, bumperror, 1, 3, 16);
84 VAR(IDF_WORLD, lightlod, 0, 0, 10);
85 bvec ambientcolor(25, 25, 25), skylightcolor(0, 0, 0);
86 VARF(IDF_HEX|IDF_WORLD, ambient, 0, 0x191919, 0xFFFFFF,
87 {
88 if(ambient <= 255) ambient |= (ambient<<8) | (ambient<<16);
89 ambientcolor = bvec((ambient>>16)&0xFF, (ambient>>8)&0xFF, ambient&0xFF);
90 });
91 VARF(IDF_HEX|IDF_WORLD, skylight, 0, 0, 0xFFFFFF,
92 {
93 if(skylight <= 255) skylight |= (skylight<<8) | (skylight<<16);
94 skylightcolor = bvec((skylight>>16)&0xFF, (skylight>>8)&0xFF, skylight&0xFF);
95 });
96
97 static const surfaceinfo brightsurfaces[6] =
98 {
99 brightsurface,
100 brightsurface,
101 brightsurface,
102 brightsurface,
103 brightsurface,
104 brightsurface
105 };
106
brightencube(cube & c)107 void brightencube(cube &c)
108 {
109 if(!c.ext) newcubeext(c, 0, false);
110 memcpy(c.ext->surfaces, brightsurfaces, sizeof(brightsurfaces));
111 }
112
setsurfaces(cube & c,const surfaceinfo * surfs,const vertinfo * verts,int numverts)113 void setsurfaces(cube &c, const surfaceinfo *surfs, const vertinfo *verts, int numverts)
114 {
115 if(!c.ext || c.ext->maxverts < numverts) newcubeext(c, numverts, false);
116 memcpy(c.ext->surfaces, surfs, sizeof(c.ext->surfaces));
117 memcpy(c.ext->verts(), verts, numverts*sizeof(vertinfo));
118 }
119
setsurface(cube & c,int orient,const surfaceinfo & src,const vertinfo * srcverts,int numsrcverts)120 void setsurface(cube &c, int orient, const surfaceinfo &src, const vertinfo *srcverts, int numsrcverts)
121 {
122 int dstoffset = 0;
123 if(!c.ext) newcubeext(c, numsrcverts, true);
124 else
125 {
126 int numbefore = 0, beforeoffset = 0;
127 loopi(orient)
128 {
129 surfaceinfo &surf = c.ext->surfaces[i];
130 int numverts = surf.totalverts();
131 if(!numverts) continue;
132 numbefore += numverts;
133 beforeoffset = surf.verts + numverts;
134 }
135 int numafter = 0, afteroffset = c.ext->maxverts;
136 for(int i = 5; i > orient; i--)
137 {
138 surfaceinfo &surf = c.ext->surfaces[i];
139 int numverts = surf.totalverts();
140 if(!numverts) continue;
141 numafter += numverts;
142 afteroffset = surf.verts;
143 }
144 if(afteroffset - beforeoffset >= numsrcverts) dstoffset = beforeoffset;
145 else
146 {
147 cubeext *ext = c.ext;
148 if(numbefore + numsrcverts + numafter > c.ext->maxverts)
149 {
150 ext = growcubeext(c.ext, numbefore + numsrcverts + numafter);
151 memcpy(ext->surfaces, c.ext->surfaces, sizeof(ext->surfaces));
152 }
153 int offset = 0;
154 if(numbefore == beforeoffset)
155 {
156 if(numbefore && c.ext != ext) memcpy(ext->verts(), c.ext->verts(), numbefore*sizeof(vertinfo));
157 offset = numbefore;
158 }
159 else loopi(orient)
160 {
161 surfaceinfo &surf = ext->surfaces[i];
162 int numverts = surf.totalverts();
163 if(!numverts) continue;
164 memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
165 surf.verts = offset;
166 offset += numverts;
167 }
168 dstoffset = offset;
169 offset += numsrcverts;
170 if(numafter && offset > afteroffset)
171 {
172 offset += numafter;
173 for(int i = 5; i > orient; i--)
174 {
175 surfaceinfo &surf = ext->surfaces[i];
176 int numverts = surf.totalverts();
177 if(!numverts) continue;
178 offset -= numverts;
179 memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
180 surf.verts = offset;
181 }
182 }
183 if(c.ext != ext) setcubeext(c, ext);
184 }
185 }
186 surfaceinfo &dst = c.ext->surfaces[orient];
187 dst = src;
188 dst.verts = dstoffset;
189 if(srcverts) memcpy(c.ext->verts() + dstoffset, srcverts, numsrcverts*sizeof(vertinfo));
190 }
191
192 VARN(IDF_WORLD, lmshadows, lmshadows_, 0, 2, 2);
193 VARN(IDF_WORLD, lmaa, lmaa_, 0, 3, 3);
194 VARN(IDF_WORLD, lerptjoints, lerptjoints_, 0, 1, 1);
195 static int lmshadows = 2, lmaa = 3, lerptjoints = 1;
196 static uint lmprog = 0, taskprogress = 0;
197 GLuint lmprogtex = 0;
198 static int lmprogtexticks = 0, lmprogid = -1;
199
200 bool calclight_canceled = false;
201 volatile bool check_calclight_lmprog = false;
202
check_calclight_canceled()203 void check_calclight_canceled()
204 {
205 if(interceptkey(SDLK_ESCAPE))
206 {
207 calclight_canceled = true;
208 loopv(lightmapworkers) lightmapworkers[i]->doneworking = true;
209 }
210 if(!calclight_canceled) check_calclight_lmprog = false;
211 }
212
show_calclight_lmprog()213 void show_calclight_lmprog()
214 {
215 float bar1 = float(lmprog) / float(allocnodes);
216
217 defformatstring(text, "%d textures used", lightmaps.length());
218
219 if(LM_PACKW <= hwtexsize && !lmprogtex)
220 {
221 glGenTextures(1, &lmprogtex);
222 createtexture(lmprogtex, LM_PACKW, LM_PACKH, NULL, 3, 1, GL_RGB);
223 }
224 // only update once a sec (4 * 250 ms ticks) to not kill performance
225 if(lmprogtex && !calclight_canceled && lmprogid >= 0 && !(lmprogtexticks++ % 4))
226 {
227 if(tasklock) SDL_LockMutex(tasklock);
228 LightMap &lm = lightmaps[lmprogid];
229 uchar *data = lm.data;
230 int bpp = lm.bpp;
231 if(tasklock) SDL_UnlockMutex(tasklock);
232 glBindTexture(GL_TEXTURE_2D, lmprogtex);
233 glPixelStorei(GL_UNPACK_ALIGNMENT, texalign(data, LM_PACKW, bpp));
234 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LM_PACKW, LM_PACKH, bpp > 3 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
235 }
236 progress(bar1, "computing lightmaps..", bar1, text);//, lmprogtexticks ? lmprogtex : 0);
237 }
238
239 #define CHECK_PROGRESS_LOCKED(exit, before, after) CHECK_CALCLIGHT_PROGRESS_LOCKED(exit, show_calclight_lmprog, before, after)
240 #define CHECK_PROGRESS(exit) CHECK_PROGRESS_LOCKED(exit, , )
241
insert(ushort & tx,ushort & ty,ushort tw,ushort th)242 bool PackNode::insert(ushort &tx, ushort &ty, ushort tw, ushort th)
243 {
244 if((available < tw && available < th) || w < tw || h < th)
245 return false;
246 if(child1)
247 {
248 bool inserted = child1->insert(tx, ty, tw, th) ||
249 child2->insert(tx, ty, tw, th);
250 available = max(child1->available, child2->available);
251 if(!available) clear();
252 return inserted;
253 }
254 if(w == tw && h == th)
255 {
256 available = 0;
257 tx = x;
258 ty = y;
259 return true;
260 }
261
262 if(w - tw > h - th)
263 {
264 child1 = new PackNode(x, y, tw, h);
265 child2 = new PackNode(x + tw, y, w - tw, h);
266 }
267 else
268 {
269 child1 = new PackNode(x, y, w, th);
270 child2 = new PackNode(x, y + th, w, h - th);
271 }
272
273 bool inserted = child1->insert(tx, ty, tw, th);
274 available = max(child1->available, child2->available);
275 return inserted;
276 }
277
insert(ushort & tx,ushort & ty,uchar * src,ushort tw,ushort th)278 bool LightMap::insert(ushort &tx, ushort &ty, uchar *src, ushort tw, ushort th)
279 {
280 if((type&LM_TYPE) != LM_BUMPMAP1 && !packroot.insert(tx, ty, tw, th))
281 return false;
282
283 copy(tx, ty, src, tw, th);
284 return true;
285 }
286
copy(ushort tx,ushort ty,uchar * src,ushort tw,ushort th)287 void LightMap::copy(ushort tx, ushort ty, uchar *src, ushort tw, ushort th)
288 {
289 uchar *dst = data + bpp * tx + ty * bpp * LM_PACKW;
290 loopi(th)
291 {
292 memcpy(dst, src, bpp * tw);
293 dst += bpp * LM_PACKW;
294 src += bpp * tw;
295 }
296 ++lightmaps;
297 lumels += tw * th;
298 }
299
insertunlit(int i)300 static void insertunlit(int i)
301 {
302 LightMap &l = lightmaps[i];
303 if((l.type&LM_TYPE) == LM_BUMPMAP1)
304 {
305 l.unlitx = l.unlity = -1;
306 return;
307 }
308 ushort x, y;
309 uchar unlit[4] = { ambientcolor[0], ambientcolor[1], ambientcolor[2], 255 };
310 if(l.insert(x, y, unlit, 1, 1))
311 {
312 if((l.type&LM_TYPE) == LM_BUMPMAP0)
313 {
314 bvec front(128, 128, 255);
315 ASSERT(lightmaps[i+1].insert(x, y, front.v, 1, 1));
316 }
317 l.unlitx = x;
318 l.unlity = y;
319 }
320 }
321
322 struct layoutinfo
323 {
324 ushort x, y, lmid;
325 uchar w, h;
326 };
327
insertlightmap(lightmapinfo & li,layoutinfo & si)328 static void insertlightmap(lightmapinfo &li, layoutinfo &si)
329 {
330 loopv(lightmaps)
331 {
332 if(lightmaps[i].type == li.type && lightmaps[i].insert(si.x, si.y, li.colorbuf, si.w, si.h))
333 {
334 si.lmid = i + LMID_RESERVED;
335 if((li.type&LM_TYPE) == LM_BUMPMAP0) ASSERT(lightmaps[i+1].insert(si.x, si.y, (uchar *)li.raybuf, si.w, si.h));
336 return;
337 }
338 }
339
340 lmprogid = lightmaps.length();
341
342 si.lmid = lightmaps.length() + LMID_RESERVED;
343 LightMap &l = lightmaps.add();
344 l.type = li.type;
345 l.bpp = li.bpp;
346 l.data = new uchar[li.bpp*LM_PACKW*LM_PACKH];
347 memset(l.data, 0, li.bpp*LM_PACKW*LM_PACKH);
348 ASSERT(l.insert(si.x, si.y, li.colorbuf, si.w, si.h));
349 if((li.type&LM_TYPE) == LM_BUMPMAP0)
350 {
351 LightMap &r = lightmaps.add();
352 r.type = LM_BUMPMAP1 | (li.type&~LM_TYPE);
353 r.bpp = 3;
354 r.data = new uchar[3*LM_PACKW*LM_PACKH];
355 memset(r.data, 0, 3*LM_PACKW*LM_PACKH);
356 ASSERT(r.insert(si.x, si.y, (uchar *)li.raybuf, si.w, si.h));
357 }
358 }
359
copylightmap(lightmapinfo & li,layoutinfo & si)360 static void copylightmap(lightmapinfo &li, layoutinfo &si)
361 {
362 lightmaps[si.lmid-LMID_RESERVED].copy(si.x, si.y, li.colorbuf, si.w, si.h);
363 if((li.type&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(si.lmid+1-LMID_RESERVED))
364 lightmaps[si.lmid+1-LMID_RESERVED].copy(si.x, si.y, (uchar *)li.raybuf, si.w, si.h);
365 }
366
htcmp(const lightmapinfo & k,const layoutinfo & v)367 static inline bool htcmp(const lightmapinfo &k, const layoutinfo &v)
368 {
369 int kw = k.w, kh = k.h;
370 if(kw != v.w || kh != v.h) return false;
371 LightMap &vlm = lightmaps[v.lmid - LMID_RESERVED];
372 int ktype = k.type;
373 if(ktype != vlm.type) return false;
374 int kbpp = k.bpp;
375 const uchar *kcolor = k.colorbuf, *vcolor = vlm.data + kbpp*(v.x + v.y*LM_PACKW);
376 loopi(kh)
377 {
378 if(memcmp(kcolor, vcolor, kbpp*kw)) return false;
379 kcolor += kbpp*kw;
380 vcolor += kbpp*LM_PACKW;
381 }
382 if((ktype&LM_TYPE) != LM_BUMPMAP0) return true;
383 const bvec *kdir = k.raybuf, *vdir = (const bvec *)lightmaps[v.lmid+1 - LMID_RESERVED].data;
384 loopi(kh)
385 {
386 if(memcmp(kdir, vdir, kw*sizeof(bvec))) return false;
387 kdir += kw;
388 vdir += LM_PACKW;
389 }
390 return true;
391 }
392
hthash(const lightmapinfo & k)393 static inline uint hthash(const lightmapinfo &k)
394 {
395 int kw = k.w, kh = k.h, kbpp = k.bpp;
396 uint hash = kw + (kh<<8);
397 const uchar *color = k.colorbuf;
398 loopi(kw*kh)
399 {
400 hash ^= color[0] + (color[1] << 4) + (color[2] << 8);
401 color += kbpp;
402 }
403 return hash;
404 }
405
406 static hashset<layoutinfo> compressed;
407
408 VAR(IDF_WORLD, lightcompress, 0, 3, 6);
409
packlightmap(lightmapinfo & l,layoutinfo & surface)410 static bool packlightmap(lightmapinfo &l, layoutinfo &surface)
411 {
412 surface.w = l.w;
413 surface.h = l.h;
414 if((int)l.w <= lightcompress && (int)l.h <= lightcompress)
415 {
416 layoutinfo *val = compressed.access(l);
417 if(!val)
418 {
419 insertlightmap(l, surface);
420 compressed[l] = surface;
421 }
422 else
423 {
424 surface.x = val->x;
425 surface.y = val->y;
426 surface.lmid = val->lmid;
427 return false;
428 }
429 }
430 else insertlightmap(l, surface);
431 return true;
432 }
433
updatelightmap(const layoutinfo & surface)434 static void updatelightmap(const layoutinfo &surface)
435 {
436 if(max(LM_PACKW, LM_PACKH) > hwtexsize) return;
437
438 LightMap &lm = lightmaps[surface.lmid-LMID_RESERVED];
439 if(lm.tex < 0)
440 {
441 lm.offsetx = lm.offsety = 0;
442 lm.tex = lightmaptexs.length();
443 LightMapTexture &tex = lightmaptexs.add();
444 tex.type = lm.type;
445 tex.w = LM_PACKW;
446 tex.h = LM_PACKH;
447 tex.unlitx = lm.unlitx;
448 tex.unlity = lm.unlity;
449 glGenTextures(1, &tex.id);
450 createtexture(tex.id, tex.w, tex.h, NULL, 3, 1, tex.type&LM_ALPHA ? GL_RGBA : GL_RGB);
451 if((lm.type&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(surface.lmid+1-LMID_RESERVED))
452 {
453 LightMap &lm2 = lightmaps[surface.lmid+1-LMID_RESERVED];
454 lm2.offsetx = lm2.offsety = 0;
455 lm2.tex = lightmaptexs.length();
456 LightMapTexture &tex2 = lightmaptexs.add();
457 tex2.type = (lm.type&~LM_TYPE) | LM_BUMPMAP0;
458 tex2.w = LM_PACKW;
459 tex2.h = LM_PACKH;
460 tex2.unlitx = lm2.unlitx;
461 tex2.unlity = lm2.unlity;
462 glGenTextures(1, &tex2.id);
463 createtexture(tex2.id, tex2.w, tex2.h, NULL, 3, 1, GL_RGB);
464 }
465 }
466
467 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
468 glPixelStorei(GL_UNPACK_ROW_LENGTH, LM_PACKW);
469
470 glBindTexture(GL_TEXTURE_2D, lightmaptexs[lm.tex].id);
471 glTexSubImage2D(GL_TEXTURE_2D, 0, lm.offsetx + surface.x, lm.offsety + surface.y, surface.w, surface.h, lm.type&LM_ALPHA ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, &lm.data[(surface.y*LM_PACKW + surface.x)*lm.bpp]);
472 if((lm.type&LM_TYPE)==LM_BUMPMAP0 && lightmaps.inrange(surface.lmid+1-LMID_RESERVED))
473 {
474 LightMap &lm2 = lightmaps[surface.lmid+1-LMID_RESERVED];
475 glBindTexture(GL_TEXTURE_2D, lightmaptexs[lm2.tex].id);
476 glTexSubImage2D(GL_TEXTURE_2D, 0, lm2.offsetx + surface.x, lm2.offsety + surface.y, surface.w, surface.h, GL_RGB, GL_UNSIGNED_BYTE, &lm2.data[(surface.y*LM_PACKW + surface.x)*3]);
477 }
478
479 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
480 }
481
generatelumel(lightmapworker * w,const float tolerance,uint lightmask,const vector<const extentity * > & lights,const vec & target,const vec & normal,vec & sample,int x,int y)482 static uint generatelumel(lightmapworker *w, const float tolerance, uint lightmask, const vector<const extentity *> &lights, const vec &target, const vec &normal, vec &sample, int x, int y)
483 {
484 vec avgray(0, 0, 0);
485 float r = 0, g = 0, b = 0;
486 uint lightused = 0;
487 loopv(lights)
488 {
489 if(lightmask&(1<<i)) continue;
490 const extentity &light = *lights[i];
491 vec ray = target;
492 ray.sub(light.o);
493 float mag = ray.magnitude();
494 if(!mag) continue;
495 float attenuation = 1;
496 if(light.attrs[0])
497 {
498 attenuation -= mag / float(light.attrs[0]);
499 if(attenuation <= 0) continue;
500 }
501 ray.mul(1.0f / mag);
502 float angle = -ray.dot(normal);
503 if(angle <= 0) continue;
504 if(!light.links.empty())
505 {
506 int slight = -1;
507 const vector<extentity *> &ents = entities::getents();
508 loopvk(light.links)
509 {
510 if(ents.inrange(light.links[k]) && ents[light.links[k]]->type == ET_LIGHTFX && ents[light.links[k]]->attrs[0] == LFX_SPOTLIGHT)
511 {
512 slight = light.links[k];
513 break;
514 }
515 }
516 if(ents.inrange(slight))
517 {
518 extentity &spotlight = *ents[slight];
519 vec spot = vec(spotlight.o).sub(light.o).normalize();
520 float maxatten = sincos360[clamp(int(spotlight.attrs[1]), 1, 89)].x, spotatten = (ray.dot(spot) - maxatten) / (1 - maxatten);
521 if(spotatten <= 0) continue;
522 attenuation *= spotatten;
523 }
524 else continue;
525 }
526 if(lmshadows && mag)
527 {
528 float dist = shadowray(w->shadowraycache, light.o, ray, mag - tolerance, RAY_SHADOW | (lmshadows > 1 ? RAY_ALPHAPOLY : 0));
529 if(dist < mag - tolerance) continue;
530 }
531 lightused |= 1<<i;
532 float intensity;
533 switch(w->type&LM_TYPE)
534 {
535 case LM_BUMPMAP0:
536 intensity = attenuation;
537 avgray.add(ray.mul(-attenuation));
538 break;
539 default:
540 intensity = angle * attenuation;
541 break;
542 }
543 r += intensity * float(light.attrs[1]);
544 g += intensity * float(light.attrs[2]);
545 b += intensity * float(light.attrs[3]);
546 }
547 switch(w->type&LM_TYPE)
548 {
549 case LM_BUMPMAP0:
550 if(avgray.iszero()) break;
551 // transform to tangent space
552 extern vec orientation_tangent[6][3];
553 extern vec orientation_bitangent[6][3];
554 vec S(orientation_tangent[w->rotate][dimension(w->orient)]),
555 T(orientation_bitangent[w->rotate][dimension(w->orient)]);
556 normal.orthonormalize(S, T);
557 avgray.normalize();
558 w->raydata[y*w->w+x].add(vec(S.dot(avgray)/S.magnitude(), T.dot(avgray)/T.magnitude(), normal.dot(avgray)));
559 break;
560 }
561 sample.x = min(255.0f, max(r, float(ambientcolor[0])));
562 sample.y = min(255.0f, max(g, float(ambientcolor[1])));
563 sample.z = min(255.0f, max(b, float(ambientcolor[2])));
564 return lightused;
565 }
566
lumelsample(const vec & sample,int aasample,int stride)567 static bool lumelsample(const vec &sample, int aasample, int stride)
568 {
569 if(sample.x >= int(ambientcolor[0])+1 || sample.y >= int(ambientcolor[1])+1 || sample.z >= int(ambientcolor[2])+1) return true;
570 #define NCHECK(n) \
571 if((n).x >= int(ambientcolor[0])+1 || (n).y >= int(ambientcolor[1])+1 || (n).z >= int(ambientcolor[2])+1) \
572 return true;
573 const vec *n = &sample - stride - aasample;
574 NCHECK(n[0]); NCHECK(n[aasample]); NCHECK(n[2*aasample]);
575 n += stride;
576 NCHECK(n[0]); NCHECK(n[2*aasample]);
577 n += stride;
578 NCHECK(n[0]); NCHECK(n[aasample]); NCHECK(n[2*aasample]);
579 return false;
580 }
581
582 VAR(IDF_WORLD, skytexturelight, 0, 1, 1);
583
calcskylight(lightmapworker * w,const vec & o,const vec & normal,float tolerance,uchar * slight,int flags=RAY_ALPHAPOLY,extentity * t=NULL)584 static void calcskylight(lightmapworker *w, const vec &o, const vec &normal, float tolerance, uchar *slight, int flags = RAY_ALPHAPOLY, extentity *t = NULL)
585 {
586 static const vec rays[17] =
587 {
588 vec(cosf(21*RAD)*cosf(50*RAD), sinf(21*RAD)*cosf(50*RAD), sinf(50*RAD)),
589 vec(cosf(111*RAD)*cosf(50*RAD), sinf(111*RAD)*cosf(50*RAD), sinf(50*RAD)),
590 vec(cosf(201*RAD)*cosf(50*RAD), sinf(201*RAD)*cosf(50*RAD), sinf(50*RAD)),
591 vec(cosf(291*RAD)*cosf(50*RAD), sinf(291*RAD)*cosf(50*RAD), sinf(50*RAD)),
592
593 vec(cosf(66*RAD)*cosf(70*RAD), sinf(66*RAD)*cosf(70*RAD), sinf(70*RAD)),
594 vec(cosf(156*RAD)*cosf(70*RAD), sinf(156*RAD)*cosf(70*RAD), sinf(70*RAD)),
595 vec(cosf(246*RAD)*cosf(70*RAD), sinf(246*RAD)*cosf(70*RAD), sinf(70*RAD)),
596 vec(cosf(336*RAD)*cosf(70*RAD), sinf(336*RAD)*cosf(70*RAD), sinf(70*RAD)),
597
598 vec(0, 0, 1),
599
600 vec(cosf(43*RAD)*cosf(60*RAD), sinf(43*RAD)*cosf(60*RAD), sinf(60*RAD)),
601 vec(cosf(133*RAD)*cosf(60*RAD), sinf(133*RAD)*cosf(60*RAD), sinf(60*RAD)),
602 vec(cosf(223*RAD)*cosf(60*RAD), sinf(223*RAD)*cosf(60*RAD), sinf(60*RAD)),
603 vec(cosf(313*RAD)*cosf(60*RAD), sinf(313*RAD)*cosf(60*RAD), sinf(60*RAD)),
604
605 vec(cosf(88*RAD)*cosf(80*RAD), sinf(88*RAD)*cosf(80*RAD), sinf(80*RAD)),
606 vec(cosf(178*RAD)*cosf(80*RAD), sinf(178*RAD)*cosf(80*RAD), sinf(80*RAD)),
607 vec(cosf(268*RAD)*cosf(80*RAD), sinf(268*RAD)*cosf(80*RAD), sinf(80*RAD)),
608 vec(cosf(358*RAD)*cosf(80*RAD), sinf(358*RAD)*cosf(80*RAD), sinf(80*RAD)),
609
610 };
611 flags |= RAY_SHADOW;
612 if(skytexturelight) flags |= RAY_SKIPSKY;
613 int hit = 0;
614 if(w) loopi(17)
615 {
616 if(normal.dot(rays[i])>=0 && shadowray(w->shadowraycache, vec(rays[i]).mul(tolerance).add(o), rays[i], 1e16f, flags, t)>1e15f) hit++;
617 }
618 else loopi(17)
619 {
620 if(normal.dot(rays[i])>=0 && shadowray(vec(rays[i]).mul(tolerance).add(o), rays[i], 1e16f, flags, t)>1e15f) hit++;
621 }
622
623 loopk(3) slight[k] = uchar(ambientcolor[k] + (max(skylightcolor[k], ambientcolor[k]) - ambientcolor[k])*hit/17.0f);
624 }
625
calcsunlight(lightmapworker * w,const vec & o,const vec & normal,float tolerance,uchar * slight,int flags=RAY_ALPHAPOLY,extentity * t=NULL)626 void calcsunlight(lightmapworker *w, const vec &o, const vec &normal, float tolerance, uchar *slight, int flags = RAY_ALPHAPOLY, extentity *t = NULL)
627 {
628 flags |= RAY_SHADOW;
629 if(skytexturelight) flags |= RAY_SKIPSKY;
630 loopv(sunlights) if(sunlights[i])
631 {
632 const extentity &light = *sunlights[i];
633 if(light.attrs.length() < 5 || (slight[0] >= light.attrs[2] && slight[1] >= light.attrs[3] && slight[2] >= light.attrs[4])) continue;
634 int yaw = light.attrs[0], pitch = light.attrs[1]+90,
635 offset = light.attrs.inrange(5) && light.attrs[5] ? (light.attrs[5] > 0 ? light.attrs[5] : 0) : 10, hit = 0;
636 vec dir(yaw*RAD, pitch*RAD);
637 if(normal.dot(dir) >= 0 &&
638 (w ? shadowray(w->shadowraycache, vec(dir).mul(tolerance).add(o), dir, 1e16f, flags, t) > 1e15f :
639 shadowray(vec(dir).mul(tolerance).add(o), dir, 1e16f, flags, t) > 1e15f))
640 hit++;
641 if(offset)
642 {
643 matrix3 rot(90*RAD, dir);
644 vec spoke(yaw*RAD, (pitch + offset)*RAD);
645 spoke.rotate(21*RAD, dir);
646 loopk(4)
647 {
648 if(normal.dot(spoke) >= 0 &&
649 (w ? shadowray(w->shadowraycache, vec(spoke).mul(tolerance).add(o), spoke, 1e16f, flags, t) > 1e15f :
650 shadowray(vec(spoke).mul(tolerance).add(o), spoke, 1e16f, flags, t) > 1e15f))
651 hit++;
652 spoke = rot.transform(spoke);
653 }
654 spoke = vec(yaw*RAD, (pitch + 0.5f*offset)*RAD).rotate((66-21)*RAD, dir);
655 loopk(4)
656 {
657 if(normal.dot(spoke) >= 0 &&
658 (w ? shadowray(w->shadowraycache, vec(spoke).mul(tolerance).add(o), spoke, 1e16f, flags, t) > 1e15f :
659 shadowray(vec(spoke).mul(tolerance).add(o), spoke, 1e16f, flags, t) > 1e15f))
660 hit++;
661 spoke = rot.transform(spoke);
662 }
663 loopk(3) slight[k] = max(uchar(light.attrs[2+k]*hit/9.f), slight[k]);
664 }
665 else if(hit) loopk(3) slight[k] = max(uchar(light.attrs[2+k]), slight[k]);
666 }
667 }
668
hasskylight()669 static inline bool hasskylight()
670 {
671 return skylightcolor[0]>ambientcolor[0] || skylightcolor[1]>ambientcolor[1] || skylightcolor[2]>ambientcolor[2];
672 }
673
674 VAR(IDF_WORLD, blurlms, 0, 0, 2);
675 VAR(IDF_WORLD, blurskylight, 0, 0, 2);
676
generatealpha(lightmapworker * w,float tolerance,const vec & pos,uchar & alpha)677 static inline void generatealpha(lightmapworker *w, float tolerance, const vec &pos, uchar &alpha)
678 {
679 alpha = lookupblendmap(w->blendmapcache, pos);
680 if(w->slot->layermask)
681 {
682 static const int sdim[] = { 1, 0, 0 }, tdim[] = { 2, 2, 1 };
683 int dim = dimension(w->orient);
684 float k = 8.0f/w->vslot->scale,
685 s = (pos[sdim[dim]] * k - w->vslot->offset.x) / w->slot->layermaskscale,
686 t = (pos[tdim[dim]] * (dim <= 1 ? -k : k) - w->vslot->offset.y) / w->slot->layermaskscale;
687 if((w->rotate&5)==1) swap(s, t);
688 if(w->rotate>=2 && w->rotate<=4) s = -s;
689 if((w->rotate>=1 && w->rotate<=2) || w->rotate==5) t = -t;
690 const ImageData &mask = *w->slot->layermask;
691 int mx = int(floor(s))%mask.w, my = int(floor(t))%mask.h;
692 if(mx < 0) mx += mask.w;
693 if(my < 0) my += mask.h;
694 uchar maskval = mask.data[mask.bpp*(mx + 1) - 1 + mask.pitch*my];
695 switch(w->slot->layermaskmode)
696 {
697 case 2: alpha = min(alpha, maskval); break;
698 case 3: alpha = max(alpha, maskval); break;
699 case 4: alpha = min(alpha, uchar(0xFF - maskval)); break;
700 case 5: alpha = max(alpha, uchar(0xFF - maskval)); break;
701 default: alpha = maskval; break;
702 }
703 }
704 }
705
706 VAR(IDF_WORLD, edgetolerance, 1, 4, 64);
707 VAR(IDF_WORLD, adaptivesample, 0, 2, 2);
708
709 enum
710 {
711 NO_SURFACE = 0,
712 SURFACE_AMBIENT_BOTTOM,
713 SURFACE_AMBIENT_TOP,
714 SURFACE_LIGHTMAP_BOTTOM,
715 SURFACE_LIGHTMAP_TOP,
716 SURFACE_LIGHTMAP_BLEND
717 };
718
719 #define SURFACE_AMBIENT SURFACE_AMBIENT_BOTTOM
720 #define SURFACE_LIGHTMAP SURFACE_LIGHTMAP_BOTTOM
721
generatelightmap(lightmapworker * w,float lpu,const lerpvert * lv,int numv,vec origin1,const vec & xstep1,const vec & ystep1,vec origin2,const vec & xstep2,const vec & ystep2,float side0,float sidestep)722 static bool generatelightmap(lightmapworker *w, float lpu, const lerpvert *lv, int numv, vec origin1, const vec &xstep1, const vec &ystep1, vec origin2, const vec &xstep2, const vec &ystep2, float side0, float sidestep)
723 {
724 static const float aacoords[8][2] =
725 {
726 {0.0f, 0.0f},
727 {-0.5f, -0.5f},
728 {0.0f, -0.5f},
729 {-0.5f, 0.0f},
730
731 {0.3f, -0.6f},
732 {0.6f, 0.3f},
733 {-0.3f, 0.6f},
734 {-0.6f, -0.3f},
735 };
736 float tolerance = 0.5 / lpu;
737 uint lightmask = 0, lightused = 0;
738 vec offsets1[8], offsets2[8];
739 loopi(8)
740 {
741 offsets1[i] = vec(xstep1).mul(aacoords[i][0]).add(vec(ystep1).mul(aacoords[i][1]));
742 offsets2[i] = vec(xstep2).mul(aacoords[i][0]).add(vec(ystep2).mul(aacoords[i][1]));
743 }
744 if((w->type&LM_TYPE) == LM_BUMPMAP0) memset(w->raydata, 0, (LM_MAXW + 4)*(LM_MAXH + 4)*sizeof(vec));
745
746 origin1.sub(vec(ystep1).add(xstep1).mul(blurlms));
747 origin2.sub(vec(ystep2).add(xstep2).mul(blurlms));
748
749 int aasample = min(1 << lmaa, 4);
750 int stride = aasample*(w->w+1);
751 vec *sample = w->colordata;
752 uchar *slight = w->ambient;
753 lerpbounds start, end;
754 initlerpbounds(-blurlms, -blurlms, lv, numv, start, end);
755 float sidex = side0 + blurlms*sidestep;
756 for(int y = 0; y < w->h; ++y, sidex += sidestep)
757 {
758 vec normal, nstep;
759 lerpnormal(-blurlms, y - blurlms, lv, numv, start, end, normal, nstep);
760
761 for(int x = 0; x < w->w; ++x, normal.add(nstep), slight += w->bpp)
762 {
763 #define EDGE_TOLERANCE(x, y) \
764 (x < blurlms \
765 || x+1 > w->w - blurlms \
766 || y < blurlms \
767 || y+1 > w->h - blurlms \
768 ? edgetolerance : 1)
769 float t = EDGE_TOLERANCE(x, y) * tolerance;
770 vec u = x < sidex ? vec(xstep1).mul(x).add(vec(ystep1).mul(y)).add(origin1) : vec(xstep2).mul(x).add(vec(ystep2).mul(y)).add(origin2);
771 lightused |= generatelumel(w, t, 0, w->lights, u, vec(normal).normalize(), *sample, x, y);
772 if(hasskylight())
773 {
774 if((w->type&LM_TYPE)==LM_BUMPMAP0 || !adaptivesample || sample->x<skylightcolor[0] || sample->y<skylightcolor[1] || sample->z<skylightcolor[2])
775 calcskylight(w, u, normal, t, slight, lmshadows > 1 ? RAY_ALPHAPOLY : 0);
776 else loopk(3) slight[k] = max(skylightcolor[k], ambientcolor[k]);
777 }
778 else loopk(3) slight[k] = ambientcolor[k];
779 if(sunlights.length()) calcsunlight(w, u, normal, t, slight, lmshadows > 1 ? RAY_ALPHAPOLY : 0);
780 if(w->type&LM_ALPHA) generatealpha(w, t, u, slight[3]);
781 sample += aasample;
782 }
783 sample += aasample;
784 }
785 if(adaptivesample > 1 && min(w->w, w->h) >= 2) lightmask = ~lightused;
786 sample = w->colordata;
787 initlerpbounds(-blurlms, -blurlms, lv, numv, start, end);
788 sidex = side0 + blurlms*sidestep;
789 for(int y = 0; y < w->h; ++y, sidex += sidestep)
790 {
791 vec normal, nstep;
792 lerpnormal(-blurlms, y - blurlms, lv, numv, start, end, normal, nstep);
793
794 for(int x = 0; x < w->w; ++x, normal.add(nstep))
795 {
796 vec ¢er = *sample++;
797 if(adaptivesample && x > 0 && x+1 < w->w && y > 0 && y+1 < w->h && !lumelsample(center, aasample, stride))
798 loopi(aasample-1) *sample++ = center;
799 else
800 {
801 #define AA_EDGE_TOLERANCE(x, y, i) EDGE_TOLERANCE(x + aacoords[i][0], y + aacoords[i][1])
802 vec u = x < sidex ? vec(xstep1).mul(x).add(vec(ystep1).mul(y)).add(origin1) : vec(xstep2).mul(x).add(vec(ystep2).mul(y)).add(origin2);
803 const vec *offsets = x < sidex ? offsets1 : offsets2;
804 vec n = vec(normal).normalize();
805 loopi(aasample-1)
806 generatelumel(w, AA_EDGE_TOLERANCE(x, y, i+1) * tolerance, lightmask, w->lights, vec(u).add(offsets[i+1]), n, *sample++, x, y);
807 if(lmaa == 3)
808 {
809 loopi(4)
810 {
811 vec s;
812 generatelumel(w, AA_EDGE_TOLERANCE(x, y, i+4) * tolerance, lightmask, w->lights, vec(u).add(offsets[i+4]), n, s, x, y);
813 center.add(s);
814 }
815 center.div(5);
816 }
817 }
818 }
819 if(aasample > 1)
820 {
821 vec u = w->w < sidex ? vec(xstep1).mul(w->w).add(vec(ystep1).mul(y)).add(origin1) : vec(xstep2).mul(w->w).add(vec(ystep2).mul(y)).add(origin2);
822 const vec *offsets = w->w < sidex ? offsets1 : offsets2;
823 vec n = vec(normal).normalize();
824 generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[1]), n, sample[1], w->w-1, y);
825 if(aasample > 2)
826 generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[3]), n, sample[3], w->w-1, y);
827 }
828 sample += aasample;
829 }
830
831 if(aasample > 1)
832 {
833 vec normal, nstep;
834 lerpnormal(-blurlms, w->h - blurlms, lv, numv, start, end, normal, nstep);
835
836 for(int x = 0; x <= w->w; ++x, normal.add(nstep))
837 {
838 vec u = x < sidex ? vec(xstep1).mul(x).add(vec(ystep1).mul(w->h)).add(origin1) : vec(xstep2).mul(x).add(vec(ystep2).mul(w->h)).add(origin2);
839 const vec *offsets = x < sidex ? offsets1 : offsets2;
840 vec n = vec(normal).normalize();
841 generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[1]), n, sample[1], min(x, w->w-1), w->h-1);
842 if(aasample > 2)
843 generatelumel(w, edgetolerance * tolerance, lightmask, w->lights, vec(u).add(offsets[2]), n, sample[2], min(x, w->w-1), w->h-1);
844 sample += aasample;
845 }
846 }
847 return true;
848 }
849
finishlightmap(lightmapworker * w)850 static int finishlightmap(lightmapworker *w)
851 {
852 if((hasskylight() || sunlights.length()) && blurskylight && (w->w>1 || w->h>1))
853 {
854 blurtexture(blurskylight, w->bpp, w->w, w->h, w->blur, w->ambient);
855 swap(w->blur, w->ambient);
856 }
857 vec *sample = w->colordata;
858 int aasample = min(1 << lmaa, 4), stride = aasample*(w->w+1);
859 float weight = 1.0f / (1.0f + 4.0f*lmaa),
860 cweight = weight * (lmaa == 3 ? 5.0f : 1.0f);
861 uchar *skylight = w->ambient;
862 vec *ray = w->raydata;
863 uchar *dstcolor = blurlms && (w->w > 1 || w->h > 1) ? w->blur : w->colorbuf;
864 uchar mincolor[4] = { 255, 255, 255, 255 }, maxcolor[4] = { 0, 0, 0, 0 };
865 bvec *dstray = blurlms && (w->w > 1 || w->h > 1) ? (bvec *)w->raydata : w->raybuf;
866 bvec minray(255, 255, 255), maxray(0, 0, 0);
867 loop(y, w->h)
868 {
869 loop(x, w->w)
870 {
871 vec l(0, 0, 0);
872 const vec ¢er = *sample++;
873 loopi(aasample-1) l.add(*sample++);
874 if(aasample > 1)
875 {
876 l.add(sample[1]);
877 if(aasample > 2) l.add(sample[3]);
878 }
879 vec *next = sample + stride - aasample;
880 if(aasample > 1)
881 {
882 l.add(next[1]);
883 if(aasample > 2) l.add(next[2]);
884 l.add(next[aasample+1]);
885 }
886
887 int r = int(center.x*cweight + l.x*weight),
888 g = int(center.y*cweight + l.y*weight),
889 b = int(center.z*cweight + l.z*weight),
890 ar = skylight[0], ag = skylight[1], ab = skylight[2];
891 dstcolor[0] = max(ar, r);
892 dstcolor[1] = max(ag, g);
893 dstcolor[2] = max(ab, b);
894 loopk(3)
895 {
896 mincolor[k] = min(mincolor[k], dstcolor[k]);
897 maxcolor[k] = max(maxcolor[k], dstcolor[k]);
898 }
899 if(w->type&LM_ALPHA)
900 {
901 dstcolor[3] = skylight[3];
902 mincolor[3] = min(mincolor[3], dstcolor[3]);
903 maxcolor[3] = max(maxcolor[3], dstcolor[3]);
904 }
905 if((w->type&LM_TYPE) == LM_BUMPMAP0)
906 {
907 if(ray->iszero()) dstray[0] = bvec(128, 128, 255);
908 else
909 {
910 // bias the normals towards the amount of ambient/skylight in the lumel
911 // this is necessary to prevent the light values in shaders from dropping too far below the skylight (to the ambient) if N.L is small
912 ray->normalize();
913 int l = max(r, max(g, b)), a = max(ar, max(ag, ab));
914 ray->mul(max(l-a, 0));
915 ray->z += a;
916 dstray[0] = bvec(ray->normalize());
917 }
918 loopk(3)
919 {
920 minray[k] = min(minray[k], dstray[0][k]);
921 maxray[k] = max(maxray[k], dstray[0][k]);
922 }
923 ray++;
924 dstray++;
925 }
926 dstcolor += w->bpp;
927 skylight += w->bpp;
928 }
929 sample += aasample;
930 }
931 if(int(maxcolor[0]) - int(mincolor[0]) <= lighterror &&
932 int(maxcolor[1]) - int(mincolor[1]) <= lighterror &&
933 int(maxcolor[2]) - int(mincolor[2]) <= lighterror &&
934 mincolor[3] >= maxcolor[3])
935 {
936 uchar color[3];
937 loopk(3) color[k] = (int(maxcolor[k]) + int(mincolor[k])) / 2;
938 if(color[0] <= int(ambientcolor[0]) + lighterror &&
939 color[1] <= int(ambientcolor[1]) + lighterror &&
940 color[2] <= int(ambientcolor[2]) + lighterror &&
941 (maxcolor[3]==0 || mincolor[3]==255))
942 return mincolor[3]==255 ? SURFACE_AMBIENT_TOP : SURFACE_AMBIENT_BOTTOM;
943 if((w->type&LM_TYPE) != LM_BUMPMAP0 ||
944 (int(maxray.x) - int(minray.x) <= bumperror &&
945 int(maxray.y) - int(minray.z) <= bumperror &&
946 int(maxray.z) - int(minray.z) <= bumperror))
947
948 {
949 memcpy(w->colorbuf, color, 3);
950 if(w->type&LM_ALPHA) w->colorbuf[3] = mincolor[3];
951 if((w->type&LM_TYPE) == LM_BUMPMAP0)
952 {
953 loopk(3) w->raybuf[0][k] = uchar((int(maxray[k])+int(minray[k]))/2);
954 }
955 w->lastlightmap->w = w->w = 1;
956 w->lastlightmap->h = w->h = 1;
957 }
958 }
959 if(blurlms && (w->w>1 || w->h>1))
960 {
961 blurtexture(blurlms, w->bpp, w->w, w->h, w->colorbuf, w->blur, blurlms);
962 if((w->type&LM_TYPE) == LM_BUMPMAP0) blurnormals(blurlms, w->w, w->h, w->raybuf, (const bvec *)w->raydata, blurlms);
963 w->lastlightmap->w = (w->w -= 2*blurlms);
964 w->lastlightmap->h = (w->h -= 2*blurlms);
965 }
966 if(mincolor[3]==255) return SURFACE_LIGHTMAP_TOP;
967 else if(maxcolor[3]==0) return SURFACE_LIGHTMAP_BOTTOM;
968 else return SURFACE_LIGHTMAP_BLEND;
969 }
970
previewlightmapalpha(lightmapworker * w,float lpu,const vec & origin1,const vec & xstep1,const vec & ystep1,const vec & origin2,const vec & xstep2,const vec & ystep2,float side0,float sidestep)971 static int previewlightmapalpha(lightmapworker *w, float lpu, const vec &origin1, const vec &xstep1, const vec &ystep1, const vec &origin2, const vec &xstep2, const vec &ystep2, float side0, float sidestep)
972 {
973 float tolerance = 0.5 / lpu;
974 uchar *dst = w->colorbuf;
975 uchar minalpha = 255, maxalpha = 0;
976 float sidex = side0;
977 for(int y = 0; y < w->h; ++y, sidex += sidestep)
978 {
979 for(int x = 0; x < w->w; ++x, dst += 4)
980 {
981 vec u = x < sidex ?
982 vec(xstep1).mul(x).add(vec(ystep1).mul(y)).add(origin1) :
983 vec(xstep2).mul(x).add(vec(ystep2).mul(y)).add(origin2);
984 loopk(3) dst[k] = fullbrightlevel;
985 generatealpha(w, tolerance, u, dst[3]);
986 minalpha = min(minalpha, dst[3]);
987 maxalpha = max(maxalpha, dst[3]);
988 }
989 }
990 if(minalpha==255) return SURFACE_AMBIENT_TOP;
991 if(maxalpha==0) return SURFACE_AMBIENT_BOTTOM;
992 if(minalpha==maxalpha) w->w = w->h = 1;
993 if((w->type&LM_TYPE) == LM_BUMPMAP0) loopi(w->w*w->h) w->raybuf[i] = bvec(128, 128, 255);
994 return SURFACE_LIGHTMAP_BLEND;
995 }
996
clearsurfaces(cube * c)997 static void clearsurfaces(cube *c)
998 {
999 loopi(8)
1000 {
1001 if(c[i].ext)
1002 {
1003 loopj(6)
1004 {
1005 surfaceinfo &surf = c[i].ext->surfaces[j];
1006 if(!surf.used()) continue;
1007 surf.clear();
1008 int numverts = surf.numverts&MAXFACEVERTS;
1009 if(numverts)
1010 {
1011 if(!(c[i].merged&(1<<j))) { surf.numverts &= ~MAXFACEVERTS; continue; }
1012
1013 vertinfo *verts = c[i].ext->verts() + surf.verts;
1014 loopk(numverts)
1015 {
1016 vertinfo &v = verts[k];
1017 v.u = 0;
1018 v.v = 0;
1019 v.norm = 0;
1020 }
1021 }
1022 }
1023 }
1024 if(c[i].children) clearsurfaces(c[i].children);
1025 }
1026 }
1027
1028 #define LIGHTCACHESIZE 1024
1029
1030 static struct lightcacheentry
1031 {
1032 int x, y;
1033 vector<int> lights;
1034 } lightcache[LIGHTCACHESIZE];
1035
1036 #define LIGHTCACHEHASH(x, y) (((((x)^(y))<<5) + (((x)^(y))>>5)) & (LIGHTCACHESIZE - 1))
1037
1038 VARF(0, lightcachesize, 4, 6, 12, clearlightcache());
1039
findsunlights()1040 void findsunlights()
1041 {
1042 sunlights.setsize(0);
1043 int numents = entities::lastent(ET_SUNLIGHT);
1044 const vector<extentity *> &ents = entities::getents();
1045 loopi(numents) if(ents[i]->type == ET_SUNLIGHT) sunlights.add(ents[i]);
1046 }
1047
clearlightcache(int id)1048 void clearlightcache(int id)
1049 {
1050 if(id >= 0)
1051 {
1052 const extentity &light = *entities::getents()[id];
1053 if(light.type == ET_LIGHT && light.attrs[0])
1054 {
1055 int radius = light.attrs[0];
1056 for(int x = int(max(light.o.x-radius, 0.0f))>>lightcachesize, ex = int(min(light.o.x+radius, hdr.worldsize-1.0f))>>lightcachesize; x <= ex; x++)
1057 for(int y = int(max(light.o.y-radius, 0.0f))>>lightcachesize, ey = int(min(light.o.y+radius, hdr.worldsize-1.0f))>>lightcachesize; y <= ey; y++)
1058 {
1059 lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
1060 if(lce.x != x || lce.y != y) continue;
1061 lce.x = -1;
1062 lce.lights.setsize(0);
1063 }
1064 return;
1065 }
1066 }
1067 else sunlights.setsize(0);
1068 for(lightcacheentry *lce = lightcache; lce < &lightcache[LIGHTCACHESIZE]; lce++)
1069 {
1070 lce->x = -1;
1071 lce->lights.setsize(0);
1072 }
1073 }
1074
checklightcache(int x,int y)1075 const vector<int> &checklightcache(int x, int y)
1076 {
1077 x >>= lightcachesize;
1078 y >>= lightcachesize;
1079 lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
1080 if(lce.x == x && lce.y == y) return lce.lights;
1081
1082 lce.lights.setsize(0);
1083 int csize = 1<<lightcachesize, cx = x<<lightcachesize, cy = y<<lightcachesize;
1084 const vector<extentity *> &ents = entities::getents();
1085 int numents = entities::lastent(ET_LIGHT);
1086 loopi(numents)
1087 {
1088 extentity &light = *ents[i];
1089 switch(light.type)
1090 {
1091 case ET_LIGHT:
1092 {
1093 int radius = light.attrs[0];
1094 if(radius > 0)
1095 {
1096 if(light.o.x + radius < cx || light.o.x - radius > cx + csize ||
1097 light.o.y + radius < cy || light.o.y - radius > cy + csize)
1098 continue;
1099 }
1100 break;
1101 }
1102 default: continue;
1103 }
1104 lce.lights.add(i);
1105 }
1106
1107 lce.x = x;
1108 lce.y = y;
1109 return lce.lights;
1110 }
1111
addlight(lightmapworker * w,const extentity & light,int cx,int cy,int cz,int size,const vec * v,const vec * n,int numv)1112 static inline void addlight(lightmapworker *w, const extentity &light, int cx, int cy, int cz, int size, const vec *v, const vec *n, int numv)
1113 {
1114 int radius = light.attrs[0];
1115 if(radius > 0)
1116 {
1117 if(light.o.x + radius < cx || light.o.x - radius > cx + size ||
1118 light.o.y + radius < cy || light.o.y - radius > cy + size ||
1119 light.o.z + radius < cz || light.o.z - radius > cz + size)
1120 return;
1121 }
1122
1123 loopi(4)
1124 {
1125 vec p(light.o);
1126 p.sub(v[i]);
1127 float dist = p.dot(n[i]);
1128 if(dist >= 0 && (!radius || dist < radius))
1129 {
1130 w->lights.add(&light);
1131 break;
1132 }
1133 }
1134 }
1135
findlights(lightmapworker * w,int cx,int cy,int cz,int size,const vec * v,const vec * n,int numv,const Slot & slot,const VSlot & vslot)1136 static bool findlights(lightmapworker *w, int cx, int cy, int cz, int size, const vec *v, const vec *n, int numv, const Slot &slot, const VSlot &vslot)
1137 {
1138 w->lights.setsize(0);
1139 const vector<extentity *> &ents = entities::getents();
1140 static volatile bool usinglightcache = false;
1141 if(size <= 1<<lightcachesize && (!lightlock || !usinglightcache))
1142 {
1143 if(lightlock) { SDL_LockMutex(lightlock); usinglightcache = true; }
1144 const vector<int> &lights = checklightcache(cx, cy);
1145 loopv(lights)
1146 {
1147 const extentity &light = *ents[lights[i]];
1148 switch(light.type)
1149 {
1150 case ET_LIGHT: addlight(w, light, cx, cy, cz, size, v, n, numv); break;
1151 }
1152 }
1153 if(lightlock) { usinglightcache = false; SDL_UnlockMutex(lightlock); }
1154 }
1155 else
1156 {
1157 int numents = entities::lastent(ET_LIGHT);
1158 loopi(numents)
1159 {
1160 const extentity &light = *ents[i];
1161 switch(light.type)
1162 {
1163 case ET_LIGHT: addlight(w, light, cx, cy, cz, size, v, n, numv); break;
1164 }
1165 }
1166 }
1167 if(vslot.layer && (setblendmaporigin(w->blendmapcache, ivec(cx, cy, cz), size) || slot.layermask)) return true;
1168 return w->lights.length() || hasskylight() || sunlights.length();
1169 }
1170
packlightmaps(lightmapworker * w=NULL)1171 static int packlightmaps(lightmapworker *w = NULL)
1172 {
1173 int numpacked = 0;
1174 for(; packidx < lightmaptasks[0].length(); packidx++, numpacked++)
1175 {
1176 lightmaptask &t = lightmaptasks[0][packidx];
1177 if(!t.lightmaps) break;
1178 if(t.ext && t.c->ext != t.ext)
1179 {
1180 lightmapext &e = lightmapexts.add();
1181 e.c = t.c;
1182 e.ext = t.ext;
1183 }
1184 lmprog = t.progress;
1185 lightmapinfo *l = t.lightmaps;
1186 if(l == (lightmapinfo *)-1) continue;
1187 int space = 0;
1188 for(; l && l->c == t.c; l = l->next)
1189 {
1190 l->packed = true;
1191 space += l->bufsize;
1192 if(l->surface < 0 || !t.ext) continue;
1193 surfaceinfo &surf = t.ext->surfaces[l->surface];
1194 layoutinfo layout;
1195 packlightmap(*l, layout);
1196 int numverts = surf.numverts&MAXFACEVERTS;
1197 vertinfo *verts = t.ext->verts() + surf.verts;
1198 if(l->layers&LAYER_DUP)
1199 {
1200 if(l->type&LM_ALPHA) surf.lmid[0] = layout.lmid;
1201 else { surf.lmid[1] = layout.lmid; verts += numverts; }
1202 }
1203 else
1204 {
1205 if(l->layers&LAYER_TOP) surf.lmid[0] = layout.lmid;
1206 if(l->layers&LAYER_BOTTOM) surf.lmid[1] = layout.lmid;
1207 }
1208 ushort offsetx = layout.x*((USHRT_MAX+1)/LM_PACKW), offsety = layout.y*((USHRT_MAX+1)/LM_PACKH);
1209 loopk(numverts)
1210 {
1211 vertinfo &v = verts[k];
1212 v.u += offsetx;
1213 v.v += offsety;
1214 }
1215 }
1216 if(t.worker == w)
1217 {
1218 w->bufused -= space;
1219 w->bufstart = (w->bufstart + space)%LIGHTMAPBUFSIZE;
1220 w->firstlightmap = l;
1221 if(!l)
1222 {
1223 w->lastlightmap = NULL;
1224 w->bufstart = w->bufused = 0;
1225 }
1226 }
1227 if(t.worker->needspace) SDL_CondSignal(t.worker->spacecond);
1228 }
1229 return numpacked;
1230 }
1231
alloclightmap(lightmapworker * w)1232 static lightmapinfo *alloclightmap(lightmapworker *w)
1233 {
1234 int needspace1 = sizeof(lightmapinfo) + w->w*w->h*w->bpp,
1235 needspace2 = (w->type&LM_TYPE) == LM_BUMPMAP0 ? w->w*w->h*3 : 0,
1236 needspace = needspace1 + needspace2,
1237 bufend = (w->bufstart + w->bufused)%LIGHTMAPBUFSIZE,
1238 availspace = LIGHTMAPBUFSIZE - w->bufused,
1239 availspace1 = min(availspace, LIGHTMAPBUFSIZE - bufend),
1240 availspace2 = min(availspace, w->bufstart);
1241 if(availspace < needspace || (max(availspace1, availspace2) < needspace && (availspace1 < needspace1 || availspace2 < needspace2)))
1242 {
1243 if(tasklock) SDL_LockMutex(tasklock);
1244 while(!w->doneworking)
1245 {
1246 lightmapinfo *l = w->firstlightmap;
1247 for(; l && l->packed; l = l->next)
1248 {
1249 w->bufused -= l->bufsize;
1250 w->bufstart = (w->bufstart + l->bufsize)%LIGHTMAPBUFSIZE;
1251 }
1252 w->firstlightmap = l;
1253 if(!l)
1254 {
1255 w->lastlightmap = NULL;
1256 w->bufstart = w->bufused = 0;
1257 }
1258 bufend = (w->bufstart + w->bufused)%LIGHTMAPBUFSIZE;
1259 availspace = LIGHTMAPBUFSIZE - w->bufused;
1260 availspace1 = min(availspace, LIGHTMAPBUFSIZE - bufend);
1261 availspace2 = min(availspace, w->bufstart);
1262 if(availspace >= needspace && (max(availspace1, availspace2) >= needspace || (availspace1 >= needspace1 && availspace2 >= needspace2))) break;
1263 if(packlightmaps(w)) continue;
1264 if(!w->spacecond || !tasklock) break;
1265 w->needspace = true;
1266 SDL_CondWait(w->spacecond, tasklock);
1267 w->needspace = false;
1268 }
1269 if(tasklock) SDL_UnlockMutex(tasklock);
1270 }
1271 int usedspace = needspace;
1272 lightmapinfo *l = NULL;
1273 if(availspace1 >= needspace1)
1274 {
1275 l = (lightmapinfo *)&w->buf[bufend];
1276 w->colorbuf = (uchar *)(l + 1);
1277 if((w->type&LM_TYPE) != LM_BUMPMAP0) w->raybuf = NULL;
1278 else if(availspace1 >= needspace) w->raybuf = (bvec *)&w->buf[bufend + needspace1];
1279 else
1280 {
1281 w->raybuf = (bvec *)w->buf;
1282 usedspace += availspace1 - needspace1;
1283 }
1284 }
1285 else if(availspace2 >= needspace)
1286 {
1287 usedspace += availspace1;
1288 l = (lightmapinfo *)w->buf;
1289 w->colorbuf = (uchar *)(l + 1);
1290 w->raybuf = (w->type&LM_TYPE) == LM_BUMPMAP0 ? (bvec *)&w->buf[needspace1] : NULL;
1291 }
1292 else return NULL;
1293 w->bufused += usedspace;
1294 l->next = NULL;
1295 l->c = w->c;
1296 l->type = w->type;
1297 l->w = w->w;
1298 l->h = w->h;
1299 l->bpp = w->bpp;
1300 l->colorbuf = w->colorbuf;
1301 l->raybuf = w->raybuf;
1302 l->packed = false;
1303 l->bufsize = usedspace;
1304 l->surface = -1;
1305 l->layers = 0;
1306 if(!w->firstlightmap) w->firstlightmap = l;
1307 if(w->lastlightmap) w->lastlightmap->next = l;
1308 w->lastlightmap = l;
1309 if(!w->curlightmaps) w->curlightmaps = l;
1310 return l;
1311 }
1312
freelightmap(lightmapworker * w)1313 static void freelightmap(lightmapworker *w)
1314 {
1315 lightmapinfo *l = w->lastlightmap;
1316 if(!l || l->surface >= 0) return;
1317 if(w->firstlightmap == w->lastlightmap)
1318 {
1319 w->firstlightmap = w->lastlightmap = w->curlightmaps = NULL;
1320 w->bufstart = w->bufused = 0;
1321 }
1322 else
1323 {
1324 w->bufused -= l->bufsize - sizeof(lightmapinfo);
1325 l->bufsize = sizeof(lightmapinfo);
1326 l->packed = true;
1327 }
1328 if(w->curlightmaps == l) w->curlightmaps = NULL;
1329 }
1330
setupsurface(lightmapworker * w,plane planes[2],int numplanes,const vec * p,const vec * n,int numverts,vertinfo * litverts,bool preview=false)1331 static int setupsurface(lightmapworker *w, plane planes[2], int numplanes, const vec *p, const vec *n, int numverts, vertinfo *litverts, bool preview = false)
1332 {
1333 vec u, v, t;
1334 vec2 c[MAXFACEVERTS];
1335
1336 u = vec(p[2]).sub(p[0]).normalize();
1337 v.cross(planes[0], u);
1338 c[0] = vec2(0, 0);
1339 if(numplanes >= 2) t.cross(planes[1], u); else t = v;
1340 vec r1 = vec(p[1]).sub(p[0]);
1341 c[1] = vec2(r1.dot(u), min(r1.dot(v), 0.0f));
1342 c[2] = vec2(vec(p[2]).sub(p[0]).dot(u), 0);
1343 for(int i = 3; i < numverts; i++)
1344 {
1345 vec r = vec(p[i]).sub(p[0]);
1346 c[i] = vec2(r.dot(u), max(r.dot(t), 0.0f));
1347 }
1348
1349 float carea = 1e16f;
1350 vec2 cx(0, 0), cy(0, 0), co(0, 0), cmin(0, 0), cmax(0, 0);
1351 loopi(numverts)
1352 {
1353 vec2 px = vec2(c[i+1 < numverts ? i+1 : 0]).sub(c[i]);
1354 float len = px.squaredlen();
1355 if(!len) continue;
1356 px.mul(1/sqrtf(len));
1357 vec2 py(-px.y, px.x), pmin(0, 0), pmax(0, 0);
1358 if(numplanes >= 2 && (i == 0 || i >= 3)) px.neg();
1359 loopj(numverts)
1360 {
1361 vec2 rj = vec2(c[j]).sub(c[i]), pj(rj.dot(px), rj.dot(py));
1362 pmin.x = min(pmin.x, pj.x);
1363 pmin.y = min(pmin.y, pj.y);
1364 pmax.x = max(pmax.x, pj.x);
1365 pmax.y = max(pmax.y, pj.y);
1366 }
1367 float area = (pmax.x-pmin.x)*(pmax.y-pmin.y);
1368 if(area < carea) { carea = area; cx = px; cy = py; co = c[i]; cmin = pmin; cmax = pmax; }
1369 }
1370
1371 int scale = int(min(cmax.x - cmin.x, cmax.y - cmin.y));
1372 float lpu = 16.0f / float(lightlod && scale < (1 << lightlod) ? max(curlightprecision / 2, 1) : curlightprecision);
1373 int lw = clamp(int(ceil((cmax.x - cmin.x + 1)*lpu)), LM_MINW, LM_MAXW), lh = clamp(int(ceil((cmax.y - cmin.y + 1)*lpu)), LM_MINH, LM_MAXH);
1374 w->w = lw;
1375 w->h = lh;
1376 if(!preview)
1377 {
1378 w->w += 2*blurlms;
1379 w->h += 2*blurlms;
1380 }
1381 if(!alloclightmap(w)) return NO_SURFACE;
1382
1383 vec2 cscale = vec2(cmax).sub(cmin).div(vec2(lw-1, lh-1)),
1384 comin = vec2(cx).mul(cmin.x).add(vec2(cy).mul(cmin.y)).add(co);
1385 loopi(numverts)
1386 {
1387 vec2 ri = vec2(c[i]).sub(comin);
1388 c[i] = vec2(ri.dot(cx)/cscale.x, ri.dot(cy)/cscale.y);
1389 }
1390
1391 vec xstep1 = vec(v).mul(cx.y).add(vec(u).mul(cx.x)).mul(cscale.x),
1392 ystep1 = vec(v).mul(cy.y).add(vec(u).mul(cy.x)).mul(cscale.y),
1393 origin1 = vec(v).mul(comin.y).add(vec(u).mul(comin.x)).add(p[0]),
1394 xstep2 = xstep1, ystep2 = ystep1, origin2 = origin1;
1395 float side0 = LM_MAXW + 1, sidestep = 0;
1396 if(numplanes >= 2)
1397 {
1398 xstep2 = vec(t).mul(cx.y).add(vec(u).mul(cx.x)).mul(cscale.x);
1399 ystep2 = vec(t).mul(cy.y).add(vec(u).mul(cy.x)).mul(cscale.y);
1400 origin2 = vec(t).mul(comin.y).add(vec(u).mul(comin.x)).add(p[0]);
1401 if(cx.y) { side0 = comin.y/-(cx.y*cscale.x); sidestep = cy.y*cscale.y/-(cx.y*cscale.x); }
1402 else if(cy.y) { side0 = ceil(comin.y/-(cy.y*cscale.y))*(LM_MAXW + 1); sidestep = -(LM_MAXW + 1); if(cy.y < 0) { side0 = (LM_MAXW + 1) - side0; sidestep = -sidestep; } }
1403 else side0 = comin.y <= 0 ? LM_MAXW + 1 : -1;
1404 }
1405
1406 int surftype = NO_SURFACE;
1407 if(preview)
1408 {
1409 surftype = previewlightmapalpha(w, lpu, origin1, xstep1, ystep1, origin2, xstep2, ystep2, side0, sidestep);
1410 }
1411 else
1412 {
1413 lerpvert lv[MAXFACEVERTS];
1414 int numv = numverts;
1415 calclerpverts(c, n, lv, numv);
1416
1417 if(!generatelightmap(w, lpu, lv, numv, origin1, xstep1, ystep1, origin2, xstep2, ystep2, side0, sidestep)) return NO_SURFACE;
1418 surftype = finishlightmap(w);
1419 }
1420 if(surftype<SURFACE_LIGHTMAP) return surftype;
1421
1422 vec2 texscale(float(USHRT_MAX+1)/LM_PACKW, float(USHRT_MAX+1)/LM_PACKH);
1423 if(lw != w->w) texscale.x *= float(w->w - 1) / (lw - 1);
1424 if(lh != w->h) texscale.y *= float(w->h - 1) / (lh - 1);
1425 loopk(numverts)
1426 {
1427 litverts[k].u = ushort(floor(clamp(c[k].x*texscale.x, 0.0f, float(USHRT_MAX))));
1428 litverts[k].v = ushort(floor(clamp(c[k].y*texscale.y, 0.0f, float(USHRT_MAX))));
1429 }
1430 return surftype;
1431 }
1432
removelmalpha(lightmapworker * w)1433 static void removelmalpha(lightmapworker *w)
1434 {
1435 if(!(w->type&LM_ALPHA)) return;
1436 for(uchar *dst = w->colorbuf, *src = w->colorbuf, *end = &src[w->w*w->h*4];
1437 src < end;
1438 dst += 3, src += 4)
1439 {
1440 dst[0] = src[0];
1441 dst[1] = src[1];
1442 dst[2] = src[2];
1443 }
1444 w->type &= ~LM_ALPHA;
1445 w->bpp = 3;
1446 w->lastlightmap->type = w->type;
1447 w->lastlightmap->bpp = w->bpp;
1448 }
1449
setupsurfaces(lightmapworker * w,lightmaptask & task)1450 static lightmapinfo *setupsurfaces(lightmapworker *w, lightmaptask &task)
1451 {
1452 cube &c = *task.c;
1453 const ivec &co = task.o;
1454 int size = task.size, usefacemask = task.usefaces;
1455
1456 w->curlightmaps = NULL;
1457 w->c = &c;
1458
1459 surfaceinfo surfaces[6];
1460 vertinfo litverts[6*2*MAXFACEVERTS];
1461 int numlitverts = 0;
1462 memset(surfaces, 0, sizeof(surfaces));
1463 loopi(6)
1464 {
1465 int usefaces = usefacemask&0xF;
1466 usefacemask >>= 4;
1467 if(!usefaces)
1468 {
1469 if(!c.ext) continue;
1470 surfaceinfo &surf = surfaces[i];
1471 surf = c.ext->surfaces[i];
1472 int numverts = surf.totalverts();
1473 if(numverts)
1474 {
1475 memcpy(&litverts[numlitverts], c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
1476 surf.verts = numlitverts;
1477 numlitverts += numverts;
1478 }
1479 continue;
1480 }
1481
1482 VSlot &vslot = lookupvslot(c.texture[i], false),
1483 *layer = vslot.layer && !(c.material&MAT_ALPHA) ? &lookupvslot(vslot.layer, false) : NULL;
1484 Shader *shader = vslot.slot->shader;
1485 int shadertype = shader->type;
1486 if(layer) shadertype |= layer->slot->shader->type;
1487
1488 surfaceinfo &surf = surfaces[i];
1489 vertinfo *curlitverts = &litverts[numlitverts];
1490 int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0;
1491 ivec mo(co);
1492 int msz = size, convex = 0;
1493 if(numverts)
1494 {
1495 vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
1496 loopj(numverts) curlitverts[j].set(verts[j].getxyz());
1497 if(c.merged&(1<<i))
1498 {
1499 msz = 1<<calcmergedsize(i, mo, size, verts, numverts);
1500 mo.mask(~(msz-1));
1501
1502 if(!(surf.numverts&MAXFACEVERTS))
1503 {
1504 surf.verts = numlitverts;
1505 surf.numverts |= numverts;
1506 numlitverts += numverts;
1507 }
1508 }
1509 else if(!flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
1510 }
1511 else
1512 {
1513 ivec v[4];
1514 genfaceverts(c, i, v);
1515 if(!flataxisface(c, i)) convex = faceconvexity(v);
1516 int order = usefaces&4 || convex < 0 ? 1 : 0;
1517 ivec vo = ivec(co).mask(0xFFF).shl(3);
1518 curlitverts[numverts++].set(v[order].mul(size).add(vo));
1519 if(usefaces&1) curlitverts[numverts++].set(v[order+1].mul(size).add(vo));
1520 curlitverts[numverts++].set(v[order+2].mul(size).add(vo));
1521 if(usefaces&2) curlitverts[numverts++].set(v[(order+3)&3].mul(size).add(vo));
1522 }
1523
1524 vec pos[MAXFACEVERTS], n[MAXFACEVERTS], po(ivec(co).mask(~0xFFF));
1525 loopj(numverts) pos[j] = vec(curlitverts[j].getxyz()).mul(1.0f/8).add(po);
1526
1527 plane planes[2];
1528 int numplanes = 0;
1529 planes[numplanes++].toplane(pos[0], pos[1], pos[2]);
1530 if(numverts < 4 || !convex) loopk(numverts) findnormal(pos[k], planes[0], n[k]);
1531 else
1532 {
1533 planes[numplanes++].toplane(pos[0], pos[2], pos[3]);
1534 vec avg = vec(planes[0]).add(planes[1]).normalize();
1535 findnormal(pos[0], avg, n[0]);
1536 findnormal(pos[1], planes[0], n[1]);
1537 findnormal(pos[2], avg, n[2]);
1538 for(int k = 3; k < numverts; k++) findnormal(pos[k], planes[1], n[k]);
1539 }
1540
1541 if(shadertype&(SHADER_NORMALSLMS | SHADER_ENVMAP))
1542 {
1543 loopk(numverts) curlitverts[k].norm = encodenormal(n[k]);
1544 if(!(surf.numverts&MAXFACEVERTS))
1545 {
1546 surf.verts = numlitverts;
1547 surf.numverts |= numverts;
1548 numlitverts += numverts;
1549 }
1550 }
1551
1552 if(!findlights(w, mo.x, mo.y, mo.z, msz, pos, n, numverts, *vslot.slot, vslot))
1553 {
1554 if(surf.numverts&MAXFACEVERTS) surf.numverts |= LAYER_TOP;
1555 continue;
1556 }
1557
1558 w->slot = vslot.slot;
1559 w->vslot = &vslot;
1560 w->type = shader->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
1561 if(layer) w->type |= LM_ALPHA;
1562 w->bpp = w->type&LM_ALPHA ? 4 : 3;
1563 w->orient = i;
1564 w->rotate = vslot.rotation;
1565 int surftype = setupsurface(w, planes, numplanes, pos, n, numverts, curlitverts);
1566 switch(surftype)
1567 {
1568 case SURFACE_LIGHTMAP_BOTTOM:
1569 if((shader->type^layer->slot->shader->type)&SHADER_NORMALSLMS ||
1570 (shader->type&SHADER_NORMALSLMS && vslot.rotation!=layer->rotation))
1571 {
1572 freelightmap(w);
1573 break;
1574 }
1575 // fall through
1576 case SURFACE_LIGHTMAP_BLEND:
1577 case SURFACE_LIGHTMAP_TOP:
1578 {
1579 if(!(surf.numverts&MAXFACEVERTS))
1580 {
1581 surf.verts = numlitverts;
1582 surf.numverts |= numverts;
1583 numlitverts += numverts;
1584 }
1585
1586 w->lastlightmap->surface = i;
1587 w->lastlightmap->layers = (surftype==SURFACE_LIGHTMAP_BOTTOM ? LAYER_BOTTOM : LAYER_TOP);
1588 if(surftype==SURFACE_LIGHTMAP_BLEND)
1589 {
1590 surf.numverts |= LAYER_BLEND;
1591 w->lastlightmap->layers = LAYER_TOP;
1592 if((shader->type^layer->slot->shader->type)&SHADER_NORMALSLMS ||
1593 (shader->type&SHADER_NORMALSLMS && vslot.rotation!=layer->rotation))
1594 break;
1595 w->lastlightmap->layers |= LAYER_BOTTOM;
1596 }
1597 else
1598 {
1599 if(surftype==SURFACE_LIGHTMAP_BOTTOM)
1600 {
1601 surf.numverts |= LAYER_BOTTOM;
1602 w->lastlightmap->layers = LAYER_BOTTOM;
1603 }
1604 else
1605 {
1606 surf.numverts |= LAYER_TOP;
1607 w->lastlightmap->layers = LAYER_TOP;
1608 }
1609 if(w->type&LM_ALPHA) removelmalpha(w);
1610 }
1611 continue;
1612 }
1613
1614 case SURFACE_AMBIENT_BOTTOM:
1615 freelightmap(w);
1616 surf.numverts |= layer ? LAYER_BOTTOM : LAYER_TOP;
1617 continue;
1618
1619 case SURFACE_AMBIENT_TOP:
1620 freelightmap(w);
1621 surf.numverts |= LAYER_TOP;
1622 continue;
1623
1624 default:
1625 freelightmap(w);
1626 continue;
1627 }
1628
1629 w->slot = layer->slot;
1630 w->vslot = layer;
1631 w->type = layer->slot->shader->type&SHADER_NORMALSLMS ? LM_BUMPMAP0 : LM_DIFFUSE;
1632 w->bpp = 3;
1633 w->rotate = layer->rotation;
1634 vertinfo *blendverts = surf.numverts&MAXFACEVERTS ? &curlitverts[numverts] : curlitverts;
1635 switch(setupsurface(w, planes, numplanes, pos, n, numverts, blendverts))
1636 {
1637 case SURFACE_LIGHTMAP_TOP:
1638 {
1639 if(!(surf.numverts&MAXFACEVERTS))
1640 {
1641 surf.verts = numlitverts;
1642 surf.numverts |= numverts;
1643 numlitverts += numverts;
1644 }
1645 else if(!(surf.numverts&LAYER_DUP))
1646 {
1647 surf.numverts |= LAYER_DUP;
1648 w->lastlightmap->layers |= LAYER_DUP;
1649 loopk(numverts)
1650 {
1651 vertinfo &src = curlitverts[k];
1652 vertinfo &dst = blendverts[k];
1653 dst.setxyz(src.getxyz());
1654 dst.norm = src.norm;
1655 }
1656 numlitverts += numverts;
1657 }
1658 surf.numverts |= LAYER_BOTTOM;
1659 w->lastlightmap->layers |= LAYER_BOTTOM;
1660
1661 w->lastlightmap->surface = i;
1662 break;
1663 }
1664
1665 case SURFACE_AMBIENT_TOP:
1666 {
1667 freelightmap(w);
1668 surf.numverts |= LAYER_BOTTOM;
1669 break;
1670 }
1671
1672 default: freelightmap(w); break;
1673 }
1674 }
1675 loopk(6)
1676 {
1677 surfaceinfo &surf = surfaces[k];
1678 if(surf.used())
1679 {
1680 cubeext *ext = c.ext && c.ext->maxverts >= numlitverts ? c.ext : growcubeext(c.ext, numlitverts);
1681 memcpy(ext->surfaces, surfaces, sizeof(ext->surfaces));
1682 memcpy(ext->verts(), litverts, numlitverts*sizeof(vertinfo));
1683 task.ext = ext;
1684 break;
1685 }
1686 }
1687 return w->curlightmaps ? w->curlightmaps : (lightmapinfo *)-1;
1688 }
1689
work(void * data)1690 int lightmapworker::work(void *data)
1691 {
1692 lightmapworker *w = (lightmapworker *)data;
1693 SDL_LockMutex(tasklock);
1694 while(!w->doneworking)
1695 {
1696 if(allocidx < lightmaptasks[0].length())
1697 {
1698 lightmaptask &t = lightmaptasks[0][allocidx++];
1699 t.worker = w;
1700 SDL_UnlockMutex(tasklock);
1701 lightmapinfo *l = setupsurfaces(w, t);
1702 SDL_LockMutex(tasklock);
1703 t.lightmaps = l;
1704 packlightmaps(w);
1705 }
1706 else
1707 {
1708 if(packidx >= lightmaptasks[0].length()) SDL_CondSignal(emptycond);
1709 SDL_CondWait(fullcond, tasklock);
1710 }
1711 }
1712 SDL_UnlockMutex(tasklock);
1713 return 0;
1714 }
1715
processtasks(bool finish=false)1716 static bool processtasks(bool finish = false)
1717 {
1718 if(tasklock) SDL_LockMutex(tasklock);
1719 while(finish || lightmaptasks[1].length())
1720 {
1721 if(packidx >= lightmaptasks[0].length())
1722 {
1723 if(lightmaptasks[1].empty()) break;
1724 lightmaptasks[0].setsize(0);
1725 lightmaptasks[0].move(lightmaptasks[1]);
1726 packidx = allocidx = 0;
1727 if(fullcond) SDL_CondBroadcast(fullcond);
1728 }
1729 else if(lightmapping > 1)
1730 {
1731 SDL_CondWaitTimeout(emptycond, tasklock, 250);
1732 CHECK_PROGRESS_LOCKED({ SDL_UnlockMutex(tasklock); return false; }, SDL_UnlockMutex(tasklock), SDL_LockMutex(tasklock));
1733 }
1734 else
1735 {
1736 while(allocidx < lightmaptasks[0].length())
1737 {
1738 lightmaptask &t = lightmaptasks[0][allocidx++];
1739 t.worker = lightmapworkers[0];
1740 t.lightmaps = setupsurfaces(lightmapworkers[0], t);
1741 packlightmaps(lightmapworkers[0]);
1742 CHECK_PROGRESS(return false);
1743 }
1744 }
1745 }
1746 if(tasklock) SDL_UnlockMutex(tasklock);
1747 return true;
1748 }
1749
generatelightmaps(cube * c,const ivec & co,int size)1750 static void generatelightmaps(cube *c, const ivec &co, int size)
1751 {
1752 CHECK_PROGRESS(return);
1753
1754 taskprogress++;
1755
1756 loopi(8)
1757 {
1758 ivec o(i, co, size);
1759 if(c[i].children)
1760 generatelightmaps(c[i].children, o, size >> 1);
1761 else if(!isempty(c[i]))
1762 {
1763 if(c[i].ext)
1764 {
1765 loopj(6)
1766 {
1767 surfaceinfo &surf = c[i].ext->surfaces[j];
1768 if(surf.lmid[0] >= LMID_RESERVED || surf.lmid[1] >= LMID_RESERVED) goto nextcube;
1769 surf.clear();
1770 }
1771 }
1772 int usefacemask = 0;
1773 loopj(6) if(c[i].texture[j] != DEFAULT_SKY && (!(c[i].merged&(1<<j)) || (c[i].ext && c[i].ext->surfaces[j].numverts&MAXFACEVERTS)))
1774 {
1775 usefacemask |= visibletris(c[i], j, o, size)<<(4*j);
1776 }
1777 if(usefacemask)
1778 {
1779 lightmaptask &t = lightmaptasks[1].add();
1780 t.o = o;
1781 t.size = size;
1782 t.usefaces = usefacemask;
1783 t.c = &c[i];
1784 t.ext = NULL;
1785 t.lightmaps = NULL;
1786 t.progress = taskprogress;
1787 if(lightmaptasks[1].length() >= MAXLIGHTMAPTASKS && !processtasks()) return;
1788 }
1789 }
1790 nextcube:;
1791 }
1792 }
1793
previewblends(lightmapworker * w,cube & c,const ivec & co,int size)1794 static bool previewblends(lightmapworker *w, cube &c, const ivec &co, int size)
1795 {
1796 if(isempty(c) || c.material&MAT_ALPHA) return false;
1797
1798 int usefacemask = 0;
1799 loopi(6) if(c.texture[i] != DEFAULT_SKY && lookupvslot(c.texture[i], false).layer)
1800 usefacemask |= visibletris(c, i, co, size)<<(4*i);
1801 if(!usefacemask) return false;
1802
1803 if(!setblendmaporigin(w->blendmapcache, co, size))
1804 {
1805 if(!c.ext) return false;
1806 bool blends = false;
1807 loopi(6) if(c.ext->surfaces[i].numverts&LAYER_BOTTOM)
1808 {
1809 c.ext->surfaces[i].brighten();
1810 blends = true;
1811 }
1812 return blends;
1813 }
1814
1815 w->firstlightmap = w->lastlightmap = w->curlightmaps = NULL;
1816 w->bufstart = w->bufused = 0;
1817 w->c = &c;
1818
1819 surfaceinfo surfaces[6];
1820 vertinfo litverts[6*2*MAXFACEVERTS];
1821 int numlitverts = 0;
1822 memcpy(surfaces, c.ext ? c.ext->surfaces : brightsurfaces, sizeof(surfaces));
1823 loopi(6)
1824 {
1825 int usefaces = usefacemask&0xF;
1826 usefacemask >>= 4;
1827 if(!usefaces)
1828 {
1829 surfaceinfo &surf = surfaces[i];
1830 int numverts = surf.totalverts();
1831 if(numverts)
1832 {
1833 memcpy(&litverts[numlitverts], c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
1834 surf.verts = numlitverts;
1835 numlitverts += numverts;
1836 }
1837 continue;
1838 }
1839
1840 VSlot &vslot = lookupvslot(c.texture[i], false),
1841 &layer = lookupvslot(vslot.layer, false);
1842 Shader *shader = vslot.slot->shader;
1843 int shadertype = shader->type | layer.slot->shader->type;
1844
1845 vertinfo *curlitverts = &litverts[numlitverts];
1846 int numverts = 0;
1847 ivec v[4];
1848 genfaceverts(c, i, v);
1849 int convex = flataxisface(c, i) ? 0 : faceconvexity(v),
1850 order = usefaces&4 || convex < 0 ? 1 : 0;
1851 ivec vo = ivec(co).mask(0xFFF).shl(3);
1852 curlitverts[numverts++].set(v[order].mul(size).add(vo));
1853 if(usefaces&1) curlitverts[numverts++].set(v[order+1].mul(size).add(vo));
1854 curlitverts[numverts++].set(v[order+2].mul(size).add(vo));
1855 if(usefaces&2) curlitverts[numverts++].set(v[(order+3)&3].mul(size).add(vo));
1856
1857 vec pos[4], n[4], po(ivec(co).mask(~0xFFF));
1858 loopj(numverts) pos[j] = vec(curlitverts[j].getxyz()).mul(1.0f/8).add(po);
1859
1860 plane planes[2];
1861 int numplanes = 0;
1862 planes[numplanes++].toplane(pos[0], pos[1], pos[2]);
1863 if(numverts < 4 || !convex) loopk(numverts) n[k] = planes[0];
1864 else
1865 {
1866 planes[numplanes++].toplane(pos[0], pos[2], pos[3]);
1867 vec avg = vec(planes[0]).add(planes[1]).normalize();
1868 n[0] = avg;
1869 n[1] = planes[0];
1870 n[2] = avg;
1871 for(int k = 3; k < numverts; k++) n[k] = planes[1];
1872 }
1873
1874 surfaceinfo &surf = surfaces[i];
1875 w->slot = vslot.slot;
1876 w->vslot = &vslot;
1877 w->type = shadertype&SHADER_NORMALSLMS ? LM_BUMPMAP0|LM_ALPHA : LM_DIFFUSE|LM_ALPHA;
1878 w->bpp = 4;
1879 w->orient = i;
1880 w->rotate = vslot.rotation;
1881 int surftype = setupsurface(w, planes, numplanes, pos, n, numverts, curlitverts, true);
1882 switch(surftype)
1883 {
1884 case SURFACE_AMBIENT_TOP:
1885 surf = brightsurface;
1886 continue;
1887
1888 case SURFACE_AMBIENT_BOTTOM:
1889 surf = brightbottomsurface;
1890 continue;
1891
1892 case SURFACE_LIGHTMAP_BLEND:
1893 {
1894 if(surf.numverts == (LAYER_BLEND|numverts) &&
1895 surf.lmid[0] == surf.lmid[1] &&
1896 (surf.numverts&MAXFACEVERTS) == numverts &&
1897 !memcmp(curlitverts, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo)) &&
1898 lightmaps.inrange(surf.lmid[0]-LMID_RESERVED) &&
1899 lightmaps[surf.lmid[0]-LMID_RESERVED].type==w->type)
1900 {
1901 vertinfo *oldverts = c.ext->verts() + surf.verts;
1902 layoutinfo layout;
1903 layout.w = w->w;
1904 layout.h = w->h;
1905 layout.x = (oldverts[0].x - curlitverts[0].x)/((USHRT_MAX+1)/LM_PACKW);
1906 layout.y = (oldverts[0].y - curlitverts[0].y)/((USHRT_MAX+1)/LM_PACKH);
1907 if(LM_PACKW - layout.x >= w->w && LM_PACKH - layout.y >= w->h)
1908 {
1909 layout.lmid = surf.lmid[0];
1910 copylightmap(*w->lastlightmap, layout);
1911 updatelightmap(layout);
1912 surf.verts = numlitverts;
1913 numlitverts += numverts;
1914 continue;
1915 }
1916 }
1917
1918 surf.verts = numlitverts;
1919 surf.numverts = LAYER_BLEND|numverts;
1920 numlitverts += numverts;
1921 layoutinfo layout;
1922 if(packlightmap(*w->lastlightmap, layout)) updatelightmap(layout);
1923 surf.lmid[0] = surf.lmid[1] = layout.lmid;
1924 ushort offsetx = layout.x*((USHRT_MAX+1)/LM_PACKW), offsety = layout.y*((USHRT_MAX+1)/LM_PACKH);
1925 loopk(numverts)
1926 {
1927 vertinfo &v = curlitverts[k];
1928 v.u += offsetx;
1929 v.v += offsety;
1930 }
1931 continue;
1932 }
1933 }
1934 }
1935
1936 setsurfaces(c, surfaces, litverts, numlitverts);
1937 return true;
1938 }
1939
previewblends(lightmapworker * w,cube * c,const ivec & co,int size,const ivec & bo,const ivec & bs)1940 static bool previewblends(lightmapworker *w, cube *c, const ivec &co, int size, const ivec &bo, const ivec &bs)
1941 {
1942 bool changed = false;
1943 loopoctabox(co, size, bo, bs)
1944 {
1945 ivec o(i, co, size);
1946 cubeext *ext = c[i].ext;
1947 if(ext && ext->va && ext->va->hasmerges)
1948 {
1949 changed = true;
1950 destroyva(ext->va);
1951 ext->va = NULL;
1952 invalidatemerges(c[i], co, size, true);
1953 }
1954 if(c[i].children ? previewblends(w, c[i].children, o, size/2, bo, bs) : previewblends(w, c[i], o, size))
1955 {
1956 changed = true;
1957 ext = c[i].ext;
1958 if(ext && ext->va)
1959 {
1960 destroyva(ext->va);
1961 ext->va = NULL;
1962 }
1963 }
1964 }
1965 return changed;
1966 }
1967
previewblends(const ivec & bo,const ivec & bs)1968 void previewblends(const ivec &bo, const ivec &bs)
1969 {
1970 loadlayermasks();
1971 if(lightmapworkers.empty()) lightmapworkers.add(new lightmapworker);
1972 lightmapworkers[0]->reset();
1973 if(previewblends(lightmapworkers[0], worldroot, ivec(0, 0, 0), hdr.worldsize/2, bo, bs))
1974 commitchanges(true);
1975 }
1976
cleanuplightmaps()1977 void cleanuplightmaps()
1978 {
1979 loopv(lightmaps)
1980 {
1981 LightMap &lm = lightmaps[i];
1982 lm.tex = lm.offsetx = lm.offsety = -1;
1983 }
1984 loopv(lightmaptexs) glDeleteTextures(1, &lightmaptexs[i].id);
1985 lightmaptexs.shrink(0);
1986 if(lmprogtex) { glDeleteTextures(1, &lmprogtex); lmprogtex = 0; }
1987 }
1988
resetlightmaps(bool fullclean)1989 void resetlightmaps(bool fullclean)
1990 {
1991 cleanuplightmaps();
1992 lightmaps.shrink(0);
1993 compressed.clear();
1994 clearlightcache();
1995 if(fullclean) while(lightmapworkers.length()) delete lightmapworkers.pop();
1996 }
1997
lightmapworker()1998 lightmapworker::lightmapworker()
1999 {
2000 buf = new uchar[LIGHTMAPBUFSIZE];
2001 bufstart = bufused = 0;
2002 firstlightmap = lastlightmap = curlightmaps = NULL;
2003 ambient = new uchar[4*(LM_MAXW + 4)*(LM_MAXH + 4)];
2004 blur = new uchar[4*(LM_MAXW + 4)*(LM_MAXH + 4)];
2005 colordata = new vec[4*(LM_MAXW+1 + 4)*(LM_MAXH+1 + 4)];
2006 raydata = new vec[(LM_MAXW + 4)*(LM_MAXH + 4)];
2007 shadowraycache = newshadowraycache();
2008 blendmapcache = newblendmapcache();
2009 needspace = doneworking = false;
2010 spacecond = NULL;
2011 thread = NULL;
2012 }
2013
~lightmapworker()2014 lightmapworker::~lightmapworker()
2015 {
2016 cleanupthread();
2017 delete[] buf;
2018 delete[] ambient;
2019 delete[] blur;
2020 delete[] colordata;
2021 delete[] raydata;
2022 freeshadowraycache(shadowraycache);
2023 freeblendmapcache(blendmapcache);
2024 }
2025
cleanupthread()2026 void lightmapworker::cleanupthread()
2027 {
2028 if(spacecond) { SDL_DestroyCond(spacecond); spacecond = NULL; }
2029 thread = NULL;
2030 }
2031
reset()2032 void lightmapworker::reset()
2033 {
2034 bufstart = bufused = 0;
2035 firstlightmap = lastlightmap = curlightmaps = NULL;
2036 needspace = doneworking = false;
2037 resetshadowraycache(shadowraycache);
2038 }
2039
setupthread()2040 bool lightmapworker::setupthread()
2041 {
2042 if(!spacecond) spacecond = SDL_CreateCond();
2043 if(!spacecond) return false;
2044 thread = SDL_CreateThread(work, "lightmap worker", this);
2045 return thread!=NULL;
2046 }
2047
calclighttimer(Uint32 interval,void * param)2048 static Uint32 calclighttimer(Uint32 interval, void *param)
2049 {
2050 check_calclight_lmprog = true;
2051 return interval;
2052 }
2053
setlightmapquality(int quality,bool quick)2054 bool setlightmapquality(int quality, bool quick)
2055 {
2056 curlightprecision = quick ? lightprecisionquick : lightprecision;
2057 switch(quality)
2058 {
2059 case 1: lmshadows = 2; lmaa = 3; lerptjoints = 1; break;
2060 case 0: lmshadows = lmshadows_; lmaa = lmaa_; lerptjoints = lerptjoints_; break;
2061 case -1: lmshadows = 1; lmaa = 0; lerptjoints = 0; break;
2062 default: return false;
2063 }
2064 return true;
2065 }
2066
2067 VAR(IDF_PERSIST, lightthreads, 0, 0, 16);
2068
2069 #define ALLOCLOCK(name, init) { if(lightmapping > 1) name = init(); if(!name) lightmapping = 1; }
2070 #define FREELOCK(name, destroy) { if(name) { destroy(name); name = NULL; } }
2071
cleanuplocks()2072 static void cleanuplocks()
2073 {
2074 FREELOCK(lightlock, SDL_DestroyMutex);
2075 FREELOCK(tasklock, SDL_DestroyMutex);
2076 FREELOCK(fullcond, SDL_DestroyCond);
2077 FREELOCK(emptycond, SDL_DestroyCond);
2078 }
2079
setupthreads(int numthreads)2080 static void setupthreads(int numthreads)
2081 {
2082 loopi(2) lightmaptasks[i].setsize(0);
2083 lightmapexts.setsize(0);
2084 packidx = allocidx = 0;
2085 lightmapping = numthreads;
2086 if(lightmapping > 1)
2087 {
2088 ALLOCLOCK(lightlock, SDL_CreateMutex);
2089 ALLOCLOCK(tasklock, SDL_CreateMutex);
2090 ALLOCLOCK(fullcond, SDL_CreateCond);
2091 ALLOCLOCK(emptycond, SDL_CreateCond);
2092 }
2093 while(lightmapworkers.length() < lightmapping) lightmapworkers.add(new lightmapworker);
2094 loopi(lightmapping)
2095 {
2096 lightmapworker *w = lightmapworkers[i];
2097 w->reset();
2098 if(lightmapping <= 1 || w->setupthread()) continue;
2099 w->cleanupthread();
2100 lightmapping = i >= 1 ? max(i, 2) : 1;
2101 break;
2102 }
2103 if(lightmapping <= 1) cleanuplocks();
2104 }
2105
cleanupthreads()2106 static void cleanupthreads()
2107 {
2108 processtasks(true);
2109 if(lightmapping > 1)
2110 {
2111 SDL_LockMutex(tasklock);
2112 loopv(lightmapworkers) lightmapworkers[i]->doneworking = true;
2113 SDL_CondBroadcast(fullcond);
2114 loopv(lightmapworkers)
2115 {
2116 lightmapworker *w = lightmapworkers[i];
2117 if(w->needspace && w->spacecond) SDL_CondSignal(w->spacecond);
2118 }
2119 SDL_UnlockMutex(tasklock);
2120 loopv(lightmapworkers)
2121 {
2122 lightmapworker *w = lightmapworkers[i];
2123 if(w->thread) SDL_WaitThread(w->thread, NULL);
2124 }
2125 }
2126 loopv(lightmapexts)
2127 {
2128 lightmapext &e = lightmapexts[i];
2129 setcubeext(*e.c, e.ext);
2130 }
2131 loopv(lightmapworkers) lightmapworkers[i]->cleanupthread();
2132 cleanuplocks();
2133 lightmapping = 0;
2134 }
2135
calclight(int * quality,int * quick)2136 void calclight(int *quality, int *quick)
2137 {
2138 if(!setlightmapquality(*quality, *quick!=0))
2139 {
2140 conoutft(CON_MESG, "\frvalid range for calclight quality is -1..1");
2141 return;
2142 }
2143 progress(0, "computing lightmaps...");
2144 mpremip(true);
2145 optimizeblendmap();
2146 loadlayermasks();
2147 int numthreads = lightthreads > 0 ? lightthreads : numcpus;
2148 if(numthreads > 1) preloadusedmapmodels(false, true);
2149 resetlightmaps(false);
2150 clearsurfaces(worldroot);
2151 findsunlights();
2152 taskprogress = lmprog = 0;
2153 lmprogtexticks = 0;
2154 lmprogid = -1;
2155 calclight_canceled = false;
2156 check_calclight_lmprog = false;
2157 SDL_TimerID timer = SDL_AddTimer(250, calclighttimer, NULL);
2158 Uint32 start = SDL_GetTicks();
2159 calcnormals(lerptjoints > 0);
2160 show_calclight_lmprog();
2161 setupthreads(numthreads);
2162 generatelightmaps(worldroot, ivec(0, 0, 0), hdr.worldsize >> 1);
2163 cleanupthreads();
2164 clearnormals();
2165 Uint32 end = SDL_GetTicks();
2166 if(timer) SDL_RemoveTimer(timer);
2167 uint total = 0, lumels = 0;
2168 loopv(lightmaps)
2169 {
2170 insertunlit(i);
2171 if(!editmode) lightmaps[i].finalize();
2172 total += lightmaps[i].lightmaps;
2173 lumels += lightmaps[i].lumels;
2174 }
2175 if(!editmode) compressed.clear();
2176 initlights();
2177 progress(0, "lighting done...");
2178 allchanged();
2179 if(calclight_canceled)
2180 conoutf("\frcalclight aborted");
2181 else
2182 conoutf("\fggenerated %d lightmaps using %d%% of %d textures (%.1f seconds)",
2183 total,
2184 lightmaps.length() ? lumels * 100 / (lightmaps.length() * LM_PACKW * LM_PACKH) : 0,
2185 lightmaps.length(),
2186 (end - start) / 1000.0f);
2187 if(lmprogtex) { glDeleteTextures(1, &lmprogtex); lmprogtex = 0; }
2188 }
2189
2190 COMMAND(0, calclight, "ii");
2191
2192 VAR(0, patchnormals, 0, 0, 1);
2193
patchlight(int * quality,int * quick)2194 void patchlight(int *quality, int *quick)
2195 {
2196 if(noedit(true)) return;
2197 if(!setlightmapquality(*quality, *quick!=0))
2198 {
2199 conoutft(CON_MESG, "\frvalid range for patchlight quality is -1..1");
2200 return;
2201 }
2202 progress(0, "patching lightmaps...");
2203 loadlayermasks();
2204 int numthreads = lightthreads > 0 ? lightthreads : numcpus;
2205 if(numthreads > 1) preloadusedmapmodels(false, true);
2206 cleanuplightmaps();
2207 findsunlights();
2208 taskprogress = lmprog = 0;
2209 lmprogtexticks = 0;
2210 lmprogid = -1;
2211 int total = 0, lumels = 0;
2212 loopv(lightmaps)
2213 {
2214 if((lightmaps[i].type&LM_TYPE) != LM_BUMPMAP1) lmprogid = i;
2215 total -= lightmaps[i].lightmaps;
2216 lumels -= lightmaps[i].lumels;
2217 }
2218 calclight_canceled = false;
2219 check_calclight_lmprog = false;
2220 SDL_TimerID timer = SDL_AddTimer(250, calclighttimer, NULL);
2221 if(patchnormals) progress(0, "computing normals...");
2222 Uint32 start = SDL_GetTicks();
2223 if(patchnormals) calcnormals(lerptjoints > 0);
2224 show_calclight_lmprog();
2225 setupthreads(numthreads);
2226 generatelightmaps(worldroot, ivec(0, 0, 0), hdr.worldsize >> 1);
2227 cleanupthreads();
2228 if(patchnormals) clearnormals();
2229 Uint32 end = SDL_GetTicks();
2230 if(timer) SDL_RemoveTimer(timer);
2231 loopv(lightmaps)
2232 {
2233 total += lightmaps[i].lightmaps;
2234 lumels += lightmaps[i].lumels;
2235 }
2236 initlights();
2237 progress(0, "lighting done...");
2238 allchanged();
2239 if(calclight_canceled)
2240 conoutf("\frpatchlight aborted");
2241 else
2242 conoutf("\fgpatched %d lightmaps using %d%% of %d textures (%.1f seconds)",
2243 total,
2244 lightmaps.length() ? lumels * 100 / (lightmaps.length() * LM_PACKW * LM_PACKH) : 0,
2245 lightmaps.length(),
2246 (end - start) / 1000.0f);
2247 if(lmprogtex) { glDeleteTextures(1, &lmprogtex); lmprogtex = 0; }
2248 }
2249
2250 COMMAND(0, patchlight, "ii");
2251
clearlightmaps()2252 void clearlightmaps()
2253 {
2254 if(noedit(true)) return;
2255 progress(0, "clearing lightmaps...");
2256 resetlightmaps(false);
2257 clearsurfaces(worldroot);
2258 initlights();
2259 allchanged();
2260 }
2261
2262 COMMAND(0, clearlightmaps, "");
2263
setfullbrightlevel(int fullbrightlevel)2264 void setfullbrightlevel(int fullbrightlevel)
2265 {
2266 if(lightmaptexs.length() > LMID_BRIGHT)
2267 {
2268 uchar bright[3] = { uchar(fullbrightlevel), uchar(fullbrightlevel), uchar(fullbrightlevel) };
2269 createtexture(lightmaptexs[LMID_BRIGHT].id, 1, 1, bright, 0, 1);
2270 }
2271 initlights();
2272 }
2273
2274 VARF(0, fullbright, 0, 0, 1, if(lightmaptexs.length()) { initlights(); lightents(); });
2275 VARF(IDF_PERSIST, fullbrightlevel, 0, 128, 255, setfullbrightlevel(fullbrightlevel));
2276
2277 vector<LightMapTexture> lightmaptexs;
2278
rotatenormals(LightMap & lmlv,int x,int y,int w,int h,int rotate)2279 static void rotatenormals(LightMap &lmlv, int x, int y, int w, int h, int rotate)
2280 {
2281 bool flipx = rotate>=2 && rotate<=4,
2282 flipy = (rotate>=1 && rotate<=2) || rotate==5,
2283 swapxy = (rotate&5)==1;
2284 uchar *lv = lmlv.data + 3*(y*LM_PACKW + x);
2285 int stride = 3*(LM_PACKW-w);
2286 loopi(h)
2287 {
2288 loopj(w)
2289 {
2290 if(flipx) lv[0] = 255 - lv[0];
2291 if(flipy) lv[1] = 255 - lv[1];
2292 if(swapxy) swap(lv[0], lv[1]);
2293 lv += 3;
2294 }
2295 lv += stride;
2296 }
2297 }
2298
rotatenormals(cube * c)2299 static void rotatenormals(cube *c)
2300 {
2301 loopi(8)
2302 {
2303 cube &ch = c[i];
2304 if(ch.children)
2305 {
2306 rotatenormals(ch.children);
2307 continue;
2308 }
2309 else if(!ch.ext) continue;
2310 loopj(6) if(lightmaps.inrange(ch.ext->surfaces[j].lmid[0]+1-LMID_RESERVED))
2311 {
2312 VSlot &vslot = lookupvslot(ch.texture[j], false);
2313 if(!vslot.rotation) continue;
2314 surfaceinfo &surface = ch.ext->surfaces[j];
2315 int numverts = surface.numverts&MAXFACEVERTS;
2316 if(!numverts) continue;
2317 LightMap &lmlv = lightmaps[surface.lmid[0]+1-LMID_RESERVED];
2318 if((lmlv.type&LM_TYPE)!=LM_BUMPMAP1) continue;
2319 ushort x1 = USHRT_MAX, y1 = USHRT_MAX, x2 = 0, y2 = 0;
2320 vertinfo *verts = ch.ext->verts() + surface.verts;
2321 loopk(numverts)
2322 {
2323 vertinfo &v = verts[k];
2324 x1 = min(x1, v.u);
2325 y1 = min(y1, v.u);
2326 x2 = max(x2, v.u);
2327 y2 = max(y2, v.v);
2328 }
2329 if(x1 > x2 || y1 > y2) continue;
2330 x1 /= (USHRT_MAX+1)/LM_PACKW;
2331 y1 /= (USHRT_MAX+1)/LM_PACKH;
2332 x2 /= (USHRT_MAX+1)/LM_PACKW;
2333 y2 /= (USHRT_MAX+1)/LM_PACKH;
2334 rotatenormals(lmlv, x1, y1, x2-x1, y1-y1, vslot.rotation < 4 ? 4-vslot.rotation : vslot.rotation);
2335 }
2336 }
2337 }
2338
fixlightmapnormals()2339 void fixlightmapnormals()
2340 {
2341 rotatenormals(worldroot);
2342 }
2343
fixrotatedlightmaps(cube & c,const ivec & co,int size)2344 void fixrotatedlightmaps(cube &c, const ivec &co, int size)
2345 {
2346 if(c.children)
2347 {
2348 loopi(8) fixrotatedlightmaps(c.children[i], ivec(i, co, size>>1), size>>1);
2349 return;
2350 }
2351 if(!c.ext) return;
2352 loopi(6)
2353 {
2354 if(c.merged&(1<<i)) continue;
2355 surfaceinfo &surf = c.ext->surfaces[i];
2356 int numverts = surf.numverts&MAXFACEVERTS;
2357 if(numverts!=4 || (surf.lmid[0] < LMID_RESERVED && surf.lmid[1] < LMID_RESERVED)) continue;
2358 vertinfo *verts = c.ext->verts() + surf.verts;
2359 int vis = visibletris(c, i, co, size);
2360 if(!vis || vis==3) continue;
2361 if((verts[0].u != verts[1].u || verts[0].v != verts[1].v) &&
2362 (verts[0].u != verts[3].u || verts[0].v != verts[3].v) &&
2363 (verts[2].u != verts[1].u || verts[2].v != verts[1].v) &&
2364 (verts[2].u != verts[3].u || verts[2].v != verts[3].v))
2365 continue;
2366 if(vis&4)
2367 {
2368 vertinfo tmp = verts[0];
2369 verts[0].x = verts[1].x; verts[0].y = verts[1].y; verts[0].z = verts[1].z;
2370 verts[1].x = verts[2].x; verts[1].y = verts[2].y; verts[1].z = verts[2].z;
2371 verts[2].x = verts[3].x; verts[2].y = verts[3].y; verts[2].z = verts[3].z;
2372 verts[3].x = tmp.x; verts[3].y = tmp.y; verts[3].z = tmp.z;
2373 if(surf.numverts&LAYER_DUP) loopk(4)
2374 {
2375 vertinfo &v = verts[k], &b = verts[k+4];
2376 b.x = v.x;
2377 b.y = v.y;
2378 b.z = v.z;
2379 }
2380 }
2381 surf.numverts = (surf.numverts & ~MAXFACEVERTS) | 3;
2382 if(vis&2)
2383 {
2384 verts[1] = verts[2]; verts[2] = verts[3];
2385 if(surf.numverts&LAYER_DUP) { verts[3] = verts[4]; verts[4] = verts[6]; verts[5] = verts[7]; }
2386 }
2387 else if(surf.numverts&LAYER_DUP) { verts[3] = verts[4]; verts[4] = verts[5]; verts[5] = verts[6]; }
2388 }
2389 }
2390
fixrotatedlightmaps()2391 void fixrotatedlightmaps()
2392 {
2393 loopi(8) fixrotatedlightmaps(worldroot[i], ivec(i, ivec(0, 0, 0), hdr.worldsize>>1), hdr.worldsize>>1);
2394 }
2395
copylightmap(LightMap & lm,uchar * dst,size_t stride)2396 static void copylightmap(LightMap &lm, uchar *dst, size_t stride)
2397 {
2398 const uchar *c = lm.data;
2399 loopi(LM_PACKH)
2400 {
2401 memcpy(dst, c, lm.bpp*LM_PACKW);
2402 c += lm.bpp*LM_PACKW;
2403 dst += stride;
2404 }
2405 }
2406
genreservedlightmaptexs()2407 void genreservedlightmaptexs()
2408 {
2409 while(lightmaptexs.length() < LMID_RESERVED)
2410 {
2411 LightMapTexture &tex = lightmaptexs.add();
2412 tex.type = lightmaptexs.length()&1 ? LM_DIFFUSE : LM_BUMPMAP1;
2413 glGenTextures(1, &tex.id);
2414 }
2415 uchar unlit[3] = { ambientcolor[0], ambientcolor[1], ambientcolor[2] };
2416 createtexture(lightmaptexs[LMID_AMBIENT].id, 1, 1, unlit, 0, 1);
2417 bvec front(128, 128, 255);
2418 createtexture(lightmaptexs[LMID_AMBIENT1].id, 1, 1, &front, 0, 1);
2419 uchar bright[3] = { uchar(fullbrightlevel), uchar(fullbrightlevel), uchar(fullbrightlevel) };
2420 createtexture(lightmaptexs[LMID_BRIGHT].id, 1, 1, bright, 0, 1);
2421 createtexture(lightmaptexs[LMID_BRIGHT1].id, 1, 1, &front, 0, 1);
2422 uchar dark[3] = { 0, 0, 0 };
2423 createtexture(lightmaptexs[LMID_DARK].id, 1, 1, dark, 0, 1);
2424 createtexture(lightmaptexs[LMID_DARK1].id, 1, 1, &front, 0, false);
2425 }
2426
findunlit(int i)2427 static void findunlit(int i)
2428 {
2429 LightMap &lm = lightmaps[i];
2430 if(lm.unlitx>=0) return;
2431 else if((lm.type&LM_TYPE)==LM_BUMPMAP0)
2432 {
2433 if(i+1>=lightmaps.length() || (lightmaps[i+1].type&LM_TYPE)!=LM_BUMPMAP1) return;
2434 }
2435 else if((lm.type&LM_TYPE)!=LM_DIFFUSE) return;
2436 uchar *data = lm.data;
2437 loop(y, 2) loop(x, LM_PACKW)
2438 {
2439 if(!data[0] && !data[1] && !data[2])
2440 {
2441 memcpy(data, ambientcolor.v, 3);
2442 if((lm.type&LM_TYPE)==LM_BUMPMAP0) ((bvec *)lightmaps[i+1].data)[y*LM_PACKW + x] = bvec(128, 128, 255);
2443 lm.unlitx = x;
2444 lm.unlity = y;
2445 return;
2446 }
2447 if(data[0]==ambientcolor[0] && data[1]==ambientcolor[1] && data[2]==ambientcolor[2])
2448 {
2449 if((lm.type&LM_TYPE)!=LM_BUMPMAP0 || ((bvec *)lightmaps[i+1].data)[y*LM_PACKW + x] == bvec(128, 128, 255))
2450 {
2451 lm.unlitx = x;
2452 lm.unlity = y;
2453 return;
2454 }
2455 }
2456 data += lm.bpp;
2457 }
2458 }
2459
2460 VARF(0, roundlightmaptex, 0, 4, 16, { cleanuplightmaps(); initlights(); allchanged(); });
2461 VARF(0, batchlightmaps, 0, 4, 256, { cleanuplightmaps(); initlights(); allchanged(); });
2462
genlightmaptexs(int flagmask,int flagval)2463 void genlightmaptexs(int flagmask, int flagval)
2464 {
2465 if(lightmaptexs.length() < LMID_RESERVED) genreservedlightmaptexs();
2466
2467 int remaining[3] = { 0, 0, 0 }, total = 0;
2468 loopv(lightmaps)
2469 {
2470 LightMap &lm = lightmaps[i];
2471 if(lm.tex >= 0 || (lm.type&flagmask)!=flagval) continue;
2472 int type = lm.type&LM_TYPE;
2473 remaining[type]++;
2474 total++;
2475 if(lm.unlitx < 0) findunlit(i);
2476 }
2477
2478 int sizelimit = (maxtexsize ? min(maxtexsize, hwtexsize) : hwtexsize)/max(LM_PACKW, LM_PACKH);
2479 sizelimit = min(batchlightmaps, sizelimit*sizelimit);
2480 while(total)
2481 {
2482 int type = LM_DIFFUSE;
2483 LightMap *firstlm = NULL;
2484 loopv(lightmaps)
2485 {
2486 LightMap &lm = lightmaps[i];
2487 if(lm.tex >= 0 || (lm.type&flagmask) != flagval) continue;
2488 type = lm.type&LM_TYPE;
2489 firstlm = &lm;
2490 break;
2491 }
2492 if(!firstlm) break;
2493 int used = 0, uselimit = min(remaining[type], sizelimit);
2494 do used++; while((1<<used) <= uselimit);
2495 used--;
2496 int oldval = remaining[type];
2497 remaining[type] -= 1<<used;
2498 if(remaining[type] && (2<<used) <= min(roundlightmaptex, sizelimit))
2499 {
2500 remaining[type] -= min(remaining[type], 1<<used);
2501 used++;
2502 }
2503 total -= oldval - remaining[type];
2504 LightMapTexture &tex = lightmaptexs.add();
2505 tex.type = firstlm->type;
2506 tex.w = LM_PACKW<<((used+1)/2);
2507 tex.h = LM_PACKH<<(used/2);
2508 int bpp = firstlm->bpp;
2509 uchar *data = used ? new uchar[bpp*tex.w*tex.h] : NULL;
2510 int offsetx = 0, offsety = 0;
2511 loopv(lightmaps)
2512 {
2513 LightMap &lm = lightmaps[i];
2514 if(lm.tex >= 0 || (lm.type&flagmask) != flagval || (lm.type&LM_TYPE) != type) continue;
2515
2516 lm.tex = lightmaptexs.length()-1;
2517 lm.offsetx = offsetx;
2518 lm.offsety = offsety;
2519 if(tex.unlitx < 0 && lm.unlitx >= 0)
2520 {
2521 tex.unlitx = offsetx + lm.unlitx;
2522 tex.unlity = offsety + lm.unlity;
2523 }
2524
2525 if(data) copylightmap(lm, &data[bpp*(offsety*tex.w + offsetx)], bpp*tex.w);
2526
2527 offsetx += LM_PACKW;
2528 if(offsetx >= tex.w) { offsetx = 0; offsety += LM_PACKH; }
2529 if(offsety >= tex.h) break;
2530 }
2531
2532 glGenTextures(1, &tex.id);
2533 createtexture(tex.id, tex.w, tex.h, data ? data : firstlm->data, 3, 1, bpp==4 ? GL_RGBA : GL_RGB);
2534 if(data) delete[] data;
2535 }
2536 }
2537
2538 bool brightengeom = false, shouldlightents = false;
2539
clearlights()2540 void clearlights()
2541 {
2542 clearlightcache();
2543 const vector<extentity *> &ents = entities::getents();
2544 loopv(ents)
2545 {
2546 extentity &e = *ents[i];
2547 e.light.color = vec(1, 1, 1);
2548 e.light.dir = vec(0, 0, 1);
2549 }
2550 shouldlightents = false;
2551
2552 genlightmaptexs(LM_ALPHA, 0);
2553 genlightmaptexs(LM_ALPHA, LM_ALPHA);
2554 brightengeom = true;
2555 }
2556
lightent(extentity & e,float height)2557 void lightent(extentity &e, float height)
2558 {
2559 if(e.type==ET_LIGHT || e.type==ET_LIGHTFX || e.type==ET_SUNLIGHT) return;
2560 float amb = 0.0f;
2561 if(e.type==ET_MAPMODEL)
2562 {
2563 model *m = loadmodel(NULL, e.attrs[0]);
2564 if(m) height = m->above()*0.75f;
2565 }
2566 else if(e.type>=ET_GAMESPECIFIC) amb = 0.4f;
2567 vec target(e.o.x, e.o.y, e.o.z + height);
2568 lightreaching(target, e.light.color, e.light.dir, false, &e, amb);
2569 }
2570
lightents(bool force)2571 void lightents(bool force)
2572 {
2573 if(!force && !shouldlightents) return;
2574
2575 const vector<extentity *> &ents = entities::getents();
2576 loopv(ents) lightent(*ents[i]);
2577
2578 shouldlightents = false;
2579 }
2580
initlights()2581 void initlights()
2582 {
2583 if((fullbright && editmode) || lightmaps.empty())
2584 {
2585 clearlights();
2586 return;
2587 }
2588
2589 clearlightcache();
2590 genlightmaptexs(LM_ALPHA, 0);
2591 genlightmaptexs(LM_ALPHA, LM_ALPHA);
2592 brightengeom = false;
2593 shouldlightents = true;
2594 }
2595
fastskylight(const vec & o,float tolerance,uchar * skylight,int flags=RAY_ALPHAPOLY,extentity * t=NULL,bool fast=false)2596 static inline void fastskylight(const vec &o, float tolerance, uchar *skylight, int flags = RAY_ALPHAPOLY, extentity *t = NULL, bool fast = false)
2597 {
2598 flags |= RAY_SHADOW;
2599 if(skytexturelight) flags |= RAY_SKIPSKY;
2600 if(fast)
2601 {
2602 static const vec ray(0, 0, 1);
2603 if(shadowray(vec(ray).mul(tolerance).add(o), ray, 1e16f, flags, t)>1e15f)
2604 memcpy(skylight, skylightcolor.v, 3);
2605 else memcpy(skylight, ambientcolor.v, 3);
2606 }
2607 else
2608 {
2609 static const vec rays[5] =
2610 {
2611 vec(cosf(66*RAD)*cosf(65*RAD), sinf(66*RAD)*cosf(65*RAD), sinf(65*RAD)),
2612 vec(cosf(156*RAD)*cosf(65*RAD), sinf(156*RAD)*cosf(65*RAD), sinf(65*RAD)),
2613 vec(cosf(246*RAD)*cosf(65*RAD), sinf(246*RAD)*cosf(65*RAD), sinf(65*RAD)),
2614 vec(cosf(336*RAD)*cosf(65*RAD), sinf(336*RAD)*cosf(65*RAD), sinf(65*RAD)),
2615 vec(0, 0, 1),
2616 };
2617 int hit = 0;
2618 loopi(5) if(shadowray(vec(rays[i]).mul(tolerance).add(o), rays[i], 1e16f, flags, t)>1e15f) hit++;
2619 loopk(3) skylight[k] = uchar(ambientcolor[k] + (max(skylightcolor[k], ambientcolor[k]) - ambientcolor[k])*hit/5.0f);
2620 }
2621 }
2622
fastsunlight(const vec & o,float tolerance,uchar * slight,int flags=RAY_ALPHAPOLY,extentity * t=NULL)2623 static inline void fastsunlight(const vec &o, float tolerance, uchar *slight, int flags = RAY_ALPHAPOLY, extentity *t = NULL)
2624 {
2625 flags |= RAY_SHADOW;
2626 if(skytexturelight) flags |= RAY_SKIPSKY;
2627 loopv(sunlights) if(sunlights[i])
2628 {
2629 const extentity &light = *sunlights[i];
2630 if(light.attrs.length() < 5 || (slight[0] >= light.attrs[2] && slight[1] >= light.attrs[3] && slight[2] >= light.attrs[4])) continue;
2631 int yaw = light.attrs[0], pitch = light.attrs[1]+90;
2632 vec dir(yaw*RAD, pitch*RAD);
2633 if(shadowray(vec(dir).mul(tolerance).add(o), dir, 1e16f, flags, t) > 1e15f)
2634 {
2635 loopk(3) slight[k] = max(uchar(light.attrs[2+k]), slight[k]);
2636 }
2637 }
2638 }
2639
lightreaching(const vec & target,vec & color,vec & dir,bool fast,extentity * t,float ambient)2640 void lightreaching(const vec &target, vec &color, vec &dir, bool fast, extentity *t, float ambient)
2641 {
2642 if((fullbright && editmode) || lightmaps.empty())
2643 {
2644 color = vec(1, 1, 1);
2645 dir = vec(0, 0, 1);
2646 return;
2647 }
2648
2649 color = dir = vec(0, 0, 0);
2650 const vector<extentity *> &ents = entities::getents();
2651 const vector<int> &lights = checklightcache(int(target.x), int(target.y));
2652 loopv(lights)
2653 {
2654 extentity &e = *ents[lights[i]];
2655 if(e.type != ET_LIGHT)
2656 continue;
2657
2658 vec ray(target);
2659 ray.sub(e.o);
2660 float mag = ray.magnitude();
2661 if(e.attrs[0] && mag >= float(e.attrs[0]))
2662 continue;
2663
2664 if(mag < 1e-4f) ray = vec(0, 0, -1);
2665 else
2666 {
2667 ray.div(mag);
2668 if(shadowray(e.o, ray, mag, RAY_SHADOW | RAY_POLY, t) < mag)
2669 continue;
2670 }
2671
2672 float intensity = 1;
2673 if(e.attrs[0])
2674 intensity -= mag / float(e.attrs[0]);
2675
2676 if(!e.links.empty())
2677 {
2678 int slight = -1;
2679 const vector<extentity *> &ents = entities::getents();
2680 loopvk(e.links)
2681 {
2682 if(ents.inrange(e.links[k]) && ents[e.links[k]]->type == ET_LIGHTFX && ents[e.links[k]]->attrs[0] == LFX_SPOTLIGHT)
2683 {
2684 slight = e.links[k];
2685 break;
2686 }
2687 }
2688 if(ents.inrange(slight))
2689 {
2690 extentity &spotlight = *ents[slight];
2691 vec spot = vec(spotlight.o).sub(e.o).normalize();
2692 float maxatten = sincos360[clamp(int(spotlight.attrs[1]), 1, 89)].x, spotatten = (ray.dot(spot) - maxatten) / (1 - maxatten);
2693 if(spotatten <= 0) continue;
2694 intensity *= spotatten;
2695 }
2696 else continue;
2697 }
2698
2699 vec lightcol = vec(e.attrs[1], e.attrs[2], e.attrs[3]).mul(1.0f/255);
2700 color.add(vec(lightcol).mul(intensity));
2701 dir.add(vec(ray).mul(-intensity*lightcol.x*lightcol.y*lightcol.z));
2702 }
2703
2704 vec slight(0, 0, 0);
2705 if(hasskylight())
2706 {
2707 uchar skylight[3];
2708 if(t) calcskylight(NULL, target, vec(0, 0, 0), 0.5f, skylight, RAY_POLY, t);
2709 else fastskylight(target, 0.5f, skylight, RAY_POLY, t, fast);
2710 loopk(3) slight[k] = max(skylight[k]/255.0f, ambient);
2711 }
2712 else loopk(3) slight[k] = max(ambientcolor[k]/255.0f, ambient);
2713 if(sunlights.length() || entities::lastent(ET_SUNLIGHT))
2714 {
2715 if(sunlights.empty()) findsunlights();
2716 uchar col[3] = {0, 0, 0};
2717 if(t) calcsunlight(NULL, target, vec(0, 0, 0), 0.5f, col, RAY_POLY, t);
2718 else fastsunlight(target, 0.5f, col, RAY_POLY, t);
2719 loopk(3) slight[k] = max(slight[k], col[k]/255.0f);
2720 }
2721 loopk(3) color[k] = clamp(color[k], slight[k], 1.5f);
2722 if(dir.iszero()) dir = vec(0, 0, 1);
2723 else dir.normalize();
2724 }
2725
brightestlight(const vec & target,const vec & dir)2726 const extentity *brightestlight(const vec &target, const vec &dir)
2727 {
2728 const vector<extentity *> &ents = entities::getents();
2729 const vector<int> &lights = checklightcache(int(target.x), int(target.y));
2730 const extentity *brightest = NULL;
2731 float bintensity = 0;
2732 loopv(lights)
2733 {
2734 const extentity &e = *ents[lights[i]];
2735 if(e.type != ET_LIGHT || vec(e.o).sub(target).dot(dir)<0)
2736 continue;
2737
2738 vec ray(target);
2739 ray.sub(e.o);
2740 float mag = ray.magnitude();
2741 if(e.attrs[0] && mag >= float(e.attrs[0]))
2742 continue;
2743
2744 ray.div(mag);
2745 if(shadowray(e.o, ray, mag, RAY_SHADOW | RAY_POLY) < mag)
2746 continue;
2747 float intensity = 1;
2748 if(e.attrs[0])
2749 intensity -= mag / float(e.attrs[0]);
2750
2751 if(!e.links.empty())
2752 {
2753 int slight = -1;
2754 const vector<extentity *> &ents = entities::getents();
2755 loopvk(e.links)
2756 {
2757 if(ents.inrange(e.links[k]) && ents[e.links[k]]->type == ET_LIGHTFX && ents[e.links[k]]->attrs[0] == LFX_SPOTLIGHT)
2758 {
2759 slight = e.links[k];
2760 break;
2761 }
2762 }
2763 if(ents.inrange(slight))
2764 {
2765 const extentity &spotlight = *ents[slight];
2766 vec spot = vec(spotlight.o).sub(e.o).normalize();
2767 float maxatten = sincos360[clamp(int(spotlight.attrs[1]), 1, 89)].x, spotatten = (ray.dot(spot) - maxatten) / (1 - maxatten);
2768 if(spotatten <= 0) continue;
2769 intensity *= spotatten;
2770 }
2771 else continue;
2772 }
2773
2774 if(!brightest || intensity > bintensity)
2775 {
2776 brightest = &e;
2777 bintensity = intensity;
2778 }
2779 }
2780 return brightest;
2781 }
2782
dumplms()2783 void dumplms()
2784 {
2785 loopv(lightmaps)
2786 {
2787 ImageData temp(LM_PACKW, LM_PACKH, lightmaps[i].bpp, lightmaps[i].data);
2788 defformatstring(fname, "%s.lm%.4d", mapname, i);
2789 saveimage(fname, temp, imageformat, compresslevel, true);
2790 }
2791 }
2792
2793 COMMAND(0, dumplms, "");
2794
2795
2796