/*==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 .
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 "resource.h"
#include "plMiscComponents.h"
#include "plComponentReg.h"
#ifdef MAXASS_AVAILABLE
#include "../../AssetMan/PublicInterface/MaxAssInterface.h"
#endif
#include "MaxMain/plPlasmaRefMsgs.h"
#include "MaxMain/plMaxNodeData.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plCoordinateInterface.h"
#include "pnSceneObject/plDrawInterface.h"
#include "MaxMain/plPluginResManager.h"
#include "plgDispatch.h"
#include "pnMessage/plObjRefMsg.h"
#include "pnMessage/plIntRefMsg.h"
#include "pnMessage/plNodeRefMsg.h"
#include "plScene/plSceneNode.h"
#include "MaxConvert/hsConverterUtils.h"
#include "MaxConvert/hsControlConverter.h"
#include "plInterp/plController.h"
#include "MaxMain/plMaxNode.h"
#include "pnKeyedObject/plKey.h"
#include "plFile/plFileUtils.h"
// Follow mod
#include "plInterp/plAnimPath.h"
#include "pfAnimation/plFollowMod.h"
//Player Attention Related
#include "pfCamera/plInterestingModifier.h"
//Player Start Position
#include "plModifier/plSpawnModifier.h"
// RunTime related (Sprites, Billboards, LightMaps, etc., etc.)
#include "pfAnimation/plViewFaceModifier.h" // ViewFace Comp
// Anim Related
#include "plMaxAnimUtils.h"
// CavView component.
#include "plScene/plPostEffectMod.h"
// Location Related
#include "plAgeDescription/plAgeDescription.h"
#include "MaxMain/plMaxCFGFile.h"
#include "MaxMain/plAgeDescInterface.h"
#include "plFile/hsFiles.h"
#include "plResMgr/plPageInfo.h"
#include "plDrawable/plGeometrySpan.h"
#include "MaxConvert/plConvert.h"
// ImageLib
#include "plModifier/plImageLibMod.h"
#include "MaxPlasmaMtls/Layers/plLayerTex.h"
#include "MaxConvert/plLayerConverter.h"
#include "plGImage/plBitmap.h"
void DummyCodeIncludeFuncMisc()
{
RegisterNotification(plPageInfoComponent::NotifyProc, nil, NOTIFY_FILE_POST_OPEN);
RegisterNotification(plPageInfoComponent::NotifyProc, nil, NOTIFY_SYSTEM_POST_RESET);
RegisterNotification(plPageInfoComponent::NotifyProc, nil, NOTIFY_SYSTEM_POST_NEW);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Interesting Component
//
//
//Class that accesses the paramblock below.
class plInterestingComponent : public plComponent
{
public:
plInterestingComponent();
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};
//Max desc stuff necessary below.
CLASS_DESC(plInterestingComponent, gInterestDesc, "Player Attention", "PlayerAttention", COMP_TYPE_MISC, Class_ID(0x6f48a7, 0x7ab86088))
enum
{
kInteresting, kCamInterestRadius, kCamInterestWeight
};
ParamBlockDesc2 gInterestBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
1, _T("Player Attention"), 0, &gInterestDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_INTEREST, IDS_COMP_INTERESTS, 0, 0, NULL,
// params
kInteresting, _T("interesting"), TYPE_STRING, 0, 0,
end,
kCamInterestRadius, _T("CamIntersestRadius"), TYPE_FLOAT, P_ANIMATABLE, 0,
p_default, 100.0f,
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
IDC_COMP_INTEREST_EDIT1, IDC_COMP_INTEREST_SPIN1, 1.0f,
end,
kCamInterestWeight, _T("CamIntersestWeight"), TYPE_FLOAT, P_ANIMATABLE, 0,
p_range, 0.0, 1.0,
p_default, 1.0,
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
IDC_COMP_INTEREST_EDIT2, IDC_COMP_INTEREST_SPIN2, 0.001f,
end,
end
);
plInterestingComponent::plInterestingComponent()
{
fClassDesc = &gInterestDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
hsBool plInterestingComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
plInterestingModifier* pMod = TRACKED_NEW plInterestingModifier;
hsScalar loader = fCompPB->GetFloat(kCamInterestRadius);
pMod->SetInterestRadius(loader);
loader = fCompPB->GetFloat(kCamInterestWeight);
pMod->SetInterestWeight(loader);
node->AddModifier(pMod, IGetUniqueName(node));
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// PageInfo Component
//
//
class plPageInfoComponentProc : public ParamMap2UserDlgProc
{
protected:
HWND fhDlg;
IParamBlock2 *fPB;
void ILoadPages()
{
HWND hPageCombo = GetDlgItem(fhDlg, IDC_COMP_LOCATION_PAGECOMBO);
ComboBox_ResetContent(hPageCombo);
int idx = ComboBox_GetCurSel( GetDlgItem( fhDlg, IDC_COMP_LOCATION_AGECOMBO ) );
if( idx == CB_ERR )
return;
char *agePath = (char *)ComboBox_GetItemData( GetDlgItem( fhDlg, IDC_COMP_LOCATION_AGECOMBO ), idx );
if( agePath == nil )
return;
// Get the age description
plAgeDescription aged( agePath );
// Set the seqPrefix here. (Where else would you suggest?)
fPB->SetValue( plPageInfoComponent::kInfoSeqPrefix, 0, (int)aged.GetSequencePrefix() );
const char *curPage = fPB->GetStr(plPageInfoComponent::kInfoPage);
if (curPage && *curPage == '\0')
curPage = nil;
// Load the page combo and select the saved page (if it's in there)
plAgePage *page;
aged.SeekFirstPage();
while( ( page = aged.GetNextPage() ) != nil )
{
int idx = ComboBox_AddString(hPageCombo, page->GetName() );
if (curPage && !strcmp(page->GetName(), curPage))
ComboBox_SetCurSel(hPageCombo, idx);
ComboBox_SetItemData( hPageCombo, idx, (int)page->GetSeqSuffix() );
}
}
void IClearAges( HWND combo )
{
while( ComboBox_GetCount( combo ) > 0 )
{
char *path = (char *)ComboBox_GetItemData( combo, 0 );
if( path != nil )
delete [] path;
ComboBox_DeleteString( combo, 0 );
}
}
bool ILoadAges()
{
HWND hAgeCombo = GetDlgItem(fhDlg, IDC_COMP_LOCATION_AGECOMBO);
IClearAges( hAgeCombo );
hsTArray ageFiles;
plAgeDescInterface::BuildAgeFileList( ageFiles );
const char *curAge = fPB->GetStr(plPageInfoComponent::kInfoAge);
if (!curAge || *curAge == '\0')
curAge = "";
for( int i = 0; i < ageFiles.GetCount(); i++ )
{
char ageName[_MAX_FNAME];
_splitpath( ageFiles[ i ], nil, nil, ageName, nil );
int idx = ComboBox_AddString( hAgeCombo, ageName );
// Store the pathas the item data for later (so don't free it yet!)
ComboBox_SetItemData( hAgeCombo, idx, (LPARAM)ageFiles[ i ] );
if( !strcmp( ageName, curAge ) )
ComboBox_SetCurSel( hAgeCombo, idx );
}
return true;
}
public:
void DeleteThis() {}
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
{
fhDlg = hWnd;
fPB = map->GetParamBlock();
ILoadAges();
ILoadPages();
return TRUE;
}
case WM_DESTROY:
IClearAges( GetDlgItem( hWnd, IDC_COMP_LOCATION_AGECOMBO ) );
return TRUE;
case WM_COMMAND:
if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_COMP_LOCATION_AGECOMBO)
{
HWND hAgeCombo = (HWND)lParam;
int idx = ComboBox_GetCurSel(hAgeCombo);
if (idx != CB_ERR)
{
char buf[256];
ComboBox_GetText(hAgeCombo, buf, sizeof(buf));
fPB->SetValue(plPageInfoComponent::kInfoAge, 0, buf);
fPB->SetValue(plPageInfoComponent::kInfoPage, 0, "");
fPB->SetValue(plPageInfoComponent::kInfoSeqSuffix, 0, (int)-1 );
ILoadPages();
}
return TRUE;
}
if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_COMP_LOCATION_PAGECOMBO)
{
HWND hPageCombo = (HWND)lParam;
int idx = ComboBox_GetCurSel(hPageCombo);
if (idx != CB_ERR)
{
char buf[256];
ComboBox_GetText(hPageCombo, buf, sizeof(buf));
fPB->SetValue( plPageInfoComponent::kInfoPage, 0, buf );
fPB->SetValue( plPageInfoComponent::kInfoSeqSuffix, 0, ComboBox_GetItemData( hPageCombo, idx ) );
}
return TRUE;
}
return FALSE;
}
return FALSE;
}
};
// For the paramblock below.
static plPageInfoComponentProc gPageInfoCompProc;
//Max desc stuff necessary.
CLASS_DESC(plPageInfoComponent, gPageInfoDesc, "Page Info", "PageInfo", COMP_TYPE_MISC, PAGEINFO_CID)
//Max paramblock2 stuff below.
ParamBlockDesc2 gPageInfoCompBk
(
plComponent::kBlkComp, _T("PageInfo"), 0, &gPageInfoDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_PAGEINFO, IDS_COMP_PAGEINFOS, 0, 0, &gPageInfoCompProc,
plPageInfoComponent::kInfoAge, _T("ageName"), TYPE_STRING, 0, 0,
end,
plPageInfoComponent::kInfoPage, _T("pageName"), TYPE_STRING, 0, 0,
end,
plPageInfoComponent::kInfoSeqPrefix, _T("sequencePrefix"), TYPE_INT, 0, 0,
end,
plPageInfoComponent::kInfoSeqSuffix, _T("sequenceSuffix"), TYPE_INT, 0, 0,
end,
plPageInfoComponent::kRefVolatile_PageInfoUpdated, _T( "pageInfoUpdated" ), TYPE_BOOL, 0, 0,
p_default, 0,
end,
plPageInfoComponent::kItinerant, _T("itinerant"), TYPE_BOOL, 0, 0,
p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK1,
end,
end
);
char plPageInfoComponent::fCurrExportedAge[ 256 ] = "";
plPageInfoComponent::plPageInfoComponent()
{
fSeqNumValidated = false;
fItinerant = false;
fClassDesc = &gPageInfoDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plPageInfoComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
// Another component already created a location, don't override it
if (pNode->GetRoomKey())
return false;
const char *age = fCompPB->GetStr(kInfoAge);
const char *room = fCompPB->GetStr(kInfoPage);
if (!age || *age == '\0' || !room || *room == '\0')
{
pErrMsg->Set(true,
"PageInfo Component Error",
"No Label for the Age, Chapter, or Page, on the Location component found on %s",
pNode->GetName()).Show();
return false;
}
// If we're only exporting a certain page, and this location isn't it, don't export this node
const char* exportPage = plConvert::Instance().GetConvertSettings()->fExportPage;
if (exportPage && stricmp(room, exportPage))
{
pNode->SetCanConvert(false);
return true;
}
// Check to make sure we don't try to export more than one age at a time
if( fCurrExportedAge[ 0 ] == 0 )
strncpy( fCurrExportedAge, age, sizeof( fCurrExportedAge ) );
else
{
if( stricmp( fCurrExportedAge, age ) != 0 )
{
// Our only currently accepted exception (eh?) is GlobalClothing and GlobalAvatars
if( ( stricmp( age, "GlobalAvatars" ) == 0 && stricmp( fCurrExportedAge, "GlobalClothing" ) == 0 ) ||
( stricmp( age, "GlobalClothing" ) == 0 && stricmp( fCurrExportedAge, "GlobalAvatars" ) == 0 ) )
{
}
else
{
pErrMsg->Set( true, "PageInfo Component Error",
"The scene you are trying to export is attempting to export to both ages %s and"
" %s. You are only allowed to export to one age at a time.",
fCurrExportedAge, age ).Show();
// Reset for next time
return false;
}
}
}
// Make sure our sequence partitions are up-to-date
IUpdateSeqNumbersFromAgeFile( pErrMsg );
// Need to re-get our age and page name here, since IUpdate() might change them
age = fCompPB->GetStr( kInfoAge );
room = fCompPB->GetStr( kInfoPage );
fItinerant = fCompPB->GetInt(kItinerant);
// Build our sequence number
Int32 newNum, seqNum;
seqNum = plPageInfoUtils::CombineSeqNum( fCompPB->GetInt( kInfoSeqPrefix ), fCompPB->GetInt( kInfoSeqSuffix ) );
newNum = plPluginResManager::ResMgr()->VerifySeqNumber( seqNum, age, room );
if( newNum != seqNum )
{
if( !fSeqNumValidated && seqNum != 0 )
{
// What error was it, exactly?
char errMsg[ 1024 ];
const plPageInfo *lastPage = plPluginResManager::ResMgr()->GetLastVerifyPage();
if( plPluginResManager::ResMgr()->GetLastVerifyError() == plPluginResManager::kErrRightPageWrongSeq )
{
sprintf( errMsg, "The Page Info component for %s>%s applied to object %s is attempting to export with a sequence number "
"different from what is already exported. Further, the already-exported page %s>%s has "
"the same sequence number as this Page Info component now. This is most likely due to the .age "
"files having been changed since the old page was exported. The recommended solution would be to "
"delete the offending page data and export both pages again.\n\n"
"The exporter has assigned a valid temporary sequence number for page %s>%s, but this data should not be used "
"for playing over the network or released for external use.\n\n"
"\t(Original sequence #: 0x%X)\n\t(Temporary sequence #: 0x%X)",
age, room, pNode->GetName(),
lastPage->GetAge(), lastPage->GetPage(), age, room, seqNum, newNum );
}
else if( plPluginResManager::ResMgr()->GetLastVerifyError() == plPluginResManager::kErrSeqAlreadyTaken )
{
sprintf( errMsg, "The Page Info component for %s>%s applied to object %s is attempting to export with "
"an identical sequence number to the already-exported page %s>%s. This is usually due to "
"either page having been exported with an invalid or missing .age file. Please verify that both "
"pages have valid .age files and their sequence numbers do not conflict in the Age Description "
"Manager.\n\n"
"The exporter has assigned a valid temporary sequence number for page %s>%s, but this data should not be used "
"for playing over the network or released for external use.\n\n"
"\t(Original sequence #: 0x%X)\n\t(Temporary sequence #: 0x%X)",
age, room, pNode->GetName(),
lastPage->GetAge(), lastPage->GetPage(), age, room, seqNum, newNum );
}
else if( plPluginResManager::ResMgr()->GetLastVerifyError() == plPluginResManager::kErrCantFindValid )
{
sprintf( errMsg, "The Page Info component for %s>%s applied to object %s is attempting to export with "
"an invalid sequence number. The exporter could not find a valid, free sequence number to use, so this "
"page cannot be exported. Contact mcn (ext 264) immediately!\n\n"
"\t(Original sequence #: 0x%X)",
age, room, pNode->GetName(), seqNum );
pErrMsg->Set( true, "PageInfo Convert Error", errMsg ).Show();
return false;
}
else
{
sprintf( errMsg, "The Page Info component for %s>%s applied to object %s is attempting to export with "
"a sequence number that is invalid for an unknown reason.\n\n"
"The exporter has assigned a valid temporary sequence number, but this data should not be used "
"for playing over the network or released for external use.\n\n"
"\t(Original sequence #: 0x%X)\n\t(Temporary sequence #: 0x%X)",
age, room, pNode->GetName(), seqNum, newNum );
}
pErrMsg->Set( true, "PageInfo Convert Error", errMsg ).Show();
pErrMsg->Set( false );
fSeqNumValidated = true;
}
seqNum = newNum;
}
if (fItinerant)
int i = 0;
plKey roomKey = plPluginResManager::ResMgr()->NameToLoc(age, room, seqNum, fItinerant );
if(!roomKey)
{
pErrMsg->Set(true,
"PageInfo Convert Error",
"Location Component %s has a Missing Location. Nuke the files in the dat directory and re-export.",
pNode->GetName()).Show();
return false;
}
pNode->SetRoomKey(roomKey);
if (!strcmp(age, "GlobalClothing"))
((plSceneNode *)roomKey->GetObjectPtr())->SetFilterGenericsOnly(true);
return true;
}
hsBool plPageInfoComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
// Make sure we clear this flag so that the next time around it's clear
fCompPB->SetValue( kRefVolatile_PageInfoUpdated, 0, (int)false );
fSeqNumValidated = false;
return true;
}
hsBool plPageInfoComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg)
{
plKey snKey = node->GetRoomKey();
plSceneObject *so = node->GetSceneObject();
if(so)
{
if (node->GetSwappableGeom())
{
snKey->Release(so->GetKey());
node->GetMaxNodeData()->SetSceneObject(nil);
// Since child refs are now passive, this isn't needed.
/*
plMaxNode *parent = (plMaxNode *)node->GetParentNode();
if (!parent->IsRootNode() && !parent->GetSwappableGeom())
{
parent->GetSceneObject()->GetKey()->Release(so->GetKey());
}
*/
}
}
return true;
}
const char *plPageInfoComponent::GetAgeName()
{
return fCompPB->GetStr(ParamID(kInfoAge));
}
//// IVerifyLatestAgeAsset ///////////////////////////////////////////////////
// Checks in assetMan to make sure we have the latest .age file to export
// with.
void plPageInfoComponent::IVerifyLatestAgeAsset( const char *ageName, const char *localPath, plErrorMsg *errMsg )
{
#ifdef MAXASS_AVAILABLE
char ageFileName[ MAX_PATH ], assetPath[ MAX_PATH ];
MaxAssInterface *assetMan = GetMaxAssInterface();
if( assetMan == nil )
return; // No AssetMan available
// Try to find it in assetMan
sprintf( ageFileName, "%s.age", ageName );
jvUniqueId assetId;
if (assetMan->FindAssetByFilename(ageFileName, assetId))
{
// Get the latest version
if (!assetMan->GetLatestVersionFile(assetId, assetPath, sizeof(assetPath)))
{
errMsg->Set( true, "PageInfo Convert Error",
"Unable to update age file for '%s' because AssetMan was unable to get the latest version. Using local copy instead.", ageName ).Show();
errMsg->Set( false );
return;
}
// Got the latest version, just copy over and roll!
plFileUtils::RemoveFile( localPath );
plFileUtils::FileCopy( assetPath, localPath );
}
else
{
// Not found, so just assume it's a local one (no error)
}
#endif
}
//// IUpdateSeqNumbersFromAgeFile ////////////////////////////////////////////
// With the new sequence numbers, it's vital that our sequence numbers that
// we use to export are synched up with the latest .age files. This function
// makes sure that we're synched before we start using 'em.
void plPageInfoComponent::IUpdateSeqNumbersFromAgeFile( plErrorMsg *errMsg )
{
// Check to see if we've updated already
if( fCompPB->GetInt( kRefVolatile_PageInfoUpdated ) )
return; // Already updated this pass!
// Mark us as updated
fCompPB->SetValue( kRefVolatile_PageInfoUpdated, 0, (int)true );
char path[MAX_PATH];
const char *ageFolder = plPageInfoUtils::GetAgeFolder();
if( ageFolder == nil )
{
errMsg->Set( true,
"PageInfo Convert Error",
"There was a problem converting the PageInfo Component %s (the age folder couldn't be located). "
"The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.",
GetINode()->GetName() ).Show();
errMsg->Set( false );
fCompPB->SetValue( kInfoSeqPrefix, 0, 0 );
fCompPB->SetValue( kInfoSeqSuffix, 0, 0 );
return;
}
const char *curAge = fCompPB->GetStr( kInfoAge );
if( !curAge || *curAge == '\0' )
{
errMsg->Set( true,
"PageInfo Convert Error",
"There was a problem converting the PageInfo Component %s (no age name was selected). "
"The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.",
GetINode()->GetName()).Show();
errMsg->Set( false );
fCompPB->SetValue( kInfoSeqPrefix, 0, 0 );
fCompPB->SetValue( kInfoSeqSuffix, 0, 0 );
return;
}
sprintf(path, "%s%s.age", ageFolder, curAge);
IVerifyLatestAgeAsset( curAge, path, errMsg );
hsUNIXStream s;
if (!s.Open(path))
{
errMsg->Set( true,
"PageInfo Convert Error",
"There was a problem converting the PageInfo Component %s (the age name \"%s\" is invalid). "
"The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.",
GetINode()->GetName(), curAge ).Show();
errMsg->Set( false );
fCompPB->SetValue( kInfoSeqPrefix, 0, 0 );
fCompPB->SetValue( kInfoSeqSuffix, 0, 0 );
return;
}
// create and read the age desc
plAgeDescription aged;
aged.Read(&s);
s.Close();
// Update based on the age file now
fCompPB->SetValue( kInfoSeqPrefix, 0, (int)aged.GetSequencePrefix() );
// Find our page
const char *compPBPageName = fCompPB->GetStr( kInfoPage );
if( compPBPageName == nil )
{
errMsg->Set( true,
"PageInfo Convert Error",
"There was a problem converting the PageInfo Component %s (no page name was specified). "
"The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.",
GetINode()->GetName() ).Show();
errMsg->Set( false );
fCompPB->SetValue( kInfoSeqPrefix, 0, 0 );
fCompPB->SetValue( kInfoSeqSuffix, 0, 0 );
return;
}
plAgePage *page;
aged.SeekFirstPage();
while( ( page = aged.GetNextPage() ) != nil )
{
if( stricmp( page->GetName(), compPBPageName ) == 0 )
{
fCompPB->SetValue( kInfoSeqSuffix, 0, (int)page->GetSeqSuffix() );
// Also re-copy the page name, just to make sure the case is correct
fCompPB->SetValue( kInfoPage, 0, (char *)page->GetName() );
return;
}
}
// If we got here, the page name is invalid
char msg[ 512 ];
sprintf( msg, "There was a problem converting the PageInfo Component %s (the page \"%s\" wasn't found in the age file for age \"%s\"). "
"The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.",
GetINode()->GetName(), compPBPageName, curAge );
errMsg->Set( true, "PageInfo Convert Error", msg ).Show();
errMsg->Set( false );
fCompPB->SetValue( kInfoSeqPrefix, 0, 0 );
fCompPB->SetValue( kInfoSeqSuffix, 0, 0 );
}
const char *plPageInfoUtils::GetAgeFolder()
{
static char ageFolder[MAX_PATH];
static bool initialized = false;
if (!initialized)
{
initialized = true;
ageFolder[0] = '\0';
const char *plasmaPath = plMaxConfig::GetClientPath();
if (!plasmaPath)
return nil;
strcpy(ageFolder, plasmaPath);
strcat(ageFolder, plAgeDescription::kAgeDescPath);
}
if (ageFolder[0] != '\0')
return ageFolder;
else
return nil;
}
Int32 plPageInfoUtils::CombineSeqNum( int prefix, int suffix )
{
hsAssert(abs(prefix) < 0xFF, "Sequence prefix must be less then the max 8-bit number");
hsAssert(suffix <= 0xFFFF, "Sequence suffix must be less then the max 16-bit number");
hsAssert(suffix >= 0, "Sequence suffix must be unsigned");
if( prefix < 0 )
return -( ( ( -prefix ) << 16 ) + suffix );
else
return ( prefix << 16 ) + suffix;
}
Int32 plPageInfoUtils::GetCommonSeqNumFromNormal( Int32 normalSeqNumber, int whichCommonPage )
{
int prefix;
const int kFirstCommonSeqSuffix = 0xffff;
hsAssert( whichCommonPage < plAgeDescription::kNumCommonPages, "Invalid common page index in GetCommonSeqNumFromNormal()" );
if( normalSeqNumber < 0 )
{
prefix = -( (-normalSeqNumber) >> 16 );
}
else
prefix = normalSeqNumber >> 16;
return CombineSeqNum( prefix, kFirstCommonSeqSuffix - whichCommonPage );
}
Int32 plPageInfoUtils::GetSeqNumFromAgeDesc( const char *ageName, const char *pageName )
{
int seqPrefix, seqSuffix = 0;
plAgeDescription *aged = GetAgeDesc( ageName );
if( aged == nil )
{
// ???? This ain't good...attempt to get the resMgr to give us a temporary seqNum...
return 0;
}
seqPrefix = aged->GetSequencePrefix();
// Find our page
plAgePage *page;
aged->SeekFirstPage();
while( ( page = aged->GetNextPage() ) != nil )
{
if( stricmp( pageName, page->GetName() ) == 0 )
{
seqSuffix = page->GetSeqSuffix();
break;
}
}
delete aged;
return CombineSeqNum( seqPrefix, seqSuffix );
}
plAgeDescription *plPageInfoUtils::GetAgeDesc( const char *ageName )
{
char path[ MAX_PATH ];
const char *ageFolder = plPageInfoUtils::GetAgeFolder();
if( ageFolder == nil || ageName == nil )
return nil;
sprintf( path, "%s%s.age", ageFolder, ageName );
hsUNIXStream s;
if( !s.Open( path ) )
return nil;
// Create and read the age desc
plAgeDescription *aged = TRACKED_NEW plAgeDescription;
aged->Read( &s );
s.Close();
return aged;
}
const char* LocCompGetPage(plComponentBase* comp)
{
if (!comp)
return nil;
const char* page = nil;
if (comp->ClassID() == PAGEINFO_CID)
{
IParamBlock2* pb = comp->GetParamBlockByID(plComponentBase::kBlkComp);
page = pb->GetStr(plPageInfoComponent::kInfoPage);
}
if (page && *page != '\0')
return page;
return nil;
}
static const char *CheckPageInfoCompsRecur(plMaxNode *node)
{
plComponentBase *comp = node->ConvertToComponent();
if (comp && comp->ClassID() == PAGEINFO_CID)
{
plPageInfoComponent* pageComp = (plPageInfoComponent*)comp;
return pageComp->GetAgeName();
}
for (int i = 0; i < node->NumberOfChildren(); i++)
{
const char *result = CheckPageInfoCompsRecur((plMaxNode*)node->GetChildNode(i));
if (result)
return result;
}
return nil;
}
void plPageInfoComponent::NotifyProc(void *param, NotifyInfo *info)
{
if (info->intcode == NOTIFY_FILE_POST_OPEN)
{
const char *ageName = CheckPageInfoCompsRecur((plMaxNode*)GetCOREInterface()->GetRootNode());
if (ageName != nil)
strncpy( fCurrExportedAge, ageName, sizeof( fCurrExportedAge ) );
}
else if (info->intcode == NOTIFY_SYSTEM_POST_RESET ||
info->intcode == NOTIFY_SYSTEM_POST_NEW)
{
fCurrExportedAge[0] = 0;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Room Component
//
//
class plRoomComponent : public plComponent
{
public:
plRoomComponent();
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};
//Max desc stuff necessary.
OBSOLETE_CLASS_DESC(plRoomComponent, gRoomDesc, "Location", "Location", COMP_TYPE_MISC, ROOM_CID)
enum
{
kLocRoom,
kLocAge,
kLocDistrict,
kLocSeqNumber
};
//Max paramblock2 stuff below.
ParamBlockDesc2 gRoomCompBk
(
1, _T("Location"), 0, &gRoomDesc, P_AUTO_CONSTRUCT+ P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_ROOM, IDS_COMP_ROOMS, 0, 0, NULL,
// params
kLocAge, _T("Age"), TYPE_STRING, 0, 0,
p_ui, TYPE_EDITBOX, IDC_COMP_ROOM_AGE_TEXTBOX,
end,
kLocDistrict, _T("District"), TYPE_STRING, 0, 0,
p_ui, TYPE_EDITBOX, IDC_COMP_ROOM_DISTRICT_TEXTBOX,
end,
kLocRoom, _T("Room"), TYPE_STRING, 0, 0,
p_ui, TYPE_EDITBOX, IDC_COMP_ROOM_ROOM_TEXTBOX,
end,
kLocSeqNumber, _T("seqNumber"), TYPE_INT, 0, 0,
p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_ROOM_SEQEDIT, IDC_ROOM_SEQSPIN, SPIN_AUTOSCALE,
end,
end
);
plRoomComponent::plRoomComponent()
{
fClassDesc = &gRoomDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
hsBool plRoomComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// View Facing Component
//
//
//Class that accesses the paramblock below.
class plViewFacingComponent : public plComponent
{
public:
plViewFacingComponent();
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};
//Max desc stuff necessary below.
CLASS_DESC(plViewFacingComponent, gViewFacingDesc, "Billboard", "Billboard", COMP_TYPE_GRAPHICS, Class_ID(0x7fab4d1f, 0x30f95438))
//
// Block not necessary, kept for backwards compat.
//
enum
{
kTypeofView, kViewFaceScaleX, kViewFaceScaleY, kViewFaceScaleZ
};
ParamBlockDesc2 gViewFacingBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
1, _T("View Facing"), 0, &gViewFacingDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp,
#if 0
IDD_COMP_VIEWFACE, IDS_COMP_VIEWFACES, 0, 0, NULL,
kTypeofView, _T("ViewType"), TYPE_INT, 0, 0,
p_ui, TYPE_RADIO, 4, IDC_RADIO_VF1, IDC_RADIO_VF2, IDC_RADIO_VF3, IDC_RADIO_VF4,
end,
kViewFaceScaleX, _T("ViewFaceScaleX"), TYPE_FLOAT, 0, 0,
p_default, 0.0f,
p_range, 0.0, 1500.0,
p_ui, TYPE_SPINNER, EDITTYPE_FLOAT,
IDC_COMP_VFSCALE_EDIT1, IDC_COMP_VFSCALE_SPIN1, 0.1f,
end,
kViewFaceScaleY, _T("ViewFaceScaleY"), TYPE_FLOAT, 0, 0,
p_default, 0.0f,
p_range, 0.0, 1500.0,
p_ui, TYPE_SPINNER, EDITTYPE_FLOAT,
IDC_COMP_VFSCALE_EDIT2, IDC_COMP_VFSCALE_SPIN2, 0.1f,
end,
kViewFaceScaleZ, _T("ViewFaceScaleZ"), TYPE_FLOAT, 0, 0,
p_default, 0.0f,
p_range, 0.0, 1500.0,
p_ui, TYPE_SPINNER, EDITTYPE_FLOAT,
IDC_COMP_VFSCALE_EDIT3, IDC_COMP_VFSCALE_SPIN3, 0.1f,
end,
#endif
end
);
plViewFacingComponent::plViewFacingComponent()
{
fClassDesc = &gViewFacingDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
static hsBool NodeHasTMAnimation(plMaxNode* node)
{
if( node->GetUnBounded() )
return true;
plPhysicalProps* props = node->GetPhysicalProps();
if( props && props->IsUsed() )
{
if( (props->GetMass() > 0) && !props->GetPinned() )
return true;
}
return node->GetTMController() && node->GetTMController()->IsAnimated();
}
static hsBool FindTMAnimatedChildrenRecur(plMaxNode* node)
{
if( !node->CanConvert() )
return false;
if( NodeHasTMAnimation(node) )
return true;
int i;
for( i = 0; i < node->NumChildren(); i++ )
{
if( FindTMAnimatedChildrenRecur((plMaxNode*)node->GetChildNode(i)) )
return true;
}
return false;
}
static void FindRecursiveBounds(plMaxNode* node, hsBounds3Ext& bnd)
{
if( !node->CanConvert() )
return;
int i;
for( i = 0; i < node->NumChildren(); i++ )
{
FindRecursiveBounds((plMaxNode*)node->GetChildNode(i), bnd);
}
const TimeValue currTime(0);
Object *obj = node->EvalWorldState(currTime).obj;
if( !obj )
return;
if( obj->CanConvertToType(triObjectClassID) )
{
TriObject *meshObj = (TriObject *)obj->ConvertToType(currTime, triObjectClassID);
if( !meshObj )
return;
Matrix3 l2w = node->GetObjectTM(currTime);
Box3 box = meshObj->mesh.getBoundingBox(&l2w);
if( !box.IsEmpty() )
{
bnd.Union(&hsPoint3(box.Min().x, box.Min().y, box.Min().z));
bnd.Union(&hsPoint3(box.Max().x, box.Max().y, box.Max().z));
}
if( meshObj != obj )
meshObj->DeleteThis();
}
return;
}
static hsBool FindMaxBounds(plMaxNode* node, hsBounds3Ext& bnd)
{
bnd.MakeEmpty();
// First, look from the node up to the root. If anything is animated, we can't do this.
plMaxNode* parent = node;
while( !parent->IsRootNode() )
{
// We shouldn't ever hit this, but whatever.
if( !parent->CanConvert() )
return false;
if( NodeHasTMAnimation(parent) )
return false;
parent = (plMaxNode*)parent->GetParentNode();
}
// Second, look down the children. If any of them are animated, we can't do this.
if( FindTMAnimatedChildrenRecur(node) )
return false;
// Now find the recursive world space bounds of us and all our children.
FindRecursiveBounds(node, bnd);
// Translate to local about our pivot
hsMatrix44 l2w = node->GetLocalToWorld44();
// Expand them to be symmetric about local origin.
bnd.MakeSymmetric(&l2w.GetTranslate());
return true;
}
hsBool plViewFacingComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
plViewFaceModifier* pMod = TRACKED_NEW plViewFaceModifier;
hsBounds3Ext maxBnd;
if( FindMaxBounds(node, maxBnd) )
pMod->SetMaxBounds(maxBnd);
// int ChosenType = fCompPB->GetInt(kTypeofView);
// switch(ChosenType)
// {
// case 0:
// pMod->SetFlag(plViewFaceModifier::kPivotFace);
// break;
// case 1:
// pMod->SetFlag(plViewFaceModifier::kPivotFavorY);
// break;
// case 2:
pMod->SetFlag(plViewFaceModifier::kPivotY);
// break;
// case 3:
// pMod->SetFlag(plViewFaceModifier::kPivotTumble);
// break;
// }
#if 0
if(fCompPB->GetFloat(kViewFaceScaleX) || fCompPB->GetFloat(kViewFaceScaleY) || fCompPB->GetFloat(kViewFaceScaleZ))
{
pMod->SetFlag(plViewFaceModifier::kScale);
hsVector3 scale;
scale.Set(1.f, 1.f, 1.f);
scale.fX = fCompPB->GetFloat(kViewFaceScaleX);
scale.fY = fCompPB->GetFloat(kViewFaceScaleY);
scale.fZ = fCompPB->GetFloat(kViewFaceScaleZ);
pMod->SetScale(scale);
}
#endif
pMod->SetOrigTransform(node->GetLocalToParent44(), node->GetParentToLocal44());
node->AddModifier(pMod, IGetUniqueName(node));
return true;
}
hsBool plViewFacingComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
pNode->SetForceLocal(true);
pNode->SetMovable(true);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Sprite Component
//
//
//Class that accesses the paramblock below.
class plSpriteComponent : public plComponent
{
public:
plSpriteComponent();
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};
//Max desc stuff necessary below.
CLASS_DESC(plSpriteComponent, gSpriteDesc, "Sprite", "Sprite", COMP_TYPE_GRAPHICS, Class_ID(0x1e18192b, 0x312f579b))
//
// Block not necessary, kept for backwards compat.
//
ParamBlockDesc2 gSpriteBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
1, _T("Sprite"), 0, &gSpriteDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp,
end
);
plSpriteComponent::plSpriteComponent()
{
fClassDesc = &gSpriteDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
hsBool plSpriteComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
plViewFaceModifier* pMod = TRACKED_NEW plViewFaceModifier;
hsBounds3Ext maxBnd;
if( FindMaxBounds(node, maxBnd) )
pMod->SetMaxBounds(maxBnd);
pMod->SetFlag(plViewFaceModifier::kPivotFace);
pMod->SetOrigTransform(node->GetLocalToParent44(), node->GetParentToLocal44());
node->AddModifier(pMod, IGetUniqueName(node));
return true;
}
hsBool plSpriteComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
pNode->SetForceLocal(true);
pNode->SetMovable(true);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Occlusion Component
//
//
enum {
kOccTwoSidedChekbox
};
class plOcclusionComponentProc : public ParamMap2UserDlgProc
{
public:
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
{
}
return true;
//////////////////
case WM_COMMAND:
{
if (LOWORD(wParam) == IDC_COMP_OCCLUSION_CKBX)
{
return true;
}
}
}
return false;
}
void DeleteThis() {}
};
static plOcclusionComponentProc gOccProc;
//Class that accesses the paramblock below.
class plOcclusionComponent : public plComponent
{
public:
plOcclusionComponent();
virtual hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg);
virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg);
virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); }
};
CLASS_DESC(plOcclusionComponent, gOcclusionDesc, "Occlusion", "Occlusion", COMP_TYPE_GRAPHICS, Class_ID(0x18c454df, 0x1ecd40f5))
ParamBlockDesc2 gOcclusionBk
(
plComponent::kBlkComp, _T("Occlusion"), 0, &gOcclusionDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_OCCLUSION, IDS_COMP_OCCLUSIONS, 0, 0, &gOccProc,
kOccTwoSidedChekbox, _T("TwoSided"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_OCCLUSION_CKBX,
end,
end
);
plOcclusionComponent::plOcclusionComponent()
{
fClassDesc = &gOcclusionDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
hsBool plOcclusionComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return true;
}
hsBool plOcclusionComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
hsBool twoSided = fCompPB->GetInt(kOccTwoSidedChekbox);
hsBool isHole = false;
return node->ConvertToOccluder(pErrMsg, twoSided, isHole);
}
hsBool plOcclusionComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
pNode->SetDrawable(false);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// CamView Component
//
//
//Class that accesses the paramblock below.
class plCamViewComponent : public plComponent
{
hsBool fBogus;
void IMakeEveryoneOpaqueRecur(plMaxNode* node);
void IMakeEveryoneOpaque(plMaxNode* node);
public:
plCamViewComponent();
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};
//Max desc stuff necessary below.
CLASS_DESC(plCamViewComponent, gCamViewDesc, "Camera View", "CamView", COMP_TYPE_GRAPHICS, Class_ID(0x5e9f0243, 0xe7f2c08))
enum
{
kWhateverCheckBox
};
ParamBlockDesc2 gCamViewBk
(
plComponent::kBlkComp, _T("CamView"), 0, &gCamViewDesc, P_AUTO_CONSTRUCT/* + P_AUTO_UI*/, plComponent::kRefComp,
// IDD_COMP_CAMVIEW, IDS_COMP_CAMVIEWS, 0, 0, NULL,
end
);
plCamViewComponent::plCamViewComponent()
: fBogus(false)
{
fClassDesc = &gCamViewDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
hsBool plCamViewComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
TimeValue timeVal(0);
Object* obj = node->EvalWorldState(timeVal).obj;
GenCamera* cam = nil;
if( obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0)) )
cam = (GenCamera *) obj->ConvertToType(timeVal, Class_ID(LOOKAT_CAM_CLASS_ID, 0));
else
if( obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0)) )
cam = (GenCamera *) obj->ConvertToType(timeVal, Class_ID(SIMPLE_CAM_CLASS_ID, 0));
if( !cam )
{
hsAssert(false, "Should have checked for the camera in PreConvert");
return false;
}
plPostEffectMod* mod = TRACKED_NEW plPostEffectMod;
float hither = cam->GetEnvRange(timeVal, ENV_NEAR_RANGE);
if( hither < 0.5f )
hither = 0.5f;
float yon = cam->GetEnvRange(timeVal, ENV_FAR_RANGE);
mod->SetHither(hither);
mod->SetYon(yon);
// radians
float fov = cam->GetFOV(timeVal);
// convert
int FOVType = cam->GetFOVType();
hsScalar fovX, fovY;
switch(FOVType)
{
case 0: // FOV_W
{
fovX = fov;
fovY = fovX *3.f / 4.f;
}
break;
case 1: // FOV_H
{
fovY = fov;
fovX = fovY * 4.f / 3.f;
}
break;
}
fovX *= 180.f / hsScalarPI;
fovY *= 180.f / hsScalarPI;
mod->SetFovX(fovX);
mod->SetFovY(fovY);
plKey sceneNodeKey = node->GetRoomKey();
mod->SetNodeKey(sceneNodeKey);
node->AddModifier(mod, IGetUniqueName(node));
return true;
}
hsBool plCamViewComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
TimeValue timeVal(0);
Object* obj = node->EvalWorldState(timeVal).obj;
if( obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0))
|| obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0)) )
fBogus = false;
else
fBogus = true;
if( fBogus )
pErrMsg->Set(true, node->GetName(), "CamView component attached to non-camera").CheckAndAsk();
if( !fBogus )
IMakeEveryoneOpaque(node);
return true;
}
void plCamViewComponent::IMakeEveryoneOpaque(plMaxNode* node)
{
plMaxNode* root = (plMaxNode *)node->GetInterface()->GetRootNode();
int i;
for( i = 0; i < root->NumberOfChildren(); i++ )
IMakeEveryoneOpaqueRecur((plMaxNode*)(root->GetChildNode(i)));
}
void plCamViewComponent::IMakeEveryoneOpaqueRecur(plMaxNode* node)
{
if( node->CanConvert() )
{
node->SetNoSpanReSort(true);
node->SetNoSpanSort(true);
int i;
for( i = 0; i < node->NumberOfChildren(); i++ )
{
IMakeEveryoneOpaqueRecur((plMaxNode *)(node->GetChildNode(i)));
}
}
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plCamViewComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
node->SetForceLocal(true);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Follow Component
//
//
enum
{
kAffectX,
kLeaderTypeRadio,
kLeaderObjectSel,
kAffectY,
kAffectZ,
kAffectRotate
};
// When one of our parameters that is a ref changes, send out the component ref
// changed message. Normally, messages from component refs are ignored since
// they pass along all the messages of the ref, which generates a lot of false
// converts.
class plLeaderObjAccessor : public PBAccessor
{
public:
void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t)
{
if( (id == kLeaderObjectSel) )
{
plComponentBase *comp = (plComponentBase*)owner;
comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED);
}
}
};
plLeaderObjAccessor gLeaderObjAccessor;
class plFollowComponentProc : public ParamMap2UserDlgProc
{
public:
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
{
IParamBlock2 *pb = map->GetParamBlock();
map->SetTooltip(kLeaderObjectSel, TRUE, "Press the button, & select the object to follow in one of the Viewports" );
if( pb->GetInt(kLeaderTypeRadio) == Int32(plFollowMod::kObject) )
map->Enable(kLeaderObjectSel, TRUE);
else
map->Enable(kLeaderObjectSel, FALSE);
}
return true;
//////////////////
case WM_COMMAND:
{
if( (LOWORD(wParam) == IDC_F_RADIO_PLAYER)
||(LOWORD(wParam) == IDC_F_RADIO_LISTENER)
|| (LOWORD(wParam) == IDC_F_RADIO_CAMERA)
|| (LOWORD(wParam) == IDC_F_RADIO_OBJECT) )
{
IParamBlock2 *pb = map->GetParamBlock();
if( pb->GetInt(kLeaderTypeRadio) == Int32(plFollowMod::kObject) )
map->Enable(kLeaderObjectSel, TRUE);
else
map->Enable(kLeaderObjectSel, FALSE);
return true;
}
}
}
return false;
}
void DeleteThis() {}
};
static plFollowComponentProc gFollowProc;
//Class that accesses the paramblock below.
class plFollowComponent : public plComponent
{
private:
hsBool fValid;
plFollowMod* IMakeFollowMod(plMaxNode* pNode, plErrorMsg* pErrMsg);
public:
plFollowComponent();
hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg);
hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};
CLASS_DESC(plFollowComponent, gFollowDesc, "Follow", "Follow", COMP_TYPE_GRAPHICS, Class_ID(0x44262418, 0x73ee145b))
ParamBlockDesc2 gFollowBk
(
plComponent::kBlkComp, _T("Follow"), 0, &gFollowDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_FOLLOW, IDS_COMP_FOLLOWS, 0, 0, &gFollowProc,
kAffectX, _T("X"), TYPE_BOOL, 0, 0,
p_default, TRUE,
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FOLLOW_X,
end,
kAffectY, _T("Y"), TYPE_BOOL, 0, 0,
p_default, TRUE,
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FOLLOW_Y,
end,
kAffectZ, _T("Z"), TYPE_BOOL, 0, 0,
p_default, TRUE,
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FOLLOW_Z,
end,
kAffectRotate, _T("Rotate"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FOLLOW_ROTATE,
end,
kLeaderTypeRadio, _T("LeaderType"), TYPE_INT, 0, 0,
p_ui, TYPE_RADIO, 4, IDC_F_RADIO_PLAYER, IDC_F_RADIO_CAMERA, IDC_F_RADIO_LISTENER, IDC_F_RADIO_OBJECT,
p_vals, plFollowMod::kLocalPlayer, plFollowMod::kCamera, plFollowMod::kListener, plFollowMod::kObject,
p_default, plFollowMod::kLocalPlayer,
end,
kLeaderObjectSel, _T("ObjectChoice"), TYPE_INODE, 0, 0,
p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_FOLLOW_CHOOSE_OBJECT,
p_prompt, IDS_COMP_LINE_CHOSE_OBJECT,
p_accessor, &gLeaderObjAccessor,
end,
end
);
plFollowComponent::plFollowComponent()
: fValid(false)
{
fClassDesc = &gFollowDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
plFollowMod* plFollowComponent::IMakeFollowMod(plMaxNode* pNode, plErrorMsg* pErrMsg)
{
plFollowMod::FollowLeaderType lType = plFollowMod::FollowLeaderType(fCompPB->GetInt(kLeaderTypeRadio));
plFollowMod* follow = TRACKED_NEW plFollowMod;
hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), follow, pNode->GetLocation());
if( plFollowMod::kObject == lType )
{
if(fCompPB->GetINode(kLeaderObjectSel) != NULL)
{
plMaxNode* targNode = (plMaxNode*)fCompPB->GetINode(kLeaderObjectSel);
if( targNode->CanConvert() )
{
plSceneObject* targObj = targNode->GetSceneObject();
if( targObj )
{
plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(follow->GetKey(), plRefMsg::kOnCreate, 0, plFollowMod::kRefLeader);
hsgResMgr::ResMgr()->AddViaNotify(targObj->GetKey(), refMsg, plRefFlags::kPassiveRef);
follow->SetType(plFollowMod::kObject);
}
}
}
}
else
{
follow->SetType(lType);
}
UInt32 mode = 0;
if( fCompPB->GetInt(kAffectX) )
mode |= plFollowMod::kPositionX;
if( fCompPB->GetInt(kAffectY) )
mode |= plFollowMod::kPositionY;
if( fCompPB->GetInt(kAffectZ) )
mode |= plFollowMod::kPositionZ;
if( fCompPB->GetInt(kAffectRotate) )
mode |= plFollowMod::kRotate;
if( !mode )
mode = plFollowMod::kFullTransform;
follow->SetMode(mode);
return follow;
}
hsBool plFollowComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !fValid )
return true;
plFollowMod* follow = IMakeFollowMod(node, pErrMsg);
if( follow )
node->AddModifier(follow, IGetUniqueName(node));
return true;
}
hsBool plFollowComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
fValid = false;
if( plFollowMod::kObject == fCompPB->GetInt(kLeaderTypeRadio) )
{
if( !fCompPB->GetINode(kLeaderObjectSel) )
{
return true;
}
}
fValid = true;
pNode->SetForceLocal(true);
pNode->SetMovable(true);
return true;
}
hsBool plFollowComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg)
{
if( !fValid )
return true;
fValid = false;
if( plFollowMod::kObject == fCompPB->GetInt(kLeaderTypeRadio) )
{
plMaxNode* followNode = (plMaxNode*)fCompPB->GetINode(kLeaderObjectSel);
if( !followNode->CanConvert() )
{
return true;
}
}
fValid = true;
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Unleash Component
//
//
//Class that accesses the paramblock below.
class plUnleashComponent : public plComponent
{
public:
plUnleashComponent();
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
};
//Max desc stuff necessary below.
CLASS_DESC(plUnleashComponent, gUnleashDesc, "Unleash Satan", "UnleashSatan", COMP_TYPE_GRAPHICS, Class_ID(0x5d937fa8, 0x1001411a))
ParamBlockDesc2 gUnleashBk
(
plComponent::kBlkComp, _T("Unleash"), 0, &gUnleashDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_UNLEASH, IDS_COMP_UNLEASH, 0, 0, NULL,
end
);
plUnleashComponent::plUnleashComponent()
{
fClassDesc = &gUnleashDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plUnleashComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
pNode->SetRunTimeLight(true);
pNode->SetForcePreShade(true);
return true;
}
hsBool plUnleashComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// ForceRTLight Component
//
//
//Class that accesses the paramblock below.
class plForceRTLightComponent : public plComponent
{
public:
plForceRTLightComponent();
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; }
};
//Max desc stuff necessary below.
CLASS_DESC(plForceRTLightComponent, gForceRTLightDesc, "Force RT Light", "ForceRTLight", COMP_TYPE_GRAPHICS, Class_ID(0x1485091b, 0x42852fb5))
ParamBlockDesc2 gForceRTLightBk
(
plComponent::kBlkComp, _T("ForceRTLight"), 0, &gForceRTLightDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_FORCE_RTLIGHT, IDS_COMP_FORCE_RTLIGHT, 0, 0, NULL,
end
);
plForceRTLightComponent::plForceRTLightComponent()
{
fClassDesc = &gForceRTLightDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plForceRTLightComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
pNode->SetRunTimeLight(true);
pNode->SetNoPreShade(true);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Terrain Optimise Component (currently just dices).
//
//
//Class that accesses the paramblock below.
class plGeoDiceComponent : public plComponent
{
public:
enum {
kActive,
kMaxFaces,
kMaxSize,
kMinFaces,
kOverride
};
public:
plGeoDiceComponent();
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; }
};
static const int kDefMaxFaces(1000);
static const float kDefMaxSize(250.f);
static const int kDefMinFaces(300);
class plGeoDiceComponentProc : public ParamMap2UserDlgProc
{
public:
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
{
IParamBlock2 *pb = map->GetParamBlock();
if( !pb->GetInt(plGeoDiceComponent::kOverride) )
{
pb->SetValue(plGeoDiceComponent::kMaxFaces, t, kDefMaxFaces);
pb->SetValue(plGeoDiceComponent::kMaxSize, t, kDefMaxSize);
pb->SetValue(plGeoDiceComponent::kMinFaces, t, kDefMinFaces);
map->Enable(plGeoDiceComponent::kMaxFaces, FALSE);
map->Enable(plGeoDiceComponent::kMaxSize, FALSE);
map->Enable(plGeoDiceComponent::kMinFaces, FALSE);
}
else
{
map->Enable(plGeoDiceComponent::kMaxFaces, TRUE);
map->Enable(plGeoDiceComponent::kMaxSize, TRUE);
map->Enable(plGeoDiceComponent::kMinFaces, TRUE);
}
}
return true;
//////////////////
case WM_COMMAND:
{
if( LOWORD(wParam) == IDC_COMP_GEO_DICE_OVERRIDE )
{
IParamBlock2 *pb = map->GetParamBlock();
if( !pb->GetInt(plGeoDiceComponent::kOverride) )
{
pb->SetValue(plGeoDiceComponent::kMaxFaces, t, kDefMaxFaces);
pb->SetValue(plGeoDiceComponent::kMaxSize, t, kDefMaxSize);
pb->SetValue(plGeoDiceComponent::kMinFaces, t, kDefMinFaces);
map->Enable(plGeoDiceComponent::kMaxFaces, FALSE);
map->Enable(plGeoDiceComponent::kMaxSize, FALSE);
map->Enable(plGeoDiceComponent::kMinFaces, FALSE);
}
else
{
map->Enable(plGeoDiceComponent::kMaxFaces, TRUE);
map->Enable(plGeoDiceComponent::kMaxSize, TRUE);
map->Enable(plGeoDiceComponent::kMinFaces, TRUE);
}
return true;
}
}
}
return false;
}
void DeleteThis() {}
};
static plGeoDiceComponentProc gGeoDiceProc;
//Max desc stuff necessary below.
CLASS_DESC(plGeoDiceComponent, gGeoDiceDesc, "Optimize Terrain", "OptimizeTerrain", COMP_TYPE_GRAPHICS, Class_ID(0x6f7a5713, 0x19595142))
ParamBlockDesc2 gGeoDiceBk
(
plComponent::kBlkComp, _T("GeoDice"), 0, &gGeoDiceDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_GEO_DICE, IDS_COMP_GEO_DICE, 0, 0, &gGeoDiceProc,
plGeoDiceComponent::kActive, _T("Active"), TYPE_BOOL, 0, 0,
p_default, TRUE,
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_GEO_DICE_ACTIVE,
end,
plGeoDiceComponent::kMaxFaces, _T("MaxFaces"), TYPE_INT, 0, 0,
p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_COMP_GEO_DICE_MAXFACES, IDC_COMP_GEO_DICE_MAXFACES_SPIN, 1.f,
p_default, 1000,
p_range, 10, 10000,
end,
plGeoDiceComponent::kMaxSize, _T("MaxSize"), TYPE_FLOAT, 0, 0,
p_default, 100.0f,
p_range, 0.0, 10000.0,
p_ui, TYPE_SPINNER, EDITTYPE_FLOAT,
IDC_COMP_GEO_DICE_MAXSIZE, IDC_COMP_GEO_DICE_MAXSIZE_SPIN, 0.1f,
end,
plGeoDiceComponent::kMinFaces, _T("MinFaces"), TYPE_INT, 0, 0,
p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_COMP_GEO_DICE_MINFACES, IDC_COMP_GEO_DICE_MINFACES_SPIN, 1.f,
p_default, 300,
p_range, 0, 5000,
end,
plGeoDiceComponent::kOverride, _T("Override"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_GEO_DICE_OVERRIDE,
end,
end
);
plGeoDiceComponent::plGeoDiceComponent()
{
fClassDesc = &gGeoDiceDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGeoDiceComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
if( fCompPB->GetInt(kActive) )
{
pNode->SetGeoDice(true, fCompPB->GetInt(kMaxFaces), fCompPB->GetFloat(kMaxSize), fCompPB->GetInt(kMinFaces));
}
return true;
}
///
///
/// reference point component
/// put this on a dummy for a handy reference point you can use in python
///
///
class plReferencePointComponent : public plComponent
{
public:
plReferencePointComponent();
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
};
CLASS_DESC(plReferencePointComponent, gReferencePointDesc, "Reference Point", "RefPoint", COMP_TYPE_MISC, Class_ID(0x3c9c6f71, 0x5774fc5))
//Max paramblock2 stuff below.
ParamBlockDesc2 gReferencePointBk
(
1, _T("reference"), 0, &gReferencePointDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp,
end
);
plReferencePointComponent::plReferencePointComponent()
{
fClassDesc = &gReferencePointDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
hsBool plReferencePointComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
// all we need is a coordinate interface...
pNode->SetForceLocal(true);
return true;
}
///////////////////////////////////////////////////////////////////////////////
//
//
#include "pnNetCommon/plSDLTypes.h"
#include "MaxConvert/hsMaterialConverter.h"
#include "plSurface/hsGMaterial.h"
#include "plSurface/plLayerInterface.h"
#include "plCreatableIndex.h"
class plNetSyncComponent : public plComponent
{
protected:
void ISetNetSync(plSynchedObject* so);
void ISetSDLType(plSynchedObject* so, int radioVal, const char* sdlName);
void ISetMtl(hsGMaterial* mtl);
public:
plNetSyncComponent();
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* errMsg);
virtual hsBool Convert(plMaxNode* node, plErrorMsg* errMsg) { return true; }
virtual hsBool DeInit(plMaxNode* node, plErrorMsg* errMsg);
};
CLASS_DESC(plNetSyncComponent, gNetSyncDesc, "Net Sync", "NetSync", COMP_TYPE_MISC, Class_ID(0x4d1b2d6f, 0x28fe08db))
enum
{
kNetSyncLocalOnly,
kNetSyncOverride,
kNetSyncPhys,
kNetSyncAnim,
kNetSyncSnd,
kNetSyncMat,
kNetSyncResp,
kNetSyncXReg,
};
enum
{
kNetSyncRadioAllow,
kNetSyncRadioNoSave,
kNetSyncRadioDeny,
};
class plNetSyncComponentProc : public ParamMap2UserDlgProc
{
protected:
void IEnableSDL(IParamMap2* map, bool enable)
{
map->Enable(kNetSyncPhys, enable);
map->Enable(kNetSyncAnim, enable);
map->Enable(kNetSyncSnd, enable);
map->Enable(kNetSyncMat, enable);
map->Enable(kNetSyncResp, enable);
map->Enable(kNetSyncXReg, enable);
}
void IEnableCtrls(IParamMap2* map)
{
IParamBlock2* pb = map->GetParamBlock();
if (pb->GetInt(kNetSyncLocalOnly))
{
map->Enable(kNetSyncOverride, FALSE);
IEnableSDL(map, false);
}
else if (pb->GetInt(kNetSyncOverride))
{
map->Enable(kNetSyncLocalOnly, FALSE);
IEnableSDL(map, true);
}
else
{
map->Enable(kNetSyncOverride, TRUE);
map->Enable(kNetSyncLocalOnly, TRUE);
IEnableSDL(map, false);
}
}
public:
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
IEnableCtrls(map);
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDC_LOCAL_ONLY_CHECK ||
LOWORD(wParam) == IDC_OVERRIDE_CHECK)
{
IEnableCtrls(map);
return TRUE;
}
break;
}
return FALSE;
}
void DeleteThis() {}
};
static plNetSyncComponentProc gNetSyncProc;
//Max paramblock2 stuff below.
ParamBlockDesc2 gHighSDLBk
(
plComponent::kBlkComp, _T("NetSync"), 0, &gNetSyncDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_NETSYNC, IDS_COMP_NETSYNC, 0, 0, &gNetSyncProc,
kNetSyncLocalOnly, _T("LocalOnly"), TYPE_BOOL, 0, 0,
p_ui, TYPE_SINGLECHEKBOX, IDC_LOCAL_ONLY_CHECK,
end,
kNetSyncOverride, _T("Override"), TYPE_BOOL, 0, 0,
p_ui, TYPE_SINGLECHEKBOX, IDC_OVERRIDE_CHECK,
end,
kNetSyncPhys, _T("PhysSDL"), TYPE_INT, 0, 0,
p_ui, TYPE_RADIO, 3, IDC_PHYS_ALLOW_RADIO, IDC_PHYS_NOSAVE_RADIO, IDC_PHYS_DENY_RADIO,
p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny,
p_default, kNetSyncRadioAllow,
end,
kNetSyncAnim, _T("AnimSDL"), TYPE_INT, 0, 0,
p_ui, TYPE_RADIO, 3, IDC_ANIM_ALLOW_RADIO, IDC_ANIM_NOSAVE_RADIO, IDC_ANIM_DENY_RADIO,
p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny,
p_default, kNetSyncRadioAllow,
end,
kNetSyncSnd, _T("SndSDL"), TYPE_INT, 0, 0,
p_ui, TYPE_RADIO, 3, IDC_SND_ALLOW_RADIO, IDC_SND_NOSAVE_RADIO, IDC_SND_DENY_RADIO,
p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny,
p_default, kNetSyncRadioAllow,
end,
kNetSyncMat, _T("MatSDL"), TYPE_INT, 0, 0,
p_ui, TYPE_RADIO, 3, IDC_MAT_ALLOW_RADIO, IDC_MAT_NOSAVE_RADIO, IDC_MAT_DENY_RADIO,
p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny,
p_default, kNetSyncRadioAllow,
end,
kNetSyncResp, _T("RespSDL"), TYPE_INT, 0, 0,
p_ui, TYPE_RADIO, 3, IDC_RESP_ALLOW_RADIO, IDC_RESP_NOSAVE_RADIO, IDC_RESP_DENY_RADIO,
p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny,
p_default, kNetSyncRadioAllow,
end,
kNetSyncXReg, _T("XRegSDL"), TYPE_INT, 0, 0,
p_ui, TYPE_RADIO, 3, IDC_XREG_ALLOW_RADIO, IDC_XREG_NOSAVE_RADIO, IDC_XREG_DENY_RADIO,
p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny,
p_default, kNetSyncRadioAllow,
end,
end
);
plNetSyncComponent::plNetSyncComponent()
{
fClassDesc = &gNetSyncDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plNetSyncComponent::SetupProperties(plMaxNode* node, plErrorMsg* errMsg)
{
// make all sdl types on this object Volatile
bool override = (fCompPB->GetInt(kNetSyncOverride) != 0);
node->SetOverrideHighLevelSDL(override);
return true;
}
void plNetSyncComponent::ISetSDLType(plSynchedObject* so, int radioVal, const char* sdlName)
{
switch (radioVal)
{
case kNetSyncRadioNoSave:
so->AddToSDLVolatileList(sdlName); // make volatile a type of persistence
break;
case kNetSyncRadioDeny:
so->AddToSDLExcludeList(sdlName); // disable a type of persistence
break;
}
}
void plNetSyncComponent::ISetNetSync(plSynchedObject* so)
{
// For local only, disable everything and exit
bool localOnly = (fCompPB->GetInt(kNetSyncLocalOnly) != 0);
if (localOnly)
{
so->SetLocalOnly(true);
return;
}
int physVal = fCompPB->GetInt(kNetSyncPhys);
int animVal = fCompPB->GetInt(kNetSyncAnim);
int sndVal = fCompPB->GetInt(kNetSyncSnd);
int matVal = fCompPB->GetInt(kNetSyncMat);
int respVal = fCompPB->GetInt(kNetSyncResp);
int xregVal = fCompPB->GetInt(kNetSyncXReg);
// If all are not saved, use that optimization
if (
physVal == kNetSyncRadioNoSave &&
animVal == kNetSyncRadioNoSave &&
sndVal == kNetSyncRadioNoSave &&
matVal == kNetSyncRadioNoSave &&
respVal == kNetSyncRadioNoSave &&
xregVal == kNetSyncRadioNoSave
)
{
so->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile);
}
// If all are volatile, use that optimization
else if (
physVal == kNetSyncRadioDeny &&
animVal == kNetSyncRadioDeny &&
sndVal == kNetSyncRadioDeny &&
matVal == kNetSyncRadioDeny &&
respVal == kNetSyncRadioDeny &&
xregVal == kNetSyncRadioDeny
)
{
so->SetSynchFlagsBit(plSynchedObject::kExcludeAllPersistentState);
}
// If it's a mix, we need to set them individually
else
{
ISetSDLType(so, physVal, kSDLPhysical);
ISetSDLType(so, animVal, kSDLAGMaster);
ISetSDLType(so, sndVal, kSDLSound);
ISetSDLType(so, matVal, kSDLLayer);
ISetSDLType(so, respVal, kSDLResponder);
ISetSDLType(so, xregVal, kSDLXRegion);
}
}
void plNetSyncComponent::ISetMtl(hsGMaterial* mtl)
{
for (int i = 0; i < mtl->GetNumLayers(); i++)
{
plLayerInterface* layer = mtl->GetLayer(i);
while (layer)
{
if (layer->ClassIndex() == CLASS_INDEX_SCOPED(plLayerAnimation) ||
layer->ClassIndex() == CLASS_INDEX_SCOPED(plLayerSDLAnimation))
{
ISetNetSync(layer);
}
layer = layer->GetAttached();
}
}
}
// We're cheating and using DeInit as an extra pass, since everything should be done at that point
hsBool plNetSyncComponent::DeInit(plMaxNode* node, plErrorMsg* errMsg)
{
plSceneObject* so = node->GetSceneObject();
if (!so)
return false;
// Set sync options on the sceneobject
ISetNetSync(so);
// Then on the textures...
Mtl* maxMaterial = hsMaterialConverter::Instance().GetBaseMtl(node);
if (maxMaterial)
{
hsTArray matArray;
// Get the textures from the material converter
if (hsMaterialConverter::Instance().IsMultiMat(maxMaterial))
{
int numMaterials = maxMaterial->NumSubMtls();
for (int i = 0; i < numMaterials; i++)
hsMaterialConverter::Instance().GetMaterialArray(maxMaterial->GetSubMtl(i), node, matArray, i);
}
else
hsMaterialConverter::Instance().GetMaterialArray(maxMaterial, node, matArray);
// Set sync on the textures we found
for (int i = 0; i < matArray.GetCount(); i++)
{
hsGMaterial* mtl = matArray[i];
if (mtl)
ISetMtl(mtl);
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// Image Lib Component
//
//
class pfImageLibProc : public ParamMap2UserDlgProc
{
protected:
void IRefreshImageList( HWND hDlg, pfImageLibComponent *comp )
{
HWND ctrl = GetDlgItem( hDlg, IDC_IMAGE_LIST );
HDC dc = GetDC( ctrl );
LONG maxWidth = 0;
SendMessage( ctrl, LB_RESETCONTENT, 0, 0 );
for( int i = 0; i < comp->GetNumBitmaps(); i++ )
{
plLayerTex *layer = comp->GetBitmap( i );
if( layer != nil )
{
const char *str = layer->GetPBBitmap()->bi.Filename();
int idx = SendMessage( ctrl, LB_ADDSTRING, 0, (LPARAM)str );
SendMessage( ctrl, LB_SETITEMDATA, (WPARAM)idx, (LPARAM)i );
SIZE strSize;
GetTextExtentPoint32( dc, str, strlen(str), &strSize );
if( strSize.cx > maxWidth )
maxWidth = strSize.cx;
}
}
SendMessage( ctrl, LB_SETHORIZONTALEXTENT, (WPARAM)maxWidth, NULL );
ReleaseDC( ctrl, dc );
EnableWindow( GetDlgItem( hDlg, IDC_IMAGE_EDIT ), false );
EnableWindow( GetDlgItem( hDlg, IDC_IMAGE_REMOVE ), false );
CheckDlgButton(hDlg, IDC_IL_COMPRESS, BST_UNCHECKED);
CheckDlgButton(hDlg, IDC_IL_FORCEPOW2, BST_UNCHECKED);
EnableWindow(GetDlgItem(hDlg, IDC_IL_COMPRESS), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_IL_FORCEPOW2), FALSE);
}
public:
void DeleteThis() {}
// virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map );
BOOL DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
IParamBlock2 *pb = pmap->GetParamBlock();
pfImageLibComponent *comp = (pfImageLibComponent *)pb->GetOwner();
switch( msg )
{
case WM_INITDIALOG:
// Fill our list with bitmap filenames
comp->Validate();
IRefreshImageList( hWnd, comp );
return true;
case WM_DESTROY:
break;
case WM_COMMAND:
if( LOWORD( wParam ) == IDC_IMAGE_ADD )
{
plLayerTex *newLayer = TRACKED_NEW plLayerTex;
if( newLayer->HandleBitmapSelection() )
{
comp->AppendBitmap( newLayer );
IRefreshImageList( hWnd, comp );
}
}
else if( LOWORD( wParam ) == IDC_IMAGE_EDIT )
{
int idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETCURSEL, 0, 0 );
if( idx != LB_ERR )
{
idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETITEMDATA, (WPARAM)idx, 0 );
plLayerTex *layer = comp->GetBitmap( idx );
if( layer != nil && layer->HandleBitmapSelection() )
{
IRefreshImageList( hWnd, comp );
}
}
}
else if( LOWORD( wParam ) == IDC_IMAGE_REMOVE )
{
int idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETCURSEL, 0, 0 );
if( idx != LB_ERR )
{
idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETITEMDATA, (WPARAM)idx, 0 );
comp->RemoveBitmap( idx );
IRefreshImageList( hWnd, comp );
}
return false;
}
else if( LOWORD( wParam ) == IDC_IMAGE_LIST && HIWORD( wParam ) == LBN_SELCHANGE )
{
int idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETCURSEL, 0, 0 );
EnableWindow( GetDlgItem( hWnd, IDC_IMAGE_EDIT ), idx != LB_ERR );
EnableWindow( GetDlgItem( hWnd, IDC_IMAGE_REMOVE ), idx != LB_ERR );
EnableWindow(GetDlgItem(hWnd, IDC_IL_COMPRESS), TRUE);
CheckDlgButton(hWnd, IDC_IL_COMPRESS, comp->GetCompress(idx) ? BST_CHECKED : BST_UNCHECKED);
}
else if (LOWORD(wParam) == IDC_IL_COMPRESS && HIWORD(wParam) == BN_CLICKED)
{
bool checked = (IsDlgButtonChecked(hWnd, IDC_IL_COMPRESS) == BST_CHECKED);
int sel = ListBox_GetCurSel(GetDlgItem(hWnd, IDC_IMAGE_LIST));
comp->SetCompress(sel, checked);
}
break;
}
return false;
}
};
static pfImageLibProc gImageLibProc;
//Max desc stuff necessary below.
CLASS_DESC(pfImageLibComponent, gImageLibDesc, "Image Library", "ImageLib", COMP_TYPE_MISC, IMAGE_LIB_CID )
ParamBlockDesc2 gImageLibBlock
( // KLUDGE: not the defined block ID, but kept for backwards compat.
1, _T("Image Lib"), 0, &gImageLibDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_IMAGELIB, IDS_COMP_IMAGELIB, 0, 0, &gImageLibProc,
// params
pfImageLibComponent::kRefImageList, _T("imageList"), TYPE_TEXMAP_TAB, 0, 0, 0,
end,
pfImageLibComponent::kCompressImage, _T("compress"), TYPE_BOOL_TAB, 0, 0, 0,
p_default, 1,
end,
end
);
pfImageLibComponent::pfImageLibComponent()
{
fClassDesc = &gImageLibDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
void pfImageLibComponent::Validate()
{
if (fCompPB->Count(kCompressImage) != fCompPB->Count(kRefImageList))
fCompPB->SetCount(kCompressImage, fCompPB->Count(kRefImageList));
}
int pfImageLibComponent::GetNumBitmaps( void ) const
{
return fCompPB->Count( (ParamID)kRefImageList );
}
plLayerTex *pfImageLibComponent::GetBitmap( int idx )
{
// If we don't have one, create one
plLayerTex *layer = (plLayerTex *)fCompPB->GetTexmap( (ParamID)kRefImageList, 0, idx );
if( layer == nil || layer->ClassID() != LAYER_TEX_CLASS_ID )
{
layer = TRACKED_NEW plLayerTex;
fCompPB->SetValue( (ParamID)kRefImageList, 0, (Texmap *)layer, idx );
}
return layer;
}
int pfImageLibComponent::AppendBitmap( plLayerTex *tex )
{
int idx = GetNumBitmaps();
fCompPB->Resize( (ParamID)kRefImageList, idx + 1 );
fCompPB->SetValue( (ParamID)kRefImageList, 0, (Texmap *)tex, idx );
fCompPB->Resize(kCompressImage, idx + 1);
return idx;
}
void pfImageLibComponent::RemoveBitmap( int idx )
{
fCompPB->Delete( (ParamID)kRefImageList, idx, 1 );
fCompPB->Delete(kCompressImage, idx, 1);
}
bool pfImageLibComponent::GetCompress(int idx)
{
return (fCompPB->GetInt(kCompressImage, 0, idx) != 0);
}
void pfImageLibComponent::SetCompress(int idx, bool compress)
{
fCompPB->SetValue(kCompressImage, 0, compress, idx);
}
hsBool pfImageLibComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg)
{
Validate();
return true;
}
hsBool pfImageLibComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
plImageLibMod *lib = TRACKED_NEW plImageLibMod;
node->AddModifier( lib, IGetUniqueName(node) );
int i;
for( i = 0; i < GetNumBitmaps(); i++ )
{
plLayerTex *layer = GetBitmap( i );
if( layer != nil )
{
PBBitmap *texture = layer->GetPBBitmap();
if( texture != nil )
{
UInt32 flags = plBitmap::kAlphaChannelFlag;
plBitmap *bMap;
if (fCompPB->GetInt(kCompressImage, 0, i) == 0)
{
flags |= plBitmap::kForceNonCompressed;
bMap = plLayerConverter::Instance().CreateSimpleTexture( texture->bi.Name(), lib->GetKey()->GetUoid().GetLocation(), 0, flags );
}
else // compress using JPEG compression scheme
bMap = plLayerConverter::Instance().CreateSimpleTexture( texture->bi.Name(), lib->GetKey()->GetUoid().GetLocation(), 0, flags, true );
if( bMap != nil )
{
hsgResMgr::ResMgr()->AddViaNotify( bMap->GetKey(), TRACKED_NEW plGenRefMsg( lib->GetKey(),
plRefMsg::kOnCreate, lib->GetNumImages(), plImageLibMod::kRefImage ), plRefFlags::kActiveRef );
}
}
}
}
return true;
}