1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "gfx/gfxTextureManager.h"
- #include "gfx/gfxDevice.h"
- #include "gfx/gfxCardProfile.h"
- #include "gfx/gfxStringEnumTranslate.h"
- #include "gfx/bitmap/imageUtils.h"
- #include "core/strings/stringFunctions.h"
- #include "core/util/safeDelete.h"
- #include "core/resourceManager.h"
- #include "core/volume.h"
- #include "core/util/dxt5nmSwizzle.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "renderInstance/renderProbeMgr.h"
- using namespace Torque;
- //#define DEBUG_SPEW
- S32 GFXTextureManager::smTextureReductionLevel = 0;
- String GFXTextureManager::smMissingTexturePath(Con::getVariable("$Core::MissingTexturePath"));
- String GFXTextureManager::smUnavailableTexturePath(Con::getVariable("$Core::UnAvailableTexturePath"));
- String GFXTextureManager::smWarningTexturePath(Con::getVariable("$Core::WarningTexturePath"));
- String GFXTextureManager::smDefaultIrradianceCubemapPath(Con::getVariable("$Core::DefaultIrradianceCubemap"));
- String GFXTextureManager::smDefaultPrefilterCubemapPath(Con::getVariable("$Core::DefaultPrefilterCubemap"));
- String GFXTextureManager::smBRDFTexturePath(Con::getVariable("$Core::BRDFTexture"));
- GFXTextureManager::EventSignal GFXTextureManager::smEventSignal;
- static const String sDDSExt( "dds" );
- void GFXTextureManager::init()
- {
- Con::addVariable( "$pref::Video::textureReductionLevel", TypeS32, &smTextureReductionLevel,
- "The number of mipmap levels to drop on loaded textures to reduce "
- "video memory usage. It will skip any textures that have been defined "
- "as not allowing down scaling.\n"
- "@ingroup GFX\n" );
- Con::addVariable( "$pref::Video::missingTexturePath", TypeRealString, &smMissingTexturePath,
- "The file path of the texture to display when the requested texture is missing.\n"
- "@ingroup GFX\n" );
- Con::addVariable( "$pref::Video::unavailableTexturePath", TypeRealString, &smUnavailableTexturePath,
- "@brief The file path of the texture to display when the requested texture is unavailable.\n\n"
- "Often this texture is used by GUI controls to indicate that the request image is unavailable.\n"
- "@ingroup GFX\n" );
- Con::addVariable( "$pref::Video::warningTexturePath", TypeRealString, &smWarningTexturePath,
- "The file path of the texture used to warn the developer.\n"
- "@ingroup GFX\n" );
- Con::addVariable("$Core::DefaultIrradianceCubemap", TypeRealString, &smDefaultIrradianceCubemapPath,
- "The file path of the texture used as the default irradiance cubemap for PBR.\n"
- "@ingroup GFX\n");
- Con::addVariable("$Core::DefaultPrefilterCubemap", TypeRealString, &smDefaultPrefilterCubemapPath,
- "The file path of the texture used as the default specular cubemap for PBR.\n"
- "@ingroup GFX\n");
- Con::addVariable("$Core::BRDFTexture", TypeRealString, &smBRDFTexturePath,
- "The file path of the texture used as the default irradiance cubemap for PBR.\n"
- "@ingroup GFX\n");
- }
- GFXTextureManager::GFXTextureManager()
- {
- mListHead = mListTail = NULL;
- mTextureManagerState = GFXTextureManager::Living;
- // Set up the hash table
- mHashCount = 1023;
- mHashTable = new GFXTextureObject *[mHashCount];
- for(U32 i = 0; i < mHashCount; i++)
- mHashTable[i] = NULL;
- }
- GFXTextureManager::~GFXTextureManager()
- {
- if( mHashTable )
- SAFE_DELETE_ARRAY( mHashTable );
- mCubemapTable.clear();
- }
- U32 GFXTextureManager::getTextureDownscalePower( GFXTextureProfile *profile )
- {
- if ( !profile || profile->canDownscale() )
- return smTextureReductionLevel;
- return 0;
- }
- bool GFXTextureManager::validateTextureQuality( GFXTextureProfile *profile, U32 &width, U32 &height )
- {
- U32 scaleFactor = getTextureDownscalePower( profile );
- if ( scaleFactor == 0 )
- return true;
- // Otherwise apply the appropriate scale...
- width >>= scaleFactor;
- height >>= scaleFactor;
- return true;
- }
- void GFXTextureManager::kill()
- {
- AssertFatal( mTextureManagerState != GFXTextureManager::Dead, "Texture Manager already killed!" );
- // Release everything in the cache we can
- // so we don't leak any textures.
- cleanupCache();
- GFXTextureObject *curr = mListHead;
- GFXTextureObject *temp;
- // Actually delete all the textures we know about.
- while( curr != NULL )
- {
- temp = curr->mNext;
- curr->kill();
- curr = temp;
- }
- mCubemapTable.clear();
- mTextureManagerState = GFXTextureManager::Dead;
- }
- void GFXTextureManager::zombify()
- {
- AssertFatal( mTextureManagerState != GFXTextureManager::Zombie, "Texture Manager already a zombie!" );
- // Notify everyone that cares about the zombification!
- smEventSignal.trigger( GFXZombify );
- // Release unused pool textures.
- cleanupPool();
- // Release everything in the cache we can.
- cleanupCache();
- // Free all the device copies of the textures.
- GFXTextureObject *temp = mListHead;
- while( temp != NULL )
- {
- freeTexture( temp, true );
- temp = temp->mNext;
- }
- // Finally, note our state.
- mTextureManagerState = GFXTextureManager::Zombie;
- }
- void GFXTextureManager::resurrect()
- {
- // Reupload all the device copies of the textures.
- GFXTextureObject *temp = mListHead;
- while( temp != NULL )
- {
- refreshTexture( temp );
- temp = temp->mNext;
- }
- // Notify callback registries.
- smEventSignal.trigger( GFXResurrect );
-
- // Update our state.
- mTextureManagerState = GFXTextureManager::Living;
- }
- void GFXTextureManager::cleanupPool()
- {
- PROFILE_SCOPE( GFXTextureManager_CleanupPool );
- TexturePoolMap::Iterator iter = mTexturePool.begin();
- for ( ; iter != mTexturePool.end(); )
- {
- if ( iter->value->getRefCount() == 1 )
- {
- // This texture is unreferenced, so take the time
- // now to completely remove it from the pool.
- TexturePoolMap::Iterator unref = iter;
- ++iter;
- unref->value = NULL;
- mTexturePool.erase( unref );
- continue;
- }
- ++iter;
- }
- }
- void GFXTextureManager::requestDeleteTexture( GFXTextureObject *texture )
- {
- // If this is a non-cached texture then just really delete it.
- if ( texture->mTextureLookupName.isEmpty() )
- {
- delete texture;
- return;
- }
- // Set the time and store it.
- texture->mDeleteTime = Platform::getTime();
- mToDelete.push_back_unique( texture );
- }
- void GFXTextureManager::cleanupCache( U32 secondsToLive )
- {
- PROFILE_SCOPE( GFXTextureManager_CleanupCache );
- U32 killTime = Platform::getTime() - secondsToLive;
- for ( U32 i=0; i < mToDelete.size(); )
- {
- GFXTextureObject *tex = mToDelete[i];
- // If the texture was picked back up by a user
- // then just remove it from the list.
- if ( tex->getRefCount() != 0 )
- {
- mToDelete.erase_fast( i );
- continue;
- }
- // If its time has expired delete it for real.
- if ( tex->mDeleteTime <= killTime )
- {
- //Con::errorf( "Killed texture: %s", tex->mTextureLookupName.c_str() );
- delete tex;
- mToDelete.erase_fast( i );
- continue;
- }
- i++;
- }
- }
- GFXTextureObject *GFXTextureManager::_lookupTexture( const char *hashName, const GFXTextureProfile *profile )
- {
- GFXTextureObject *ret = hashFind( hashName );
- //compare just the profile flags and not the entire profile, names could be different but otherwise identical flags
- if (ret && (ret->mProfile->compareFlags(*profile)))
- return ret;
- else if (ret)
- Con::warnf("GFXTextureManager::_lookupTexture: Cached texture %s has different profile flags: (%s,%s) ", hashName, ret->mProfile->getName().c_str(), profile->getName().c_str());
- return NULL;
- }
- GFXTextureObject *GFXTextureManager::_lookupTexture( const DDSFile *ddsFile, const GFXTextureProfile *profile )
- {
- if( ddsFile->getTextureCacheString().isNotEmpty() )
- {
- // Call _lookupTexture()
- return _lookupTexture( ddsFile->getTextureCacheString(), profile );
- }
- return NULL;
- }
- GFXTextureObject *GFXTextureManager::createTexture( GBitmap *bmp, const String &resourceName, GFXTextureProfile *profile, bool deleteBmp )
- {
- AssertFatal(bmp, "GFXTextureManager::createTexture() - Got NULL bitmap!");
- GFXTextureObject *cacheHit = _lookupTexture( resourceName, profile );
- if( cacheHit != NULL)
- {
- // Con::errorf("Cached texture '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown"));
- if (deleteBmp)
- delete bmp;
- return cacheHit;
- }
- return _createTexture( bmp, resourceName, profile, deleteBmp, NULL );
- }
- GFXTextureObject *GFXTextureManager::_createTexture( GBitmap *bmp,
- const String &resourceName,
- GFXTextureProfile *profile,
- bool deleteBmp,
- GFXTextureObject *inObj )
- {
- PROFILE_SCOPE( GFXTextureManager_CreateTexture_Bitmap );
-
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[GFXTextureManager] _createTexture (GBitmap) '%s'",
- resourceName.c_str()
- );
- #endif
- // Massage the bitmap based on any resize rules.
- U32 scalePower = getTextureDownscalePower( profile );
- GBitmap *realBmp = bmp;
- U32 realWidth = bmp->getWidth();
- U32 realHeight = bmp->getHeight();
- if ( scalePower &&
- isPow2(bmp->getWidth()) &&
- isPow2(bmp->getHeight()) &&
- profile->canDownscale() )
- {
- // We only work with power of 2 textures for now, so we
- // don't have to worry about padding.
- // We downscale the bitmap on the CPU... this is the reason
- // you should be using DDS which already has good looking mips.
- GBitmap *padBmp = bmp;
- padBmp->extrudeMipLevels();
- scalePower = getMin( scalePower, padBmp->getNumMipLevels() - 1 );
- realWidth = getMax( (U32)1, padBmp->getWidth() >> scalePower );
- realHeight = getMax( (U32)1, padBmp->getHeight() >> scalePower );
- realBmp = new GBitmap( realWidth, realHeight, false, bmp->getFormat() );
- // Copy to the new bitmap...
- dMemcpy( realBmp->getWritableBits(),
- padBmp->getBits(scalePower),
- padBmp->getBytesPerPixel() * realWidth * realHeight );
- // This line is commented out because createPaddedBitmap is commented out.
- // If that line is added back in, this line should be added back in.
- // delete padBmp;
- }
- // Call the internal create... (use the real* variables now, as they
- // reflect the reality of the texture we are creating.)
- U32 numMips = 0;
- GFXFormat realFmt = realBmp->getFormat();
- _validateTexParams( realWidth, realHeight, profile, numMips, realFmt );
- GFXTextureObject *ret;
- if ( inObj )
- {
- // If the texture has changed in dimensions
- // then we need to recreate it.
- if ( inObj->getWidth() != realWidth ||
- inObj->getHeight() != realHeight ||
- inObj->getFormat() != realFmt )
- ret = _createTextureObject( realHeight, realWidth, 0, realFmt, profile, numMips, false, 0, inObj );
- else
- ret = inObj;
- }
- else
- ret = _createTextureObject(realHeight, realWidth, 0, realFmt, profile, numMips );
- if(!ret)
- {
- SAFE_DELETE(realBmp);
- Con::errorf("GFXTextureManager - failed to create texture (1) for '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown"));
- return NULL;
- }
- // Extrude mip levels
- // Don't do this for fonts!
- if( ret->mMipLevels > 1 && ( realBmp->getNumMipLevels() == 1 ) && ( realBmp->getFormat() != GFXFormatA8 ) &&
- isPow2( realBmp->getHeight() ) && isPow2( realBmp->getWidth() ) && !profile->noMip() )
- {
- // NOTE: This should really be done by extruding mips INTO a DDS file instead
- // of modifying the gbitmap
- realBmp->extrudeMipLevels(false);
- }
- // If _validateTexParams kicked back a different format, than there needs to be
- // a conversion unless it's a sRGB format
- DDSFile *bmpDDS = NULL;
- if( realBmp->getFormat() != realFmt && !profile->isSRGB() )
- {
- const GFXFormat oldFmt = realBmp->getFormat();
- // TODO: Set it up so that ALL format conversions use DDSFile. Rip format
- // switching out of GBitmap entirely.
- if( !realBmp->setFormat( realFmt ) )
- {
- // This is not the ideal implementation...
- bmpDDS = DDSFile::createDDSFileFromGBitmap( realBmp );
- bool convSuccess = false;
- if( bmpDDS != NULL )
- {
- // This shouldn't live here, I don't think
- switch( realFmt )
- {
- case GFXFormatBC1:
- case GFXFormatBC2:
- case GFXFormatBC3:
- // If this is a Normal Map profile, than the data needs to be conditioned
- // to use the swizzle trick
- if( ret->mProfile->getType() == GFXTextureProfile::NormalMap )
- {
- PROFILE_START(DXT_DXTNMSwizzle);
- static DXT5nmSwizzle sDXT5nmSwizzle;
- ImageUtil::swizzleDDS( bmpDDS, sDXT5nmSwizzle );
- PROFILE_END();
- }
- convSuccess = ImageUtil::ddsCompress( bmpDDS, realFmt );
- break;
- default:
- AssertFatal(false, "Attempting to convert to a non-DXT format");
- break;
- }
- }
- if( !convSuccess )
- {
- Con::errorf( "[GFXTextureManager]: Failed to change source format from %s to %s. Cannot create texture.",
- GFXStringTextureFormat[oldFmt], GFXStringTextureFormat[realFmt] );
- delete bmpDDS;
- return NULL;
- }
- }
- #ifdef TORQUE_DEBUG
- else
- {
- //Con::warnf( "[GFXTextureManager]: Changed bitmap format from %s to %s.",
- // GFXStringTextureFormat[oldFmt], GFXStringTextureFormat[realFmt] );
- }
- #endif
- }
- // Call the internal load...
- if( ( bmpDDS == NULL && !_loadTexture( ret, realBmp ) ) || // If we aren't doing a DDS format change, use bitmap load
- ( bmpDDS != NULL && !_loadTexture( ret, bmpDDS ) ) ) // If there is a DDS, than load that instead. A format change took place.
- {
- Con::errorf("GFXTextureManager - failed to load GBitmap for '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown"));
- return NULL;
- }
- // Do statistics and book-keeping...
-
- // - info for the texture...
- ret->mTextureLookupName = resourceName;
- ret->mBitmapSize.set(realWidth, realHeight,0);
- #ifdef TORQUE_DEBUG
- if (resourceName.isNotEmpty())
- ret->mDebugDescription = resourceName;
- else
- ret->mDebugDescription = "Anonymous Texture Object";
- #endif
- if(profile->doStoreBitmap())
- {
- // NOTE: may store a downscaled copy!
- SAFE_DELETE( ret->mBitmap );
- SAFE_DELETE( ret->mDDS );
- if( bmpDDS == NULL )
- ret->mBitmap = new GBitmap( *realBmp );
- else
- ret->mDDS = bmpDDS;
- }
- else
- {
- // Delete the DDS if we made one
- SAFE_DELETE( bmpDDS );
- }
- if ( !inObj )
- _linkTexture( ret );
- // - output debug info?
- // Save texture for debug purpose
- // static int texId = 0;
- // char buff[256];
- // dSprintf(buff, sizeof(buff), "tex_%d", texId++);
- // bmp->writePNGDebug(buff);
- // texId++;
- // Before we delete the bitmap save our transparency flag
- ret->mHasTransparency = realBmp->getHasTransparency();
- // Some final cleanup...
- if(realBmp != bmp)
- SAFE_DELETE(realBmp);
- if (deleteBmp)
- SAFE_DELETE(bmp);
- // Return the new texture!
- return ret;
- }
- GFXTextureObject *GFXTextureManager::createTexture( DDSFile *dds, GFXTextureProfile *profile, bool deleteDDS )
- {
- AssertFatal(dds, "GFXTextureManager::createTexture() - Got NULL dds!");
- // Check the cache first...
- GFXTextureObject *cacheHit = _lookupTexture( dds, profile );
- if ( cacheHit )
- {
- // Con::errorf("Cached texture '%s'", (fileName.isNotEmpty() ? fileName.c_str() : "unknown"));
- if( deleteDDS )
- delete dds;
- return cacheHit;
- }
- return _createTexture( dds, profile, deleteDDS, NULL );
- }
- GFXTextureObject *GFXTextureManager::_createTexture( DDSFile *dds,
- GFXTextureProfile *profile,
- bool deleteDDS,
- GFXTextureObject *inObj )
- {
- PROFILE_SCOPE( GFXTextureManager_CreateTexture_DDS );
- const char *fileName = dds->getTextureCacheString();
- if( !fileName )
- fileName = "unknown";
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[GFXTextureManager] _createTexture (DDS) '%s'",
- fileName
- );
- #endif
- // Ignore padding from the profile.
- U32 numMips = dds->mMipMapCount;
- GFXFormat fmt = dds->mFormat;
- _validateTexParams( dds->getHeight(), dds->getWidth(), profile, numMips, fmt );
- if( fmt != dds->mFormat && !profile->isSRGB())
- {
- Con::errorf( "GFXTextureManager - failed to validate texture parameters for DDS file '%s'", fileName );
- return NULL;
- }
- // Call the internal create... (use the real* variables now, as they
- // reflect the reality of the texture we are creating.)
- GFXTextureObject *ret;
- if ( inObj )
- {
- // If the texture has changed in dimensions
- // then we need to recreate it.
- if ( inObj->getWidth() != dds->getWidth() ||
- inObj->getHeight() != dds->getHeight() ||
- inObj->getFormat() != fmt ||
- inObj->getMipLevels() != numMips )
- ret = _createTextureObject( dds->getHeight(), dds->getWidth(), 0,
- fmt, profile, numMips,
- true, 0, inObj );
- else
- ret = inObj;
- }
- else
- ret = _createTextureObject( dds->getHeight(), dds->getWidth(), 0,
- fmt, profile, numMips, true );
- if(!ret)
- {
- Con::errorf("GFXTextureManager - failed to create texture (1) for '%s' DDSFile.", fileName);
- return NULL;
- }
- // Call the internal load...
- if(!_loadTexture(ret, dds))
- {
- Con::errorf("GFXTextureManager - failed to load DDS for '%s'", fileName);
- return NULL;
- }
- // Do statistics and book-keeping...
- // - info for the texture...
- ret->mTextureLookupName = dds->getTextureCacheString();
- ret->mBitmapSize.set( dds->mWidth, dds->mHeight, 0 );
- #ifdef TORQUE_DEBUG
- ret->mDebugDescription = fileName;
- #endif
- if(profile->doStoreBitmap())
- {
- // NOTE: may store a downscaled copy!
- SAFE_DELETE( ret->mBitmap );
- SAFE_DELETE( ret->mDDS );
- ret->mDDS = new DDSFile( *dds );
- }
- if ( !inObj )
- _linkTexture( ret );
- // - output debug info?
- // Save texture for debug purpose
- // static int texId = 0;
- // char buff[256];
- // dSprintf(buff, sizeof(buff), "tex_%d", texId++);
- // bmp->writePNGDebug(buff);
- // texId++;
- // Save our transparency flag
- ret->mHasTransparency = dds->getHasTransparency();
- if( deleteDDS )
- delete dds;
- // Return the new texture!
- return ret;
- }
- GFXTextureObject *GFXTextureManager::createTexture( const Torque::Path &path, GFXTextureProfile *profile )
- {
- PROFILE_SCOPE( GFXTextureManager_createTexture );
-
- // Resource handles used for loading. Hold on to them
- // throughout this function so that change notifications
- // don't get added, then removed, and then re-added.
-
- Resource< DDSFile > dds;
- Resource< GBitmap > bitmap;
-
- // We need to handle path's that have had "incorrect"
- // extensions parsed out of the file name
- Torque::Path correctPath = validatePath(path);
- // Check the cache first...
- String pathNoExt = Torque::Path::Join( correctPath.getRoot(), ':', correctPath.getPath() );
- pathNoExt = Torque::Path::Join( pathNoExt, '/', correctPath.getFileName() );
- GFXTextureObject *retTexObj = _lookupTexture( pathNoExt, profile );
- if( retTexObj )
- return retTexObj;
- const U32 scalePower = getTextureDownscalePower( profile );
- // If this is a valid file (has an extension) than load it
- Path realPath;
- if( Torque::FS::IsFile( correctPath ) )
- {
- // Check for DDS
- if( sDDSExt.equal(correctPath.getExtension(), String::NoCase ) )
- {
- dds = DDSFile::load( correctPath, scalePower );
- if( dds != NULL )
- {
- realPath = dds.getPath();
- retTexObj = createTexture( dds, profile, false );
- }
- }
- else // Let GBitmap take care of it
- {
- bitmap = GBitmap::load( correctPath );
- if( bitmap != NULL )
- {
- realPath = bitmap.getPath();
- retTexObj = createTexture( bitmap, pathNoExt, profile, false );
- }
- }
- }
- else
- {
- // NOTE -- We should probably remove the code from GBitmap that tries different
- // extensions for things GBitmap loads, and move it here. I think it should
- // be a bit more involved than just a list of extensions. Some kind of
- // extension registration thing, maybe.
- // Check to see if there is a .DDS file with this name (if no extension is provided)
- Torque::Path tryDDSPath = pathNoExt;
- if( tryDDSPath.getExtension().isNotEmpty() )
- tryDDSPath.setFileName( tryDDSPath.getFullFileName() );
- tryDDSPath.setExtension( sDDSExt );
- if( Torque::FS::IsFile( tryDDSPath ) )
- {
- dds = DDSFile::load( tryDDSPath, scalePower );
- if( dds != NULL )
- {
- realPath = dds.getPath();
- retTexObj = createTexture( dds, profile, false );
- }
- }
-
- // Otherwise, retTexObj stays NULL, and fall through to the generic GBitmap
- // load.
- }
- // If we still don't have a texture object yet, feed the correctPath to GBitmap and
- // it will try a bunch of extensions
- if( retTexObj == NULL )
- {
- // Find and load the texture.
- bitmap = GBitmap::load( correctPath );
- if ( bitmap != NULL )
- {
- realPath = bitmap.getPath();
- retTexObj = createTexture( bitmap, pathNoExt, profile, false );
- }
- }
- if ( retTexObj )
- {
- // Store the path for later use.
- retTexObj->mPath = realPath;
- // Register the texture file for change notifications.
- FS::AddChangeNotification( retTexObj->getPath(), this, &GFXTextureManager::_onFileChanged );
- }
- // Could put in a final check for 'retTexObj == NULL' here as an error message.
- return retTexObj;
- }
- GFXTextureObject *GFXTextureManager::createTexture( U32 width, U32 height, void *pixels, GFXFormat format, GFXTextureProfile *profile )
- {
- // For now, stuff everything into a GBitmap and pass it off... This may need to be revisited -- BJG
- GBitmap *bmp = new GBitmap(width, height, 0, format);
- dMemcpy(bmp->getWritableBits(), pixels, width * height * bmp->getBytesPerPixel());
- return createTexture( bmp, String::EmptyString, profile, true );
- }
- GFXTextureObject *GFXTextureManager::createTexture( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, S32 antialiasLevel )
- {
- // Deal with sizing issues...
- U32 localWidth = width;
- U32 localHeight = height;
- // TODO: Format check HERE! -patw
- validateTextureQuality(profile, localWidth, localHeight);
- U32 numMips = numMipLevels;
- GFXFormat checkFmt = format;
- _validateTexParams( localWidth, localHeight, profile, numMips, checkFmt );
- //check to see if we've handled the mips just now, and if not, then handle them here
- if (numMips == numMipLevels && (localWidth != width || localHeight != height))
- {
- numMips = mFloor(mLog2(mMax(localWidth, localHeight))) + 1;
- }
- // AssertFatal( checkFmt == format, "Anonymous texture didn't get the format it wanted." );
- GFXTextureObject *outTex = NULL;
- // If this is a pooled profile then look there first.
- if ( profile->isPooled() )
- {
- outTex = _findPooledTexure( localWidth, localHeight, checkFmt,
- profile, numMips, antialiasLevel );
- // If we got a pooled texture then its
- // already setup... just return it.
- if ( outTex )
- return outTex;
- }
-
- // Create the texture if we didn't get one from the pool.
- if ( !outTex )
- {
- outTex = _createTextureObject( localHeight, localWidth, 0, format, profile, numMips, false, antialiasLevel );
- // Make sure we add it to the pool.
- if ( outTex && profile->isPooled() )
- mTexturePool.insertEqual( profile, outTex );
- }
- if ( !outTex )
- {
- Con::errorf("GFXTextureManager - failed to create anonymous texture.");
- return NULL;
- }
- // And do book-keeping...
- // - texture info
- outTex->mBitmapSize.set(localWidth, localHeight, 0);
- outTex->mAntialiasLevel = antialiasLevel;
- // PWTODO: Need to assign this a lookup name before _linkTexture() is called
- // otherwise it won't get a hash insert call
- _linkTexture( outTex );
- return outTex;
- }
- GFXTextureObject *GFXTextureManager::createTexture( U32 width,
- U32 height,
- U32 depth,
- GFXFormat format,
- GFXTextureProfile *profile,
- U32 numMipLevels)
- {
- PROFILE_SCOPE( GFXTextureManager_CreateTexture_3D );
- // Create texture...
- GFXTextureObject *ret = _createTextureObject( height, width, depth, format, profile, numMipLevels );
- if(!ret)
- {
- Con::errorf("GFXTextureManager - failed to create anonymous texture.");
- return NULL;
- }
- // And do book-keeping...
- // - texture info
- ret->mBitmapSize.set( width, height, depth );
- _linkTexture( ret );
- // Return the new texture!
- return ret;
- }
- Torque::Path GFXTextureManager::validatePath(const Torque::Path &path)
- {
- // We need to handle path's that have had "incorrect"
- // extensions parsed out of the file name
- Torque::Path correctPath = path;
- bool textureExt = false;
- // Easiest case to handle is when there isn't an extension
- if (path.getExtension().isEmpty())
- textureExt = true;
- // Since "dds" isn't registered with GBitmap currently we
- // have to test it separately
- if (sDDSExt.equal(path.getExtension(), String::NoCase))
- textureExt = true;
- // Now loop through the rest of the GBitmap extensions
- // to see if we have any matches
- for (U32 i = 0; i < GBitmap::sRegistrations.size(); i++)
- {
- // If we have gotten a match (either in this loop or before)
- // then we can exit
- if (textureExt)
- break;
- const GBitmap::Registration ® = GBitmap::sRegistrations[i];
- const Vector<String> &extensions = reg.extensions;
- for (U32 j = 0; j < extensions.size(); ++j)
- {
- if (extensions[j].equal(path.getExtension(), String::NoCase))
- {
- // Found a valid texture extension
- textureExt = true;
- break;
- }
- }
- }
- // If we didn't find a valid texture extension then assume that
- // the parsed out "extension" was actually intended to be part of
- // the texture name so add it back
- if (!textureExt)
- {
- correctPath.setFileName(Torque::Path::Join(path.getFileName(), '.', path.getExtension()));
- correctPath.setExtension(String::EmptyString);
- }
- return correctPath;
- }
- GBitmap *GFXTextureManager::loadUncompressedTexture(const Torque::Path &path, GFXTextureProfile *profile, U32 width, U32 height, bool genMips)
- {
- GBitmap* inBitmap = loadUncompressedTexture(path, &GFXTexturePersistentProfile);
- if (inBitmap == NULL)
- {
- Con::warnf("GFXTextureManager::loadUncompressedTexture unable to load texture: %s", path.getFullPath().c_str());
- return NULL;
- }
- // Set the format so we don't have to handle which channels are where.
- if (!inBitmap->setFormat(GFXFormatR8G8B8A8))
- {
- Con::warnf("GFXTextureManager::loadUncompressedTexture unable to handle texture format: %s", path.getFullPath().c_str());
- return NULL;
- }
- GBitmap* outBmp = new GBitmap(width, height, true, GFXFormatR8G8B8A8);
- U8* oBits = (U8*)outBmp->getWritableBits();
- for (S32 y = 0; y < width; y++)
- {
- for (S32 x = 0; x < height; x++)
- {
- ColorI texelColor = inBitmap->sampleTexel(x / F32(width), y / F32(height), true).toColorI(true);
- oBits[(y * width + x) * 4] = texelColor.red;
- oBits[(y * width + x) * 4 + 1] = texelColor.green;
- oBits[(y * width + x) * 4 + 2] = texelColor.blue;
- oBits[(y * width + x) * 4 + 3] = texelColor.alpha;
- }
- }
- if (genMips)
- outBmp->extrudeMipLevels();
- return outBmp;
- }
- GBitmap *GFXTextureManager::loadUncompressedTexture(const Torque::Path &path, GFXTextureProfile *profile)
- {
- PROFILE_SCOPE(GFXTextureManager_loadUncompressedTexture);
- GBitmap *retBitmap = NULL;
- // Resource handles used for loading. Hold on to them
- // throughout this function so that change notifications
- // don't get added, then removed, and then re-added.
- Resource< DDSFile > dds;
- Resource< GBitmap > bitmap;
- // We need to handle path's that have had "incorrect"
- // extensions parsed out of the file name
- Torque::Path correctPath = validatePath(path);
- U32 scalePower = profile ? getTextureDownscalePower(profile) : 0;
- // Check the cache first...
- String pathNoExt = Torque::Path::Join(correctPath.getRoot(), ':', correctPath.getPath());
- pathNoExt = Torque::Path::Join(pathNoExt, '/', correctPath.getFileName());
- // If this is a valid file (has an extension) than load it
- Path realPath;
- if (Torque::FS::IsFile(correctPath))
- {
- PROFILE_SCOPE(GFXTextureManager_loadUncompressedTexture_INNNER1)
- // Check for DDS
- if (sDDSExt.equal(correctPath.getExtension(), String::NoCase))
- {
- dds = DDSFile::load(correctPath, scalePower);
- if (dds != NULL)
- {
- realPath = dds.getPath();
- retBitmap = new GBitmap();
- if (!dds->decompressToGBitmap(retBitmap))
- {
- delete retBitmap;
- retBitmap = NULL;
- }
- }
- }
- else // Let GBitmap take care of it
- {
- bitmap = GBitmap::load(correctPath);
- if (bitmap != NULL)
- {
- realPath = bitmap.getPath();
- retBitmap = new GBitmap(*bitmap);
- if (scalePower &&
- isPow2(retBitmap->getWidth()) &&
- isPow2(retBitmap->getHeight()) &&
- profile->canDownscale())
- {
- retBitmap->extrudeMipLevels();
- retBitmap->chopTopMips(scalePower);
- }
- }
- }
- }
- else
- {
- PROFILE_SCOPE(GFXTextureManager_loadUncompressedTexture_INNNER2)
- // NOTE -- We should probably remove the code from GBitmap that tries different
- // extensions for things GBitmap loads, and move it here. I think it should
- // be a bit more involved than just a list of extensions. Some kind of
- // extension registration thing, maybe.
- // Check to see if there is a .DDS file with this name (if no extension is provided)
- Torque::Path tryDDSPath = pathNoExt;
- if (tryDDSPath.getExtension().isNotEmpty())
- tryDDSPath.setFileName(tryDDSPath.getFullFileName());
- tryDDSPath.setExtension(sDDSExt);
- if (Torque::FS::IsFile(tryDDSPath))
- {
- dds = DDSFile::load(tryDDSPath, scalePower);
- if (dds != NULL)
- {
- realPath = dds.getPath();
- // Decompress dds into the GBitmap
- retBitmap = new GBitmap();
- if (!dds->decompressToGBitmap(retBitmap))
- {
- delete retBitmap;
- retBitmap = NULL;
- }
- }
- }
- // Otherwise, retTexObj stays NULL, and fall through to the generic GBitmap
- // load.
- }
- // If we still don't have a texture object yet, feed the correctPath to GBitmap and
- // it will try a bunch of extensions
- if (retBitmap == NULL)
- {
- PROFILE_SCOPE(GFXTextureManager_loadUncompressedTexture_INNNER3)
- // Find and load the texture.
- bitmap = GBitmap::load(correctPath);
- if (bitmap != NULL)
- {
- retBitmap = new GBitmap(*bitmap);
- if (scalePower &&
- isPow2(retBitmap->getWidth()) &&
- isPow2(retBitmap->getHeight()) &&
- profile->canDownscale())
- {
- retBitmap->extrudeMipLevels();
- retBitmap->chopTopMips(scalePower);
- }
- }
- }
- return retBitmap;
- }
- GFXTextureObject *GFXTextureManager::createCompositeTexture(const Torque::Path &pathR, const Torque::Path &pathG, const Torque::Path &pathB, const Torque::Path &pathA, U32 inputKey[4],
- GFXTextureProfile *profile)
- {
- PROFILE_SCOPE(GFXTextureManager_createCompositeTexture);
-
- String inputKeyStr = String::ToString("%d%d%d%d", inputKey[0], inputKey[1], inputKey[2], inputKey[3]);
- String resourceTag = pathR.getFileName() + pathG.getFileName() + pathB.getFileName() + pathA.getFileName() + inputKeyStr; //associate texture object with a key combo
- GFXTextureObject *cacheHit = _lookupTexture(resourceTag, profile);
- if (cacheHit != NULL) return cacheHit;
- Torque::Path lastValidPath = "";
- GBitmap*bitmap[4];
- if (!pathR.isEmpty())
- {
- bitmap[0] = loadUncompressedTexture(pathR, profile);
- lastValidPath = pathR;
- }
- else
- bitmap[0] = NULL;
- if (!pathG.isEmpty())
- {
- bitmap[1] = loadUncompressedTexture(pathG, profile);
- lastValidPath = pathG;
- }
- else
- bitmap[1] = NULL;
- if (!pathB.isEmpty())
- {
- bitmap[2] = loadUncompressedTexture(pathB, profile);
- lastValidPath = pathB;
- }
- else
- bitmap[2] = NULL;
- if (!pathA.isEmpty())
- {
- bitmap[3] = loadUncompressedTexture(pathA, profile);
- lastValidPath = pathA;
- }
- else
- bitmap[3] = NULL;
-
- Path realPath;
- GFXTextureObject *retTexObj = NULL;
- realPath = validatePath(lastValidPath); //associate path with last valid channel texture in.
- retTexObj = createCompositeTexture(bitmap, inputKey, resourceTag, profile, false);
- if (retTexObj)
- {
- // Store the path for later use.
- retTexObj->mPath = resourceTag;
- // Register the texture file for change notifications.
- FS::AddChangeNotification(retTexObj->getPath(), this, &GFXTextureManager::_onFileChanged);
- }
- // Could put in a final check for 'retTexObj == NULL' here as an error message.
- for (U32 i = 0; i < 4; i++)
- {
- if (bitmap[i])
- {
- bitmap[i]->deleteImage();
- delete bitmap[i];
- }
- }
- return retTexObj;
- }
- void GFXTextureManager::saveCompositeTexture(const Torque::Path &pathR, const Torque::Path &pathG, const Torque::Path &pathB, const Torque::Path &pathA, U32 inputKey[4],
- const Torque::Path &saveAs,GFXTextureProfile *profile)
- {
- PROFILE_SCOPE(GFXTextureManager_saveCompositeTexture);
- String inputKeyStr = String::ToString("%d%d%d%d", inputKey[0], inputKey[1], inputKey[2], inputKey[3]);
- String resourceTag = pathR.getFileName() + pathG.getFileName() + pathB.getFileName() + pathA.getFileName() + inputKeyStr; //associate texture object with a key combo
- GFXTextureObject *cacheHit = _lookupTexture(resourceTag, profile);
- if (cacheHit != NULL)
- {
- cacheHit->dumpToDisk("png", saveAs.getFullPath());
- return;
- }
- Torque::Path lastValidPath = "";
- GBitmap* bitmap[4];
- if (!pathR.isEmpty())
- {
- bitmap[0] = loadUncompressedTexture(pathR, profile);
- lastValidPath = pathR;
- }
- else
- bitmap[0] = NULL;
- if (!pathG.isEmpty())
- {
- bitmap[1] = loadUncompressedTexture(pathG, profile);
- lastValidPath = pathG;
- }
- else
- bitmap[1] = NULL;
- if (!pathB.isEmpty())
- {
- bitmap[2] = loadUncompressedTexture(pathB, profile);
- lastValidPath = pathB;
- }
- else
- bitmap[2] = NULL;
- if (!pathA.isEmpty())
- {
- bitmap[3] = loadUncompressedTexture(pathA, profile);
- lastValidPath = pathA;
- }
- else
- bitmap[3] = NULL;
- Path realPath;
- GFXTextureObject *retTexObj = NULL;
- realPath = validatePath(lastValidPath); //associate path with last valid channel texture in.
- retTexObj = createCompositeTexture(bitmap, inputKey, resourceTag, profile, false);
- if (retTexObj != NULL)
- retTexObj->dumpToDisk("png", saveAs.getFullPath());
- return;
- }
- DefineEngineFunction(saveCompositeTexture, void, (const char* pathR, const char* pathG, const char* pathB, const char* pathA,
- const char * inputKeyString, const char* saveAs),
- ("", "", "", "", "", ""), "File1,file2,file3,file4,[chanels for r g b and a locations],saveAs")
- {
- U32 inputKey[4] = {0,0,0,0};
- if (String::compare(inputKeyString, "") != 0)
- {
- dSscanf(inputKeyString, "%i %i %i %i", &inputKey[0], &inputKey[1], &inputKey[2], &inputKey[3]);
- }
- GFX->getTextureManager()->saveCompositeTexture(pathR, pathG, pathB, pathA, inputKey, saveAs, &GFXTexturePersistentProfile);
- }
- GFXTextureObject *GFXTextureManager::createCompositeTexture(GBitmap*bmp[4], U32 inputKey[4],
- const String &resourceName, GFXTextureProfile *profile, bool deleteBmp)
- {
- S32 lastValidTex = -1;
- for (U32 i = 0; i < 4; i++)
- {
- if (bmp[i])
- lastValidTex = i;
- }
- if (lastValidTex == -1)
- {
- Con::errorf(ConsoleLogEntry::General, "GFXTextureManager::createCompositeTexture() - Got all NULL bitmaps!");
- return NULL;
- }
- GFXTextureObject* cacheHit = _lookupTexture(resourceName, profile);
- if (cacheHit != NULL)
- {
- // Con::errorf("Cached texture '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown"));
- return cacheHit;
- }
- U8 rChan, gChan, bChan, aChan;
- GBitmap *outBitmap = new GBitmap();
- outBitmap->allocateBitmap(bmp[lastValidTex]->getWidth(), bmp[lastValidTex]->getHeight(),false, GFXFormatR8G8B8A8);
- //pack additional bitmaps into the origional
- for (U32 x = 0; x < bmp[lastValidTex]->getWidth(); x++)
- {
- for (U32 y = 0; y < bmp[lastValidTex]->getHeight(); y++)
- {
- if (bmp[0])
- rChan = bmp[0]->getChanelValueAt(x, y, inputKey[0]);
- else
- rChan = 255;
- if (bmp[1])
- gChan = bmp[1]->getChanelValueAt(x, y, inputKey[1]);
- else
- gChan = 255;
- if (bmp[2])
- bChan = bmp[2]->getChanelValueAt(x, y, inputKey[2]);
- else
- bChan = 255;
- if (bmp[3])
- aChan = bmp[3]->getChanelValueAt(x, y, inputKey[3]);
- else
- aChan = 255;
- outBitmap->setColor(x, y, ColorI(rChan, gChan, bChan, aChan));
- }
- }
- if (deleteBmp)
- {
- delete[] bmp;
- }
- GFXTextureObject * ret= _createTexture(outBitmap, resourceName, profile, deleteBmp, NULL);
- delete outBitmap;
- return ret;
- }
- GFXTextureObject* GFXTextureManager::_findPooledTexure( U32 width,
- U32 height,
- GFXFormat format,
- GFXTextureProfile *profile,
- U32 numMipLevels,
- S32 antialiasLevel )
- {
- PROFILE_SCOPE( GFXTextureManager_FindPooledTexure );
- GFXTextureObject *outTex;
- // First see if we have a free one in the pool.
- TexturePoolMap::Iterator iter = mTexturePool.find( profile );
- for ( ; iter != mTexturePool.end() && iter->key == profile; iter++ )
- {
- outTex = iter->value;
- // If the reference count is 1 then we're the only
- // ones holding on to this texture and we can hand
- // it out if the size matches... else its in use.
- if ( outTex->getRefCount() != 1 )
- continue;
- // Check for a match... if so return it. The assignment
- // to a GFXTexHandle will take care of incrementing the
- // reference count and keeping it from being handed out
- // to anyone else.
- if ( outTex->getFormat() == format &&
- outTex->getWidth() == width &&
- outTex->getHeight() == height &&
- outTex->getMipLevels() == numMipLevels &&
- outTex->mAntialiasLevel == antialiasLevel )
- return outTex;
- }
- return NULL;
- }
- void GFXTextureManager::hashInsert( GFXTextureObject *object )
- {
- if ( object->mTextureLookupName.isEmpty() )
- return;
-
- U32 key = object->mTextureLookupName.getHashCaseInsensitive() % mHashCount;
- object->mHashNext = mHashTable[key];
- mHashTable[key] = object;
- }
- void GFXTextureManager::hashRemove( GFXTextureObject *object )
- {
- if ( object->mTextureLookupName.isEmpty() )
- return;
- U32 key = object->mTextureLookupName.getHashCaseInsensitive() % mHashCount;
- GFXTextureObject **walk = &mHashTable[key];
- while(*walk)
- {
- if(*walk == object)
- {
- *walk = object->mHashNext;
- break;
- }
- walk = &((*walk)->mHashNext);
- }
- }
- GFXTextureObject* GFXTextureManager::hashFind( const String &name )
- {
- if ( name.isEmpty() )
- return NULL;
- U32 key = name.getHashCaseInsensitive() % mHashCount;
- GFXTextureObject *walk = mHashTable[key];
- for(; walk; walk = walk->mHashNext)
- {
- if( walk->mTextureLookupName.equal( name, String::NoCase ) )
- break;
- }
- return walk;
- }
- void GFXTextureManager::freeTexture(GFXTextureObject *texture, bool zombify)
- {
- // Ok, let the backend deal with it.
- _freeTexture(texture, zombify);
- }
- void GFXTextureManager::refreshTexture(GFXTextureObject *texture)
- {
- _refreshTexture(texture);
- }
- void GFXTextureManager::_linkTexture( GFXTextureObject *obj )
- {
- // info for the profile
- GFXTextureProfile::updateStatsForCreation(obj);
- // info for the cache
- hashInsert(obj);
- // info for the master list
- if( mListHead == NULL )
- mListHead = obj;
- if( mListTail != NULL )
- mListTail->mNext = obj;
- obj->mPrev = mListTail;
- mListTail = obj;
- }
- void GFXTextureManager::deleteTexture( GFXTextureObject *texture )
- {
- if ( mTextureManagerState == GFXTextureManager::Dead )
- return;
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[GFXTextureManager] deleteTexture '%s'",
- texture->mTextureLookupName.c_str()
- );
- #endif
- if( mListHead == texture )
- mListHead = texture->mNext;
- if( mListTail == texture )
- mListTail = texture->mPrev;
- hashRemove( texture );
- // If we have a path for the texture then
- // remove change notifications for it.
- Path texPath = texture->getPath();
- if ( !texPath.isEmpty() )
- FS::RemoveChangeNotification( texPath, this, &GFXTextureManager::_onFileChanged );
- GFXTextureProfile::updateStatsForDeletion(texture);
- freeTexture( texture );
- }
- void GFXTextureManager::_validateTexParams( const U32 width, const U32 height,
- const GFXTextureProfile *profile,
- U32 &inOutNumMips, GFXFormat &inOutFormat )
- {
- // Validate mipmap parameter. If this profile requests no mips, set mips to 1.
- if( profile->noMip() )
- {
- inOutNumMips = 1;
- }
- else if( !isPow2( width ) || !isPow2( height ) )
- {
- // If a texture is not power-of-2 in size for both dimensions, it must
- // have only 1 mip level.
- inOutNumMips = 1;
- }
-
- // Check format, and compatibility with texture profile requirements
- bool autoGenSupp = ( inOutNumMips == 0 );
- // If the format is non-compressed, and the profile requests a compressed format
- // than change the format.
- GFXFormat testingFormat = inOutFormat;
- if( profile->getCompression() != GFXTextureProfile::NONE )
- {
- const S32 offset = profile->getCompression() - GFXTextureProfile::BC1;
- testingFormat = GFXFormat( GFXFormatBC1 + offset );
- // No auto-gen mips on compressed textures
- autoGenSupp = false;
- }
- if (profile->isSRGB())
- testingFormat = ImageUtil::toSRGBFormat(testingFormat);
- // inOutFormat is not modified by this method
- GFXCardProfiler* cardProfiler = GFX->getCardProfiler();
- bool chekFmt = cardProfiler->checkFormat(testingFormat, profile, autoGenSupp);
-
- if( !chekFmt )
- {
- // It tested for a compressed format, and didn't like it
- if( testingFormat != inOutFormat && profile->getCompression() )
- testingFormat = inOutFormat; // Reset to requested format, and try again
- // Trying again here, so reset autogen mip
- autoGenSupp = ( inOutNumMips == 0 );
- // Wow more weak sauce. There should be a better way to do this.
- switch( inOutFormat )
- {
- case GFXFormatR8G8B8:
- testingFormat = GFXFormatR8G8B8X8;
- chekFmt = cardProfiler->checkFormat(testingFormat, profile, autoGenSupp);
- break;
- case GFXFormatA8:
- testingFormat = GFXFormatR8G8B8A8;
- chekFmt = cardProfiler->checkFormat(testingFormat, profile, autoGenSupp);
- break;
-
- default:
- chekFmt = cardProfiler->checkFormat(testingFormat, profile, autoGenSupp);
- break;
- }
- }
- // Write back num mips that need to be generated by GBitmap
- if( !chekFmt )
- Con::errorf( "Format %s not supported with specified profile.", GFXStringTextureFormat[inOutFormat] );
- else
- {
- inOutFormat = testingFormat;
- // If auto gen mipmaps were requested, and they aren't supported for whatever
- // reason, than write out the number of mips that need to be generated.
- //
- // NOTE: Does this belong here?
- if( inOutNumMips == 0 && !autoGenSupp )
- {
- inOutNumMips = mFloor(mLog2(mMax(width, height))) + 1;
- }
- }
- }
- GFXCubemap* GFXTextureManager::createCubemap( const Torque::Path &path )
- {
- // Very first thing... check the cache.
- CubemapTable::Iterator iter = mCubemapTable.find( path.getFullPath() );
- if ( iter != mCubemapTable.end() )
- return iter->value;
- // Not in the cache... we have to load it ourselves.
- // First check for a DDS file.
- if ( !sDDSExt.equal( path.getExtension(), String::NoCase ) )
- {
- // At the moment we only support DDS cubemaps.
- return NULL;
- }
- const U32 scalePower = getTextureDownscalePower( NULL );
- // Ok... load the DDS file then.
- Resource<DDSFile> dds = DDSFile::load( path, scalePower );
- if ( !dds || !dds->isCubemap() )
- {
- // This wasn't a cubemap... give up too.
- return NULL;
- }
- // We loaded the cubemap dds, so now we create the GFXCubemap from it.
- GFXCubemap *cubemap = GFX->createCubemap();
- cubemap->initStatic( dds );
- cubemap->_setPath( path.getFullPath() );
- // Store the cubemap into the cache.
- mCubemapTable.insertUnique( path.getFullPath(), cubemap );
- return cubemap;
- }
- void GFXTextureManager::releaseCubemap( GFXCubemap *cubemap )
- {
- if ( mTextureManagerState == GFXTextureManager::Dead )
- return;
- const String &path = cubemap->getPath();
- CubemapTable::Iterator iter = mCubemapTable.find( path );
- if ( iter != mCubemapTable.end() && iter->value == cubemap )
- mCubemapTable.erase( iter );
- // If we have a path for the texture then
- // remove change notifications for it.
- //Path texPath = texture->getPath();
- //if ( !texPath.isEmpty() )
- //FS::RemoveChangeNotification( texPath, this, &GFXTextureManager::_onFileChanged );
- }
- void GFXTextureManager::_onFileChanged( const Torque::Path &path )
- {
- String pathNoExt = Torque::Path::Join( path.getRoot(), ':', path.getPath() );
- pathNoExt = Torque::Path::Join( pathNoExt, '/', path.getFileName() );
- // See if we've got it loaded.
- GFXTextureObject *obj = hashFind( pathNoExt );
- if ( !obj || path != obj->getPath() )
- return;
- Con::errorf( "[GFXTextureManager::_onFileChanged] : File changed [%s]", path.getFullPath().c_str() );
- const U32 scalePower = getTextureDownscalePower( obj->mProfile );
- if ( sDDSExt.equal( path.getExtension(), String::NoCase) )
- {
- Resource<DDSFile> dds = DDSFile::load( path, scalePower );
- if ( dds )
- _createTexture( dds, obj->mProfile, false, obj );
- }
- else
- {
- Resource<GBitmap> bmp = GBitmap::load( path );
- if( bmp )
- _createTexture( bmp, obj->mTextureLookupName, obj->mProfile, false, obj );
- }
- }
- void GFXTextureManager::reloadTextures()
- {
- GFXTextureObject *tex = mListHead;
- while ( tex != NULL )
- {
- const Torque::Path path( tex->mPath );
- if ( !path.isEmpty() )
- {
- const U32 scalePower = getTextureDownscalePower( tex->mProfile );
- if ( sDDSExt.equal( path.getExtension(), String::NoCase ) )
- {
- Resource<DDSFile> dds = DDSFile::load( path, scalePower );
- if ( dds )
- _createTexture( dds, tex->mProfile, false, tex );
- }
- else
- {
- Resource<GBitmap> bmp = GBitmap::load( path );
- if( bmp )
- _createTexture( bmp, tex->mTextureLookupName, tex->mProfile, false, tex );
- }
- }
- tex = tex->mNext;
- }
- }
- DefineEngineFunction( flushTextureCache, void, (),,
- "Releases all textures and resurrects the texture manager.\n"
- "@ingroup GFX\n" )
- {
- if ( !GFX || !TEXMGR )
- return;
- TEXMGR->zombify();
- TEXMGR->resurrect();
- }
- DefineEngineFunction( cleanupTexturePool, void, (),,
- "Release the unused pooled textures in texture manager freeing up video memory.\n"
- "@ingroup GFX\n" )
- {
- if ( !GFX || !TEXMGR )
- return;
- TEXMGR->cleanupPool();
- }
- DefineEngineFunction( reloadTextures, void, (),,
- "Reload all the textures from disk.\n"
- "@ingroup GFX\n" )
- {
- if ( !GFX || !TEXMGR )
- return;
- TEXMGR->reloadTextures();
- PROBEMGR->reloadTextures();
- }
|