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