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