/*==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 .
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==*/
///////////////////////////////////////////////////////////////////////////////
// //
// plPlasmaMAXLayer - MAX Layer type that is the basis for all Plasma layer //
// types //
// Note: All export-side functions are contained in //
// MaxConvert/plPlasmaMaxLayerExport.cpp, for linking purposes. //
// Cyan, Inc. //
// //
//// Version History //////////////////////////////////////////////////////////
// //
// 1.13.2002 mcn - Created. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "HeadSpin.h"
#include "plPlasmaMAXLayer.h"
#include "stdmat.h"
#include "istdplug.h"
#include "iparamb2.h"
#include "iparamm2.h"
#include "../resource.h"
#ifdef MAXASS_AVAILABLE
#include "../../AssetMan/PublicInterface/MaxAssInterface.h"
#endif
#include "pnKeyedObject/hsKeyedObject.h"
#include "pnMessage/plRefMsg.h"
#include "plSurface/plLayerInterface.h"
#include "hsResMgr.h"
//// Derived Types List ///////////////////////////////////////////////////////
// If you create a new Plasma layer type, add a define for the class ID in
// the header and add it to the list here.
const Class_ID plPlasmaMAXLayer::fDerivedTypes[] =
{
LAYER_TEX_CLASS_ID,
STATIC_ENV_LAYER_CLASS_ID,
DYNAMIC_ENV_LAYER_CLASS_ID,
DYN_TEXT_LAYER_CLASS_ID,
ANGLE_ATTEN_LAYER_CLASS_ID,
MAX_CAMERA_LAYER_CLASS_ID
};
//// Constructor/Destructor ///////////////////////////////////////////////////
plPlasmaMAXLayer::plPlasmaMAXLayer()
{
fConversionTargets = nil;
}
plPlasmaMAXLayer::~plPlasmaMAXLayer()
{
}
//// GetPlasmaMAXLayer ////////////////////////////////////////////////////////
// Static function that checks the classID of the given texMap and, if it's a
// valid Plasma MAX Layer, returns a pointer to such.
plPlasmaMAXLayer *plPlasmaMAXLayer::GetPlasmaMAXLayer( Texmap *map )
{
if (!map)
return NULL;
int i;
for( i = 0; i < sizeof( fDerivedTypes ) / sizeof( Class_ID ); i++ )
{
if( map->ClassID() == fDerivedTypes[ i ] )
return (plPlasmaMAXLayer *)map;
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
//// Conversion Targets ///////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// plLayerTargetContainer ///////////////////////////////////////////////////
// This is a helper class that just contains a passive ref list of the layers
// that are our conversion targets at export time. See, it's possible that a
// layer gets converted, added to the target list, then destroyed as the
// parent material is suddenly thrown away. In order to avoid our pointers
// from being trashed (or keeping active refs on the layers when they're not
// actually used), we have a small helper class that just keep passive refs,
// so when one of them goes away, we get a notify about it.
class plLayerTargetContainer : public hsKeyedObject
{
static uint32_t fKeyCount;
public:
hsTArray fLayers;
virtual hsBool MsgReceive( plMessage *msg )
{
plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( msg );
if( ref != nil )
{
if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) )
fLayers[ ref->fWhich ] = plLayerInterface::ConvertNoRef( ref->GetRef() );
else
fLayers[ ref->fWhich ] = nil;
}
return hsKeyedObject::MsgReceive( msg );
}
plLayerTargetContainer()
{
char str[ 512 ];
sprintf( str, "plLayerTargetContainer-%d", fKeyCount++ );
hsgResMgr::ResMgr()->NewKey( str, this, plLocation::kGlobalFixedLoc );
}
};
uint32_t plLayerTargetContainer::fKeyCount = 0;
void plPlasmaMAXLayer::IAddConversionTarget( plLayerInterface *target )
{
if( fConversionTargets == nil )
{
// Create us a new container
fConversionTargets = new plLayerTargetContainer;
fConversionTargets->GetKey()->RefObject();
}
fConversionTargets->fLayers.Append( target );
hsgResMgr::ResMgr()->AddViaNotify( target->GetKey(),
new plGenRefMsg( fConversionTargets->GetKey(), plRefMsg::kOnCreate,
fConversionTargets->fLayers.GetCount() - 1, 0 ),
plRefFlags::kPassiveRef );
}
void plPlasmaMAXLayer::IClearConversionTargets( void )
{
if( fConversionTargets != nil )
{
fConversionTargets->GetKey()->UnRefObject();
fConversionTargets = nil;
}
}
int plPlasmaMAXLayer::GetNumConversionTargets( void )
{
if( fConversionTargets == nil )
return 0;
int i, count = 0;
for( i = 0; i < fConversionTargets->fLayers.GetCount(); i++ )
{
if( fConversionTargets->fLayers[ i ] != nil )
count++;
}
return count;
}
plLayerInterface *plPlasmaMAXLayer::GetConversionTarget( int index )
{
if( fConversionTargets == nil )
return nil;
int i;
for( i = 0; i < fConversionTargets->fLayers.GetCount(); i++ )
{
if( fConversionTargets->fLayers[ i ] != nil )
{
if( index == 0 )
return fConversionTargets->fLayers[ i ];
index--;
}
}
return nil;
}
///////////////////////////////////////////////////////////////////////////////
//// Asset Management, and textures ///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#ifdef MAXASS_AVAILABLE
void plPlasmaMAXLayer::SetBitmapAssetId(jvUniqueId& assetId, int index /* = 0 */)
{
PBBitmap *pbbm = GetPBBitmap(index);
if (pbbm && GetMaxAssInterface())
{
char buf[20];
GetMaxAssInterface()->UniqueIdToString(assetId, buf);
pbbm->bi.SetDevice(buf);
}
}
void plPlasmaMAXLayer::GetBitmapAssetId(jvUniqueId& assetId, int index /* = 0 */)
{
PBBitmap *pbbm = GetPBBitmap(index);
if (pbbm && GetMaxAssInterface())
assetId = GetMaxAssInterface()->StringToUniqueId(pbbm->bi.Device());
else
assetId.SetEmpty();
}
#endif
void plPlasmaMAXLayer::SetBitmap(BitmapInfo *bi, int index)
{
#ifdef MAXASS_AVAILABLE
jvUniqueId targetAssetId;
GetBitmapAssetId(targetAssetId, index);
#endif
Bitmap *BM = GetMaxBitmap(index);
if (BM)
{
BM->DeleteThis();
BM = NULL;
}
if (bi)
{
#ifdef MAXASS_AVAILABLE
if (!targetAssetId.IsEmpty())
{
// If this texture has an assetId, we will check the
// asset database and make sure we have the latest version
// of the texture file before loading it
MaxAssInterface* assInterface = GetMaxAssInterface();
if (assInterface)
{
char buf[20];
assInterface->UniqueIdToString(targetAssetId, buf);
bi->SetDevice(buf);
const char* filename = bi->Name();
// Download the latest version and retrieve the filename
char newfilename[MAX_PATH];
if (assInterface->GetLatestVersionFile(targetAssetId, newfilename, sizeof(newfilename)))
{
// If the filename has changed, we have to reset the bitmap in the ParamBlock
if(stricmp(filename, newfilename) != 0)
bi->SetName(newfilename);
}
}
}
#endif
BMMRES result;
BM = TheManager->Load(bi, &result);
if (result == BMMRES_SUCCESS)
ISetMaxBitmap(BM, index);
else
ISetMaxBitmap(NULL, index);
// The load may have failed, but we still want to set the paramblock. We
// don't want to modify the layer if we're just missing the file.
PBBitmap pbBitmap(*bi);
ISetPBBitmap(&pbBitmap, index);
}
else
{
ISetMaxBitmap(NULL, index);
ISetPBBitmap(NULL, index);
}
/*
Bitmap *BM = GetMaxBitmap(index);
if (BM)
{
BM->DeleteThis();
BM = NULL;
}
if (filename)
{
BitmapInfo bi;
bi.SetName(filename);
// If this texture has an assetId, get the latest version from AssetMan before loading it
if (assetId && !assetId->IsEmpty())
{
MaxAssInterface* maxAssInterface = GetMaxAssInterface();
if (maxAssInterface)
{
// Download the latest version and retrieve the filename
char newfilename[MAX_PATH];
if (maxAssInterface->GetLatestVersionFile(*assetId, newfilename, sizeof(newfilename)))
{
// If the filename has changed, we have to reset the bitmap in the ParamBlock
if (stricmp(filename, newfilename) != 0)
{
bi.SetName(newfilename);
}
}
}
}
ISetMaxBitmap(TheManager->Load(&bi));
PBBitmap pbBitmap(bi);
// TheManager->LoadInto(&pbBitmap.bi, &pbBitmap.bm, TRUE);
ISetPBBitmap(&pbBitmap, index);
if (assetId)
SetBitmapAssetId(*assetId, index);
}
else
{
ISetMaxBitmap(NULL, index);
ISetPBBitmap(NULL, index);
}
NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
*/
}
//// RefreshBitmaps ///////////////////////////////////////////////////////////
// Makes sure the bitmap asset is the latest from AssetMan, if we're using it.
void plPlasmaMAXLayer::RefreshBitmaps()
{
int i, count = GetNumBitmaps();
for( i = 0; i < count; i++ )
{
PBBitmap *pbbm = GetPBBitmap(i);
if (pbbm)
{
SetBitmap(&pbbm->bi, i);
}
}
}
//// GetBitmapFileName ////////////////////////////////////////////////////////
// Returns the filename of the ith bitmap. Makes sure we have the latest
// version from assetMan as well, if applicable.
hsBool plPlasmaMAXLayer::GetBitmapFileName( char *destFilename, int maxLength, int index /* = 0 */ )
{
#ifdef MAXASS_AVAILABLE
jvUniqueId targetAssetId;
GetBitmapAssetId(targetAssetId, index);
MaxAssInterface* maxAssInterface = GetMaxAssInterface();
if (maxAssInterface != nil && !targetAssetId.IsEmpty())
{
// Download the latest version and retrieve the filename
if (maxAssInterface->GetLatestVersionFile(targetAssetId, destFilename, maxLength))
return true;
}
#endif
// Normal return
if( GetPBBitmap( index ) == nil )
return false;
strncpy( destFilename, GetPBBitmap( index )->bi.Name(), maxLength );
return true;
}
BOOL plPlasmaMAXLayer::HandleBitmapSelection(int index /* = 0 */)
{
static ICustButton* bmSelectBtn;
PBBitmap *pbbm = GetPBBitmap( index );
#ifdef MAXASS_AVAILABLE
MaxAssInterface* maxAssInterface = GetMaxAssInterface();
#endif
// If the control key is held, we want to get rid of this texture
if ((GetKeyState(VK_CONTROL) & 0x8000) && pbbm != nil)
{
char msg[512];
sprintf(msg, "Are you sure you want to change this bitmap from %s to (none)?", pbbm->bi.Name());
if (hsMessageBox(msg, "Remove texture?", hsMessageBoxYesNo) == hsMBoxYes)
{
SetBitmap(nil, index);
return TRUE;
}
return FALSE;
}
// if we have the assetman plug-in, then try to use it, unless shift is held down
#ifdef MAXASS_AVAILABLE
else if(maxAssInterface && !(GetKeyState(VK_SHIFT) & 0x8000))
{
jvUniqueId assetId;
GetBitmapAssetId(assetId, index);
char filename[MAX_PATH];
if (maxAssInterface->OpenBitmapDlg(assetId, filename, sizeof(filename)))
{
SetBitmapAssetId(assetId, index);
BitmapInfo bi;
bi.SetName(filename);
SetBitmap(&bi, index);
return TRUE;
}
}
#endif
else
{
BitmapInfo bi;
if( pbbm != NULL )
bi.SetName( pbbm->bi.Name() );
BOOL selectedNewBitmap = TheManager->SelectFileInput(&bi,
GetCOREInterface()->GetMAXHWnd(),
_T("Select Bitmap Image File"));
if (selectedNewBitmap)
{
#ifdef MAXASS_AVAILABLE
// Set the assetId to empty so our new, unmanaged texture will take
jvUniqueId emptyId;
SetBitmapAssetId(emptyId, index);
#endif
SetBitmap(&bi, index);
return TRUE;
}
}
return FALSE;
}