1 #include "build.h"
2
3 #if USE_POLYMOST && USE_OPENGL
4
5 #include "baselayer.h"
6 #include "glbuild.h"
7 #include "kplib.h"
8 #include "cache1d.h"
9 #include "pragmas.h"
10 #include "crc32.h"
11 #include "engine_priv.h"
12 #include "polymost_priv.h"
13 #include "hightile_priv.h"
14 #include "polymosttex_priv.h"
15 #include "polymosttexcache.h"
16 #include "polymosttexcompress.h"
17
18 /** a texture hash entry */
19 struct PTHash_typ {
20 struct PTHash_typ *next;
21 PTHead head;
22 struct PTHash_typ *deferto; // if pt_findpthash can't find an exact match for a set of
23 // parameters, it creates a header and defers it to another
24 // entry that stands in its place
25 int primecnt; // a count of how many times the texture is touched when priming
26 };
27 typedef struct PTHash_typ PTHash;
28
29 /** a texture manager hash entry */
30 struct PTMHash_typ {
31 struct PTMHash_typ *next;
32 PTMHead head;
33 unsigned int idcrc; // crc32 of the id below for quick comparisons
34 PTMIdent id;
35 };
36 typedef struct PTMHash_typ PTMHash;
37
38 /** an iterator for walking the hash */
39 struct PTIter_typ {
40 int i;
41 PTHash *pth;
42
43 // criteria for doing selective matching
44 int match;
45 int picnum;
46 int palnum;
47 unsigned short flagsmask;
48 unsigned short flags;
49 };
50
51 /** a convenient structure for passing around texture data that is being baked */
52 struct PTTexture_typ {
53 coltype * pic;
54 GLsizei sizx, sizy; // padded size
55 GLsizei tsizx, tsizy; // true size
56 GLenum rawfmt; // raw format of the data (GL_RGBA, GL_BGRA)
57 int hasalpha;
58 };
59 typedef struct PTTexture_typ PTTexture;
60
61 static int primecnt = 0; // expected number of textures to load during priming
62 static int primedone = 0; // running total of how many textures have been primed
63 static int primepos = 0; // the position in pthashhead where we are up to in priming
64
65 int polymosttexverbosity = 1; // 0 = none, 1 = errors, 2 = all
66 int polymosttexfullbright = 256; // first index of the fullbright palette entries
67
68 #define PTHASHHEADSIZ 4096
69 static PTHash * pthashhead[PTHASHHEADSIZ]; // will be initialised 0 by .bss segment
70
71 #define PTMHASHHEADSIZ 4096
72 static PTMHash * ptmhashhead[PTMHASHHEADSIZ]; // will be initialised 0 by .bss segment
73
74 static const char *compressfourcc[] = {
75 "NONE",
76 "DXT1",
77 "DXT5",
78 "ETC1",
79 };
80
81 static void ptm_fixtransparency(PTTexture * tex, int clamped);
82 static void ptm_applyeffects(PTTexture * tex, int effects);
83 static void ptm_mipscale(PTTexture * tex);
84 static void ptm_uploadtexture(PTMHead * ptm, unsigned short flags, PTTexture * tex, PTCacheTile * tdef);
85
86
pt_gethashhead(const int picnum)87 static inline int pt_gethashhead(const int picnum)
88 {
89 return picnum & (PTHASHHEADSIZ-1);
90 }
91
ptm_gethashhead(const unsigned int idcrc)92 static inline int ptm_gethashhead(const unsigned int idcrc)
93 {
94 return idcrc & (PTMHASHHEADSIZ-1);
95 }
96
detect_texture_size()97 static void detect_texture_size()
98 {
99 if (gltexmaxsize <= 0) {
100 GLint siz = glinfo.maxtexsize;
101 if (siz == 0) {
102 gltexmaxsize = 6; // 2^6 = 64 == default GL max texture size
103 } else {
104 gltexmaxsize = 0;
105 for (; siz > 1; siz >>= 1) {
106 gltexmaxsize++;
107 }
108 }
109 }
110 }
111
112
113 /**
114 * Calculates a texture id from the information in a PTHead structure
115 * @param id the PTMIdent to initialise using...
116 * @param pth the PTHead structure
117 */
PTM_InitIdent(PTMIdent * id,PTHead * pth)118 void PTM_InitIdent(PTMIdent *id, PTHead *pth)
119 {
120 memset(id, 0, sizeof(PTMIdent));
121
122 if (!pth) {
123 return;
124 }
125
126 if (pth->flags & PTH_HIGHTILE) {
127 id->type = PTMIDENT_HIGHTILE;
128
129 if (!pth->repldef) {
130 if (polymosttexverbosity >= 1) {
131 buildprintf("PolymostTex: cannot calculate texture id for pth with no repldef\n");
132 }
133 memset(id, 0, 16);
134 return;
135 }
136
137 id->flags = pth->flags & (PTH_CLAMPED | PTH_SKYBOX);
138 id->effects = (pth->palnum != pth->repldef->palnum)
139 ? hictinting[pth->palnum].f : 0;
140 if (pth->flags & PTH_SKYBOX) {
141 // hightile + skybox + picnum ought to cover it
142 id->picnum = pth->picnum;
143 } else {
144 strncpy(id->filename, pth->repldef->filename, BMAX_PATH);
145 }
146 } else {
147 id->type = PTMIDENT_ART;
148 id->flags = pth->flags & (PTH_CLAMPED);
149 id->palnum = pth->palnum;
150 id->picnum = pth->picnum;
151 }
152 }
153
154
155 /**
156 * Returns a PTMHead pointer for the given texture id
157 * @param id the identifier of the texture
158 * @return the PTMHead item, or null if it couldn't be created
159 */
PTM_GetHead(const PTMIdent * id)160 PTMHead * PTM_GetHead(const PTMIdent *id)
161 {
162 PTMHash * ptmh;
163 int i;
164 unsigned int idcrc;
165
166 idcrc = crc32once((unsigned char *)id, sizeof(PTMIdent));
167
168 i = ptm_gethashhead(idcrc);
169 ptmh = ptmhashhead[i];
170
171 while (ptmh) {
172 if (ptmh->idcrc == idcrc &&
173 memcmp(&ptmh->id, id, sizeof(PTMIdent)) == 0) {
174 return &ptmh->head;
175 }
176 ptmh = ptmh->next;
177 }
178
179 ptmh = (PTMHash *) malloc(sizeof(PTMHash));
180 if (ptmh) {
181 memset(ptmh, 0, sizeof(PTMHash));
182
183 ptmh->idcrc = idcrc;
184 memcpy(&ptmh->id, id, sizeof(PTMIdent));
185 ptmh->next = ptmhashhead[i];
186 ptmhashhead[i] = ptmh;
187 }
188
189 return &ptmh->head;
190 }
191
192
193 /**
194 * Loads a texture file into OpenGL from the PolymostTex cache
195 * @param filename the texture filename
196 * @param ptmh the PTMHead structure to receive the texture details
197 * @param flags PTH_* flags to tune the load process
198 * @param effects HICEFFECT_* effects
199 * @return 0 on success, <0 on error
200 */
ptm_loadcachedtexturefile(const char * filename,PTMHead * ptmh,int flags,int effects)201 static int ptm_loadcachedtexturefile(const char* filename, PTMHead* ptmh, int flags, int effects)
202 {
203 int mipmap = 0, i = 0;
204 PTCacheTile * tdef = 0;
205 int compress = PTCOMPRESS_NONE;
206
207 tdef = PTCacheLoadTile(filename, effects, flags & (PTH_CLAMPED));
208 if (!tdef) {
209 return -1;
210 }
211
212 switch (tdef->format) {
213 #if GL_EXT_texture_compression_dxt1 || GL_EXT_texture_compression_s3tc
214 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
215 compress = PTCOMPRESS_DXT1;
216 if (!glinfo.texcomprdxt1) goto incompatible;
217 break;
218 #endif
219 #if GL_EXT_texture_compression_s3tc
220 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
221 compress = PTCOMPRESS_DXT5;
222 if (!glinfo.texcomprdxt5) goto incompatible;
223 break;
224 #endif
225 #if GL_OES_compressed_ETC1_RGB8_texture
226 case GL_ETC1_RGB8_OES:
227 compress = PTCOMPRESS_ETC1;
228 if (!glinfo.texcompretc1) goto incompatible;
229 break;
230 #endif
231 default:
232 incompatible:
233 if (polymosttexverbosity >= 2) {
234 buildprintf("PolymostTex: cached %s (effects %d, flags %d) has incompatible format %s (0x%x)\n",
235 tdef->filename, tdef->effects, tdef->flags,
236 compress ? compressfourcc[compress] : "?",
237 tdef->format);
238 }
239 PTCacheFreeTile(tdef);
240 return -1;
241 }
242
243 if (polymosttexverbosity >= 2) {
244 buildprintf("PolymostTex: loaded %s (effects %d, flags %d, %s) from cache\n",
245 tdef->filename, tdef->effects, tdef->flags, compressfourcc[compress]);
246 }
247
248 if (ptmh->glpic == 0) {
249 glfunc.glGenTextures(1, &ptmh->glpic);
250 }
251 ptmh->tsizx = tdef->tsizx;
252 ptmh->tsizy = tdef->tsizy;
253 ptmh->sizx = tdef->mipmap[0].sizx;
254 ptmh->sizy = tdef->mipmap[0].sizy;
255 ptmh->flags = tdef->flags & PTH_HASALPHA;
256 glfunc.glBindTexture(GL_TEXTURE_2D, ptmh->glpic);
257
258 mipmap = 0;
259 if (! (flags & PTH_NOMIPLEVEL)) {
260 // if we aren't instructed to preserve all mipmap levels,
261 // immediately throw away gltexmiplevel mipmaps
262 mipmap = max(0, gltexmiplevel);
263 }
264 while (tdef->mipmap[mipmap].sizx > (1 << gltexmaxsize) ||
265 tdef->mipmap[mipmap].sizy > (1 << gltexmaxsize)) {
266 // throw away additional mipmaps until the texture fits within
267 // the maximum size permitted by the GL driver
268 mipmap++;
269 }
270
271 for (i = 0; i + mipmap < tdef->nummipmaps; i++) {
272 glfunc.glCompressedTexImage2D(GL_TEXTURE_2D, i,
273 tdef->format,
274 tdef->mipmap[i + mipmap].sizx,
275 tdef->mipmap[i + mipmap].sizy,
276 0, tdef->mipmap[i + mipmap].length,
277 (const GLvoid *) tdef->mipmap[i + mipmap].data);
278 }
279
280 PTCacheFreeTile(tdef);
281
282 return 0;
283 }
284
285
286 /**
287 * Loads a texture file into OpenGL
288 * @param filename the texture filename
289 * @param ptmh the PTMHead structure to receive the texture details
290 * @param flags PTH_* flags to tune the load process
291 * @param effects HICEFFECT_* effects to apply
292 * @return 0 on success, <0 on error
293 */
PTM_LoadTextureFile(const char * filename,PTMHead * ptmh,int flags,int effects)294 int PTM_LoadTextureFile(const char* filename, PTMHead* ptmh, int flags, int effects)
295 {
296 PTTexture tex;
297 int filh, picdatalen;
298 int x, y;
299 char * picdata = 0;
300 PTCacheTile * tdef = 0;
301 int writetocache = 0, iscached = 0;
302
303 if (!(flags & PTH_NOCOMPRESS) && glusetexcache && glusetexcompr) {
304 iscached = PTCacheHasTile(filename, effects, (flags & PTH_CLAMPED));
305
306 // if the texture exists in the cache but the original file is newer,
307 // ignore what's in the cache and overwrite it
308 /*if (iscached && filemtime(filename) > filemtime(cacheitem)) {
309 iscached = 0;
310 }*/
311
312 if (!iscached) {
313 writetocache = 1;
314 }
315 }
316
317 if (iscached) {
318 if (ptm_loadcachedtexturefile(filename, ptmh, flags, effects) == 0) {
319 return 0;
320 }
321 }
322
323 filh = kopen4load((char *) filename, 0);
324 if (filh < 0) {
325 return -1;
326 }
327 picdatalen = kfilelength(filh);
328
329 picdata = (char *) malloc(picdatalen);
330 if (!picdata) {
331 kclose(filh);
332 return -2;
333 }
334
335 if (kread(filh, picdata, picdatalen) != picdatalen) {
336 kclose(filh);
337 return -3;
338 }
339
340 kclose(filh);
341
342 kpgetdim(picdata, picdatalen, (int *) &tex.tsizx, (int *) &tex.tsizy);
343 if (tex.tsizx == 0 || tex.tsizy == 0) {
344 free(picdata);
345 return -4;
346 }
347
348 if (!glinfo.texnpot || writetocache) {
349 for (tex.sizx = 1; tex.sizx < tex.tsizx; tex.sizx += tex.sizx) ;
350 for (tex.sizy = 1; tex.sizy < tex.tsizy; tex.sizy += tex.sizy) ;
351 } else {
352 tex.sizx = tex.tsizx;
353 tex.sizy = tex.tsizy;
354 }
355
356 tex.pic = (coltype *) malloc(tex.sizx * tex.sizy * sizeof(coltype));
357 if (!tex.pic) {
358 return -2;
359 }
360 memset(tex.pic, 0, tex.sizx * tex.sizy * sizeof(coltype));
361
362 if (kprender(picdata, picdatalen, tex.pic, tex.sizx * sizeof(coltype), tex.sizx, tex.sizy, 0, 0)) {
363 free(picdata);
364 free(tex.pic);
365 return -5;
366 }
367
368 free(picdata);
369 picdata = 0;
370
371 ptm_applyeffects(&tex, effects); // updates tex.hasalpha
372
373 ptmh->flags = 0;
374 if (tex.hasalpha) {
375 ptmh->flags |= PTH_HASALPHA;
376 }
377
378 if (! (flags & PTH_CLAMPED) || (flags & PTH_SKYBOX)) { //Duplicate texture pixels (wrapping tricks for non power of 2 texture sizes)
379 if (tex.sizx > tex.tsizx) { //Copy left to right
380 coltype * lptr = tex.pic;
381 for (y = 0; y < tex.tsizy; y++, lptr += tex.sizx) {
382 memcpy(&lptr[tex.tsizx], lptr, (tex.sizx - tex.tsizx) << 2);
383 }
384 }
385 if (tex.sizy > tex.tsizy) { //Copy top to bottom
386 memcpy(&tex.pic[tex.sizx * tex.tsizy], tex.pic, (tex.sizy - tex.tsizy) * tex.sizx << 2);
387 }
388 }
389
390 tex.rawfmt = GL_BGRA;
391 if (!glinfo.bgra) {
392 int j;
393 for (j = tex.sizx * tex.sizy - 1; j >= 0; j--) {
394 swapchar(&tex.pic[j].r, &tex.pic[j].b);
395 }
396 tex.rawfmt = GL_RGBA;
397 }
398
399 ptmh->tsizx = tex.tsizx;
400 ptmh->tsizy = tex.tsizy;
401 ptmh->sizx = tex.sizx;
402 ptmh->sizy = tex.sizy;
403
404 if (writetocache) {
405 int nmips = 0;
406 while (max(1, (tex.sizx >> nmips)) > 1 ||
407 max(1, (tex.sizy >> nmips)) > 1) {
408 nmips++;
409 }
410 nmips++;
411
412 tdef = PTCacheAllocNewTile(nmips);
413 tdef->filename = strdup(filename);
414 tdef->effects = effects;
415 tdef->flags = (flags | ptmh->flags) & (PTH_CLAMPED | PTH_HASALPHA);
416 }
417
418 ptm_uploadtexture(ptmh, flags, &tex, tdef);
419
420 if (writetocache) {
421 if (polymosttexverbosity >= 2) {
422 buildprintf("PolymostTex: writing %s (effects %d, flags %d) to cache\n",
423 tdef->filename, tdef->effects, tdef->flags);
424 }
425 PTCacheWriteTile(tdef);
426 PTCacheFreeTile(tdef);
427 tdef = 0;
428 }
429
430
431 if (tex.pic) {
432 free(tex.pic);
433 tex.pic = 0;
434 }
435
436 return 0;
437 }
438
439 /**
440 * Returns a string describing the error returned by PTM_LoadTextureFile
441 * @param err the error code
442 * @return the error string
443 */
PTM_GetLoadTextureFileErrorString(int err)444 const char * PTM_GetLoadTextureFileErrorString(int err)
445 {
446 switch (err) {
447 case 0:
448 return "no error";
449 case -1:
450 return "not found";
451 case -2:
452 return "out of memory";
453 case -3:
454 return "truncated read";
455 case -4:
456 return "unrecognised format";
457 case -5:
458 return "decode error";
459 default:
460 return "unknown error";
461 }
462 }
463
464
465 /**
466 * Finds the pthash entry for a tile, possibly creating it if one doesn't exist
467 * @param picnum tile number
468 * @param palnum palette number
469 * @param flags PTH_HIGHTILE = try for hightile, PTH_CLAMPED
470 * @param create !0 = create if none found
471 * @return the PTHash item, or null if none was found
472 */
pt_findhash(int picnum,int palnum,unsigned short flags,int create)473 static PTHash * pt_findhash(int picnum, int palnum, unsigned short flags, int create)
474 {
475 int i = pt_gethashhead(picnum);
476 PTHash * pth;
477 PTHash * basepth; // palette 0 in case we find we need it
478
479 unsigned short flagmask = flags & (PTH_HIGHTILE | PTH_CLAMPED | PTH_SKYBOX);
480
481 // first, try and find an existing match for our parameters
482 pth = pthashhead[i];
483 while (pth) {
484 if (pth->head.picnum == picnum &&
485 pth->head.palnum == palnum &&
486 (pth->head.flags & (PTH_HIGHTILE | PTH_CLAMPED | PTH_SKYBOX)) == flagmask
487 ) {
488 while (pth->deferto) {
489 pth = pth->deferto; // find the end of the chain
490 }
491 return pth;
492 }
493
494 pth = pth->next;
495 }
496
497 if (!create) {
498 return 0;
499 } else {
500 // we didn't find one, so we have to create one
501 hicreplctyp * replc = 0;
502
503 if ((flags & PTH_HIGHTILE)) {
504 replc = hicfindsubst(picnum, palnum, (flags & PTH_SKYBOX));
505 }
506
507 pth = (PTHash *) malloc(sizeof(PTHash));
508 if (!pth) {
509 return 0;
510 }
511 memset(pth, 0, sizeof(PTHash));
512
513 pth->next = pthashhead[i];
514 pth->head.picnum = picnum;
515 pth->head.palnum = palnum;
516 pth->head.flags = flagmask;
517 pth->head.repldef = replc;
518
519 pthashhead[i] = pth;
520
521 if (replc && replc->palnum != palnum) {
522 // we were given a substitute by hightile, so
523 if (hictinting[palnum].f & HICEFFECTMASK) {
524 // if effects are defined for the palette we actually want
525 // we DO NOT defer immediately to the substitute. instead
526 // we apply effects to the replacement and treat it as a
527 // distinct texture
528 ;
529 } else {
530 // we defer to the substitute
531 pth->deferto = pt_findhash(picnum, replc->palnum, flags, create);
532 while (pth->deferto) {
533 pth = pth->deferto; // find the end of the chain
534 }
535 }
536 } else if ((flags & PTH_HIGHTILE) && !replc) {
537 // there is no replacement, so defer to ART
538 if (flags & PTH_SKYBOX) {
539 return 0;
540 } else {
541 pth->deferto = pt_findhash(picnum, palnum, (flags & ~PTH_HIGHTILE), create);
542 while (pth->deferto) {
543 pth = pth->deferto; // find the end of the chain
544 }
545 }
546 }
547 }
548
549 return pth;
550 }
551
552 /**
553 * Unloads a texture from memory
554 * @param pth pointer to the pthash of the loaded texture
555 */
pt_unload(PTHash * pth)556 static void pt_unload(PTHash * pth)
557 {
558 int i;
559 for (i = PTHPIC_SIZE - 1; i>=0; i--) {
560 if (pth->head.pic[i] && pth->head.pic[i]->glpic) {
561 glfunc.glDeleteTextures(1, &pth->head.pic[i]->glpic);
562 pth->head.pic[i]->glpic = 0;
563 }
564 }
565 }
566
567 static int pt_load_art(PTHead * pth);
568 static int pt_load_hightile(PTHead * pth);
569 static void pt_load_applyparameters(PTHead * pth);
570
571 /**
572 * Loads a texture into memory from disk
573 * @param pth pointer to the pthash of the texture to load
574 * @return !0 on success
575 */
pt_load(PTHash * pth)576 static int pt_load(PTHash * pth)
577 {
578 if (pth->head.pic[PTHPIC_BASE] &&
579 pth->head.pic[PTHPIC_BASE]->glpic != 0 &&
580 (pth->head.pic[PTHPIC_BASE]->flags & PTH_DIRTY) == 0) {
581 return 1; // loaded
582 }
583
584 if ((pth->head.flags & PTH_HIGHTILE)) {
585 // try and load from a replacement
586 if (pt_load_hightile(&pth->head)) {
587 return 1;
588 }
589
590 // if that failed, get the hash for the ART version and
591 // defer this to there
592 pth->deferto = pt_findhash(
593 pth->head.picnum, pth->head.palnum,
594 (pth->head.flags & ~PTH_HIGHTILE),
595 1);
596 if (!pth->deferto) {
597 return 0;
598 }
599 return pt_load(pth->deferto);
600 }
601
602 if (pt_load_art(&pth->head)) {
603 return 1;
604 }
605
606 // we're SOL
607 return 0;
608 }
609
610
611 /**
612 * Load an ART tile into an OpenGL texture
613 * @param pth the header to populate
614 * @return !0 on success
615 */
pt_load_art(PTHead * pth)616 static int pt_load_art(PTHead * pth)
617 {
618 PTTexture tex, fbtex;
619 coltype * wpptr, * fpptr;
620 int x, y, x2, y2;
621 int dacol;
622 int hasalpha = 0, hasfullbright = 0;
623 PTMIdent id;
624
625 tex.tsizx = tilesizx[pth->picnum];
626 tex.tsizy = tilesizy[pth->picnum];
627 if (!glinfo.texnpot) {
628 for (tex.sizx = 1; tex.sizx < tex.tsizx; tex.sizx += tex.sizx) ;
629 for (tex.sizy = 1; tex.sizy < tex.tsizy; tex.sizy += tex.sizy) ;
630 } else {
631 if ((tex.tsizx | tex.tsizy) == 0) {
632 tex.sizx = tex.sizy = 1;
633 } else {
634 tex.sizx = tex.tsizx;
635 tex.sizy = tex.tsizy;
636 }
637 }
638
639 tex.rawfmt = GL_RGBA;
640
641 memcpy(&fbtex, &tex, sizeof(PTTexture));
642
643 if (!waloff[pth->picnum]) {
644 loadtile(pth->picnum);
645 }
646
647 tex.pic = (coltype *) malloc(tex.sizx * tex.sizy * sizeof(coltype));
648 if (!tex.pic) {
649 return 0;
650 }
651
652 // fullbright is initialised transparent
653 fbtex.pic = (coltype *) malloc(tex.sizx * tex.sizy * sizeof(coltype));
654 if (!fbtex.pic) {
655 free(tex.pic);
656 return 0;
657 }
658 memset(fbtex.pic, 0, tex.sizx * tex.sizy * sizeof(coltype));
659
660 if (!waloff[pth->picnum]) {
661 // Force invalid textures to draw something - an almost purely transparency texture
662 // This allows the Z-buffer to be updated for mirrors (which are invalidated textures)
663 tex.pic[0].r = tex.pic[0].g = tex.pic[0].b = 0; tex.pic[0].a = 1;
664 tex.tsizx = tex.tsizy = 1;
665 hasalpha = 1;
666 } else {
667 for (y = 0; y < tex.sizy; y++) {
668 if (y < tex.tsizy) {
669 y2 = y;
670 } else {
671 y2 = y-tex.tsizy;
672 }
673 wpptr = &tex.pic[y*tex.sizx];
674 fpptr = &fbtex.pic[y*tex.sizx];
675 for (x = 0; x < tex.sizx; x++, wpptr++, fpptr++) {
676 if ((pth->flags & PTH_CLAMPED) && (x >= tex.tsizx || y >= tex.tsizy)) {
677 // Clamp texture
678 wpptr->r = wpptr->g = wpptr->b = wpptr->a = 0;
679 continue;
680 }
681 if (x < tex.tsizx) {
682 x2 = x;
683 } else {
684 // wrap around to fill the repeated region
685 x2 = x-tex.tsizx;
686 }
687 dacol = (int) (*(unsigned char *)(waloff[pth->picnum]+x2*tex.tsizy+y2));
688 if (dacol == 255) {
689 wpptr->a = 0;
690 hasalpha = 1;
691 } else {
692 wpptr->a = 255;
693 dacol = (int) ((unsigned char)palookup[pth->palnum][dacol]);
694 }
695 if (gammabrightness) {
696 wpptr->r = curpalette[dacol].r;
697 wpptr->g = curpalette[dacol].g;
698 wpptr->b = curpalette[dacol].b;
699 } else {
700 wpptr->r = britable[curbrightness][ curpalette[dacol].r ];
701 wpptr->g = britable[curbrightness][ curpalette[dacol].g ];
702 wpptr->b = britable[curbrightness][ curpalette[dacol].b ];
703 }
704
705 if (dacol >= polymosttexfullbright && dacol < 255) {
706 *fpptr = *wpptr;
707 hasfullbright = 1;
708 }
709 }
710 }
711 }
712
713 pth->scalex = 1.0;
714 pth->scaley = 1.0;
715 pth->flags &= ~(PTH_HASALPHA | PTH_SKYBOX);
716 pth->flags |= (PTH_NOCOMPRESS | PTH_NOMIPLEVEL);
717 tex.hasalpha = hasalpha;
718
719 PTM_InitIdent(&id, pth);
720 id.layer = PTHPIC_BASE;
721 pth->pic[PTHPIC_BASE] = PTM_GetHead(&id);
722 pth->pic[PTHPIC_BASE]->tsizx = tex.tsizx;
723 pth->pic[PTHPIC_BASE]->tsizy = tex.tsizy;
724 pth->pic[PTHPIC_BASE]->sizx = tex.sizx;
725 pth->pic[PTHPIC_BASE]->sizy = tex.sizy;
726 ptm_uploadtexture(pth->pic[PTHPIC_BASE], pth->flags, &tex, 0);
727
728 if (hasfullbright) {
729 id.layer = PTHPIC_GLOW;
730 pth->pic[PTHPIC_GLOW] = PTM_GetHead(&id);
731 pth->pic[PTHPIC_GLOW]->tsizx = tex.tsizx;
732 pth->pic[PTHPIC_GLOW]->tsizy = tex.tsizy;
733 pth->pic[PTHPIC_GLOW]->sizx = tex.sizx;
734 pth->pic[PTHPIC_GLOW]->sizy = tex.sizy;
735 fbtex.hasalpha = 1;
736 ptm_uploadtexture(pth->pic[PTHPIC_GLOW], pth->flags, &fbtex, 0);
737 } else {
738 // it might be that after reloading an invalidated texture, the
739 // glow map might not be needed anymore, so release it
740 pth->pic[PTHPIC_GLOW] = 0;//FIXME should really call a disposal function
741 }
742 pt_load_applyparameters(pth);
743
744 if (tex.pic) {
745 free(tex.pic);
746 }
747 if (fbtex.pic) {
748 free(fbtex.pic);
749 }
750
751 return 1;
752 }
753
754 /**
755 * Load a Hightile texture into an OpenGL texture
756 * @param pth the header to populate
757 * @return !0 on success. Success is defined as all faces of a skybox being loaded,
758 * or at least the base texture of a regular replacement.
759 */
pt_load_hightile(PTHead * pth)760 static int pt_load_hightile(PTHead * pth)
761 {
762 const char *filename = 0;
763 int hasalpha = 0;
764 int effects = 0;
765 int err = 0;
766 int texture = 0, loaded[PTHPIC_SIZE] = { 0,0,0,0,0,0, };
767 PTMIdent id;
768
769 if (!pth->repldef) {
770 return 0;
771 } else if ((pth->flags & PTH_SKYBOX) && (pth->repldef->skybox == 0 || pth->repldef->skybox->ignore)) {
772 return 0;
773 } else if (pth->repldef->ignore) {
774 return 0;
775 }
776
777 effects = (pth->palnum != pth->repldef->palnum) ? hictinting[pth->palnum].f : 0;
778
779 pth->flags &= ~(PTH_NOCOMPRESS | PTH_HASALPHA);
780 if (pth->repldef->flags & HIC_NOCOMPRESS) {
781 pth->flags |= PTH_NOCOMPRESS;
782 }
783
784 for (texture = 0; texture < PTHPIC_SIZE; texture++) {
785 if (pth->flags & PTH_SKYBOX) {
786 if (texture >= 6) {
787 texture = PTHPIC_SIZE;
788 continue;
789 }
790 filename = pth->repldef->skybox->face[texture];
791 } else {
792 switch (texture) {
793 case PTHPIC_BASE:
794 filename = pth->repldef->filename;
795 break;
796 default:
797 // future developments may use the other indices
798 texture = PTHPIC_SIZE;
799 continue;
800 }
801 }
802
803 if (!filename) {
804 continue;
805 }
806
807 PTM_InitIdent(&id, pth);
808 id.layer = texture;
809 pth->pic[texture] = PTM_GetHead(&id);
810
811 if ((err = PTM_LoadTextureFile(filename, pth->pic[texture], pth->flags, effects))) {
812 if (polymosttexverbosity >= 1) {
813 const char * errstr = PTM_GetLoadTextureFileErrorString(err);
814 buildprintf("PolymostTex: %s (pic %d pal %d) %s\n",
815 filename, pth->picnum, pth->palnum, errstr);
816 }
817 continue;
818 }
819
820 if (texture == 0) {
821 if (pth->flags & PTH_SKYBOX) {
822 pth->scalex = (float)pth->pic[texture]->tsizx / 64.0;
823 pth->scaley = (float)pth->pic[texture]->tsizy / 64.0;
824 } else {
825 pth->scalex = (float)pth->pic[texture]->tsizx / (float)tilesizx[pth->picnum];
826 pth->scaley = (float)pth->pic[texture]->tsizy / (float)tilesizy[pth->picnum];
827 }
828 }
829
830 loaded[texture] = 1;
831 }
832
833 pt_load_applyparameters(pth);
834
835 if (pth->flags & PTH_SKYBOX) {
836 int i = 0;
837 for (texture = 0; texture < 6; texture++) i += loaded[texture];
838 return (i == 6);
839 } else {
840 return loaded[PTHPIC_BASE];
841 }
842 }
843
844 /**
845 * Applies the global texture filter parameters to the given texture
846 * @param pth the cache header
847 */
pt_load_applyparameters(PTHead * pth)848 static void pt_load_applyparameters(PTHead * pth)
849 {
850 int i;
851
852 for (i = 0; i < PTHPIC_SIZE; i++) {
853 if (pth->pic[i] == 0 || pth->pic[i]->glpic == 0) {
854 continue;
855 }
856
857 glfunc.glBindTexture(GL_TEXTURE_2D, pth->pic[i]->glpic);
858
859 if (gltexfiltermode < 0) {
860 gltexfiltermode = 0;
861 } else if (gltexfiltermode >= (int)numglfiltermodes) {
862 gltexfiltermode = numglfiltermodes-1;
863 }
864 glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glfiltermodes[gltexfiltermode].mag);
865 glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glfiltermodes[gltexfiltermode].min);
866
867 if (glinfo.maxanisotropy > 1.0) {
868 if (glanisotropy <= 0 || glanisotropy > glinfo.maxanisotropy) {
869 glanisotropy = (int)glinfo.maxanisotropy;
870 }
871 glfunc.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glanisotropy);
872 }
873
874 if (! (pth->flags & PTH_CLAMPED)) {
875 glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
876 glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
877 } else { //For sprite textures, clamping looks better than wrapping
878 GLint c = glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
879 glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c);
880 glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c);
881 }
882 }
883 }
884
885
886 /**
887 * Applies a filter to transparent pixels to improve their appearence when bilinearly filtered
888 * @param tex the texture to process
889 * @param clamped whether the texture is to be used clamped
890 */
ptm_fixtransparency(PTTexture * tex,int clamped)891 static void ptm_fixtransparency(PTTexture * tex, int clamped)
892 {
893 coltype *wpptr;
894 int j, x, y, r, g, b;
895 int dox, doy, naxsiz2;
896 int daxsiz = tex->tsizx, daysiz = tex->tsizy;
897 int daxsiz2 = tex->sizx, daysiz2 = tex->sizy;
898
899 dox = daxsiz2-1;
900 doy = daysiz2-1;
901 if (clamped) {
902 dox = min(dox,daxsiz);
903 doy = min(doy,daysiz);
904 } else {
905 // Make repeating textures duplicate top/left parts
906 daxsiz = daxsiz2;
907 daysiz = daysiz2;
908 }
909
910 daxsiz--; daysiz--; naxsiz2 = -daxsiz2; // Hacks for optimization inside loop
911
912 // Set transparent pixels to average color of neighboring opaque pixels
913 // Doing this makes bilinear filtering look much better for masked textures (I.E. sprites)
914 for (y = doy; y >= 0; y--) {
915 wpptr = &tex->pic[y*daxsiz2+dox];
916 for (x = dox; x >= 0; x--, wpptr--) {
917 if (wpptr->a) {
918 continue;
919 }
920 r = g = b = j = 0;
921 if ((x> 0) && (wpptr[ -1].a)) {
922 r += (int)wpptr[ -1].r;
923 g += (int)wpptr[ -1].g;
924 b += (int)wpptr[ -1].b;
925 j++;
926 }
927 if ((x<daxsiz) && (wpptr[ +1].a)) {
928 r += (int)wpptr[ +1].r;
929 g += (int)wpptr[ +1].g;
930 b += (int)wpptr[ +1].b;
931 j++;
932 }
933 if ((y> 0) && (wpptr[naxsiz2].a)) {
934 r += (int)wpptr[naxsiz2].r;
935 g += (int)wpptr[naxsiz2].g;
936 b += (int)wpptr[naxsiz2].b;
937 j++;
938 }
939 if ((y<daysiz) && (wpptr[daxsiz2].a)) {
940 r += (int)wpptr[daxsiz2].r;
941 g += (int)wpptr[daxsiz2].g;
942 b += (int)wpptr[daxsiz2].b;
943 j++;
944 }
945 switch (j) {
946 case 1: wpptr->r = r ;
947 wpptr->g = g ;
948 wpptr->b = b ;
949 break;
950 case 2: wpptr->r = ((r + 1)>>1);
951 wpptr->g = ((g + 1)>>1);
952 wpptr->b = ((b + 1)>>1);
953 break;
954 case 3: wpptr->r = ((r*85+128)>>8);
955 wpptr->g = ((g*85+128)>>8);
956 wpptr->b = ((b*85+128)>>8);
957 break;
958 case 4: wpptr->r = ((r + 2)>>2);
959 wpptr->g = ((g + 2)>>2);
960 wpptr->b = ((b + 2)>>2);
961 break;
962 default: break;
963 }
964 }
965 }
966 }
967
968 /**
969 * Applies brightness (if no gammabrightness is available) and other hightile
970 * effects to a texture. As a bonus, it also checks if there is any transparency
971 * in the texture (and updates tex->hasalpha).
972 * @param tex the texture
973 * @param effects the effects
974 */
ptm_applyeffects(PTTexture * tex,int effects)975 static void ptm_applyeffects(PTTexture * tex, int effects)
976 {
977 int alph = 255;
978 int x, y;
979 coltype * tptr = tex->pic;
980
981 if (effects == 0 && gammabrightness) {
982 // use a quicker scan for alpha since we don't need
983 // to be swizzling texel components
984 for (y = tex->tsizy - 1; y >= 0; y--, tptr += tex->sizx) {
985 for (x = tex->tsizx - 1; x >= 0; x--) {
986 alph &= tptr[x].a;
987 }
988 }
989 } else {
990 unsigned char *brit = &britable[gammabrightness ? 0 : curbrightness][0];
991 coltype tcol;
992
993 for (y = tex->tsizy - 1; y >= 0; y--, tptr += tex->sizx) {
994 for (x = tex->tsizx - 1; x >= 0; x--) {
995 tcol.b = brit[tptr[x].b];
996 tcol.g = brit[tptr[x].g];
997 tcol.r = brit[tptr[x].r];
998 tcol.a = tptr[x].a;
999 alph &= tptr[x].a;
1000
1001 if (effects & HICEFFECT_GREYSCALE) {
1002 float y;
1003 y = 0.3 * (float)tcol.r;
1004 y += 0.59 * (float)tcol.g;
1005 y += 0.11 * (float)tcol.b;
1006 tcol.b = (unsigned char)max(0.0, min(255.0, y));
1007 tcol.g = tcol.r = tcol.b;
1008 }
1009 if (effects & HICEFFECT_INVERT) {
1010 tcol.b = 255-tcol.b;
1011 tcol.g = 255-tcol.g;
1012 tcol.r = 255-tcol.r;
1013 }
1014
1015 tptr[x] = tcol;
1016 }
1017 }
1018 }
1019
1020 tex->hasalpha = (alph != 255);
1021 }
1022
1023
1024 /**
1025 * Scales down the texture by half in-place
1026 * @param tex the texture
1027 */
ptm_mipscale(PTTexture * tex)1028 static void ptm_mipscale(PTTexture * tex)
1029 {
1030 GLsizei newx, newy;
1031 GLsizei x, y;
1032 coltype *wpptr, *rpptr;
1033 int r, g, b, a, k;
1034
1035 newx = max(1, (tex->sizx >> 1));
1036 newy = max(1, (tex->sizy >> 1));
1037
1038 for (y = 0; y < newy; y++) {
1039 wpptr = &tex->pic[y * newx];
1040 rpptr = &tex->pic[(y << 1) * tex->sizx];
1041
1042 for (x = 0; x < newx; x++, wpptr++, rpptr += 2) {
1043 r = g = b = a = k = 0;
1044 if (rpptr[0].a) {
1045 r += (int)rpptr[0].r;
1046 g += (int)rpptr[0].g;
1047 b += (int)rpptr[0].b;
1048 a += (int)rpptr[0].a;
1049 k++;
1050 }
1051 if ((x+x+1 < tex->sizx) && (rpptr[1].a)) {
1052 r += (int)rpptr[1].r;
1053 g += (int)rpptr[1].g;
1054 b += (int)rpptr[1].b;
1055 a += (int)rpptr[1].a;
1056 k++;
1057 }
1058 if (y+y+1 < tex->sizy) {
1059 if (rpptr[tex->sizx].a) {
1060 r += (int)rpptr[tex->sizx ].r;
1061 g += (int)rpptr[tex->sizx ].g;
1062 b += (int)rpptr[tex->sizx ].b;
1063 a += (int)rpptr[tex->sizx ].a;
1064 k++;
1065 }
1066 if ((x+x+1 < tex->sizx) && (rpptr[tex->sizx+1].a)) {
1067 r += (int)rpptr[tex->sizx+1].r;
1068 g += (int)rpptr[tex->sizx+1].g;
1069 b += (int)rpptr[tex->sizx+1].b;
1070 a += (int)rpptr[tex->sizx+1].a;
1071 k++;
1072 }
1073 }
1074 switch(k) {
1075 case 0:
1076 case 1: wpptr->r = r;
1077 wpptr->g = g;
1078 wpptr->b = b;
1079 wpptr->a = a;
1080 break;
1081 case 2: wpptr->r = ((r+1)>>1);
1082 wpptr->g = ((g+1)>>1);
1083 wpptr->b = ((b+1)>>1);
1084 wpptr->a = ((a+1)>>1);
1085 break;
1086 case 3: wpptr->r = ((r*85+128)>>8);
1087 wpptr->g = ((g*85+128)>>8);
1088 wpptr->b = ((b*85+128)>>8);
1089 wpptr->a = ((a*85+128)>>8);
1090 break;
1091 case 4: wpptr->r = ((r+2)>>2);
1092 wpptr->g = ((g+2)>>2);
1093 wpptr->b = ((b+2)>>2);
1094 wpptr->a = ((a+2)>>2);
1095 break;
1096 default: break;
1097 }
1098 //if (wpptr->a) wpptr->a = 255;
1099 }
1100 }
1101
1102 tex->sizx = newx;
1103 tex->sizy = newy;
1104 }
1105
1106
1107 /**
1108 * Sends texture data to GL
1109 * @param ptm the texture management header
1110 * @param flags extra flags to modify how the texture is uploaded
1111 * @param tex the texture to upload
1112 * @param tdef the polymosttexcache definition to receive compressed mipmaps, or null
1113 */
ptm_uploadtexture(PTMHead * ptm,unsigned short flags,PTTexture * tex,PTCacheTile * tdef)1114 static void ptm_uploadtexture(PTMHead * ptm, unsigned short flags, PTTexture * tex, PTCacheTile * tdef)
1115 {
1116 int i;
1117 GLint mipmap;
1118 GLint intexfmt;
1119 int compress = PTCOMPRESS_NONE;
1120 unsigned char * comprdata = 0;
1121 int tdefmip = 0, comprsize = 0;
1122 int starttime;
1123
1124 detect_texture_size();
1125
1126 #if USE_OPENGL == USE_GLES2
1127 // GLES permits BGRA as an internal format.
1128 intexfmt = tex->rawfmt;
1129 #else
1130 intexfmt = GL_RGBA;
1131 #endif
1132 if (!(flags & PTH_NOCOMPRESS) && glusetexcompr) {
1133 #if GL_EXT_texture_compression_dxt1 || GL_EXT_texture_compression_s3tc
1134 if (!compress && !tex->hasalpha && glinfo.texcomprdxt1) {
1135 intexfmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1136 compress = PTCOMPRESS_DXT1;
1137 }
1138 #endif
1139 #if GL_OES_compressed_ETC1_RGB8_texture
1140 if (!compress && !tex->hasalpha && glinfo.texcompretc1) {
1141 intexfmt = GL_ETC1_RGB8_OES;
1142 compress = PTCOMPRESS_ETC1;
1143 }
1144 #endif
1145 #if GL_EXT_texture_compression_s3tc
1146 if (!compress && tex->hasalpha && glinfo.texcomprdxt5) {
1147 intexfmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1148 compress = PTCOMPRESS_DXT5;
1149 }
1150 #endif
1151 }
1152
1153 if (compress && tdef) {
1154 tdef->format = intexfmt;
1155 tdef->tsizx = tex->tsizx;
1156 tdef->tsizy = tex->tsizy;
1157 }
1158
1159 if (ptm->glpic == 0) {
1160 glfunc.glGenTextures(1, &ptm->glpic);
1161 }
1162 glfunc.glBindTexture(GL_TEXTURE_2D, ptm->glpic);
1163
1164 ptm_fixtransparency(tex, (flags & PTH_CLAMPED));
1165
1166 mipmap = 0;
1167 if (! (flags & PTH_NOMIPLEVEL)) {
1168 // if we aren't instructed to preserve all mipmap levels,
1169 // immediately throw away gltexmiplevel mipmaps
1170 mipmap = max(0, gltexmiplevel);
1171 }
1172 while ((tex->sizx >> mipmap) > (1 << gltexmaxsize) ||
1173 (tex->sizy >> mipmap) > (1 << gltexmaxsize)) {
1174 // throw away additional mipmaps until the texture fits within
1175 // the maximum size permitted by the GL driver
1176 mipmap++;
1177 }
1178
1179 for ( ;
1180 mipmap > 0 && (tex->sizx > 1 || tex->sizy > 1);
1181 mipmap--) {
1182 if (compress && tdef) {
1183 comprsize = ptcompress_getstorage(tex->sizx, tex->sizy, compress);
1184 comprdata = (unsigned char *) malloc(comprsize);
1185
1186 starttime = getticks();
1187 ptcompress_compress(tex->pic, tex->sizx, tex->sizy, comprdata, compress);
1188 if (polymosttexverbosity >= 2) {
1189 buildprintf("PolymostTex: ptcompress_compress (%dx%d, %s) took %f sec\n",
1190 tex->sizx, tex->sizy, compressfourcc[compress],
1191 (float)(getticks() - starttime) / 1000.f);
1192 }
1193
1194 tdef->mipmap[tdefmip].sizx = tex->sizx;
1195 tdef->mipmap[tdefmip].sizy = tex->sizy;
1196 tdef->mipmap[tdefmip].length = comprsize;
1197 tdef->mipmap[tdefmip].data = comprdata;
1198 tdefmip++;
1199
1200 comprdata = 0;
1201 }
1202
1203 ptm_mipscale(tex);
1204 ptm_fixtransparency(tex, (flags & PTH_CLAMPED));
1205 }
1206
1207 if (compress) {
1208 comprsize = ptcompress_getstorage(tex->sizx, tex->sizy, compress);
1209 comprdata = (unsigned char *) malloc(comprsize);
1210
1211 starttime = getticks();
1212 ptcompress_compress(tex->pic, tex->sizx, tex->sizy, comprdata, compress);
1213 if (polymosttexverbosity >= 2) {
1214 buildprintf("PolymostTex: ptcompress_compress (%dx%d, %s) took %f sec\n",
1215 tex->sizx, tex->sizy, compressfourcc[compress],
1216 (float)(getticks() - starttime) / 1000.f);
1217 }
1218
1219 if (tdef) {
1220 tdef->mipmap[tdefmip].sizx = tex->sizx;
1221 tdef->mipmap[tdefmip].sizy = tex->sizy;
1222 tdef->mipmap[tdefmip].length = comprsize;
1223 tdef->mipmap[tdefmip].data = comprdata;
1224 tdefmip++;
1225 }
1226
1227 glfunc.glCompressedTexImage2D(GL_TEXTURE_2D, 0,
1228 intexfmt, tex->sizx, tex->sizy, 0,
1229 comprsize, (const GLvoid *) comprdata);
1230
1231 if (tdef) {
1232 // we need to retain each mipmap for the tdef struct, so
1233 // force each mipmap to be allocated afresh in the loop below
1234 comprdata = 0;
1235 }
1236 } else {
1237 glfunc.glTexImage2D(GL_TEXTURE_2D, 0,
1238 intexfmt, tex->sizx, tex->sizy, 0, tex->rawfmt,
1239 GL_UNSIGNED_BYTE, (const GLvoid *) tex->pic);
1240 }
1241
1242 for (mipmap = 1; tex->sizx > 1 || tex->sizy > 1; mipmap++) {
1243 ptm_mipscale(tex);
1244 ptm_fixtransparency(tex, (flags & PTH_CLAMPED));
1245
1246 if (compress) {
1247 comprsize = ptcompress_getstorage(tex->sizx, tex->sizy, compress);
1248 if (tdef) {
1249 comprdata = (unsigned char *) malloc(comprsize);
1250 }
1251
1252 starttime = getticks();
1253 ptcompress_compress(tex->pic, tex->sizx, tex->sizy, comprdata, compress);
1254 if (polymosttexverbosity >= 2) {
1255 buildprintf("PolymostTex: ptcompress_compress (%dx%d, %s) took %f sec\n",
1256 tex->sizx, tex->sizy, compressfourcc[compress],
1257 (float)(getticks() - starttime) / 1000.f);
1258 }
1259
1260 if (tdef) {
1261 tdef->mipmap[tdefmip].sizx = tex->sizx;
1262 tdef->mipmap[tdefmip].sizy = tex->sizy;
1263 tdef->mipmap[tdefmip].length = comprsize;
1264 tdef->mipmap[tdefmip].data = comprdata;
1265 tdefmip++;
1266 }
1267
1268 glfunc.glCompressedTexImage2D(GL_TEXTURE_2D, mipmap,
1269 intexfmt, tex->sizx, tex->sizy, 0,
1270 comprsize, (const GLvoid *) comprdata);
1271
1272 if (tdef) {
1273 // we need to retain each mipmap for the tdef struct, so
1274 // force each mipmap to be allocated afresh in this loop
1275 comprdata = 0;
1276 }
1277 } else {
1278 glfunc.glTexImage2D(GL_TEXTURE_2D, mipmap,
1279 intexfmt, tex->sizx, tex->sizy, 0, tex->rawfmt,
1280 GL_UNSIGNED_BYTE, (const GLvoid *) tex->pic);
1281 }
1282 }
1283
1284 ptm->flags = 0;
1285 ptm->flags |= (tex->hasalpha ? PTH_HASALPHA : 0);
1286
1287 if (comprdata) {
1288 free(comprdata);
1289 }
1290 }
1291
1292
1293 /**
1294 * Prepare for priming by sweeping through the textures and marking them as all unused
1295 */
PTBeginPriming(void)1296 void PTBeginPriming(void)
1297 {
1298 PTHash * pth;
1299 int i;
1300
1301 for (i=PTHASHHEADSIZ-1; i>=0; i--) {
1302 pth = pthashhead[i];
1303 while (pth) {
1304 pth->primecnt = 0;
1305 pth = pth->next;
1306 }
1307 }
1308
1309 primecnt = 0;
1310 primedone = 0;
1311 primepos = 0;
1312 }
1313
1314 /**
1315 * Flag a texture as required for priming
1316 */
PTMarkPrime(int picnum,int palnum,unsigned short flags)1317 void PTMarkPrime(int picnum, int palnum, unsigned short flags)
1318 {
1319 PTHash * pth;
1320
1321 pth = pt_findhash(picnum, palnum, flags, 1);
1322 if (pth) {
1323 if (pth->primecnt == 0) {
1324 primecnt++;
1325 }
1326 pth->primecnt++;
1327 }
1328 }
1329
1330 /**
1331 * Runs a cycle of the priming process. Call until nonzero is returned.
1332 * @param done receives the number of textures primed so far
1333 * @param total receives the total number of textures to be primed
1334 * @return 0 when priming is complete
1335 */
PTDoPrime(int * done,int * total)1336 int PTDoPrime(int* done, int* total)
1337 {
1338 PTHash * pth;
1339
1340 if (primepos >= PTHASHHEADSIZ) {
1341 // done
1342 return 0;
1343 }
1344
1345 if (primepos == 0) {
1346 int i;
1347
1348 // first, unload all the textures that are not marked
1349 for (i=PTHASHHEADSIZ-1; i>=0; i--) {
1350 pth = pthashhead[i];
1351 while (pth) {
1352 if (pth->primecnt == 0) {
1353 pt_unload(pth);
1354 }
1355 pth = pth->next;
1356 }
1357 }
1358 }
1359
1360 pth = pthashhead[primepos];
1361 while (pth) {
1362 if (pth->primecnt > 0) {
1363 primedone++;
1364 pt_load(pth);
1365 }
1366 pth = pth->next;
1367 }
1368
1369 *done = primedone;
1370 *total = primecnt;
1371 primepos++;
1372
1373 return (primepos < PTHASHHEADSIZ);
1374 }
1375
1376 /**
1377 * Resets the texture hash but leaves the headers in memory
1378 */
PTReset()1379 void PTReset()
1380 {
1381 PTHash * pth;
1382 int i;
1383
1384 for (i=PTHASHHEADSIZ-1; i>=0; i--) {
1385 pth = pthashhead[i];
1386 while (pth) {
1387 pt_unload(pth);
1388 pth = pth->next;
1389 }
1390 }
1391 }
1392
1393 /**
1394 * Clears the texture hash of all content
1395 */
PTClear()1396 void PTClear()
1397 {
1398 PTHash * pth, * next;
1399 PTMHash * ptmh, * mnext;
1400 int i;
1401
1402 for (i=PTHASHHEADSIZ-1; i>=0; i--) {
1403 pth = pthashhead[i];
1404 while (pth) {
1405 next = pth->next;
1406 pt_unload(pth);
1407 free(pth);
1408 pth = next;
1409 }
1410 pthashhead[i] = 0;
1411 }
1412
1413 for (i=PTMHASHHEADSIZ-1; i>=0; i--) {
1414 ptmh = ptmhashhead[i];
1415 while (ptmh) {
1416 mnext = ptmh->next;
1417 free(ptmh);
1418 ptmh = mnext;
1419 }
1420 ptmhashhead[i] = 0;
1421 }
1422 }
1423
1424
1425
1426 /**
1427 * Fetches a texture header ready for rendering
1428 * @param picnum
1429 * @param palnum
1430 * @param flags
1431 * @param peek if !0, does not try and create a header if none exists
1432 * @return pointer to the header, or null if peek!=0 and none exists
1433 */
PT_GetHead(int picnum,int palnum,unsigned short flags,int peek)1434 PTHead * PT_GetHead(int picnum, int palnum, unsigned short flags, int peek)
1435 {
1436 PTHash * pth;
1437
1438 pth = pt_findhash(picnum, palnum, flags, peek == 0);
1439 if (pth == 0) {
1440 return 0;
1441 }
1442
1443 if (!pt_load(pth)) {
1444 return 0;
1445 }
1446
1447 while (pth->deferto) {
1448 // this might happen if pt_load needs to defer to ART
1449 pth = pth->deferto;
1450 }
1451
1452 return &pth->head;
1453 }
1454
1455
1456
1457
1458
ptiter_matches(PTIter iter)1459 static inline int ptiter_matches(PTIter iter)
1460 {
1461 if (iter->match == 0) {
1462 return 1; // matching every item
1463 }
1464 if ((iter->match & PTITER_PICNUM) && iter->pth->head.picnum != iter->picnum) {
1465 return 0;
1466 }
1467 if ((iter->match & PTITER_PALNUM) && iter->pth->head.palnum != iter->palnum) {
1468 return 0;
1469 }
1470 if ((iter->match & PTITER_FLAGS) && (iter->pth->head.flags & iter->flagsmask) != iter->flags) {
1471 return 0;
1472 }
1473 return 1;
1474 }
1475
ptiter_seekforward(PTIter iter)1476 static void ptiter_seekforward(PTIter iter)
1477 {
1478 while (1) {
1479 if (iter->pth && ptiter_matches(iter)) {
1480 break;
1481 }
1482 if (iter->pth == 0) {
1483 if ((iter->match & PTITER_PICNUM)) {
1484 // because the hash key is based on picture number,
1485 // reaching the end of the hash chain means we need
1486 // not look further
1487 break;
1488 }
1489 iter->i++;
1490 if (iter->i >= PTHASHHEADSIZ) {
1491 // end of hash
1492 iter->pth = 0;
1493 iter->i = PTHASHHEADSIZ;
1494 break;
1495 }
1496 iter->pth = pthashhead[iter->i];
1497 } else {
1498 iter->pth = iter->pth->next;
1499 }
1500 }
1501 }
1502
1503 /**
1504 * Creates a new iterator for walking the header hash looking for particular
1505 * parameters that match.
1506 * @param match PTITER_* flags indicating which parameters to test
1507 * @param picnum when (match&PTITER_PICNUM), specifies the picnum
1508 * @param palnum when (match&PTITER_PALNUM), specifies the palnum
1509 * @param flagsmask when (match&PTITER_FLAGS), specifies the mask to apply to flags
1510 * @param flags when (match&PTITER_FLAGS), specifies the flags to test
1511 * @return an iterator
1512 */
PTIterNewMatch(int match,int picnum,int palnum,unsigned short flagsmask,unsigned short flags)1513 PTIter PTIterNewMatch(int match, int picnum, int palnum, unsigned short flagsmask, unsigned short flags)
1514 {
1515 PTIter iter;
1516
1517 iter = (PTIter) malloc(sizeof(struct PTIter_typ));
1518 if (!iter) {
1519 return 0;
1520 }
1521
1522 iter->i = 0;
1523 iter->pth = pthashhead[0];
1524 iter->match = match;
1525 iter->picnum = picnum;
1526 iter->palnum = palnum;
1527 iter->flagsmask = flagsmask;
1528 iter->flags = flags;
1529
1530 if ((match & PTITER_PICNUM)) {
1531 iter->i = pt_gethashhead(picnum);
1532 iter->pth = pthashhead[iter->i];
1533 if (iter->pth == 0) {
1534 iter->pth = 0;
1535 return iter;
1536 }
1537 }
1538
1539 ptiter_seekforward(iter);
1540
1541 return iter;
1542 }
1543
1544 /**
1545 * Creates a new iterator for walking the header hash
1546 * @return an iterator
1547 */
PTIterNew(void)1548 PTIter PTIterNew(void)
1549 {
1550 return PTIterNewMatch(0, 0, 0, 0, 0);
1551 }
1552
1553 /**
1554 * Gets the next header from an iterator
1555 * @param iter the iterator
1556 * @return the next header, or null if at the end
1557 */
PTIterNext(PTIter iter)1558 PTHead * PTIterNext(PTIter iter)
1559 {
1560 PTHead * found = 0;
1561
1562 if (!iter) return 0;
1563
1564 if (iter->pth == 0) {
1565 return 0;
1566 }
1567
1568 found = &iter->pth->head;
1569 iter->pth = iter->pth->next;
1570
1571 ptiter_seekforward(iter);
1572
1573 return found;
1574 }
1575
1576 /**
1577 * Frees an iterator
1578 * @param iter the iterator
1579 */
PTIterFree(PTIter iter)1580 void PTIterFree(PTIter iter)
1581 {
1582 if (!iter) return;
1583 free(iter);
1584 }
1585
1586 #endif //USE_OPENGL
1587