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 &center = *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 &center = *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