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 0 : const char *MatSlot::name() const
1092 : {
1093 0 : return tempformatstring("material slot %s", findmaterialname(Slot::index));
1094 : }
1095 :
1096 0 : VSlot &MatSlot::emptyvslot()
1097 : {
1098 0 : return *this;
1099 : }
1100 :
1101 2 : const char *DecalSlot::name() const
1102 : {
1103 2 : return tempformatstring("decal slot %d", Slot::index);
1104 : }
1105 :
1106 1 : void texturereset(const int *n)
1107 : {
1108 1 : if(!(identflags&Idf_Overridden) && !allowediting)
1109 : {
1110 1 : return;
1111 : }
1112 0 : defslot = nullptr;
1113 0 : resetslotshader();
1114 0 : int limit = std::clamp(*n, 0, static_cast<int>(slots.size()));
1115 0 : for(size_t i = limit; i < slots.size(); i++)
1116 : {
1117 0 : Slot *s = slots[i];
1118 0 : for(VSlot *vs = s->variants; vs; vs = vs->next)
1119 : {
1120 0 : vs->slot = &dummyslot;
1121 : }
1122 0 : delete s;
1123 : }
1124 0 : slots.resize(limit);
1125 0 : while(vslots.size())
1126 : {
1127 0 : VSlot *vs = vslots.back();
1128 0 : if(vs->slot != &dummyslot || vs->changed)
1129 : {
1130 : break;
1131 : }
1132 0 : delete vslots.back();
1133 0 : vslots.pop_back();
1134 : }
1135 : }
1136 :
1137 1 : void materialreset()
1138 : {
1139 1 : if(!(identflags&Idf_Overridden) && !allowediting)
1140 : {
1141 1 : return;
1142 : }
1143 0 : defslot = nullptr;
1144 0 : for(int i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
1145 : {
1146 0 : materialslots[i].reset();
1147 : }
1148 : }
1149 :
1150 1 : void decalreset(const int *n)
1151 : {
1152 1 : if(!(identflags&Idf_Overridden) && !allowediting)
1153 : {
1154 1 : return;
1155 : }
1156 0 : defslot = nullptr;
1157 0 : resetslotshader();
1158 0 : for(size_t i = *n; i < decalslots.size(); ++i)
1159 : {
1160 0 : delete decalslots.at(i);
1161 : }
1162 0 : decalslots.resize(*n);
1163 : }
1164 :
1165 : static int compactedvslots = 0,
1166 : compactvslotsprogress = 0,
1167 : clonedvslots = 0;
1168 : static bool markingvslots = false;
1169 :
1170 0 : void clearslots()
1171 : {
1172 0 : defslot = nullptr;
1173 0 : resetslotshader();
1174 0 : for(Slot * i : slots)
1175 : {
1176 0 : delete i;
1177 : }
1178 0 : slots.clear();
1179 0 : for(VSlot * i : vslots)
1180 : {
1181 0 : delete i;
1182 : }
1183 0 : vslots.clear();
1184 0 : for(int i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
1185 : {
1186 0 : materialslots[i].reset();
1187 : }
1188 0 : decalslots.clear();
1189 0 : clonedvslots = 0;
1190 0 : }
1191 :
1192 0 : static void assignvslot(VSlot &vs)
1193 : {
1194 0 : vs.index = compactedvslots++;
1195 0 : }
1196 :
1197 0 : void compactvslot(int &index)
1198 : {
1199 0 : if(static_cast<long>(vslots.size()) > index)
1200 : {
1201 0 : VSlot &vs = *vslots[index];
1202 0 : if(vs.index < 0)
1203 : {
1204 0 : assignvslot(vs);
1205 : }
1206 0 : if(!markingvslots)
1207 : {
1208 0 : index = vs.index;
1209 : }
1210 : }
1211 0 : }
1212 :
1213 0 : void compactvslot(VSlot &vs)
1214 : {
1215 0 : if(vs.index < 0)
1216 : {
1217 0 : assignvslot(vs);
1218 : }
1219 0 : }
1220 :
1221 : //n will be capped at 8
1222 0 : void compactvslots(cube * const c, int n)
1223 : {
1224 0 : if((compactvslotsprogress++&0xFFF)==0)
1225 : {
1226 0 : renderprogress(std::min(static_cast<float>(compactvslotsprogress)/allocnodes, 1.0f), markingvslots ? "marking slots..." : "compacting slots...");
1227 : }
1228 0 : for(int i = 0; i < std::min(n, 8); ++i)
1229 : {
1230 0 : if(c[i].children)
1231 : {
1232 0 : compactvslots(c[i].children->data());
1233 : }
1234 : else
1235 : {
1236 0 : for(int j = 0; j < 6; ++j)
1237 : {
1238 0 : if(vslots.size() > c[i].texture[j])
1239 : {
1240 0 : VSlot &vs = *vslots[c[i].texture[j]];
1241 0 : if(vs.index < 0)
1242 : {
1243 0 : assignvslot(vs);
1244 : }
1245 0 : if(!markingvslots)
1246 : {
1247 0 : c[i].texture[j] = vs.index;
1248 : }
1249 : }
1250 : }
1251 : }
1252 : }
1253 0 : }
1254 :
1255 1 : int cubeworld::compactvslots(bool cull)
1256 : {
1257 1 : if(!worldroot)
1258 : {
1259 1 : conoutf(Console_Error, "no cube to compact");
1260 1 : return 0;
1261 : }
1262 0 : defslot = nullptr;
1263 0 : clonedvslots = 0;
1264 0 : markingvslots = cull;
1265 0 : compactedvslots = 0;
1266 0 : compactvslotsprogress = 0;
1267 0 : for(size_t i = 0; i < vslots.size(); i++)
1268 : {
1269 0 : vslots[i]->index = -1;
1270 : }
1271 0 : if(cull)
1272 : {
1273 0 : uint numdefaults = std::min(static_cast<uint>(Default_NumDefaults), static_cast<uint>(slots.size()));
1274 0 : for(uint i = 0; i < numdefaults; ++i)
1275 : {
1276 0 : slots[i]->variants->index = compactedvslots++;
1277 : }
1278 : }
1279 : else
1280 : {
1281 0 : for(const Slot *i : slots)
1282 : {
1283 0 : i->variants->index = compactedvslots++;
1284 : }
1285 0 : for(const VSlot *i : vslots)
1286 : {
1287 0 : if(!i->changed && i->index < 0)
1288 : {
1289 0 : markingvslots = true;
1290 0 : break;
1291 : }
1292 : }
1293 : }
1294 0 : ::compactvslots(worldroot->data());
1295 0 : int total = compactedvslots;
1296 0 : compacteditvslots();
1297 0 : for(VSlot *i : vslots)
1298 : {
1299 0 : if(i->changed)
1300 : {
1301 0 : continue;
1302 : }
1303 0 : while(i->next)
1304 : {
1305 0 : if(i->next->index < 0)
1306 : {
1307 0 : i->next = i->next->next;
1308 : }
1309 : else
1310 : {
1311 0 : i = i->next;
1312 : }
1313 : }
1314 : }
1315 0 : if(markingvslots)
1316 : {
1317 0 : markingvslots = false;
1318 0 : compactedvslots = 0;
1319 0 : compactvslotsprogress = 0;
1320 0 : int lastdiscard = 0;
1321 0 : for(size_t i = 0; i < vslots.size(); i++)
1322 : {
1323 0 : VSlot &vs = *vslots[i];
1324 0 : if(vs.changed || (vs.index < 0 && !vs.next))
1325 : {
1326 0 : vs.index = -1;
1327 : }
1328 : else
1329 : {
1330 0 : if(!cull)
1331 : {
1332 0 : while(lastdiscard < static_cast<int>(i))
1333 : {
1334 0 : VSlot &ds = *vslots[lastdiscard++];
1335 0 : if(!ds.changed && ds.index < 0)
1336 : {
1337 0 : ds.index = compactedvslots++;
1338 : }
1339 : }
1340 : }
1341 0 : vs.index = compactedvslots++;
1342 : }
1343 : }
1344 0 : ::compactvslots(worldroot->data());
1345 0 : total = compactedvslots;
1346 0 : compacteditvslots();
1347 : }
1348 0 : compactmruvslots();
1349 0 : if(cull)
1350 : {
1351 0 : for(int i = static_cast<int>(slots.size()); --i >=0;) //note reverse iteration
1352 : {
1353 0 : if(slots[i]->variants->index < 0)
1354 : {
1355 0 : delete slots.at(i);
1356 0 : slots.erase(slots.begin() + i);
1357 : }
1358 : }
1359 0 : for(size_t i = 0; i < slots.size(); i++)
1360 : {
1361 0 : slots[i]->index = i;
1362 : }
1363 : }
1364 0 : for(size_t i = 0; i < vslots.size(); i++)
1365 : {
1366 0 : while(vslots[i]->index >= 0 && vslots[i]->index != static_cast<int>(i))
1367 : {
1368 0 : std::swap(vslots[i], vslots[vslots[i]->index]);
1369 : }
1370 : }
1371 0 : for(size_t i = compactedvslots; i < vslots.size(); i++)
1372 : {
1373 0 : delete vslots[i];
1374 : }
1375 0 : vslots.resize(compactedvslots);
1376 0 : return total;
1377 : }
1378 :
1379 0 : static void clampvslotoffset(VSlot &dst, Slot *slot = nullptr)
1380 : {
1381 0 : if(!slot)
1382 : {
1383 0 : slot = dst.slot;
1384 : }
1385 0 : if(slot && slot->sts.size())
1386 : {
1387 0 : if(!slot->loaded)
1388 : {
1389 0 : slot->load();
1390 : }
1391 0 : const Texture *t = slot->sts[0].t;
1392 0 : int xs = t->xs,
1393 0 : ys = t->ys;
1394 0 : if(t->type & Texture::MIRROR)
1395 : {
1396 0 : xs *= 2;
1397 0 : ys *= 2;
1398 : }
1399 0 : if(texrotations[dst.rotation].swapxy)
1400 : {
1401 0 : std::swap(xs, ys);
1402 : }
1403 0 : dst.offset.x() %= xs;
1404 0 : if(dst.offset.x() < 0)
1405 : {
1406 0 : dst.offset.x() += xs;
1407 : }
1408 0 : dst.offset.y() %= ys;
1409 0 : if(dst.offset.y() < 0)
1410 : {
1411 0 : dst.offset.y() += ys;
1412 : }
1413 : }
1414 : else
1415 : {
1416 0 : dst.offset.max(0);
1417 : }
1418 0 : }
1419 :
1420 0 : static void propagatevslot(VSlot &dst, const VSlot &src, int diff, bool edit = false)
1421 : {
1422 0 : if(diff & (1 << VSlot_ShParam))
1423 : {
1424 0 : for(size_t i = 0; i < src.params.size(); i++)
1425 : {
1426 0 : dst.params.push_back(src.params[i]);
1427 : }
1428 : }
1429 0 : if(diff & (1 << VSlot_Scale))
1430 : {
1431 0 : dst.scale = src.scale;
1432 : }
1433 0 : if(diff & (1 << VSlot_Rotation))
1434 : {
1435 0 : dst.rotation = src.rotation;
1436 0 : if(edit && !dst.offset.iszero())
1437 : {
1438 0 : clampvslotoffset(dst);
1439 : }
1440 : }
1441 0 : if(diff & (1 << VSlot_Angle))
1442 : {
1443 0 : dst.angle = src.angle;
1444 : }
1445 0 : if(diff & (1 << VSlot_Offset))
1446 : {
1447 0 : dst.offset = src.offset;
1448 0 : if(edit)
1449 : {
1450 0 : clampvslotoffset(dst);
1451 : }
1452 : }
1453 0 : if(diff & (1 << VSlot_Scroll))
1454 : {
1455 0 : dst.scroll = src.scroll;
1456 : }
1457 0 : if(diff & (1 << VSlot_Alpha))
1458 : {
1459 0 : dst.alphafront = src.alphafront;
1460 0 : dst.alphaback = src.alphaback;
1461 : }
1462 0 : if(diff & (1 << VSlot_Color))
1463 : {
1464 0 : dst.colorscale = src.colorscale;
1465 : }
1466 0 : if(diff & (1 << VSlot_Refract))
1467 : {
1468 0 : dst.refractscale = src.refractscale;
1469 0 : dst.refractcolor = src.refractcolor;
1470 : }
1471 0 : }
1472 :
1473 0 : static void propagatevslot(const VSlot *root, int changed)
1474 : {
1475 0 : for(VSlot *vs = root->next; vs; vs = vs->next)
1476 : {
1477 0 : int diff = changed & ~vs->changed;
1478 0 : if(diff)
1479 : {
1480 0 : propagatevslot(*vs, *root, diff);
1481 : }
1482 : }
1483 0 : }
1484 :
1485 0 : static void mergevslot(VSlot &dst, const VSlot &src, int diff, Slot *slot = nullptr)
1486 : {
1487 0 : if(diff & (1 << VSlot_ShParam))
1488 : {
1489 0 : for(size_t i = 0; i < src.params.size(); i++)
1490 : {
1491 0 : const SlotShaderParam &sp = src.params[i];
1492 0 : for(size_t j = 0; j < dst.params.size(); j++)
1493 : {
1494 0 : SlotShaderParam &dp = dst.params[j];
1495 0 : if(sp.name == dp.name)
1496 : {
1497 0 : std::memcpy(dp.val, sp.val, sizeof(dp.val));
1498 0 : goto nextparam; //bail out of loop
1499 : }
1500 : }
1501 0 : dst.params.push_back(sp);
1502 0 : nextparam:;
1503 : }
1504 : }
1505 0 : if(diff & (1 << VSlot_Scale))
1506 : {
1507 0 : dst.scale = std::clamp(dst.scale*src.scale, 1/8.0f, 8.0f);
1508 : }
1509 0 : if(diff & (1 << VSlot_Rotation))
1510 : {
1511 0 : dst.rotation = std::clamp(dst.rotation + src.rotation, 0, 7);
1512 0 : if(!dst.offset.iszero())
1513 : {
1514 0 : clampvslotoffset(dst, slot);
1515 : }
1516 : }
1517 0 : if(diff & (1 << VSlot_Angle))
1518 : {
1519 0 : dst.angle.add(src.angle);
1520 : }
1521 0 : if(diff & (1 << VSlot_Offset))
1522 : {
1523 0 : dst.offset.add(src.offset);
1524 0 : clampvslotoffset(dst, slot);
1525 : }
1526 0 : if(diff & (1 << VSlot_Scroll))
1527 : {
1528 0 : dst.scroll.add(src.scroll);
1529 : }
1530 0 : if(diff & (1 << VSlot_Alpha))
1531 : {
1532 0 : dst.alphafront = src.alphafront;
1533 0 : dst.alphaback = src.alphaback;
1534 : }
1535 0 : if(diff & (1 << VSlot_Color))
1536 : {
1537 0 : dst.colorscale.mul(src.colorscale);
1538 : }
1539 0 : if(diff & (1 << VSlot_Refract))
1540 : {
1541 0 : dst.refractscale *= src.refractscale;
1542 0 : dst.refractcolor.mul(src.refractcolor);
1543 : }
1544 0 : }
1545 :
1546 0 : void mergevslot(VSlot &dst, const VSlot &src, const VSlot &delta)
1547 : {
1548 0 : dst.changed = src.changed | delta.changed;
1549 0 : propagatevslot(dst, src, (1 << VSlot_Num) - 1);
1550 0 : mergevslot(dst, delta, delta.changed, src.slot);
1551 0 : }
1552 :
1553 0 : static VSlot *reassignvslot(Slot &owner, VSlot *vs)
1554 : {
1555 0 : vs->reset();
1556 0 : owner.variants = vs;
1557 0 : while(vs)
1558 : {
1559 0 : vs->slot = &owner;
1560 0 : vs->linked = false;
1561 0 : vs = vs->next;
1562 : }
1563 0 : return owner.variants;
1564 : }
1565 :
1566 0 : static VSlot *emptyvslot(Slot &owner)
1567 : {
1568 0 : int offset = 0;
1569 0 : for(int i = static_cast<int>(slots.size()); --i >=0;) //note reverse iteration
1570 : {
1571 0 : if(slots[i]->variants)
1572 : {
1573 0 : offset = slots[i]->variants->index + 1;
1574 0 : break;
1575 : }
1576 : }
1577 0 : for(size_t i = offset; i < vslots.size(); i++)
1578 : {
1579 0 : if(!vslots[i]->changed)
1580 : {
1581 0 : return reassignvslot(owner, vslots[i]);
1582 : }
1583 : }
1584 0 : vslots.push_back(new VSlot(&owner, vslots.size()));
1585 0 : return vslots.back();
1586 : }
1587 :
1588 0 : static bool comparevslot(const VSlot &dst, const VSlot &src, int diff)
1589 : {
1590 0 : if(diff & (1 << VSlot_ShParam))
1591 : {
1592 0 : if(src.params.size() != dst.params.size())
1593 : {
1594 0 : return false;
1595 : }
1596 0 : for(size_t i = 0; i < src.params.size(); i++)
1597 : {
1598 0 : const SlotShaderParam &sp = src.params[i], &dp = dst.params[i];
1599 0 : if(sp.name != dp.name || std::memcmp(sp.val, dp.val, sizeof(sp.val)))
1600 : {
1601 0 : return false;
1602 : }
1603 : }
1604 : }
1605 0 : if(diff & (1 << VSlot_Scale) && dst.scale != src.scale) return false;
1606 0 : if(diff & (1 << VSlot_Rotation)&& dst.rotation != src.rotation) return false;
1607 0 : if(diff & (1 << VSlot_Angle) && dst.angle != src.angle) return false;
1608 0 : if(diff & (1 << VSlot_Offset) && dst.offset != src.offset) return false;
1609 0 : if(diff & (1 << VSlot_Scroll) && dst.scroll != src.scroll) return false;
1610 0 : if(diff & (1 << VSlot_Alpha) && (dst.alphafront != src.alphafront || dst.alphaback != src.alphaback)) return false;
1611 0 : if(diff & (1 << VSlot_Color) && dst.colorscale != src.colorscale) return false;
1612 0 : if(diff & (1 << VSlot_Refract) && (dst.refractscale != src.refractscale || dst.refractcolor != src.refractcolor)) return false;
1613 0 : return true;
1614 : }
1615 :
1616 0 : void packvslot(std::vector<uchar> &buf, const VSlot &src)
1617 : {
1618 0 : if(src.changed & (1 << VSlot_ShParam))
1619 : {
1620 0 : for(size_t i = 0; i < src.params.size(); i++)
1621 : {
1622 0 : const SlotShaderParam &p = src.params[i];
1623 0 : buf.push_back(VSlot_ShParam);
1624 0 : sendstring(p.name, buf);
1625 0 : for(int j = 0; j < 4; ++j)
1626 : {
1627 0 : putfloat(buf, p.val[j]);
1628 : }
1629 : }
1630 : }
1631 0 : if(src.changed & (1 << VSlot_Scale))
1632 : {
1633 0 : buf.push_back(VSlot_Scale);
1634 0 : putfloat(buf, src.scale);
1635 : }
1636 0 : if(src.changed & (1 << VSlot_Rotation))
1637 : {
1638 0 : buf.push_back(VSlot_Rotation);
1639 0 : putint(buf, src.rotation);
1640 : }
1641 0 : if(src.changed & (1 << VSlot_Angle))
1642 : {
1643 0 : buf.push_back(VSlot_Angle);
1644 0 : putfloat(buf, src.angle.x);
1645 0 : putfloat(buf, src.angle.y);
1646 0 : putfloat(buf, src.angle.z);
1647 : }
1648 0 : if(src.changed & (1 << VSlot_Offset))
1649 : {
1650 0 : buf.push_back(VSlot_Offset);
1651 0 : putint(buf, src.offset.x());
1652 0 : putint(buf, src.offset.y());
1653 : }
1654 0 : if(src.changed & (1 << VSlot_Scroll))
1655 : {
1656 0 : buf.push_back(VSlot_Scroll);
1657 0 : putfloat(buf, src.scroll.x);
1658 0 : putfloat(buf, src.scroll.y);
1659 : }
1660 0 : if(src.changed & (1 << VSlot_Alpha))
1661 : {
1662 0 : buf.push_back(VSlot_Alpha);
1663 0 : putfloat(buf, src.alphafront);
1664 0 : putfloat(buf, src.alphaback);
1665 : }
1666 0 : if(src.changed & (1 << VSlot_Color))
1667 : {
1668 0 : buf.push_back(VSlot_Color);
1669 0 : putfloat(buf, src.colorscale.r());
1670 0 : putfloat(buf, src.colorscale.g());
1671 0 : putfloat(buf, src.colorscale.b());
1672 : }
1673 0 : if(src.changed & (1 << VSlot_Refract))
1674 : {
1675 0 : buf.push_back(VSlot_Refract);
1676 0 : putfloat(buf, src.refractscale);
1677 0 : putfloat(buf, src.refractcolor.r());
1678 0 : putfloat(buf, src.refractcolor.g());
1679 0 : putfloat(buf, src.refractcolor.b());
1680 : }
1681 0 : buf.push_back(0xFF);
1682 0 : }
1683 :
1684 : //used in iengine.h
1685 0 : void packvslot(std::vector<uchar> &buf, int index)
1686 : {
1687 0 : if(static_cast<long>(vslots.size()) > index)
1688 : {
1689 0 : packvslot(buf, *vslots[index]);
1690 : }
1691 : else
1692 : {
1693 0 : buf.push_back(0xFF);
1694 : }
1695 0 : }
1696 :
1697 : //used in iengine.h
1698 0 : void packvslot(std::vector<uchar> &buf, const VSlot *vs)
1699 : {
1700 0 : if(vs)
1701 : {
1702 0 : packvslot(buf, *vs);
1703 : }
1704 : else
1705 : {
1706 0 : buf.push_back(0xFF);
1707 : }
1708 0 : }
1709 :
1710 0 : bool unpackvslot(ucharbuf &buf, VSlot &dst, bool delta)
1711 : {
1712 0 : while(buf.remaining())
1713 : {
1714 0 : int changed = buf.get();
1715 0 : if(changed >= 0x80)
1716 : {
1717 0 : break;
1718 : }
1719 0 : switch(changed)
1720 : {
1721 0 : case VSlot_ShParam:
1722 : {
1723 : string name;
1724 0 : getstring(name, buf);
1725 0 : SlotShaderParam p = { name[0] ? getshaderparamname(name) : nullptr, SIZE_MAX, 0, { 0, 0, 0, 0 } };
1726 0 : for(int i = 0; i < 4; ++i)
1727 : {
1728 0 : p.val[i] = getfloat(buf);
1729 : }
1730 0 : if(p.name)
1731 : {
1732 0 : dst.params.push_back(p);
1733 : }
1734 0 : break;
1735 : }
1736 0 : case VSlot_Scale:
1737 : {
1738 0 : dst.scale = getfloat(buf);
1739 0 : if(dst.scale <= 0)
1740 : {
1741 0 : dst.scale = 1;
1742 : }
1743 0 : else if(!delta)
1744 : {
1745 0 : dst.scale = std::clamp(dst.scale, 1/8.0f, 8.0f);
1746 : }
1747 0 : break;
1748 : }
1749 0 : case VSlot_Rotation:
1750 0 : dst.rotation = getint(buf);
1751 0 : if(!delta)
1752 : {
1753 0 : dst.rotation = std::clamp(dst.rotation, 0, 7);
1754 : }
1755 0 : break;
1756 0 : case VSlot_Angle:
1757 : {
1758 0 : dst.angle.x = getfloat(buf);
1759 0 : dst.angle.y = getfloat(buf);
1760 0 : dst.angle.z = getfloat(buf);
1761 0 : break;
1762 : }
1763 0 : case VSlot_Offset:
1764 : {
1765 0 : dst.offset.x() = getint(buf);
1766 0 : dst.offset.y() = getint(buf);
1767 0 : if(!delta)
1768 : {
1769 0 : dst.offset.max(0);
1770 : }
1771 0 : break;
1772 : }
1773 0 : case VSlot_Scroll:
1774 : {
1775 0 : dst.scroll.x = getfloat(buf);
1776 0 : dst.scroll.y = getfloat(buf);
1777 0 : break;
1778 : }
1779 0 : case VSlot_Alpha:
1780 : {
1781 0 : dst.alphafront = std::clamp(getfloat(buf), 0.0f, 1.0f);
1782 0 : dst.alphaback = std::clamp(getfloat(buf), 0.0f, 1.0f);
1783 0 : break;
1784 : }
1785 0 : case VSlot_Color:
1786 : {
1787 0 : dst.colorscale.r() = std::clamp(getfloat(buf), 0.0f, 2.0f);
1788 0 : dst.colorscale.g() = std::clamp(getfloat(buf), 0.0f, 2.0f);
1789 0 : dst.colorscale.b() = std::clamp(getfloat(buf), 0.0f, 2.0f);
1790 0 : break;
1791 : }
1792 0 : case VSlot_Refract:
1793 : {
1794 0 : dst.refractscale = std::clamp(getfloat(buf), 0.0f, 1.0f);
1795 0 : dst.refractcolor.r() = std::clamp(getfloat(buf), 0.0f, 1.0f);
1796 0 : dst.refractcolor.g() = std::clamp(getfloat(buf), 0.0f, 1.0f);
1797 0 : dst.refractcolor.b() = std::clamp(getfloat(buf), 0.0f, 1.0f);
1798 0 : break;
1799 : }
1800 0 : default:
1801 : {
1802 0 : return false;
1803 : }
1804 : }
1805 0 : dst.changed |= 1<<changed;
1806 : }
1807 0 : if(buf.overread())
1808 : {
1809 0 : return false;
1810 : }
1811 0 : return true;
1812 : }
1813 :
1814 0 : VSlot *findvslot(const Slot &slot, const VSlot &src, const VSlot &delta)
1815 : {
1816 0 : for(VSlot *dst = slot.variants; dst; dst = dst->next)
1817 : {
1818 0 : if((!dst->changed || dst->changed == (src.changed | delta.changed)) &&
1819 0 : comparevslot(*dst, src, src.changed & ~delta.changed) &&
1820 0 : comparevslot(*dst, delta, delta.changed))
1821 : {
1822 0 : return dst;
1823 : }
1824 : }
1825 0 : return nullptr;
1826 : }
1827 :
1828 0 : static VSlot *clonevslot(const VSlot &src, const VSlot &delta)
1829 : {
1830 0 : vslots.push_back(new VSlot(src.slot, vslots.size()));
1831 0 : VSlot *dst = vslots.back();
1832 0 : dst->changed = src.changed | delta.changed;
1833 0 : propagatevslot(*dst, src, ((1 << VSlot_Num) - 1) & ~delta.changed);
1834 0 : propagatevslot(*dst, delta, delta.changed, true);
1835 0 : return dst;
1836 : }
1837 :
1838 : VARP(autocompactvslots, 0, 256, 0x10000);
1839 :
1840 0 : VSlot *editvslot(const VSlot &src, const VSlot &delta)
1841 : {
1842 0 : VSlot *exists = findvslot(*src.slot, src, delta);
1843 0 : if(exists)
1844 : {
1845 0 : return exists;
1846 : }
1847 0 : if(vslots.size()>=0x10000)
1848 : {
1849 0 : ::rootworld.compactvslots();
1850 0 : rootworld.allchanged();
1851 0 : if(vslots.size()>=0x10000)
1852 : {
1853 0 : return nullptr;
1854 : }
1855 : }
1856 0 : if(autocompactvslots && ++clonedvslots >= autocompactvslots)
1857 : {
1858 0 : ::rootworld.compactvslots();
1859 0 : rootworld.allchanged();
1860 : }
1861 0 : return clonevslot(src, delta);
1862 : }
1863 :
1864 0 : static void fixinsidefaces(std::array<cube, 8> &c, const ivec &o, int size, int tex)
1865 : {
1866 0 : for(int i = 0; i < 8; ++i)
1867 : {
1868 0 : ivec co(i, o, size);
1869 0 : if(c[i].children)
1870 : {
1871 0 : fixinsidefaces(*(c[i].children), co, size>>1, tex);
1872 : }
1873 : else
1874 : {
1875 0 : for(int j = 0; j < 6; ++j)
1876 : {
1877 0 : if(!visibletris(c[i], j, co, size))
1878 : {
1879 0 : c[i].texture[j] = tex;
1880 : }
1881 : }
1882 : }
1883 : }
1884 0 : }
1885 :
1886 1 : int findslottex(const char *name)
1887 : {
1888 :
1889 : const struct SlotTex
1890 : {
1891 : const char *name;
1892 : int id;
1893 1 : } slottexs[] =
1894 : {
1895 : {"0", Tex_Diffuse},
1896 : {"1", Tex_Unknown},
1897 :
1898 : {"c", Tex_Diffuse},
1899 : {"u", Tex_Unknown},
1900 : {"n", Tex_Normal},
1901 : {"g", Tex_Glow},
1902 : {"s", Tex_Spec},
1903 : {"z", Tex_Depth},
1904 : {"a", Tex_Alpha}
1905 : };
1906 :
1907 10 : for(int i = 0; i < static_cast<int>(sizeof(slottexs)/sizeof(SlotTex)); ++i)
1908 : {
1909 9 : if(!std::strcmp(slottexs[i].name, name))
1910 : {
1911 0 : return slottexs[i].id;
1912 : }
1913 : }
1914 1 : return -1;
1915 : }
1916 :
1917 1 : static void texture(const char *type, const char *name, const int *rot, const int *xoffset, const int *yoffset, const float *scale)
1918 : {
1919 1 : int tnum = findslottex(type), matslot;
1920 1 : if(tnum == Tex_Diffuse)
1921 : {
1922 0 : if(slots.size() >= 0x10000)
1923 : {
1924 0 : return;
1925 : }
1926 0 : slots.push_back(new Slot(slots.size()));
1927 0 : defslot = slots.back();
1928 : }
1929 1 : else if(!std::strcmp(type, "decal"))
1930 : {
1931 0 : if(decalslots.size() >= 0x10000)
1932 : {
1933 0 : return;
1934 : }
1935 0 : tnum = Tex_Diffuse;
1936 0 : decalslots.push_back(new DecalSlot(decalslots.size()));
1937 0 : defslot = decalslots.back();
1938 : }
1939 1 : else if((matslot = findmaterial(type)) >= 0)
1940 : {
1941 0 : tnum = Tex_Diffuse;
1942 0 : defslot = &materialslots[matslot];
1943 0 : defslot->reset();
1944 : }
1945 1 : else if(!defslot)
1946 : {
1947 1 : return;
1948 : }
1949 0 : else if(tnum < 0)
1950 : {
1951 0 : tnum = Tex_Unknown;
1952 : }
1953 0 : Slot &s = *defslot;
1954 0 : s.loaded = false;
1955 0 : s.texmask |= 1<<tnum;
1956 0 : if(s.sts.size()>=8)
1957 : {
1958 0 : conoutf(Console_Warn, "warning: too many textures in %s", s.name());
1959 : }
1960 0 : s.sts.emplace_back();
1961 0 : Slot::Tex &st = s.sts.back();
1962 0 : st.type = tnum;
1963 0 : copystring(st.name, name);
1964 0 : path(st.name);
1965 0 : if(tnum == Tex_Diffuse)
1966 : {
1967 0 : setslotshader(s);
1968 0 : VSlot &vs = s.emptyvslot();
1969 0 : vs.rotation = std::clamp(*rot, 0, 7);
1970 0 : vs.offset = ivec2(*xoffset, *yoffset).max(0);
1971 0 : vs.scale = *scale <= 0 ? 1 : *scale;
1972 0 : propagatevslot(&vs, (1 << VSlot_Num) - 1);
1973 : }
1974 : }
1975 :
1976 1 : void texgrass(const char *name)
1977 : {
1978 1 : if(!defslot)
1979 : {
1980 1 : return;
1981 : }
1982 0 : Slot &s = *defslot;
1983 0 : delete[] s.grass;
1984 0 : s.grass = name[0] ? newstring(makerelpath("media/texture", name)) : nullptr;
1985 : }
1986 :
1987 1 : void texscroll(const float *scrollS, const float *scrollT)
1988 : {
1989 1 : if(!defslot)
1990 : {
1991 1 : return;
1992 : }
1993 0 : Slot &s = *defslot;
1994 0 : s.variants->scroll = vec2(*scrollS/1000.0f, *scrollT/1000.0f);
1995 0 : propagatevslot(s.variants, 1 << VSlot_Scroll);
1996 : }
1997 :
1998 1 : void texoffset_(const int *xoffset, const int *yoffset)
1999 : {
2000 1 : if(!defslot)
2001 : {
2002 1 : return;
2003 : }
2004 0 : Slot &s = *defslot;
2005 0 : s.variants->offset = ivec2(*xoffset, *yoffset).max(0);
2006 0 : propagatevslot(s.variants, 1 << VSlot_Offset);
2007 : }
2008 :
2009 1 : void texrotate_(const int *rot)
2010 : {
2011 1 : if(!defslot)
2012 : {
2013 1 : return;
2014 : }
2015 0 : Slot &s = *defslot;
2016 0 : s.variants->rotation = std::clamp(*rot, 0, 7);
2017 0 : propagatevslot(s.variants, 1 << VSlot_Rotation);
2018 : }
2019 :
2020 1 : void texangle_(const float *a)
2021 : {
2022 1 : if(!defslot)
2023 : {
2024 1 : return;
2025 : }
2026 0 : Slot &s = *defslot;
2027 0 : s.variants->angle = vec(*a, std::sin(*a/RAD), std::cos(*a/RAD));
2028 0 : propagatevslot(s.variants, 1 << VSlot_Angle);
2029 : }
2030 :
2031 1 : void texscale(const float *scale)
2032 : {
2033 1 : if(!defslot)
2034 : {
2035 1 : return;
2036 : }
2037 0 : Slot &s = *defslot;
2038 0 : s.variants->scale = *scale <= 0 ? 1 : *scale;
2039 0 : propagatevslot(s.variants, 1 << VSlot_Scale);
2040 : }
2041 :
2042 1 : void texalpha(const float *front, const float *back)
2043 : {
2044 1 : if(!defslot)
2045 : {
2046 1 : return;
2047 : }
2048 0 : Slot &s = *defslot;
2049 0 : s.variants->alphafront = std::clamp(*front, 0.0f, 1.0f);
2050 0 : s.variants->alphaback = std::clamp(*back, 0.0f, 1.0f);
2051 0 : propagatevslot(s.variants, 1 << VSlot_Alpha);
2052 : }
2053 :
2054 1 : void texcolor(const float *r, const float *g, const float *b)
2055 : {
2056 1 : if(!defslot)
2057 : {
2058 1 : return;
2059 : }
2060 0 : Slot &s = *defslot;
2061 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));
2062 0 : propagatevslot(s.variants, 1 << VSlot_Color);
2063 : }
2064 :
2065 1 : void texrefract(const float *k, const float *r, const float *g, const float *b)
2066 : {
2067 1 : if(!defslot)
2068 : {
2069 1 : return;
2070 : }
2071 0 : Slot &s = *defslot;
2072 0 : s.variants->refractscale = std::clamp(*k, 0.0f, 1.0f);
2073 0 : if(s.variants->refractscale > 0 && (*r > 0 || *g > 0 || *b > 0))
2074 : {
2075 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));
2076 : }
2077 : else
2078 : {
2079 0 : s.variants->refractcolor = vec(1, 1, 1);
2080 : }
2081 0 : propagatevslot(s.variants, 1 << VSlot_Refract);
2082 : }
2083 :
2084 1 : void texsmooth(const int *id, const int *angle)
2085 : {
2086 1 : if(!defslot)
2087 : {
2088 1 : return;
2089 : }
2090 0 : Slot &s = *defslot;
2091 0 : s.smooth = smoothangle(*id, *angle);
2092 : }
2093 :
2094 1 : void decaldepth(const float *depth, const float *fade)
2095 : {
2096 1 : if(!defslot || defslot->type() != Slot::SlotType_Decal)
2097 : {
2098 1 : return;
2099 : }
2100 0 : DecalSlot &s = *static_cast<DecalSlot *>(defslot);
2101 0 : s.depth = std::clamp(*depth, 1e-3f, 1e3f);
2102 0 : s.fade = std::clamp(*fade, 0.0f, s.depth);
2103 : }
2104 :
2105 0 : int DecalSlot::cancombine(int type) const
2106 : {
2107 0 : switch(type)
2108 : {
2109 0 : case Tex_Glow:
2110 : {
2111 0 : return Tex_Spec;
2112 : }
2113 0 : case Tex_Normal:
2114 : {
2115 0 : return texmask&(1 << Tex_Depth) ? Tex_Depth : (texmask & (1 << Tex_Glow) ? -1 : Tex_Spec);
2116 : }
2117 0 : default:
2118 : {
2119 0 : return -1;
2120 : }
2121 : }
2122 : }
2123 :
2124 0 : bool DecalSlot::shouldpremul(int type) const
2125 : {
2126 0 : switch(type)
2127 : {
2128 0 : case Tex_Diffuse:
2129 : {
2130 0 : return true;
2131 : }
2132 0 : default:
2133 : {
2134 0 : return false;
2135 : }
2136 : }
2137 : }
2138 :
2139 0 : static void addname(std::vector<char> &key, Slot &slot, Slot::Tex &t, bool combined = false, const char *prefix = nullptr)
2140 : {
2141 0 : if(combined)
2142 : {
2143 0 : key.push_back('&');
2144 : }
2145 0 : if(prefix)
2146 : {
2147 0 : while(*prefix)
2148 : {
2149 0 : key.push_back(*prefix++);
2150 : }
2151 : }
2152 0 : DEF_FORMAT_STRING(tname, "%s/%s", slot.texturedir(), t.name);
2153 0 : for(const char *s = path(tname); *s; key.push_back(*s++))
2154 : {
2155 : //(empty body)
2156 : }
2157 0 : }
2158 :
2159 : // Slot object
2160 :
2161 0 : VSlot &Slot::emptyvslot()
2162 : {
2163 0 : return *::emptyvslot(*this);
2164 : }
2165 :
2166 0 : int Slot::findtextype(int type, int last) const
2167 : {
2168 0 : for(size_t i = last+1; i<sts.size(); i++)
2169 : {
2170 0 : if((type&(1<<sts[i].type)) && sts[i].combined<0)
2171 : {
2172 0 : return i;
2173 : }
2174 : }
2175 0 : return -1;
2176 : }
2177 :
2178 0 : int Slot::cancombine(int type) const
2179 : {
2180 0 : switch(type)
2181 : {
2182 0 : case Tex_Diffuse:
2183 : {
2184 0 : return texmask&((1 << Tex_Spec) | (1 << Tex_Normal)) ? Tex_Spec : Tex_Alpha;
2185 : }
2186 0 : case Tex_Normal:
2187 : {
2188 0 : return texmask&(1 << Tex_Depth) ? Tex_Depth : Tex_Alpha;
2189 : }
2190 0 : default:
2191 : {
2192 0 : return -1;
2193 : }
2194 : }
2195 : }
2196 :
2197 0 : void Slot::load(int index, Slot::Tex &t)
2198 : {
2199 0 : std::vector<char> key;
2200 0 : addname(key, *this, t, false, shouldpremul(t.type) ? "<premul>" : nullptr);
2201 0 : Slot::Tex *combine = nullptr;
2202 0 : for(size_t i = 0; i < sts.size(); i++)
2203 : {
2204 0 : Slot::Tex &c = sts[i];
2205 0 : if(c.combined == index)
2206 : {
2207 0 : combine = &c;
2208 0 : addname(key, *this, c, true);
2209 0 : break;
2210 : }
2211 : }
2212 0 : key.push_back('\0');
2213 0 : auto itr = textures.find(key.data());
2214 0 : if(itr != textures.end())
2215 : {
2216 0 : t.t = &(*itr).second;
2217 0 : return;
2218 : }
2219 0 : t.t = nullptr;
2220 0 : int compress = 0,
2221 0 : wrap = 0;
2222 0 : ImageData ts;
2223 0 : if(!ts.texturedata(*this, t, true, &compress, &wrap))
2224 : {
2225 0 : t.t = notexture;
2226 0 : return;
2227 : }
2228 0 : if(!ts.compressed)
2229 : {
2230 0 : switch(t.type)
2231 : {
2232 0 : case Tex_Spec:
2233 : {
2234 0 : if(ts.depth() > 1)
2235 : {
2236 0 : ts.collapsespec();
2237 : }
2238 0 : break;
2239 : }
2240 0 : case Tex_Glow:
2241 : case Tex_Diffuse:
2242 : case Tex_Normal:
2243 0 : if(combine)
2244 : {
2245 0 : ImageData cs;
2246 0 : if(cs.texturedata(*this, *combine))
2247 : {
2248 0 : if(cs.width()!=ts.width() || cs.height()!=ts.height())
2249 : {
2250 0 : cs.scaleimage(ts.width(), ts.height());
2251 : }
2252 0 : switch(combine->type)
2253 : {
2254 0 : case Tex_Spec:
2255 : {
2256 0 : ts.mergespec(cs);
2257 0 : break;
2258 : }
2259 0 : case Tex_Depth:
2260 : {
2261 0 : ts.mergedepth(cs);
2262 0 : break;
2263 : }
2264 0 : case Tex_Alpha:
2265 : {
2266 0 : ts.mergealpha(cs);
2267 0 : break;
2268 : }
2269 : }
2270 : }
2271 0 : }
2272 0 : if(ts.depth() < 3)
2273 : {
2274 0 : ts.swizzleimage();
2275 : }
2276 0 : break;
2277 : }
2278 : }
2279 0 : if(!ts.compressed && shouldpremul(t.type))
2280 : {
2281 0 : ts.texpremul();
2282 : }
2283 0 : t.t = newtexture(nullptr, key.data(), ts, wrap, true, true, true, compress);
2284 0 : }
2285 :
2286 0 : void Slot::load()
2287 : {
2288 0 : linkslotshader(*this);
2289 0 : for(size_t i = 0; i < sts.size(); i++)
2290 : {
2291 0 : Slot::Tex &t = sts[i];
2292 0 : if(t.combined >= 0)
2293 : {
2294 0 : continue;
2295 : }
2296 0 : int combine = cancombine(t.type);
2297 0 : if(combine >= 0 && (combine = findtextype(1<<combine)) >= 0)
2298 : {
2299 0 : Slot::Tex &c = sts[combine];
2300 0 : c.combined = i;
2301 : }
2302 : }
2303 0 : for(size_t i = 0; i < sts.size(); i++)
2304 : {
2305 0 : Slot::Tex &t = sts[i];
2306 0 : if(t.combined >= 0)
2307 : {
2308 0 : continue;
2309 : }
2310 0 : switch(t.type)
2311 : {
2312 : default:
2313 : {
2314 0 : load(i, t);
2315 0 : break;
2316 : }
2317 : }
2318 : }
2319 0 : loaded = true;
2320 0 : }
2321 :
2322 : // VSlot
2323 :
2324 40 : void VSlot::addvariant(Slot *slot)
2325 : {
2326 40 : if(!slot->variants)
2327 : {
2328 40 : slot->variants = this;
2329 : }
2330 : else
2331 : {
2332 0 : VSlot *prev = slot->variants;
2333 0 : while(prev->next)
2334 : {
2335 0 : prev = prev->next;
2336 : }
2337 0 : prev->next = this;
2338 : }
2339 40 : }
2340 :
2341 0 : bool VSlot::isdynamic() const
2342 : {
2343 0 : return !scroll.iszero() || slot->shader->isdynamic();
2344 : }
2345 :
2346 : // end of Slot/VSlot
2347 0 : MatSlot &lookupmaterialslot(int index, bool load)
2348 : {
2349 0 : MatSlot &s = materialslots[index];
2350 0 : if(load && !s.linked)
2351 : {
2352 0 : if(!s.loaded)
2353 : {
2354 0 : s.load();
2355 : }
2356 0 : linkvslotshader(s);
2357 0 : s.linked = true;
2358 : }
2359 0 : return s;
2360 : }
2361 :
2362 0 : Slot &lookupslot(int index, bool load)
2363 : {
2364 0 : Slot &s = (static_cast<long>(slots.size()) > index) ? *slots[index] : ((slots.size() > Default_Geom) ? *slots[Default_Geom] : dummyslot);
2365 0 : if(!s.loaded && load)
2366 : {
2367 0 : s.load();
2368 : }
2369 0 : return s;
2370 : }
2371 :
2372 0 : VSlot &lookupvslot(int index, bool load)
2373 : {
2374 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);
2375 0 : if(load && !s.linked)
2376 : {
2377 0 : if(!s.slot->loaded)
2378 : {
2379 0 : s.slot->load();
2380 : }
2381 0 : linkvslotshader(s);
2382 0 : s.linked = true;
2383 : }
2384 0 : return s;
2385 : }
2386 :
2387 0 : DecalSlot &lookupdecalslot(int index, bool load)
2388 : {
2389 0 : DecalSlot &s = (static_cast<int>(decalslots.size()) > index) ? *decalslots[index] : dummydecalslot;
2390 0 : if(load && !s.linked)
2391 : {
2392 0 : if(!s.loaded)
2393 : {
2394 0 : s.load();
2395 : }
2396 0 : linkvslotshader(s);
2397 0 : s.linked = true;
2398 : }
2399 0 : return s;
2400 : }
2401 :
2402 0 : void linkslotshaders()
2403 : {
2404 0 : for(Slot * const &i : slots)
2405 : {
2406 0 : if(i->loaded)
2407 : {
2408 0 : linkslotshader(*i);
2409 : }
2410 : }
2411 0 : for(VSlot * const &i : vslots)
2412 : {
2413 0 : if(i->linked)
2414 : {
2415 0 : linkvslotshader(*i);
2416 : }
2417 : }
2418 0 : for(size_t i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
2419 : {
2420 0 : if(materialslots[i].loaded)
2421 : {
2422 0 : linkslotshader(materialslots[i]);
2423 0 : linkvslotshader(materialslots[i]);
2424 : }
2425 : }
2426 0 : for(DecalSlot * const &i : decalslots)
2427 : {
2428 0 : if(i->loaded)
2429 : {
2430 0 : linkslotshader(*i);
2431 0 : linkvslotshader(*i);
2432 : }
2433 : }
2434 0 : }
2435 :
2436 0 : static void blitthumbnail(ImageData &d, ImageData &s, int x, int y)
2437 : {
2438 0 : d.forcergbimage();
2439 0 : s.forcergbimage();
2440 0 : uchar *dstrow = &d.data[d.pitch*y + d.depth()*x],
2441 0 : *srcrow = s.data;
2442 0 : for(int y = 0; y < s.height(); ++y)
2443 : {
2444 0 : for(uchar *dst = dstrow, *src = srcrow, *end = &srcrow[s.width()*s.depth()]; src < end; dst += d.depth(), src += s.depth())
2445 : {
2446 0 : for(int k = 0; k < 3; ++k)
2447 : {
2448 0 : dst[k] = src[k];
2449 : }
2450 : }
2451 0 : dstrow += d.pitch;
2452 0 : srcrow += s.pitch;
2453 : }
2454 0 : }
2455 :
2456 1 : Texture *Slot::loadthumbnail()
2457 : {
2458 1 : if(thumbnail)
2459 : {
2460 0 : return thumbnail;
2461 : }
2462 1 : if(!variants)
2463 : {
2464 1 : thumbnail = notexture;
2465 1 : return thumbnail;
2466 : }
2467 0 : VSlot &vslot = *variants;
2468 0 : linkslotshader(*this, false);
2469 0 : linkvslotshader(vslot, false);
2470 0 : std::vector<char> name;
2471 0 : if(vslot.colorscale == vec(1, 1, 1))
2472 : {
2473 0 : addname(name, *this, sts[0], false, "<thumbnail>");
2474 : }
2475 : else
2476 : {
2477 0 : DEF_FORMAT_STRING(prefix, "<thumbnail:%.2f/%.2f/%.2f>", vslot.colorscale.x, vslot.colorscale.y, vslot.colorscale.z);
2478 0 : addname(name, *this, sts[0], false, prefix);
2479 : }
2480 0 : int glow = -1;
2481 0 : if(texmask&(1 << Tex_Glow))
2482 : {
2483 0 : for(size_t j = 0; j < sts.size(); j++)
2484 : {
2485 0 : if(sts[j].type == Tex_Glow)
2486 : {
2487 0 : glow = j;
2488 0 : break;
2489 : }
2490 : }
2491 0 : if(glow >= 0)
2492 : {
2493 0 : DEF_FORMAT_STRING(prefix, "<glow:%.2f/%.2f/%.2f>", vslot.glowcolor.x, vslot.glowcolor.y, vslot.glowcolor.z);
2494 0 : addname(name, *this, sts[glow], true, prefix);
2495 : }
2496 : }
2497 0 : name.push_back('\0');
2498 0 : auto itr = textures.find(path(name.data()));
2499 0 : if(itr != textures.end())
2500 : {
2501 0 : thumbnail = &(*itr).second;
2502 0 : return &(*itr).second;
2503 : }
2504 : else
2505 : {
2506 0 : auto insert = textures.insert( { std::string(name.data()), Texture() } ).first;
2507 0 : Texture *t = &(*insert).second;
2508 0 : ImageData s, g, l, d;
2509 0 : s.texturedata(*this, sts[0], false);
2510 0 : if(glow >= 0)
2511 : {
2512 0 : g.texturedata(*this, sts[glow], false);
2513 : }
2514 0 : if(!s.data)
2515 : {
2516 0 : t = thumbnail = notexture;
2517 : }
2518 : else
2519 : {
2520 0 : if(vslot.colorscale != vec(1, 1, 1))
2521 : {
2522 0 : s.texmad(vslot.colorscale, vec(0, 0, 0));
2523 : }
2524 0 : int xs = s.width(),
2525 0 : ys = s.height();
2526 0 : if(s.width() > 128 || s.height() > 128)
2527 : {
2528 0 : s.scaleimage(std::min(s.width(), 128), std::min(s.height(), 128));
2529 : }
2530 0 : if(g.data)
2531 : {
2532 0 : if(g.width() != s.width() || g.height() != s.height())
2533 : {
2534 0 : g.scaleimage(s.width(), s.height());
2535 : }
2536 0 : s.addglow(g, vslot.glowcolor);
2537 : }
2538 0 : if(l.data)
2539 : {
2540 0 : if(l.width() != s.width()/2 || l.height() != s.height()/2)
2541 : {
2542 0 : l.scaleimage(s.width()/2, s.height()/2);
2543 : }
2544 0 : blitthumbnail(s, l, s.width()-l.width(), s.height()-l.height());
2545 : }
2546 0 : if(d.data)
2547 : {
2548 0 : if(vslot.colorscale != vec(1, 1, 1))
2549 : {
2550 0 : d.texmad(vslot.colorscale, vec(0, 0, 0));
2551 : }
2552 0 : if(d.width() != s.width()/2 || d.height() != s.height()/2)
2553 : {
2554 0 : d.scaleimage(s.width()/2, s.height()/2);
2555 : }
2556 0 : blitthumbnail(s, d, 0, 0);
2557 : }
2558 0 : if(s.depth() < 3)
2559 : {
2560 0 : s.forcergbimage();
2561 : }
2562 0 : t = newtexture(nullptr, name.data(), s, 0, false, false, true);
2563 0 : t->xs = xs;
2564 0 : t->ys = ys;
2565 0 : thumbnail = t;
2566 : }
2567 0 : return t;
2568 0 : }
2569 0 : }
2570 :
2571 : // environment mapped reflections
2572 :
2573 0 : void cleanuptextures()
2574 : {
2575 0 : for(Slot * const &i : slots)
2576 : {
2577 0 : i->cleanup();
2578 : }
2579 0 : for(VSlot * const &i : vslots)
2580 : {
2581 0 : i->cleanup();
2582 : }
2583 0 : for(size_t i = 0; i < (MatFlag_Volume|MatFlag_Index)+1; ++i)
2584 : {
2585 0 : materialslots[i].cleanup();
2586 : }
2587 0 : for(DecalSlot * const &i : decalslots)
2588 : {
2589 0 : i->cleanup();
2590 : }
2591 0 : for(auto itr = textures.begin(); itr != textures.end(); ++itr)
2592 : {
2593 0 : Texture &t = (*itr).second;
2594 0 : delete[] t.alphamask;
2595 0 : t.alphamask = nullptr;
2596 0 : if(t.id)
2597 : {
2598 0 : glDeleteTextures(1, &t.id);
2599 0 : t.id = 0;
2600 : }
2601 0 : if(t.type&Texture::TRANSIENT)
2602 : {
2603 0 : itr = textures.erase(itr);
2604 : }
2605 : }
2606 0 : }
2607 :
2608 0 : bool reloadtexture(const char *name)
2609 : {
2610 0 : auto itr = textures.find(path(std::string(name)));
2611 0 : if(itr != textures.end())
2612 : {
2613 0 : return (*itr).second.reload();
2614 : }
2615 0 : return true;
2616 : }
2617 :
2618 0 : bool Texture::reload()
2619 : {
2620 0 : if(id)
2621 : {
2622 0 : return true;
2623 : }
2624 0 : switch(type&TYPE)
2625 : {
2626 0 : case IMAGE:
2627 : {
2628 0 : int compress = 0;
2629 0 : ImageData s;
2630 0 : if(!s.texturedata(name, true, &compress) || !newtexture(this, nullptr, s, clamp, mipmap, false, false, compress))
2631 : {
2632 0 : return false;
2633 : }
2634 0 : break;
2635 0 : }
2636 : }
2637 0 : return true;
2638 : }
2639 :
2640 1 : void reloadtex(const char *name)
2641 : {
2642 1 : auto itr = textures.find(path(std::string(name)));
2643 1 : if(itr == textures.end())
2644 : {
2645 1 : conoutf(Console_Error, "texture %s is not loaded", name);
2646 1 : return;
2647 : }
2648 0 : Texture *t = &(*itr).second;
2649 0 : if(t->type&Texture::TRANSIENT)
2650 : {
2651 0 : conoutf(Console_Error, "can't reload transient texture %s", name);
2652 0 : return;
2653 : }
2654 0 : delete[] t->alphamask;
2655 0 : t->alphamask = nullptr;
2656 0 : Texture oldtex = *t;
2657 0 : t->id = 0;
2658 0 : if(!t->reload())
2659 : {
2660 0 : if(t->id)
2661 : {
2662 0 : glDeleteTextures(1, &t->id);
2663 : }
2664 0 : *t = oldtex;
2665 0 : conoutf(Console_Error, "failed to reload texture %s", name);
2666 : }
2667 : }
2668 :
2669 0 : void reloadtextures()
2670 : {
2671 0 : int reloaded = 0;
2672 0 : for(auto &[k, t] : textures)
2673 : {
2674 0 : loadprogress = static_cast<float>(++reloaded)/textures.size();
2675 0 : t.reload();
2676 : }
2677 0 : loadprogress = 0;
2678 0 : }
2679 :
2680 0 : static void writepngchunk(stream *f, const char *type, const uchar *data = nullptr, uint len = 0)
2681 : {
2682 0 : f->putbig<uint>(len);
2683 0 : f->write(type, 4);
2684 0 : f->write(data, len);
2685 :
2686 0 : uint crc = crc32(0, Z_NULL, 0);
2687 0 : crc = crc32(crc, reinterpret_cast<const Bytef *>(type), 4);
2688 0 : if(data)
2689 : {
2690 0 : crc = crc32(crc, data, len);
2691 : }
2692 0 : f->putbig<uint>(crc);
2693 0 : }
2694 :
2695 : static VARP(compresspng, 0, 9, 9);
2696 :
2697 0 : static void flushzip(z_stream &z, uchar *buf, const uint &buflen, uint &len, stream *f, uint &crc)
2698 : {
2699 0 : int flush = buflen- z.avail_out;
2700 0 : crc = crc32(crc, buf, flush);
2701 0 : len += flush;
2702 0 : f->write(buf, flush);
2703 0 : z.next_out = static_cast<Bytef *>(buf);
2704 0 : z.avail_out = buflen;
2705 0 : }
2706 :
2707 1 : static void savepng(const char *filename, const ImageData &image, bool flip)
2708 : {
2709 1 : if(!image.height() || !image.width())
2710 : {
2711 1 : conoutf(Console_Error, "cannot save 0-size png");
2712 1 : return;
2713 : }
2714 0 : uchar ctype = 0;
2715 0 : switch(image.depth())
2716 : {
2717 0 : case 1:
2718 : {
2719 0 : ctype = 0;
2720 0 : break;
2721 : }
2722 0 : case 2:
2723 : {
2724 0 : ctype = 4;
2725 0 : break;
2726 : }
2727 0 : case 3:
2728 : {
2729 0 : ctype = 2;
2730 0 : break;
2731 : }
2732 0 : case 4:
2733 : {
2734 0 : ctype = 6;
2735 0 : break;
2736 : }
2737 0 : default:
2738 : {
2739 0 : conoutf(Console_Error, "failed saving png to %s", filename);
2740 0 : return;
2741 : }
2742 : }
2743 0 : stream *f = openfile(filename, "wb");
2744 0 : if(!f)
2745 : {
2746 0 : conoutf(Console_Error, "could not write to %s", filename);
2747 0 : return;
2748 : }
2749 0 : std::array<uchar, 8> signature = {{ 137, 80, 78, 71, 13, 10, 26, 10 }};
2750 0 : f->write(signature.data(), signature.size());
2751 : struct PngIHdr
2752 : {
2753 : uint width,
2754 : height;
2755 : uchar bitdepth,
2756 : colortype,
2757 : compress,
2758 : filter,
2759 : interlace;
2760 : };
2761 0 : PngIHdr ihdr =
2762 : {
2763 0 : static_cast<uint>(endianswap(image.width())),
2764 0 : static_cast<uint>(endianswap(image.height())),
2765 : 8,
2766 : ctype,
2767 : 0,
2768 : 0,
2769 : 0
2770 0 : };
2771 0 : writepngchunk(f, "IHDR", reinterpret_cast<uchar *>(&ihdr), 13);
2772 0 : stream::offset idat = f->tell();
2773 0 : uint len = 0;
2774 0 : f->write("\0\0\0\0IDAT", 8);
2775 0 : uint crc = crc32(0, Z_NULL, 0);
2776 0 : crc = crc32(crc, reinterpret_cast<const Bytef *>("IDAT"), 4);
2777 : z_stream z;
2778 0 : z.zalloc = nullptr;
2779 0 : z.zfree = nullptr;
2780 0 : z.opaque = nullptr;
2781 0 : if(deflateInit(&z, compresspng) != Z_OK)
2782 : {
2783 0 : goto error; //goto is beneath FLUSHZ macro
2784 : }
2785 : std::array<uchar, 1<<12> buf;
2786 0 : z.next_out = static_cast<Bytef *>(buf.data());
2787 0 : z.avail_out = buf.size();
2788 0 : for(int i = 0; i < image.height(); ++i)
2789 : {
2790 0 : uchar filter = 0;
2791 0 : for(int j = 0; j < 2; ++j)
2792 : {
2793 0 : z.next_in = j ? static_cast<Bytef *>(image.data) + (flip ? image.height()-i-1 : i)*image.pitch : static_cast<Bytef *>(&filter);
2794 0 : z.avail_in = j ? image.width()*image.depth() : 1;
2795 0 : while(z.avail_in > 0)
2796 : {
2797 0 : if(deflate(&z, Z_NO_FLUSH) != Z_OK)
2798 : {
2799 0 : goto cleanuperror; //goto is beneath FLUSHZ macro
2800 : }
2801 0 : flushzip(z, buf.data(), buf.size(), len, f, crc);
2802 : }
2803 : }
2804 : }
2805 :
2806 : for(;;)
2807 : {
2808 0 : int err = deflate(&z, Z_FINISH);
2809 0 : if(err != Z_OK && err != Z_STREAM_END)
2810 : {
2811 0 : goto cleanuperror;
2812 : }
2813 0 : flushzip(z, buf.data(), buf.size(), len, f, crc);
2814 0 : if(err == Z_STREAM_END)
2815 : {
2816 0 : break;
2817 : }
2818 0 : }
2819 0 : deflateEnd(&z);
2820 :
2821 0 : f->seek(idat, SEEK_SET);
2822 0 : f->putbig<uint>(len);
2823 0 : f->seek(0, SEEK_END);
2824 0 : f->putbig<uint>(crc);
2825 0 : writepngchunk(f, "IEND");
2826 0 : delete f;
2827 0 : return;
2828 :
2829 0 : cleanuperror:
2830 0 : deflateEnd(&z);
2831 :
2832 0 : error:
2833 0 : delete f;
2834 0 : conoutf(Console_Error, "failed saving png to %s", filename);
2835 : }
2836 :
2837 : static SVARP(screenshotdir, "screenshot");
2838 :
2839 1 : void screenshot(const char *filename)
2840 : {
2841 : static string buf;
2842 1 : int dirlen = 0;
2843 1 : copystring(buf, screenshotdir);
2844 1 : if(screenshotdir[0])
2845 : {
2846 1 : dirlen = std::strlen(buf);
2847 1 : if(buf[dirlen] != '/' && buf[dirlen] != '\\' && dirlen+1 < static_cast<int>(sizeof(buf)))
2848 : {
2849 1 : buf[dirlen++] = '/';
2850 1 : buf[dirlen] = '\0';
2851 : }
2852 1 : const char *dir = findfile(buf, "w");
2853 1 : if(!fileexists(dir, "w"))
2854 : {
2855 1 : createdir(dir);
2856 : }
2857 : }
2858 1 : if(filename[0])
2859 : {
2860 0 : concatstring(buf, filename);
2861 : }
2862 : else
2863 : {
2864 : string sstime;
2865 1 : time_t t = std::time(nullptr);
2866 1 : size_t len = std::strftime(sstime, sizeof(sstime), "%Y-%m-%d_%H.%M.%S.png", std::localtime(&t));
2867 1 : sstime[std::min(len, sizeof(sstime)-1)] = '\0';
2868 1 : concatstring(buf, sstime);
2869 24 : for(char *s = &buf[dirlen]; *s; s++)
2870 : {
2871 23 : if(iscubespace(*s) || *s == '/' || *s == '\\')
2872 : {
2873 0 : *s = '-';
2874 : }
2875 : }
2876 : }
2877 :
2878 1 : ImageData image(hudw(), hudh(), 3);
2879 1 : glPixelStorei(GL_PACK_ALIGNMENT, texalign(hudw(), 3));
2880 1 : glReadPixels(0, 0, hudw(), hudh(), GL_RGB, GL_UNSIGNED_BYTE, image.data);
2881 1 : savepng(path(buf), image, true);
2882 1 : }
2883 :
2884 : //used in libprimis api, avoids having to provide entire Shader iface
2885 0 : void setldrnotexture()
2886 : {
2887 0 : ldrnotextureshader->set();
2888 0 : }
2889 :
2890 1 : void inittexturecmds()
2891 : {
2892 1 : addcommand("texturereset", reinterpret_cast<identfun>(texturereset), "i", Id_Command);
2893 1 : addcommand("materialreset", reinterpret_cast<identfun>(materialreset), "", Id_Command);
2894 1 : addcommand("decalreset", reinterpret_cast<identfun>(decalreset), "i", Id_Command);
2895 1 : addcommand("compactvslots", reinterpret_cast<identfun>(+[](const int *cull)
2896 : {
2897 1 : multiplayerwarn();
2898 1 : rootworld.compactvslots(*cull!=0);
2899 1 : rootworld.allchanged();
2900 1 : }), "i", Id_Command);
2901 1 : addcommand("texture", reinterpret_cast<identfun>(texture), "ssiiif", Id_Command);
2902 1 : addcommand("texgrass", reinterpret_cast<identfun>(texgrass), "s", Id_Command);
2903 1 : addcommand("texscroll", reinterpret_cast<identfun>(texscroll), "ff", Id_Command);
2904 1 : addcommand("texoffset", reinterpret_cast<identfun>(texoffset_), "ii", Id_Command);
2905 1 : addcommand("texrotate", reinterpret_cast<identfun>(texrotate_), "i", Id_Command);
2906 1 : addcommand("texangle", reinterpret_cast<identfun>(texangle_), "f", Id_Command);
2907 1 : addcommand("texscale", reinterpret_cast<identfun>(texscale), "f", Id_Command);
2908 1 : addcommand("texalpha", reinterpret_cast<identfun>(texalpha), "ff", Id_Command);
2909 1 : addcommand("texcolor", reinterpret_cast<identfun>(texcolor), "fff", Id_Command);
2910 1 : addcommand("texrefract", reinterpret_cast<identfun>(texrefract), "ffff", Id_Command);
2911 1 : addcommand("texsmooth", reinterpret_cast<identfun>(texsmooth), "ib", Id_Command);
2912 1 : addcommand("decaldepth", reinterpret_cast<identfun>(decaldepth), "ff", Id_Command);
2913 1 : addcommand("reloadtex", reinterpret_cast<identfun>(reloadtex), "s", Id_Command);
2914 1 : addcommand("screenshot", reinterpret_cast<identfun>(screenshot), "s", Id_Command);
2915 1 : }
2916 :
|