Line data Source code
1 : // texture.cpp: texture slot management
2 :
3 : #include "../libprimis-headers/cube.h"
4 : #include "../../shared/geomexts.h"
5 : #include "../../shared/glexts.h"
6 : #include "../../shared/stream.h"
7 :
8 : #include "SDL_image.h"
9 :
10 : #include "imagedata.h"
11 : #include "octarender.h"
12 : #include "renderwindow.h"
13 : #include "shader.h"
14 : #include "shaderparam.h"
15 : #include "texture.h"
16 :
17 : #include "world/light.h"
18 : #include "world/material.h"
19 : #include "world/octaedit.h"
20 : #include "world/octaworld.h"
21 : #include "world/world.h"
22 :
23 : #include "interface/console.h"
24 : #include "interface/control.h"
25 :
26 : extern const texrotation texrotations[8] =
27 : {
28 : { false, false, false }, // 0: default
29 : { false, true, true }, // 1: 90 degrees
30 : { true, true, false }, // 2: 180 degrees
31 : { true, false, true }, // 3: 270 degrees
32 : { true, false, false }, // 4: flip X
33 : { false, true, false }, // 5: flip Y
34 : { false, false, true }, // 6: transpose
35 : { true, true, true }, // 7: flipped transpose
36 : };
37 :
38 : //copies every other pixel into a destination buffer
39 : //sw,sh are source width/height
40 : template<int BPP>
41 3 : static void halvetexture(const uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst)
42 : {
43 10 : for(const uchar *yend = &src[sh*stride]; src < yend;)
44 : {
45 28 : for(const uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += 2*BPP, dst += BPP)
46 : {
47 42 : for(int i = 0; i < BPP; ++i)
48 : {
49 21 : dst[i] = (static_cast<uint>(xsrc[i]) + static_cast<uint>(xsrc[i+BPP]) + static_cast<uint>(xsrc[stride+i]) + static_cast<uint>(xsrc[stride+i+BPP]))>>2;
50 : }
51 : }
52 7 : src += 2*stride;
53 : }
54 3 : }
55 :
56 : template<int BPP>
57 0 : static void shifttexture(const uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst, uint dw, uint dh)
58 : {
59 0 : uint wfrac = sw/dw,
60 0 : hfrac = sh/dh,
61 0 : wshift = 0,
62 0 : hshift = 0;
63 0 : while(dw<<wshift < sw)
64 : {
65 0 : wshift++;
66 : }
67 0 : while(dh<<hshift < sh)
68 : {
69 0 : hshift++;
70 : }
71 0 : uint tshift = wshift + hshift;
72 0 : for(const uchar *yend = &src[sh*stride]; src < yend;)
73 : {
74 0 : for(const uchar *xend = &src[sw*BPP], *xsrc = src; xsrc < xend; xsrc += wfrac*BPP, dst += BPP)
75 : {
76 :
77 0 : uint t[BPP] = {0};
78 0 : for(const uchar *ycur = xsrc, *xend = &ycur[wfrac*BPP], *yend = &src[hfrac*stride];
79 0 : ycur < yend;
80 0 : ycur += stride, xend += stride)
81 : {
82 : //going to (xend - 1) seems to be necessary to avoid buffer overrun
83 0 : for(const uchar *xcur = ycur; xcur < xend -1; xcur += BPP)
84 : {
85 0 : for(int i = 0; i < BPP; ++i)
86 : {
87 0 : t[i] += xcur[i];
88 : }
89 : }
90 : }
91 0 : for(int i = 0; i < BPP; ++i)
92 : {
93 0 : dst[i] = t[i] >> tshift;
94 : }
95 : }
96 0 : src += hfrac*stride;
97 : }
98 0 : }
99 :
100 : template<int BPP>
101 0 : static void scaletexture(const uchar * RESTRICT src, uint sw, uint sh, uint stride, uchar * RESTRICT dst, uint dw, uint dh)
102 : {
103 0 : uint wfrac = (sw<<12)/dw,
104 0 : hfrac = (sh<<12)/dh,
105 0 : darea = dw*dh,
106 0 : sarea = sw*sh;
107 : int over, under;
108 : //the for loops here are merely to increment over & under vars which are used later
109 0 : for(over = 0; (darea>>over) > sarea; over++)
110 : {
111 : //(empty body)
112 : }
113 0 : for(under = 0; (darea<<under) < sarea; under++)
114 : {
115 : //(empty body)
116 : }
117 0 : uint cscale = std::clamp(under, over - 12, 12),
118 0 : ascale = std::clamp(12 + under - over, 0, 24),
119 0 : dscale = ascale + 12 - cscale,
120 0 : area = (static_cast<ullong>(darea)<<ascale)/sarea;
121 0 : dw *= wfrac;
122 0 : dh *= hfrac;
123 0 : for(uint y = 0; y < dh; y += hfrac)
124 : {
125 0 : const uint yn = y + hfrac - 1,
126 0 : yi = y>>12, h = (yn>>12) - yi,
127 0 : ylow = ((yn|(-static_cast<int>(h)>>24))&0xFFFU) + 1 - (y&0xFFFU),
128 0 : yhigh = (yn&0xFFFU) + 1;
129 0 : const uchar *ysrc = &src[yi*stride];
130 0 : for(uint x = 0; x < dw; x += wfrac, dst += BPP)
131 : {
132 0 : const uint xn = x + wfrac - 1,
133 0 : xi = x>>12,
134 0 : w = (xn>>12) - xi,
135 0 : xlow = ((w+0xFFFU)&0x1000U) - (x&0xFFFU),
136 0 : xhigh = (xn&0xFFFU) + 1;
137 0 : const uchar *xsrc = &ysrc[xi*BPP],
138 0 : *xend = &xsrc[w*BPP];
139 0 : uint t[BPP] = {0};
140 0 : for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
141 : {
142 0 : for(int i = 0; i < BPP; ++i)
143 : {
144 0 : t[i] += xcur[i];
145 : }
146 : }
147 0 : for(int i = 0; i < BPP; ++i)
148 : {
149 0 : t[i] = (ylow*(t[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale;
150 : }
151 0 : if(h)
152 : {
153 0 : xsrc += stride;
154 0 : xend += stride;
155 0 : for(uint hcur = h; --hcur; xsrc += stride, xend += stride)
156 : {
157 0 : uint c[BPP] = {0};
158 0 : for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
159 0 : for(int i = 0; i < BPP; ++i)
160 : {
161 0 : c[i] += xcur[i];
162 : }
163 0 : for(int i = 0; i < BPP; ++i)
164 : {
165 0 : t[i] += ((c[i]<<12) + xsrc[i]*xlow + xend[i]*xhigh)>>cscale;
166 : }
167 : }
168 0 : uint c[BPP] = {0};
169 0 : for(const uchar *xcur = &xsrc[BPP]; xcur < xend; xcur += BPP)
170 0 : for(int i = 0; i < BPP; ++i)
171 : {
172 0 : c[i] += xcur[i];
173 : }
174 0 : for(int i = 0; i < BPP; ++i)
175 : {
176 0 : t[i] += (yhigh*(c[i] + ((xsrc[i]*xlow + xend[i]*xhigh)>>12)))>>cscale;
177 : }
178 : }
179 0 : for(int i = 0; i < BPP; ++i)
180 : {
181 0 : dst[i] = (t[i] * area)>>dscale;
182 : }
183 : }
184 : }
185 0 : }
186 :
187 3 : void scaletexture(const uchar * RESTRICT src, uint sw, uint sh, uint bpp, uint pitch, uchar * RESTRICT dst, uint dw, uint dh)
188 : {
189 3 : if(sw == dw*2 && sh == dh*2)
190 : {
191 3 : switch(bpp)
192 : {
193 3 : case 1: return halvetexture<1>(src, sw, sh, pitch, dst);
194 0 : case 2: return halvetexture<2>(src, sw, sh, pitch, dst);
195 0 : case 3: return halvetexture<3>(src, sw, sh, pitch, dst);
196 0 : case 4: return halvetexture<4>(src, sw, sh, pitch, dst);
197 : }
198 : }
199 0 : else if(sw < dw || sh < dh || sw&(sw-1) || sh&(sh-1) || dw&(dw-1) || dh&(dh-1))
200 : {
201 0 : switch(bpp)
202 : {
203 0 : case 1: return scaletexture<1>(src, sw, sh, pitch, dst, dw, dh);
204 0 : case 2: return scaletexture<2>(src, sw, sh, pitch, dst, dw, dh);
205 0 : case 3: return scaletexture<3>(src, sw, sh, pitch, dst, dw, dh);
206 0 : case 4: return scaletexture<4>(src, sw, sh, pitch, dst, dw, dh);
207 : }
208 : }
209 : else
210 : {
211 0 : switch(bpp)
212 : {
213 0 : case 1: return shifttexture<1>(src, sw, sh, pitch, dst, dw, dh);
214 0 : case 2: return shifttexture<2>(src, sw, sh, pitch, dst, dw, dh);
215 0 : case 3: return shifttexture<3>(src, sw, sh, pitch, dst, dw, dh);
216 0 : case 4: return shifttexture<4>(src, sw, sh, pitch, dst, dw, dh);
217 : }
218 : }
219 : }
220 :
221 : template<int BPP>
222 0 : static void reorienttexture(const uchar * RESTRICT src, int sw, int sh, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy)
223 : {
224 0 : int stridex = BPP,
225 0 : stridey = BPP;
226 0 : if(swapxy)
227 : {
228 0 : stridex *= sh;
229 : }
230 : else
231 : {
232 0 : stridey *= sw;
233 : }
234 0 : if(flipx)
235 : {
236 0 : dst += (sw-1)*stridex;
237 0 : stridex = -stridex;
238 : }
239 0 : if(flipy)
240 : {
241 0 : dst += (sh-1)*stridey;
242 0 : stridey = -stridey;
243 : }
244 0 : const uchar *srcrow = src;
245 0 : for(int i = 0; i < sh; ++i)
246 : {
247 : uchar* curdst;
248 : const uchar* src;
249 : const uchar* end;
250 0 : for(curdst = dst, src = srcrow, end = &srcrow[sw*BPP]; src < end;)
251 : {
252 0 : for(int k = 0; k < BPP; ++k)
253 : {
254 0 : curdst[k] = *src++;
255 : }
256 0 : curdst += stridex;
257 : }
258 0 : srcrow += stride;
259 0 : dst += stridey;
260 : }
261 0 : }
262 :
263 0 : void reorienttexture(const uchar * RESTRICT src, int sw, int sh, int bpp, int stride, uchar * RESTRICT dst, bool flipx, bool flipy, bool swapxy)
264 : {
265 0 : switch(bpp)
266 : {
267 0 : case 1: return reorienttexture<1>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
268 0 : case 2: return reorienttexture<2>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
269 0 : case 3: return reorienttexture<3>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
270 0 : case 4: return reorienttexture<4>(src, sw, sh, stride, dst, flipx, flipy, swapxy);
271 : }
272 : }
273 : /* var min default max */
274 : VAR(hwtexsize, 1, 0, 0);
275 : VAR(hwcubetexsize, 1, 0, 0);
276 : VAR(hwmaxaniso, 1, 0, 0);
277 : VAR(hwtexunits, 1, 0, 0);
278 : VAR(hwvtexunits, 1, 0, 0);
279 0 : VARFP(maxtexsize, 0, 0, 1<<12, initwarning("texture quality", Init_Load));
280 0 : VARFP(reducefilter, 0, 1, 1, initwarning("texture quality", Init_Load));
281 0 : VARF(trilinear, 0, 1, 1, initwarning("texture filtering", Init_Load));
282 0 : VARF(bilinear, 0, 1, 1, initwarning("texture filtering", Init_Load));
283 0 : VARFP(aniso, 0, 0, 16, initwarning("texture filtering", Init_Load));
284 :
285 1 : static int formatsize(GLenum format)
286 : {
287 1 : switch(format)
288 : {
289 1 : case GL_RED:
290 : {
291 1 : return 1;
292 : }
293 0 : case GL_RG:
294 : {
295 0 : return 2;
296 : }
297 0 : case GL_RGB:
298 : {
299 0 : return 3;
300 : }
301 0 : case GL_RGBA:
302 : {
303 0 : return 4;
304 : }
305 0 : default:
306 : {
307 0 : return 4;
308 : }
309 : }
310 : }
311 :
312 1 : static void resizetexture(int w, int h, bool mipmap, bool canreduce, GLenum target, int compress, int &tw, int &th)
313 : {
314 1 : int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize,
315 1 : sizelimit = mipmap && maxtexsize ? std::min(maxtexsize, hwlimit) : hwlimit;
316 1 : if(compress > 0)
317 : {
318 0 : w = std::max(w/compress, 1);
319 0 : h = std::max(h/compress, 1);
320 : }
321 1 : w = std::min(w, sizelimit);
322 1 : h = std::min(h, sizelimit);
323 1 : tw = w;
324 1 : th = h;
325 1 : }
326 :
327 5 : static int texalign(int w, int bpp)
328 : {
329 5 : int stride = w*bpp;
330 5 : if(stride&1)
331 : {
332 1 : return 1;
333 : }
334 4 : if(stride&2)
335 : {
336 1 : return 2;
337 : }
338 3 : return 4;
339 : }
340 :
341 1 : static void uploadtexture(GLenum target, GLenum internal, int tw, int th, GLenum format, GLenum type, const void *pixels, int pw, int ph, int pitch, bool mipmap)
342 : {
343 1 : int bpp = formatsize(format),
344 1 : row = 0,
345 1 : rowalign = 0;
346 1 : if(!pitch)
347 : {
348 0 : pitch = pw*bpp;
349 : }
350 1 : uchar *buf = nullptr;
351 1 : if(pw!=tw || ph!=th)
352 : {
353 0 : buf = new uchar[tw*th*bpp];
354 0 : scaletexture(static_cast<uchar *>(const_cast<void *>(pixels)), pw, ph, bpp, pitch, buf, tw, th);
355 : }
356 1 : else if(tw*bpp != pitch)
357 : {
358 0 : row = pitch/bpp;
359 0 : rowalign = texalign(pitch, 1);
360 0 : while(rowalign > 0 && ((row*bpp + rowalign - 1)/rowalign)*rowalign != pitch)
361 : {
362 0 : rowalign >>= 1;
363 : }
364 0 : if(!rowalign)
365 : {
366 0 : row = 0;
367 0 : buf = new uchar[tw*th*bpp];
368 0 : for(int i = 0; i < th; ++i)
369 : {
370 0 : std::memcpy(&buf[i*tw*bpp], &(const_cast<uchar *>(reinterpret_cast<const uchar *>(pixels)))[i*pitch], tw*bpp);
371 : }
372 : }
373 : }
374 1 : for(int level = 0, align = 0;; level++)
375 : {
376 4 : uchar *src = buf ? buf : const_cast<uchar *>(reinterpret_cast<const uchar *>(pixels));
377 4 : if(buf)
378 : {
379 3 : pitch = tw*bpp;
380 : }
381 4 : int srcalign = row > 0 ? rowalign : texalign(pitch, 1);
382 4 : if(align != srcalign)
383 : {
384 3 : glPixelStorei(GL_UNPACK_ALIGNMENT, align = srcalign);
385 : }
386 4 : if(row > 0)
387 : {
388 0 : glPixelStorei(GL_UNPACK_ROW_LENGTH, row);
389 : }
390 4 : if(target==GL_TEXTURE_1D)
391 : {
392 0 : glTexImage1D(target, level, internal, tw, 0, format, type, src);
393 : }
394 : else
395 : {
396 4 : glTexImage2D(target, level, internal, tw, th, 0, format, type, src);
397 : }
398 4 : if(row > 0)
399 : {
400 0 : glPixelStorei(GL_UNPACK_ROW_LENGTH, row = 0);
401 : }
402 4 : if(!mipmap || std::max(tw, th) <= 1)
403 : {
404 1 : break;
405 : }
406 3 : int srcw = tw,
407 3 : srch = th;
408 3 : if(tw > 1)
409 : {
410 3 : tw /= 2;
411 : }
412 3 : if(th > 1)
413 : {
414 3 : th /= 2;
415 : }
416 3 : if(src)
417 : {
418 3 : if(!buf)
419 : {
420 1 : buf = new uchar[tw*th*bpp];
421 : }
422 3 : scaletexture(src, srcw, srch, bpp, pitch, buf, tw, th);
423 : }
424 3 : }
425 1 : if(buf)
426 : {
427 1 : delete[] buf;
428 : }
429 1 : }
430 :
431 0 : static void uploadcompressedtexture(GLenum target, GLenum subtarget, GLenum format, int w, int h, const uchar *data, int align, int blocksize, int levels, bool mipmap)
432 : {
433 0 : int hwlimit = target==GL_TEXTURE_CUBE_MAP ? hwcubetexsize : hwtexsize,
434 0 : sizelimit = levels > 1 && maxtexsize ? std::min(maxtexsize, hwlimit) : hwlimit;
435 0 : int level = 0;
436 0 : for(int i = 0; i < levels; ++i)
437 : {
438 0 : int size = ((w + align-1)/align) * ((h + align-1)/align) * blocksize;
439 0 : if(w <= sizelimit && h <= sizelimit)
440 : {
441 0 : if(target==GL_TEXTURE_1D)
442 : {
443 0 : glCompressedTexImage1D(subtarget, level, format, w, 0, size, data);
444 : }
445 : else
446 : {
447 0 : glCompressedTexImage2D(subtarget, level, format, w, h, 0, size, data);
448 : }
449 0 : level++;
450 0 : if(!mipmap)
451 : {
452 0 : break;
453 : }
454 : }
455 0 : if(std::max(w, h) <= 1)
456 : {
457 0 : break;
458 : }
459 0 : if(w > 1)
460 : {
461 0 : w /= 2;
462 : }
463 0 : if(h > 1)
464 : {
465 0 : h /= 2;
466 : }
467 0 : data += size;
468 : }
469 0 : }
470 :
471 1 : static GLenum textarget(GLenum subtarget)
472 : {
473 1 : switch(subtarget)
474 : {
475 0 : case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
476 : case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
477 : case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
478 : case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
479 : case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
480 : case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
481 : {
482 0 : return GL_TEXTURE_CUBE_MAP;
483 : }
484 : }
485 1 : return subtarget;
486 : }
487 :
488 1 : const GLint *swizzlemask(GLenum format)
489 : {
490 : static const GLint luminance[4] = { GL_RED, GL_RED, GL_RED, GL_ONE },
491 : luminancealpha[4] = { GL_RED, GL_RED, GL_RED, GL_GREEN };
492 1 : switch(format)
493 : {
494 1 : case GL_RED:
495 : {
496 1 : return luminance;
497 : }
498 0 : case GL_RG:
499 : {
500 0 : return luminancealpha;
501 : }
502 : }
503 0 : return nullptr;
504 : }
505 :
506 1 : static void setuptexparameters(int tnum, const void *pixels, int clamp, int filter, GLenum format, GLenum target, bool swizzle)
507 : {
508 1 : glBindTexture(target, tnum);
509 1 : glTexParameteri(target, GL_TEXTURE_WRAP_S, clamp&1 ? GL_CLAMP_TO_EDGE : (clamp&0x100 ? GL_MIRRORED_REPEAT : GL_REPEAT));
510 1 : if(target!=GL_TEXTURE_1D)
511 : {
512 1 : glTexParameteri(target, GL_TEXTURE_WRAP_T, clamp&2 ? GL_CLAMP_TO_EDGE : (clamp&0x200 ? GL_MIRRORED_REPEAT : GL_REPEAT));
513 : }
514 1 : if(target==GL_TEXTURE_3D)
515 : {
516 0 : glTexParameteri(target, GL_TEXTURE_WRAP_R, clamp&4 ? GL_CLAMP_TO_EDGE : (clamp&0x400 ? GL_MIRRORED_REPEAT : GL_REPEAT));
517 : }
518 1 : if(target==GL_TEXTURE_2D && std::min(aniso, hwmaxaniso) > 0 && filter > 1)
519 : {
520 0 : glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, std::min(aniso, hwmaxaniso));
521 : }
522 1 : glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter && bilinear ? GL_LINEAR : GL_NEAREST);
523 2 : glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
524 : filter > 1 ?
525 2 : (trilinear ?
526 1 : (bilinear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR) :
527 0 : (bilinear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST)) :
528 0 : (filter && bilinear ? GL_LINEAR : GL_NEAREST));
529 1 : if(swizzle)
530 : {
531 1 : const GLint *mask = swizzlemask(format);
532 1 : if(mask)
533 : {
534 1 : glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, mask);
535 : }
536 : }
537 1 : }
538 :
539 1 : static GLenum textype(GLenum &component, GLenum &format)
540 : {
541 1 : GLenum type = GL_UNSIGNED_BYTE;
542 1 : switch(component)
543 : {
544 0 : case GL_R16F:
545 : case GL_R32F:
546 : {
547 0 : if(!format)
548 : {
549 0 : format = GL_RED;
550 : }
551 0 : type = GL_FLOAT;
552 0 : break;
553 : }
554 0 : case GL_RG16F:
555 : case GL_RG32F:
556 : {
557 0 : if(!format)
558 : {
559 0 : format = GL_RG;
560 : }
561 0 : type = GL_FLOAT;
562 0 : break;
563 : }
564 0 : case GL_RGB16F:
565 : case GL_RGB32F:
566 : case GL_R11F_G11F_B10F:
567 : {
568 0 : if(!format)
569 : {
570 0 : format = GL_RGB;
571 : }
572 0 : type = GL_FLOAT;
573 0 : break;
574 : }
575 0 : case GL_RGBA16F:
576 : case GL_RGBA32F:
577 : {
578 0 : if(!format)
579 : {
580 0 : format = GL_RGBA;
581 : }
582 0 : type = GL_FLOAT;
583 0 : break;
584 : }
585 0 : case GL_DEPTH_COMPONENT16:
586 : case GL_DEPTH_COMPONENT24:
587 : case GL_DEPTH_COMPONENT32:
588 : {
589 0 : if(!format)
590 : {
591 0 : format = GL_DEPTH_COMPONENT;
592 : }
593 0 : break;
594 : }
595 0 : case GL_DEPTH_STENCIL:
596 : case GL_DEPTH24_STENCIL8:
597 : {
598 0 : if(!format)
599 : {
600 0 : format = GL_DEPTH_STENCIL;
601 : }
602 0 : type = GL_UNSIGNED_INT_24_8;
603 0 : break;
604 : }
605 0 : case GL_R8:
606 : case GL_R16:
607 : {
608 0 : if(!format)
609 : {
610 0 : format = GL_RED;
611 : }
612 0 : break;
613 : }
614 0 : case GL_RG8:
615 : case GL_RG16:
616 : {
617 0 : if(!format)
618 : {
619 0 : format = GL_RG;
620 : }
621 0 : break;
622 : }
623 0 : case GL_RGB5:
624 : case GL_RGB8:
625 : case GL_RGB16:
626 : case GL_RGB10:
627 : {
628 0 : if(!format)
629 : {
630 0 : format = GL_RGB;
631 : }
632 0 : break;
633 : }
634 0 : case GL_RGB5_A1:
635 : case GL_RGBA8:
636 : case GL_RGBA16:
637 : case GL_RGB10_A2:
638 : {
639 0 : if(!format)
640 : {
641 0 : format = GL_RGBA;
642 : }
643 0 : break;
644 : }
645 0 : case GL_RGB8UI:
646 : case GL_RGB16UI:
647 : case GL_RGB32UI:
648 : case GL_RGB8I:
649 : case GL_RGB16I:
650 : case GL_RGB32I:
651 : {
652 0 : if(!format)
653 : {
654 0 : format = GL_RGB_INTEGER;
655 : }
656 0 : break;
657 : }
658 0 : case GL_RGBA8UI:
659 : case GL_RGBA16UI:
660 : case GL_RGBA32UI:
661 : case GL_RGBA8I:
662 : case GL_RGBA16I:
663 : case GL_RGBA32I:
664 : {
665 0 : if(!format)
666 : {
667 0 : format = GL_RGBA_INTEGER;
668 : }
669 0 : break;
670 : }
671 0 : case GL_R8UI:
672 : case GL_R16UI:
673 : case GL_R32UI:
674 : case GL_R8I:
675 : case GL_R16I:
676 : case GL_R32I:
677 : {
678 0 : if(!format)
679 : {
680 0 : format = GL_RED_INTEGER;
681 : }
682 0 : break;
683 : }
684 0 : case GL_RG8UI:
685 : case GL_RG16UI:
686 : case GL_RG32UI:
687 : case GL_RG8I:
688 : case GL_RG16I:
689 : case GL_RG32I:
690 : {
691 0 : if(!format)
692 : {
693 0 : format = GL_RG_INTEGER;
694 : }
695 0 : break;
696 : }
697 : }
698 1 : if(!format)
699 : {
700 0 : format = component;
701 : }
702 1 : return type;
703 : }
704 :
705 1 : void createtexture(int tnum, int w, int h, const void *pixels, int clamp, int filter, GLenum component, GLenum subtarget, int pw, int ph, int pitch, bool resize, GLenum format, bool swizzle)
706 : {
707 1 : GLenum target = textarget(subtarget),
708 1 : type = textype(component, format);
709 1 : if(tnum)
710 : {
711 1 : setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle);
712 : }
713 1 : if(!pw)
714 : {
715 0 : pw = w;
716 : }
717 1 : if(!ph)
718 : {
719 0 : ph = h;
720 : }
721 1 : int tw = w,
722 1 : th = h;
723 1 : bool mipmap = filter > 1;
724 1 : if(resize && pixels)
725 : {
726 0 : resizetexture(w, h, mipmap, false, target, 0, tw, th);
727 : }
728 1 : uploadtexture(subtarget, component, tw, th, format, type, pixels, pw, ph, pitch, mipmap);
729 1 : }
730 :
731 0 : static void createcompressedtexture(int tnum, int w, int h, const uchar *data, int align, int blocksize, int levels, int clamp, int filter, GLenum format, GLenum subtarget, bool swizzle = false)
732 : {
733 0 : GLenum target = textarget(subtarget);
734 0 : if(tnum)
735 : {
736 0 : setuptexparameters(tnum, data, clamp, filter, format, target, swizzle);
737 : }
738 0 : uploadcompressedtexture(target, subtarget, format, w, h, data, align, blocksize, levels, filter > 1);
739 0 : }
740 :
741 0 : void create3dtexture(int tnum, int w, int h, int d, const void *pixels, int clamp, int filter, GLenum component, GLenum target, bool swizzle)
742 : {
743 0 : GLenum format = GL_FALSE, type = textype(component, format);
744 0 : if(tnum)
745 : {
746 0 : setuptexparameters(tnum, pixels, clamp, filter, format, target, swizzle);
747 : }
748 0 : glTexImage3D(target, 0, component, w, h, d, 0, format, type, pixels);
749 0 : }
750 :
751 : std::unordered_map<std::string, Texture> textures;
752 :
753 : Texture *notexture = nullptr; // used as default, ensured to be loaded
754 :
755 2 : GLenum texformat(int bpp, bool swizzle)
756 : {
757 2 : switch(bpp)
758 : {
759 2 : case 1:
760 : {
761 2 : return GL_RED;
762 : }
763 0 : case 2:
764 : {
765 0 : return GL_RG;
766 : }
767 0 : case 3:
768 : {
769 0 : return GL_RGB;
770 : }
771 0 : case 4:
772 : {
773 0 : return GL_RGBA;
774 : }
775 0 : default:
776 : {
777 0 : return 0;
778 : }
779 : }
780 : }
781 :
782 1 : static bool alphaformat(GLenum format)
783 : {
784 1 : switch(format)
785 : {
786 0 : case GL_RG:
787 : case GL_RGBA:
788 : {
789 0 : return true;
790 : }
791 1 : default:
792 : {
793 1 : return false;
794 : }
795 : }
796 : }
797 :
798 0 : bool floatformat(GLenum format)
799 : {
800 0 : switch(format)
801 : {
802 0 : case GL_R16F:
803 : case GL_R32F:
804 : case GL_RG16F:
805 : case GL_RG32F:
806 : case GL_RGB16F:
807 : case GL_RGB32F:
808 : case GL_R11F_G11F_B10F:
809 : case GL_RGBA16F:
810 : case GL_RGBA32F:
811 : {
812 0 : return true;
813 : }
814 0 : default:
815 : {
816 0 : return false;
817 : }
818 : }
819 : }
820 :
821 1 : static Texture *newtexture(Texture *t, const char *rname, ImageData &s, int clamp = 0, bool mipit = true, bool canreduce = false, bool transient = false, int compress = 0)
822 : {
823 1 : if(!t)
824 : {
825 1 : char *key = newstring(rname);
826 1 : auto itr = textures.insert_or_assign(key, Texture()).first;
827 1 : t = &(*itr).second;
828 1 : t->name = key;
829 : }
830 :
831 1 : t->clamp = clamp;
832 1 : t->mipmap = mipit;
833 1 : t->type = Texture::IMAGE;
834 1 : if(transient)
835 : {
836 0 : t->type |= Texture::TRANSIENT;
837 : }
838 1 : if(clamp&0x300)
839 : {
840 0 : t->type |= Texture::MIRROR;
841 : }
842 1 : if(!s.data)
843 : {
844 0 : t->type |= Texture::STUB;
845 0 : t->w = t->h = t->xs = t->ys = t->bpp = 0;
846 0 : return t;
847 : }
848 :
849 1 : bool swizzle = !(clamp&0x10000);
850 : GLenum format;
851 1 : format = texformat(s.bpp, swizzle);
852 1 : t->bpp = s.bpp;
853 1 : if(alphaformat(format))
854 : {
855 0 : t->type |= Texture::ALPHA;
856 : }
857 1 : t->w = t->xs = s.w;
858 1 : t->h = t->ys = s.h;
859 1 : int filter = !canreduce || reducefilter ? (mipit ? 2 : 1) : 0;
860 1 : glGenTextures(1, &t->id);
861 1 : if(s.compressed)
862 : {
863 0 : uchar *data = s.data;
864 0 : int levels = s.levels, level = 0;
865 0 : int sizelimit = mipit && maxtexsize ? std::min(maxtexsize, hwtexsize) : hwtexsize;
866 0 : while(t->w > sizelimit || t->h > sizelimit)
867 : {
868 0 : data += s.calclevelsize(level++);
869 0 : levels--;
870 0 : if(t->w > 1)
871 : {
872 0 : t->w /= 2;
873 : }
874 0 : if(t->h > 1)
875 : {
876 0 : t->h /= 2;
877 : }
878 : }
879 0 : createcompressedtexture(t->id, t->w, t->h, data, s.align, s.bpp, levels, clamp, filter, s.compressed, GL_TEXTURE_2D, swizzle);
880 : }
881 : else
882 : {
883 1 : resizetexture(t->w, t->h, mipit, canreduce, GL_TEXTURE_2D, compress, t->w, t->h);
884 1 : createtexture(t->id, t->w, t->h, s.data, clamp, filter, format, GL_TEXTURE_2D, t->xs, t->ys, s.pitch, false, format, swizzle);
885 : }
886 1 : return t;
887 : }
888 :
889 0 : static SDL_Surface *creatergbsurface(SDL_Surface *os)
890 : {
891 0 : SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 24, 0x0000ff, 0x00ff00, 0xff0000, 0);
892 0 : if(ns)
893 : {
894 0 : SDL_BlitSurface(os, nullptr, ns, nullptr);
895 : }
896 0 : SDL_FreeSurface(os);
897 0 : return ns;
898 : }
899 :
900 0 : static SDL_Surface *creatergbasurface(SDL_Surface *os)
901 : {
902 0 : SDL_Surface *ns = SDL_CreateRGBSurface(SDL_SWSURFACE, os->w, os->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
903 0 : if(ns)
904 : {
905 0 : SDL_SetSurfaceBlendMode(os, SDL_BLENDMODE_NONE);
906 0 : SDL_BlitSurface(os, nullptr, ns, nullptr);
907 : }
908 0 : SDL_FreeSurface(os);
909 0 : return ns;
910 : }
911 :
912 1 : static bool checkgrayscale(SDL_Surface *s)
913 : {
914 : // gray scale images have 256 levels, no colorkey, and the palette is a ramp
915 1 : if(s->format->palette)
916 : {
917 1 : if(s->format->palette->ncolors != 256 || SDL_GetColorKey(s, nullptr) >= 0)
918 : {
919 0 : return false;
920 : }
921 1 : const SDL_Color *colors = s->format->palette->colors;
922 257 : for(int i = 0; i < 256; ++i)
923 : {
924 256 : if(colors[i].r != i || colors[i].g != i || colors[i].b != i)
925 : {
926 0 : return false;
927 : }
928 : }
929 : }
930 1 : return true;
931 : }
932 :
933 10 : static SDL_Surface *fixsurfaceformat(SDL_Surface *s)
934 : {
935 10 : if(!s)
936 : {
937 9 : return nullptr;
938 : }
939 1 : if(!s->pixels || std::min(s->w, s->h) <= 0 || s->format->BytesPerPixel <= 0)
940 : {
941 0 : SDL_FreeSurface(s);
942 0 : return nullptr;
943 : }
944 : static const uint rgbmasks[] = { 0x0000ff, 0x00ff00, 0xff0000, 0 },
945 : rgbamasks[] = { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 };
946 1 : switch(s->format->BytesPerPixel)
947 : {
948 1 : case 1:
949 1 : if(!checkgrayscale(s))
950 : {
951 0 : return SDL_GetColorKey(s, nullptr) >= 0 ? creatergbasurface(s) : creatergbsurface(s);
952 : }
953 1 : break;
954 0 : case 3:
955 0 : if(s->format->Rmask != rgbmasks[0] || s->format->Gmask != rgbmasks[1] || s->format->Bmask != rgbmasks[2])
956 : {
957 0 : return creatergbsurface(s);
958 : }
959 0 : break;
960 0 : case 4:
961 0 : if(s->format->Rmask != rgbamasks[0] || s->format->Gmask != rgbamasks[1] || s->format->Bmask != rgbamasks[2] || s->format->Amask != rgbamasks[3])
962 : {
963 0 : return s->format->Amask ? creatergbasurface(s) : creatergbsurface(s);
964 : }
965 0 : break;
966 : }
967 1 : return s;
968 : }
969 :
970 10 : SDL_Surface *loadsurface(const char *name)
971 : {
972 10 : SDL_Surface *s = nullptr;
973 10 : stream *z = openzipfile(name, "rb");
974 10 : if(z)
975 : {
976 0 : SDL_RWops *rw = z->rwops();
977 0 : if(rw)
978 : {
979 0 : const char *ext = std::strrchr(name, '.');
980 0 : if(ext)
981 : {
982 0 : ++ext;
983 : }
984 0 : s = IMG_LoadTyped_RW(rw, 0, ext);
985 0 : SDL_FreeRW(rw);
986 : }
987 0 : delete z;
988 : }
989 10 : if(!s)
990 : {
991 10 : s = IMG_Load(findfile(name, "rb"));
992 : }
993 10 : return fixsurfaceformat(s);
994 : }
995 :
996 2 : const uchar * Texture::loadalphamask()
997 : {
998 2 : if(alphamask)
999 : {
1000 0 : return alphamask;
1001 : }
1002 2 : if(!(type&Texture::ALPHA))
1003 : {
1004 2 : return nullptr;
1005 : }
1006 0 : ImageData s;
1007 0 : if(!s.texturedata(name, false) || !s.data || s.compressed)
1008 : {
1009 0 : return nullptr;
1010 : }
1011 0 : alphamask = new uchar[s.h * ((s.w+7)/8)];
1012 0 : uchar *srcrow = s.data,
1013 0 : *dst = alphamask-1;
1014 0 : for(int y = 0; y < s.h; ++y)
1015 : {
1016 0 : uchar *src = srcrow+s.bpp-1;
1017 0 : for(int x = 0; x < s.w; ++x)
1018 : {
1019 0 : int offset = x%8;
1020 0 : if(!offset)
1021 : {
1022 0 : *++dst = 0;
1023 : }
1024 0 : if(*src)
1025 : {
1026 0 : *dst |= 1<<offset;
1027 : }
1028 0 : src += s.bpp;
1029 : }
1030 0 : srcrow += s.pitch;
1031 : }
1032 0 : return alphamask;
1033 0 : }
1034 :
1035 0 : float Texture::ratio() const
1036 : {
1037 0 : return (w / static_cast<float>(h));
1038 : }
1039 :
1040 17 : Texture *textureload(const char *name, int clamp, bool mipit, bool msg)
1041 : {
1042 17 : std::string tname(name);
1043 17 : auto itr = textures.find(path(tname));
1044 17 : if(itr != textures.end())
1045 : {
1046 7 : return &(*itr).second;
1047 : }
1048 10 : int compress = 0;
1049 10 : ImageData s;
1050 10 : if(s.texturedata(tname.c_str(), msg, &compress, &clamp))
1051 : {
1052 1 : return newtexture(nullptr, tname.c_str(), s, clamp, mipit, false, false, compress);
1053 : }
1054 9 : return notexture;
1055 17 : }
1056 :
1057 0 : bool settexture(const char *name, int clamp)
1058 : {
1059 0 : Texture *t = textureload(name, clamp, true, false);
1060 0 : glBindTexture(GL_TEXTURE_2D, t->id);
1061 0 : return t != notexture;
1062 : }
1063 :
1064 : std::vector<VSlot *> vslots;
1065 : std::vector<Slot *> slots;
1066 : MatSlot materialslots[(MatFlag_Volume|MatFlag_Index)+1];
1067 : Slot dummyslot;
1068 : VSlot dummyvslot(&dummyslot);
1069 : static std::vector<DecalSlot *> decalslots;
1070 : DecalSlot dummydecalslot;
1071 : Slot *defslot = nullptr;
1072 :
1073 2 : const char *Slot::name() const
1074 : {
1075 2 : return tempformatstring("slot %d", index);
1076 : }
1077 :
1078 32 : MatSlot::MatSlot() : Slot(static_cast<int>(this - materialslots)), VSlot(this) {}
1079 0 : const char *MatSlot::name() const
1080 : {
1081 0 : return tempformatstring("material slot %s", findmaterialname(Slot::index));
1082 : }
1083 :
1084 2 : const char *DecalSlot::name() const
1085 : {
1086 2 : return tempformatstring("decal slot %d", Slot::index);
1087 : }
1088 :
1089 1 : void texturereset(int *n)
1090 : {
1091 1 : if(!(identflags&Idf_Overridden) && !allowediting)
1092 : {
1093 1 : return;
1094 : }
1095 0 : defslot = nullptr;
1096 0 : resetslotshader();
1097 0 : int limit = std::clamp(*n, 0, static_cast<int>(slots.size()));
1098 0 : for(size_t i = limit; i < slots.size(); i++)
1099 : {
1100 0 : Slot *s = slots[i];
1101 0 : for(VSlot *vs = s->variants; vs; vs = vs->next)
1102 : {
1103 0 : vs->slot = &dummyslot;
1104 : }
1105 0 : delete s;
1106 : }
1107 0 : slots.resize(limit);
1108 0 : while(vslots.size())
1109 : {
1110 0 : VSlot *vs = vslots.back();
1111 0 : if(vs->slot != &dummyslot || vs->changed)
1112 : {
1113 : break;
1114 : }
1115 0 : delete vslots.back();
1116 0 : vslots.pop_back();
1117 : }
1118 : }
1119 :
1120 1 : void materialreset()
1121 : {
1122 1 : if(!(identflags&Idf_Overridden) && !allowediting)
1123 : {
1124 1 : return;
1125 : }
1126 0 : defslot = nullptr;
1127 0 : for(int i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
1128 : {
1129 0 : materialslots[i].reset();
1130 : }
1131 : }
1132 :
1133 1 : void decalreset(const int *n)
1134 : {
1135 1 : if(!(identflags&Idf_Overridden) && !allowediting)
1136 : {
1137 1 : return;
1138 : }
1139 0 : defslot = nullptr;
1140 0 : resetslotshader();
1141 0 : for(uint i = *n; i < decalslots.size(); ++i)
1142 : {
1143 0 : delete decalslots.at(i);
1144 : }
1145 0 : decalslots.resize(*n);
1146 : }
1147 :
1148 : static int compactedvslots = 0,
1149 : compactvslotsprogress = 0,
1150 : clonedvslots = 0;
1151 : static bool markingvslots = false;
1152 :
1153 0 : void clearslots()
1154 : {
1155 0 : defslot = nullptr;
1156 0 : resetslotshader();
1157 0 : for(Slot * i : slots)
1158 : {
1159 0 : delete i;
1160 : }
1161 0 : slots.clear();
1162 0 : for(VSlot * i : vslots)
1163 : {
1164 0 : delete i;
1165 : }
1166 0 : vslots.clear();
1167 0 : for(int i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
1168 : {
1169 0 : materialslots[i].reset();
1170 : }
1171 0 : decalslots.clear();
1172 0 : clonedvslots = 0;
1173 0 : }
1174 :
1175 0 : static void assignvslot(VSlot &vs)
1176 : {
1177 0 : vs.index = compactedvslots++;
1178 0 : }
1179 :
1180 0 : void compactvslot(int &index)
1181 : {
1182 0 : if(static_cast<long>(vslots.size()) > index)
1183 : {
1184 0 : VSlot &vs = *vslots[index];
1185 0 : if(vs.index < 0)
1186 : {
1187 0 : assignvslot(vs);
1188 : }
1189 0 : if(!markingvslots)
1190 : {
1191 0 : index = vs.index;
1192 : }
1193 : }
1194 0 : }
1195 :
1196 0 : void compactvslot(VSlot &vs)
1197 : {
1198 0 : if(vs.index < 0)
1199 : {
1200 0 : assignvslot(vs);
1201 : }
1202 0 : }
1203 :
1204 : //n will be capped at 8
1205 0 : void compactvslots(cube * const c, int n)
1206 : {
1207 0 : if((compactvslotsprogress++&0xFFF)==0)
1208 : {
1209 0 : renderprogress(std::min(static_cast<float>(compactvslotsprogress)/allocnodes, 1.0f), markingvslots ? "marking slots..." : "compacting slots...");
1210 : }
1211 0 : for(int i = 0; i < std::min(n, 8); ++i)
1212 : {
1213 0 : if(c[i].children)
1214 : {
1215 0 : compactvslots(c[i].children->data());
1216 : }
1217 : else
1218 : {
1219 0 : for(int j = 0; j < 6; ++j)
1220 : {
1221 0 : if(vslots.size() > c[i].texture[j])
1222 : {
1223 0 : VSlot &vs = *vslots[c[i].texture[j]];
1224 0 : if(vs.index < 0)
1225 : {
1226 0 : assignvslot(vs);
1227 : }
1228 0 : if(!markingvslots)
1229 : {
1230 0 : c[i].texture[j] = vs.index;
1231 : }
1232 : }
1233 : }
1234 : }
1235 : }
1236 0 : }
1237 :
1238 1 : int cubeworld::compactvslots(bool cull)
1239 : {
1240 1 : if(!worldroot)
1241 : {
1242 1 : conoutf(Console_Error, "no cube to compact");
1243 1 : return 0;
1244 : }
1245 0 : defslot = nullptr;
1246 0 : clonedvslots = 0;
1247 0 : markingvslots = cull;
1248 0 : compactedvslots = 0;
1249 0 : compactvslotsprogress = 0;
1250 0 : for(uint i = 0; i < vslots.size(); i++)
1251 : {
1252 0 : vslots[i]->index = -1;
1253 : }
1254 0 : if(cull)
1255 : {
1256 0 : uint numdefaults = std::min(static_cast<uint>(Default_NumDefaults), static_cast<uint>(slots.size()));
1257 0 : for(uint i = 0; i < numdefaults; ++i)
1258 : {
1259 0 : slots[i]->variants->index = compactedvslots++;
1260 : }
1261 : }
1262 : else
1263 : {
1264 0 : for(const Slot *i : slots)
1265 : {
1266 0 : i->variants->index = compactedvslots++;
1267 : }
1268 0 : for(const VSlot *i : vslots)
1269 : {
1270 0 : if(!i->changed && i->index < 0)
1271 : {
1272 0 : markingvslots = true;
1273 0 : break;
1274 : }
1275 : }
1276 : }
1277 0 : ::compactvslots(worldroot->data());
1278 0 : int total = compactedvslots;
1279 0 : compacteditvslots();
1280 0 : for(VSlot *i : vslots)
1281 : {
1282 0 : if(i->changed)
1283 : {
1284 0 : continue;
1285 : }
1286 0 : while(i->next)
1287 : {
1288 0 : if(i->next->index < 0)
1289 : {
1290 0 : i->next = i->next->next;
1291 : }
1292 : else
1293 : {
1294 0 : i = i->next;
1295 : }
1296 : }
1297 : }
1298 0 : if(markingvslots)
1299 : {
1300 0 : markingvslots = false;
1301 0 : compactedvslots = 0;
1302 0 : compactvslotsprogress = 0;
1303 0 : int lastdiscard = 0;
1304 0 : for(uint i = 0; i < vslots.size(); i++)
1305 : {
1306 0 : VSlot &vs = *vslots[i];
1307 0 : if(vs.changed || (vs.index < 0 && !vs.next))
1308 : {
1309 0 : vs.index = -1;
1310 : }
1311 : else
1312 : {
1313 0 : if(!cull)
1314 : {
1315 0 : while(lastdiscard < static_cast<int>(i))
1316 : {
1317 0 : VSlot &ds = *vslots[lastdiscard++];
1318 0 : if(!ds.changed && ds.index < 0)
1319 : {
1320 0 : ds.index = compactedvslots++;
1321 : }
1322 : }
1323 : }
1324 0 : vs.index = compactedvslots++;
1325 : }
1326 : }
1327 0 : ::compactvslots(worldroot->data());
1328 0 : total = compactedvslots;
1329 0 : compacteditvslots();
1330 : }
1331 0 : compactmruvslots();
1332 0 : if(cull)
1333 : {
1334 0 : for(int i = static_cast<int>(slots.size()); --i >=0;) //note reverse iteration
1335 : {
1336 0 : if(slots[i]->variants->index < 0)
1337 : {
1338 0 : delete slots.at(i);
1339 0 : slots.erase(slots.begin() + i);
1340 : }
1341 : }
1342 0 : for(uint i = 0; i < slots.size(); i++)
1343 : {
1344 0 : slots[i]->index = i;
1345 : }
1346 : }
1347 0 : for(uint i = 0; i < vslots.size(); i++)
1348 : {
1349 0 : while(vslots[i]->index >= 0 && vslots[i]->index != static_cast<int>(i))
1350 : {
1351 0 : std::swap(vslots[i], vslots[vslots[i]->index]);
1352 : }
1353 : }
1354 0 : for(uint i = compactedvslots; i < vslots.size(); i++)
1355 : {
1356 0 : delete vslots[i];
1357 : }
1358 0 : vslots.resize(compactedvslots);
1359 0 : return total;
1360 : }
1361 :
1362 0 : static void clampvslotoffset(VSlot &dst, Slot *slot = nullptr)
1363 : {
1364 0 : if(!slot)
1365 : {
1366 0 : slot = dst.slot;
1367 : }
1368 0 : if(slot && slot->sts.size())
1369 : {
1370 0 : if(!slot->loaded)
1371 : {
1372 0 : slot->load();
1373 : }
1374 0 : Texture *t = slot->sts[0].t;
1375 0 : int xs = t->xs,
1376 0 : ys = t->ys;
1377 0 : if(t->type & Texture::MIRROR)
1378 : {
1379 0 : xs *= 2;
1380 0 : ys *= 2;
1381 : }
1382 0 : if(texrotations[dst.rotation].swapxy)
1383 : {
1384 0 : std::swap(xs, ys);
1385 : }
1386 0 : dst.offset.x %= xs;
1387 0 : if(dst.offset.x < 0)
1388 : {
1389 0 : dst.offset.x += xs;
1390 : }
1391 0 : dst.offset.y %= ys;
1392 0 : if(dst.offset.y < 0)
1393 : {
1394 0 : dst.offset.y += ys;
1395 : }
1396 : }
1397 : else
1398 : {
1399 0 : dst.offset.max(0);
1400 : }
1401 0 : }
1402 :
1403 0 : static void propagatevslot(VSlot &dst, const VSlot &src, int diff, bool edit = false)
1404 : {
1405 0 : if(diff & (1 << VSlot_ShParam))
1406 : {
1407 0 : for(uint i = 0; i < src.params.size(); i++)
1408 : {
1409 0 : dst.params.push_back(src.params[i]);
1410 : }
1411 : }
1412 0 : if(diff & (1 << VSlot_Scale))
1413 : {
1414 0 : dst.scale = src.scale;
1415 : }
1416 0 : if(diff & (1 << VSlot_Rotation))
1417 : {
1418 0 : dst.rotation = src.rotation;
1419 0 : if(edit && !dst.offset.iszero())
1420 : {
1421 0 : clampvslotoffset(dst);
1422 : }
1423 : }
1424 0 : if(diff & (1 << VSlot_Angle))
1425 : {
1426 0 : dst.angle = src.angle;
1427 : }
1428 0 : if(diff & (1 << VSlot_Offset))
1429 : {
1430 0 : dst.offset = src.offset;
1431 0 : if(edit)
1432 : {
1433 0 : clampvslotoffset(dst);
1434 : }
1435 : }
1436 0 : if(diff & (1 << VSlot_Scroll))
1437 : {
1438 0 : dst.scroll = src.scroll;
1439 : }
1440 0 : if(diff & (1 << VSlot_Alpha))
1441 : {
1442 0 : dst.alphafront = src.alphafront;
1443 0 : dst.alphaback = src.alphaback;
1444 : }
1445 0 : if(diff & (1 << VSlot_Color))
1446 : {
1447 0 : dst.colorscale = src.colorscale;
1448 : }
1449 0 : if(diff & (1 << VSlot_Refract))
1450 : {
1451 0 : dst.refractscale = src.refractscale;
1452 0 : dst.refractcolor = src.refractcolor;
1453 : }
1454 0 : }
1455 :
1456 0 : static void propagatevslot(const VSlot *root, int changed)
1457 : {
1458 0 : for(VSlot *vs = root->next; vs; vs = vs->next)
1459 : {
1460 0 : int diff = changed & ~vs->changed;
1461 0 : if(diff)
1462 : {
1463 0 : propagatevslot(*vs, *root, diff);
1464 : }
1465 : }
1466 0 : }
1467 :
1468 0 : static void mergevslot(VSlot &dst, const VSlot &src, int diff, Slot *slot = nullptr)
1469 : {
1470 0 : if(diff & (1 << VSlot_ShParam))
1471 : {
1472 0 : for(uint i = 0; i < src.params.size(); i++)
1473 : {
1474 0 : const SlotShaderParam &sp = src.params[i];
1475 0 : for(uint j = 0; j < dst.params.size(); j++)
1476 : {
1477 0 : SlotShaderParam &dp = dst.params[j];
1478 0 : if(sp.name == dp.name)
1479 : {
1480 0 : std::memcpy(dp.val, sp.val, sizeof(dp.val));
1481 0 : goto nextparam; //bail out of loop
1482 : }
1483 : }
1484 0 : dst.params.push_back(sp);
1485 0 : nextparam:;
1486 : }
1487 : }
1488 0 : if(diff & (1 << VSlot_Scale))
1489 : {
1490 0 : dst.scale = std::clamp(dst.scale*src.scale, 1/8.0f, 8.0f);
1491 : }
1492 0 : if(diff & (1 << VSlot_Rotation))
1493 : {
1494 0 : dst.rotation = std::clamp(dst.rotation + src.rotation, 0, 7);
1495 0 : if(!dst.offset.iszero())
1496 : {
1497 0 : clampvslotoffset(dst, slot);
1498 : }
1499 : }
1500 0 : if(diff & (1 << VSlot_Angle))
1501 : {
1502 0 : dst.angle.add(src.angle);
1503 : }
1504 0 : if(diff & (1 << VSlot_Offset))
1505 : {
1506 0 : dst.offset.add(src.offset);
1507 0 : clampvslotoffset(dst, slot);
1508 : }
1509 0 : if(diff & (1 << VSlot_Scroll))
1510 : {
1511 0 : dst.scroll.add(src.scroll);
1512 : }
1513 0 : if(diff & (1 << VSlot_Alpha))
1514 : {
1515 0 : dst.alphafront = src.alphafront;
1516 0 : dst.alphaback = src.alphaback;
1517 : }
1518 0 : if(diff & (1 << VSlot_Color))
1519 : {
1520 0 : dst.colorscale.mul(src.colorscale);
1521 : }
1522 0 : if(diff & (1 << VSlot_Refract))
1523 : {
1524 0 : dst.refractscale *= src.refractscale;
1525 0 : dst.refractcolor.mul(src.refractcolor);
1526 : }
1527 0 : }
1528 :
1529 0 : void mergevslot(VSlot &dst, const VSlot &src, const VSlot &delta)
1530 : {
1531 0 : dst.changed = src.changed | delta.changed;
1532 0 : propagatevslot(dst, src, (1 << VSlot_Num) - 1);
1533 0 : mergevslot(dst, delta, delta.changed, src.slot);
1534 0 : }
1535 :
1536 0 : static VSlot *reassignvslot(Slot &owner, VSlot *vs)
1537 : {
1538 0 : vs->reset();
1539 0 : owner.variants = vs;
1540 0 : while(vs)
1541 : {
1542 0 : vs->slot = &owner;
1543 0 : vs->linked = false;
1544 0 : vs = vs->next;
1545 : }
1546 0 : return owner.variants;
1547 : }
1548 :
1549 0 : static VSlot *emptyvslot(Slot &owner)
1550 : {
1551 0 : int offset = 0;
1552 0 : for(int i = static_cast<int>(slots.size()); --i >=0;) //note reverse iteration
1553 : {
1554 0 : if(slots[i]->variants)
1555 : {
1556 0 : offset = slots[i]->variants->index + 1;
1557 0 : break;
1558 : }
1559 : }
1560 0 : for(uint i = offset; i < vslots.size(); i++)
1561 : {
1562 0 : if(!vslots[i]->changed)
1563 : {
1564 0 : return reassignvslot(owner, vslots[i]);
1565 : }
1566 : }
1567 0 : vslots.push_back(new VSlot(&owner, vslots.size()));
1568 0 : return vslots.back();
1569 : }
1570 :
1571 0 : static bool comparevslot(const VSlot &dst, const VSlot &src, int diff)
1572 : {
1573 0 : if(diff & (1 << VSlot_ShParam))
1574 : {
1575 0 : if(src.params.size() != dst.params.size())
1576 : {
1577 0 : return false;
1578 : }
1579 0 : for(uint i = 0; i < src.params.size(); i++)
1580 : {
1581 0 : const SlotShaderParam &sp = src.params[i], &dp = dst.params[i];
1582 0 : if(sp.name != dp.name || std::memcmp(sp.val, dp.val, sizeof(sp.val)))
1583 : {
1584 0 : return false;
1585 : }
1586 : }
1587 : }
1588 0 : if(diff & (1 << VSlot_Scale) && dst.scale != src.scale) return false;
1589 0 : if(diff & (1 << VSlot_Rotation)&& dst.rotation != src.rotation) return false;
1590 0 : if(diff & (1 << VSlot_Angle) && dst.angle != src.angle) return false;
1591 0 : if(diff & (1 << VSlot_Offset) && dst.offset != src.offset) return false;
1592 0 : if(diff & (1 << VSlot_Scroll) && dst.scroll != src.scroll) return false;
1593 0 : if(diff & (1 << VSlot_Alpha) && (dst.alphafront != src.alphafront || dst.alphaback != src.alphaback)) return false;
1594 0 : if(diff & (1 << VSlot_Color) && dst.colorscale != src.colorscale) return false;
1595 0 : if(diff & (1 << VSlot_Refract) && (dst.refractscale != src.refractscale || dst.refractcolor != src.refractcolor)) return false;
1596 0 : return true;
1597 : }
1598 :
1599 0 : void packvslot(std::vector<uchar> &buf, const VSlot &src)
1600 : {
1601 0 : if(src.changed & (1 << VSlot_ShParam))
1602 : {
1603 0 : for(uint i = 0; i < src.params.size(); i++)
1604 : {
1605 0 : const SlotShaderParam &p = src.params[i];
1606 0 : buf.push_back(VSlot_ShParam);
1607 0 : sendstring(p.name, buf);
1608 0 : for(int j = 0; j < 4; ++j)
1609 : {
1610 0 : putfloat(buf, p.val[j]);
1611 : }
1612 : }
1613 : }
1614 0 : if(src.changed & (1 << VSlot_Scale))
1615 : {
1616 0 : buf.push_back(VSlot_Scale);
1617 0 : putfloat(buf, src.scale);
1618 : }
1619 0 : if(src.changed & (1 << VSlot_Rotation))
1620 : {
1621 0 : buf.push_back(VSlot_Rotation);
1622 0 : putint(buf, src.rotation);
1623 : }
1624 0 : if(src.changed & (1 << VSlot_Angle))
1625 : {
1626 0 : buf.push_back(VSlot_Angle);
1627 0 : putfloat(buf, src.angle.x);
1628 0 : putfloat(buf, src.angle.y);
1629 0 : putfloat(buf, src.angle.z);
1630 : }
1631 0 : if(src.changed & (1 << VSlot_Offset))
1632 : {
1633 0 : buf.push_back(VSlot_Offset);
1634 0 : putint(buf, src.offset.x);
1635 0 : putint(buf, src.offset.y);
1636 : }
1637 0 : if(src.changed & (1 << VSlot_Scroll))
1638 : {
1639 0 : buf.push_back(VSlot_Scroll);
1640 0 : putfloat(buf, src.scroll.x);
1641 0 : putfloat(buf, src.scroll.y);
1642 : }
1643 0 : if(src.changed & (1 << VSlot_Alpha))
1644 : {
1645 0 : buf.push_back(VSlot_Alpha);
1646 0 : putfloat(buf, src.alphafront);
1647 0 : putfloat(buf, src.alphaback);
1648 : }
1649 0 : if(src.changed & (1 << VSlot_Color))
1650 : {
1651 0 : buf.push_back(VSlot_Color);
1652 0 : putfloat(buf, src.colorscale.r);
1653 0 : putfloat(buf, src.colorscale.g);
1654 0 : putfloat(buf, src.colorscale.b);
1655 : }
1656 0 : if(src.changed & (1 << VSlot_Refract))
1657 : {
1658 0 : buf.push_back(VSlot_Refract);
1659 0 : putfloat(buf, src.refractscale);
1660 0 : putfloat(buf, src.refractcolor.r);
1661 0 : putfloat(buf, src.refractcolor.g);
1662 0 : putfloat(buf, src.refractcolor.b);
1663 : }
1664 0 : buf.push_back(0xFF);
1665 0 : }
1666 :
1667 : //used in iengine.h
1668 0 : void packvslot(std::vector<uchar> &buf, int index)
1669 : {
1670 0 : if(static_cast<long>(vslots.size()) > index)
1671 : {
1672 0 : packvslot(buf, *vslots[index]);
1673 : }
1674 : else
1675 : {
1676 0 : buf.push_back(0xFF);
1677 : }
1678 0 : }
1679 :
1680 : //used in iengine.h
1681 0 : void packvslot(std::vector<uchar> &buf, const VSlot *vs)
1682 : {
1683 0 : if(vs)
1684 : {
1685 0 : packvslot(buf, *vs);
1686 : }
1687 : else
1688 : {
1689 0 : buf.push_back(0xFF);
1690 : }
1691 0 : }
1692 :
1693 0 : bool unpackvslot(ucharbuf &buf, VSlot &dst, bool delta)
1694 : {
1695 0 : while(buf.remaining())
1696 : {
1697 0 : int changed = buf.get();
1698 0 : if(changed >= 0x80)
1699 : {
1700 0 : break;
1701 : }
1702 0 : switch(changed)
1703 : {
1704 0 : case VSlot_ShParam:
1705 : {
1706 : string name;
1707 0 : getstring(name, buf);
1708 0 : SlotShaderParam p = { name[0] ? getshaderparamname(name) : nullptr, SIZE_MAX, 0, { 0, 0, 0, 0 } };
1709 0 : for(int i = 0; i < 4; ++i)
1710 : {
1711 0 : p.val[i] = getfloat(buf);
1712 : }
1713 0 : if(p.name)
1714 : {
1715 0 : dst.params.push_back(p);
1716 : }
1717 0 : break;
1718 : }
1719 0 : case VSlot_Scale:
1720 : {
1721 0 : dst.scale = getfloat(buf);
1722 0 : if(dst.scale <= 0)
1723 : {
1724 0 : dst.scale = 1;
1725 : }
1726 0 : else if(!delta)
1727 : {
1728 0 : dst.scale = std::clamp(dst.scale, 1/8.0f, 8.0f);
1729 : }
1730 0 : break;
1731 : }
1732 0 : case VSlot_Rotation:
1733 0 : dst.rotation = getint(buf);
1734 0 : if(!delta)
1735 : {
1736 0 : dst.rotation = std::clamp(dst.rotation, 0, 7);
1737 : }
1738 0 : break;
1739 0 : case VSlot_Angle:
1740 : {
1741 0 : dst.angle.x = getfloat(buf);
1742 0 : dst.angle.y = getfloat(buf);
1743 0 : dst.angle.z = getfloat(buf);
1744 0 : break;
1745 : }
1746 0 : case VSlot_Offset:
1747 : {
1748 0 : dst.offset.x = getint(buf);
1749 0 : dst.offset.y = getint(buf);
1750 0 : if(!delta)
1751 : {
1752 0 : dst.offset.max(0);
1753 : }
1754 0 : break;
1755 : }
1756 0 : case VSlot_Scroll:
1757 : {
1758 0 : dst.scroll.x = getfloat(buf);
1759 0 : dst.scroll.y = getfloat(buf);
1760 0 : break;
1761 : }
1762 0 : case VSlot_Alpha:
1763 : {
1764 0 : dst.alphafront = std::clamp(getfloat(buf), 0.0f, 1.0f);
1765 0 : dst.alphaback = std::clamp(getfloat(buf), 0.0f, 1.0f);
1766 0 : break;
1767 : }
1768 0 : case VSlot_Color:
1769 : {
1770 0 : dst.colorscale.r = std::clamp(getfloat(buf), 0.0f, 2.0f);
1771 0 : dst.colorscale.g = std::clamp(getfloat(buf), 0.0f, 2.0f);
1772 0 : dst.colorscale.b = std::clamp(getfloat(buf), 0.0f, 2.0f);
1773 0 : break;
1774 : }
1775 0 : case VSlot_Refract:
1776 : {
1777 0 : dst.refractscale = std::clamp(getfloat(buf), 0.0f, 1.0f);
1778 0 : dst.refractcolor.r = std::clamp(getfloat(buf), 0.0f, 1.0f);
1779 0 : dst.refractcolor.g = std::clamp(getfloat(buf), 0.0f, 1.0f);
1780 0 : dst.refractcolor.b = std::clamp(getfloat(buf), 0.0f, 1.0f);
1781 0 : break;
1782 : }
1783 0 : default:
1784 : {
1785 0 : return false;
1786 : }
1787 : }
1788 0 : dst.changed |= 1<<changed;
1789 : }
1790 0 : if(buf.overread())
1791 : {
1792 0 : return false;
1793 : }
1794 0 : return true;
1795 : }
1796 :
1797 0 : VSlot *findvslot(const Slot &slot, const VSlot &src, const VSlot &delta)
1798 : {
1799 0 : for(VSlot *dst = slot.variants; dst; dst = dst->next)
1800 : {
1801 0 : if((!dst->changed || dst->changed == (src.changed | delta.changed)) &&
1802 0 : comparevslot(*dst, src, src.changed & ~delta.changed) &&
1803 0 : comparevslot(*dst, delta, delta.changed))
1804 : {
1805 0 : return dst;
1806 : }
1807 : }
1808 0 : return nullptr;
1809 : }
1810 :
1811 0 : static VSlot *clonevslot(const VSlot &src, const VSlot &delta)
1812 : {
1813 0 : vslots.push_back(new VSlot(src.slot, vslots.size()));
1814 0 : VSlot *dst = vslots.back();
1815 0 : dst->changed = src.changed | delta.changed;
1816 0 : propagatevslot(*dst, src, ((1 << VSlot_Num) - 1) & ~delta.changed);
1817 0 : propagatevslot(*dst, delta, delta.changed, true);
1818 0 : return dst;
1819 : }
1820 :
1821 : VARP(autocompactvslots, 0, 256, 0x10000);
1822 :
1823 0 : VSlot *editvslot(const VSlot &src, const VSlot &delta)
1824 : {
1825 0 : VSlot *exists = findvslot(*src.slot, src, delta);
1826 0 : if(exists)
1827 : {
1828 0 : return exists;
1829 : }
1830 0 : if(vslots.size()>=0x10000)
1831 : {
1832 0 : ::rootworld.compactvslots();
1833 0 : rootworld.allchanged();
1834 0 : if(vslots.size()>=0x10000)
1835 : {
1836 0 : return nullptr;
1837 : }
1838 : }
1839 0 : if(autocompactvslots && ++clonedvslots >= autocompactvslots)
1840 : {
1841 0 : ::rootworld.compactvslots();
1842 0 : rootworld.allchanged();
1843 : }
1844 0 : return clonevslot(src, delta);
1845 : }
1846 :
1847 0 : static void fixinsidefaces(std::array<cube, 8> &c, const ivec &o, int size, int tex)
1848 : {
1849 0 : for(int i = 0; i < 8; ++i)
1850 : {
1851 0 : ivec co(i, o, size);
1852 0 : if(c[i].children)
1853 : {
1854 0 : fixinsidefaces(*(c[i].children), co, size>>1, tex);
1855 : }
1856 : else
1857 : {
1858 0 : for(int j = 0; j < 6; ++j)
1859 : {
1860 0 : if(!visibletris(c[i], j, co, size))
1861 : {
1862 0 : c[i].texture[j] = tex;
1863 : }
1864 : }
1865 : }
1866 : }
1867 0 : }
1868 :
1869 1 : int findslottex(const char *name)
1870 : {
1871 :
1872 : const struct slottex
1873 : {
1874 : const char *name;
1875 : int id;
1876 1 : } slottexs[] =
1877 : {
1878 : {"0", Tex_Diffuse},
1879 : {"1", Tex_Unknown},
1880 :
1881 : {"c", Tex_Diffuse},
1882 : {"u", Tex_Unknown},
1883 : {"n", Tex_Normal},
1884 : {"g", Tex_Glow},
1885 : {"s", Tex_Spec},
1886 : {"z", Tex_Depth},
1887 : {"a", Tex_Alpha}
1888 : };
1889 :
1890 10 : for(int i = 0; i < static_cast<int>(sizeof(slottexs)/sizeof(slottex)); ++i)
1891 : {
1892 9 : if(!std::strcmp(slottexs[i].name, name))
1893 : {
1894 0 : return slottexs[i].id;
1895 : }
1896 : }
1897 1 : return -1;
1898 : }
1899 :
1900 1 : static void texture(char *type, char *name, int *rot, int *xoffset, int *yoffset, float *scale)
1901 : {
1902 1 : int tnum = findslottex(type), matslot;
1903 1 : if(tnum == Tex_Diffuse)
1904 : {
1905 0 : if(slots.size() >= 0x10000)
1906 : {
1907 0 : return;
1908 : }
1909 0 : slots.push_back(new Slot(slots.size()));
1910 0 : defslot = slots.back();
1911 : }
1912 1 : else if(!std::strcmp(type, "decal"))
1913 : {
1914 0 : if(decalslots.size() >= 0x10000)
1915 : {
1916 0 : return;
1917 : }
1918 0 : tnum = Tex_Diffuse;
1919 0 : decalslots.push_back(new DecalSlot(decalslots.size()));
1920 0 : defslot = decalslots.back();
1921 : }
1922 1 : else if((matslot = findmaterial(type)) >= 0)
1923 : {
1924 0 : tnum = Tex_Diffuse;
1925 0 : defslot = &materialslots[matslot];
1926 0 : defslot->reset();
1927 : }
1928 1 : else if(!defslot)
1929 : {
1930 1 : return;
1931 : }
1932 0 : else if(tnum < 0)
1933 : {
1934 0 : tnum = Tex_Unknown;
1935 : }
1936 0 : Slot &s = *defslot;
1937 0 : s.loaded = false;
1938 0 : s.texmask |= 1<<tnum;
1939 0 : if(s.sts.size()>=8)
1940 : {
1941 0 : conoutf(Console_Warn, "warning: too many textures in %s", s.name());
1942 : }
1943 0 : s.sts.emplace_back();
1944 0 : Slot::Tex &st = s.sts.back();
1945 0 : st.type = tnum;
1946 0 : copystring(st.name, name);
1947 0 : path(st.name);
1948 0 : if(tnum == Tex_Diffuse)
1949 : {
1950 0 : setslotshader(s);
1951 0 : VSlot &vs = s.emptyvslot();
1952 0 : vs.rotation = std::clamp(*rot, 0, 7);
1953 0 : vs.offset = ivec2(*xoffset, *yoffset).max(0);
1954 0 : vs.scale = *scale <= 0 ? 1 : *scale;
1955 0 : propagatevslot(&vs, (1 << VSlot_Num) - 1);
1956 : }
1957 : }
1958 :
1959 1 : void texgrass(char *name)
1960 : {
1961 1 : if(!defslot)
1962 : {
1963 1 : return;
1964 : }
1965 0 : Slot &s = *defslot;
1966 0 : delete[] s.grass;
1967 0 : s.grass = name[0] ? newstring(makerelpath("media/texture", name)) : nullptr;
1968 : }
1969 :
1970 1 : void texscroll(float *scrollS, float *scrollT)
1971 : {
1972 1 : if(!defslot)
1973 : {
1974 1 : return;
1975 : }
1976 0 : Slot &s = *defslot;
1977 0 : s.variants->scroll = vec2(*scrollS/1000.0f, *scrollT/1000.0f);
1978 0 : propagatevslot(s.variants, 1 << VSlot_Scroll);
1979 : }
1980 :
1981 1 : void texoffset_(int *xoffset, int *yoffset)
1982 : {
1983 1 : if(!defslot)
1984 : {
1985 1 : return;
1986 : }
1987 0 : Slot &s = *defslot;
1988 0 : s.variants->offset = ivec2(*xoffset, *yoffset).max(0);
1989 0 : propagatevslot(s.variants, 1 << VSlot_Offset);
1990 : }
1991 :
1992 1 : void texrotate_(int *rot)
1993 : {
1994 1 : if(!defslot)
1995 : {
1996 1 : return;
1997 : }
1998 0 : Slot &s = *defslot;
1999 0 : s.variants->rotation = std::clamp(*rot, 0, 7);
2000 0 : propagatevslot(s.variants, 1 << VSlot_Rotation);
2001 : }
2002 :
2003 1 : void texangle_(float *a)
2004 : {
2005 1 : if(!defslot)
2006 : {
2007 1 : return;
2008 : }
2009 0 : Slot &s = *defslot;
2010 0 : s.variants->angle = vec(*a, std::sin(*a/RAD), std::cos(*a/RAD));
2011 0 : propagatevslot(s.variants, 1 << VSlot_Angle);
2012 : }
2013 :
2014 1 : void texscale(float *scale)
2015 : {
2016 1 : if(!defslot)
2017 : {
2018 1 : return;
2019 : }
2020 0 : Slot &s = *defslot;
2021 0 : s.variants->scale = *scale <= 0 ? 1 : *scale;
2022 0 : propagatevslot(s.variants, 1 << VSlot_Scale);
2023 : }
2024 :
2025 1 : void texalpha(float *front, float *back)
2026 : {
2027 1 : if(!defslot)
2028 : {
2029 1 : return;
2030 : }
2031 0 : Slot &s = *defslot;
2032 0 : s.variants->alphafront = std::clamp(*front, 0.0f, 1.0f);
2033 0 : s.variants->alphaback = std::clamp(*back, 0.0f, 1.0f);
2034 0 : propagatevslot(s.variants, 1 << VSlot_Alpha);
2035 : }
2036 :
2037 1 : void texcolor(float *r, float *g, float *b)
2038 : {
2039 1 : if(!defslot)
2040 : {
2041 1 : return;
2042 : }
2043 0 : Slot &s = *defslot;
2044 0 : s.variants->colorscale = vec(std::clamp(*r, 0.0f, 2.0f), std::clamp(*g, 0.0f, 2.0f), std::clamp(*b, 0.0f, 2.0f));
2045 0 : propagatevslot(s.variants, 1 << VSlot_Color);
2046 : }
2047 :
2048 1 : void texrefract(float *k, float *r, float *g, float *b)
2049 : {
2050 1 : if(!defslot)
2051 : {
2052 1 : return;
2053 : }
2054 0 : Slot &s = *defslot;
2055 0 : s.variants->refractscale = std::clamp(*k, 0.0f, 1.0f);
2056 0 : if(s.variants->refractscale > 0 && (*r > 0 || *g > 0 || *b > 0))
2057 : {
2058 0 : s.variants->refractcolor = vec(std::clamp(*r, 0.0f, 1.0f), std::clamp(*g, 0.0f, 1.0f), std::clamp(*b, 0.0f, 1.0f));
2059 : }
2060 : else
2061 : {
2062 0 : s.variants->refractcolor = vec(1, 1, 1);
2063 : }
2064 0 : propagatevslot(s.variants, 1 << VSlot_Refract);
2065 : }
2066 :
2067 1 : void texsmooth(int *id, int *angle)
2068 : {
2069 1 : if(!defslot)
2070 : {
2071 1 : return;
2072 : }
2073 0 : Slot &s = *defslot;
2074 0 : s.smooth = smoothangle(*id, *angle);
2075 : }
2076 :
2077 1 : void decaldepth(float *depth, float *fade)
2078 : {
2079 1 : if(!defslot || defslot->type() != Slot::SlotType_Decal)
2080 : {
2081 1 : return;
2082 : }
2083 0 : DecalSlot &s = *static_cast<DecalSlot *>(defslot);
2084 0 : s.depth = std::clamp(*depth, 1e-3f, 1e3f);
2085 0 : s.fade = std::clamp(*fade, 0.0f, s.depth);
2086 : }
2087 :
2088 0 : int DecalSlot::cancombine(int type) const
2089 : {
2090 0 : switch(type)
2091 : {
2092 0 : case Tex_Glow:
2093 : {
2094 0 : return Tex_Spec;
2095 : }
2096 0 : case Tex_Normal:
2097 : {
2098 0 : return texmask&(1 << Tex_Depth) ? Tex_Depth : (texmask & (1 << Tex_Glow) ? -1 : Tex_Spec);
2099 : }
2100 0 : default:
2101 : {
2102 0 : return -1;
2103 : }
2104 : }
2105 : }
2106 :
2107 0 : bool DecalSlot::shouldpremul(int type) const
2108 : {
2109 0 : switch(type)
2110 : {
2111 0 : case Tex_Diffuse:
2112 : {
2113 0 : return true;
2114 : }
2115 0 : default:
2116 : {
2117 0 : return false;
2118 : }
2119 : }
2120 : }
2121 :
2122 0 : static void addname(std::vector<char> &key, Slot &slot, Slot::Tex &t, bool combined = false, const char *prefix = nullptr)
2123 : {
2124 0 : if(combined)
2125 : {
2126 0 : key.push_back('&');
2127 : }
2128 0 : if(prefix)
2129 : {
2130 0 : while(*prefix)
2131 : {
2132 0 : key.push_back(*prefix++);
2133 : }
2134 : }
2135 0 : DEF_FORMAT_STRING(tname, "%s/%s", slot.texturedir(), t.name);
2136 0 : for(const char *s = path(tname); *s; key.push_back(*s++))
2137 : {
2138 : //(empty body)
2139 : }
2140 0 : }
2141 :
2142 : // Slot object
2143 :
2144 0 : VSlot &Slot::emptyvslot()
2145 : {
2146 0 : return *::emptyvslot(*this);
2147 : }
2148 :
2149 0 : int Slot::findtextype(int type, int last) const
2150 : {
2151 0 : for(uint i = last+1; i<sts.size(); i++)
2152 : {
2153 0 : if((type&(1<<sts[i].type)) && sts[i].combined<0)
2154 : {
2155 0 : return i;
2156 : }
2157 : }
2158 0 : return -1;
2159 : }
2160 :
2161 0 : int Slot::cancombine(int type) const
2162 : {
2163 0 : switch(type)
2164 : {
2165 0 : case Tex_Diffuse:
2166 : {
2167 0 : return texmask&((1 << Tex_Spec) | (1 << Tex_Normal)) ? Tex_Spec : Tex_Alpha;
2168 : }
2169 0 : case Tex_Normal:
2170 : {
2171 0 : return texmask&(1 << Tex_Depth) ? Tex_Depth : Tex_Alpha;
2172 : }
2173 0 : default:
2174 : {
2175 0 : return -1;
2176 : }
2177 : }
2178 : }
2179 :
2180 0 : void Slot::load(int index, Slot::Tex &t)
2181 : {
2182 0 : std::vector<char> key;
2183 0 : addname(key, *this, t, false, shouldpremul(t.type) ? "<premul>" : nullptr);
2184 0 : Slot::Tex *combine = nullptr;
2185 0 : for(uint i = 0; i < sts.size(); i++)
2186 : {
2187 0 : Slot::Tex &c = sts[i];
2188 0 : if(c.combined == index)
2189 : {
2190 0 : combine = &c;
2191 0 : addname(key, *this, c, true);
2192 0 : break;
2193 : }
2194 : }
2195 0 : key.push_back('\0');
2196 0 : auto itr = textures.find(key.data());
2197 0 : if(itr != textures.end())
2198 : {
2199 0 : t.t = &(*itr).second;
2200 0 : return;
2201 : }
2202 0 : t.t = nullptr;
2203 0 : int compress = 0,
2204 0 : wrap = 0;
2205 0 : ImageData ts;
2206 0 : if(!ts.texturedata(*this, t, true, &compress, &wrap))
2207 : {
2208 0 : t.t = notexture;
2209 0 : return;
2210 : }
2211 0 : if(!ts.compressed)
2212 : {
2213 0 : switch(t.type)
2214 : {
2215 0 : case Tex_Spec:
2216 : {
2217 0 : if(ts.bpp > 1)
2218 : {
2219 0 : ts.collapsespec();
2220 : }
2221 0 : break;
2222 : }
2223 0 : case Tex_Glow:
2224 : case Tex_Diffuse:
2225 : case Tex_Normal:
2226 0 : if(combine)
2227 : {
2228 0 : ImageData cs;
2229 0 : if(cs.texturedata(*this, *combine))
2230 : {
2231 0 : if(cs.w!=ts.w || cs.h!=ts.h)
2232 : {
2233 0 : cs.scaleimage(ts.w, ts.h);
2234 : }
2235 0 : switch(combine->type)
2236 : {
2237 0 : case Tex_Spec:
2238 : {
2239 0 : ts.mergespec(cs);
2240 0 : break;
2241 : }
2242 0 : case Tex_Depth:
2243 : {
2244 0 : ts.mergedepth(cs);
2245 0 : break;
2246 : }
2247 0 : case Tex_Alpha:
2248 : {
2249 0 : ts.mergealpha(cs);
2250 0 : break;
2251 : }
2252 : }
2253 : }
2254 0 : }
2255 0 : if(ts.bpp < 3)
2256 : {
2257 0 : ts.swizzleimage();
2258 : }
2259 0 : break;
2260 : }
2261 : }
2262 0 : if(!ts.compressed && shouldpremul(t.type))
2263 : {
2264 0 : ts.texpremul();
2265 : }
2266 0 : t.t = newtexture(nullptr, key.data(), ts, wrap, true, true, true, compress);
2267 0 : }
2268 :
2269 0 : void Slot::load()
2270 : {
2271 0 : linkslotshader(*this);
2272 0 : for(uint i = 0; i < sts.size(); i++)
2273 : {
2274 0 : Slot::Tex &t = sts[i];
2275 0 : if(t.combined >= 0)
2276 : {
2277 0 : continue;
2278 : }
2279 0 : int combine = cancombine(t.type);
2280 0 : if(combine >= 0 && (combine = findtextype(1<<combine)) >= 0)
2281 : {
2282 0 : Slot::Tex &c = sts[combine];
2283 0 : c.combined = i;
2284 : }
2285 : }
2286 0 : for(uint i = 0; i < sts.size(); i++)
2287 : {
2288 0 : Slot::Tex &t = sts[i];
2289 0 : if(t.combined >= 0)
2290 : {
2291 0 : continue;
2292 : }
2293 0 : switch(t.type)
2294 : {
2295 : default:
2296 : {
2297 0 : load(i, t);
2298 0 : break;
2299 : }
2300 : }
2301 : }
2302 0 : loaded = true;
2303 0 : }
2304 :
2305 : // VSlot
2306 :
2307 40 : void VSlot::addvariant(Slot *slot)
2308 : {
2309 40 : if(!slot->variants)
2310 : {
2311 40 : slot->variants = this;
2312 : }
2313 : else
2314 : {
2315 0 : VSlot *prev = slot->variants;
2316 0 : while(prev->next)
2317 : {
2318 0 : prev = prev->next;
2319 : }
2320 0 : prev->next = this;
2321 : }
2322 40 : }
2323 :
2324 0 : bool VSlot::isdynamic() const
2325 : {
2326 0 : return !scroll.iszero() || slot->shader->isdynamic();
2327 : }
2328 :
2329 : // end of Slot/VSlot
2330 0 : MatSlot &lookupmaterialslot(int index, bool load)
2331 : {
2332 0 : MatSlot &s = materialslots[index];
2333 0 : if(load && !s.linked)
2334 : {
2335 0 : if(!s.loaded)
2336 : {
2337 0 : s.load();
2338 : }
2339 0 : linkvslotshader(s);
2340 0 : s.linked = true;
2341 : }
2342 0 : return s;
2343 : }
2344 :
2345 0 : Slot &lookupslot(int index, bool load)
2346 : {
2347 0 : Slot &s = (static_cast<long>(slots.size()) > index) ? *slots[index] : ((slots.size() > Default_Geom) ? *slots[Default_Geom] : dummyslot);
2348 0 : if(!s.loaded && load)
2349 : {
2350 0 : s.load();
2351 : }
2352 0 : return s;
2353 : }
2354 :
2355 0 : VSlot &lookupvslot(int index, bool load)
2356 : {
2357 0 : VSlot &s = (static_cast<long>(vslots.size()) > index) && vslots[index]->slot ? *vslots[index] : ((slots.size() > Default_Geom) && slots[Default_Geom]->variants ? *slots[Default_Geom]->variants : dummyvslot);
2358 0 : if(load && !s.linked)
2359 : {
2360 0 : if(!s.slot->loaded)
2361 : {
2362 0 : s.slot->load();
2363 : }
2364 0 : linkvslotshader(s);
2365 0 : s.linked = true;
2366 : }
2367 0 : return s;
2368 : }
2369 :
2370 0 : DecalSlot &lookupdecalslot(int index, bool load)
2371 : {
2372 0 : DecalSlot &s = (static_cast<int>(decalslots.size()) > index) ? *decalslots[index] : dummydecalslot;
2373 0 : if(load && !s.linked)
2374 : {
2375 0 : if(!s.loaded)
2376 : {
2377 0 : s.load();
2378 : }
2379 0 : linkvslotshader(s);
2380 0 : s.linked = true;
2381 : }
2382 0 : return s;
2383 : }
2384 :
2385 0 : void linkslotshaders()
2386 : {
2387 0 : for(Slot * const &i : slots)
2388 : {
2389 0 : if(i->loaded)
2390 : {
2391 0 : linkslotshader(*i);
2392 : }
2393 : }
2394 0 : for(VSlot * const &i : vslots)
2395 : {
2396 0 : if(i->linked)
2397 : {
2398 0 : linkvslotshader(*i);
2399 : }
2400 : }
2401 0 : for(uint i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
2402 : {
2403 0 : if(materialslots[i].loaded)
2404 : {
2405 0 : linkslotshader(materialslots[i]);
2406 0 : linkvslotshader(materialslots[i]);
2407 : }
2408 : }
2409 0 : for(DecalSlot * const &i : decalslots)
2410 : {
2411 0 : if(i->loaded)
2412 : {
2413 0 : linkslotshader(*i);
2414 0 : linkvslotshader(*i);
2415 : }
2416 : }
2417 0 : }
2418 :
2419 0 : static void blitthumbnail(ImageData &d, ImageData &s, int x, int y)
2420 : {
2421 0 : d.forcergbimage();
2422 0 : s.forcergbimage();
2423 0 : uchar *dstrow = &d.data[d.pitch*y + d.bpp*x],
2424 0 : *srcrow = s.data;
2425 0 : for(int y = 0; y < s.h; ++y)
2426 : {
2427 0 : for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[s.w*s.bpp]; src < end; dst += d.bpp, src += s.bpp)
2428 : {
2429 0 : for(int k = 0; k < 3; ++k)
2430 : {
2431 0 : dst[k] = src[k];
2432 : }
2433 : }
2434 0 : dstrow += d.pitch;
2435 0 : srcrow += s.pitch;
2436 : }
2437 0 : }
2438 :
2439 0 : Texture *Slot::loadthumbnail()
2440 : {
2441 0 : if(thumbnail)
2442 : {
2443 0 : return thumbnail;
2444 : }
2445 0 : if(!variants)
2446 : {
2447 0 : thumbnail = notexture;
2448 0 : return thumbnail;
2449 : }
2450 0 : VSlot &vslot = *variants;
2451 0 : linkslotshader(*this, false);
2452 0 : linkvslotshader(vslot, false);
2453 0 : std::vector<char> name;
2454 0 : if(vslot.colorscale == vec(1, 1, 1))
2455 : {
2456 0 : addname(name, *this, sts[0], false, "<thumbnail>");
2457 : }
2458 : else
2459 : {
2460 0 : DEF_FORMAT_STRING(prefix, "<thumbnail:%.2f/%.2f/%.2f>", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z);
2461 0 : addname(name, *this, sts[0], false, prefix);
2462 : }
2463 0 : int glow = -1;
2464 0 : if(texmask&(1 << Tex_Glow))
2465 : {
2466 0 : for(uint j = 0; j < sts.size(); j++)
2467 : {
2468 0 : if(sts[j].type == Tex_Glow)
2469 : {
2470 0 : glow = j;
2471 0 : break;
2472 : }
2473 : }
2474 0 : if(glow >= 0)
2475 : {
2476 0 : DEF_FORMAT_STRING(prefix, "<glow:%.2f/%.2f/%.2f>", vslot.glowcolor.x, vslot.glowcolor.y, vslot.glowcolor.z);
2477 0 : addname(name, *this, sts[glow], true, prefix);
2478 : }
2479 : }
2480 0 : name.push_back('\0');
2481 0 : auto itr = textures.find(path(name.data()));
2482 0 : if(itr != textures.end())
2483 : {
2484 0 : thumbnail = &(*itr).second;
2485 0 : return &(*itr).second;
2486 : }
2487 : else
2488 : {
2489 0 : auto insert = textures.insert( { std::string(name.data()), Texture() } ).first;
2490 0 : Texture *t = &(*insert).second;
2491 0 : ImageData s, g, l, d;
2492 0 : s.texturedata(*this, sts[0], false);
2493 0 : if(glow >= 0)
2494 : {
2495 0 : g.texturedata(*this, sts[glow], false);
2496 : }
2497 0 : if(!s.data)
2498 : {
2499 0 : t = thumbnail = notexture;
2500 : }
2501 : else
2502 : {
2503 0 : if(vslot.colorscale != vec(1, 1, 1))
2504 : {
2505 0 : s.texmad(vslot.colorscale, vec(0, 0, 0));
2506 : }
2507 0 : int xs = s.w, ys = s.h;
2508 0 : if(s.w > 128 || s.h > 128)
2509 : {
2510 0 : s.scaleimage(std::min(s.w, 128), std::min(s.h, 128));
2511 : }
2512 0 : if(g.data)
2513 : {
2514 0 : if(g.w != s.w || g.h != s.h) g.scaleimage(s.w, s.h);
2515 0 : s.addglow(g, vslot.glowcolor);
2516 : }
2517 0 : if(l.data)
2518 : {
2519 0 : if(l.w != s.w/2 || l.h != s.h/2)
2520 : {
2521 0 : l.scaleimage(s.w/2, s.h/2);
2522 : }
2523 0 : blitthumbnail(s, l, s.w-l.w, s.h-l.h);
2524 : }
2525 0 : if(d.data)
2526 : {
2527 0 : if(vslot.colorscale != vec(1, 1, 1))
2528 : {
2529 0 : d.texmad(vslot.colorscale, vec(0, 0, 0));
2530 : }
2531 0 : if(d.w != s.w/2 || d.h != s.h/2)
2532 : {
2533 0 : d.scaleimage(s.w/2, s.h/2);
2534 : }
2535 0 : blitthumbnail(s, d, 0, 0);
2536 : }
2537 0 : if(s.bpp < 3)
2538 : {
2539 0 : s.forcergbimage();
2540 : }
2541 0 : t = newtexture(nullptr, name.data(), s, 0, false, false, true);
2542 0 : t->xs = xs;
2543 0 : t->ys = ys;
2544 0 : thumbnail = t;
2545 : }
2546 0 : return t;
2547 0 : }
2548 0 : }
2549 :
2550 : // environment mapped reflections
2551 :
2552 0 : void cleanuptextures()
2553 : {
2554 0 : for(Slot * const &i : slots)
2555 : {
2556 0 : i->cleanup();
2557 : }
2558 0 : for(VSlot * const &i : vslots)
2559 : {
2560 0 : i->cleanup();
2561 : }
2562 0 : for(uint i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
2563 : {
2564 0 : materialslots[i].cleanup();
2565 : }
2566 0 : for(DecalSlot * const &i : decalslots)
2567 : {
2568 0 : i->cleanup();
2569 : }
2570 0 : for(auto itr = textures.begin(); itr != textures.end(); ++itr)
2571 : {
2572 0 : Texture &t = (*itr).second;
2573 0 : delete[] t.alphamask;
2574 0 : t.alphamask = nullptr;
2575 0 : if(t.id)
2576 : {
2577 0 : glDeleteTextures(1, &t.id);
2578 0 : t.id = 0;
2579 : }
2580 0 : if(t.type&Texture::TRANSIENT)
2581 : {
2582 0 : itr = textures.erase(itr);
2583 : }
2584 : }
2585 0 : }
2586 :
2587 0 : bool reloadtexture(const char *name)
2588 : {
2589 0 : auto itr = textures.find(path(std::string(name)));
2590 0 : if(itr != textures.end())
2591 : {
2592 0 : return (*itr).second.reload();
2593 : }
2594 0 : return true;
2595 : }
2596 :
2597 0 : bool Texture::reload()
2598 : {
2599 0 : if(id)
2600 : {
2601 0 : return true;
2602 : }
2603 0 : switch(type&TYPE)
2604 : {
2605 0 : case IMAGE:
2606 : {
2607 0 : int compress = 0;
2608 0 : ImageData s;
2609 0 : if(!s.texturedata(name, true, &compress) || !newtexture(this, nullptr, s, clamp, mipmap, false, false, compress))
2610 : {
2611 0 : return false;
2612 : }
2613 0 : break;
2614 0 : }
2615 : }
2616 0 : return true;
2617 : }
2618 :
2619 1 : void reloadtex(char *name)
2620 : {
2621 1 : auto itr = textures.find(path(std::string(name)));
2622 1 : if(itr == textures.end())
2623 : {
2624 1 : conoutf(Console_Error, "texture %s is not loaded", name);
2625 1 : return;
2626 : }
2627 0 : Texture *t = &(*itr).second;
2628 0 : if(t->type&Texture::TRANSIENT)
2629 : {
2630 0 : conoutf(Console_Error, "can't reload transient texture %s", name);
2631 0 : return;
2632 : }
2633 0 : delete[] t->alphamask;
2634 0 : t->alphamask = nullptr;
2635 0 : Texture oldtex = *t;
2636 0 : t->id = 0;
2637 0 : if(!t->reload())
2638 : {
2639 0 : if(t->id)
2640 : {
2641 0 : glDeleteTextures(1, &t->id);
2642 : }
2643 0 : *t = oldtex;
2644 0 : conoutf(Console_Error, "failed to reload texture %s", name);
2645 : }
2646 : }
2647 :
2648 0 : void reloadtextures()
2649 : {
2650 0 : int reloaded = 0;
2651 0 : for(auto &[k, t] : textures)
2652 : {
2653 0 : loadprogress = static_cast<float>(++reloaded)/textures.size();
2654 0 : t.reload();
2655 : }
2656 0 : loadprogress = 0;
2657 0 : }
2658 :
2659 0 : static void writepngchunk(stream *f, const char *type, const uchar *data = nullptr, uint len = 0)
2660 : {
2661 0 : f->putbig<uint>(len);
2662 0 : f->write(type, 4);
2663 0 : f->write(data, len);
2664 :
2665 0 : uint crc = crc32(0, Z_NULL, 0);
2666 0 : crc = crc32(crc, reinterpret_cast<const Bytef *>(type), 4);
2667 0 : if(data)
2668 : {
2669 0 : crc = crc32(crc, data, len);
2670 : }
2671 0 : f->putbig<uint>(crc);
2672 0 : }
2673 :
2674 : VARP(compresspng, 0, 9, 9);
2675 :
2676 0 : static void flushzip(z_stream &z, uchar *buf, const uint &buflen, uint &len, stream *f, uint &crc)
2677 : {
2678 0 : int flush = buflen- z.avail_out;
2679 0 : crc = crc32(crc, buf, flush);
2680 0 : len += flush;
2681 0 : f->write(buf, flush);
2682 0 : z.next_out = static_cast<Bytef *>(buf);
2683 0 : z.avail_out = buflen;
2684 0 : }
2685 :
2686 1 : static void savepng(const char *filename, const ImageData &image, bool flip)
2687 : {
2688 1 : if(!image.h || !image.w)
2689 : {
2690 1 : conoutf(Console_Error, "cannot save 0-size png");
2691 1 : return;
2692 : }
2693 0 : uchar ctype = 0;
2694 0 : switch(image.bpp)
2695 : {
2696 0 : case 1:
2697 : {
2698 0 : ctype = 0;
2699 0 : break;
2700 : }
2701 0 : case 2:
2702 : {
2703 0 : ctype = 4;
2704 0 : break;
2705 : }
2706 0 : case 3:
2707 : {
2708 0 : ctype = 2;
2709 0 : break;
2710 : }
2711 0 : case 4:
2712 : {
2713 0 : ctype = 6;
2714 0 : break;
2715 : }
2716 0 : default:
2717 : {
2718 0 : conoutf(Console_Error, "failed saving png to %s", filename);
2719 0 : return;
2720 : }
2721 : }
2722 0 : stream *f = openfile(filename, "wb");
2723 0 : if(!f)
2724 : {
2725 0 : conoutf(Console_Error, "could not write to %s", filename);
2726 0 : return;
2727 : }
2728 0 : uchar signature[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
2729 0 : f->write(signature, sizeof(signature));
2730 : struct pngihdr
2731 : {
2732 : uint width,
2733 : height;
2734 : uchar bitdepth,
2735 : colortype,
2736 : compress,
2737 : filter,
2738 : interlace;
2739 : };
2740 0 : pngihdr ihdr =
2741 : {
2742 0 : static_cast<uint>(endianswap(image.w)),
2743 0 : static_cast<uint>(endianswap(image.h)),
2744 : 8,
2745 : ctype,
2746 : 0,
2747 : 0,
2748 : 0
2749 0 : };
2750 0 : writepngchunk(f, "IHDR", reinterpret_cast<uchar *>(&ihdr), 13);
2751 0 : stream::offset idat = f->tell();
2752 0 : uint len = 0;
2753 0 : f->write("\0\0\0\0IDAT", 8);
2754 0 : uint crc = crc32(0, Z_NULL, 0);
2755 0 : crc = crc32(crc, reinterpret_cast<const Bytef *>("IDAT"), 4);
2756 : z_stream z;
2757 0 : z.zalloc = nullptr;
2758 0 : z.zfree = nullptr;
2759 0 : z.opaque = nullptr;
2760 0 : if(deflateInit(&z, compresspng) != Z_OK)
2761 : {
2762 0 : goto error; //goto is beneath FLUSHZ macro
2763 : }
2764 : uchar buf[1<<12];
2765 0 : z.next_out = static_cast<Bytef *>(buf);
2766 0 : z.avail_out = sizeof(buf);
2767 0 : for(int i = 0; i < image.h; ++i)
2768 : {
2769 0 : uchar filter = 0;
2770 0 : for(int j = 0; j < 2; ++j)
2771 : {
2772 0 : z.next_in = j ? static_cast<Bytef *>(image.data) + (flip ? image.h-i-1 : i)*image.pitch : static_cast<Bytef *>(&filter);
2773 0 : z.avail_in = j ? image.w*image.bpp : 1;
2774 0 : while(z.avail_in > 0)
2775 : {
2776 0 : if(deflate(&z, Z_NO_FLUSH) != Z_OK)
2777 : {
2778 0 : goto cleanuperror; //goto is beneath FLUSHZ macro
2779 : }
2780 0 : flushzip(z, buf, sizeof(buf), len, f, crc);
2781 : }
2782 : }
2783 : }
2784 :
2785 : for(;;)
2786 : {
2787 0 : int err = deflate(&z, Z_FINISH);
2788 0 : if(err != Z_OK && err != Z_STREAM_END)
2789 : {
2790 0 : goto cleanuperror;
2791 : }
2792 0 : flushzip(z, buf, sizeof(buf), len, f, crc);
2793 0 : if(err == Z_STREAM_END)
2794 : {
2795 0 : break;
2796 : }
2797 0 : }
2798 0 : deflateEnd(&z);
2799 :
2800 0 : f->seek(idat, SEEK_SET);
2801 0 : f->putbig<uint>(len);
2802 0 : f->seek(0, SEEK_END);
2803 0 : f->putbig<uint>(crc);
2804 0 : writepngchunk(f, "IEND");
2805 0 : delete f;
2806 0 : return;
2807 :
2808 0 : cleanuperror:
2809 0 : deflateEnd(&z);
2810 :
2811 0 : error:
2812 0 : delete f;
2813 0 : conoutf(Console_Error, "failed saving png to %s", filename);
2814 : }
2815 :
2816 : SVARP(screenshotdir, "screenshot");
2817 :
2818 1 : void screenshot(char *filename)
2819 : {
2820 : static string buf;
2821 1 : int dirlen = 0;
2822 1 : copystring(buf, screenshotdir);
2823 1 : if(screenshotdir[0])
2824 : {
2825 1 : dirlen = std::strlen(buf);
2826 1 : if(buf[dirlen] != '/' && buf[dirlen] != '\\' && dirlen+1 < static_cast<int>(sizeof(buf)))
2827 : {
2828 1 : buf[dirlen++] = '/';
2829 1 : buf[dirlen] = '\0';
2830 : }
2831 1 : const char *dir = findfile(buf, "w");
2832 1 : if(!fileexists(dir, "w"))
2833 : {
2834 1 : createdir(dir);
2835 : }
2836 : }
2837 1 : if(filename[0])
2838 : {
2839 0 : concatstring(buf, filename);
2840 : }
2841 : else
2842 : {
2843 : string sstime;
2844 1 : time_t t = std::time(nullptr);
2845 1 : size_t len = std::strftime(sstime, sizeof(sstime), "%Y-%m-%d_%H.%M.%S.png", std::localtime(&t));
2846 1 : sstime[std::min(len, sizeof(sstime)-1)] = '\0';
2847 1 : concatstring(buf, sstime);
2848 24 : for(char *s = &buf[dirlen]; *s; s++)
2849 : {
2850 23 : if(iscubespace(*s) || *s == '/' || *s == '\\')
2851 : {
2852 0 : *s = '-';
2853 : }
2854 : }
2855 : }
2856 :
2857 1 : ImageData image(screenw, screenh, 3);
2858 1 : glPixelStorei(GL_PACK_ALIGNMENT, texalign(screenw, 3));
2859 1 : glReadPixels(0, 0, screenw, screenh, GL_RGB, GL_UNSIGNED_BYTE, image.data);
2860 1 : savepng(path(buf), image, true);
2861 1 : }
2862 :
2863 : //used in libprimis api, avoids having to provide entire Shader iface
2864 0 : void setldrnotexture()
2865 : {
2866 0 : ldrnotextureshader->set();
2867 0 : }
2868 :
2869 1 : void inittexturecmds()
2870 : {
2871 1 : addcommand("texturereset", reinterpret_cast<identfun>(texturereset), "i", Id_Command);
2872 1 : addcommand("materialreset", reinterpret_cast<identfun>(materialreset), "", Id_Command);
2873 1 : addcommand("decalreset", reinterpret_cast<identfun>(decalreset), "i", Id_Command);
2874 2 : addcommand("compactvslots", reinterpret_cast<identfun>(+[](int *cull){multiplayerwarn();rootworld.compactvslots(*cull!=0);rootworld.allchanged();}), "i", Id_Command);
2875 1 : addcommand("texture", reinterpret_cast<identfun>(texture), "ssiiif", Id_Command);
2876 1 : addcommand("texgrass", reinterpret_cast<identfun>(texgrass), "s", Id_Command);
2877 1 : addcommand("texscroll", reinterpret_cast<identfun>(texscroll), "ff", Id_Command);
2878 1 : addcommand("texoffset", reinterpret_cast<identfun>(texoffset_), "ii", Id_Command);
2879 1 : addcommand("texrotate", reinterpret_cast<identfun>(texrotate_), "i", Id_Command);
2880 1 : addcommand("texangle", reinterpret_cast<identfun>(texangle_), "f", Id_Command);
2881 1 : addcommand("texscale", reinterpret_cast<identfun>(texscale), "f", Id_Command);
2882 1 : addcommand("texalpha", reinterpret_cast<identfun>(texalpha), "ff", Id_Command);
2883 1 : addcommand("texcolor", reinterpret_cast<identfun>(texcolor), "fff", Id_Command);
2884 1 : addcommand("texrefract", reinterpret_cast<identfun>(texrefract), "ffff", Id_Command);
2885 1 : addcommand("texsmooth", reinterpret_cast<identfun>(texsmooth), "ib", Id_Command);
2886 1 : addcommand("decaldepth", reinterpret_cast<identfun>(decaldepth), "ff", Id_Command);
2887 1 : addcommand("reloadtex", reinterpret_cast<identfun>(reloadtex), "s", Id_Command);
2888 1 : addcommand("screenshot", reinterpret_cast<identfun>(screenshot), "s", Id_Command);
2889 1 : }
2890 :
|