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.
2581 lines
79 KiB
2581 lines
79 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 "plCreatableIndex.h" |
|
#include "plgDispatch.h" |
|
#include "plFileUtils.h" |
|
#include "hsFiles.h" |
|
|
|
#include "plComponentReg.h" |
|
#include "plMiscComponents.h" |
|
#include "MaxMain/plMaxNode.h" |
|
#include "MaxMain/plMaxNodeData.h" |
|
#include "resource.h" |
|
|
|
#include <iparamm2.h> |
|
#include <notify.h> |
|
#pragma hdrstop |
|
|
|
#ifdef MAXASS_AVAILABLE |
|
# include "../../AssetMan/PublicInterface/MaxAssInterface.h" |
|
#endif |
|
|
|
#include "MaxMain/plPlasmaRefMsgs.h" |
|
|
|
#include "pnSceneObject/plSceneObject.h" |
|
#include "pnSceneObject/plCoordinateInterface.h" |
|
#include "pnSceneObject/plDrawInterface.h" |
|
|
|
#include "MaxMain/plPluginResManager.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" |
|
|
|
// 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 "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" |
|
|
|
// NetSync |
|
#include "pnNetCommon/plSDLTypes.h" |
|
#include "MaxConvert/hsMaterialConverter.h" |
|
#include "plSurface/hsGMaterial.h" |
|
#include "plSurface/plLayerInterface.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(); |
|
bool 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); |
|
} |
|
|
|
bool plInterestingComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
|
|
plInterestingModifier* pMod = new plInterestingModifier; |
|
|
|
float 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<char *> 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. |
|
bool 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_t 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; |
|
} |
|
|
|
bool 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; |
|
} |
|
|
|
bool 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_t 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_t plPageInfoUtils::GetCommonSeqNumFromNormal( int32_t 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_t 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 = 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(); |
|
|
|
bool 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); |
|
} |
|
|
|
bool 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. |
|
bool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); |
|
bool 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 bool 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 bool 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 bool 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; |
|
} |
|
|
|
bool plViewFacingComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
|
|
plViewFaceModifier* pMod = 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; |
|
} |
|
|
|
bool 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. |
|
bool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); |
|
bool 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); |
|
} |
|
|
|
bool plSpriteComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
plViewFaceModifier* pMod = 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; |
|
} |
|
|
|
bool 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 bool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); |
|
virtual bool Convert(plMaxNode *node, plErrorMsg *pErrMsg); |
|
virtual bool 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); |
|
} |
|
|
|
bool plOcclusionComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
return true; |
|
} |
|
|
|
bool plOcclusionComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
bool twoSided = fCompPB->GetInt(kOccTwoSidedChekbox); |
|
bool isHole = false; |
|
return node->ConvertToOccluder(pErrMsg, twoSided, isHole); |
|
} |
|
|
|
bool plOcclusionComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) |
|
{ |
|
pNode->SetDrawable(false); |
|
return true; |
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// CamView Component |
|
// |
|
// |
|
|
|
//Class that accesses the paramblock below. |
|
class plCamViewComponent : public plComponent |
|
{ |
|
bool 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. |
|
bool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); |
|
|
|
bool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); |
|
bool 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); |
|
} |
|
|
|
bool 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 = 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(); |
|
float 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 / M_PI; |
|
fovY *= 180.f / M_PI; |
|
mod->SetFovX(fovX); |
|
mod->SetFovY(fovY); |
|
|
|
plKey sceneNodeKey = node->GetRoomKey(); |
|
|
|
mod->SetNodeKey(sceneNodeKey); |
|
|
|
node->AddModifier(mod, IGetUniqueName(node)); |
|
|
|
return true; |
|
} |
|
|
|
bool 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. |
|
bool 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_t(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_t(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: |
|
bool fValid; |
|
|
|
plFollowMod* IMakeFollowMod(plMaxNode* pNode, plErrorMsg* pErrMsg); |
|
|
|
public: |
|
plFollowComponent(); |
|
|
|
bool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); |
|
bool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); |
|
bool 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 = 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 = 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_t 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; |
|
} |
|
|
|
bool plFollowComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
if( !fValid ) |
|
return true; |
|
|
|
plFollowMod* follow = IMakeFollowMod(node, pErrMsg); |
|
|
|
if( follow ) |
|
node->AddModifier(follow, IGetUniqueName(node)); |
|
|
|
return true; |
|
} |
|
|
|
bool 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; |
|
} |
|
|
|
bool 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. |
|
bool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); |
|
bool 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. |
|
bool plUnleashComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) |
|
{ |
|
pNode->SetRunTimeLight(true); |
|
|
|
pNode->SetForcePreShade(true); |
|
|
|
return true; |
|
} |
|
|
|
bool 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. |
|
bool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); |
|
bool 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. |
|
bool 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. |
|
bool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); |
|
bool 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. |
|
bool 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(); |
|
|
|
bool 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 bool 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); |
|
} |
|
|
|
bool plReferencePointComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) |
|
{ |
|
// all we need is a coordinate interface... |
|
pNode->SetForceLocal(true); |
|
return true; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// |
|
|
|
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 bool SetupProperties(plMaxNode* node, plErrorMsg* errMsg); |
|
virtual bool Convert(plMaxNode* node, plErrorMsg* errMsg) { return true; } |
|
virtual bool 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. |
|
bool 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 |
|
bool 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<hsGMaterial*> 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 = 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 = 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); |
|
} |
|
|
|
bool pfImageLibComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) |
|
{ |
|
Validate(); |
|
return true; |
|
} |
|
|
|
bool pfImageLibComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
plImageLibMod *lib = 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_t 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(), new plGenRefMsg( lib->GetKey(), |
|
plRefMsg::kOnCreate, lib->GetNumImages(), plImageLibMod::kRefImage ), plRefFlags::kActiveRef ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
|