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.
1224 lines
40 KiB
1224 lines
40 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 "resource.h" |
|
|
|
#include "plAnimComponent.h" |
|
#include "plComponentProcBase.h" |
|
#include "plPhysicalComponents.h" |
|
#include "plMiscComponents.h" |
|
#include "MaxMain/plPhysicalProps.h" |
|
|
|
#include "pnSceneObject/plSceneObject.h" |
|
|
|
#include "plInterp/plController.h" |
|
#include "plNotetrackAnim.h" |
|
#include "hsResMgr.h" |
|
#include "plAvatar/plAGModifier.h" |
|
#include "plAvatar/plAGChannel.h" |
|
#include "plAvatar/plAGAnim.h" |
|
#include "plAvatar/plAGMasterMod.h" |
|
#include "plAvatar/plMatrixChannel.h" |
|
#include "plAvatar/plPointChannel.h" |
|
#include "plAvatar/plScalarChannel.h" |
|
#include "MaxMain/plMaxNode.h" |
|
#include "MaxConvert/hsControlConverter.h" |
|
|
|
#include "pnKeyedObject/plUoid.h" |
|
#include "plMaxAnimUtils.h" |
|
|
|
#include "MaxPlasmaLights/plRealTimeLightBase.h" |
|
#include "pfAnimation/plLightModifier.h" |
|
#include "pnKeyedObject/plMsgForwarder.h" |
|
|
|
#include "plSDL/plSDL.h" |
|
#include "plSDL/plSDLDescriptor.h" |
|
|
|
#include "plPickNodeBase.h" |
|
|
|
|
|
// For material animations |
|
#include "MaxPlasmaMtls/Materials/plAnimStealthNode.h" |
|
|
|
// So that the linker won't throw this code away, since it doesn't appear to be used |
|
void DummyCodeIncludeFunc() {} |
|
|
|
bool HasPhysicalComponent(plMaxNodeBase *node, bool searchChildren) |
|
{ |
|
int i; |
|
for (i = 0; i < node->NumAttachedComponents(); i++) |
|
{ |
|
if (node->GetAttachedComponent(i)->CanConvertToType(PHYSICS_BASE_CID)) |
|
return true; |
|
} |
|
|
|
if (searchChildren) |
|
{ |
|
for (i = 0; i < node->NumberOfChildren(); i++) |
|
if (HasPhysicalComponent((plMaxNodeBase *)node->GetChildNode(i), searchChildren)) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool HasPhysicalComponent(plComponentBase *comp) |
|
{ |
|
int i; |
|
for (i = 0; i < comp->NumTargets(); i++) |
|
{ |
|
plMaxNodeBase *node = comp->GetTarget(i); |
|
if (node && HasPhysicalComponent(node, true)) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool plAnimComponentBase::GetAnimKey( plMaxNode *node, hsTArray<plKey> &outKeys ) |
|
{ |
|
plComponentBase *comp = node->ConvertToComponent(); |
|
if( comp != nil ) |
|
{ |
|
if( IsAnimComponent( comp ) ) |
|
{ |
|
plAnimComponentBase *base = (plAnimComponentBase *)comp; |
|
// Grab this guy's key |
|
} |
|
} |
|
// else if( ) |
|
{ |
|
} |
|
return true; |
|
} |
|
|
|
plAnimObjInterface *plAnimComponentBase::GetAnimInterface( INode *inode ) |
|
{ |
|
if( inode == nil ) |
|
return nil; |
|
|
|
plMaxNode *node = (plMaxNode *)inode; |
|
plComponentBase *comp = node->ConvertToComponent(); |
|
if( comp != nil ) |
|
{ |
|
if( IsAnimComponent( comp ) ) |
|
{ |
|
plAnimComponentBase *base = (plAnimComponentBase *)comp; |
|
return (plAnimObjInterface *)base; |
|
} |
|
} |
|
else |
|
{ |
|
plAnimStealthNode *stealth = plAnimStealthNode::ConvertToStealth( node ); |
|
if( stealth != nil ) |
|
return (plAnimObjInterface *)stealth; |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
void plAnimComponentProc::EnableGlobal(HWND hWnd, hsBool enable) |
|
{ |
|
ComboBox_Enable(GetDlgItem(hWnd, IDC_ANIM_GLOBAL_LIST), enable); |
|
ComboBox_Enable(GetDlgItem(hWnd, IDC_ANIM_NAMES), !enable); |
|
ComboBox_Enable(GetDlgItem(hWnd, IDC_LOOP_NAMES), !enable); |
|
Button_Enable(GetDlgItem(hWnd, IDC_COMP_ANIM_AUTOSTART_CKBX), !enable); |
|
Button_Enable(GetDlgItem(hWnd, IDC_COMP_ANIM_LOOP_CKBX), !enable); |
|
} |
|
|
|
void plAnimComponentProc::FillAgeGlobalComboBox(HWND box, const char *varName) |
|
{ |
|
plStateDescriptor *sd = plSDLMgr::GetInstance()->FindDescriptor(plPageInfoComponent::GetCurrExportAgeName(), plSDL::kLatestVersion); |
|
if (sd) |
|
{ |
|
int i; |
|
for (i = 0; i < sd->GetNumVars(); i++) |
|
{ |
|
plVarDescriptor *var = sd->GetVar(i); |
|
if (var->GetType() == plVarDescriptor::kFloat || |
|
var->GetType() == plVarDescriptor::kDouble || |
|
var->GetType() == plVarDescriptor::kTime || |
|
var->GetType() == plVarDescriptor::kAgeTimeOfDay) |
|
{ |
|
ComboBox_AddString(box, var->GetName()); |
|
} |
|
} |
|
} |
|
ComboBox_AddString(box, "(none)"); |
|
} |
|
|
|
void plAnimComponentProc::SetBoxToAgeGlobal(HWND box, const char *varName) |
|
{ |
|
char buff[512]; |
|
if (!varName || !strcmp(varName, "")) |
|
varName = "(none)"; |
|
|
|
ComboBox_SelectString(box, 0, varName); |
|
ComboBox_GetLBText(box, ComboBox_GetCurSel(box), buff); |
|
if (strcmp(varName, buff)) |
|
{ |
|
// Didn't find our variable in the age SDL file... |
|
// Probably just missing the sdl file, |
|
// so we'll force it in there. It'll export fine. |
|
ComboBox_AddString(box, varName); |
|
ComboBox_SelectString(box, 0, varName); |
|
} |
|
} |
|
|
|
BOOL plAnimComponentProc::DlgProc(TimeValue t, IParamMap2 *pMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
HWND gWnd = GetDlgItem(hWnd, IDC_ANIM_GLOBAL_LIST); |
|
char buff[512]; |
|
switch (msg) |
|
{ |
|
case WM_INITDIALOG: |
|
{ |
|
fPB = pMap->GetParamBlock(); |
|
|
|
fNoteTrackDlg.Init(GetDlgItem(hWnd, IDC_ANIM_NAMES), |
|
GetDlgItem(hWnd, IDC_LOOP_NAMES), |
|
kAnimName, |
|
kAnimLoopName, |
|
fPB, |
|
fPB->GetOwner()); |
|
fNoteTrackDlg.Load(); |
|
|
|
EnableWindow(GetDlgItem(hWnd, IDC_LOOP_NAMES), fPB->GetInt(kAnimLoop)); |
|
|
|
FillAgeGlobalComboBox(gWnd, fPB->GetStr(ParamID(kAnimGlobalName))); |
|
SetBoxToAgeGlobal(gWnd, fPB->GetStr(ParamID(kAnimGlobalName))); |
|
EnableGlobal(hWnd, fPB->GetInt(ParamID(kAnimUseGlobal))); |
|
Button_Enable(GetDlgItem(hWnd, IDC_COMP_ANIM_PHYSANIM), |
|
HasPhysicalComponent((plComponentBase*)fPB->GetOwner())); |
|
} |
|
return TRUE; |
|
case WM_COMMAND: |
|
if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_ANIM_NAMES) |
|
{ |
|
fNoteTrackDlg.AnimChanged(); |
|
return TRUE; |
|
} |
|
else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_LOOP_NAMES) |
|
{ |
|
// Get the new loop name |
|
fNoteTrackDlg.LoopChanged(); |
|
return TRUE; |
|
} |
|
else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_ANIM_GLOBAL_LIST) |
|
{ |
|
ComboBox_GetLBText(gWnd, ComboBox_GetCurSel(gWnd), buff); |
|
fPB->SetValue(ParamID(kAnimGlobalName), 0, _T(buff)); |
|
} |
|
// Catch loop button updates |
|
else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_COMP_ANIM_LOOP_CKBX) |
|
EnableWindow(GetDlgItem(hWnd, IDC_LOOP_NAMES), fPB->GetInt(kAnimLoop)); |
|
else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_COMP_ANIM_USE_GLOBAL) |
|
{ |
|
EnableGlobal(hWnd, fPB->GetInt(ParamID(kAnimUseGlobal))); |
|
} |
|
break; |
|
} |
|
return false; |
|
} |
|
|
|
void plAnimComponentProc::Update( TimeValue t, Interval &valid, IParamMap2 *pmap ) |
|
{ |
|
HWND hWnd = pmap->GetHWnd(); |
|
IParamBlock2 *pb = pmap->GetParamBlock(); |
|
SetBoxToAgeGlobal(GetDlgItem(hWnd, IDC_ANIM_GLOBAL_LIST), pb->GetStr(ParamID(kAnimGlobalName))); |
|
} |
|
|
|
void plAnimComponentProc::DeleteThis() |
|
{ |
|
fNoteTrackDlg.DeleteCache(); |
|
} |
|
|
|
// For the paramblock below. |
|
static plAnimComponentProc gAnimCompProc; |
|
|
|
#define WM_ROLLOUT_OPEN WM_USER+1 |
|
|
|
class plAnimEaseComponentProc : public ParamMap2UserDlgProc |
|
{ |
|
protected: |
|
void EnableStopPoints(IParamMap2 *pm, bool enable) |
|
{ |
|
pm->Enable(kAnimEaseInMin, enable); |
|
pm->Enable(kAnimEaseInMax, enable); |
|
pm->Enable(kAnimEaseOutMin, enable); |
|
pm->Enable(kAnimEaseOutMax, enable); |
|
} |
|
|
|
public: |
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
switch (msg) |
|
{ |
|
case WM_INITDIALOG: |
|
{ |
|
IParamBlock2 *pb = map->GetParamBlock(); |
|
|
|
// Enable the min and max controls (that are only valid with stop points) |
|
// if at least one of the targets has a stop point |
|
plAnimComponent *comp = (plAnimComponent*)pb->GetOwner(); |
|
int num = comp->NumTargets(); |
|
bool stopPoints = false; |
|
for (int i = 0; i < num; i++) |
|
{ |
|
if (DoesHaveStopPoints(comp->GetTarget(i))) |
|
{ |
|
stopPoints = true; |
|
break; |
|
} |
|
} |
|
EnableStopPoints(map, stopPoints); |
|
|
|
// If we're doing an ease, set the ease rollup to open |
|
if (pb->GetInt(kAnimEaseInType) != plAnimEaseTypes::kNoEase || |
|
pb->GetInt(kAnimEaseOutType) != plAnimEaseTypes::kNoEase) |
|
PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); |
|
} |
|
return TRUE; |
|
|
|
// Max doesn't know about the rollup until after WM_CREATE, so we get |
|
// around it by posting a message |
|
case WM_ROLLOUT_OPEN: |
|
{ |
|
IRollupWindow *rollup = GetCOREInterface()->GetCommandPanelRollup(); |
|
int idx = rollup->GetPanelIndex(hWnd); |
|
rollup->SetPanelOpen(idx, TRUE); |
|
} |
|
return TRUE; |
|
} |
|
return FALSE; |
|
} |
|
|
|
void DeleteThis() {} |
|
}; |
|
// For the paramblock below. |
|
static plAnimEaseComponentProc gAnimEaseCompProc; |
|
|
|
/* |
|
// Make sure min is less than normal, which is less than max |
|
class EaseAccessor : public PBAccessor |
|
{ |
|
protected: |
|
bool fDoingUpdate; |
|
|
|
void AdjustMin(IParamBlock2 *pb, ParamID minID, ParamID normalID, ParamID maxID, float value) |
|
{ |
|
if (value > pb->GetFloat(normalID)) |
|
{ |
|
pb->SetValue(normalID, 0, value); |
|
if (value > pb->GetFloat(maxID)) |
|
pb->SetValue(maxID, 0, value); |
|
} |
|
} |
|
void AdjustNormal(IParamBlock2 *pb, ParamID minID, ParamID normalID, ParamID maxID, float value) |
|
{ |
|
if (value < pb->GetFloat(minID)) |
|
pb->SetValue(minID, 0, value); |
|
if (value > pb->GetFloat(maxID)) |
|
pb->SetValue(maxID, 0, value); |
|
} |
|
void AdjustMax(IParamBlock2 *pb, ParamID minID, ParamID normalID, ParamID maxID, float value) |
|
{ |
|
if (value < pb->GetFloat(normalID)) |
|
{ |
|
pb->SetValue(normalID, 0, value); |
|
if (value < pb->GetFloat(minID)) |
|
pb->SetValue(minID, 0, value); |
|
} |
|
} |
|
|
|
public: |
|
EaseAccessor() : fDoingUpdate(false) {} |
|
|
|
void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) |
|
{ |
|
if (fDoingUpdate) |
|
return; |
|
fDoingUpdate = true; |
|
|
|
plAnimComponent *comp = (plAnimComponent*)owner; |
|
IParamBlock2 *pb = comp->GetParamBlockByID(plComponentBase::kBlkComp); |
|
|
|
if (id == kAnimEaseInMin) |
|
AdjustMin(pb, kAnimEaseInMin, kAnimEaseInLength, kAnimEaseInMin, v.f); |
|
else if (id == kAnimEaseInLength) |
|
AdjustNormal(pb, kAnimEaseInMin, kAnimEaseInLength, kAnimEaseInMax, v.f); |
|
else if (id == kAnimEaseInMax) |
|
AdjustMax(pb, kAnimEaseInMin, kAnimEaseInLength, kAnimEaseInMax, v.f); |
|
else if (id == kAnimEaseOutMin) |
|
AdjustMin(pb, kAnimEaseOutMin, kAnimEaseOutLength, kAnimEaseOutMax, v.f); |
|
else if (id == kAnimEaseOutLength) |
|
AdjustNormal(pb, kAnimEaseOutMin, kAnimEaseOutLength, kAnimEaseOutMax, v.f); |
|
else if (id == kAnimEaseOutMax) |
|
AdjustMax(pb, kAnimEaseOutMin, kAnimEaseOutLength, kAnimEaseOutMax, v.f); |
|
|
|
fDoingUpdate = false; |
|
} |
|
}; |
|
*/ |
|
|
|
CLASS_DESC(plAnimComponent, gAnimDesc, "Animation", "Animation", COMP_TYPE_MISC, ANIM_COMP_CID) |
|
CLASS_DESC(plAnimGroupedComponent, gAnimGroupedDesc, "Animation Grouped", "AnimGrouped", COMP_TYPE_MISC, ANIM_GROUP_COMP_CID) |
|
|
|
hsBool plAnimComponentBase::IsAnimComponent(plComponentBase *comp) |
|
{ |
|
return (comp->ClassID() == ANIM_COMP_CID || |
|
comp->ClassID() == ANIM_GROUP_COMP_CID); |
|
} |
|
|
|
enum { kAnimMain, kAnimEase }; |
|
|
|
ParamBlockDesc2 gAnimBlock |
|
( |
|
plComponent::kBlkComp, _T("animation"), 0, &gAnimDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, |
|
|
|
// map rollups |
|
2, |
|
kAnimMain, IDD_COMP_ANIM, IDS_COMP_ANIM, 0, 0, &gAnimCompProc, |
|
kAnimEase, IDD_COMP_ANIM_EASE, IDS_COMP_ANIM_EASE, 0, APPENDROLL_CLOSED, &gAnimEaseCompProc, |
|
|
|
// Anim Main rollout |
|
kAnimAutoStart, _T("autoStart"), TYPE_BOOL, 0, 0, |
|
p_ui, kAnimMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_AUTOSTART_CKBX, |
|
p_default, FALSE, |
|
end, |
|
kAnimLoop, _T("loop"), TYPE_BOOL, 0, 0, |
|
p_ui, kAnimMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_LOOP_CKBX, |
|
p_default, FALSE, |
|
end, |
|
kAnimName, _T("animName"), TYPE_STRING, 0, 0, |
|
end, |
|
kAnimUseGlobal, _T("UseGlobal"), TYPE_BOOL, 0, 0, |
|
p_default, FALSE, |
|
p_ui, kAnimMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_USE_GLOBAL, |
|
end, |
|
kAnimGlobalName, _T("GlobalName"), TYPE_STRING, 0, 0, |
|
p_default, _T(""), |
|
end, |
|
kAnimLoopName, _T("loopName"), TYPE_STRING, 0, 0, |
|
end, |
|
kAnimPhysAnim, _T("PhysAnim"), TYPE_BOOL, 0, 0, |
|
p_default, TRUE, |
|
p_ui, kAnimMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_PHYSANIM, |
|
end, |
|
|
|
// Anim Ease rollout |
|
kAnimEaseInType, _T("easeInType"), TYPE_INT, 0, 0, |
|
p_ui, kAnimEase, TYPE_RADIO, 3, IDC_COMP_ANIM_EASE_IN_NONE, IDC_COMP_ANIM_EASE_IN_CONST_ACCEL, IDC_COMP_ANIM_EASE_IN_SPLINE, |
|
p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, |
|
p_default, plAnimEaseTypes::kNoEase, |
|
end, |
|
kAnimEaseInLength, _T("easeInLength"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ANIM_EASE_IN_TIME, IDC_COMP_ANIM_EASE_IN_TIME_SPIN, 1.0, |
|
p_accessor, &gAnimCompEaseAccessor, |
|
end, |
|
kAnimEaseInMin, _T("easeInMin"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ANIM_EASE_IN_MIN, IDC_COMP_ANIM_EASE_IN_MIN_SPIN, 1.0, |
|
p_accessor, &gAnimCompEaseAccessor, |
|
end, |
|
kAnimEaseInMax, _T("easeInMax"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ANIM_EASE_IN_MAX, IDC_COMP_ANIM_EASE_IN_MAX_SPIN, 1.0, |
|
p_accessor, &gAnimCompEaseAccessor, |
|
end, |
|
|
|
kAnimEaseOutType, _T("easeOutType"), TYPE_INT, 0, 0, |
|
p_ui, kAnimEase, TYPE_RADIO, 3, IDC_COMP_ANIM_EASE_OUT_NONE, IDC_COMP_ANIM_EASE_OUT_CONST_ACCEL, IDC_COMP_ANIM_EASE_OUT_SPLINE, |
|
p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, |
|
p_default, plAnimEaseTypes::kNoEase, |
|
end, |
|
kAnimEaseOutLength, _T("easeOutLength"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ANIM_EASE_OUT_TIME, IDC_COMP_ANIM_EASE_OUT_TIME_SPIN, 1.0, |
|
p_accessor, &gAnimCompEaseAccessor, |
|
end, |
|
kAnimEaseOutMin, _T("easeOutMin"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ANIM_EASE_OUT_MIN, IDC_COMP_ANIM_EASE_OUT_MIN_SPIN, 1.0, |
|
p_accessor, &gAnimCompEaseAccessor, |
|
end, |
|
kAnimEaseOutMax, _T("easeOutMax"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ANIM_EASE_OUT_MAX, IDC_COMP_ANIM_EASE_OUT_MAX_SPIN, 1.0, |
|
p_accessor, &gAnimCompEaseAccessor, |
|
end, |
|
|
|
end |
|
); |
|
|
|
ParamBlockDesc2 gAnimGroupedBlock |
|
( |
|
plComponent::kBlkComp, _T("animGrouped"), 0, &gAnimGroupedDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, |
|
|
|
// map rollups |
|
2, |
|
kAnimMain, IDD_COMP_ANIM, IDS_COMP_ANIM_GROUPED, 0, 0, &gAnimCompProc, |
|
kAnimEase, IDD_COMP_ANIM_EASE, IDS_COMP_ANIM_EASE, 0, APPENDROLL_CLOSED, &gAnimEaseCompProc, |
|
|
|
// use params from existing descriptor |
|
&gAnimBlock, |
|
|
|
end |
|
); |
|
|
|
plAnimComponent::plAnimComponent() |
|
{ |
|
fClassDesc = &gAnimDesc; |
|
fClassDesc->MakeAutoParamBlocks(this); |
|
} |
|
|
|
plKey plAnimComponent::GetModKey(plMaxNode *node) |
|
{ |
|
if (fMods.find(node) != fMods.end()) |
|
return fMods[node]->GetKey(); |
|
|
|
return nil; |
|
} |
|
|
|
hsBool plAnimComponent::GetKeyList( INode *restrictedNode, hsTArray<plKey> &outKeys ) |
|
{ |
|
if( restrictedNode != nil ) |
|
{ |
|
if( fMods.find( (plMaxNode *)restrictedNode ) != fMods.end() ) |
|
{ |
|
outKeys.Append( fMods[ (plMaxNode *)restrictedNode ]->GetKey() ); |
|
return true; |
|
} |
|
return false; |
|
} |
|
else |
|
{ |
|
hsAssert( false, "DO SOMETHING!" ); |
|
return false; |
|
} |
|
} |
|
|
|
plAnimGroupedComponent::plAnimGroupedComponent() : fForward(nil) |
|
{ |
|
fClassDesc = &gAnimGroupedDesc; |
|
fClassDesc->MakeAutoParamBlocks(this); |
|
} |
|
|
|
plKey plAnimGroupedComponent::GetModKey(plMaxNode *node) |
|
{ |
|
if( fForward ) |
|
return fForward->GetKey(); |
|
return nil; |
|
} |
|
|
|
hsBool plAnimGroupedComponent::GetKeyList( INode *restrictedNode, hsTArray<plKey> &outKeys ) |
|
{ |
|
if( fForward ) |
|
{ |
|
outKeys.Append( fForward->GetKey() ); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
|
|
#include "pnMessage/plNodeRefMsg.h" |
|
|
|
hsBool plAnimGroupedComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
bool needSetMaster = fNeedReset; |
|
if (fNeedReset) |
|
{ |
|
fForward = TRACKED_NEW plMsgForwarder; |
|
plKey forwardKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), fForward, node->GetLocation()); |
|
|
|
plNodeRefMsg *refMsg = TRACKED_NEW plNodeRefMsg(node->GetRoomKey(), plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric); |
|
hsgResMgr::ResMgr()->AddViaNotify(forwardKey, refMsg, plRefFlags::kActiveRef); |
|
} |
|
|
|
hsBool ret = plAnimComponentBase::PreConvert(node, pErrMsg); |
|
|
|
plAGMasterMod *mod = fMods[node]; |
|
|
|
if (needSetMaster) |
|
mod->SetIsGroupMaster(true, fForward); |
|
mod->SetIsGrouped(true); |
|
|
|
fForward->AddForwardKey(mod->GetKey()); |
|
|
|
return ret; |
|
} |
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
plAnimComponentBase::plAnimComponentBase() : fNeedReset(true) |
|
{ |
|
} |
|
|
|
|
|
const char *plAnimComponentBase::GetAnimName() |
|
{ |
|
const char *name = fCompPB->GetStr(kAnimName); |
|
if (!name || name[0] == '\0') |
|
return nil; |
|
return name; |
|
} |
|
|
|
bool IsSubworld(plMaxNode* node) |
|
{ |
|
uint32_t numComps = node->NumAttachedComponents(); |
|
for (int i = 0; i < numComps; i++) |
|
{ |
|
plComponentBase* comp = node->GetAttachedComponent(i); |
|
if (comp && comp->ClassID() == PHYS_SUBWORLD_CID) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void SetPhysAnimRecurse(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
// If we hit a subworld, stop. The subworld may be animated, but the |
|
// physicals in it aren't. |
|
if (IsSubworld(node)) |
|
return; |
|
|
|
if (HasPhysicalComponent(node, false)) |
|
{ char* debugName = node->GetName(); |
|
node->GetPhysicalProps()->SetPhysAnim(true, node, pErrMsg); |
|
} |
|
int i; |
|
for (i = 0; i < node->NumberOfChildren(); i++) |
|
SetPhysAnimRecurse((plMaxNode *)node->GetChildNode(i), pErrMsg); |
|
} |
|
|
|
hsBool plAnimComponentBase::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
if (node->IsTMAnimated()) |
|
{ |
|
node->SetMovable(true); |
|
node->SetForceLocal(true); |
|
|
|
// |
|
// forceLocal on our parent (since keys work in local space) |
|
// |
|
plMaxNode *parent = (plMaxNode *)node->GetParentNode(); |
|
if (!parent->IsRootNode()) |
|
{ |
|
parent->SetForceLocal(true); |
|
|
|
//char str[512]; |
|
//sprintf(str, "Forcing local on '%s' because of animated child '%s'\n",parent->GetName(),node->GetName() ); |
|
//OutputDebugString(str); |
|
} |
|
} |
|
|
|
if (fCompPB->GetInt(ParamID(kAnimPhysAnim))) |
|
SetPhysAnimRecurse(node, pErrMsg); |
|
|
|
/* |
|
int childCount = node->NumberOfChildren(); |
|
for (int i = 0; i < childCount; i++) |
|
{ |
|
SetupProperties((plMaxNode *)node->GetChildNode(i), pErrMsg); |
|
} |
|
*/ |
|
return true; |
|
} |
|
|
|
hsBool plAnimComponentBase::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
// If this is the first time in the preconvert, reset the map |
|
if (fNeedReset) |
|
{ |
|
fNeedReset = false; |
|
} |
|
|
|
// If this node is animated, create it's modifier and key now so we can give |
|
// it out to anyone that needs it |
|
// if (node->IsTMAnimated() || node->IsAnimatedLight()) |
|
// { |
|
const char *name = node->GetName(); |
|
|
|
plAGMasterMod *mod = node->GetAGMasterMod(); |
|
if (mod == nil) |
|
{ |
|
if (!node->HasAGMod()) // Need to add this before the MasterMod, if it doesn't have one already. |
|
{ |
|
node->AddModifier(new plAGModifier(node->GetName()), IGetUniqueName(node)); |
|
} |
|
mod = TRACKED_NEW plAGMasterMod(); |
|
|
|
plKey modKey = node->AddModifier(mod, IGetUniqueName(node)); |
|
} |
|
fMods[node] = mod; |
|
// } |
|
|
|
|
|
// Small change here. We're setting up the timing specs on the |
|
// plAGAnim object during preconvert, so that the info is available |
|
// when actually converting the anim (and for other components |
|
// that need it, but may or may not actually convert before us.) |
|
|
|
// Note: if the component uses the "(Entire Animation)" segment for |
|
// the main start/end, the start/end times won't be valid until |
|
// we've added all keys during convert. Some cleanup might |
|
// be necessary in this case. |
|
|
|
const char *animName = fCompPB->GetStr(kAnimName); |
|
if (animName == nil || !strcmp(animName, "")) |
|
animName = ENTIRE_ANIMATION_NAME; |
|
|
|
if (fCompPB->GetInt(ParamID(kAnimUseGlobal))) |
|
{ |
|
plAgeGlobalAnim *ageAnim = TRACKED_NEW plAgeGlobalAnim(animName, 0, 0); |
|
ageAnim->SetGlobalVarName((char*)fCompPB->GetStr(ParamID(kAnimGlobalName))); |
|
|
|
fAnims[node] = ageAnim; |
|
} |
|
else |
|
{ |
|
plATCAnim *ATCAnim = TRACKED_NEW plATCAnim(animName, 0, 0); |
|
plNotetrackAnim noteAnim(node, pErrMsg); |
|
plAnimInfo info = noteAnim.GetAnimInfo(animName); |
|
ATCAnim->SetAutoStart(fCompPB->GetInt(kAnimAutoStart)); |
|
|
|
float start = info.GetAnimStart(); |
|
float end = info.GetAnimEnd(); |
|
float initial = info.GetAnimInitial(); |
|
if (start != -1) |
|
ATCAnim->SetStart(start); |
|
if (end != -1) |
|
ATCAnim->SetEnd(end); |
|
if (initial != -1) |
|
ATCAnim->SetInitial(initial); |
|
|
|
if (fCompPB->GetInt(kAnimLoop)) |
|
{ |
|
ATCAnim->SetLoop(true); |
|
const char *loopName = fCompPB->GetStr(kAnimLoopName); |
|
float loopStart = info.GetLoopStart(loopName); |
|
float loopEnd = info.GetLoopEnd(loopName); |
|
|
|
ATCAnim->SetLoopStart(loopStart == -1 ? ATCAnim->GetStart() : loopStart); |
|
ATCAnim->SetLoopEnd(loopEnd == -1 ? ATCAnim->GetEnd() : loopEnd); |
|
} |
|
|
|
while (const char *loop = info.GetNextLoopName()) |
|
ATCAnim->AddLoop(loop, info.GetLoopStart(loop), info.GetLoopEnd(loop)); |
|
|
|
while (const char *marker = info.GetNextMarkerName()) |
|
ATCAnim->AddMarker(marker, info.GetMarkerTime(marker)); |
|
|
|
float stopPoint = -1; |
|
while ((stopPoint = info.GetNextStopPoint()) != -1) |
|
ATCAnim->AddStopPoint(stopPoint); |
|
|
|
ATCAnim->SetEaseInType(fCompPB->GetInt(kAnimEaseInType)); |
|
ATCAnim->SetEaseOutType(fCompPB->GetInt(kAnimEaseOutType)); |
|
ATCAnim->SetEaseInLength(fCompPB->GetFloat(kAnimEaseInLength)); |
|
ATCAnim->SetEaseInMin(fCompPB->GetFloat(kAnimEaseInMin)); |
|
ATCAnim->SetEaseInMax(fCompPB->GetFloat(kAnimEaseInMax)); |
|
ATCAnim->SetEaseOutLength(fCompPB->GetFloat(kAnimEaseOutLength)); |
|
ATCAnim->SetEaseOutMin(fCompPB->GetFloat(kAnimEaseOutMin)); |
|
ATCAnim->SetEaseOutMax(fCompPB->GetFloat(kAnimEaseOutMax)); |
|
|
|
fAnims[node] = ATCAnim; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
hsBool plAnimComponentBase::IAddTMToAnim(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg) |
|
{ |
|
hsBool result = false; |
|
|
|
// Get the affine parts and the TM Controller |
|
plSceneObject *obj = node->GetSceneObject(); |
|
hsAffineParts * parts = TRACKED_NEW hsAffineParts; |
|
plController* tmc; |
|
|
|
if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) |
|
tmc = hsControlConverter::Instance().ConvertTMAnim(obj, node, parts); |
|
else |
|
tmc = hsControlConverter::Instance().ConvertTMAnim(obj, node, parts, anim->GetStart(), anim->GetEnd()); |
|
|
|
if (tmc) |
|
{ |
|
plMatrixChannelApplicator *app = TRACKED_NEW plMatrixChannelApplicator(); |
|
app->SetChannelName(node->GetName()); |
|
plMatrixControllerChannel *channel = TRACKED_NEW plMatrixControllerChannel(tmc, parts); |
|
app->SetChannel(channel); |
|
anim->AddApplicator(app); |
|
if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) |
|
anim->ExtendToLength(tmc->GetLength()); |
|
result = true; |
|
} |
|
|
|
delete parts; // We copy this over, so no need to keep it around |
|
return result; |
|
} |
|
|
|
hsBool plAnimComponentBase::IAddLightToAnim(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg) |
|
{ |
|
if (!node->IsAnimatedLight()) |
|
return false; |
|
|
|
Object *obj = node->GetObjectRef(); |
|
Class_ID cid = obj->ClassID(); |
|
|
|
IParamBlock2 *pb = nil; |
|
if (cid == RTSPOT_LIGHT_CLASSID) |
|
pb = obj->GetParamBlockByID(plRTLightBase::kBlkSpotLight); |
|
else if (cid == RTOMNI_LIGHT_CLASSID) |
|
pb = obj->GetParamBlockByID(plRTLightBase::kBlkOmniLight); |
|
else if (cid == RTDIR_LIGHT_CLASSID) |
|
pb = obj->GetParamBlockByID(plRTLightBase::kBlkTSpotLight); |
|
else if (cid == RTPDIR_LIGHT_CLASSID) |
|
pb = obj->GetParamBlockByID(plRTLightBase::kBlkMain); |
|
|
|
node->GetRTLightColAnim(pb, anim); |
|
|
|
if (cid == RTSPOT_LIGHT_CLASSID || cid == RTOMNI_LIGHT_CLASSID) |
|
node->GetRTLightAttenAnim(pb, anim); |
|
|
|
if (cid == RTSPOT_LIGHT_CLASSID) |
|
node->GetRTConeAnim(pb, anim); |
|
|
|
return true; |
|
} |
|
|
|
hsBool plAnimComponentBase::IConvertNodeSegmentBranch(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg) |
|
{ |
|
hsBool madeAnim = false; |
|
int i; |
|
|
|
if (IAddTMToAnim(node, anim, pErrMsg)) |
|
madeAnim = true; |
|
if (IAddLightToAnim(node, anim, pErrMsg)) |
|
madeAnim = true; |
|
|
|
for (i = 0; i < node->NumAttachedComponents(); i++) |
|
{ |
|
if (node->GetAttachedComponent(i)->AddToAnim(anim, node)) |
|
madeAnim = true; |
|
} |
|
|
|
if (madeAnim) |
|
{ |
|
// It has an animation, we're going to need a plAGMod when loading the anim |
|
if (!node->HasAGMod()) |
|
{ |
|
node->AddModifier(new plAGModifier(node->GetName()), IGetUniqueName(node)); |
|
} |
|
madeAnim = true; |
|
} |
|
/* |
|
// let's see if the children have any segments specified... |
|
int childCount = node->NumberOfChildren(); |
|
for (int i = 0; i < childCount; i++) |
|
{ |
|
if (IConvertNodeSegmentBranch((plMaxNode *)(node->GetChildNode(i)), anim, pErrMsg)) |
|
madeAnim = true; |
|
} |
|
*/ |
|
return madeAnim; |
|
} |
|
|
|
hsBool plAnimComponentBase::IMakePersistent(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg) |
|
{ |
|
// anims made by this component are private to the specific AGMasterMod, so we attach them there. |
|
plAGMasterMod *mod = plAGMasterMod::ConvertNoRef(fMods[node]); |
|
hsAssert(mod != nil, "No MasterMod to make animation persistent!"); |
|
|
|
char buffer[256]; |
|
sprintf(buffer, "%s_%s_anim_%d", node->GetName(), anim->GetName(), mod->GetNumPrivateAnimations()); |
|
plLocation nodeLoc = node->GetLocation(); |
|
plKey animKey = hsgResMgr::ResMgr()->NewKey(buffer, anim, nodeLoc); |
|
|
|
plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(mod->GetKey(), plRefMsg::kOnCreate, 0, 0); |
|
hsgResMgr::ResMgr()->AddViaNotify(animKey, refMsg, plRefFlags::kActiveRef); |
|
|
|
return true; |
|
} |
|
|
|
|
|
hsBool plAnimComponentBase::Convert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
fNeedReset = true; |
|
|
|
if (!IConvertNodeSegmentBranch(node, fAnims[node], pErrMsg)) |
|
{ |
|
// Either we delete it here, or we make it persistent below and the resMgr handles it |
|
delete fAnims[node]; |
|
fAnims[node] = nil; |
|
return false; |
|
} |
|
|
|
if (fCompPB->GetInt(ParamID(kAnimUseGlobal))) |
|
{ |
|
((plAgeGlobalAnim *)fAnims[node])->SetGlobalVarName((char*)fCompPB->GetStr(ParamID(kAnimGlobalName))); |
|
} |
|
else // It's an ATCAnim |
|
{ |
|
// If we're on an "(Entire Animation)" segment. The loops won't know their lengths until |
|
// after the nodes have been converted and added. So we adjust them here if necessary. |
|
((plATCAnim *)fAnims[node])->CheckLoop(); |
|
} |
|
|
|
IMakePersistent(node, fAnims[node], pErrMsg); |
|
return true; |
|
} |
|
|
|
hsBool plAnimComponentBase::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
fMods.clear(); |
|
fLightMods.clear(); |
|
fAnims.clear(); |
|
return true; |
|
} |
|
|
|
void plAnimComponentBase::SetupCtl( plAGAnim *anim, plController *ctl, plAGApplicator *app, plMaxNode *node ) |
|
{ |
|
plScalarControllerChannel *channel = TRACKED_NEW plScalarControllerChannel(ctl); |
|
app->SetChannel(channel); |
|
anim->AddApplicator(app); |
|
if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) |
|
anim->ExtendToLength(ctl->GetLength()); |
|
} |
|
|
|
//// Picker Dialog for Restricted Animation Components ////////////////////////////////////////// |
|
|
|
class plPickAnimCompNode : public plPickCompNode |
|
{ |
|
protected: |
|
ParamID fTypeID; |
|
|
|
void IAddUserType(HWND hList) |
|
{ |
|
int type = fPB->GetInt(fTypeID); |
|
|
|
int idx = ListBox_AddString(hList, kUseParamBlockNodeString); |
|
if (type == plAnimObjInterface::kUseParamBlockNode && !fPB->GetINode(fNodeParamID)) |
|
ListBox_SetCurSel(hList, idx); |
|
|
|
|
|
idx = ListBox_AddString(hList, kUseOwnerNodeString); |
|
if (type == plAnimObjInterface::kUseOwnerNode) |
|
ListBox_SetCurSel(hList, idx); |
|
} |
|
|
|
void ISetUserType(plMaxNode* node, const char* userType) |
|
{ |
|
if (hsStrEQ(userType, kUseParamBlockNodeString)) |
|
{ |
|
ISetNodeValue(nil); |
|
fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseParamBlockNode); |
|
} |
|
else if (hsStrEQ(userType, kUseOwnerNodeString)) |
|
{ |
|
ISetNodeValue(nil); |
|
fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseOwnerNode); |
|
} |
|
else |
|
fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseParamBlockNode); |
|
} |
|
|
|
public: |
|
plPickAnimCompNode(IParamBlock2* pb, ParamID nodeParamID, ParamID typeID, plComponentBase *comp) : |
|
plPickCompNode(pb, nodeParamID, comp), fTypeID(typeID) |
|
{ |
|
} |
|
}; |
|
|
|
void plAnimComponentBase::PickTargetNode( IParamBlock2 *destPB, ParamID destParamID, ParamID destTypeID ) |
|
{ |
|
plPickAnimCompNode pick( destPB, destParamID, destTypeID, (plComponentBase *)this ); |
|
pick.DoPick(); |
|
} |
|
|
|
const char *plAnimComponentBase::GetIfaceSegmentName( hsBool allowNil ) |
|
{ |
|
const char *name = GetAnimName(); |
|
if( allowNil || name != nil ) |
|
return name; |
|
return ENTIRE_ANIMATION_NAME; |
|
} |
|
|
|
//// Hit Callback for Animations ///////////////////////////////////////////// |
|
|
|
class plPlasmaAnimHitCallback : public HitByNameDlgCallback |
|
{ |
|
protected: |
|
IParamBlock2* fPB; |
|
ParamID fParamID; |
|
TCHAR fTitle[ 128 ]; |
|
|
|
public: |
|
plPlasmaAnimHitCallback( IParamBlock2 *pb, ParamID paramID, TCHAR *title = nil ) |
|
: fPB( pb ), fParamID( paramID ) |
|
|
|
{ |
|
strcpy( fTitle, title ); |
|
} |
|
|
|
virtual TCHAR *dialogTitle() { return fTitle; } |
|
virtual TCHAR *buttonText() { return "OK"; } |
|
|
|
virtual int filter( INode *node ) |
|
{ |
|
plComponentBase *comp = ( (plMaxNodeBase *)node )->ConvertToComponent(); |
|
if( comp != nil && plAnimComponentBase::IsAnimComponent( comp ) ) |
|
{ |
|
// Make sure it won't create a cyclical reference (Max doesn't like those) |
|
if( comp->TestForLoop( FOREVER, fPB ) == REF_FAIL ) |
|
return FALSE; |
|
|
|
return TRUE; |
|
} |
|
else |
|
{ |
|
plAnimStealthNode *stealth = plAnimStealthNode::ConvertToStealth( node ); |
|
if( stealth != nil ) |
|
{ |
|
if( stealth->TestForLoop( FOREVER, fPB ) == REF_FAIL ) |
|
return FALSE; |
|
|
|
if( !stealth->IsParentUsedInScene() ) |
|
return FALSE; |
|
|
|
return TRUE; |
|
} |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
virtual void proc( INodeTab &nodeTab ) |
|
{ |
|
fPB->SetValue( fParamID, (TimeValue)0, nodeTab[ 0 ] ); |
|
} |
|
|
|
virtual BOOL showHiddenAndFrozen() { return TRUE; } |
|
virtual BOOL singleSelect() { return TRUE; } |
|
}; |
|
|
|
//// Dialog Proc For Anim Selection ///////////////////////////////////////////////////////////// |
|
|
|
plPlasmaAnimSelectDlgProc::plPlasmaAnimSelectDlgProc( ParamID paramID, int dlgItem, TCHAR *promptTitle, ParamMap2UserDlgProc *chainedDlgProc ) |
|
{ |
|
fParamID = paramID; |
|
fDlgItem = dlgItem; |
|
fUseNode = false; |
|
strcpy( fTitle, promptTitle ); |
|
fChain = chainedDlgProc; |
|
} |
|
|
|
plPlasmaAnimSelectDlgProc::plPlasmaAnimSelectDlgProc( ParamID paramID, int dlgItem, ParamID nodeParamID, ParamID typeParamID, int nodeDlgItem, |
|
TCHAR *promptTitle, ParamMap2UserDlgProc *chainedDlgProc ) |
|
{ |
|
fParamID = paramID; |
|
fDlgItem = dlgItem; |
|
fUseNode = true; |
|
fNodeParamID = nodeParamID; |
|
fTypeParamID = typeParamID; |
|
fNodeDlgItem = nodeDlgItem; |
|
strcpy( fTitle, promptTitle ); |
|
fChain = chainedDlgProc; |
|
} |
|
|
|
void plPlasmaAnimSelectDlgProc::SetThing( ReferenceTarget *m ) |
|
{ |
|
if( fChain != nil ) |
|
fChain->SetThing( m ); |
|
} |
|
|
|
void plPlasmaAnimSelectDlgProc::Update( TimeValue t, Interval &valid, IParamMap2 *pmap ) |
|
{ |
|
if( fChain != nil ) |
|
fChain->Update( t, valid, pmap ); |
|
} |
|
|
|
void plPlasmaAnimSelectDlgProc::IUpdateNodeBtn( HWND hWnd, IParamBlock2 *pb ) |
|
{ |
|
if( fUseNode ) |
|
{ |
|
int type = pb->GetInt( fTypeParamID ); |
|
if( type == plAnimObjInterface::kUseOwnerNode ) |
|
::SetWindowText( ::GetDlgItem( hWnd, fNodeDlgItem ), kUseOwnerNodeString ); |
|
else |
|
{ |
|
INode *node = pb->GetINode( fNodeParamID ); |
|
TSTR newName( node ? node->GetName() : kUseParamBlockNodeString ); |
|
::SetWindowText( ::GetDlgItem( hWnd, fNodeDlgItem ), newName ); |
|
} |
|
|
|
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( pb->GetINode( fParamID ) ); |
|
if( iface == nil || !iface->IsNodeRestricted() ) |
|
::EnableWindow( ::GetDlgItem( hWnd, fNodeDlgItem ), false ); |
|
else |
|
{ |
|
::EnableWindow( ::GetDlgItem( hWnd, fNodeDlgItem ), true ); |
|
} |
|
} |
|
} |
|
|
|
BOOL plPlasmaAnimSelectDlgProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) |
|
{ |
|
switch ( msg ) |
|
{ |
|
case WM_INITDIALOG: |
|
{ |
|
IParamBlock2 *pb = pmap->GetParamBlock(); |
|
|
|
INode *node = pb->GetINode( fParamID ); |
|
TSTR newName( node ? node->GetName() : "Pick" ); |
|
::SetWindowText( ::GetDlgItem( hWnd, fDlgItem ), newName ); |
|
|
|
IUpdateNodeBtn( hWnd, pb ); |
|
} |
|
break; |
|
|
|
case WM_COMMAND: |
|
if( ( HIWORD( wParam ) == BN_CLICKED ) ) |
|
{ |
|
if( LOWORD( wParam ) == fDlgItem ) |
|
{ |
|
IParamBlock2 *pb = pmap->GetParamBlock(); |
|
plPlasmaAnimHitCallback hitCB( pb, fParamID, fTitle ); |
|
GetCOREInterface()->DoHitByNameDialog( &hitCB ); |
|
|
|
INode *node = pb->GetINode( fParamID ); |
|
TSTR newName( node ? node->GetName() : "Pick" ); |
|
::SetWindowText( ::GetDlgItem(hWnd, fDlgItem ), newName ); |
|
pmap->Invalidate( fParamID ); |
|
::InvalidateRect( hWnd, NULL, TRUE ); |
|
|
|
IUpdateNodeBtn( hWnd, pb ); |
|
return true; |
|
} |
|
else if( fUseNode && LOWORD( wParam ) == fNodeDlgItem ) |
|
{ |
|
IParamBlock2 *pb = pmap->GetParamBlock(); |
|
|
|
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( pb->GetINode( fParamID ) ); |
|
iface->PickTargetNode( pb, fNodeParamID, fTypeParamID ); |
|
|
|
IUpdateNodeBtn( hWnd, pb ); |
|
return true; |
|
} |
|
} |
|
break; |
|
} |
|
|
|
if( fChain != nil ) |
|
return fChain->DlgProc( t, pmap, hWnd, msg, wParam, lParam ); |
|
|
|
return false; |
|
} |
|
|
|
void plPlasmaAnimSelectDlgProc::DeleteThis() |
|
{ |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
CLASS_DESC(plAnimCompressComp, gAnimCompressDesc, "Anim Compress", "AnimCompress", COMP_TYPE_MISC, ANIM_COMPRESS_COMP_CID) |
|
|
|
ParamBlockDesc2 gAnimCompressBk |
|
( |
|
plComponent::kBlkComp, _T("AnimCompress"), 0, &gAnimCompressDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, |
|
IDD_COMP_ANIM_COMPRESS, IDS_COMP_ANIM_COMPRESS_ROLL, 0, 0, nil, |
|
|
|
plAnimCompressComp::kAnimCompressLevel, _T("compressLevel"), TYPE_INT, 0, 0, |
|
p_ui, TYPE_RADIO, 3, IDC_COMP_ANIM_COMPRESS_NONE, IDC_COMP_ANIM_COMPRESS_LOW, IDC_COMP_ANIM_COMPRESS_HIGH, |
|
p_vals, plAnimCompressComp::kCompressionNone, plAnimCompressComp::kCompressionLow, plAnimCompressComp::kCompressionHigh, |
|
p_default, plAnimCompressComp::kCompressionLow, |
|
end, |
|
|
|
plAnimCompressComp::kAnimCompressThreshold, _T("Threshold"), TYPE_FLOAT, 0, 0, |
|
p_default, 0.01, |
|
p_range, 0.0, 1.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, |
|
IDC_COMP_ANIM_COMPRESS_THRESHOLD, IDC_COMP_ANIM_COMPRESS_THRESHOLD_SPIN, 0.001, |
|
end, |
|
|
|
end |
|
); |
|
|
|
plAnimCompressComp::plAnimCompressComp() |
|
{ |
|
fClassDesc = &gAnimCompressDesc; |
|
fClassDesc->MakeAutoParamBlocks(this); |
|
} |
|
|
|
hsBool plAnimCompressComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
node->SetAnimCompress(fCompPB->GetInt(ParamID(kAnimCompressLevel))); |
|
|
|
// We use Max's key reduction code, which doesn't seem to match up with its own UI. |
|
// Manually using Max's "Reduce Keys" option with a threshold of .01 seems to give |
|
// approximately the same results as calling the function ApplyKeyReduction with |
|
// a threshold of .0002. I want the UI to appear consistent to the artist, so we |
|
// shrug our shoulders and scale down by 50. |
|
node->SetKeyReduceThreshold(fCompPB->GetFloat(ParamID(kAnimCompressThreshold)) / 50.f); |
|
return true; |
|
} |
|
|
|
hsBool plAnimCompressComp::Convert(plMaxNode *node, plErrorMsg *pErrMsg) |
|
{ |
|
return true; |
|
} |