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