12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
- 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.
- 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.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "tr_local.h"
- /*
- PROBLEM: compressed textures may break the zero clamp rule!
- */
- static bool FormatIsDXT( int internalFormat ) {
- if ( internalFormat < GL_COMPRESSED_RGB_S3TC_DXT1_EXT
- || internalFormat > GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ) {
- return false;
- }
- return true;
- }
- int MakePowerOfTwo( int num ) {
- int pot;
- for (pot = 1 ; pot < num ; pot<<=1) {
- }
- return pot;
- }
- /*
- ================
- BitsForInternalFormat
- Used for determining memory utilization
- ================
- */
- int idImage::BitsForInternalFormat( int internalFormat ) const {
- switch ( internalFormat ) {
- case GL_INTENSITY8:
- case 1:
- return 8;
- case 2:
- case GL_LUMINANCE8_ALPHA8:
- return 16;
- case 3:
- return 32; // on some future hardware, this may actually be 24, but be conservative
- case 4:
- return 32;
- case GL_LUMINANCE8:
- return 8;
- case GL_ALPHA8:
- return 8;
- case GL_RGBA8:
- return 32;
- case GL_RGB8:
- return 32; // on some future hardware, this may actually be 24, but be conservative
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- return 4;
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- return 4;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- return 8;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- return 8;
- case GL_RGBA4:
- return 16;
- case GL_RGB5:
- return 16;
- case GL_COLOR_INDEX8_EXT:
- return 8;
- case GL_COLOR_INDEX:
- return 8;
- case GL_COMPRESSED_RGB_ARB:
- return 4; // not sure
- case GL_COMPRESSED_RGBA_ARB:
- return 8; // not sure
- default:
- common->Error( "R_BitsForInternalFormat: BAD FORMAT:%i", internalFormat );
- }
- return 0;
- }
- /*
- ==================
- UploadCompressedNormalMap
- Create a 256 color palette to be used by compressed normal maps
- ==================
- */
- void idImage::UploadCompressedNormalMap( int width, int height, const byte *rgba, int mipLevel ) {
- byte *normals;
- const byte *in;
- byte *out;
- int i, j;
- int x, y, z;
- int row;
- // OpenGL's pixel packing rule
- row = width < 4 ? 4 : width;
- normals = (byte *)_alloca( row * height );
- if ( !normals ) {
- common->Error( "R_UploadCompressedNormalMap: _alloca failed" );
- }
- in = rgba;
- out = normals;
- for ( i = 0 ; i < height ; i++, out += row, in += width * 4 ) {
- for ( j = 0 ; j < width ; j++ ) {
- x = in[ j * 4 + 0 ];
- y = in[ j * 4 + 1 ];
- z = in[ j * 4 + 2 ];
- int c;
- if ( x == 128 && y == 128 && z == 128 ) {
- // the "nullnormal" color
- c = 255;
- } else {
- c = ( globalImages->originalToCompressed[x] << 4 ) | globalImages->originalToCompressed[y];
- if ( c == 255 ) {
- c = 254; // don't use the nullnormal color
- }
- }
- out[j] = c;
- }
- }
- if ( mipLevel == 0 ) {
- // Optionally write out the paletized normal map to a .tga
- if ( globalImages->image_writeNormalTGAPalletized.GetBool() ) {
- char filename[MAX_IMAGE_NAME];
- ImageProgramStringToCompressedFileName( imgName, filename );
- char *ext = strrchr(filename, '.');
- if ( ext ) {
- strcpy(ext, "_pal.tga");
- R_WritePalTGA( filename, normals, globalImages->compressedPalette, width, height);
- }
- }
- }
- if ( glConfig.sharedTexturePaletteAvailable ) {
- qglTexImage2D( GL_TEXTURE_2D,
- mipLevel,
- GL_COLOR_INDEX8_EXT,
- width,
- height,
- 0,
- GL_COLOR_INDEX,
- GL_UNSIGNED_BYTE,
- normals );
- }
- }
- //=======================================================================
- static byte mipBlendColors[16][4] = {
- {0,0,0,0},
- {255,0,0,128},
- {0,255,0,128},
- {0,0,255,128},
- {255,0,0,128},
- {0,255,0,128},
- {0,0,255,128},
- {255,0,0,128},
- {0,255,0,128},
- {0,0,255,128},
- {255,0,0,128},
- {0,255,0,128},
- {0,0,255,128},
- {255,0,0,128},
- {0,255,0,128},
- {0,0,255,128},
- };
- /*
- ===============
- SelectInternalFormat
- This may need to scan six cube map images
- ===============
- */
- GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, int width, int height,
- textureDepth_t minimumDepth, bool *monochromeResult ) const {
- int i, c;
- const byte *scan;
- int rgbOr, rgbAnd, aOr, aAnd;
- int rgbDiffer, rgbaDiffer;
- // determine if the rgb channels are all the same
- // and if either all rgb or all alpha are 255
- c = width*height;
- rgbDiffer = 0;
- rgbaDiffer = 0;
- rgbOr = 0;
- rgbAnd = -1;
- aOr = 0;
- aAnd = -1;
- *monochromeResult = true; // until shown otherwise
- for ( int side = 0 ; side < numDataPtrs ; side++ ) {
- scan = dataPtrs[side];
- for ( i = 0; i < c; i++, scan += 4 ) {
- int cor, cand;
- aOr |= scan[3];
- aAnd &= scan[3];
- cor = scan[0] | scan[1] | scan[2];
- cand = scan[0] & scan[1] & scan[2];
-
- // if rgb are all the same, the or and and will match
- rgbDiffer |= ( cor ^ cand );
- // our "isMonochrome" test is more lax than rgbDiffer,
- // allowing the values to be off by several units and
- // still use the NV20 mono path
- if ( *monochromeResult ) {
- if ( abs( scan[0] - scan[1] ) > 16
- || abs( scan[0] - scan[2] ) > 16 ) {
- *monochromeResult = false;
- }
- }
- rgbOr |= cor;
- rgbAnd &= cand;
- cor |= scan[3];
- cand &= scan[3];
- rgbaDiffer |= ( cor ^ cand );
- }
- }
- // we assume that all 0 implies that the alpha channel isn't needed,
- // because some tools will spit out 32 bit images with a 0 alpha instead
- // of 255 alpha, but if the alpha actually is referenced, there will be
- // different behavior in the compressed vs uncompressed states.
- bool needAlpha;
- if ( aAnd == 255 || aOr == 0 ) {
- needAlpha = false;
- } else {
- needAlpha = true;
- }
- // catch normal maps first
- if ( minimumDepth == TD_BUMP ) {
- if ( globalImages->image_useCompression.GetBool() && globalImages->image_useNormalCompression.GetInteger() == 1 && glConfig.sharedTexturePaletteAvailable ) {
- // image_useNormalCompression should only be set to 1 on nv_10 and nv_20 paths
- return GL_COLOR_INDEX8_EXT;
- } else if ( globalImages->image_useCompression.GetBool() && globalImages->image_useNormalCompression.GetInteger() && glConfig.textureCompressionAvailable ) {
- // image_useNormalCompression == 2 uses rxgb format which produces really good quality for medium settings
- return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- } else {
- // we always need the alpha channel for bump maps for swizzling
- return GL_RGBA8;
- }
- }
- // allow a complete override of image compression with a cvar
- if ( !globalImages->image_useCompression.GetBool() ) {
- minimumDepth = TD_HIGH_QUALITY;
- }
- if ( minimumDepth == TD_SPECULAR ) {
- // we are assuming that any alpha channel is unintentional
- if ( glConfig.textureCompressionAvailable ) {
- return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
- } else {
- return GL_RGB5;
- }
- }
- if ( minimumDepth == TD_DIFFUSE ) {
- // we might intentionally have an alpha channel for alpha tested textures
- if ( glConfig.textureCompressionAvailable ) {
- if ( !needAlpha ) {
- return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
- } else {
- return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
- }
- } else if ( ( aAnd == 255 || aOr == 0 ) ) {
- return GL_RGB5;
- } else {
- return GL_RGBA4;
- }
- }
- // there will probably be some drivers that don't
- // correctly handle the intensity/alpha/luminance/luminance+alpha
- // formats, so provide a fallback that only uses the rgb/rgba formats
- if ( !globalImages->image_useAllFormats.GetBool() ) {
- // pretend rgb is varying and inconsistant, which
- // prevents any of the more compact forms
- rgbDiffer = 1;
- rgbaDiffer = 1;
- rgbAnd = 0;
- }
- // cases without alpha
- if ( !needAlpha ) {
- if ( minimumDepth == TD_HIGH_QUALITY ) {
- return GL_RGB8; // four bytes
- }
- if ( glConfig.textureCompressionAvailable ) {
- return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; // half byte
- }
- return GL_RGB5; // two bytes
- }
- // cases with alpha
- if ( !rgbaDiffer ) {
- if ( minimumDepth != TD_HIGH_QUALITY && glConfig.textureCompressionAvailable ) {
- return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
- }
- return GL_INTENSITY8; // single byte for all channels
- }
- #if 0
- // we don't support alpha textures any more, because there
- // is a discrepancy in the definition of TEX_ENV_COMBINE that
- // causes them to be treated as 0 0 0 A, instead of 1 1 1 A as
- // normal texture modulation treats them
- if ( rgbAnd == 255 ) {
- return GL_ALPHA8; // single byte, only alpha
- }
- #endif
- if ( minimumDepth == TD_HIGH_QUALITY ) {
- return GL_RGBA8; // four bytes
- }
- if ( glConfig.textureCompressionAvailable ) {
- return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
- }
- if ( !rgbDiffer ) {
- return GL_LUMINANCE8_ALPHA8; // two bytes, max quality
- }
- return GL_RGBA4; // two bytes
- }
- /*
- ==================
- SetImageFilterAndRepeat
- ==================
- */
- void idImage::SetImageFilterAndRepeat() const {
- // set the minimize / maximize filtering
- switch( filter ) {
- case TF_DEFAULT:
- qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
- qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
- break;
- case TF_LINEAR:
- qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
- break;
- case TF_NEAREST:
- qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
- qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
- break;
- default:
- common->FatalError( "R_CreateImage: bad texture filter" );
- }
- if ( glConfig.anisotropicAvailable ) {
- // only do aniso filtering on mip mapped images
- if ( filter == TF_DEFAULT ) {
- qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, globalImages->textureAnisotropy );
- } else {
- qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
- }
- }
- if ( glConfig.textureLODBiasAvailable ) {
- qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS_EXT, globalImages->textureLODBias );
- }
- // set the wrap/clamp modes
- switch( repeat ) {
- case TR_REPEAT:
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
- break;
- case TR_CLAMP_TO_BORDER:
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
- break;
- case TR_CLAMP_TO_ZERO:
- case TR_CLAMP_TO_ZERO_ALPHA:
- case TR_CLAMP:
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
- break;
- default:
- common->FatalError( "R_CreateImage: bad texture repeat" );
- }
- }
- /*
- ================
- idImage::Downsize
- helper function that takes the current width/height and might make them smaller
- ================
- */
- void idImage::GetDownsize( int &scaled_width, int &scaled_height ) const {
- int size = 0;
- // perform optional picmip operation to save texture memory
- if ( depth == TD_SPECULAR && globalImages->image_downSizeSpecular.GetInteger() ) {
- size = globalImages->image_downSizeSpecularLimit.GetInteger();
- if ( size == 0 ) {
- size = 64;
- }
- } else if ( depth == TD_BUMP && globalImages->image_downSizeBump.GetInteger() ) {
- size = globalImages->image_downSizeBumpLimit.GetInteger();
- if ( size == 0 ) {
- size = 64;
- }
- } else if ( ( allowDownSize || globalImages->image_forceDownSize.GetBool() ) && globalImages->image_downSize.GetInteger() ) {
- size = globalImages->image_downSizeLimit.GetInteger();
- if ( size == 0 ) {
- size = 256;
- }
- }
- if ( size > 0 ) {
- while ( scaled_width > size || scaled_height > size ) {
- if ( scaled_width > 1 ) {
- scaled_width >>= 1;
- }
- if ( scaled_height > 1 ) {
- scaled_height >>= 1;
- }
- }
- }
- // clamp to minimum size
- if ( scaled_width < 1 ) {
- scaled_width = 1;
- }
- if ( scaled_height < 1 ) {
- scaled_height = 1;
- }
- // clamp size to the hardware specific upper limit
- // scale both axis down equally so we don't have to
- // deal with a half mip resampling
- // This causes a 512*256 texture to sample down to
- // 256*128 on a voodoo3, even though it could be 256*256
- while ( scaled_width > glConfig.maxTextureSize
- || scaled_height > glConfig.maxTextureSize ) {
- scaled_width >>= 1;
- scaled_height >>= 1;
- }
- }
- /*
- ================
- GenerateImage
- The alpha channel bytes should be 255 if you don't
- want the channel.
- We need a material characteristic to ask for specific texture modes.
- Designed limitations of flexibility:
- No support for texture borders.
- No support for texture border color.
- No support for texture environment colors or GL_BLEND or GL_DECAL
- texture environments, because the automatic optimization to single
- or dual component textures makes those modes potentially undefined.
- No non-power-of-two images.
- No palettized textures.
- There is no way to specify separate wrap/clamp values for S and T
- There is no way to specify explicit mip map levels
- ================
- */
- void idImage::GenerateImage( const byte *pic, int width, int height,
- textureFilter_t filterParm, bool allowDownSizeParm,
- textureRepeat_t repeatParm, textureDepth_t depthParm ) {
- bool preserveBorder;
- byte *scaledBuffer;
- int scaled_width, scaled_height;
- byte *shrunk;
- PurgeImage();
- filter = filterParm;
- allowDownSize = allowDownSizeParm;
- repeat = repeatParm;
- depth = depthParm;
- // if we don't have a rendering context, just return after we
- // have filled in the parms. We must have the values set, or
- // an image match from a shader before OpenGL starts would miss
- // the generated texture
- if ( !glConfig.isInitialized ) {
- return;
- }
- // don't let mip mapping smear the texture into the clamped border
- if ( repeat == TR_CLAMP_TO_ZERO ) {
- preserveBorder = true;
- } else {
- preserveBorder = false;
- }
- // make sure it is a power of 2
- scaled_width = MakePowerOfTwo( width );
- scaled_height = MakePowerOfTwo( height );
- if ( scaled_width != width || scaled_height != height ) {
- common->Error( "R_CreateImage: not a power of 2 image" );
- }
- // Optionally modify our width/height based on options/hardware
- GetDownsize( scaled_width, scaled_height );
- scaledBuffer = NULL;
- // generate the texture number
- qglGenTextures( 1, &texnum );
- // select proper internal format before we resample
- internalFormat = SelectInternalFormat( &pic, 1, width, height, depth, &isMonochrome );
- // copy or resample data as appropriate for first MIP level
- if ( ( scaled_width == width ) && ( scaled_height == height ) ) {
- // we must copy even if unchanged, because the border zeroing
- // would otherwise modify const data
- scaledBuffer = (byte *)R_StaticAlloc( sizeof( unsigned ) * scaled_width * scaled_height );
- memcpy (scaledBuffer, pic, width*height*4);
- } else {
- // resample down as needed (FIXME: this doesn't seem like it resamples anymore!)
- // scaledBuffer = R_ResampleTexture( pic, width, height, width >>= 1, height >>= 1 );
- scaledBuffer = R_MipMap( pic, width, height, preserveBorder );
- width >>= 1;
- height >>= 1;
- if ( width < 1 ) {
- width = 1;
- }
- if ( height < 1 ) {
- height = 1;
- }
- while ( width > scaled_width || height > scaled_height ) {
- shrunk = R_MipMap( scaledBuffer, width, height, preserveBorder );
- R_StaticFree( scaledBuffer );
- scaledBuffer = shrunk;
- width >>= 1;
- height >>= 1;
- if ( width < 1 ) {
- width = 1;
- }
- if ( height < 1 ) {
- height = 1;
- }
- }
- // one might have shrunk down below the target size
- scaled_width = width;
- scaled_height = height;
- }
- uploadHeight = scaled_height;
- uploadWidth = scaled_width;
- type = TT_2D;
- // zero the border if desired, allowing clamped projection textures
- // even after picmip resampling or careless artists.
- if ( repeat == TR_CLAMP_TO_ZERO ) {
- byte rgba[4];
- rgba[0] = rgba[1] = rgba[2] = 0;
- rgba[3] = 255;
- R_SetBorderTexels( (byte *)scaledBuffer, width, height, rgba );
- }
- if ( repeat == TR_CLAMP_TO_ZERO_ALPHA ) {
- byte rgba[4];
- rgba[0] = rgba[1] = rgba[2] = 255;
- rgba[3] = 0;
- R_SetBorderTexels( (byte *)scaledBuffer, width, height, rgba );
- }
- if ( generatorFunction == NULL && ( depth == TD_BUMP && globalImages->image_writeNormalTGA.GetBool() || depth != TD_BUMP && globalImages->image_writeTGA.GetBool() ) ) {
- // Optionally write out the texture to a .tga
- char filename[MAX_IMAGE_NAME];
- ImageProgramStringToCompressedFileName( imgName, filename );
- char *ext = strrchr(filename, '.');
- if ( ext ) {
- strcpy( ext, ".tga" );
- // swap the red/alpha for the write
- /*
- if ( depth == TD_BUMP ) {
- for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
- scaledBuffer[ i ] = scaledBuffer[ i + 3 ];
- scaledBuffer[ i + 3 ] = 0;
- }
- }
- */
- R_WriteTGA( filename, scaledBuffer, scaled_width, scaled_height, false );
- // put it back
- /*
- if ( depth == TD_BUMP ) {
- for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
- scaledBuffer[ i + 3 ] = scaledBuffer[ i ];
- scaledBuffer[ i ] = 0;
- }
- }
- */
- }
- }
- // swap the red and alpha for rxgb support
- // do this even on tga normal maps so we only have to use
- // one fragment program
- // if the image is precompressed ( either in palletized mode or true rxgb mode )
- // then it is loaded above and the swap never happens here
- if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 1 ) {
- for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
- scaledBuffer[ i + 3 ] = scaledBuffer[ i ];
- scaledBuffer[ i ] = 0;
- }
- }
- // upload the main image level
- Bind();
- if ( internalFormat == GL_COLOR_INDEX8_EXT ) {
- /*
- if ( depth == TD_BUMP ) {
- for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
- scaledBuffer[ i ] = scaledBuffer[ i + 3 ];
- scaledBuffer[ i + 3 ] = 0;
- }
- }
- */
- UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, 0 );
- } else {
- qglTexImage2D( GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
- }
- // create and upload the mip map levels, which we do in all cases, even if we don't think they are needed
- int miplevel;
- miplevel = 0;
- while ( scaled_width > 1 || scaled_height > 1 ) {
- // preserve the border after mip map unless repeating
- shrunk = R_MipMap( scaledBuffer, scaled_width, scaled_height, preserveBorder );
- R_StaticFree( scaledBuffer );
- scaledBuffer = shrunk;
- scaled_width >>= 1;
- scaled_height >>= 1;
- if ( scaled_width < 1 ) {
- scaled_width = 1;
- }
- if ( scaled_height < 1 ) {
- scaled_height = 1;
- }
- miplevel++;
- // this is a visualization tool that shades each mip map
- // level with a different color so you can see the
- // rasterizer's texture level selection algorithm
- // Changing the color doesn't help with lumminance/alpha/intensity formats...
- if ( depth == TD_DIFFUSE && globalImages->image_colorMipLevels.GetBool() ) {
- R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] );
- }
- // upload the mip map
- if ( internalFormat == GL_COLOR_INDEX8_EXT ) {
- UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, miplevel );
- } else {
- qglTexImage2D( GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
- }
- }
- if ( scaledBuffer != 0 ) {
- R_StaticFree( scaledBuffer );
- }
- SetImageFilterAndRepeat();
- // see if we messed anything up
- GL_CheckErrors();
- }
- /*
- ==================
- Generate3DImage
- ==================
- */
- void idImage::Generate3DImage( const byte *pic, int width, int height, int picDepth,
- textureFilter_t filterParm, bool allowDownSizeParm,
- textureRepeat_t repeatParm, textureDepth_t minDepthParm ) {
- int scaled_width, scaled_height, scaled_depth;
- PurgeImage();
- filter = filterParm;
- allowDownSize = allowDownSizeParm;
- repeat = repeatParm;
- depth = minDepthParm;
- // if we don't have a rendering context, just return after we
- // have filled in the parms. We must have the values set, or
- // an image match from a shader before OpenGL starts would miss
- // the generated texture
- if ( !glConfig.isInitialized ) {
- return;
- }
- // make sure it is a power of 2
- scaled_width = MakePowerOfTwo( width );
- scaled_height = MakePowerOfTwo( height );
- scaled_depth = MakePowerOfTwo( picDepth );
- if ( scaled_width != width || scaled_height != height || scaled_depth != picDepth ) {
- common->Error( "R_Create3DImage: not a power of 2 image" );
- }
- // FIXME: allow picmip here
- // generate the texture number
- qglGenTextures( 1, &texnum );
- // select proper internal format before we resample
- // this function doesn't need to know it is 3D, so just make it very "tall"
- internalFormat = SelectInternalFormat( &pic, 1, width, height * picDepth, minDepthParm, &isMonochrome );
- uploadHeight = scaled_height;
- uploadWidth = scaled_width;
- uploadDepth = scaled_depth;
- type = TT_3D;
- // upload the main image level
- Bind();
- qglTexImage3D(GL_TEXTURE_3D, 0, internalFormat, scaled_width, scaled_height, scaled_depth,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, pic );
- // create and upload the mip map levels
- int miplevel;
- byte *scaledBuffer, *shrunk;
- scaledBuffer = (byte *)R_StaticAlloc( scaled_width * scaled_height * scaled_depth * 4 );
- memcpy( scaledBuffer, pic, scaled_width * scaled_height * scaled_depth * 4 );
- miplevel = 0;
- while ( scaled_width > 1 || scaled_height > 1 || scaled_depth > 1 ) {
- // preserve the border after mip map unless repeating
- shrunk = R_MipMap3D( scaledBuffer, scaled_width, scaled_height, scaled_depth,
- (bool)(repeat != TR_REPEAT) );
- R_StaticFree( scaledBuffer );
- scaledBuffer = shrunk;
- scaled_width >>= 1;
- scaled_height >>= 1;
- scaled_depth >>= 1;
- if ( scaled_width < 1 ) {
- scaled_width = 1;
- }
- if ( scaled_height < 1 ) {
- scaled_height = 1;
- }
- if ( scaled_depth < 1 ) {
- scaled_depth = 1;
- }
- miplevel++;
- // upload the mip map
- qglTexImage3D(GL_TEXTURE_3D, miplevel, internalFormat, scaled_width, scaled_height, scaled_depth,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
- }
- R_StaticFree( scaledBuffer );
- // set the minimize / maximize filtering
- switch( filter ) {
- case TF_DEFAULT:
- qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
- qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
- break;
- case TF_LINEAR:
- qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
- break;
- case TF_NEAREST:
- qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
- qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
- break;
- default:
- common->FatalError( "R_CreateImage: bad texture filter" );
- }
- // set the wrap/clamp modes
- switch( repeat ) {
- case TR_REPEAT:
- qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT );
- qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT );
- qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT );
- break;
- case TR_CLAMP_TO_BORDER:
- qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
- qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
- break;
- case TR_CLAMP_TO_ZERO:
- case TR_CLAMP_TO_ZERO_ALPHA:
- case TR_CLAMP:
- qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
- qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
- qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
- break;
- default:
- common->FatalError( "R_CreateImage: bad texture repeat" );
- }
- // see if we messed anything up
- GL_CheckErrors();
- }
- /*
- ====================
- GenerateCubeImage
- Non-square cube sides are not allowed
- ====================
- */
- void idImage::GenerateCubeImage( const byte *pic[6], int size,
- textureFilter_t filterParm, bool allowDownSizeParm,
- textureDepth_t depthParm ) {
- int scaled_width, scaled_height;
- int width, height;
- int i;
- PurgeImage();
- filter = filterParm;
- allowDownSize = allowDownSizeParm;
- depth = depthParm;
- type = TT_CUBIC;
- // if we don't have a rendering context, just return after we
- // have filled in the parms. We must have the values set, or
- // an image match from a shader before OpenGL starts would miss
- // the generated texture
- if ( !glConfig.isInitialized ) {
- return;
- }
- if ( ! glConfig.cubeMapAvailable ) {
- return;
- }
- width = height = size;
- // generate the texture number
- qglGenTextures( 1, &texnum );
- // select proper internal format before we resample
- internalFormat = SelectInternalFormat( pic, 6, width, height, depth, &isMonochrome );
- // don't bother with downsample for now
- scaled_width = width;
- scaled_height = height;
- uploadHeight = scaled_height;
- uploadWidth = scaled_width;
- Bind();
- // no other clamp mode makes sense
- qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- // set the minimize / maximize filtering
- switch( filter ) {
- case TF_DEFAULT:
- qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
- qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
- break;
- case TF_LINEAR:
- qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
- break;
- case TF_NEAREST:
- qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
- qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
- break;
- default:
- common->FatalError( "R_CreateImage: bad texture filter" );
- }
- // upload the base level
- // FIXME: support GL_COLOR_INDEX8_EXT?
- for ( i = 0 ; i < 6 ; i++ ) {
- qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, internalFormat, scaled_width, scaled_height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, pic[i] );
- }
- // create and upload the mip map levels
- int miplevel;
- byte *shrunk[6];
- for ( i = 0 ; i < 6 ; i++ ) {
- shrunk[i] = R_MipMap( pic[i], scaled_width, scaled_height, false );
- }
- miplevel = 1;
- while ( scaled_width > 1 ) {
- for ( i = 0 ; i < 6 ; i++ ) {
- byte *shrunken;
- qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, miplevel, internalFormat,
- scaled_width / 2, scaled_height / 2, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, shrunk[i] );
- if ( scaled_width > 2 ) {
- shrunken = R_MipMap( shrunk[i], scaled_width/2, scaled_height/2, false );
- } else {
- shrunken = NULL;
- }
- R_StaticFree( shrunk[i] );
- shrunk[i] = shrunken;
- }
- scaled_width >>= 1;
- scaled_height >>= 1;
- miplevel++;
- }
- // see if we messed anything up
- GL_CheckErrors();
- }
- /*
- ================
- ImageProgramStringToFileCompressedFileName
- ================
- */
- void idImage::ImageProgramStringToCompressedFileName( const char *imageProg, char *fileName ) const {
- const char *s;
- char *f;
- strcpy( fileName, "dds/" );
- f = fileName + strlen( fileName );
- int depth = 0;
- // convert all illegal characters to underscores
- // this could conceivably produce a duplicated mapping, but we aren't going to worry about it
- for ( s = imageProg ; *s ; s++ ) {
- if ( *s == '/' || *s == '\\' || *s == '(') {
- if ( depth < 4 ) {
- *f = '/';
- depth ++;
- } else {
- *f = ' ';
- }
- f++;
- } else if ( *s == '<' || *s == '>' || *s == ':' || *s == '|' || *s == '"' || *s == '.' ) {
- *f = '_';
- f++;
- } else if ( *s == ' ' && *(f-1) == '/' ) { // ignore a space right after a slash
- } else if ( *s == ')' || *s == ',' ) { // always ignore these
- } else {
- *f = *s;
- f++;
- }
- }
- *f++ = 0;
- strcat( fileName, ".dds" );
- }
- /*
- ==================
- NumLevelsForImageSize
- ==================
- */
- int idImage::NumLevelsForImageSize( int width, int height ) const {
- int numLevels = 1;
- while ( width > 1 || height > 1 ) {
- numLevels++;
- width >>= 1;
- height >>= 1;
- }
- return numLevels;
- }
- /*
- ================
- WritePrecompressedImage
- When we are happy with our source data, we can write out precompressed
- versions of everything to speed future load times.
- ================
- */
- void idImage::WritePrecompressedImage() {
- // Always write the precompressed image if we're making a build
- if ( !com_makingBuild.GetBool() ) {
- if ( !globalImages->image_writePrecompressedTextures.GetBool() || !globalImages->image_usePrecompressedTextures.GetBool() ) {
- return;
- }
- }
- if ( !glConfig.isInitialized ) {
- return;
- }
- char filename[MAX_IMAGE_NAME];
- ImageProgramStringToCompressedFileName( imgName, filename );
- int numLevels = NumLevelsForImageSize( uploadWidth, uploadHeight );
- if ( numLevels > MAX_TEXTURE_LEVELS ) {
- common->Warning( "R_WritePrecompressedImage: level > MAX_TEXTURE_LEVELS for image %s", filename );
- return;
- }
- // glGetTexImage only supports a small subset of all the available internal formats
- // We have to use BGRA because DDS is a windows based format
- int altInternalFormat = 0;
- int bitSize = 0;
- switch ( internalFormat ) {
- case GL_COLOR_INDEX8_EXT:
- case GL_COLOR_INDEX:
- // this will not work with dds viewers but we need it in this format to save disk
- // load speed ( i.e. size )
- altInternalFormat = GL_COLOR_INDEX;
- bitSize = 24;
- break;
- case 1:
- case GL_INTENSITY8:
- case GL_LUMINANCE8:
- case 3:
- case GL_RGB8:
- altInternalFormat = GL_BGR_EXT;
- bitSize = 24;
- break;
- case GL_LUMINANCE8_ALPHA8:
- case 4:
- case GL_RGBA8:
- altInternalFormat = GL_BGRA_EXT;
- bitSize = 32;
- break;
- case GL_ALPHA8:
- altInternalFormat = GL_ALPHA;
- bitSize = 8;
- break;
- default:
- if ( FormatIsDXT( internalFormat ) ) {
- altInternalFormat = internalFormat;
- } else {
- common->Warning("Unknown or unsupported format for %s", filename);
- return;
- }
- }
- if ( globalImages->image_useOffLineCompression.GetBool() && FormatIsDXT( altInternalFormat ) ) {
- idStr outFile = fileSystem->RelativePathToOSPath( filename, "fs_basepath" );
- idStr inFile = outFile;
- inFile.StripFileExtension();
- inFile.SetFileExtension( "tga" );
- idStr format;
- if ( depth == TD_BUMP ) {
- format = "RXGB +red 0.0 +green 0.5 +blue 0.5";
- } else {
- switch ( altInternalFormat ) {
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- format = "DXT1";
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- format = "DXT1 -alpha_threshold";
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- format = "DXT3";
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- format = "DXT5";
- break;
- }
- }
- globalImages->AddDDSCommand( va( "z:/d3xp/compressonator/thecompressonator -convert \"%s\" \"%s\" %s -mipmaps\n", inFile.c_str(), outFile.c_str(), format.c_str() ) );
- return;
- }
- ddsFileHeader_t header;
- memset( &header, 0, sizeof(header) );
- header.dwSize = sizeof(header);
- header.dwFlags = DDSF_CAPS | DDSF_PIXELFORMAT | DDSF_WIDTH | DDSF_HEIGHT;
- header.dwHeight = uploadHeight;
- header.dwWidth = uploadWidth;
- // hack in our monochrome flag for the NV20 optimization
- if ( isMonochrome ) {
- header.dwFlags |= DDSF_ID_MONOCHROME;
- }
- if ( FormatIsDXT( altInternalFormat ) ) {
- // size (in bytes) of the compressed base image
- header.dwFlags |= DDSF_LINEARSIZE;
- header.dwPitchOrLinearSize = ( ( uploadWidth + 3 ) / 4 ) * ( ( uploadHeight + 3 ) / 4 )*
- (altInternalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
- }
- else {
- // 4 Byte aligned line width (from nv_dds)
- header.dwFlags |= DDSF_PITCH;
- header.dwPitchOrLinearSize = ( ( uploadWidth * bitSize + 31 ) & -32 ) >> 3;
- }
- header.dwCaps1 = DDSF_TEXTURE;
- if ( numLevels > 1 ) {
- header.dwMipMapCount = numLevels;
- header.dwFlags |= DDSF_MIPMAPCOUNT;
- header.dwCaps1 |= DDSF_MIPMAP | DDSF_COMPLEX;
- }
- header.ddspf.dwSize = sizeof(header.ddspf);
- if ( FormatIsDXT( altInternalFormat ) ) {
- header.ddspf.dwFlags = DDSF_FOURCC;
- switch ( altInternalFormat ) {
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','1');
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- header.ddspf.dwFlags |= DDSF_ALPHAPIXELS;
- header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','1');
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','3');
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','5');
- break;
- }
- } else {
- header.ddspf.dwFlags = ( internalFormat == GL_COLOR_INDEX8_EXT ) ? DDSF_RGB | DDSF_ID_INDEXCOLOR : DDSF_RGB;
- header.ddspf.dwRGBBitCount = bitSize;
- switch ( altInternalFormat ) {
- case GL_BGRA_EXT:
- case GL_LUMINANCE_ALPHA:
- header.ddspf.dwFlags |= DDSF_ALPHAPIXELS;
- header.ddspf.dwABitMask = 0xFF000000;
- // Fall through
- case GL_BGR_EXT:
- case GL_LUMINANCE:
- case GL_COLOR_INDEX:
- header.ddspf.dwRBitMask = 0x00FF0000;
- header.ddspf.dwGBitMask = 0x0000FF00;
- header.ddspf.dwBBitMask = 0x000000FF;
- break;
- case GL_ALPHA:
- header.ddspf.dwFlags = DDSF_ALPHAPIXELS;
- header.ddspf.dwABitMask = 0xFF000000;
- break;
- default:
- common->Warning( "Unknown or unsupported format for %s", filename );
- return;
- }
- }
- idFile *f = fileSystem->OpenFileWrite( filename );
- if ( f == NULL ) {
- common->Warning( "Could not open %s trying to write precompressed image", filename );
- return;
- }
- common->Printf( "Writing precompressed image: %s\n", filename );
- f->Write( "DDS ", 4 );
- f->Write( &header, sizeof(header) );
- // bind to the image so we can read back the contents
- Bind();
- qglPixelStorei( GL_PACK_ALIGNMENT, 1 ); // otherwise small rows get padded to 32 bits
- int uw = uploadWidth;
- int uh = uploadHeight;
- // Will be allocated first time through the loop
- byte *data = NULL;
- for ( int level = 0 ; level < numLevels ; level++ ) {
- int size = 0;
- if ( FormatIsDXT( altInternalFormat ) ) {
- size = ( ( uw + 3 ) / 4 ) * ( ( uh + 3 ) / 4 ) *
- (altInternalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
- } else {
- size = uw * uh * (bitSize / 8);
- }
- if (data == NULL) {
- data = (byte *)R_StaticAlloc( size );
- }
- if ( FormatIsDXT( altInternalFormat ) ) {
- qglGetCompressedTexImageARB( GL_TEXTURE_2D, level, data );
- } else {
- qglGetTexImage( GL_TEXTURE_2D, level, altInternalFormat, GL_UNSIGNED_BYTE, data );
- }
- f->Write( data, size );
- uw /= 2;
- uh /= 2;
- if (uw < 1) {
- uw = 1;
- }
- if (uh < 1) {
- uh = 1;
- }
- }
- if (data != NULL) {
- R_StaticFree( data );
- }
- fileSystem->CloseFile( f );
- }
- /*
- ================
- ShouldImageBePartialCached
- Returns true if there is a precompressed image, and it is large enough
- to be worth caching
- ================
- */
- bool idImage::ShouldImageBePartialCached() {
- if ( !glConfig.textureCompressionAvailable ) {
- return false;
- }
- if ( !globalImages->image_useCache.GetBool() ) {
- return false;
- }
- // the allowDownSize flag does double-duty as don't-partial-load
- if ( !allowDownSize ) {
- return false;
- }
- if ( globalImages->image_cacheMinK.GetInteger() <= 0 ) {
- return false;
- }
- // if we are doing a copyFiles, make sure the original images are referenced
- if ( fileSystem->PerformingCopyFiles() ) {
- return false;
- }
- char filename[MAX_IMAGE_NAME];
- ImageProgramStringToCompressedFileName( imgName, filename );
- // get the file timestamp
- fileSystem->ReadFile( filename, NULL, ×tamp );
- if ( timestamp == FILE_NOT_FOUND_TIMESTAMP ) {
- return false;
- }
- // open it and get the file size
- idFile *f;
- f = fileSystem->OpenFileRead( filename );
- if ( !f ) {
- return false;
- }
- int len = f->Length();
- fileSystem->CloseFile( f );
- if ( len <= globalImages->image_cacheMinK.GetInteger() * 1024 ) {
- return false;
- }
- // we do want to do a partial load
- return true;
- }
- /*
- ================
- CheckPrecompressedImage
- If fullLoad is false, only the small mip levels of the image will be loaded
- ================
- */
- bool idImage::CheckPrecompressedImage( bool fullLoad ) {
- if ( !glConfig.isInitialized || !glConfig.textureCompressionAvailable ) {
- return false;
- }
- #if 1 // ( _D3XP had disabled ) - Allow grabbing of DDS's from original Doom pak files
- // if we are doing a copyFiles, make sure the original images are referenced
- if ( fileSystem->PerformingCopyFiles() ) {
- return false;
- }
- #endif
- if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 2 ) {
- return false;
- }
- // god i love last minute hacks :-)
- if ( com_machineSpec.GetInteger() >= 1 && com_videoRam.GetInteger() >= 128 && imgName.Icmpn( "lights/", 7 ) == 0 ) {
- return false;
- }
- char filename[MAX_IMAGE_NAME];
- ImageProgramStringToCompressedFileName( imgName, filename );
- // get the file timestamp
- ID_TIME_T precompTimestamp;
- fileSystem->ReadFile( filename, NULL, &precompTimestamp );
- if ( precompTimestamp == FILE_NOT_FOUND_TIMESTAMP ) {
- return false;
- }
- if ( !generatorFunction && timestamp != FILE_NOT_FOUND_TIMESTAMP ) {
- if ( precompTimestamp < timestamp ) {
- // The image has changed after being precompressed
- return false;
- }
- }
- timestamp = precompTimestamp;
- // open it and just read the header
- idFile *f;
- f = fileSystem->OpenFileRead( filename );
- if ( !f ) {
- return false;
- }
- int len = f->Length();
- if ( len < sizeof( ddsFileHeader_t ) ) {
- fileSystem->CloseFile( f );
- return false;
- }
- if ( !fullLoad && len > globalImages->image_cacheMinK.GetInteger() * 1024 ) {
- len = globalImages->image_cacheMinK.GetInteger() * 1024;
- }
- byte *data = (byte *)R_StaticAlloc( len );
- f->Read( data, len );
- fileSystem->CloseFile( f );
- unsigned long magic = LittleLong( *(unsigned long *)data );
- ddsFileHeader_t *_header = (ddsFileHeader_t *)(data + 4);
- int ddspf_dwFlags = LittleLong( _header->ddspf.dwFlags );
- if ( magic != DDS_MAKEFOURCC('D', 'D', 'S', ' ')) {
- common->Printf( "CheckPrecompressedImage( %s ): magic != 'DDS '\n", imgName.c_str() );
- R_StaticFree( data );
- return false;
- }
- // if we don't support color index textures, we must load the full image
- // should we just expand the 256 color image to 32 bit for upload?
- if ( ddspf_dwFlags & DDSF_ID_INDEXCOLOR && !glConfig.sharedTexturePaletteAvailable ) {
- R_StaticFree( data );
- return false;
- }
- // upload all the levels
- UploadPrecompressedImage( data, len );
- R_StaticFree( data );
- return true;
- }
- /*
- ===================
- UploadPrecompressedImage
- This can be called by the front end during nromal loading,
- or by the backend after a background read of the file
- has completed
- ===================
- */
- void idImage::UploadPrecompressedImage( byte *data, int len ) {
- ddsFileHeader_t *header = (ddsFileHeader_t *)(data + 4);
- // ( not byte swapping dwReserved1 dwReserved2 )
- header->dwSize = LittleLong( header->dwSize );
- header->dwFlags = LittleLong( header->dwFlags );
- header->dwHeight = LittleLong( header->dwHeight );
- header->dwWidth = LittleLong( header->dwWidth );
- header->dwPitchOrLinearSize = LittleLong( header->dwPitchOrLinearSize );
- header->dwDepth = LittleLong( header->dwDepth );
- header->dwMipMapCount = LittleLong( header->dwMipMapCount );
- header->dwCaps1 = LittleLong( header->dwCaps1 );
- header->dwCaps2 = LittleLong( header->dwCaps2 );
- header->ddspf.dwSize = LittleLong( header->ddspf.dwSize );
- header->ddspf.dwFlags = LittleLong( header->ddspf.dwFlags );
- header->ddspf.dwFourCC = LittleLong( header->ddspf.dwFourCC );
- header->ddspf.dwRGBBitCount = LittleLong( header->ddspf.dwRGBBitCount );
- header->ddspf.dwRBitMask = LittleLong( header->ddspf.dwRBitMask );
- header->ddspf.dwGBitMask = LittleLong( header->ddspf.dwGBitMask );
- header->ddspf.dwBBitMask = LittleLong( header->ddspf.dwBBitMask );
- header->ddspf.dwABitMask = LittleLong( header->ddspf.dwABitMask );
- // generate the texture number
- qglGenTextures( 1, &texnum );
- int externalFormat = 0;
- precompressedFile = true;
- uploadWidth = header->dwWidth;
- uploadHeight = header->dwHeight;
- if ( header->ddspf.dwFlags & DDSF_FOURCC ) {
- switch ( header->ddspf.dwFourCC ) {
- case DDS_MAKEFOURCC( 'D', 'X', 'T', '1' ):
- if ( header->ddspf.dwFlags & DDSF_ALPHAPIXELS ) {
- internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
- } else {
- internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
- }
- break;
- case DDS_MAKEFOURCC( 'D', 'X', 'T', '3' ):
- internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
- break;
- case DDS_MAKEFOURCC( 'D', 'X', 'T', '5' ):
- internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- break;
- case DDS_MAKEFOURCC( 'R', 'X', 'G', 'B' ):
- internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- break;
- default:
- common->Warning( "Invalid compressed internal format\n" );
- return;
- }
- } else if ( ( header->ddspf.dwFlags & DDSF_RGBA ) && header->ddspf.dwRGBBitCount == 32 ) {
- externalFormat = GL_BGRA_EXT;
- internalFormat = GL_RGBA8;
- } else if ( ( header->ddspf.dwFlags & DDSF_RGB ) && header->ddspf.dwRGBBitCount == 32 ) {
- externalFormat = GL_BGRA_EXT;
- internalFormat = GL_RGBA8;
- } else if ( ( header->ddspf.dwFlags & DDSF_RGB ) && header->ddspf.dwRGBBitCount == 24 ) {
- if ( header->ddspf.dwFlags & DDSF_ID_INDEXCOLOR ) {
- externalFormat = GL_COLOR_INDEX;
- internalFormat = GL_COLOR_INDEX8_EXT;
- } else {
- externalFormat = GL_BGR_EXT;
- internalFormat = GL_RGB8;
- }
- } else if ( header->ddspf.dwRGBBitCount == 8 ) {
- externalFormat = GL_ALPHA;
- internalFormat = GL_ALPHA8;
- } else {
- common->Warning( "Invalid uncompressed internal format\n" );
- return;
- }
- // we need the monochrome flag for the NV20 optimized path
- if ( header->dwFlags & DDSF_ID_MONOCHROME ) {
- isMonochrome = true;
- }
- type = TT_2D; // FIXME: we may want to support pre-compressed cube maps in the future
- Bind();
- int numMipmaps = 1;
- if ( header->dwFlags & DDSF_MIPMAPCOUNT ) {
- numMipmaps = header->dwMipMapCount;
- }
- int uw = uploadWidth;
- int uh = uploadHeight;
- // We may skip some mip maps if we are downsizing
- int skipMip = 0;
- GetDownsize( uploadWidth, uploadHeight );
- byte *imagedata = data + sizeof(ddsFileHeader_t) + 4;
- for ( int i = 0 ; i < numMipmaps; i++ ) {
- int size = 0;
- if ( FormatIsDXT( internalFormat ) ) {
- size = ( ( uw + 3 ) / 4 ) * ( ( uh + 3 ) / 4 ) *
- (internalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
- } else {
- size = uw * uh * (header->ddspf.dwRGBBitCount / 8);
- }
- if ( uw > uploadWidth || uh > uploadHeight ) {
- skipMip++;
- } else {
- if ( FormatIsDXT( internalFormat ) ) {
- qglCompressedTexImage2DARB( GL_TEXTURE_2D, i - skipMip, internalFormat, uw, uh, 0, size, imagedata );
- } else {
- qglTexImage2D( GL_TEXTURE_2D, i - skipMip, internalFormat, uw, uh, 0, externalFormat, GL_UNSIGNED_BYTE, imagedata );
- }
- }
- imagedata += size;
- uw /= 2;
- uh /= 2;
- if (uw < 1) {
- uw = 1;
- }
- if (uh < 1) {
- uh = 1;
- }
- }
- SetImageFilterAndRepeat();
- }
- /*
- ===============
- ActuallyLoadImage
- Absolutely every image goes through this path
- On exit, the idImage will have a valid OpenGL texture number that can be bound
- ===============
- */
- void idImage::ActuallyLoadImage( bool checkForPrecompressed, bool fromBackEnd ) {
- int width, height;
- byte *pic;
- // this is the ONLY place generatorFunction will ever be called
- if ( generatorFunction ) {
- generatorFunction( this );
- return;
- }
- // if we are a partial image, we are only going to load from a compressed file
- if ( isPartialImage ) {
- if ( CheckPrecompressedImage( false ) ) {
- return;
- }
- // this is an error -- the partial image failed to load
- MakeDefault();
- return;
- }
- //
- // load the image from disk
- //
- if ( cubeFiles != CF_2D ) {
- byte *pics[6];
- // we don't check for pre-compressed cube images currently
- R_LoadCubeImages( imgName, cubeFiles, pics, &width, ×tamp );
- if ( pics[0] == NULL ) {
- common->Warning( "Couldn't load cube image: %s", imgName.c_str() );
- MakeDefault();
- return;
- }
- GenerateCubeImage( (const byte **)pics, width, filter, allowDownSize, depth );
- precompressedFile = false;
- for ( int i = 0 ; i < 6 ; i++ ) {
- if ( pics[i] ) {
- R_StaticFree( pics[i] );
- }
- }
- } else {
- // see if we have a pre-generated image file that is
- // already image processed and compressed
- if ( checkForPrecompressed && globalImages->image_usePrecompressedTextures.GetBool() ) {
- if ( CheckPrecompressedImage( true ) ) {
- // we got the precompressed image
- return;
- }
- // fall through to load the normal image
- }
- R_LoadImageProgram( imgName, &pic, &width, &height, ×tamp, &depth );
- if ( pic == NULL ) {
- common->Warning( "Couldn't load image: %s", imgName.c_str() );
- MakeDefault();
- return;
- }
- /*
- // swap the red and alpha for rxgb support
- // do this even on tga normal maps so we only have to use
- // one fragment program
- // if the image is precompressed ( either in palletized mode or true rxgb mode )
- // then it is loaded above and the swap never happens here
- if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 1 ) {
- for ( int i = 0; i < width * height * 4; i += 4 ) {
- pic[ i + 3 ] = pic[ i ];
- pic[ i ] = 0;
- }
- }
- */
- // build a hash for checking duplicate image files
- // NOTE: takes about 10% of image load times (SD)
- // may not be strictly necessary, but some code uses it, so let's leave it in
- imageHash = MD4_BlockChecksum( pic, width * height * 4 );
- GenerateImage( pic, width, height, filter, allowDownSize, repeat, depth );
- timestamp = timestamp;
- precompressedFile = false;
- R_StaticFree( pic );
- // write out the precompressed version of this file if needed
- WritePrecompressedImage();
- }
- }
- //=========================================================================================================
- /*
- ===============
- PurgeImage
- ===============
- */
- void idImage::PurgeImage() {
- if ( texnum != TEXTURE_NOT_LOADED ) {
- // sometimes is NULL when exiting with an error
- if ( qglDeleteTextures ) {
- qglDeleteTextures( 1, &texnum ); // this should be the ONLY place it is ever called!
- }
- texnum = TEXTURE_NOT_LOADED;
- }
- // clear all the current binding caches, so the next bind will do a real one
- for ( int i = 0 ; i < MAX_MULTITEXTURE_UNITS ; i++ ) {
- backEnd.glState.tmu[i].current2DMap = -1;
- backEnd.glState.tmu[i].current3DMap = -1;
- backEnd.glState.tmu[i].currentCubeMap = -1;
- }
- }
- /*
- ==============
- Bind
- Automatically enables 2D mapping, cube mapping, or 3D texturing if needed
- ==============
- */
- void idImage::Bind() {
- if ( tr.logFile ) {
- RB_LogComment( "idImage::Bind( %s )\n", imgName.c_str() );
- }
- // if this is an image that we are caching, move it to the front of the LRU chain
- if ( partialImage ) {
- if ( cacheUsageNext ) {
- // unlink from old position
- cacheUsageNext->cacheUsagePrev = cacheUsagePrev;
- cacheUsagePrev->cacheUsageNext = cacheUsageNext;
- }
- // link in at the head of the list
- cacheUsageNext = globalImages->cacheLRU.cacheUsageNext;
- cacheUsagePrev = &globalImages->cacheLRU;
- cacheUsageNext->cacheUsagePrev = this;
- cacheUsagePrev->cacheUsageNext = this;
- }
- // load the image if necessary (FIXME: not SMP safe!)
- if ( texnum == TEXTURE_NOT_LOADED ) {
- if ( partialImage ) {
- // if we have a partial image, go ahead and use that
- this->partialImage->Bind();
- // start a background load of the full thing if it isn't already in the queue
- if ( !backgroundLoadInProgress ) {
- StartBackgroundImageLoad();
- }
- return;
- }
- // load the image on demand here, which isn't our normal game operating mode
- ActuallyLoadImage( true, true ); // check for precompressed, load is from back end
- }
- // bump our statistic counters
- frameUsed = backEnd.frameCount;
- bindCount++;
- tmu_t *tmu = &backEnd.glState.tmu[backEnd.glState.currenttmu];
- // enable or disable apropriate texture modes
- if ( tmu->textureType != type && ( backEnd.glState.currenttmu < glConfig.maxTextureUnits ) ) {
- if ( tmu->textureType == TT_CUBIC ) {
- qglDisable( GL_TEXTURE_CUBE_MAP_EXT );
- } else if ( tmu->textureType == TT_3D ) {
- qglDisable( GL_TEXTURE_3D );
- } else if ( tmu->textureType == TT_2D ) {
- qglDisable( GL_TEXTURE_2D );
- }
- if ( type == TT_CUBIC ) {
- qglEnable( GL_TEXTURE_CUBE_MAP_EXT );
- } else if ( type == TT_3D ) {
- qglEnable( GL_TEXTURE_3D );
- } else if ( type == TT_2D ) {
- qglEnable( GL_TEXTURE_2D );
- }
- tmu->textureType = type;
- }
- // bind the texture
- if ( type == TT_2D ) {
- if ( tmu->current2DMap != texnum ) {
- tmu->current2DMap = texnum;
- qglBindTexture( GL_TEXTURE_2D, texnum );
- }
- } else if ( type == TT_CUBIC ) {
- if ( tmu->currentCubeMap != texnum ) {
- tmu->currentCubeMap = texnum;
- qglBindTexture( GL_TEXTURE_CUBE_MAP_EXT, texnum );
- }
- } else if ( type == TT_3D ) {
- if ( tmu->current3DMap != texnum ) {
- tmu->current3DMap = texnum;
- qglBindTexture( GL_TEXTURE_3D, texnum );
- }
- }
- if ( com_purgeAll.GetBool() ) {
- GLclampf priority = 1.0f;
- qglPrioritizeTextures( 1, &texnum, &priority );
- }
- }
- /*
- ==============
- BindFragment
- Fragment programs explicitly say which type of map they want, so we don't need to
- do any enable / disable changes
- ==============
- */
- void idImage::BindFragment() {
- if ( tr.logFile ) {
- RB_LogComment( "idImage::BindFragment %s )\n", imgName.c_str() );
- }
- // if this is an image that we are caching, move it to the front of the LRU chain
- if ( partialImage ) {
- if ( cacheUsageNext ) {
- // unlink from old position
- cacheUsageNext->cacheUsagePrev = cacheUsagePrev;
- cacheUsagePrev->cacheUsageNext = cacheUsageNext;
- }
- // link in at the head of the list
- cacheUsageNext = globalImages->cacheLRU.cacheUsageNext;
- cacheUsagePrev = &globalImages->cacheLRU;
- cacheUsageNext->cacheUsagePrev = this;
- cacheUsagePrev->cacheUsageNext = this;
- }
- // load the image if necessary (FIXME: not SMP safe!)
- if ( texnum == TEXTURE_NOT_LOADED ) {
- if ( partialImage ) {
- // if we have a partial image, go ahead and use that
- this->partialImage->BindFragment();
- // start a background load of the full thing if it isn't already in the queue
- if ( !backgroundLoadInProgress ) {
- StartBackgroundImageLoad();
- }
- return;
- }
- // load the image on demand here, which isn't our normal game operating mode
- ActuallyLoadImage( true, true ); // check for precompressed, load is from back end
- }
- // bump our statistic counters
- frameUsed = backEnd.frameCount;
- bindCount++;
- // bind the texture
- if ( type == TT_2D ) {
- qglBindTexture( GL_TEXTURE_2D, texnum );
- } else if ( type == TT_RECT ) {
- qglBindTexture( GL_TEXTURE_RECTANGLE_NV, texnum );
- } else if ( type == TT_CUBIC ) {
- qglBindTexture( GL_TEXTURE_CUBE_MAP_EXT, texnum );
- } else if ( type == TT_3D ) {
- qglBindTexture( GL_TEXTURE_3D, texnum );
- }
- }
- /*
- ====================
- CopyFramebuffer
- ====================
- */
- void idImage::CopyFramebuffer( int x, int y, int imageWidth, int imageHeight, bool useOversizedBuffer ) {
- Bind();
- if ( cvarSystem->GetCVarBool( "g_lowresFullscreenFX" ) ) {
- imageWidth = 512;
- imageHeight = 512;
- }
- // if the size isn't a power of 2, the image must be increased in size
- int potWidth, potHeight;
- potWidth = MakePowerOfTwo( imageWidth );
- potHeight = MakePowerOfTwo( imageHeight );
- GetDownsize( imageWidth, imageHeight );
- GetDownsize( potWidth, potHeight );
- qglReadBuffer( GL_BACK );
- // only resize if the current dimensions can't hold it at all,
- // otherwise subview renderings could thrash this
- if ( ( useOversizedBuffer && ( uploadWidth < potWidth || uploadHeight < potHeight ) )
- || ( !useOversizedBuffer && ( uploadWidth != potWidth || uploadHeight != potHeight ) ) ) {
- uploadWidth = potWidth;
- uploadHeight = potHeight;
- if ( potWidth == imageWidth && potHeight == imageHeight ) {
- qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, x, y, imageWidth, imageHeight, 0 );
- } else {
- byte *junk;
- // we need to create a dummy image with power of two dimensions,
- // then do a qglCopyTexSubImage2D of the data we want
- // this might be a 16+ meg allocation, which could fail on _alloca
- junk = (byte *)Mem_Alloc( potWidth * potHeight * 4 );
- memset( junk, 0, potWidth * potHeight * 4 ); //!@#
- #if 0 // Disabling because it's unnecessary and introduces a green strip on edge of _currentRender
- for ( int i = 0 ; i < potWidth * potHeight * 4 ; i+=4 ) {
- junk[i+1] = 255;
- }
- #endif
- qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, potWidth, potHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, junk );
- Mem_Free( junk );
- qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
- }
- } else {
- // otherwise, just subimage upload it so that drivers can tell we are going to be changing
- // it and don't try and do a texture compression or some other silliness
- qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
- }
- // if the image isn't a full power of two, duplicate an extra row and/or column to fix bilerps
- if ( imageWidth != potWidth ) {
- qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, imageWidth, 0, x+imageWidth-1, y, 1, imageHeight );
- }
- if ( imageHeight != potHeight ) {
- qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, imageHeight, x, y+imageHeight-1, imageWidth, 1 );
- }
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
- backEnd.c_copyFrameBuffer++;
- }
- /*
- ====================
- CopyDepthbuffer
- This should just be part of copyFramebuffer once we have a proper image type field
- ====================
- */
- void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight ) {
- Bind();
- // if the size isn't a power of 2, the image must be increased in size
- int potWidth, potHeight;
- potWidth = MakePowerOfTwo( imageWidth );
- potHeight = MakePowerOfTwo( imageHeight );
- if ( uploadWidth != potWidth || uploadHeight != potHeight ) {
- uploadWidth = potWidth;
- uploadHeight = potHeight;
- if ( potWidth == imageWidth && potHeight == imageHeight ) {
- qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, x, y, imageWidth, imageHeight, 0 );
- } else {
- // we need to create a dummy image with power of two dimensions,
- // then do a qglCopyTexSubImage2D of the data we want
- qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, potWidth, potHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
- qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
- }
- } else {
- // otherwise, just subimage upload it so that drivers can tell we are going to be changing
- // it and don't try and do a texture compression or some other silliness
- qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
- }
- // qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
- // qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
- }
- /*
- =============
- RB_UploadScratchImage
- if rows = cols * 6, assume it is a cube map animation
- =============
- */
- void idImage::UploadScratch( const byte *data, int cols, int rows ) {
- int i;
- // if rows = cols * 6, assume it is a cube map animation
- if ( rows == cols * 6 ) {
- if ( type != TT_CUBIC ) {
- type = TT_CUBIC;
- uploadWidth = -1; // for a non-sub upload
- }
- Bind();
- rows /= 6;
- // if the scratchImage isn't in the format we want, specify it as a new texture
- if ( cols != uploadWidth || rows != uploadHeight ) {
- uploadWidth = cols;
- uploadHeight = rows;
- // upload the base level
- for ( i = 0 ; i < 6 ; i++ ) {
- qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, GL_RGB8, cols, rows, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, data + cols*rows*4*i );
- }
- } else {
- // otherwise, just subimage upload it so that drivers can tell we are going to be changing
- // it and don't try and do a texture compression
- for ( i = 0 ; i < 6 ; i++ ) {
- qglTexSubImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, 0, 0, cols, rows,
- GL_RGBA, GL_UNSIGNED_BYTE, data + cols*rows*4*i );
- }
- }
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
- // no other clamp mode makes sense
- qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- } else {
- // otherwise, it is a 2D image
- if ( type != TT_2D ) {
- type = TT_2D;
- uploadWidth = -1; // for a non-sub upload
- }
- Bind();
- // if the scratchImage isn't in the format we want, specify it as a new texture
- if ( cols != uploadWidth || rows != uploadHeight ) {
- uploadWidth = cols;
- uploadHeight = rows;
- qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
- } else {
- // otherwise, just subimage upload it so that drivers can tell we are going to be changing
- // it and don't try and do a texture compression
- qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data );
- }
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
- // these probably should be clamp, but we have a lot of issues with editor
- // geometry coming out with texcoords slightly off one side, resulting in
- // a smear across the entire polygon
- #if 1
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
- #else
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
- qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
- #endif
- }
- }
- void idImage::SetClassification( int tag ) {
- classification = tag;
- }
- /*
- ==================
- StorageSize
- ==================
- */
- int idImage::StorageSize() const {
- int baseSize;
- if ( texnum == TEXTURE_NOT_LOADED ) {
- return 0;
- }
- switch ( type ) {
- default:
- case TT_2D:
- baseSize = uploadWidth*uploadHeight;
- break;
- case TT_3D:
- baseSize = uploadWidth*uploadHeight*uploadDepth;
- break;
- case TT_CUBIC:
- baseSize = 6 * uploadWidth*uploadHeight;
- break;
- }
- baseSize *= BitsForInternalFormat( internalFormat );
- baseSize /= 8;
- // account for mip mapping
- baseSize = baseSize * 4 / 3;
- return baseSize;
- }
- /*
- ==================
- Print
- ==================
- */
- void idImage::Print() const {
- if ( precompressedFile ) {
- common->Printf( "P" );
- } else if ( generatorFunction ) {
- common->Printf( "F" );
- } else {
- common->Printf( " " );
- }
- switch ( type ) {
- case TT_2D:
- common->Printf( " " );
- break;
- case TT_3D:
- common->Printf( "3" );
- break;
- case TT_CUBIC:
- common->Printf( "C" );
- break;
- case TT_RECT:
- common->Printf( "R" );
- break;
- default:
- common->Printf( "<BAD TYPE:%i>", type );
- break;
- }
- common->Printf( "%4i %4i ", uploadWidth, uploadHeight );
- switch( filter ) {
- case TF_DEFAULT:
- common->Printf( "dflt " );
- break;
- case TF_LINEAR:
- common->Printf( "linr " );
- break;
- case TF_NEAREST:
- common->Printf( "nrst " );
- break;
- default:
- common->Printf( "<BAD FILTER:%i>", filter );
- break;
- }
- switch ( internalFormat ) {
- case GL_INTENSITY8:
- case 1:
- common->Printf( "I " );
- break;
- case 2:
- case GL_LUMINANCE8_ALPHA8:
- common->Printf( "LA " );
- break;
- case 3:
- common->Printf( "RGB " );
- break;
- case 4:
- common->Printf( "RGBA " );
- break;
- case GL_LUMINANCE8:
- common->Printf( "L " );
- break;
- case GL_ALPHA8:
- common->Printf( "A " );
- break;
- case GL_RGBA8:
- common->Printf( "RGBA8 " );
- break;
- case GL_RGB8:
- common->Printf( "RGB8 " );
- break;
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- common->Printf( "DXT1 " );
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- common->Printf( "DXT1A " );
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- common->Printf( "DXT3 " );
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- common->Printf( "DXT5 " );
- break;
- case GL_RGBA4:
- common->Printf( "RGBA4 " );
- break;
- case GL_RGB5:
- common->Printf( "RGB5 " );
- break;
- case GL_COLOR_INDEX8_EXT:
- common->Printf( "CI8 " );
- break;
- case GL_COLOR_INDEX:
- common->Printf( "CI " );
- break;
- case GL_COMPRESSED_RGB_ARB:
- common->Printf( "RGBC " );
- break;
- case GL_COMPRESSED_RGBA_ARB:
- common->Printf( "RGBAC " );
- break;
- case 0:
- common->Printf( " " );
- break;
- default:
- common->Printf( "<BAD FORMAT:%i>", internalFormat );
- break;
- }
- switch ( repeat ) {
- case TR_REPEAT:
- common->Printf( "rept " );
- break;
- case TR_CLAMP_TO_ZERO:
- common->Printf( "zero " );
- break;
- case TR_CLAMP_TO_ZERO_ALPHA:
- common->Printf( "azro " );
- break;
- case TR_CLAMP:
- common->Printf( "clmp " );
- break;
- default:
- common->Printf( "<BAD REPEAT:%i>", repeat );
- break;
- }
-
- common->Printf( "%4ik ", StorageSize() / 1024 );
- common->Printf( " %s\n", imgName.c_str() );
- }
|