1 #include "engine.h"
2
setlightdir(vec & dir,float yaw,float pitch)3 void setlightdir(vec &dir, float yaw, float pitch)
4 {
5 dir = vec(yaw*RAD, pitch*RAD);
6 loopk(3) if(fabs(dir[k]) < 1e-5f) dir[k] = 0;
7 dir.normalize();
8 clearradiancehintscache();
9 }
10
11 #define PIESKYVARS(name, type) \
12 CVAR1F(IDF_WORLD, sunlight##name, 0, \
13 { \
14 if(!checkmapvariant(type)) return; \
15 clearradiancehintscache(); \
16 cleardeferredlightshaders(); \
17 clearshadowcache(); \
18 }); \
19 FVARF(IDF_WORLD, sunlightscale##name, 0, 1, 16, if(checkmapvariant(type)) clearradiancehintscache()); \
20 vec sunlightdir##name(0, 0, 1); \
21 extern float sunlightpitch##name; \
22 FVARF(IDF_WORLD, sunlightyaw##name, 0, 0, 360, setlightdir(sunlightdir##name, sunlightyaw##name, sunlightpitch##name)); \
23 FVARF(IDF_WORLD, sunlightpitch##name, -90, 90, 90, setlightdir(sunlightdir##name, sunlightyaw##name, sunlightpitch##name)); \
24
25 PIESKYVARS(, MPV_DEF);
26 PIESKYVARS(alt, MPV_ALT);
27
28 #define GETSKYPIE(name, type) \
29 type getpie##name() \
30 { \
31 if(checkmapvariant(MPV_ALT)) return sun##name##alt; \
32 return sun##name; \
33 }
34
35 GETSKYPIE(light, bvec &);
36 GETSKYPIE(lightscale, float);
37 GETSKYPIE(lightdir, vec &);
38 GETSKYPIE(lightyaw, float);
39 GETSKYPIE(lightpitch, float);
40
getlightfx(const extentity & e,int * radius,int * spotlight,vec * color,bool normalize)41 bool getlightfx(const extentity &e, int *radius, int *spotlight, vec *color, bool normalize)
42 {
43 if(!checkmapvariant(e.attrs[9]) || !checkmapeffects(e.attrs[10])) return false;
44 if(color)
45 {
46 *color = vec(e.attrs[1], e.attrs[2], e.attrs[3]);
47 if(e.attrs[7] || e.attrs[8]) color->mul(game::getpalette(e.attrs[7], e.attrs[8]));
48 if(normalize) color->div(255.f);
49 color->max(0);
50 }
51 static int tempradius;
52 if(!radius) radius = &tempradius;
53 *radius = e.attrs[0] ? e.attrs[0] : worldsize; // after this, "0" becomes "off"
54
55 const vector<extentity *> &ents = entities::getents();
56 loopv(e.links) if(ents.inrange(e.links[i]))
57 {
58 extentity &f = *ents[e.links[i]];
59 if(f.type != ET_LIGHTFX || f.attrs[0] < 0 || f.attrs[0] >= LFX_MAX || !checkmapvariant(f.attrs[5]) || !checkmapeffects(f.attrs[6])) continue;
60 bool hastrigger = false;
61 loopvk(f.links) if(ents.inrange(f.links[k]) && ents[f.links[k]]->type != ET_LIGHT)
62 {
63 hastrigger = true;
64 break;
65 }
66 if(hastrigger && !f.spawned())
67 {
68 *radius = 0;
69 break;
70 }
71 int effect = f.attrs[0], millis = lastmillis-f.emit[2], interval = f.emit[0]+f.emit[1];
72 bool hasemit = f.emit[0] && f.emit[1] && f.emit[2], expired = millis >= interval;
73 if(!hasemit || expired)
74 {
75 bool israndom = false;
76 loopk(2)
77 {
78 int val = f.attrs[k+2] > 0 ? f.attrs[k+2] : 500;
79 if(f.attrs[4]&(1<<k))
80 {
81 f.emit[k] = 1+(val >= 2 ? rnd(val-1) : 0);
82 israndom = true;
83 }
84 else f.emit[k] = val;
85 }
86 int oldinterval = interval;
87 interval = f.emit[0]+f.emit[1];
88 if(israndom && interval == oldinterval) israndom = false;
89 f.emit[2] = lastmillis;
90 if(israndom) f.emit[2] -= millis-oldinterval;
91 else f.emit[2] -= f.emit[2]%interval;
92 millis = lastmillis-f.emit[2];
93 }
94 if(millis >= f.emit[0]) loopi(LFX_MAX-1) if(f.attrs[4]&(1<<(LFX_S_MAX+i)))
95 {
96 effect = i+1;
97 break;
98 }
99 float skew = clamp(millis < f.emit[0] ? 1.f-(float(millis)/float(f.emit[0])) : float(millis-f.emit[0])/float(f.emit[1]), 0.f, 1.f);
100 switch(effect)
101 {
102 case LFX_SPOTLIGHT:
103 {
104 if(spotlight && *spotlight <= 0) *spotlight = e.links[i];
105 break;
106 }
107 case LFX_FLICKER:
108 {
109 if(millis >= f.emit[0])
110 *radius -= (f.attrs[1] ? f.attrs[1] : *radius);
111 break;
112 }
113 case LFX_INVPULSE: skew = 1-skew;
114 case LFX_PULSE:
115 {
116 *radius -= (f.attrs[1] ? f.attrs[1] : *radius)*skew;
117 break;
118 }
119 case LFX_INVGLOW: skew = 1-skew;
120 case LFX_GLOW:
121 {
122 if(color) color->mul(skew);
123 *radius -= f.attrs[1]*skew;
124 break;
125 }
126 default: break;
127 }
128 }
129 return *radius > 0;
130 }
131
132 static const surfaceinfo brightsurfaces[6] =
133 {
134 brightsurface,
135 brightsurface,
136 brightsurface,
137 brightsurface,
138 brightsurface,
139 brightsurface
140 };
141
brightencube(cube & c)142 void brightencube(cube &c)
143 {
144 if(!c.ext) newcubeext(c, 0, false);
145 memcpy(c.ext->surfaces, brightsurfaces, sizeof(brightsurfaces));
146 }
147
setsurfaces(cube & c,const surfaceinfo * surfs,const vertinfo * verts,int numverts)148 void setsurfaces(cube &c, const surfaceinfo *surfs, const vertinfo *verts, int numverts)
149 {
150 if(!c.ext || c.ext->maxverts < numverts) newcubeext(c, numverts, false);
151 memcpy(c.ext->surfaces, surfs, sizeof(c.ext->surfaces));
152 memcpy(c.ext->verts(), verts, numverts*sizeof(vertinfo));
153 }
154
setsurface(cube & c,int orient,const surfaceinfo & src,const vertinfo * srcverts,int numsrcverts)155 void setsurface(cube &c, int orient, const surfaceinfo &src, const vertinfo *srcverts, int numsrcverts)
156 {
157 int dstoffset = 0;
158 if(!c.ext) newcubeext(c, numsrcverts, true);
159 else
160 {
161 int numbefore = 0, beforeoffset = 0;
162 loopi(orient)
163 {
164 surfaceinfo &surf = c.ext->surfaces[i];
165 int numverts = surf.totalverts();
166 if(!numverts) continue;
167 numbefore += numverts;
168 beforeoffset = surf.verts + numverts;
169 }
170 int numafter = 0, afteroffset = c.ext->maxverts;
171 for(int i = 5; i > orient; i--)
172 {
173 surfaceinfo &surf = c.ext->surfaces[i];
174 int numverts = surf.totalverts();
175 if(!numverts) continue;
176 numafter += numverts;
177 afteroffset = surf.verts;
178 }
179 if(afteroffset - beforeoffset >= numsrcverts) dstoffset = beforeoffset;
180 else
181 {
182 cubeext *ext = c.ext;
183 if(numbefore + numsrcverts + numafter > c.ext->maxverts)
184 {
185 ext = growcubeext(c.ext, numbefore + numsrcverts + numafter);
186 memcpy(ext->surfaces, c.ext->surfaces, sizeof(ext->surfaces));
187 }
188 int offset = 0;
189 if(numbefore == beforeoffset)
190 {
191 if(numbefore && c.ext != ext) memcpy(ext->verts(), c.ext->verts(), numbefore*sizeof(vertinfo));
192 offset = numbefore;
193 }
194 else loopi(orient)
195 {
196 surfaceinfo &surf = ext->surfaces[i];
197 int numverts = surf.totalverts();
198 if(!numverts) continue;
199 memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
200 surf.verts = offset;
201 offset += numverts;
202 }
203 dstoffset = offset;
204 offset += numsrcverts;
205 if(numafter && offset > afteroffset)
206 {
207 offset += numafter;
208 for(int i = 5; i > orient; i--)
209 {
210 surfaceinfo &surf = ext->surfaces[i];
211 int numverts = surf.totalverts();
212 if(!numverts) continue;
213 offset -= numverts;
214 memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
215 surf.verts = offset;
216 }
217 }
218 if(c.ext != ext) setcubeext(c, ext);
219 }
220 }
221 surfaceinfo &dst = c.ext->surfaces[orient];
222 dst = src;
223 dst.verts = dstoffset;
224 if(srcverts) memcpy(c.ext->verts() + dstoffset, srcverts, numsrcverts*sizeof(vertinfo));
225 }
226
insert(ushort & tx,ushort & ty,ushort tw,ushort th)227 bool PackNode::insert(ushort &tx, ushort &ty, ushort tw, ushort th)
228 {
229 if((available < tw && available < th) || w < tw || h < th)
230 return false;
231 if(child1)
232 {
233 bool inserted = child1->insert(tx, ty, tw, th) ||
234 child2->insert(tx, ty, tw, th);
235 available = max(child1->available, child2->available);
236 if(!available) discardchildren();
237 return inserted;
238 }
239 if(w == tw && h == th)
240 {
241 available = 0;
242 tx = x;
243 ty = y;
244 return true;
245 }
246
247 if(w - tw > h - th)
248 {
249 child1 = new PackNode(x, y, tw, h);
250 child2 = new PackNode(x + tw, y, w - tw, h);
251 }
252 else
253 {
254 child1 = new PackNode(x, y, w, th);
255 child2 = new PackNode(x, y + th, w, h - th);
256 }
257
258 bool inserted = child1->insert(tx, ty, tw, th);
259 available = max(child1->available, child2->available);
260 return inserted;
261 }
262
reserve(ushort tx,ushort ty,ushort tw,ushort th)263 void PackNode::reserve(ushort tx, ushort ty, ushort tw, ushort th)
264 {
265 if(tx + tw <= x || tx >= x + w || ty + th <= y || ty >= y + h) return;
266 if(child1)
267 {
268 child1->reserve(tx, ty, tw, th);
269 child2->reserve(tx, ty, tw, th);
270 available = max(child1->available, child2->available);
271 return;
272 }
273 int dx1 = tx - x, dx2 = x + w - tx - tw, dx = max(dx1, dx2),
274 dy1 = ty - y, dy2 = y + h - ty - th, dy = max(dy1, dy2),
275 split;
276 if(dx > dy)
277 {
278 if(dx1 > dx2) split = min(dx1, int(w));
279 else split = w - max(dx2, 0);
280 if(w - split <= 0)
281 {
282 w = split;
283 available = min(w, h);
284 if(dy > 0) reserve(tx, ty, tw, th);
285 else if(tx <= x && tx + tw >= x + w) available = 0;
286 return;
287 }
288 if(split <= 0)
289 {
290 x += split;
291 w -= split;
292 available = min(w, h);
293 if(dy > 0) reserve(tx, ty, tw, th);
294 else if(tx <= x && tx + tw >= x + w) available = 0;
295 return;
296 }
297 child1 = new PackNode(x, y, split, h);
298 child2 = new PackNode(x + split, y, w - split, h);
299 }
300 else
301 {
302 if(dy1 > dy2) split = min(dy1, int(h));
303 else split = h - max(dy2, 0);
304 if(h - split <= 0)
305 {
306 h = split;
307 available = min(w, h);
308 if(dx > 0) reserve(tx, ty, tw, th);
309 else if(ty <= y && ty + th >= y + h) available = 0;
310 return;
311 }
312 if(split <= 0)
313 {
314 y += split;
315 h -= split;
316 available = min(w, h);
317 if(dx > 0) reserve(tx, ty, tw, th);
318 else if(ty <= y && ty + th >= y + h) available = 0;
319 return;
320 }
321 child1 = new PackNode(x, y, w, split);
322 child2 = new PackNode(x, y + split, w, h - split);
323 }
324 child1->reserve(tx, ty, tw, th);
325 child2->reserve(tx, ty, tw, th);
326 available = max(child1->available, child2->available);
327 }
328
clearsurfaces(cube * c)329 static void clearsurfaces(cube *c)
330 {
331 loopi(8)
332 {
333 if(c[i].ext)
334 {
335 loopj(6)
336 {
337 surfaceinfo &surf = c[i].ext->surfaces[j];
338 if(!surf.used()) continue;
339 surf.clear();
340 int numverts = surf.numverts&MAXFACEVERTS;
341 if(numverts)
342 {
343 if(!(c[i].merged&(1<<j))) { surf.numverts &= ~MAXFACEVERTS; continue; }
344
345 vertinfo *verts = c[i].ext->verts() + surf.verts;
346 loopk(numverts)
347 {
348 vertinfo &v = verts[k];
349 v.norm = 0;
350 }
351 }
352 }
353 }
354 if(c[i].children) clearsurfaces(c[i].children);
355 }
356 }
357
358 #define LIGHTCACHESIZE 1024
359
360 static struct lightcacheentry
361 {
362 int x, y;
363 vector<int> lights;
364 } lightcache[LIGHTCACHESIZE];
365
366 #define LIGHTCACHEHASH(x, y) (((((x)^(y))<<5) + (((x)^(y))>>5)) & (LIGHTCACHESIZE - 1))
367
368 VARF(0, lightcachesize, 4, 6, 12, clearlightcache());
369
clearlightcache(int id)370 void clearlightcache(int id)
371 {
372 if(id >= 0)
373 {
374 const extentity &light = *entities::getents()[id];
375 int radius = light.attrs[0];
376 if(radius <= 0) return;
377 for(int x = int(max(light.o.x-radius, 0.0f))>>lightcachesize, ex = int(min(light.o.x+radius, worldsize-1.0f))>>lightcachesize; x <= ex; x++)
378 for(int y = int(max(light.o.y-radius, 0.0f))>>lightcachesize, ey = int(min(light.o.y+radius, worldsize-1.0f))>>lightcachesize; y <= ey; y++)
379 {
380 lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
381 if(lce.x != x || lce.y != y) continue;
382 lce.x = -1;
383 lce.lights.setsize(0);
384 }
385 return;
386 }
387
388 for(lightcacheentry *lce = lightcache; lce < &lightcache[LIGHTCACHESIZE]; lce++)
389 {
390 lce->x = -1;
391 lce->lights.setsize(0);
392 }
393 }
394
checklightcache(int x,int y)395 const vector<int> &checklightcache(int x, int y)
396 {
397 x >>= lightcachesize;
398 y >>= lightcachesize;
399 lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
400 if(lce.x == x && lce.y == y) return lce.lights;
401
402 lce.lights.setsize(0);
403 int csize = 1<<lightcachesize, cx = x<<lightcachesize, cy = y<<lightcachesize;
404 const vector<extentity *> &ents = entities::getents();
405 loopv(ents)
406 {
407 const extentity &light = *ents[i];
408 switch(light.type)
409 {
410 case ET_LIGHT:
411 {
412 int radius = light.attrs[0];
413 if(!getlightfx(light, &radius) ||
414 light.o.x + radius < cx || light.o.x - radius > cx + csize ||
415 light.o.y + radius < cy || light.o.y - radius > cy + csize)
416 continue;
417 break;
418 }
419 default: continue;
420 }
421 lce.lights.add(i);
422 }
423
424 lce.x = x;
425 lce.y = y;
426 return lce.lights;
427 }
428
429 static uint lightprogress = 0;
430
431 bool calclight_canceled = false;
432 volatile bool check_calclight_progress = false;
433
check_calclight_canceled()434 void check_calclight_canceled()
435 {
436 if(interceptkey(SDLK_ESCAPE))
437 {
438 calclight_canceled = true;
439 }
440 if(!calclight_canceled) check_calclight_progress = false;
441 }
442
show_calclight_progress()443 void show_calclight_progress()
444 {
445 float amt = float(lightprogress)/float(allocnodes);
446 progress(amt, "Computing lighting... (ESC to abort)");
447 }
448
calcsurfaces(cube & c,const ivec & co,int size,int usefacemask,int preview=0)449 static void calcsurfaces(cube &c, const ivec &co, int size, int usefacemask, int preview = 0)
450 {
451 surfaceinfo surfaces[6];
452 vertinfo litverts[6*2*MAXFACEVERTS];
453 int numlitverts = 0;
454 memset(surfaces, 0, sizeof(surfaces));
455 loopi(6)
456 {
457 int usefaces = usefacemask&0xF;
458 usefacemask >>= 4;
459 if(!usefaces)
460 {
461 if(!c.ext) continue;
462 surfaceinfo &surf = surfaces[i];
463 surf = c.ext->surfaces[i];
464 int numverts = surf.totalverts();
465 if(numverts)
466 {
467 memcpy(&litverts[numlitverts], c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
468 surf.verts = numlitverts;
469 numlitverts += numverts;
470 }
471 continue;
472 }
473
474 VSlot &vslot = lookupvslot(c.texture[i], false);
475 surfaceinfo &surf = surfaces[i];
476 vertinfo *curlitverts = &litverts[numlitverts];
477 int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0;
478 ivec mo(co);
479 int msz = size, convex = 0;
480 if(numverts)
481 {
482 vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
483 loopj(numverts) curlitverts[j].set(verts[j].getxyz());
484 if(c.merged&(1<<i))
485 {
486 msz = 1<<calcmergedsize(i, mo, size, verts, numverts);
487 mo.mask(~(msz-1));
488
489 if(!(surf.numverts&MAXFACEVERTS))
490 {
491 surf.verts = numlitverts;
492 surf.numverts |= numverts;
493 numlitverts += numverts;
494 }
495 }
496 else if(!flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
497 }
498 else
499 {
500 ivec v[4];
501 genfaceverts(c, i, v);
502 if(!flataxisface(c, i)) convex = faceconvexity(v);
503 int order = usefaces&4 || convex < 0 ? 1 : 0;
504 ivec vo = ivec(co).mask(0xFFF).shl(3);
505 curlitverts[numverts++].set(v[order].mul(size).add(vo));
506 if(usefaces&1) curlitverts[numverts++].set(v[order+1].mul(size).add(vo));
507 curlitverts[numverts++].set(v[order+2].mul(size).add(vo));
508 if(usefaces&2) curlitverts[numverts++].set(v[(order+3)&3].mul(size).add(vo));
509 }
510
511 vec pos[MAXFACEVERTS], n[MAXFACEVERTS], po(ivec(co).mask(~0xFFF));
512 loopj(numverts) pos[j] = vec(curlitverts[j].getxyz()).mul(1.0f/8).add(po);
513
514 int smooth = vslot.slot->smooth;
515 plane planes[2];
516 int numplanes = 0;
517 planes[numplanes++].toplane(pos[0], pos[1], pos[2]);
518 if(numverts < 4 || !convex) loopk(numverts) findnormal(pos[k], smooth, planes[0], n[k]);
519 else
520 {
521 planes[numplanes++].toplane(pos[0], pos[2], pos[3]);
522 vec avg = vec(planes[0]).add(planes[1]).normalize();
523 findnormal(pos[0], smooth, avg, n[0]);
524 findnormal(pos[1], smooth, planes[0], n[1]);
525 findnormal(pos[2], smooth, avg, n[2]);
526 for(int k = 3; k < numverts; k++) findnormal(pos[k], smooth, planes[1], n[k]);
527 }
528
529 loopk(numverts) curlitverts[k].norm = encodenormal(n[k]);
530 if(!(surf.numverts&MAXFACEVERTS))
531 {
532 surf.verts = numlitverts;
533 surf.numverts |= numverts;
534 numlitverts += numverts;
535 }
536
537 if(preview) { surf.numverts |= preview; continue; }
538
539 int surflayer = LAYER_TOP;
540 if(vslot.layer)
541 {
542 int x1 = curlitverts[numverts-1].x, y1 = curlitverts[numverts-1].y, x2 = x1, y2 = y1;
543 loopj(numverts-1)
544 {
545 const vertinfo &v = curlitverts[j];
546 x1 = min(x1, int(v.x));
547 y1 = min(y1, int(v.y));
548 x2 = max(x2, int(v.x));
549 y2 = max(y2, int(v.y));
550 }
551 x2 = max(x2, x1+1);
552 y2 = max(y2, y1+1);
553 x1 = (x1>>3) + (co.x&~0xFFF);
554 y1 = (y1>>3) + (co.y&~0xFFF);
555 x2 = ((x2+7)>>3) + (co.x&~0xFFF);
556 y2 = ((y2+7)>>3) + (co.y&~0xFFF);
557 surflayer = calcblendlayer(x1, y1, x2, y2);
558 }
559 surf.numverts |= surflayer;
560 }
561 if(preview) setsurfaces(c, surfaces, litverts, numlitverts);
562 else loopk(6)
563 {
564 surfaceinfo &surf = surfaces[k];
565 if(surf.used())
566 {
567 cubeext *ext = c.ext && c.ext->maxverts >= numlitverts ? c.ext : growcubeext(c.ext, numlitverts);
568 memcpy(ext->surfaces, surfaces, sizeof(ext->surfaces));
569 memcpy(ext->verts(), litverts, numlitverts*sizeof(vertinfo));
570 if(c.ext != ext) setcubeext(c, ext);
571 break;
572 }
573 }
574 }
575
calcsurfaces(cube * c,const ivec & co,int size)576 static void calcsurfaces(cube *c, const ivec &co, int size)
577 {
578 CHECK_CALCLIGHT_PROGRESS(return, show_calclight_progress);
579
580 lightprogress++;
581
582 loopi(8)
583 {
584 ivec o(i, co, size);
585 if(c[i].children)
586 calcsurfaces(c[i].children, o, size >> 1);
587 else if(!isempty(c[i]))
588 {
589 if(c[i].ext)
590 {
591 loopj(6) c[i].ext->surfaces[j].clear();
592 }
593 int usefacemask = 0;
594 loopj(6) if(c[i].texture[j] != DEFAULT_SKY && (!(c[i].merged&(1<<j)) || (c[i].ext && c[i].ext->surfaces[j].numverts&MAXFACEVERTS)))
595 {
596 usefacemask |= visibletris(c[i], j, o, size)<<(4*j);
597 }
598 if(usefacemask) calcsurfaces(c[i], o, size, usefacemask);
599 }
600 }
601 }
602
previewblends(cube & c,const ivec & o,int size)603 static inline bool previewblends(cube &c, const ivec &o, int size)
604 {
605 if(isempty(c) || c.material&MAT_ALPHA) return false;
606 int usefacemask = 0;
607 loopj(6) if(c.texture[j] != DEFAULT_SKY && lookupvslot(c.texture[j], false).layer)
608 usefacemask |= visibletris(c, j, o, size)<<(4*j);
609 if(!usefacemask) return false;
610 int layer = calcblendlayer(o.x, o.y, o.x + size, o.y + size);
611 if(!(layer&LAYER_BOTTOM))
612 {
613 if(!c.ext) return false;
614 bool blends = false;
615 loopi(6) if(c.ext->surfaces[i].numverts&LAYER_BOTTOM)
616 {
617 c.ext->surfaces[i].brighten();
618 blends = true;
619 }
620 return blends;
621 }
622 calcsurfaces(c, o, size, usefacemask, layer);
623 return true;
624 }
625
previewblends(cube * c,const ivec & co,int size,const ivec & bo,const ivec & bs)626 static bool previewblends(cube *c, const ivec &co, int size, const ivec &bo, const ivec &bs)
627 {
628 bool changed = false;
629 loopoctabox(co, size, bo, bs)
630 {
631 ivec o(i, co, size);
632 cubeext *ext = c[i].ext;
633 if(ext && ext->va && ext->va->hasmerges)
634 {
635 changed = true;
636 destroyva(ext->va);
637 ext->va = NULL;
638 invalidatemerges(c[i], co, size, true);
639 }
640 if(c[i].children ? previewblends(c[i].children, o, size/2, bo, bs) : previewblends(c[i], o, size))
641 {
642 changed = true;
643 ext = c[i].ext;
644 if(ext && ext->va)
645 {
646 destroyva(ext->va);
647 ext->va = NULL;
648 }
649 }
650 }
651 return changed;
652 }
653
previewblends(const ivec & bo,const ivec & bs)654 void previewblends(const ivec &bo, const ivec &bs)
655 {
656 updateblendtextures(bo.x, bo.y, bo.x+bs.x, bo.y+bs.y);
657 if(previewblends(worldroot, ivec(0, 0, 0), worldsize/2, bo, bs))
658 commitchanges(true);
659 }
660
661 extern int filltjoints;
662
calclighttimer(Uint32 interval,void * param)663 static Uint32 calclighttimer(Uint32 interval, void *param)
664 {
665 check_calclight_progress = true;
666 return interval;
667 }
668
calclight()669 void calclight()
670 {
671 progress(-1, "Computing lighting... (ESC to abort)");
672 remip();
673 optimizeblendmap();
674 clearsurfaces(worldroot);
675 lightprogress = 0;
676 calclight_canceled = false;
677 check_calclight_progress = false;
678 SDL_TimerID timer = SDL_AddTimer(250, calclighttimer, NULL);
679 Uint32 start = SDL_GetTicks();
680 calcnormals(filltjoints > 0);
681 calcsurfaces(worldroot, ivec(0, 0, 0), worldsize >> 1);
682 clearnormals();
683 Uint32 end = SDL_GetTicks();
684 if(timer) SDL_RemoveTimer(timer);
685 progress(0, "Lighting done...");
686 allchanged();
687 if(calclight_canceled) conoutf("Calclight aborted");
688 else conoutf("Computed lighting (%.1f seconds)", (end - start) / 1000.0f);
689 }
690
mpcalclight(bool local)691 void mpcalclight(bool local)
692 {
693 if(local) client::edittrigger(sel, EDIT_CALCLIGHT);
694 calclight();
695 }
696
697 ICOMMAND(0, calclight, "", (), mpcalclight(true));
698
699 VAR(0, fullbright, 0, 0, 1);
700 VAR(0, fullbrightlevel, 0, 160, 255);
701
clearlights()702 void clearlights()
703 {
704 clearlightcache();
705 clearshadowcache();
706 cleardeferredlightshaders();
707 resetsmoothgroups();
708 }
709
initlights()710 void initlights()
711 {
712 clearlightcache();
713 clearshadowcache();
714 loaddeferredlightshaders();
715 }
716
lightreaching(const vec & target,vec & color,vec & dir,bool fast,extentity * t,float minambient)717 void lightreaching(const vec &target, vec &color, vec &dir, bool fast, extentity *t, float minambient)
718 {
719 if(fullbright && editmode)
720 {
721 color = vec(1, 1, 1);
722 dir = vec(0, 0, 1);
723 return;
724 }
725
726 color = dir = vec(0, 0, 0);
727 const vector<extentity *> &ents = entities::getents();
728 const vector<int> &lights = checklightcache(int(target.x), int(target.y));
729 loopv(lights)
730 {
731 extentity &e = *ents[lights[i]];
732 if(e.type != ET_LIGHT) continue;
733
734 float intensity = 1;
735 int radius = e.attrs[0], slight = -1;
736 vec lightcol(1, 1, 1);
737 if(!getlightfx(e, &radius, &slight, &lightcol)) continue;
738
739 vec ray(target);
740 ray.sub(e.o);
741 if(ents.inrange(slight))
742 {
743 extentity &spotlight = *ents[slight];
744 vec spot = vec(spotlight.o).sub(e.o).normalize();
745 float spotatten = 1 - (1 - ray.dot(spot)) / (1 - cos360(clamp(int(spotlight.attrs[1]), 1, 89)));
746 if(spotatten <= 0) continue;
747 intensity *= spotatten;
748 }
749 float mag = ray.magnitude();
750 if(mag >= float(radius)) continue;
751
752 intensity *= 1 - mag / float(radius);
753
754 if(mag < 1e-4f) ray = vec(0, 0, -1);
755 else
756 {
757 ray.div(mag);
758 if(shadowray(e.o, ray, mag, RAY_SHADOW | RAY_POLY, t) < mag)
759 continue;
760 }
761
762 color.add(vec(lightcol).mul(intensity));
763 dir.add(vec(ray).mul(-intensity*lightcol.x*lightcol.y*lightcol.z));
764 }
765 bvec pie = getpielight();
766 vec piedir = getpielightdir();
767 if(!pie.iszero() && shadowray(target, piedir, 1e16f, RAY_SHADOW | RAY_POLY, t) > 1e15f)
768 {
769 vec lightcol = pie.tocolor().mul(getpielightscale());
770 color.add(lightcol);
771 dir.add(vec(piedir).mul(lightcol.x*lightcol.y*lightcol.z));
772 }
773 color.max(getambient().tocolor().max(minambient)).min(1.5f);
774 if(dir.iszero()) dir = vec(0, 0, 1);
775 else dir.normalize();
776 }
777
778