2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
28 #include "../idlib/precompiled.h"
33 idCVar idMegaTexture::r_megaTextureLevel( "r_megaTextureLevel", "0", CVAR_RENDERER | CVAR_INTEGER, "draw only a specific level" );
34 idCVar idMegaTexture::r_showMegaTexture( "r_showMegaTexture", "0", CVAR_RENDERER | CVAR_BOOL, "display all the level images" );
35 idCVar idMegaTexture::r_showMegaTextureLabels( "r_showMegaTextureLabels", "0", CVAR_RENDERER | CVAR_BOOL, "draw colored blocks in each tile" );
36 idCVar idMegaTexture::r_skipMegaTexture( "r_skipMegaTexture", "0", CVAR_RENDERER | CVAR_INTEGER, "only use the lowest level image" );
37 idCVar idMegaTexture::r_terrainScale( "r_terrainScale", "3", CVAR_RENDERER | CVAR_INTEGER, "vertically scale USGS data" );
41 allow sparse population of the upper detail tiles
45 int RoundDownToPowerOfTwo( int num ) {
47 for (pot = 1 ; (pot*2) <= num ; pot<<=1) {
57 static byte colors[8][4] = {
65 { 255, 255, 255, 255 }
68 static void R_EmptyLevelImage( idImage *image ) {
69 int c = MAX_LEVEL_WIDTH * MAX_LEVEL_WIDTH;
70 byte *data = (byte *)_alloca( c*4 );
72 for ( int i = 0 ; i < c ; i++ ) {
73 ((int *)data)[i] = fillColor.intVal;
76 // FIXME: this won't live past vid mode changes
77 image->GenerateImage( data, MAX_LEVEL_WIDTH, MAX_LEVEL_WIDTH,
78 TF_DEFAULT, false, TR_REPEAT, TD_HIGH_QUALITY );
87 bool idMegaTexture::InitFromMegaFile( const char *fileBase ) {
88 idStr name = "megaTextures/";
90 name.StripFileExtension();
95 fileHandle = fileSystem->OpenFileRead( name.c_str() );
97 common->Printf( "idMegaTexture: failed to open %s\n", name.c_str() );
101 fileHandle->Read( &header, sizeof( header ) );
102 if ( header.tileSize < 64 || header.tilesWide < 1 || header.tilesHigh < 1 ) {
103 common->Printf( "idMegaTexture: bad header on %s\n", name.c_str() );
107 currentTriMapping = NULL;
110 width = header.tilesWide;
111 height = header.tilesHigh;
113 int tileOffset = 1; // just past the header
115 memset( levels, 0, sizeof( levels ) );
117 idTextureLevel *level = &levels[numLevels];
120 level->tileOffset = tileOffset;
121 level->tilesWide = width;
122 level->tilesHigh = height;
123 level->parms[0] = -1; // initially mask everything
126 level->parms[3] = (float)width / TILE_PER_LEVEL;
129 tileOffset += level->tilesWide * level->tilesHigh;
132 sprintf( str, "MEGA_%s_%i", fileBase, numLevels );
134 // give each level a default fill color
135 for (int i = 0 ; i < 4 ; i++ ) {
136 fillColor.color[i] = colors[numLevels+1][i];
139 levels[numLevels].image = globalImages->ImageFromFunction( str, R_EmptyLevelImage );
142 if ( width <= TILE_PER_LEVEL && height <= TILE_PER_LEVEL ) {
145 width = ( width + 1 ) >> 1;
146 height = ( height + 1 ) >> 1;
149 // force first bind to load everything
150 currentViewOrigin[0] = -99999999.0f;
151 currentViewOrigin[1] = -99999999.0f;
152 currentViewOrigin[2] = -99999999.0f;
161 analyzes xyz and st to create a mapping
162 This is not very robust, but works for rectangular grids
165 void idMegaTexture::SetMappingForSurface( const srfTriangles_t *tri ) {
166 if ( tri == currentTriMapping ) {
169 currentTriMapping = tri;
175 idDrawVert origin, axis[2];
186 for ( int i = 0 ; i < tri->numVerts ; i++ ) {
187 idDrawVert *v = &tri->verts[i];
189 if ( v->st[0] <= origin.st[0] && v->st[1] <= origin.st[1] ) {
192 if ( v->st[0] >= axis[0].st[0] && v->st[1] <= axis[0].st[1] ) {
195 if ( v->st[0] <= axis[1].st[0] && v->st[1] >= axis[1].st[1] ) {
200 for ( int i = 0 ; i < 2 ; i++ ) {
201 idVec3 dir = axis[i].xyz - origin.xyz;
202 float texLen = axis[i].st[i] - origin.st[i];
203 float spaceLen = (axis[i].xyz - origin.xyz).Length();
205 float scale = texLen / (spaceLen*spaceLen);
208 float c = origin.xyz * dir - origin.st[i];
210 localViewToTextureCenter[i][0] = dir[0];
211 localViewToTextureCenter[i][1] = dir[1];
212 localViewToTextureCenter[i][2] = dir[2];
213 localViewToTextureCenter[i][3] = -c;
222 void idMegaTexture::BindForViewOrigin( const idVec3 viewOrigin ) {
224 SetViewOrigin( viewOrigin );
226 // borderClamp image goes in texture 0
227 GL_SelectTexture( 0 );
228 globalImages->borderClampImage->Bind();
230 // level images in higher textures, blurriest first
231 for ( int i = 0 ; i < 7 ; i++ ) {
232 GL_SelectTexture( 1+i );
234 if ( i >= numLevels ) {
235 globalImages->whiteImage->Bind();
237 static float parms[4] = { -2, -2, 0, 1 }; // no contribution
238 qglProgramLocalParameter4fvARB( GL_VERTEX_PROGRAM_ARB, i, parms );
240 idTextureLevel *level = &levels[ numLevels-1-i ];
242 if ( r_showMegaTexture.GetBool() ) {
244 globalImages->blackImage->Bind();
246 globalImages->whiteImage->Bind();
249 level->image->Bind();
251 qglProgramLocalParameter4fvARB( GL_VERTEX_PROGRAM_ARB, i, level->parms );
260 qglProgramLocalParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 7, parms );
264 parms[2] = r_terrainScale.GetFloat();
266 qglProgramLocalParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 8, parms );
273 This can go away once everything uses fragment programs so the enable states don't
277 void idMegaTexture::Unbind( void ) {
278 for ( int i = 0 ; i < numLevels ; i++ ) {
279 GL_SelectTexture( 1+i );
280 globalImages->BindNull();
290 void idMegaTexture::SetViewOrigin( const idVec3 viewOrigin ) {
291 if ( r_showMegaTextureLabels.IsModified() ) {
292 r_showMegaTextureLabels.ClearModified();
293 currentViewOrigin[0] = viewOrigin[0] + 0.1; // force a change
294 for ( int i = 0 ; i < numLevels ; i++ ) {
295 levels[i].Invalidate();
299 if ( viewOrigin == currentViewOrigin ) {
302 if ( r_skipMegaTexture.GetBool() ) {
306 currentViewOrigin = viewOrigin;
310 // convert the viewOrigin to a texture center, which will
311 // be a different conversion for each megaTexture
312 for ( int i = 0 ; i < 2 ; i++ ) {
314 viewOrigin[0] * localViewToTextureCenter[i][0] +
315 viewOrigin[1] * localViewToTextureCenter[i][1] +
316 viewOrigin[2] * localViewToTextureCenter[i][2] +
317 localViewToTextureCenter[i][3];
320 for ( int i = 0 ; i < numLevels ; i++ ) {
321 levels[i].UpdateForCenter( texCenter );
330 A local tile will only be mapped to globalTile[ localTile + X * TILE_PER_LEVEL ] for some x
333 void idTextureLevel::UpdateTile( int localX, int localY, int globalX, int globalY ) {
334 idTextureTile *tile = &tileMap[localX][localY];
336 if ( tile->x == globalX && tile->y == globalY ) {
339 if ( (globalX & (TILE_PER_LEVEL-1)) != localX || (globalY & (TILE_PER_LEVEL-1)) != localY ) {
340 common->Error( "idTextureLevel::UpdateTile: bad coordinate mod" );
346 byte data[ TILE_SIZE * TILE_SIZE * 4 ];
348 if ( globalX >= tilesWide || globalX < 0 || globalY >= tilesHigh || globalY < 0 ) {
350 memset( data, 0, sizeof( data ) );
352 // extract the data from the full image (FIXME: background load from disk)
353 int tileNum = tileOffset + tile->y * tilesWide + tile->x;
355 int tileSize = TILE_SIZE * TILE_SIZE * 4;
357 mega->fileHandle->Seek( tileNum * tileSize, FS_SEEK_SET );
358 memset( data, 128, sizeof( data ) );
359 mega->fileHandle->Read( data, tileSize );
362 if ( idMegaTexture::r_showMegaTextureLabels.GetBool() ) {
363 // put a color marker in it
364 byte color[4] = { 255 * localX / TILE_PER_LEVEL, 255 * localY / TILE_PER_LEVEL, 0, 0 };
365 for ( int x = 0 ; x < 8 ; x++ ) {
366 for ( int y = 0 ; y < 8 ; y++ ) {
367 *(int *)&data[ ( ( y + TILE_SIZE/2 - 4 ) * TILE_SIZE + x + TILE_SIZE/2 - 4 ) * 4 ] = *(int *)color;
372 // upload all the mip-map levels
374 int size = TILE_SIZE;
376 qglTexSubImage2D( GL_TEXTURE_2D, level, localX * size, localY * size, size, size, GL_RGBA, GL_UNSIGNED_BYTE, data );
384 int byteSize = size * 4;
386 for ( int y = 0 ; y < size ; y++ ) {
387 byte *in, *in2, *out;
388 in = data + y * size * 16;
390 out = data + y * size * 4;
391 for ( int x = 0 ; x < size ; x++ ) {
392 out[x*4+0] = ( in[x*8+0] + in[x*8+4+0] + in2[x*8+0] + in2[x*8+4+0] ) >> 2;
393 out[x*4+1] = ( in[x*8+1] + in[x*8+4+1] + in2[x*8+1] + in2[x*8+4+1] ) >> 2;
394 out[x*4+2] = ( in[x*8+2] + in[x*8+4+2] + in2[x*8+2] + in2[x*8+4+2] ) >> 2;
395 out[x*4+3] = ( in[x*8+3] + in[x*8+4+3] + in2[x*8+3] + in2[x*8+4+3] ) >> 2;
405 Center is in the 0.0 to 1.0 range
408 void idTextureLevel::UpdateForCenter( float center[2] ) {
409 int globalTileCorner[2];
410 int localTileOffset[2];
412 if ( tilesWide <= TILE_PER_LEVEL && tilesHigh <= TILE_PER_LEVEL ) {
413 globalTileCorner[0] = 0;
414 globalTileCorner[1] = 0;
415 localTileOffset[0] = 0;
416 localTileOffset[1] = 0;
417 // orient the mask so that it doesn't mask anything at all
422 for ( int i = 0 ; i < 2 ; i++ ) {
425 // this value will be outside the 0.0 to 1.0 range unless
426 // we are in the corner of the megaTexture
427 global[i] = ( center[i] * parms[3] - 0.5 ) * TILE_PER_LEVEL;
429 globalTileCorner[i] = (int)( global[i] + 0.5 );
431 localTileOffset[i] = globalTileCorner[i] & (TILE_PER_LEVEL-1);
433 // scaling for the mask texture to only allow the proper window
434 // of tiles to show through
435 parms[i] = -globalTileCorner[i] / (float)TILE_PER_LEVEL;
441 for ( int x = 0 ; x < TILE_PER_LEVEL ; x++ ) {
442 for ( int y = 0 ; y < TILE_PER_LEVEL ; y++ ) {
445 globalTile[0] = globalTileCorner[0] + ( ( x - localTileOffset[0] ) & (TILE_PER_LEVEL-1) );
446 globalTile[1] = globalTileCorner[1] + ( ( y - localTileOffset[1] ) & (TILE_PER_LEVEL-1) );
448 UpdateTile( x, y, globalTile[0], globalTile[1] );
454 =====================
457 Forces all tiles to be regenerated
458 =====================
460 void idTextureLevel::Invalidate() {
461 for ( int x = 0 ; x < TILE_PER_LEVEL ; x++ ) {
462 for ( int y = 0 ; y < TILE_PER_LEVEL ; y++ ) {
464 tileMap[x][y].y = -99999;
469 //===================================================================================================
472 typedef struct _TargaHeader {
473 unsigned char id_length, colormap_type, image_type;
474 unsigned short colormap_index, colormap_length;
475 unsigned char colormap_size;
476 unsigned short x_origin, y_origin, width, height;
477 unsigned char pixel_size, attributes;
481 static byte ReadByte( idFile *f ) {
488 static short ReadShort( idFile *f ) {
493 return b[0] + ( b[1] << 8 );
502 void idMegaTexture::GenerateMegaMipMaps( megaTextureHeader_t *header, idFile *outFile ) {
505 // out fileSystem doesn't allow read / write access...
506 idFile *inFile = fileSystem->OpenFileRead( outFile->GetName() );
509 int width = header->tilesWide;
510 int height = header->tilesHigh;
512 int tileSize = header->tileSize * header->tileSize * 4;
513 byte *oldBlock = (byte *)_alloca( tileSize );
514 byte *newBlock = (byte *)_alloca( tileSize );
516 while ( width > 1 || height > 1 ) {
517 int newHeight = (height+1) >> 1;
518 if ( newHeight < 1 ) {
521 int newWidth = (width+1) >> 1;
525 common->Printf( "generating %i x %i block mip level\n", newWidth, newHeight );
529 for ( int y = 0 ; y < newHeight ; y++ ) {
530 common->Printf( "row %i\n", y );
531 session->UpdateScreen();
533 for ( int x = 0 ; x < newWidth ; x++ ) {
534 // mip map four original blocks down into a single new block
535 for ( int yy = 0 ; yy < 2 ; yy++ ) {
536 for ( int xx = 0 ; xx< 2 ; xx++ ) {
540 if ( tx > width || ty > height ) {
541 // off edge, zero fill
542 memset( newBlock, 0, sizeof( newBlock ) );
544 tileNum = tileOffset + ty * width + tx;
545 inFile->Seek( tileNum * tileSize, FS_SEEK_SET );
546 inFile->Read( oldBlock, tileSize );
548 // mip map the new pixels
549 for ( int yyy = 0 ; yyy < TILE_SIZE / 2 ; yyy++ ) {
550 for ( int xxx = 0 ; xxx < TILE_SIZE / 2 ; xxx++ ) {
551 byte *in = &oldBlock[ ( yyy * 2 * TILE_SIZE + xxx * 2 ) * 4 ];
552 byte *out = &newBlock[ ( ( ( TILE_SIZE/2 * yy ) + yyy ) * TILE_SIZE + ( TILE_SIZE/2 * xx ) + xxx ) * 4 ];
553 out[0] = ( in[0] + in[4] + in[0+TILE_SIZE*4] + in[4+TILE_SIZE*4] ) >> 2;
554 out[1] = ( in[1] + in[5] + in[1+TILE_SIZE*4] + in[5+TILE_SIZE*4] ) >> 2;
555 out[2] = ( in[2] + in[6] + in[2+TILE_SIZE*4] + in[6+TILE_SIZE*4] ) >> 2;
556 out[3] = ( in[3] + in[7] + in[3+TILE_SIZE*4] + in[7+TILE_SIZE*4] ) >> 2;
560 // write the block out
561 tileNum = tileOffset + width * height + y * newWidth + x;
562 outFile->Seek( tileNum * tileSize, FS_SEEK_SET );
563 outFile->Write( newBlock, tileSize );
569 tileOffset += width * height;
581 Make a 2k x 2k preview image for a mega texture that can be used in modeling programs
584 void idMegaTexture::GenerateMegaPreview( const char *fileName ) {
585 idFile *fileHandle = fileSystem->OpenFileRead( fileName );
587 common->Printf( "idMegaTexture: failed to open %s\n", fileName );
591 idStr outName = fileName;
592 outName.StripFileExtension();
593 outName += "_preview.tga";
595 common->Printf( "Creating %s.\n", outName.c_str() );
597 megaTextureHeader_t header;
599 fileHandle->Read( &header, sizeof( header ) );
600 if ( header.tileSize < 64 || header.tilesWide < 1 || header.tilesHigh < 1 ) {
601 common->Printf( "idMegaTexture: bad header on %s\n", fileName );
605 int tileSize = header.tileSize;
606 int width = header.tilesWide;
607 int height = header.tilesHigh;
609 int tileBytes = tileSize * tileSize * 4;
610 // find the level that fits
611 while ( width * tileSize > 2048 || height * tileSize > 2048 ) {
612 tileOffset += width * height;
623 byte *pic = (byte *)R_StaticAlloc( width * height * tileBytes );
624 byte *oldBlock = (byte *)_alloca( tileBytes );
625 for ( int y = 0 ; y < height ; y++ ) {
626 for ( int x = 0 ; x < width ; x++ ) {
627 int tileNum = tileOffset + y * width + x;
628 fileHandle->Seek( tileNum * tileBytes, FS_SEEK_SET );
629 fileHandle->Read( oldBlock, tileBytes );
631 for ( int yy = 0 ; yy < tileSize ; yy++ ) {
632 memcpy( pic + ( ( y * tileSize + yy ) * width * tileSize + x * tileSize ) * 4,
633 oldBlock + yy * tileSize * 4, tileSize * 4 );
638 R_WriteTGA( outName.c_str(), pic, width * tileSize, height * tileSize, false );
650 Incrementally load a giant tga file and process into the mega texture block format
653 void idMegaTexture::MakeMegaTexture_f( const idCmdArgs &args ) {
654 int columns, rows, fileSize, numBytes;
657 TargaHeader targa_header;
659 if ( args.Argc() != 2 ) {
660 common->Printf( "USAGE: makeMegaTexture <filebase>\n" );
664 idStr name_s = "megaTextures/";
665 name_s += args.Argv(1);
666 name_s.StripFileExtension();
669 const char *name = name_s.c_str();
674 common->Printf( "Opening %s.\n", name );
675 fileSize = fileSystem->ReadFile( name, NULL, NULL );
676 idFile *file = fileSystem->OpenFileRead( name );
679 common->Printf( "Couldn't open %s\n", name );
683 targa_header.id_length = ReadByte( file );
684 targa_header.colormap_type = ReadByte( file );
685 targa_header.image_type = ReadByte( file );
687 targa_header.colormap_index = ReadShort( file );
688 targa_header.colormap_length = ReadShort( file );
689 targa_header.colormap_size = ReadByte( file );
690 targa_header.x_origin = ReadShort( file );
691 targa_header.y_origin = ReadShort( file );
692 targa_header.width = ReadShort( file );
693 targa_header.height = ReadShort( file );
694 targa_header.pixel_size = ReadByte( file );
695 targa_header.attributes = ReadByte( file );
697 if ( targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3 ) {
698 common->Error( "LoadTGA( %s ): Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n", name );
701 if ( targa_header.colormap_type != 0 ) {
702 common->Error( "LoadTGA( %s ): colormaps not supported\n", name );
705 if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 ) {
706 common->Error( "LoadTGA( %s ): Only 32 or 24 bit images supported (no colormaps)\n", name );
709 if ( targa_header.image_type == 2 || targa_header.image_type == 3 ) {
710 numBytes = targa_header.width * targa_header.height * ( targa_header.pixel_size >> 3 );
711 if ( numBytes > fileSize - 18 - targa_header.id_length ) {
712 common->Error( "LoadTGA( %s ): incomplete file\n", name );
716 columns = targa_header.width;
717 rows = targa_header.height;
719 // skip TARGA image comment
720 if ( targa_header.id_length != 0 ) {
721 file->Seek( targa_header.id_length, FS_SEEK_CUR );
724 megaTextureHeader_t mtHeader;
726 mtHeader.tileSize = TILE_SIZE;
727 mtHeader.tilesWide = RoundDownToPowerOfTwo( targa_header.width ) / TILE_SIZE;
728 mtHeader.tilesHigh = RoundDownToPowerOfTwo( targa_header.height ) / TILE_SIZE;
730 idStr outName = name;
731 outName.StripFileExtension();
734 common->Printf( "Writing %i x %i size %i tiles to %s.\n",
735 mtHeader.tilesWide, mtHeader.tilesHigh, mtHeader.tileSize, outName.c_str() );
737 // open the output megatexture file
738 idFile *out = fileSystem->OpenFileWrite( outName.c_str() );
740 out->Write( &mtHeader, sizeof( mtHeader ) );
741 out->Seek( TILE_SIZE * TILE_SIZE * 4, FS_SEEK_SET );
743 // we will process this one row of tiles at a time, since the entire thing
744 // won't fit in memory
745 byte *targa_rgba = (byte *)R_StaticAlloc( TILE_SIZE * targa_header.width * 4 );
747 int blockRowsRemaining = mtHeader.tilesHigh;
748 while ( blockRowsRemaining-- ) {
749 common->Printf( "%i blockRowsRemaining\n", blockRowsRemaining );
750 session->UpdateScreen();
752 if ( targa_header.image_type == 2 || targa_header.image_type == 3 ) {
753 // Uncompressed RGB or gray scale image
754 for( row = 0 ; row < TILE_SIZE ; row++ ) {
755 pixbuf = targa_rgba + row*columns*4;
756 for( column = 0; column < columns; column++) {
757 unsigned char red,green,blue,alphabyte;
758 switch( targa_header.pixel_size ) {
760 blue = ReadByte( file );
770 blue = ReadByte( file );
771 green = ReadByte( file );
772 red = ReadByte( file );
779 blue = ReadByte( file );
780 green = ReadByte( file );
781 red = ReadByte( file );
782 alphabyte = ReadByte( file );
786 *pixbuf++ = alphabyte;
789 common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
794 } else if ( targa_header.image_type == 10 ) { // Runlength encoded RGB images
795 unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
802 for( row = 0 ; row < TILE_SIZE ; row++ ) {
803 pixbuf = targa_rgba + row*columns*4;
804 for( column = 0; column < columns; ) {
805 packetHeader= ReadByte( file );
806 packetSize = 1 + (packetHeader & 0x7f);
807 if ( packetHeader & 0x80 ) { // run-length packet
808 switch( targa_header.pixel_size ) {
810 blue = ReadByte( file );
811 green = ReadByte( file );
812 red = ReadByte( file );
816 blue = ReadByte( file );
817 green = ReadByte( file );
818 red = ReadByte( file );
819 alphabyte = ReadByte( file );
822 common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
826 for( j = 0; j < packetSize; j++ ) {
832 if ( column == columns ) { // run spans across rows
833 common->Error( "TGA had RLE across columns, probably breaks block" );
841 pixbuf = targa_rgba + row*columns*4;
844 } else { // non run-length packet
845 for( j = 0; j < packetSize; j++ ) {
846 switch( targa_header.pixel_size ) {
848 blue = ReadByte( file );
849 green = ReadByte( file );
850 red = ReadByte( file );
857 blue = ReadByte( file );
858 green = ReadByte( file );
859 red = ReadByte( file );
860 alphabyte = ReadByte( file );
864 *pixbuf++ = alphabyte;
867 common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
871 if ( column == columns ) { // pixel packet run spans across rows
879 pixbuf = targa_rgba + row*columns*4;
889 // write out individual blocks from the full row block buffer
891 for ( int rowBlock = 0 ; rowBlock < mtHeader.tilesWide ; rowBlock++ ) {
892 for ( int y = 0 ; y < TILE_SIZE ; y++ ) {
893 out->Write( targa_rgba + ( y * targa_header.width + rowBlock * TILE_SIZE ) * 4, TILE_SIZE * 4 );
898 R_StaticFree( targa_rgba );
900 GenerateMegaMipMaps( &mtHeader, out );
905 GenerateMegaPreview( outName.c_str() );
907 if ( (targa_header.attributes & (1<<5)) ) { // image flp bit
908 R_VerticalFlip( *pic, *width, *height );