]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake2/qdata_heretic2/tmix.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake2 / qdata_heretic2 / tmix.c
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 */\r
21 \r
22 #include "qdata.h"\r
23 #include "flex.h"\r
24 \r
25 #define         MAXFILES        2048\r
26 \r
27 typedef struct\r
28 {\r
29         int                     x;\r
30         int                     y;\r
31         int                     w;\r
32         int                     h;\r
33         int                     cw;\r
34         int                     ch;\r
35         int                     rw;\r
36         int                     index;\r
37         int                     depth;\r
38         int                     col;\r
39         int                     baseline;\r
40         char            name[128];\r
41 } Coords;\r
42 \r
43 int                             filenum;\r
44 int                             valid;\r
45 Coords                  in[MAXFILES];\r
46 Coords                  out;\r
47 char                    outscript[256];\r
48 char                    sourcedir[256];\r
49 char                    outusage[256];\r
50 char                    root[32];\r
51 \r
52 int                             destsize = 0;\r
53 byte                    *pixels = NULL;                         // Buffer to load image\r
54 long                    *outpixels = NULL;                      // Buffer to store combined textures\r
55 long                    *usagemap = NULL;                       // Buffer of usage map\r
56 void                    *bmptemp = NULL;                        // Buffer of usage map\r
57 byte                    *map = NULL;\r
58 \r
59 int                             xcharsize;\r
60 int                             ycharsize;\r
61 int                             dosort = 0;\r
62 int                             missed = 0;\r
63 int                             overlap = 0;\r
64 int                             nobaseline = 0;\r
65 int                             percent;\r
66 \r
67 //////////////////////////////////////////////////\r
68 // Setting the char based usage map                             //\r
69 //////////////////////////////////////////////////\r
70 \r
71 byte    TryPlace(Coords *coord)\r
72 {\r
73         int             x, y;\r
74         byte    entry = 0;\r
75         byte    *mapitem;\r
76         \r
77         mapitem = map + (coord->x / xcharsize) + ((coord->y / ycharsize) * out.cw);\r
78 \r
79         for (y = 0; y < coord->ch; y++, mapitem += out.cw - coord->cw)\r
80         {\r
81                 for (x = 0; x < coord->cw; x++)\r
82                 {\r
83                         if (entry |= *mapitem++ & 8)\r
84                         {\r
85                                 return(entry);\r
86                         }\r
87                 }\r
88         }\r
89         return(entry);\r
90 }\r
91 \r
92 void    SetMap(Coords *coord)\r
93 {\r
94         int             x, y;\r
95         byte    *mapitem;\r
96 \r
97         mapitem = map + (coord->x / xcharsize) + ((coord->y / ycharsize) * out.cw);\r
98 \r
99         for (y = 0; y < coord->ch; y++, mapitem += out.cw - coord->cw)\r
100                 for (x = 0; x < coord->cw; x++)\r
101                         *mapitem++ |= 8;\r
102 }\r
103 \r
104 //////////////////////////////////////////////////\r
105 // Setting the pixel based usage map                    //\r
106 //////////////////////////////////////////////////\r
107 \r
108 void    CheckOverlap(Coords *coord)\r
109 {\r
110         int                     x;\r
111         int                     y;\r
112         long            *dest;\r
113 \r
114         x = coord->x;\r
115         y = coord->y;\r
116 \r
117         dest = (long *)(usagemap + x + (y * out.w));\r
118 \r
119         for (y = 0; y < coord->h; y++, dest += out.w - coord->w)\r
120         {\r
121                 for (x = 0; x < coord->w; x++)\r
122                 {\r
123                         if (*dest++)\r
124                         {\r
125                                 overlap++;\r
126                                 return;\r
127                         }\r
128                 }\r
129         }\r
130 }\r
131 \r
132 void    SetUsageMap(Coords *coord)\r
133 {\r
134         int                     x;\r
135         int                     y;\r
136         long            *dest;\r
137 \r
138         x = coord->x;\r
139         y = coord->y;\r
140 \r
141         dest = (long *)(usagemap + x + (y * out.w));\r
142 \r
143         for (y = 0; y < coord->h; y++, dest += out.w - coord->w)\r
144         {\r
145                 for (x = 0; x < coord->w; x++)\r
146                 {\r
147                         *dest++ = coord->col;\r
148                 }\r
149         }\r
150 }\r
151 \r
152 //////////////////////////////////////////////////\r
153 // Flips the BMP image to the correct way up    //\r
154 //////////////////////////////////////////////////\r
155 \r
156 void    CopyLine(byte *dest, byte *src, int size)\r
157 {\r
158         int             x;\r
159         \r
160         for (x = 0; x < size; x++)\r
161                 *dest++ = *src++;\r
162 }\r
163 \r
164 /****************************************************/\r
165 /* Printing headers etc                                                         */\r
166 /****************************************************/\r
167 \r
168 void RemoveLeading(char *name)\r
169 {\r
170         int             i;\r
171         char    temp[128];\r
172 \r
173         for(i = strlen(name) - 1; i > 0; i--)\r
174         {\r
175                 if((name[i] == '\\') || (name[i] == '/'))\r
176                 {\r
177                         strcpy(temp, name + i + 1);\r
178                         strcpy(name, temp);\r
179                         return;\r
180                 }\r
181         }\r
182 }\r
183 \r
184 void RemoveExt(char *name)\r
185 {\r
186         while ((*name != '.') && *name)\r
187                 name++;\r
188         *name = 0;\r
189 }\r
190 \r
191 /****************************************************/\r
192 /* Misc calcualtions                                                            */\r
193 /****************************************************/\r
194 \r
195 int     TotalArea()\r
196 {\r
197         int             i;\r
198         int             total = 0;\r
199 \r
200         for (i = 0; i < (filenum + 2); i++)\r
201                 total += in[i].w * in[i].h;\r
202 \r
203         return(total);\r
204 }\r
205 \r
206 /****************************************************/\r
207 /* Setup and checking of all info                                       */\r
208 /****************************************************/\r
209 \r
210 void    InitVars()\r
211 {\r
212         filenum = 0;\r
213         valid = 0;\r
214         dosort = 0;\r
215         missed = 0;\r
216         overlap = 0;\r
217         nobaseline = 0;\r
218         \r
219         memset(outscript, 0, sizeof(outscript));\r
220         memset(outscript, 0, sizeof(sourcedir));\r
221         memset(outscript, 0, sizeof(outusage));\r
222         memset(outscript, 0, sizeof(root));\r
223 \r
224         memset(in, 0, sizeof(in));\r
225         memset(&out, 0, sizeof(out));\r
226 }\r
227 void Cleanup()\r
228 {\r
229         if (pixels)\r
230                 free(pixels);\r
231         if (usagemap)\r
232                 free(usagemap);\r
233         if (outpixels)\r
234                 free(outpixels);\r
235         if (bmptemp)\r
236                 free(bmptemp);\r
237         if (map)\r
238                 free(map);\r
239 }\r
240 \r
241 typedef struct glxy_s\r
242 {\r
243         float   xl, yt, xr, yb;\r
244         int             w, h, baseline;\r
245 } glxy_t;\r
246 \r
247 int     SaveScript(char *name)\r
248 {\r
249         FILE            *fp;\r
250         int                     i, j;\r
251         glxy_t          buff;\r
252         \r
253         if(fp = fopen(name, "wb"))\r
254         {\r
255                 for (j = 0; j < filenum; j++)\r
256                 {\r
257                         for (i = 0; i < filenum; i++)\r
258                         {\r
259                                 if (in[i].index == j)\r
260                                 {\r
261                                         if (in[i].depth)\r
262                                         {\r
263                                                 buff.xl = (float)in[i].x / (float)out.w;\r
264                                                 buff.yt = (float)in[i].y / (float)out.h;\r
265                                                 buff.xr = ((float)in[i].w + (float)in[i].x) / (float)out.w;\r
266                                                 buff.yb = ((float)in[i].h + (float)in[i].y) / (float)out.h;\r
267                                                 buff.w = in[i].w;\r
268                                                 buff.h = in[i].h;\r
269                                                 buff.baseline = in[i].baseline;\r
270                                         }\r
271                                         else\r
272                                         {\r
273                                                 memset(&buff, 0, sizeof(glxy_t));\r
274                                         }\r
275                                         fwrite(&buff, 1, sizeof(glxy_t), fp);\r
276                                         i = filenum;\r
277                                 }\r
278                         }\r
279                 }\r
280                 fclose(fp);\r
281                 return(true);\r
282         }\r
283         else\r
284                 return(false);\r
285 }\r
286 \r
287 int             GetScriptInfo(char *name)\r
288 {\r
289         FILE            *fp;\r
290         char            buffer[256];\r
291         char            tempbuff[256];\r
292         char            delims[] = {" \t,\n"};\r
293 \r
294         printf("Opening script file %s.\n", name);\r
295 \r
296         if (fp = fopen(name, "r"))\r
297         {\r
298                 while(fgets(buffer, 256, fp))\r
299                 {\r
300                         if (strncmp(buffer, "//", 2) && strncmp(buffer, "\n", 1))\r
301                         {\r
302                                 strupr(buffer);\r
303                                 strcpy(tempbuff, buffer);\r
304                                 if (strcmp(strtok(tempbuff, delims), "OUTPUT") == 0)\r
305                                 {\r
306                                         strcpy(out.name, strtok(NULL, delims));\r
307                                         strlwr(out.name);\r
308                                 }\r
309 \r
310                                 strcpy(tempbuff, buffer);\r
311                                 if (strcmp(strtok(tempbuff, delims), "SOURCEDIR") == 0)\r
312                                 {\r
313                                         strcpy(tempbuff, strtok(NULL, delims));\r
314                                         strcpy(sourcedir, ExpandPathAndArchive(tempbuff));\r
315                                 }\r
316 \r
317                                 strcpy(tempbuff, buffer);\r
318                                 if (strcmp(strtok(tempbuff, delims), "DOSORT") == 0)\r
319                                         dosort = 1;\r
320 \r
321                                 strcpy(tempbuff, buffer);\r
322                                 if (strcmp(strtok(tempbuff, delims), "XCHARSIZE") == 0)\r
323                                         xcharsize = strtol(strtok(NULL, delims), NULL, 0);\r
324 \r
325                                 strcpy(tempbuff, buffer);\r
326                                 if (strcmp(strtok(tempbuff, delims), "YCHARSIZE") == 0)\r
327                                         ycharsize = strtol(strtok(NULL, delims), NULL, 0);\r
328 \r
329                                 strcpy(tempbuff, buffer);\r
330                                 if (strcmp(strtok(tempbuff, delims), "OUTSCRIPT") == 0)\r
331                                 {\r
332                                         strcpy(outscript, strtok(NULL, delims));\r
333                                         strlwr(outscript);\r
334                                 }\r
335 \r
336                                 strcpy(tempbuff, buffer);\r
337                                 if (strcmp(strtok(tempbuff, delims), "OUTUSAGE") == 0)\r
338                                         strcpy(outusage, strtok(NULL, delims));\r
339 \r
340                                 strcpy(tempbuff, buffer);\r
341                                 if (strcmp(strtok(tempbuff, delims), "POS") == 0)\r
342                                 {\r
343                                         out.w = strtol(strtok(NULL, delims), NULL, 0);\r
344                                         out.h = strtol(strtok(NULL, delims), NULL, 0);\r
345                                 }\r
346 \r
347                                 strcpy(tempbuff, buffer);\r
348                                 if (strcmp(strtok(tempbuff, delims), "FILE") == 0)\r
349                                 {\r
350                                         strcpy(in[filenum].name, strtok(NULL, delims));\r
351                                         in[filenum].x = strtol(strtok(NULL, delims), NULL, 0);\r
352                                         in[filenum].y = strtol(strtok(NULL, delims), NULL, 0);\r
353                                         in[filenum].col = strtol(strtok(NULL, delims), NULL, 0);\r
354                                         filenum++;\r
355                                 }\r
356                         }\r
357                 }\r
358                 fclose(fp);\r
359                 return(true);\r
360         }\r
361         else\r
362         {\r
363                 printf("ERROR : Could not open script file.\n");\r
364                 return(false);\r
365         }\r
366 }\r
367 \r
368 int     CheckVars()\r
369 {\r
370         int             i;\r
371 \r
372          if (out.name[0] == 0)\r
373          {\r
374                 printf("ERROR : No output name specified.\n");\r
375                 return(false);\r
376         }\r
377         if ((out.w <= 0) || (out.h <= 0))\r
378         {\r
379                 printf("ERROR : Invalid VRAM coordinates.\n");\r
380                 return(false);\r
381         }\r
382         if (filenum == 0)\r
383         {\r
384                 printf("ERROR : No input files specified.\n");\r
385                 return(false);\r
386         }\r
387         for (i = 0; i < filenum; i++)\r
388                 if (in[i].name[0] == 0)\r
389                 {\r
390                         printf("ERROR : Input filename invalid.\n");\r
391                         return(false);\r
392                 }\r
393         return(true);\r
394 }\r
395 \r
396 // Makes sure texture is totally within the output area\r
397 \r
398 int     CheckCoords(Coords *coord)\r
399 {\r
400         if ((coord->x + coord->w) > out.w)\r
401                 return(false);\r
402         if ((coord->y + coord->h) > out.h)\r
403                 return(false);\r
404 \r
405         return(true);\r
406 }\r
407 // Gets the width, height, palette width and palette height of each BMP file\r
408 \r
409 int             GetFileDimensions()\r
410 {\r
411         int                     i;\r
412         int                     width, height;\r
413         char            name[128];\r
414 \r
415         for (i = 0; i < filenum; i++)\r
416         {\r
417                 in[i].index = i;\r
418 \r
419                 strcpy(name, sourcedir);\r
420                 strcat(name, in[i].name);\r
421                 printf("Getting file dimensions, file : %s        \r", in[i].name);\r
422                 if(FileExists(name))\r
423                 {\r
424                         LoadAnyImage(name, NULL, NULL, &width, &height);\r
425                         in[i].depth = 32;\r
426                         in[i].rw = width;\r
427                         in[i].w = width;                                                // makes it width in \r
428                         in[i].h = height;\r
429                         in[i].cw = (in[i].w + (xcharsize - 1)) / xcharsize;\r
430                         in[i].ch = (in[i].h + (ycharsize - 1)) / ycharsize;\r
431 \r
432                         if (!CheckCoords(&in[i]) && (in[i].x >= 0))\r
433                         {\r
434                                 printf("Error : texture %s out of bounds.\n", in[i].name);\r
435                                 return(false);\r
436                         }\r
437                         valid++;\r
438                 }\r
439                 else\r
440                 {\r
441                         in[i].depth = 0;\r
442                         in[i].x = -1;\r
443                         in[i].y = -1;\r
444                         in[i].w = 0;\r
445                         in[i].h = 0;\r
446                 }\r
447         }\r
448         printf("\n\n");\r
449         return(true);\r
450 }\r
451 \r
452 // Sorts files into order for optimal space finding\r
453 // Fixed position ones first, followed by the others in descending size\r
454 // The theory being that it is easier to find space for smaller textures.\r
455 // size = (width + height)\r
456 // For space finding it is easier to place a 32x32 than a 128x2\r
457 \r
458 #define WEIGHT  0x8000\r
459 \r
460 void    Swap(Coords *a, Coords *b)\r
461 {\r
462         Coords          c;\r
463 \r
464         c = *a;\r
465         *a = *b;\r
466         *b = c;\r
467 }\r
468 \r
469 void    SortInNames()\r
470 {\r
471         int             i, j;\r
472         int             largest, largcount;\r
473         int             size;\r
474 \r
475         printf("Sorting filenames by size.\n\n");\r
476 \r
477         for (j = 0; j < filenum; j++)\r
478         {\r
479                 largest = -1;\r
480                 largcount = -1;\r
481 \r
482                 for (i = j; i < filenum; i++)\r
483                 {\r
484                         if (in[i].depth)\r
485                         {\r
486                                 size = in[i].w + in[i].h;\r
487                 \r
488                                 if ((in[i].x < 0) && (size > largest))\r
489                                 {\r
490                                         largcount = i;\r
491                                         largest = size;\r
492                                 }\r
493                         }\r
494                 }\r
495                 if ((largcount >= 0) && (largcount != j))\r
496                         Swap(&in[j], &in[largcount]);\r
497         }\r
498 }\r
499 \r
500 int     SetVars(char *name)\r
501 {\r
502         if (!GetScriptInfo(name))\r
503                 return(false);\r
504 \r
505         if (!CheckVars())\r
506                 return(false);\r
507 \r
508         destsize = out.w * out.h;\r
509 \r
510         out.cw = out.w / xcharsize;\r
511         out.ch = out.h / ycharsize;\r
512 \r
513         if ((usagemap = (long *)SafeMalloc(destsize * 4, "")) == NULL)\r
514                 return(false);\r
515         if ((outpixels = (long *)SafeMalloc(destsize * 4, "")) == NULL)\r
516                 return(false); \r
517         if ((bmptemp = (void *)SafeMalloc(destsize * 4, "")) == NULL)\r
518                 return(false);\r
519         if ((map = (byte *)SafeMalloc(destsize / (xcharsize * ycharsize), "")) == NULL)\r
520                 return(false);\r
521 \r
522         if (GetFileDimensions() == false)\r
523                 return(false);\r
524 \r
525         if (dosort)\r
526                 SortInNames();\r
527 \r
528         return(true);\r
529 }\r
530 /****************************************************/\r
531 /* Actual copying routines                                                      */\r
532 /****************************************************/\r
533 \r
534 int FindCoords(Coords *coord)\r
535 {\r
536         int             tx, ty;\r
537 \r
538         if (coord->x >= 0)\r
539         {       \r
540                 SetMap(coord);\r
541                 return(true);\r
542         }\r
543         else\r
544         {\r
545                 for (ty = 0; ty < out.ch; ty++)\r
546                 {\r
547                         for (tx = 0; tx < out.cw; tx++)\r
548                         {\r
549                                 coord->x = (tx * xcharsize);\r
550                                 coord->y = (ty * ycharsize);\r
551         \r
552                                 if (CheckCoords(coord) && !TryPlace(coord))\r
553                                 {\r
554                                         SetMap(coord);\r
555                                         return(true);\r
556                                 }\r
557                         }\r
558                 }\r
559         }\r
560         coord->x = -1;\r
561         coord->y = -1;\r
562 \r
563         return(false);\r
564 }\r
565 \r
566 void CheckBaseline(int i)\r
567 {\r
568         int             y;\r
569         long    *pix;\r
570 \r
571         in[i].baseline = -1;\r
572         pix = (long *)pixels;\r
573 \r
574         for(y = 0; y < in[i].h; y++, pix += in[i].w)\r
575         {\r
576                 if((*pix & 0x00ffffff) == 0x00ff00ff)\r
577                 {\r
578                         in[i].baseline = y;\r
579                         break;\r
580                 }\r
581         }\r
582         pix = (long *)pixels;\r
583         for(y = 0; y < in[i].w * in[i].h; y++, pix++)\r
584         {\r
585                 if((*pix & 0x00ffffff) == 0x00ff00ff)\r
586                 {\r
587                         *pix = 0;\r
588                 }\r
589         }\r
590 \r
591         if(in[i].baseline == -1)\r
592         {\r
593                 printf("\nERROR : %s has no baseline\n", in[i].name);\r
594                 nobaseline++;\r
595         }\r
596 }\r
597 \r
598 void    CopyToMain32(Coords *coord)\r
599 {\r
600         int                     x;\r
601         int                     y;\r
602         long            *source;\r
603         long            *dest;\r
604 \r
605         x = coord->x;\r
606         y = coord->y;\r
607 \r
608         source = (long *)pixels;\r
609         dest = (long *)(outpixels + x + (y * out.w));\r
610 \r
611         for (y = 0; y < coord->h; y++, dest += out.w - coord->w)\r
612         {\r
613                 for (x = 0; x < coord->w; x++)\r
614                 {\r
615                         *dest++ = *source++;\r
616                 }\r
617         }\r
618 }\r
619 \r
620 void CreateMain()\r
621 {\r
622         int                     i, count;\r
623         int                     width, height;\r
624         char            name[128];\r
625         \r
626         for (i = 0, count = 0; i < filenum; i++)\r
627         {\r
628                 if (in[i].depth)\r
629                 {\r
630                         printf("\rProcessing %d of %d (%d missed, %d overlapping, %d nobase)\r", count + 1, valid, missed, overlap, nobaseline);\r
631                         count++;\r
632                         if (!FindCoords(&in[i]))\r
633                                 missed++;\r
634                         else\r
635                         {\r
636                                 strcpy(name, sourcedir);\r
637                                 strcat(name, in[i].name);\r
638                                 LoadAnyImage(name, &pixels, NULL, &width, &height);\r
639                                 CheckBaseline(i);\r
640                                 CheckOverlap(&in[i]);\r
641                                 CopyToMain32(&in[i]);\r
642                                 SetUsageMap(&in[i]);\r
643                         }\r
644                 }\r
645         }\r
646 }\r
647 \r
648 void Cmd_TextureMix()\r
649 {\r
650         miptex32_t              *qtex32;\r
651         char                    filename[1024];\r
652         int                             size;\r
653 \r
654         InitVars();\r
655 \r
656         GetScriptToken (false);\r
657 \r
658         strcpy(root, token);\r
659         RemoveExt(root);\r
660         RemoveLeading(root);\r
661 \r
662         strcpy(filename, ExpandPathAndArchive(token));\r
663         if (SetVars(filename))\r
664         {\r
665                 // Create combined texture\r
666                 percent = ((TotalArea() * 100) / (out.w * out.h));\r
667                 printf("Total area consumed : %d%%\n", percent);\r
668                 printf("Texture resolution  : %dx%d pixels.\n", xcharsize, ycharsize);\r
669                 CreateMain();\r
670 \r
671                 // Save image as m32\r
672                 sprintf (filename, "%spics/misc/%s.m32", gamedir, out.name);\r
673                 qtex32 = CreateMip32((unsigned *)outpixels, out.w, out.h, &size, false);\r
674 \r
675                 qtex32->contents = 0;\r
676                 qtex32->value = 0;\r
677                 qtex32->scale_x = 1.0;\r
678                 qtex32->scale_y = 1.0;\r
679                 sprintf (qtex32->name, "misc/%s", out.name);\r
680 \r
681                 printf ("\n\nwriting %s\n", filename);\r
682                 SaveFile (filename, (byte *)qtex32, size);\r
683                 free (qtex32);\r
684 \r
685                 // Save out script file\r
686                 sprintf (filename, "%spics/misc/%s.fnt", gamedir, outscript);\r
687                 printf("Writing %s as script file\n", filename);\r
688                 if (!SaveScript(filename))\r
689                 {\r
690                         printf("Unable to save output script.\n");\r
691                 }\r
692         }\r
693         printf("Everythings groovy.\n");\r
694         Cleanup();\r
695 }\r
696 \r
697 // end\r
698 \r