/********************************************************************** *< FILE: followu.cpp DESCRIPTION: A utility that generates path follow CREATED BY: Rolf Berteig HISTORY: created 1/27/97 *> Copyright (c) 1996, All Rights Reserved. **********************************************************************/ #include "ctrl.h" #include "utilapi.h" #define FOLLOW_CLASS_ID Class_ID(0x4557a2c4,0x00186d34) #define FOLLOW_CNAME GetString(IDS_RB_FOLLOWUTIL) class FollowUtil : public UtilityObj { public: IUtil *iu; Interface *ip; HWND hPanel; BOOL bank, flip; float bankAmount, tracking; TimeValue start, end; int samples; FollowUtil(); void BeginEditParams(Interface *ip,IUtil *iu); void EndEditParams(Interface *ip,IUtil *iu); void DeleteThis() {} void SelectionSetChanged(Interface *ip,IUtil *iu); void Init(HWND hWnd); void Destroy(HWND hWnd); void SetStates(HWND hWnd); Matrix3 CalcRefFrame(TimeValue t,INode *node); Point3 GetPosition(INode *node,TimeValue t); void DoFollow(); }; static FollowUtil theFollowUtil; //--- Class Descriptor ------------------------------------------ class FollowUtilClassDesc:public ClassDesc { public: int IsPublic() {return 1;} void * Create(BOOL loading = FALSE) {return &theFollowUtil;} const TCHAR * ClassName() {return FOLLOW_CNAME;} SClass_ID SuperClassID() {return UTILITY_CLASS_ID;} Class_ID ClassID() {return FOLLOW_CLASS_ID;} const TCHAR* Category() {return _T("");} void ResetClassParams(BOOL fileReset); }; void FollowUtilClassDesc::ResetClassParams(BOOL fileReset) { if (fileReset) { theFollowUtil = FollowUtil(); } } static FollowUtilClassDesc followUtilDesc; ClassDesc* GetFollowUtilDesc() {return &followUtilDesc;} #define BANKSCALE 100.0f #define FromBankUI(a) ((a)*BANKSCALE) #define ToBankUI(a) ((a)/BANKSCALE) #define TRACKSCALE 0.04f #define FromTrackUI(a) ((a)*TRACKSCALE) #define ToTrackUI(a) ((a)/TRACKSCALE) ///--- Panel Proc ------------------------------------------------ static BOOL CALLBACK FollowUtilDlgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: theFollowUtil.Init(hWnd); break; case WM_DESTROY: theFollowUtil.Destroy(hWnd); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_PATH_BANK: theFollowUtil.bank = IsDlgButtonChecked(hWnd,IDC_PATH_BANK); theFollowUtil.SetStates(hWnd); break; case IDC_PATH_ALLOWFLIP: theFollowUtil.flip = IsDlgButtonChecked(hWnd,IDC_PATH_ALLOWFLIP); break; case IDC_FOLLOW_APPLY: theFollowUtil.DoFollow(); break; case IDOK: theFollowUtil.iu->CloseUtility(); break; } break; case CC_SPINNER_CHANGE: { ISpinnerControl *spin = (ISpinnerControl*)lParam; switch (LOWORD(wParam)) { case IDC_FOLLOW_BANKSPIN: theFollowUtil.bankAmount = FromBankUI(spin->GetFVal()); break; case IDC_FOLLOW_TRACKSPIN: theFollowUtil.tracking = FromTrackUI(spin->GetFVal()); break; case IDC_FOLLOW_STARTSPIN: theFollowUtil.start = spin->GetIVal(); break; case IDC_FOLLOW_ENDSPIN: theFollowUtil.end = spin->GetIVal(); break; case IDC_FOLLOW_SAMPSPIN: theFollowUtil.samples = spin->GetIVal(); break; } break; } case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MOUSEMOVE: theFollowUtil.ip->RollupMouseMessage(hWnd,msg,wParam,lParam); break; default: return FALSE; } return TRUE; } // --- FollowUtil methods ----------------------------------------- FollowUtil::FollowUtil() { bank = FALSE; flip = FALSE; bankAmount = FromBankUI(0.5f); tracking = FromTrackUI(0.5f); start = 0; end = 16000; samples = 25; } void FollowUtil::BeginEditParams(Interface *ip,IUtil *iu) { this->iu = iu; this->ip = ip; hPanel = ip->AddRollupPage( hInstance, MAKEINTRESOURCE(IDD_FOLLOW_PANEL), FollowUtilDlgProc, GetString(IDS_RB_FOLLOWUTIL), 0); } void FollowUtil::EndEditParams(Interface *ip,IUtil *iu) { this->iu = NULL; this->ip = NULL; ip->DeleteRollupPage(hPanel); hPanel = NULL; } void FollowUtil::SelectionSetChanged(Interface *ip,IUtil *iu) { SetStates(hPanel); if (ip->GetSelNodeCount()==1) { SetDlgItemText(hPanel,IDC_SEL_NAME,ip->GetSelNode(0)->GetName()); } else if (ip->GetSelNodeCount()) { SetDlgItemText(hPanel,IDC_SEL_NAME,GetString(IDS_RB_MULTISEL)); } else { SetDlgItemText(hPanel,IDC_SEL_NAME,GetString(IDS_RB_NONESEL)); } } void FollowUtil::Init(HWND hWnd) { hPanel = hWnd; CheckDlgButton(hWnd,IDC_PATH_BANK,bank); CheckDlgButton(hWnd,IDC_PATH_ALLOWFLIP,flip); ISpinnerControl * spin; spin = GetISpinner(GetDlgItem(hWnd,IDC_FOLLOW_STARTSPIN)); spin->SetLimits(TIME_NegInfinity,TIME_PosInfinity,FALSE); spin->SetScale(10.0f); spin->LinkToEdit(GetDlgItem(hWnd,IDC_FOLLOW_START),EDITTYPE_TIME); spin->SetValue(start,FALSE); ReleaseISpinner(spin); spin = GetISpinner(GetDlgItem(hWnd,IDC_FOLLOW_ENDSPIN)); spin->SetLimits(TIME_NegInfinity,TIME_PosInfinity,FALSE); spin->SetScale(10.0f); spin->LinkToEdit(GetDlgItem(hWnd,IDC_FOLLOW_END),EDITTYPE_TIME); spin->SetValue(end,FALSE); ReleaseISpinner(spin); spin = GetISpinner(GetDlgItem(hWnd,IDC_FOLLOW_SAMPSPIN)); spin->SetLimits(1,999999999,FALSE); spin->SetScale(0.1f); spin->LinkToEdit(GetDlgItem(hWnd,IDC_FOLLOW_SAMP),EDITTYPE_INT); spin->SetValue(samples,FALSE); ReleaseISpinner(spin); spin = GetISpinner(GetDlgItem(hWnd,IDC_FOLLOW_BANKSPIN)); spin->SetLimits(-999999999,999999999,FALSE); spin->SetScale(0.01f); spin->LinkToEdit(GetDlgItem(hWnd,IDC_FOLLOW_BANK),EDITTYPE_FLOAT); spin->SetValue(ToBankUI(bankAmount),FALSE); ReleaseISpinner(spin); spin = GetISpinner(GetDlgItem(hWnd,IDC_FOLLOW_TRACKSPIN)); spin->SetLimits(0.01f,10.0f,FALSE); spin->SetScale(0.01f); spin->LinkToEdit(GetDlgItem(hWnd,IDC_FOLLOW_TRACK),EDITTYPE_FLOAT); spin->SetValue(ToTrackUI(tracking),FALSE); ReleaseISpinner(spin); SelectionSetChanged(ip,iu); } void FollowUtil::Destroy(HWND hWnd) { hPanel = NULL; } void FollowUtil::SetStates(HWND hWnd) { if (ip->GetSelNodeCount()) { EnableWindow(GetDlgItem(hPanel,IDC_FOLLOW_APPLY),TRUE); } else { EnableWindow(GetDlgItem(hPanel,IDC_FOLLOW_APPLY),FALSE); } if (bank) { EnableWindow(GetDlgItem(hWnd,IDC_FOLLOW_BANKLABEL),TRUE); EnableWindow(GetDlgItem(hWnd,IDC_FOLLOW_BANKSPIN),TRUE); EnableWindow(GetDlgItem(hWnd,IDC_FOLLOW_TRACKLABEL),TRUE); EnableWindow(GetDlgItem(hWnd,IDC_FOLLOW_TRACKSPIN),TRUE); } else { EnableWindow(GetDlgItem(hWnd,IDC_FOLLOW_BANKLABEL),FALSE); EnableWindow(GetDlgItem(hWnd,IDC_FOLLOW_BANKSPIN),FALSE); EnableWindow(GetDlgItem(hWnd,IDC_FOLLOW_TRACKLABEL),FALSE); EnableWindow(GetDlgItem(hWnd,IDC_FOLLOW_TRACKSPIN),FALSE); } } Point3 FollowUtil::GetPosition(INode *node,TimeValue t) { Matrix3 tm = node->GetNodeTM(t); return tm.GetTrans(); } #define DT 1 #define NUMSTEPS 5 Matrix3 FollowUtil::CalcRefFrame(TimeValue t,INode *node) { Interval valid; Matrix3 tm(1); TimeValue s = start, e = end; if (s==e) return tm; if (s>e) { int temp = e; e = s; s = temp; } float u = float(t-s)/float(e); int length = e-s; Point3 pt0, pt1; pt0 = GetPosition(node,t-DT); pt1 = GetPosition(node,t+DT); Point3 pathNorm(0,0,0); if (flip) { // Compute the normal to the plane of the path by sampling points on the path #define NUM_SAMPLES 20 Point3 v[NUM_SAMPLES], cent(0,0,0); for (int i=0; i 1.0f) u = 1.0f - (NUMSTEPS+2)*tracking; if (u<0.0f) u=0.0f; pt1 = GetPosition(node,int(u*length)+s); u += tracking; pt2 = GetPosition(node,int(u*length)+s); u += tracking; for (int i=0; i1.0f) { break; } else { pt2 = GetPosition(node,int(u*length)+s); } v0 = Normalize(pt2-pt1); v1 = Normalize(pt1-pt0); v0.z = v1.z = 0.0f; // remove Z component. cv += (v0^v1).z * bankAmount / Length(pt1-pt0); u += tracking; } if (i) tm.PreRotateX(cv/float(i)); } return tm; } void FollowUtil::DoFollow() { theHold.Begin(); SuspendAnimate(); AnimateOn(); for (int j=0; jGetSelNodeCount(); j++) { INode *node = ip->GetSelNode(j); Control *cont=node->GetTMController()->GetRotationController(); if (cont && cont->IsKeyable()) { cont->DeleteTime(Interval(start,end),TIME_INCLEFT|TIME_INCRIGHT|TIME_NOSLIDE); for (int i=0; i<=samples; i++) { TimeValue t = int((float(i)/float(samples))*(end-start)) + start; Matrix3 tm = CalcRefFrame(t,node); Quat q(tm); cont->SetValue(t,&q,1,CTRL_ABSOLUTE); } } } ResumeAnimate(); theHold.Accept(GetString(IDS_RB_APPLYFOLLOW)); ip->RedrawViews(ip->GetTime()); }