You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
701 lines
24 KiB
701 lines
24 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program 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. |
|
|
|
This program 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 this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include "HeadSpin.h" |
|
#include "max.h" |
|
#include <commdlg.h> |
|
#include "bmmlib.h" |
|
|
|
#include "plGImage/plMipmap.h" |
|
#include "hsExceptionStack.h" |
|
#include "plGImage/hsCodecManager.h" |
|
|
|
#include "plBitmapCreator.h" |
|
|
|
#include "MaxMain/plPluginResManager.h" |
|
#include "MaxExport/plErrorMsg.h" |
|
#include "MaxPlasmaMtls/Layers/plStaticEnvLayer.h" |
|
|
|
#include "plGImage/plMipmap.h" |
|
#include "plGImage/plDynamicTextMap.h" |
|
#include "plGImage/plCubicEnvironmap.h" |
|
#include "pnKeyedObject/plKey.h" |
|
#include "pnKeyedObject/plUoid.h" |
|
#include "plResMgr/plRegistryHelpers.h" |
|
#include "plResMgr/plLocalization.h" |
|
#include "plAgeDescription/plAgeDescription.h" |
|
|
|
//// plCommonBitmapLib /////////////////////////////////////////////////////// |
|
// Derived class for our textures, since they all go in a common page |
|
// (namely, "Textures") |
|
|
|
#include "MaxMain/plCommonObjLib.h" |
|
|
|
class plCommonBitmapLib : public plCommonObjLib |
|
{ |
|
public: |
|
virtual bool IsInteresting( const plKey &objectKey ) |
|
{ |
|
if( objectKey->GetUoid().GetClassType() == plCubicEnvironmap::Index() || |
|
objectKey->GetUoid().GetClassType() == plMipmap::Index() ) |
|
{ |
|
return true; |
|
} |
|
return false; |
|
} |
|
}; |
|
|
|
static plCommonBitmapLib sCommonBitmapLib; |
|
|
|
|
|
plBitmapCreator::plBitmapCreator() |
|
{ |
|
fErrorMsg = nil; |
|
} |
|
plBitmapCreator::~plBitmapCreator() |
|
{ |
|
} |
|
|
|
plBitmapCreator &plBitmapCreator::Instance() |
|
{ |
|
static plBitmapCreator fInstance; |
|
return fInstance; |
|
} |
|
|
|
void plBitmapCreator::Init( bool save, plErrorMsg *msg ) |
|
{ |
|
fErrorMsg = msg; |
|
} |
|
|
|
void plBitmapCreator::DeInit( void ) |
|
{ |
|
CleanUpMaps(); |
|
} |
|
|
|
void plBitmapCreator::CleanUpMaps( void ) |
|
{ |
|
sCommonBitmapLib.ClearObjectList(); |
|
} |
|
|
|
void plBitmapCreator::DeleteExportedBitmap( const plKey &constKey ) |
|
{ |
|
plKey key = constKey; |
|
sCommonBitmapLib.RemoveObjectAndKey( key ); |
|
} |
|
|
|
// |
|
// Create Bitmap |
|
// |
|
plMipmap *plBitmapCreator::ICreateBitmap(plBitmapData *bd) |
|
{ |
|
hsGuardBegin("hsConverterUtils::CreateBitmap"); |
|
|
|
// Load the bitmap |
|
BitmapInfo bi; |
|
bi.SetName(bd->fileName); |
|
|
|
#if 0 // This isn't really an issue since the textures are packed -Colin |
|
const int kMaxFileNameLength = 30; |
|
if (strlen(bi.Filename()) > kMaxFileNameLength) |
|
{ |
|
// Allow to continue, But make it painful |
|
char errStr[256]; |
|
sprintf(errStr, "File name longer than %d, won't burn to CD (%s)", kMaxFileNameLength, bi.Filename());//bitmapTex->GetName()); |
|
MessageBox(GetActiveWindow(), errStr, bd->fileName, MB_OK|MB_ICONEXCLAMATION); |
|
} |
|
#endif |
|
|
|
bool notMipped = (bd->texFlags & plMipmap::kForceOneMipLevel) != 0; |
|
float sigma = bd->sig; |
|
|
|
// Load the bitmap |
|
Bitmap *bm = TheManager->Load(&bi); |
|
if (!bm) |
|
{ |
|
// FIXME |
|
/* |
|
if (fErrorMsg->Set(!(fWarned & kWarnedNoMoreBitmapLoadErr), |
|
"Error loading bitmap", pathName).CheckAskOrCancel()) |
|
{ |
|
fWarned |= kWarnedNoMoreBitmapLoadErr; |
|
} |
|
*/ |
|
return nil; |
|
} |
|
BitmapStorage *storage = bm->Storage(); |
|
BitmapInfo *bInfo = &storage->bi; |
|
|
|
ICheckOutBitmap(bInfo, bm, bd->fileName); |
|
|
|
// |
|
// Create a plMipmap |
|
// |
|
plMipmap *hBitmap = new plMipmap; |
|
if( (bm->Width() ^ (bm->Width() & -bm->Width())) |
|
||(bm->Height() ^ (bm->Height() & -bm->Height())) ) |
|
{ |
|
IResampBitmap(bm, *hBitmap); |
|
} |
|
else if( ((bm->Width() >> 3) > bm->Height())||((bm->Height() >> 3) > bm->Width()) ) |
|
{ |
|
IResampBitmap(bm, *hBitmap); |
|
} |
|
else |
|
{ |
|
ICopyBitmap(bm, *hBitmap); |
|
} |
|
bm->DeleteThis(); |
|
|
|
if( bd->invertAlpha ) |
|
IInvertAlpha(*hBitmap); |
|
|
|
// Do it |
|
plMipmap *hMipmap = nil; |
|
if (sigma > 0.f) |
|
{ |
|
hMipmap = new plMipmap(hBitmap, sigma, bd->createFlags, bd->detailDropoffStart, |
|
bd->detailDropoffStop, bd->detailMax, bd->detailMin); |
|
} |
|
else |
|
{ |
|
hMipmap = new plMipmap(hBitmap, -1.f, bd->createFlags, bd->detailDropoffStart, |
|
bd->detailDropoffStop, bd->detailMax, bd->detailMin); |
|
} |
|
delete hBitmap; |
|
|
|
/// Clamp the border if we're using clamping |
|
if( bd->clampFlags != 0 ) |
|
{ |
|
hMipmap->EnsureKonstantBorder( ( bd->clampFlags & plBitmapData::kClampU ) ? true : false, |
|
( bd->clampFlags & plBitmapData::kClampV ) ? true : false ); |
|
} |
|
|
|
/// Cut this down to whatever size we were told to :) |
|
if( bd->maxDimension != 0 ) |
|
hMipmap->ClipToMaxSize( bd->maxDimension ); |
|
|
|
if( notMipped ) |
|
{ |
|
// Done AFTER ClipToMaxSize() so we still get the export size specified |
|
hMipmap->RemoveMipping(); |
|
} |
|
|
|
hBitmap = hMipmap; |
|
|
|
uint32_t flagsToSet = 0; |
|
|
|
if( bd->texFlags & plMipmap::kNoMaxSize ) |
|
flagsToSet |= plMipmap::kNoMaxSize; |
|
if( bd->texFlags & plMipmap::kHalfSize ) |
|
flagsToSet |= plMipmap::kHalfSize; |
|
if( bd->texFlags & plMipmap::kDontThrowAwayImage ) |
|
flagsToSet |= plMipmap::kDontThrowAwayImage; |
|
|
|
hBitmap->SetFlags( hBitmap->GetFlags() | flagsToSet ); |
|
if (bd->useJPEG) |
|
hBitmap->fCompressionType = plMipmap::kJPEGCompression; |
|
|
|
// FIXME |
|
if (/*fSave &&*/ !(bd->texFlags & plMipmap::kForceNonCompressed) && !bd->useJPEG) |
|
{ |
|
// Are we on? Check Plasma Util panel |
|
// SwitchUtil *pu = (SwitchUtil *)CreateInstance(UTILITY_CLASS_ID, PlasmaUtilClassID); |
|
// if (!pu || pu->TextureCompressionEnabled()) |
|
{ |
|
plMipmap *compressed = hsCodecManager::Instance().CreateCompressedMipmap(plMipmap::kDirectXCompression, hBitmap); |
|
// hsDXTSoftwareCodec::Instance().CreateCompressedBitmap(hBitmap); |
|
// hsDXTDirectXCodec::Instance().CreateCompressedBitmap(hBitmap); |
|
|
|
if (compressed) |
|
{ |
|
delete hBitmap; |
|
hBitmap = compressed; |
|
hBitmap->SetFlags( hBitmap->GetFlags() | flagsToSet ); |
|
} |
|
} |
|
} |
|
|
|
return hBitmap; |
|
hsGuardEnd; |
|
} |
|
|
|
|
|
|
|
|
|
// |
|
// Verify that bitmap is the correct type/size |
|
// |
|
void plBitmapCreator::ICheckOutBitmap(BitmapInfo* bInfo, Bitmap* bm, const char *fileName) |
|
{ |
|
hsGuardBegin("hsConverterUtils::ICheckOutBitmap"); |
|
|
|
// Check out bitmap |
|
if (bm->Flags() & MAP_FLIPPED) |
|
MessageBox(GetActiveWindow(), "Bitmap is flipped horizontally", fileName, MB_OK); |
|
if (bm->Flags() & MAP_INVERTED) |
|
MessageBox(GetActiveWindow(), "Bitmap is inverted vertically", fileName, MB_OK); |
|
|
|
if (bInfo->Flags() & MAP_FLIPPED) |
|
MessageBox(GetActiveWindow(), "BI:Bitmap is flipped horizontally", fileName, MB_OK); |
|
if (bInfo->Flags() & MAP_INVERTED) |
|
MessageBox(GetActiveWindow(), "BI:Bitmap is inverted vertically", fileName, MB_OK); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
int plBitmapCreator::IResampBitmap(Bitmap *bm, plMipmap &hBitmap) |
|
{ |
|
hsGuardBegin("hsConverterUtils::IResampBitmap"); |
|
|
|
BitmapStorage *storage = bm->Storage(); |
|
BitmapInfo *bInfo = &storage->bi; |
|
|
|
int dbgW = bm->Width(), dbgH = bm->Height(); |
|
int it; |
|
for( it = 1; it <= bm->Width(); it <<= 1 ); |
|
it >>= 1; |
|
hBitmap.fWidth = it; |
|
for( it = 1; it <= bm->Height(); it <<= 1 ); |
|
it >>= 1; |
|
hBitmap.fHeight = it; |
|
if( (hBitmap.fHeight >> 3) > hBitmap.fWidth ) |
|
hBitmap.fHeight = hBitmap.fWidth << 3; |
|
else |
|
if( (hBitmap.fWidth >> 3) > hBitmap.fHeight ) |
|
hBitmap.fWidth = hBitmap.fHeight << 3; |
|
|
|
hBitmap.fPixelSize = 32; |
|
hBitmap.fRowBytes = hBitmap.fWidth * hBitmap.fPixelSize >> 3; |
|
hBitmap.fNumLevels = 1; |
|
|
|
hBitmap.fImage = HSMemory::New(hBitmap.fRowBytes * hBitmap.fHeight); |
|
|
|
#ifdef COLOR_BLACK_WHITE |
|
hBitmap.fFlags |= plMipmap::kColorWhite | plMipmap::kColorBlack; |
|
#endif // COLOR_BLACK_WHITE |
|
hBitmap.fFlags &= ~( plMipmap::kAlphaBitFlag | plMipmap::kAlphaChannelFlag ); |
|
|
|
int y,x; |
|
float scaleY, scaleX; |
|
hsRGBAColor32 *dstColor; |
|
dstColor = (hsRGBAColor32*)hBitmap.fImage; |
|
|
|
scaleX = ((float)bm->Width())/(float)hBitmap.fWidth; |
|
scaleY = ((float)bm->Height())/(float)hBitmap.fHeight; |
|
for (y = 0; y < hBitmap.fHeight; y++) |
|
{ |
|
for (x = 0; x < hBitmap.fWidth; x++) |
|
{ |
|
BMM_Color_64 c64_00, c64_01, c64_10, c64_11; |
|
int ix, iy; |
|
float fracX, fracY; |
|
float t; |
|
t = x * scaleX; |
|
ix = (int)t; |
|
fracX = t-ix; |
|
t = y * scaleY; |
|
iy = (int)t; |
|
fracY = t-iy; |
|
|
|
int ret = storage->GetPixels(ix,iy,1,&c64_00); |
|
// FIXME |
|
// fErrorMsg->Set(ret == 0, "ResampBitmap", "Failure getting pixels %dX%d", x, y).Check(); |
|
ret = storage->GetPixels(ix+1,iy,1,&c64_10); |
|
ret = storage->GetPixels(ix,iy+1,1,&c64_01); |
|
ret = storage->GetPixels(ix+1,iy+1,1,&c64_11); |
|
|
|
dstColor->r = (unsigned char)(255.0 / 65535.0 |
|
* ( c64_00.r * (1.f - fracX) * (1.f - fracY) |
|
+ c64_10.r * ( fracX) * (1.f - fracY) |
|
+ c64_01.r * (1.f - fracX) * ( fracY) |
|
+ c64_11.r * ( fracX) * ( fracY) )); |
|
dstColor->g = (unsigned char)(255.0 / 65535.0 |
|
* ( c64_00.g * (1.f - fracX) * (1.f - fracY) |
|
+ c64_10.g * ( fracX) * (1.f - fracY) |
|
+ c64_01.g * (1.f - fracX) * ( fracY) |
|
+ c64_11.g * ( fracX) * ( fracY) )); |
|
dstColor->b = (unsigned char)(255.0 / 65535.0 |
|
* ( c64_00.b * (1.f - fracX) * (1.f - fracY) |
|
+ c64_10.b * ( fracX) * (1.f - fracY) |
|
+ c64_01.b * (1.f - fracX) * ( fracY) |
|
+ c64_11.b * ( fracX) * ( fracY) )); |
|
dstColor->a = (unsigned char)(255.0 / 65535.0 |
|
* ( c64_00.a * (1.f - fracX) * (1.f - fracY) |
|
+ c64_10.a * ( fracX) * (1.f - fracY) |
|
+ c64_01.a * (1.f - fracX) * ( fracY) |
|
+ c64_11.a * ( fracX) * ( fracY) )); |
|
|
|
|
|
#ifdef COLOR_BLACK_WHITE |
|
if( dstColor->r | dstColor->g | dstColor->b ) |
|
hBitmap.fFlags &= ~plMipmap::kColorBlack; |
|
if( ~(dstColor->r & dstColor->g & dstColor->b) ) |
|
hBitmap.fFlags &= ~plMipmap::kColorWhite; |
|
#endif // COLOR_BLACK_WHITE |
|
|
|
if( dstColor->a < 255 ) |
|
{ |
|
hBitmap.fFlags |= plMipmap::kAlphaBitFlag; |
|
if( dstColor->a > 0 ) |
|
hBitmap.fFlags |= plMipmap::kAlphaChannelFlag; |
|
} |
|
dstColor++; |
|
} |
|
} |
|
if( hBitmap.fFlags & plMipmap::kAlphaChannelFlag ) |
|
hBitmap.fFlags &= ~plMipmap::kAlphaBitFlag; |
|
|
|
return 0; |
|
hsGuardEnd; |
|
} |
|
|
|
int plBitmapCreator::ICopyBitmap(Bitmap *bm, plMipmap &hBitmap) |
|
{ |
|
hsGuardBegin("hsConverterUtils::ICopyBitmap"); |
|
|
|
BitmapStorage *storage = bm->Storage(); |
|
BitmapInfo *bInfo = &storage->bi; |
|
|
|
hBitmap.fWidth = bm->Width(); |
|
hBitmap.fHeight = bm->Height(); |
|
hBitmap.fPixelSize = 32; |
|
hBitmap.fRowBytes = bm->Width()*hBitmap.fPixelSize/8; |
|
hBitmap.fNumLevels = 1; |
|
|
|
hBitmap.fImage = HSMemory::New(hBitmap.fRowBytes * hBitmap.fHeight); |
|
|
|
#ifdef COLOR_BLACK_WHITE |
|
hBitmap.fFlags |= plMipmap::kColorWhite | plMipmap::kColorBlack; |
|
#endif // COLOR_BLACK_WHITE |
|
hBitmap.fFlags &= ~( plMipmap::kAlphaBitFlag | plMipmap::kAlphaChannelFlag ); |
|
|
|
int y,x; |
|
hsRGBAColor32 *dstColor; |
|
dstColor = (hsRGBAColor32*)hBitmap.fImage; |
|
|
|
for (y = 0; y < hBitmap.fHeight; y++) |
|
{ |
|
for (x = 0; x < hBitmap.fWidth; x++) |
|
{ |
|
BMM_Color_64 c64; |
|
int ret = storage->GetPixels(x,y,1,&c64); |
|
// FIXME |
|
// fErrorMsg->Set(ret == 0, "CopyBitmap", "Failure getting pixels %dX%d", x, y).Check(); |
|
|
|
// Convert from 16 bits to 8 bits |
|
dstColor->r = (char)(255.0*c64.r/65535.0); |
|
dstColor->g = (char)(255.0*c64.g/65535.0); |
|
dstColor->b = (char)(255.0*c64.b/65535.0); |
|
dstColor->a = (char)(255.0*c64.a/65535.0); |
|
|
|
#ifdef COLOR_BLACK_WHITE |
|
if( dstColor->r | dstColor->g | dstColor->b ) |
|
hBitmap.fFlags &= ~plMipmap::kColorBlack; |
|
if( ~(dstColor->r & dstColor->g & dstColor->b) ) |
|
hBitmap.fFlags &= ~plMipmap::kColorWhite; |
|
#endif // COLOR_BLACK_WHITE |
|
|
|
if( dstColor->a < 255 ) |
|
{ |
|
hBitmap.fFlags |= plMipmap::kAlphaBitFlag; |
|
if( dstColor->a > 0 ) |
|
hBitmap.fFlags |= plMipmap::kAlphaChannelFlag; |
|
} |
|
dstColor++; |
|
} |
|
} |
|
if( hBitmap.fFlags & plMipmap::kAlphaChannelFlag ) |
|
hBitmap.fFlags &= ~plMipmap::kAlphaBitFlag; |
|
|
|
return 0; |
|
hsGuardEnd; |
|
} |
|
|
|
int plBitmapCreator::IInvertAlpha(plMipmap& hBitmap) |
|
{ |
|
hsGuardBegin("hsConverterUtils::ICopyBitmap"); |
|
|
|
hsAssert(hBitmap.fPixelSize == 32, "Only RGBA32 implemented"); |
|
if( hBitmap.fPixelSize != 32 ) |
|
return -1; |
|
|
|
hsRGBAColor32* dstColor = (hsRGBAColor32*)hBitmap.fImage; |
|
int n = hBitmap.GetWidth() * hBitmap.GetHeight(); |
|
int i; |
|
for( i = 0; i < n; i++ ) |
|
{ |
|
dstColor->a = 255 - dstColor->a; |
|
dstColor++; |
|
} |
|
|
|
return 0; |
|
hsGuardEnd; |
|
} |
|
|
|
plBitmap *plBitmapCreator::CreateTexture(plBitmapData *bd, const plLocation &loc, int clipID) |
|
{ |
|
plBitmap* bm = ICreateTexture(bd, loc, clipID); |
|
|
|
for (int i = 0; i < plLocalization::GetNumLocales(); i++) |
|
{ |
|
char localName[MAX_PATH]; |
|
if (plLocalization::ExportGetLocalized(bd->fileName, i, localName)) |
|
{ |
|
const char* oldName = bd->fileName; |
|
bd->fileName = localName; |
|
ICreateTexture(bd, loc, clipID); |
|
bd->fileName = oldName; |
|
} |
|
} |
|
|
|
return bm; |
|
} |
|
|
|
//// ICreateTexture //////////////////////////////////////////////////////////// |
|
// Plasma texture creator. Pass it a completed bitmapdata structure and it |
|
// returns a registered texture pointer, or nil if something goes wrong. |
|
// |
|
// 9.14.2001 mcn - clipID added to uniquely identify mipmaps that have been |
|
// rescaled differently (approximately represents how many powers of 2 it was |
|
// scaled down from the original source). |
|
// |
|
// 3.29.2002 mcn - Moved to plBitmapCreator, where it really belongs, and |
|
// added code to handle tracking/cleaning up all materials that are exported. |
|
|
|
plBitmap *plBitmapCreator::ICreateTexture( plBitmapData *bd, const plLocation &loc, int clipID ) |
|
{ |
|
hsGuardBegin( "plBitmapCreator::CreateTexture" ); |
|
|
|
const plLocation &textureLoc = plPluginResManager::ResMgr()->GetCommonPage( loc, plAgeDescription::kTextures ); |
|
|
|
if( !bd ) |
|
{ |
|
fErrorMsg->Set( true, "Bitmap Error", "No bitmap data" ).Show(); |
|
fErrorMsg->Set(); |
|
return nil; |
|
} |
|
|
|
if( bd->fileName == nil || bd->fileName[ 0 ] == 0 ) |
|
{ |
|
fErrorMsg->Set( true, "Bitmap Error", "Material texture has null bitmap name." ).Show(); |
|
fErrorMsg->Set(); |
|
return nil; |
|
} |
|
|
|
// Get and mangle key name |
|
plString name; |
|
char temp[ 256 ]; |
|
_splitpath(bd->fileName, NULL, NULL, temp, NULL); |
|
|
|
// Somehow, sometimes, we get the same file in with different cases. So we need to force the |
|
// case identical all the time, else the patching process for dat files will think they're |
|
// "different" when they're really not |
|
strlwr( temp ); |
|
|
|
/// Mangle name for detail textures, so we don't end up overwriting settings elsewhere |
|
if( bd->createFlags & plMipmap::kCreateDetailMask ) |
|
{ |
|
// Mangle of the form: name@dropStart&dropStop&max&min |
|
if( clipID != -1 ) |
|
name = plString::Format( "%s*%x#%d@%s&%3.2f&%3.2f&%3.2f&%3.2f", temp, bd->texFlags, clipID, |
|
bd->createFlags & plMipmap::kCreateDetailAlpha ? "al" : ( bd->createFlags & plMipmap::kCreateDetailAdd ? "ad" : "mu" ), |
|
bd->detailDropoffStart, bd->detailDropoffStop, bd->detailMax, bd->detailMin ); |
|
else |
|
name = plString::Format( "%s*%x@%s&%3.2f&%3.2f&%3.2f&%3.2f", temp, bd->texFlags, |
|
bd->createFlags & plMipmap::kCreateDetailAlpha ? "al" : ( bd->createFlags == plMipmap::kCreateDetailAdd ? "ad" : "mu" ), |
|
bd->detailDropoffStart, bd->detailDropoffStop, bd->detailMax, bd->detailMin ); |
|
} |
|
else if( clipID != -1 ) |
|
name = plString::Format( "%s*%x#%d", temp, bd->texFlags, clipID ); |
|
else |
|
name = plString::Format( "%s*%x", temp, bd->texFlags ); |
|
if( bd->invertAlpha ) |
|
name += _TEMP_CONVERT_FROM_LITERAL( "_inva" ); |
|
name += _TEMP_CONVERT_FROM_LITERAL( ".hsm" ); |
|
|
|
|
|
// Has this texture been used before? |
|
plKey key; |
|
|
|
plBitmap *texture = plBitmap::ConvertNoRef( sCommonBitmapLib.FindObject( name, ( bd->isStaticCubicEnvMap ) ? plCubicEnvironmap::Index() : plMipmap::Index() ) ); |
|
//hsAssert( texture == nil || texture->GetKey()->GetUoid().GetLocation() == textureLoc, "Somehow our texture objectLib has a texture not in the right page? Should be harmless tho..." ); |
|
|
|
// Texture reuse optimization |
|
if( texture ) |
|
{ |
|
WIN32_FILE_ATTRIBUTE_DATA fileAttrib; |
|
GetFileAttributesEx(bd->fileName, GetFileExInfoStandard, &fileAttrib); |
|
FILETIME &fileTime = fileAttrib.ftLastWriteTime; |
|
|
|
// If this texture has been modified since the last export, delete the old version but reuse the key |
|
if (!texture->IsSameModifiedTime(fileTime.dwLowDateTime, fileTime.dwHighDateTime)) |
|
{ |
|
DeleteExportedBitmap( texture->GetKey() ); |
|
texture = nil; |
|
key = nil; |
|
} |
|
} |
|
|
|
if( texture ) |
|
{ |
|
// If it's in the registry, great, use it. |
|
if( bd->texFlags & plMipmap::kNoMaxSize ) |
|
texture->SetFlags( texture->GetFlags() | plMipmap::kNoMaxSize ); |
|
|
|
if( bd->texFlags & plMipmap::kHalfSize ) |
|
texture->SetFlags( texture->GetFlags() | plMipmap::kHalfSize ); |
|
} |
|
else |
|
{ |
|
// If it hasn't been used before, make a new texture |
|
if( bd->isStaticCubicEnvMap ) |
|
{ |
|
plCubicEnvironmap *cubic = new plCubicEnvironmap; |
|
|
|
plMipmap *face; |
|
|
|
/// Build and set the faces |
|
bd->fileName = bd->faceNames[ plStaticEnvLayer::kTopFace ]; |
|
face = ICreateBitmap( bd ); |
|
if( face == nil ) return nil; |
|
cubic->CopyToFace( face, plCubicEnvironmap::kTopFace ); |
|
|
|
bd->fileName = bd->faceNames[ plStaticEnvLayer::kBottomFace ]; |
|
face = ICreateBitmap( bd ); |
|
if( face == nil ) return nil; |
|
cubic->CopyToFace( face, plCubicEnvironmap::kBottomFace ); |
|
|
|
bd->fileName = bd->faceNames[ plStaticEnvLayer::kLeftFace ]; |
|
face = ICreateBitmap( bd ); |
|
if( face == nil ) return nil; |
|
cubic->CopyToFace( face, plCubicEnvironmap::kLeftFace ); |
|
|
|
bd->fileName = bd->faceNames[ plStaticEnvLayer::kRightFace ]; |
|
face = ICreateBitmap( bd ); |
|
if( face == nil ) return nil; |
|
cubic->CopyToFace( face, plCubicEnvironmap::kRightFace ); |
|
|
|
/// NOTE: For whatever reason, MAX decided that the front and back faces should be' |
|
/// switched, literally. It's as if the cube for the cube map starts at the back face |
|
/// and then wraps around, instead of starting at the front face. Since we do things |
|
/// the RIGHT way (or rather, the front way :) on client-side, we need to flip the |
|
/// two here. If you convert this to the real MAX UI, make sure the faces are still |
|
/// flipped!!!!!!!! |
|
|
|
bd->fileName = bd->faceNames[ plStaticEnvLayer::kBackFace ]; |
|
face = ICreateBitmap( bd ); |
|
if( face == nil ) return nil; |
|
cubic->CopyToFace( face, plCubicEnvironmap::kFrontFace ); |
|
|
|
bd->fileName = bd->faceNames[ plStaticEnvLayer::kFrontFace ]; |
|
face = ICreateBitmap( bd ); |
|
if( face == nil ) return nil; |
|
cubic->CopyToFace( face, plCubicEnvironmap::kBackFace ); |
|
|
|
|
|
key = hsgResMgr::ResMgr()->NewKey( name, cubic, textureLoc ); |
|
|
|
texture = (plBitmap *)cubic; |
|
} |
|
else |
|
{ |
|
plMipmap *mipmap = ICreateBitmap(bd); |
|
if (!mipmap) |
|
return nil; |
|
|
|
key = hsgResMgr::ResMgr()->NewKey( name, mipmap, textureLoc ); |
|
|
|
texture = (plBitmap *)mipmap; |
|
} |
|
|
|
// Texture reuse optimization |
|
WIN32_FILE_ATTRIBUTE_DATA fileAttrib; |
|
GetFileAttributesEx(bd->fileName, GetFileExInfoStandard, &fileAttrib); |
|
FILETIME &fileTime = fileAttrib.ftLastWriteTime; |
|
texture->SetModifiedTime(fileTime.dwLowDateTime, fileTime.dwHighDateTime); |
|
|
|
// Add to our list of created textures and ref, since we have a hold of them |
|
IAddBitmap( texture ); |
|
} |
|
|
|
return texture; |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
//// IAddBitmap /////////////////////////////////////////////////////////////// |
|
|
|
void plBitmapCreator::IAddBitmap( plBitmap *bitmap, bool dontRef ) |
|
{ |
|
sCommonBitmapLib.AddObject( bitmap ); |
|
} |
|
|
|
//// CreateBlankMipmap //////////////////////////////////////////////////////// |
|
// Simple mipmap creator, but importantly, it also adds the mipmap to the list |
|
// of "converted" maps to clean up at the end of export. |
|
|
|
plMipmap *plBitmapCreator::CreateBlankMipmap( uint32_t width, uint32_t height, unsigned config, uint8_t numLevels, |
|
const plString &keyName, const plLocation &keyLocation ) |
|
{ |
|
hsGuardBegin( "plBitmapCreator::CreateBlankMipmap" ); |
|
|
|
// Get our real location |
|
const plLocation &textureLoc = plPluginResManager::ResMgr()->GetCommonPage( keyLocation, plAgeDescription::kTextures ); |
|
|
|
// Is it already created? |
|
plKey key = hsgResMgr::ResMgr()->FindKey( plUoid( textureLoc, plMipmap::Index(), keyName ) ); |
|
if( key != nil ) |
|
return plMipmap::ConvertNoRef( key->GetObjectPtr() ); |
|
|
|
// Create |
|
plMipmap *mip = new plMipmap( width, height, config, numLevels ); |
|
|
|
// Assign key |
|
hsgResMgr::ResMgr()->NewKey( keyName, mip, textureLoc ); |
|
|
|
// Add to our list |
|
IAddBitmap( mip ); |
|
|
|
return mip; |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
|