/********************************************************************** *< FILE: attach.cpp DESCRIPTION: Attachment controller CREATED BY: Rolf Berteig HISTORY: 11/18/96 *> Copyright (c) 1996, All Rights Reserved. **********************************************************************/ #include "ctrl.h" #include "units.h" #include "tcbgraph.h" #define ATTACH_CONTROL_CLASS_ID Class_ID(0xbb27e611,0xa72f43e7) #define ATTACH_CONTROL_CNAME GetString(IDS_RB_ATTACHCONTROL) static void ComputeHermiteBasis(float u, float *v); static float Ease(float u, float a, float b); class AKey { public: TimeValue time; DWORD flags; DWORD face; float u0, u1; float tens, cont, bias, easeIn, easeOut; Point3 pos, norm, din, dout; Quat quat, qa, qb; AKey() { flags=0; face=0; u0=u1=tens=cont=bias=easeIn=easeOut=0.0f; pos=din=dout=Point3(0,0,0); norm=Point3(1,0,0); quat=qa=qb=IdentQuat(); } AKey &operator=(AKey &k) { time = k.time; flags = k.flags; face = k.face; u0 = k.u0; u1 = k.u1; tens = k.tens; cont = k.cont; bias = k.bias; easeIn = k.easeIn; easeOut = k.easeOut; pos = k.pos; din = k.din; dout = k.dout; norm = k.norm; quat = k.quat; qa = k.qa; qb = k.qb; return *this; } }; #define KEY_SELECTED (1<<0) #define KEY_FLAGGED (1<<1) class AttachCtrl; class PickObjectMode : public PickModeCallback, public PickNodeCallback { public: AttachCtrl *cont; PickObjectMode(AttachCtrl *c) {cont=c;} BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags); BOOL Pick(IObjParam *ip,ViewExp *vpt); void EnterMode(IObjParam *ip); void ExitMode(IObjParam *ip); BOOL RightClick(IObjParam *ip,ViewExp *vpt) {return TRUE;} BOOL Filter(INode *node); PickNodeCallback *GetFilter() {return this;} }; #define CID_SETPOS 0x938ff27a class SetPosMouseProc : public MouseCallBack { public: AttachCtrl *cont; IObjParam *ip; SetPosMouseProc(AttachCtrl *c,IObjParam *i) {cont=c;ip=i;} int proc(HWND hWnd, int msg, int point, int flags, IPoint2 m); void SetPos(HWND hWnd,IPoint2 m); }; class SetPosCMode : public CommandMode { public: ChangeFGObject fgProc; SetPosMouseProc proc; IObjParam *ip; AttachCtrl *cont; SetPosCMode(AttachCtrl *c,IObjParam *i) : fgProc((ReferenceTarget*)c), proc(c,i) {ip=i;cont=c;} int Class() {return MOVE_COMMAND;} int ID() {return CID_SETPOS;} MouseCallBack *MouseProc(int *numPoints) {*numPoints=2;return &proc;} ChangeForegroundCallback *ChangeFGProc() {return &fgProc;} BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc() != &fgProc;} void EnterMode(); void ExitMode(); }; class AttachCtrl : public StdControl, public TimeChangeCallback { public: // Controller data Tab keys; INode *node; BOOL rangeLinked, align, manUpdate; // Current value cache Interval range, valid; Point3 val; Quat qval; BOOL trackValid, doManUpdate; static HWND hWnd; static IObjParam *ip; static AttachCtrl *editCont; static BOOL uiValid; static ISpinnerControl *iTime, *iFace, *iA, *iB, *iTens, *iCont, *iBias, *iEaseTo, *iEaseFrom; static ICustButton *iPickOb, *iSetPos, *iPrev, *iNext, *iUpdate; static ICustStatus *iStat; static int index; static PickObjectMode *pickObMode; static SetPosCMode *setPosMode; AttachCtrl(); // Animatable methods void DeleteThis() {delete this;} int IsKeyable() {return 1;} BOOL IsAnimated() {return keys.Count?TRUE:FALSE;} Class_ID ClassID() {return ATTACH_CONTROL_CLASS_ID;} SClass_ID SuperClassID() {return CTRL_POSITION_CLASS_ID;} void GetClassName(TSTR& s) {s = ATTACH_CONTROL_CNAME;} void BeginEditParams(IObjParam *ip, ULONG flags,Animatable *prev); void EndEditParams(IObjParam *ip, ULONG flags,Animatable *next); // Reference methods RefResult NotifyRefChanged(Interval, RefTargetHandle, PartID&, RefMessage); IOResult Save(ISave *isave); IOResult Load(ILoad *iload); RefTargetHandle Clone(RemapDir &remap = NoRemap()); int NumRefs() {return Control::NumRefs()+1;}; RefTargetHandle GetReference(int i); void SetReference(int i, RefTargetHandle rtarg); // Control methods void Copy(Control *from); BOOL IsLeaf() {return TRUE;} void CommitValue(TimeValue t) {} void RestoreValue(TimeValue t) {} // Animatable methods Interval GetTimeRange(DWORD flags); void EditTimeRange(Interval range,DWORD flags); void MapKeys(TimeMap *map,DWORD flags ); int NumKeys() {return keys.Count();} TimeValue GetKeyTime(int index) {return keys[index].time;} int GetKeyIndex(TimeValue t); void DeleteKeyAtTime(TimeValue t); BOOL IsKeyAtTime(TimeValue t,DWORD flags); int GetKeyTimes(Tab ×,Interval range,DWORD flags); int GetKeySelState(BitArray &sel,Interval range,DWORD flags); BOOL GetNextKeyTime(TimeValue t,DWORD flags,TimeValue &nt); void DeleteTime(Interval iv, DWORD flags); void ReverseTime(Interval iv, DWORD flags); void ScaleTime(Interval iv, float s); void InsertTime(TimeValue ins, TimeValue amount); BOOL SupportTimeOperations() {return TRUE;} void DeleteKeys(DWORD flags); void DeleteKeyByIndex(int index); void SelectKeys(TrackHitTab& sel, DWORD flags); void SelectKeyByIndex(int i,BOOL sel); void FlagKey(TrackHitRecord hit); int GetFlagKeyIndex(); int NumSelKeys(); void CloneSelectedKeys(BOOL offset=FALSE); void AddNewKey(TimeValue t,DWORD flags); BOOL IsKeySelected(int index); BOOL CanCopyTrack(Interval iv, DWORD flags) {return TRUE;} BOOL CanPasteTrack(TrackClipObject *cobj,Interval iv, DWORD flags) {return cobj->ClassID()==ClassID();} TrackClipObject *CopyTrack(Interval iv, DWORD flags); void PasteTrack(TrackClipObject *cobj,Interval iv, DWORD flags); int HitTestTrack( TrackHitTab& hits, Rect& rcHit, Rect& rcTrack, float zoom, int scroll, DWORD flags); int PaintTrack( ParamDimensionBase *dim, HDC hdc, Rect& rcTrack, Rect& rcPaint, float zoom, int scroll, DWORD flags); void EditTrackParams( TimeValue t, ParamDimensionBase *dim, TCHAR *pname, HWND hParent, IObjParam *ip, DWORD flags); int TrackParamsType() {return TRACKPARAMS_KEY;} // StdControl methods void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE) {} void GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE); void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {} void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type) {} void *CreateTempValue() {return new Point3;} void DeleteTempValue(void *val) {delete (Point3*)val;} void ApplyValue(void *val, void *delta) {((Matrix3*)val)->PreTranslate(*((Point3*)delta));} void MultiplyValue(void *val, float m) {*((Point3*)val) *= m;} // TimeChangedCallback void TimeChanged(TimeValue t) {InvalidateUI();} // Local methods void SortKeys(); void HoldTrack(); void Invalidate(); void PrepareTrack(TimeValue t); float GetInterpVal(TimeValue t,int &n0, int &n1); Point3 PointOnPath(TimeValue t); Quat QuatOnPath(TimeValue t); Interval CompValidity(TimeValue t); BOOL SetObject(INode *node); void SetKeyPos(TimeValue t, DWORD fi, Point3 bary); void CompFirstDeriv(); void CompLastDeriv(); void Comp2KeyDeriv(); void CompMiddleDeriv(int i); void CompAB(int i); void InvalidateUI(); void UpdateUI(); void UpdateTCBGraph(); void UpdateBaryGraph(); void SetupWindow(HWND hWnd); void DestroyWindow(); void SpinnerChange(int id); void Command(int id, LPARAM lParam); }; HWND AttachCtrl::hWnd = NULL; IObjParam *AttachCtrl::ip = NULL; AttachCtrl *AttachCtrl::editCont = NULL; BOOL AttachCtrl::uiValid = FALSE; ISpinnerControl *AttachCtrl::iTime = NULL; ISpinnerControl *AttachCtrl::iFace = NULL; ISpinnerControl *AttachCtrl::iA = NULL; ISpinnerControl *AttachCtrl::iB = NULL; ISpinnerControl *AttachCtrl::iTens = NULL; ISpinnerControl *AttachCtrl::iCont = NULL; ISpinnerControl *AttachCtrl::iBias = NULL; ISpinnerControl *AttachCtrl::iEaseTo = NULL; ISpinnerControl *AttachCtrl::iEaseFrom = NULL; ICustButton *AttachCtrl::iPickOb = NULL; ICustButton *AttachCtrl::iSetPos = NULL; ICustButton *AttachCtrl::iPrev = NULL; ICustButton *AttachCtrl::iNext = NULL; ICustButton *AttachCtrl::iUpdate = NULL; ICustStatus *AttachCtrl::iStat = NULL; int AttachCtrl::index = -1; PickObjectMode *AttachCtrl::pickObMode = NULL; SetPosCMode *AttachCtrl::setPosMode = NULL; class AttachClassDesc:public ClassDesc { public: int IsPublic() {return 1;} void * Create(BOOL loading) {return new AttachCtrl();} const TCHAR * ClassName() {return ATTACH_CONTROL_CNAME;} SClass_ID SuperClassID() {return CTRL_POSITION_CLASS_ID; } Class_ID ClassID() {return ATTACH_CONTROL_CLASS_ID;} const TCHAR* Category() {return _T("");} }; static AttachClassDesc attachCD; ClassDesc* GetAttachControlDesc() {return &attachCD;} class AttachCtrlRestore : public RestoreObj { public: Tab undo; Tab redo; Interval urange, rrange; AttachCtrl *cont; AttachCtrlRestore(AttachCtrl *c) { cont = c; undo = c->keys; urange = c->range; } void Restore(int isUndo) { if (isUndo) { redo = cont->keys; rrange = cont->range; } cont->keys = undo; cont->range = urange; cont->Invalidate(); cont->NotifyDependents(FOREVER,0,REFMSG_CHANGE); } void Redo() { cont->keys = redo; cont->range = rrange; cont->Invalidate(); cont->NotifyDependents(FOREVER,0,REFMSG_CHANGE); } int Size() {return 1;} void EndHold() {cont->ClearAFlag(A_HELD);} }; class AttachClipObject : public TrackClipObject { public: Tab tab; Class_ID ClassID() {return ATTACH_CONTROL_CLASS_ID;} SClass_ID SuperClassID() { return CTRL_FLOAT_CLASS_ID; } void DeleteThis() {delete this;} AttachClipObject(Interval iv) : TrackClipObject(iv) {} }; //----------------------------------------------------------------------------- AttachCtrl::AttachCtrl() { node = NULL; rangeLinked = TRUE; align = TRUE; trackValid = FALSE; manUpdate = FALSE; doManUpdate = FALSE; val = Point3(0,0,0); qval = IdentQuat(); } RefTargetHandle AttachCtrl::Clone(RemapDir &remap) { AttachCtrl *bc = new AttachCtrl; bc->range = range; bc->valid = valid; bc->keys = keys; bc->val = val; bc->node = NULL; bc->manUpdate = manUpdate; CloneControl(bc,remap); return bc; } #define KEYTIME_CHUNKID 0x0100 #define KEYFLAGS_CHUNKID 0x0110 #define NUMKEYS_CHUNKID 0x0130 #define KEYFACE_CHUNKID 0x0140 #define KEYU0_CHUNKID 0x0150 #define KEYU1_CHUNKID 0x0160 #define KEYTENS_CHUNKID 0x0170 #define KEYCONT_CHUNKID 0x0180 #define KEYBIAS_CHUNKID 0x0190 #define KEYEASEIN_CHUNKID 0x0200 #define KEYEASEOUT_CHUNKID 0x0210 #define ALIGN_CHUNKID 0x0220 #define MANUPDATE_CHUNKID 0x0230 IOResult AttachCtrl::Save(ISave *isave) { ULONG nb; int ct = keys.Count(); // Save align isave->BeginChunk(ALIGN_CHUNKID); isave->Write(&align,sizeof(align),&nb); isave->EndChunk(); // Manual update isave->BeginChunk(MANUPDATE_CHUNKID); isave->Write(&manUpdate,sizeof(manUpdate),&nb); isave->EndChunk(); // Save the number of keys isave->BeginChunk(NUMKEYS_CHUNKID); isave->Write(&ct,sizeof(ct),&nb); isave->EndChunk(); // Write each key for (int i=0; iBeginChunk(KEYTIME_CHUNKID); isave->Write(&keys[i].time,sizeof(keys[i].time),&nb); isave->EndChunk(); isave->BeginChunk(KEYFLAGS_CHUNKID); isave->Write(&keys[i].flags,sizeof(keys[i].flags),&nb); isave->EndChunk(); isave->BeginChunk(KEYFACE_CHUNKID); isave->Write(&keys[i].face,sizeof(keys[i].face),&nb); isave->EndChunk(); isave->BeginChunk(KEYU0_CHUNKID); isave->Write(&keys[i].u0,sizeof(keys[i].u0),&nb); isave->EndChunk(); isave->BeginChunk(KEYU1_CHUNKID); isave->Write(&keys[i].u1,sizeof(keys[i].u1),&nb); isave->EndChunk(); isave->BeginChunk(KEYTENS_CHUNKID); isave->Write(&keys[i].tens,sizeof(keys[i].tens),&nb); isave->EndChunk(); isave->BeginChunk(KEYCONT_CHUNKID); isave->Write(&keys[i].cont,sizeof(keys[i].cont),&nb); isave->EndChunk(); isave->BeginChunk(KEYBIAS_CHUNKID); isave->Write(&keys[i].bias,sizeof(keys[i].bias),&nb); isave->EndChunk(); isave->BeginChunk(KEYEASEIN_CHUNKID); isave->Write(&keys[i].easeIn,sizeof(keys[i].easeIn),&nb); isave->EndChunk(); isave->BeginChunk(KEYEASEOUT_CHUNKID); isave->Write(&keys[i].easeOut,sizeof(keys[i].easeOut),&nb); isave->EndChunk(); } return IO_OK; } IOResult AttachCtrl::Load(ILoad *iload) { ULONG nb; int ct; IOResult res; while (IO_OK==(res=iload->OpenChunk())) { switch (iload->CurChunkID()) { case ALIGN_CHUNKID: iload->Read(&align,sizeof(align),&nb); break; case MANUPDATE_CHUNKID: iload->Read(&manUpdate,sizeof(manUpdate),&nb); break; case NUMKEYS_CHUNKID: iload->Read(&ct,sizeof(ct),&nb); iload->CloseChunk(); keys.SetCount(ct); // Key data always follows for (int i=0; iOpenChunk(); assert(iload->CurChunkID()==KEYTIME_CHUNKID); iload->Read(&keys[i].time,sizeof(keys[i].time),&nb); iload->CloseChunk(); iload->OpenChunk(); assert(iload->CurChunkID()==KEYFLAGS_CHUNKID); iload->Read(&keys[i].flags,sizeof(keys[i].flags),&nb); iload->CloseChunk(); iload->OpenChunk(); assert(iload->CurChunkID()==KEYFACE_CHUNKID); iload->Read(&keys[i].face,sizeof(keys[i].face),&nb); iload->CloseChunk(); iload->OpenChunk(); assert(iload->CurChunkID()==KEYU0_CHUNKID); iload->Read(&keys[i].u0,sizeof(keys[i].u0),&nb); iload->CloseChunk(); iload->OpenChunk(); assert(iload->CurChunkID()==KEYU1_CHUNKID); iload->Read(&keys[i].u1,sizeof(keys[i].u1),&nb); iload->CloseChunk(); iload->OpenChunk(); assert(iload->CurChunkID()==KEYTENS_CHUNKID); iload->Read(&keys[i].tens,sizeof(keys[i].tens),&nb); iload->CloseChunk(); iload->OpenChunk(); assert(iload->CurChunkID()==KEYCONT_CHUNKID); iload->Read(&keys[i].cont,sizeof(keys[i].cont),&nb); iload->CloseChunk(); iload->OpenChunk(); assert(iload->CurChunkID()==KEYBIAS_CHUNKID); iload->Read(&keys[i].bias,sizeof(keys[i].bias),&nb); iload->CloseChunk(); iload->OpenChunk(); assert(iload->CurChunkID()==KEYEASEIN_CHUNKID); iload->Read(&keys[i].easeIn,sizeof(keys[i].easeIn),&nb); iload->CloseChunk(); iload->OpenChunk(); assert(iload->CurChunkID()==KEYEASEOUT_CHUNKID); iload->Read(&keys[i].easeOut,sizeof(keys[i].easeOut),&nb); iload->CloseChunk(); } continue; } iload->CloseChunk(); if (res!=IO_OK) return res; } return IO_OK; } void AttachCtrl::Copy(Control *from) { } void AttachCtrl::HoldTrack() { if (theHold.Holding()&&!TestAFlag(A_HELD)) { theHold.Put(new AttachCtrlRestore(this)); SetAFlag(A_HELD); } } void AttachCtrl::EditTimeRange(Interval range,DWORD flags) { HoldTrack(); if (flags&EDITRANGE_LINKTOKEYS && keys.Count()) { this->range.Set(keys[0].time,keys[keys.Count()-1].time); rangeLinked = TRUE; } else { rangeLinked = FALSE; this->range = range; } Invalidate(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } Interval AttachCtrl::GetTimeRange(DWORD flags) { if (rangeLinked && keys.Count()) return Interval(keys[0].time,keys[keys.Count()-1].time); else return range; } void AttachCtrl::MapKeys(TimeMap *map,DWORD flags) { int n = keys.Count(); BOOL changed = FALSE; if (!n) return; HoldTrack(); if (flags&TRACK_MAPRANGE) { TimeValue t0 = map->map(range.Start()); TimeValue t1 = map->map(range.End()); range.Set(t0,t1); changed = TRUE; } if (flags&TRACK_DOALL) { for (int i=0; imap(keys[i].time); changed = TRUE; } } else if (flags&TRACK_DOSEL) { BOOL slide = flags&TRACK_SLIDEUNSEL; TimeValue delta = 0, prev; int start, end, inc; if (flags&TRACK_RIGHTTOLEFT) { start = n-1; end = -1; inc = -1; } else { start = 0; end = n; inc = 1; } for (int i=start; i!=end; i+=inc) { if (keys[i].flags & KEY_SELECTED) { prev = keys[i].time; keys[i].time = map->map(keys[i].time); delta = keys[i].time - prev; changed = TRUE; } else if (slide) { keys[i].time += delta; } } } if (changed) { Invalidate(); SortKeys(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } } int AttachCtrl::GetKeyIndex(TimeValue t) { for (int i=0; it) return -1; } return -1; } void AttachCtrl::DeleteKeyAtTime(TimeValue t) { int index = GetKeyIndex(t); if (index>=0) { HoldTrack(); keys.Delete(index,1); Invalidate(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } } BOOL AttachCtrl::IsKeyAtTime(TimeValue t,DWORD flags) { for (int i=0; it) return FALSE; if (keys[i].time==t) return TRUE; } return FALSE; } int AttachCtrl::GetKeyTimes( Tab ×,Interval range,DWORD flags) { int skip = 0; for (int i=0; i= range.Start() && keys[i].time <= range.End()) { times.Append(1,&keys[i].time,10); } else { if (keys[i].time > range.End()) break; skip++; } } times.Shrink(); return skip; } int AttachCtrl::GetKeySelState( BitArray &sel,Interval range,DWORD flags) { int c = 0; int skip = 0; for (int i=0; i= range.Start() && keys[i].time <= range.End()) { sel.Set(c++,keys[i].flags&KEY_SELECTED); } else { if (keys[i].time > range.End()) break; skip++; } } return skip; } BOOL AttachCtrl::GetNextKeyTime( TimeValue t,DWORD flags,TimeValue &nt) { if (!keys.Count()) return FALSE; if (flags&NEXTKEY_RIGHT) { for (int i=0; i t ) { nt = keys[i].time; return TRUE; } } nt = keys[0].time; return TRUE; } else { for (int i=keys.Count()-1; i>=0; i--) { if (keys[i].time < t ) { nt = keys[i].time; return TRUE; } } nt = keys[keys.Count()-1].time; return TRUE; } } void AttachCtrl::DeleteTime(Interval iv, DWORD flags) { Interval test = TestInterval(iv,flags); int n = keys.Count(); int d = iv.Duration()-1; if (d<0) d = 0; HoldTrack(); for (int i = n-1; i >= 0; i--) { if (test.InInterval(keys[i].time)) { keys.Delete(i,1); } else if (!(flags&TIME_NOSLIDE)) { if (keys[i].time > test.End()) { keys[i].time -= d; } } } Invalidate(); SortKeys(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } void AttachCtrl::ReverseTime(Interval iv, DWORD flags) { Interval test = TestInterval(iv,flags); int n = keys.Count(); HoldTrack(); for (int i = 0; i < n; i++) { if (test.InInterval(keys[i].time)) { TimeValue delta = keys[i].time - iv.Start(); keys[i].time = iv.End()-delta; } } Invalidate(); SortKeys(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } void AttachCtrl::ScaleTime(Interval iv, float s) { int n = keys.Count(); TimeValue delta = int(s*float(iv.End()-iv.Start())) + iv.Start()-iv.End(); HoldTrack(); for (int i = 0; i < n; i++) { if (iv.InInterval(keys[i].time)) { keys[i].time = int(s*float(keys[i].time - iv.Start())) + iv.Start(); } else if (keys[i].time > iv.End()) { keys[i].time += delta; } } Invalidate(); SortKeys(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } void AttachCtrl::InsertTime(TimeValue ins, TimeValue amount) { int n = keys.Count(); HoldTrack(); for (int i = 0; i < n; i++) { if (keys[i].time >= ins) { keys[i].time += amount; } } Invalidate(); SortKeys(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } void AttachCtrl::DeleteKeys(DWORD flags) { int n = keys.Count(); HoldTrack(); for (int i = n-1; i >= 0; i--) { if (flags&TRACK_DOALL || keys[i].flags&KEY_SELECTED) { keys.Delete(i,1); } } Invalidate(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } void AttachCtrl::DeleteKeyByIndex(int index) { HoldTrack(); keys.Delete(index,1); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } void AttachCtrl::SelectKeyByIndex(int i,BOOL sel) { HoldTrack(); if (sel) keys[i].flags |= KEY_SELECTED; else keys[i].flags &= ~KEY_SELECTED; NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } void AttachCtrl::SelectKeys(TrackHitTab& sel, DWORD flags) { HoldTrack(); if (flags&SELKEYS_CLEARKEYS) { int n = keys.Count(); for (int i = 0; i < n; i++ ) { keys[i].flags &= ~KEY_SELECTED; } } if (flags&SELKEYS_DESELECT) { for (int i = 0; i < sel.Count(); i++ ) { keys[sel[i].hit].flags &= ~KEY_SELECTED; } } if (flags&SELKEYS_SELECT) { for (int i = 0; i < sel.Count(); i++ ) { keys[sel[i].hit].flags |= KEY_SELECTED; } } NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } void AttachCtrl::FlagKey(TrackHitRecord hit) { int n = keys.Count(); for (int i = 0; i < n; i++) { keys[i].flags &= ~KEY_FLAGGED; } assert(hit.hit>=0&&hit.hit<(DWORD)n); keys[hit.hit].flags |= KEY_FLAGGED; } int AttachCtrl::GetFlagKeyIndex() { int n = keys.Count(); for (int i = 0; i < n; i++) { if (keys[i].flags & KEY_FLAGGED) { return i; } } return -1; } int AttachCtrl::NumSelKeys() { int n = keys.Count(); int c = 0; for ( int i = 0; i < n; i++ ) { if (keys[i].flags & KEY_SELECTED) { c++; } } return c; } void AttachCtrl::CloneSelectedKeys(BOOL offset) { int n = keys.Count(); HoldTrack(); BOOL changed = FALSE; for (int i = 0; i < n; i++) { if (keys[i].flags & KEY_SELECTED) { AKey key(keys[i]); key.flags |= KEY_SELECTED; keys.Append(1,&key,5); keys[i].flags &= ~KEY_SELECTED; changed = TRUE; } } if (changed) { keys.Shrink(); Invalidate(); SortKeys(); } } void AttachCtrl::AddNewKey(TimeValue t,DWORD flags) { HoldTrack(); AKey key; key.time = t; key.face = 0; key.u0 = key.u1 = 1.0f/3.0f; key.tens = key.cont = key.bias = key.easeIn = key.easeOut = 0.0f; // Try to find a nearby key if (keys.Count()) { int n0, n1; GetInterpVal(t,n0,n1); key.face = keys[n0].face; key.u0 = keys[n0].u0; key.u1 = keys[n0].u1; } if (flags&ADDKEY_SELECT) { key.flags |= KEY_SELECTED; } if (flags&ADDKEY_FLAGGED) { int n = keys.Count(); for (int i = 0; i < n; i++) { keys[i].flags &= ~KEY_FLAGGED; } key.flags |= KEY_FLAGGED; } keys.Append(1,&key); Invalidate(); SortKeys(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } BOOL AttachCtrl::IsKeySelected(int index) { return keys[index].flags & KEY_SELECTED; } TrackClipObject *AttachCtrl::CopyTrack(Interval iv, DWORD flags) { AttachClipObject *cobj = new AttachClipObject(iv); Interval test = TestInterval(iv,flags); for (int i = 0; i < keys.Count(); i++) { if (test.InInterval(keys[i].time)) { AKey nk(keys[i]); cobj->tab.Append(1,&nk,10); } } cobj->tab.Shrink(); return cobj; } void AttachCtrl::PasteTrack(TrackClipObject *cobj,Interval iv, DWORD flags) { AttachClipObject *cob = (AttachClipObject*)cobj; HoldTrack(); DeleteTime(iv,flags); InsertTime(iv.Start(),cob->clip.Duration()-1); for (int i = 0; i < cob->tab.Count(); i++) { AKey key(cob->tab[i]); key.time -= cob->clip.Start() - iv.Start(); keys.Append(1,&key); } Invalidate(); SortKeys(); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } int AttachCtrl::HitTestTrack( TrackHitTab& hits, Rect& rcHit, Rect& rcTrack, float zoom, int scroll, DWORD flags) { int left = ScreenToTime(rcTrack.left,zoom,scroll) - 4; int right = ScreenToTime(rcTrack.right,zoom,scroll) + 4; int n = keys.Count(); int y = (rcTrack.top+rcTrack.bottom)/2; for ( int i = 0; i < n; i++ ) { if (flags&HITTRACK_SELONLY && !(keys[i].flags & KEY_SELECTED)) continue; if (flags&HITTRACK_UNSELONLY && (keys[i].flags & KEY_SELECTED)) continue; if (keys[i].time > right) { break; } if (keys[i].time > left) { int x = TimeToScreen(keys[i].time,zoom,scroll); if (rcHit.Contains(IPoint2(x,y))) { TrackHitRecord rec(i,0); hits.Append(1,&rec); if (flags&HITTRACK_ABORTONHIT) return TRACK_DONE; } } } return TRACK_DONE; } int AttachCtrl::PaintTrack( ParamDimensionBase *dim, HDC hdc, Rect& rcTrack, Rect& rcPaint, float zoom, int scroll, DWORD flags) { int left = ScreenToTime(rcPaint.left-8,zoom,scroll); int right = ScreenToTime(rcPaint.right+8,zoom,scroll); int n = keys.Count(); int y = (rcTrack.top+rcTrack.bottom)/2, x = rcPaint.left-2; HBRUSH selBrush = CreateSolidBrush(RGB(255,255,255)); HBRUSH unselBrush = (HBRUSH)GetStockObject(GRAY_BRUSH); SelectObject(hdc,GetStockObject(BLACK_PEN)); for (int i = 0; i < n; i++) { if (keys[i].time > right) { break; } if (keys[i].time > left) { x = TimeToScreen(keys[i].time,zoom,scroll); if ((flags&PAINTTRACK_SHOWSEL) && (keys[i].flags&KEY_SELECTED)) { SelectObject(hdc,selBrush); } else { SelectObject(hdc,unselBrush); } Ellipse(hdc,x-4,y-5,x+4,y+5); } } DeleteObject(selBrush); return TRACK_DONE; } static int __cdecl CompareAKeys(const AKey *k1, const AKey *k2) { if (k1->time < k2->time) return -1; if (k1->time > k2->time) return 1; return 0; } void AttachCtrl::SortKeys() { keys.Sort((CompareFnc)CompareAKeys); } RefResult AttachCtrl::NotifyRefChanged( Interval valid, RefTargetHandle targ, PartID &partID, RefMessage msg) { switch (msg) { case REFMSG_CHANGE: Invalidate(); break; case REFMSG_TARGET_DELETED: node = NULL; break; } return REF_SUCCEED; } RefTargetHandle AttachCtrl::GetReference(int i) { if (ikeys[keys.Count()-1].time) { v.Set(keys[keys.Count()-1].time,TIME_PosInfinity); } else { v.SetInstant(t); } } return v; } void AttachCtrl::GetValue( TimeValue t, void *val, Interval &valid, GetSetMethod method) { TimeValue oldTime = t; t = ApplyEase(t,valid); if (!this->valid.InInterval(t)) { this->valid = CompValidity(t); PrepareTrack(t); this->val = PointOnPath(t); if (align) { qval = QuatOnPath(t); } if (this->valid.Empty()) this->valid.Set(oldTime,oldTime); } if (method==CTRL_RELATIVE) { Matrix3 *tm = (Matrix3*)val; if (align) qval.MakeMatrix(*tm); tm->SetTrans(this->val); } else { *((Point3*)val) = this->val; } valid &= this->valid; } float AttachCtrl::GetInterpVal(TimeValue t,int &n0, int &n1) { float u=0.0f; for (int i=0; it) { if (i) { n0 = i-1; n1 = i; u = float(t-keys[i-1].time)/ float(keys[i].time-keys[i-1].time); u = Ease(u,keys[i-1].easeOut,keys[i].easeIn); return u; } else { n0 = n1 = 0; return u; } } } n0 = n1 = keys.Count()-1; return u; } Point3 AttachCtrl::PointOnPath(TimeValue t) { Point3 p(0,0,0); if (keys.Count()) { int n0, n1; float u = GetInterpVal(t,n0,n1); float v[4]; ComputeHermiteBasis(u, v); p = keys[n0].pos * v[0] + keys[n1].pos * v[1] + keys[n0].dout * v[2] + keys[n1].din * v[3]; } return p; } Quat AttachCtrl::QuatOnPath(TimeValue t) { Quat q = IdentQuat(); if (keys.Count()) { int n0, n1; float u = GetInterpVal(t,n0,n1); Quat qd = keys[n1].quat/keys[n0].quat; float ang; Point3 axis; AngAxisFromQ(qd, &ang, axis); q = squadrev( ang, axis, keys[n0].quat, keys[n0].qa, keys[n1].qb, keys[n1].quat, u); } return q; } void AttachCtrl::Invalidate() { valid.SetEmpty(); trackValid = FALSE; InvalidateUI(); } static void MakeAngPositive(float &ang, Point3 &axis) { if (ang < 0.0f) { ang = -ang; axis = -axis; } } static void MakeAngLessThan180(float &ang, Point3 &axis) { MakeAngPositive(ang,axis); while (ang > TWOPI) ang -= TWOPI; if (fabs(ang) > PI) ang -= TWOPI; MakeAngPositive(ang,axis); } static Quat ConstructOrientation(Point3 norm, Point3 dir) { Matrix3 tm(1); tm.SetRow(2,norm); tm.SetRow(1,Normalize(norm^dir)); tm.SetRow(0,tm.GetRow(1)^tm.GetRow(2)); Quat q(tm); float ang; Point3 axis; AngAxisFromQ(q, &ang, axis); MakeAngLessThan180(ang, axis); return QFromAngAxis(ang,axis); } void AttachCtrl::PrepareTrack(TimeValue t) { if (trackValid && keys.Count()!=1) return; trackValid = TRUE; if (!node) return; if (manUpdate && !doManUpdate) return; doManUpdate = FALSE; // First compute key positions and normals for (int i=0; iEvalWorldState( keys.Count()==1 ? t : k.time); TriObject *obj = NULL; BOOL needsDel = FALSE; // Convert it to a tri object if (os.obj->IsSubClassOf(triObjectClassID)) { obj = (TriObject*)os.obj; needsDel = FALSE; } else { if (os.obj->CanConvertToType(triObjectClassID)) { Object *oldObj = os.obj; obj = (TriObject*) os.obj->ConvertToType( keys.Count()==1 ? t : k.time,triObjectClassID); needsDel = (obj != oldObj); } } // Get the position on the face if (obj) { Interval iv = FOREVER; Matrix3 tm = node->GetObjTMAfterWSM( keys.Count()==1 ? t : k.time, &iv); if (keys.Count()==1) { // Special case for only one key valid &= iv; valid &= obj->ChannelValidity(t,GEOM_CHAN_NUM); valid &= obj->ChannelValidity(t,TOPO_CHAN_NUM); } Mesh &m = obj->mesh; if (m.getNumFaces()) { DWORD f = k.face % m.getNumFaces(); k.pos = m.verts[m.faces[f].v[0]] * k.u0 + m.verts[m.faces[f].v[1]] * k.u1 + m.verts[m.faces[f].v[2]] * (1.0f-k.u0-k.u1); k.pos = k.pos * tm; k.norm = Normalize( (m.verts[m.faces[f].v[1]]-m.verts[m.faces[f].v[0]])^ (m.verts[m.faces[f].v[2]]-m.verts[m.faces[f].v[1]])); k.norm = VectorTransform(tm,k.norm); } else { k.pos = Point3(0,0,0) * tm; k.norm = Point3(0,0,1); } if (needsDel) obj->DeleteThis(); } } // Next compute position derivatives switch (keys.Count()) { case 0: break; case 1: keys[0].din = keys[0].dout = Point3(0,0,0); break; case 2: Comp2KeyDeriv(); break; default: for (i=1; i 1.0f) { a = a/s; b = b/s; } k = 1/(2.0f - a - b); if (u < a) return ((k/a)*u*u); else if (u < 1.0f - b) return (k*(2.0f*u - a)); else { u = 1.0f - u; return (1.0f - (k/b) *u*u); } } static void ComputeHermiteBasis(float u, float *v) { float u2,u3,a; u2 = u*u; u3 = u2*u; a = 2.0f*u3 - 3.0f*u2; v[0] = 1.0f + a; v[1] = -a; v[2] = u - 2.0f*u2 + u3; v[3] = -u2 + u3; } static float qdot(Quat p, Quat q) { return (q[0]*p[0] + q[1]*p[1] + q[2]*p[2] + q[3]*p[3]); } void AttachCtrl::CompAB(int i) { int n = keys.Count(); Quat qprev, qnext, q; Quat qp, qm, qa, qb, qae, qbe; float tm,cm,cp,bm,bp,tmcm,tmcp,ksm,ksp,kdm,kdp,c; float dt,fp,fn; if (i!=0) { if (qdot(keys[i-1].quat, keys[i].quat) < 0.0f) qprev = -keys[i-1].quat; else qprev = keys[i-1].quat; qm = LnDif(qprev,keys[i].quat); } if (i0 && iTestForLoop(FOREVER,this)==REF_SUCCEED) { ReplaceReference(Control::NumRefs(),(RefTargetHandle)node); NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); Invalidate(); return TRUE; } else { return FALSE; } } void AttachCtrl::SetKeyPos(TimeValue t, DWORD fi, Point3 bary) { if (manUpdate) doManUpdate = TRUE; int index = GetKeyIndex(t); if (index<0) { AddNewKey(t,0); index = GetKeyIndex(t); } if (index>=0) { keys[index].face = fi; keys[index].u0 = bary.x; keys[index].u1 = bary.y; Invalidate(); NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); } } //--- UI ------------------------------------------------------------- #define WM_GETBARY (WM_USER+0xa38c) #define WM_SETBARY (WM_USER+0xa38d) #define BARYGRAPH_UP 1 #define BARYGRAPH_DOWN 2 #define BARYGRAPH_MOVE 3 static LRESULT CALLBACK BaryGraphWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); static BOOL CALLBACK AttachParamDialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); #define ToTCBUI(a) (((a)+1.0f)*25.0f) #define FromTCBUI(a) (((a)/25.0f)-1.0f) #define ToEaseUI(a) ((a)*50.0f) #define FromEaseUI(a) ((a)/50.0f) void RegisterClasses() { static BOOL registered=FALSE; if (!registered) { InitTCBGraph(hInstance); WNDCLASS wc; wc.style = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.lpfnWndProc = BaryGraphWindowProc; wc.lpszClassName = _T("FacePosGraph"); RegisterClass(&wc); registered = TRUE; } } void AttachCtrl::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev) { RegisterClasses(); this->ip = ip; editCont = this; uiValid = FALSE; hWnd = ip->AddRollupPage( hInstance, MAKEINTRESOURCE(IDD_ATTACH_PARAMS), AttachParamDialogProc, GetString(IDS_RB_ATTACHPARAMS), (LPARAM)this ); ip->RegisterTimeChangeCallback(this); pickObMode = new PickObjectMode(this); setPosMode = new SetPosCMode(this,ip); } void AttachCtrl::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next) { ip->ClearPickMode(); ip->DeleteMode(setPosMode); ip->UnRegisterTimeChangeCallback(this); if (hWnd) ip->DeleteRollupPage(hWnd); hWnd = NULL; ip = NULL; editCont = NULL; delete pickObMode; pickObMode = NULL; delete setPosMode; setPosMode = NULL; } void AttachCtrl::SpinnerChange(int id) { HoldTrack(); assert(index>=0); switch (id) { case IDC_ATTACH_KEYTIMESPIN: keys[index].time = iTime->GetIVal(); ip->SetTime(keys[index].time); break; case IDC_ATTACH_KEYFACESPIN: keys[index].face = iFace->GetIVal(); break; case IDC_ATTACH_KEYASPIN: keys[index].u0 = iA->GetFVal(); UpdateBaryGraph(); break; case IDC_ATTACH_KEYBSPIN: keys[index].u1 = iB->GetFVal(); UpdateBaryGraph(); break; case IDC_ATTACH_KEYTENSSPIN: keys[index].tens = FromTCBUI(iTens->GetFVal()); UpdateTCBGraph(); break; case IDC_ATTACH_KEYCONTSPIN: keys[index].cont = FromTCBUI(iCont->GetFVal()); UpdateTCBGraph(); break; case IDC_ATTACH_KEYBIASSPIN: keys[index].bias = FromTCBUI(iBias->GetFVal()); UpdateTCBGraph(); break; case IDC_ATTACH_KEYEASETOSPIN: keys[index].easeIn = FromEaseUI(iEaseTo->GetFVal()); UpdateTCBGraph(); break; case IDC_ATTACH_KEYEASEFROMSPIN: keys[index].easeOut = FromEaseUI(iEaseFrom->GetFVal()); UpdateTCBGraph(); break; } Invalidate(); NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); ip->RedrawViews(ip->GetTime()); } void AttachCtrl::Command(int id, LPARAM lParam) { switch (id) { case IDC_ATTACH_PICKOB: ip->SetPickMode(pickObMode); break; case IDC_PREVKEY: if (index==0) index = keys.Count()-1; else index--; ip->SetTime(keys[index].time); break; case IDC_NEXTKEY: if (index==keys.Count()-1) index = 0; else index++; ip->SetTime(keys[index].time); break; case IDC_ATTACH_SETPOS: if (ip->GetCommandMode()==setPosMode) { ip->SetStdCommandMode(CID_OBJMOVE); } else { ip->SetCommandMode(setPosMode); } break; case IDC_ATTACH_ALIGN: align = IsDlgButtonChecked(hWnd,id); valid.SetEmpty(); NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); ip->RedrawViews(ip->GetTime()); break; case IDC_ATTACH_UPDATE: doManUpdate = TRUE; Invalidate(); NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); ip->RedrawViews(ip->GetTime()); break; case IDC_ATTACH_MANUPDATE: manUpdate = IsDlgButtonChecked(hWnd,IDC_ATTACH_MANUPDATE); if (manUpdate) iUpdate->Enable(); else iUpdate->Disable(); break; case IDC_ATTACH_FACEPOSGRAPH: switch (LOWORD(lParam)) { case BARYGRAPH_DOWN: theHold.Begin(); break; case BARYGRAPH_UP: if (HIWORD(lParam)) theHold.Accept(GetString(IDS_RB_EDITATTACHKEY)); else theHold.Cancel(); break; case BARYGRAPH_MOVE: { HoldTrack(); Point2 bary; SendDlgItemMessage(hWnd,IDC_ATTACH_FACEPOSGRAPH, WM_GETBARY, 0, (LPARAM)&bary); keys[index].u0 = bary.x; keys[index].u1 = bary.y; Invalidate(); NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); UpdateBaryGraph(); ip->RedrawViews(ip->GetTime()); break; } } break; } } void AttachCtrl::InvalidateUI() { if (editCont==this) { InvalidateRect(hWnd,NULL,FALSE); uiValid = FALSE; } } void AttachCtrl::UpdateTCBGraph() { if (index>=0 && hWnd) { TCBGraphParams tp; tp.tens = keys[index].tens; tp.cont = keys[index].cont; tp.bias = keys[index].bias; tp.easeTo = keys[index].easeIn; tp.easeFrom = keys[index].easeOut; SendDlgItemMessage(hWnd,IDC_TCB_GRAPH,WM_SETTCBGRAPHPARAMS,0,(LPARAM)&tp); UpdateWindow(GetDlgItem(hWnd,IDC_TCB_GRAPH)); } } void AttachCtrl::UpdateBaryGraph() { if (index>=0 && hWnd) { Point2 pt(keys[index].u0,keys[index].u1); SendDlgItemMessage(hWnd,IDC_ATTACH_FACEPOSGRAPH,WM_SETBARY,0,(LONG)&pt); UpdateWindow(GetDlgItem(hWnd,IDC_ATTACH_FACEPOSGRAPH)); } } void AttachCtrl::UpdateUI() { if (!uiValid) { uiValid = TRUE; index = GetKeyIndex(ip->GetTime()); if (index<0) { iTime->Disable(); iFace->Disable(); iA->Disable(); iB->Disable(); iTens->Disable(); iCont->Disable(); iBias->Disable(); iEaseFrom->Disable(); iEaseTo->Disable(); iStat->SetText(_T("")); iPrev->Disable(); iNext->Disable(); EnableWindow(GetDlgItem(hWnd,IDC_TCB_GRAPH),FALSE); EnableWindow(GetDlgItem(hWnd,IDC_ATTACH_FACEPOSGRAPH),FALSE); } else { iTime->Enable(); iFace->Enable(); iA->Enable(); iB->Enable(); iTens->Enable(); iCont->Enable(); iBias->Enable(); iEaseFrom->Enable(); iEaseTo->Enable(); iTime->SetLimits(GetAnimStart(),GetAnimEnd(),FALSE); iPrev->Enable(); iNext->Enable(); TSTR buf; buf.printf(_T("%d"),index+1); iStat->SetText(buf); iTime->SetValue(keys[index].time,FALSE); iFace->SetValue((int)keys[index].face,FALSE); iA->SetValue(keys[index].u0,FALSE); iB->SetValue(keys[index].u1,FALSE); iTens->SetValue(ToTCBUI(keys[index].tens),FALSE); iCont->SetValue(ToTCBUI(keys[index].cont),FALSE); iBias->SetValue(ToTCBUI(keys[index].bias),FALSE); iEaseTo->SetValue(ToEaseUI(keys[index].easeIn),FALSE); iEaseFrom->SetValue(ToEaseUI(keys[index].easeOut),FALSE); UpdateTCBGraph(); EnableWindow(GetDlgItem(hWnd,IDC_TCB_GRAPH),TRUE); UpdateBaryGraph(); EnableWindow(GetDlgItem(hWnd,IDC_ATTACH_FACEPOSGRAPH),TRUE); } CheckDlgButton(hWnd,IDC_ATTACH_ALIGN,align); if (node) { SetDlgItemText(hWnd,IDC_ATTACH_OBNAME,node->GetName()); } else { SetDlgItemText(hWnd,IDC_ATTACH_OBNAME,GetString(IDS_RB_NONE)); } CheckDlgButton(hWnd,IDC_ATTACH_MANUPDATE,manUpdate); if (manUpdate) iUpdate->Enable(); else iUpdate->Disable(); } } void AttachCtrl::SetupWindow(HWND hWnd) { this->hWnd = hWnd; iTime = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYTIMESPIN)); iTime->SetLimits(GetAnimStart(),GetAnimEnd(),FALSE); iTime->SetScale(10.0f); iTime->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYTIME),EDITTYPE_TIME); iFace = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYFACESPIN)); iFace->SetLimits(0,0xfffffff,FALSE); iFace->SetScale(0.1f); iFace->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYFACE),EDITTYPE_INT); iA = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYASPIN)); iA->SetLimits(-999999999.0f,999999999.0f,FALSE); iA->SetScale(0.005f); iA->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYA),EDITTYPE_FLOAT); iB = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYBSPIN)); iB->SetLimits(-999999999.0f,999999999.0f,FALSE); iB->SetScale(0.005f); iB->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYB),EDITTYPE_FLOAT); iTens = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYTENSSPIN)); iTens->SetLimits(0.0f,50.0f,FALSE); iTens->SetScale(0.1f); iTens->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYTENS),EDITTYPE_FLOAT); iCont = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYCONTSPIN)); iCont->SetLimits(0.0f,50.0f,FALSE); iCont->SetScale(0.1f); iCont->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYCONT),EDITTYPE_FLOAT); iBias = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYBIASSPIN)); iBias->SetLimits(0.0f,50.0f,FALSE); iBias->SetScale(0.1f); iBias->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYBIAS),EDITTYPE_FLOAT); iEaseTo = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYEASETOSPIN)); iEaseTo->SetLimits(0.0f,50.0f,FALSE); iEaseTo->SetScale(0.1f); iEaseTo->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYEASETO),EDITTYPE_FLOAT); iEaseFrom = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYEASEFROMSPIN)); iEaseFrom->SetLimits(0.0f,50.0f,FALSE); iEaseFrom->SetScale(0.1f); iEaseFrom->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYEASEFROM),EDITTYPE_FLOAT); iPickOb = GetICustButton(GetDlgItem(hWnd,IDC_ATTACH_PICKOB)); iPickOb->SetType(CBT_CHECK); iPickOb->SetHighlightColor(GREEN_WASH); iSetPos = GetICustButton(GetDlgItem(hWnd,IDC_ATTACH_SETPOS)); iSetPos->SetType(CBT_CHECK); iSetPos->SetHighlightColor(GREEN_WASH); iPrev = GetICustButton(GetDlgItem(hWnd,IDC_PREVKEY)); iNext = GetICustButton(GetDlgItem(hWnd,IDC_NEXTKEY)); iUpdate = GetICustButton(GetDlgItem(hWnd,IDC_ATTACH_UPDATE)); iStat = GetICustStatus(GetDlgItem(hWnd,IDC_KEYNUM)); iStat->SetTextFormat(STATUSTEXT_CENTERED); } void AttachCtrl::DestroyWindow() { ReleaseICustStatus(iStat); ReleaseISpinner(iTime); ReleaseISpinner(iFace); ReleaseISpinner(iA); ReleaseISpinner(iB); ReleaseISpinner(iTens); ReleaseISpinner(iCont); ReleaseISpinner(iBias); ReleaseICustButton(iPickOb); ReleaseICustButton(iSetPos); ReleaseICustButton(iPrev); ReleaseICustButton(iNext); ReleaseICustButton(iUpdate); ReleaseISpinner(iEaseTo); ReleaseISpinner(iEaseFrom); iEaseTo = iEaseFrom = iTime = iFace = iA = iB = iTens = iCont = iBias = NULL; iStat = NULL; iPickOb = iSetPos = iPrev = iNext = iUpdate = NULL; } static BOOL CALLBACK AttachParamDialogProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { AttachCtrl *ctrl = (AttachCtrl*)GetWindowLong(hWnd,GWL_USERDATA); if (!ctrl && msg != WM_INITDIALOG) return FALSE; switch (msg) { case WM_INITDIALOG: ctrl = (AttachCtrl*)lParam; SetWindowLong(hWnd, GWL_USERDATA, lParam); ctrl->SetupWindow(hWnd); break; case WM_DESTROY: ctrl->DestroyWindow(); break; case WM_PAINT: ctrl->UpdateUI(); return FALSE; case WM_LBUTTONDOWN:case WM_LBUTTONUP: case WM_MOUSEMOVE: ctrl->ip->RollupMouseMessage(hWnd,msg,wParam,lParam); return FALSE; case CC_SPINNER_BUTTONDOWN: theHold.Begin(); break; case WM_CUSTEDIT_ENTER: case CC_SPINNER_BUTTONUP: if (HIWORD(wParam)) { theHold.Accept(GetString(IDS_RB_EDITATTACHKEY)); } else { theHold.Cancel(); } break; case CC_SPINNER_CHANGE: ctrl->SpinnerChange(LOWORD(wParam)); break; case WM_COMMAND: ctrl->Command(LOWORD(wParam),lParam); break; default: return FALSE; } return TRUE; } //--- PickObjectMode ------------------------------------------------ BOOL PickObjectMode::Filter(INode *node) { if (node) { ObjectState os = node->EvalWorldState(0); if (os.obj->CanConvertToType(triObjectClassID)) { return TRUE; } } return FALSE; } BOOL PickObjectMode::HitTest( IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags) { INode *node = cont->ip->PickNode(hWnd,m); return node?TRUE:FALSE; } BOOL PickObjectMode::Pick(IObjParam *ip,ViewExp *vpt) { INode *node = vpt->GetClosestHit(); if (node) { ObjectState os = node->EvalWorldState(0); if (os.obj->CanConvertToType(triObjectClassID)) { if (cont->SetObject(node)) { cont->ip->RedrawViews(cont->ip->GetTime()); } else { TSTR buf1 = GetString(IDS_RB_ATTACHCONTROLLER); TSTR buf2 = GetString(IDS_RB_ILLEGALOBJECT); MessageBox(ip->GetMAXHWnd(),buf2,buf1,MB_OK|MB_ICONEXCLAMATION); } } } return TRUE; } void PickObjectMode::EnterMode(IObjParam *ip) {cont->iPickOb->SetCheck(TRUE);} void PickObjectMode::ExitMode(IObjParam *ip) {cont->iPickOb->SetCheck(FALSE);} //--- SetPosMode ------------------------------------------------ void SetPosMouseProc::SetPos(HWND hWnd,IPoint2 m) { if (!cont->node) return; ViewExp *vpt = ip->GetViewport(hWnd); if (!vpt) return; Ray ray, wray; float at; TimeValue t = ip->GetTime(); Point3 norm, pt, bary; DWORD fi; Interval valid; // Calculate a ray from the mouse point vpt->MapScreenToWorldRay(float(m.x), float(m.y),wray); // Back transform the ray into object space. Matrix3 obtm = cont->node->GetObjTMAfterWSM(t); Matrix3 iobtm = Inverse(obtm); ray.p = iobtm * wray.p; ray.dir = VectorTransform(iobtm, wray.dir); // Evaluate the node ObjectState os = cont->node->EvalWorldState(t); TriObject *obj = NULL; BOOL needsDel = FALSE; // Convert it to a tri object if (os.obj->IsSubClassOf(triObjectClassID)) { obj = (TriObject*)os.obj; needsDel = FALSE; } else { if (os.obj->CanConvertToType(triObjectClassID)) { Object *oldObj = os.obj; obj = (TriObject*) os.obj->ConvertToType(t,triObjectClassID); needsDel = (obj != oldObj); } } if (!obj) return; if (obj->mesh.IntersectRay(ray, at, norm, fi, bary)) { cont->HoldTrack(); cont->SetKeyPos(t, fi, bary); } if (needsDel) obj->DeleteThis(); ip->ReleaseViewport(vpt); } int SetPosMouseProc::proc( HWND hWnd, int msg, int point, int flags, IPoint2 m) { static BOOL animated = FALSE; switch (msg) { case MOUSE_POINT: if (point==0) { animated = FALSE; if (!Animating() && cont->keys.Count()==1) { int index = cont->GetKeyIndex(ip->GetTime()); if (index<0) { animated = TRUE; } } theHold.Begin(); ip->RedrawViews(ip->GetTime(),REDRAW_BEGIN); } else { if (animated) { int res = MessageBox(hWnd, _T("Are you sure you want to animate the position of this object?"), _T("Attachment Controller"), MB_ICONQUESTION|MB_YESNO); if (res!=IDYES) { theHold.Cancel(); ip->RedrawViews(ip->GetTime(),REDRAW_END); return FALSE; } } theHold.Accept(GetString(IDS_RB_EDITATTACHKEY)); ip->RedrawViews(ip->GetTime(),REDRAW_END); } break; case MOUSE_MOVE: { theHold.Restore(); SetPos(hWnd,m); ip->RedrawViews(ip->GetTime(),REDRAW_INTERACTIVE); break; } case MOUSE_ABORT: theHold.Cancel(); ip->RedrawViews(ip->GetTime(),REDRAW_END); break; case MOUSE_FREEMOVE: SetCursor(ip->GetSysCursor(SYSCUR_SELECT)); break; } return TRUE; } void SetPosCMode::EnterMode() { if (cont->iSetPos) cont->iSetPos->SetCheck(TRUE); } void SetPosCMode::ExitMode() { if (cont->iSetPos) cont->iSetPos->SetCheck(FALSE); } //--- Barycentric graph ---------------------------------------------- static LRESULT CALLBACK BaryGraphWindowProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { Point2 *bary = (Point2*)GetWindowLong(hWnd,GWL_USERDATA); static int mx, my; static Point2 oldBary; static BOOL mDown = FALSE; switch (msg) { case WM_CREATE: SetWindowLong(hWnd,GWL_USERDATA,(LONG)new Point2(0.33f,0.33f)); break; case WM_SETBARY: { Point2 *nb = (Point2*)lParam; *bary = *nb; InvalidateRect(hWnd,NULL,TRUE); break; } case WM_GETBARY: { Point2 *nb = (Point2*)lParam; *nb = *bary; break; } case WM_ENABLE: InvalidateRect(hWnd,NULL,TRUE); break; case WM_LBUTTONDOWN: mDown = TRUE; mx = LOWORD(lParam); my = HIWORD(lParam); oldBary = *bary; SetCapture(hWnd); SendMessage(GetParent(hWnd),WM_COMMAND,IDC_ATTACH_FACEPOSGRAPH, MAKELPARAM(BARYGRAPH_DOWN,0)); // Fallthrough case WM_MOUSEMOVE: if (mDown) { Rect rect; GetClientRect(hWnd,&rect); int x = (short)LOWORD(lParam) - 5; int y = (short)HIWORD(lParam) - 5; float u = float(x)/float(rect.w()-11); float v = 1.0f-float(y)/float(rect.h()-11); bary->x = v; bary->y = 1.0f-v-u; SendMessage(GetParent(hWnd),WM_COMMAND,IDC_ATTACH_FACEPOSGRAPH, MAKELPARAM(BARYGRAPH_MOVE,0)); } break; case WM_LBUTTONUP: mDown = FALSE; ReleaseCapture(); SendMessage(GetParent(hWnd),WM_COMMAND,IDC_ATTACH_FACEPOSGRAPH, MAKELPARAM(BARYGRAPH_UP,TRUE)); break; case WM_RBUTTONDOWN: mDown = FALSE; *bary = oldBary; ReleaseCapture(); SendMessage(GetParent(hWnd),WM_COMMAND,IDC_ATTACH_FACEPOSGRAPH, MAKELPARAM(BARYGRAPH_UP,FALSE)); break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd,&ps); Rect rect, orect; GetClientRect(hWnd,&rect); orect = rect; if (IsWindowEnabled(hWnd)) { rect.left += 5; rect.right -= 5; rect.top += 5; rect.bottom -= 5; IPoint2 pt0(rect.left,rect.top); IPoint2 pt1(rect.left,rect.bottom); IPoint2 pt2(rect.right,rect.bottom); SelectObject(hdc,GetStockObject(BLACK_PEN)); MoveToEx(hdc,pt0.x, pt0.y,NULL); LineTo(hdc,pt1.x, pt1.y); LineTo(hdc,pt2.x, pt2.y); LineTo(hdc,pt0.x, pt0.y); IPoint2 pt( int(pt0.x*bary->x + pt1.x*bary->y + pt2.x*(1.0f-bary->x-bary->y)), int(pt0.y*bary->x + pt1.y*bary->y + pt2.y*(1.0f-bary->x-bary->y))); SelectObject(hdc,CreatePen(PS_SOLID,0,RGB(255,0,0))); MoveToEx(hdc,pt.x, pt.y-3,NULL); LineTo(hdc,pt.x, pt.y+4); MoveToEx(hdc,pt.x-3, pt.y,NULL); LineTo(hdc,pt.x+4, pt.y); DeleteObject(SelectObject(hdc,GetStockObject(BLACK_PEN))); } WhiteRect3D(hdc,orect,TRUE); EndPaint(hWnd,&ps); break; } case WM_DESTROY: delete bary; break; default: return DefWindowProc(hWnd,msg,wParam,lParam); } return 0; } //--- Key Dialog ---------------------------------------------------- static BOOL CALLBACK AttachDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); #define ATTACHDLG_CLASS_ID Class_ID(0x8736fff2,0xaab37f23) class AttachKeyDlg : public ReferenceMaker { public: HWND hWnd; AttachCtrl *cont; IObjParam *ip; BOOL valid; ISpinnerControl *iTime, *iFace, *iA, *iB, *iTens, *iCont, *iBias, *iEaseTo, *iEaseFrom; ICustButton *iPrev, *iNext; ICustStatus *iStat; AttachKeyDlg(AttachCtrl *c,TCHAR *pname,IObjParam *ip,HWND hParent); ~AttachKeyDlg(); Class_ID ClassID() {return ATTACHDLG_CLASS_ID;} SClass_ID SuperClassID() {return REF_MAKER_CLASS_ID;} RefResult NotifyRefChanged(Interval iv, RefTargetHandle hTarg,PartID& partID,RefMessage msg); int NumRefs() {return 1;} RefTargetHandle GetReference(int i) {return cont;} void SetReference(int i, RefTargetHandle rtarg) {cont=(AttachCtrl*)rtarg;} void MaybeCloseWindow(); void Invalidate(); void Update(); void UpdateBaryGraph(float a, float b); void UpdateTCBGraph(float t, float c, float b, float easeIn, float easeOut); void SetupUI(HWND hWnd); void Command(int id, LPARAM lParam); void SpinnerChange(int id); }; void AttachCtrl::EditTrackParams( TimeValue t, ParamDimensionBase *dim, TCHAR *pname, HWND hParent, IObjParam *ip, DWORD flags) { RegisterClasses(); new AttachKeyDlg(this,pname,ip,hParent); } AttachKeyDlg::AttachKeyDlg( AttachCtrl *c,TCHAR *pname,IObjParam *ip,HWND hParent) { valid = FALSE; cont = NULL; hWnd = NULL; this->ip = ip; MakeRefByID(FOREVER,0,c); hWnd = CreateDialogParam( hInstance, MAKEINTRESOURCE(IDD_ATTACH_DIALOG), hParent, AttachDlgProc, (LPARAM)this); TSTR title = TSTR(GetString(IDS_RB_ATTACHTITLE)) + TSTR(pname); SetWindowText(hWnd,title); } AttachKeyDlg::~AttachKeyDlg() { DeleteAllRefsFromMe(); ReleaseICustStatus(iStat); ReleaseISpinner(iTime); ReleaseISpinner(iFace); ReleaseISpinner(iA); ReleaseISpinner(iB); ReleaseISpinner(iTens); ReleaseISpinner(iCont); ReleaseISpinner(iBias); ReleaseICustButton(iPrev); ReleaseICustButton(iNext); ReleaseISpinner(iEaseTo); ReleaseISpinner(iEaseFrom); } class CheckForNonAttachDlg : public DependentEnumProc { public: BOOL non; ReferenceMaker *me; CheckForNonAttachDlg(ReferenceMaker *m) {non = FALSE;me = m;} int proc(ReferenceMaker *rmaker) { if (rmaker==me) return 0; if (rmaker->SuperClassID()!=REF_MAKER_CLASS_ID && rmaker->ClassID()!=ATTACHDLG_CLASS_ID) { non = TRUE; return 1; } return 0; } }; void AttachKeyDlg::MaybeCloseWindow() { CheckForNonAttachDlg check(cont); cont->EnumDependents(&check); if (!check.non) { PostMessage(hWnd,WM_CLOSE,0,0); } } void AttachKeyDlg::SpinnerChange(int id) { cont->HoldTrack(); for (int index=0; indexkeys.Count(); index++) { if (!(cont->keys[index].flags&KEY_SELECTED)) continue; switch (id) { case IDC_ATTACH_KEYTIMESPIN: cont->keys[index].time = iTime->GetIVal(); break; case IDC_ATTACH_KEYFACESPIN: cont->keys[index].face = iFace->GetIVal(); break; case IDC_ATTACH_KEYASPIN: cont->keys[index].u0 = iA->GetFVal(); break; case IDC_ATTACH_KEYBSPIN: cont->keys[index].u1 = iB->GetFVal(); break; case IDC_ATTACH_KEYTENSSPIN: cont->keys[index].tens = FromTCBUI(iTens->GetFVal()); break; case IDC_ATTACH_KEYCONTSPIN: cont->keys[index].cont = FromTCBUI(iCont->GetFVal()); break; case IDC_ATTACH_KEYBIASSPIN: cont->keys[index].bias = FromTCBUI(iBias->GetFVal()); break; case IDC_ATTACH_KEYEASETOSPIN: cont->keys[index].easeIn = FromEaseUI(iEaseTo->GetFVal()); break; case IDC_ATTACH_KEYEASEFROMSPIN: cont->keys[index].easeOut = FromEaseUI(iEaseFrom->GetFVal()); break; } } cont->Invalidate(); cont->NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); ip->RedrawViews(ip->GetTime()); UpdateWindow(hWnd); } void AttachKeyDlg::Command(int id, LPARAM lParam) { switch (id) { case IDC_PREVKEY: { int n=0; for (int i=0; ikeys.Count(); i++) { if (cont->keys[i].flags&KEY_SELECTED) { if (i) n = i-1; else n = cont->keys.Count()-1; } } for (i=0; ikeys.Count(); i++) { if (i==n) cont->keys[i].flags |= KEY_SELECTED; else cont->keys[i].flags &= ~KEY_SELECTED; } cont->NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); break; } case IDC_NEXTKEY: { int n=0; for (int i=0; ikeys.Count(); i++) { if (cont->keys[i].flags&KEY_SELECTED) { if (ikeys.Count()-1) n = i+1; else n = 0; } } for (i=0; ikeys.Count(); i++) { if (i==n) cont->keys[i].flags |= KEY_SELECTED; else cont->keys[i].flags &= ~KEY_SELECTED; } cont->NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); break; } case IDC_ATTACH_FACEPOSGRAPH: switch (LOWORD(lParam)) { case BARYGRAPH_DOWN: theHold.Begin(); break; case BARYGRAPH_UP: if (HIWORD(lParam)) theHold.Accept(GetString(IDS_RB_EDITATTACHKEY)); else theHold.Cancel(); break; case BARYGRAPH_MOVE: { cont->HoldTrack(); Point2 bary; SendDlgItemMessage(hWnd,IDC_ATTACH_FACEPOSGRAPH, WM_GETBARY, 0, (LPARAM)&bary); for (int index=0; indexkeys.Count(); index++) { if (!(cont->keys[index].flags&KEY_SELECTED)) continue; cont->keys[index].u0 = bary.x; cont->keys[index].u1 = bary.y; } cont->Invalidate(); cont->NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE); ip->RedrawViews(ip->GetTime()); UpdateWindow(hWnd); break; } } break; } } void AttachKeyDlg::Invalidate() { valid = FALSE; InvalidateRect(hWnd,NULL,FALSE); } void AttachKeyDlg::UpdateBaryGraph(float a, float b) { Point2 pt(a,b); SendDlgItemMessage(hWnd,IDC_ATTACH_FACEPOSGRAPH,WM_SETBARY,0,(LONG)&pt); UpdateWindow(GetDlgItem(hWnd,IDC_ATTACH_FACEPOSGRAPH)); } void AttachKeyDlg::UpdateTCBGraph( float t, float c, float b, float easeIn, float easeOut) { TCBGraphParams tp; tp.tens = t; tp.cont = c; tp.bias = b; tp.easeTo = easeIn; tp.easeFrom = easeOut; SendDlgItemMessage(hWnd,IDC_TCB_GRAPH,WM_SETTCBGRAPHPARAMS,0,(LPARAM)&tp); UpdateWindow(GetDlgItem(hWnd,IDC_TCB_GRAPH)); } void AttachKeyDlg::Update() { if (valid || !hWnd) return; valid = TRUE; BOOL init = FALSE; TimeValue time; DWORD face; float a=0.0f, b=0.0f, tens=0.0f, cnt=0.0f, bias=0.0f, easeTo=0.0f, easeFrom=0.0f; BOOL common[9] = {0,0,0,0,0,0,0,0,0}; int selIndex = -1, selCount=0; for (int i=0; ikeys.Count(); i++) { if (!(cont->keys[i].flags&KEY_SELECTED)) continue; selCount++; if (!init) { init = TRUE; time = cont->keys[i].time; a = cont->keys[i].u0; b = cont->keys[i].u1; tens = cont->keys[i].tens; cnt = cont->keys[i].cont; bias = cont->keys[i].bias; easeTo = cont->keys[i].easeIn; easeFrom = cont->keys[i].easeOut; face = cont->keys[i].face; selIndex = i; for (int j=0; j<9; j++) common[j] = TRUE; } else { if (time !=cont->keys[i].time) common[0] = FALSE; if (a !=cont->keys[i].u0) common[1] = FALSE; if (b !=cont->keys[i].u1) common[2] = FALSE; if (tens !=cont->keys[i].tens) common[3] = FALSE; if (cnt !=cont->keys[i].cont) common[4] = FALSE; if (bias !=cont->keys[i].bias) common[5] = FALSE; if (easeTo !=cont->keys[i].easeIn) common[6] = FALSE; if (easeFrom!=cont->keys[i].easeOut) common[7] = FALSE; if (face !=cont->keys[i].face) common[8] = FALSE; selIndex = -1; } } UpdateBaryGraph(a,b); if (common[3]&&common[4]&&common[5]&&common[6]&&common[7]) { UpdateTCBGraph(tens,cnt,bias,easeTo,easeFrom); EnableWindow(GetDlgItem(hWnd,IDC_TCB_GRAPH),TRUE); } else { EnableWindow(GetDlgItem(hWnd,IDC_TCB_GRAPH),FALSE); } if (selIndex>=0) { TSTR buf; buf.printf(_T("%d"),selIndex+1); iStat->SetText(buf); } else { iStat->SetText(_T("")); } if (selCount==0) { iTime->Disable(); iA->Disable(); iB->Disable(); iTens->Disable(); iCont->Disable(); iBias->Disable(); iEaseTo->Disable(); iEaseFrom->Disable(); iFace->Disable(); EnableWindow(GetDlgItem(hWnd,IDC_ATTACH_FACEPOSGRAPH),FALSE); return; } else { iTime->Enable(); iA->Enable(); iB->Enable(); iTens->Enable(); iCont->Enable(); iBias->Enable(); iEaseTo->Enable(); iEaseFrom->Enable(); iFace->Enable(); EnableWindow(GetDlgItem(hWnd,IDC_ATTACH_FACEPOSGRAPH),TRUE); } if (common[0]) { iTime->SetIndeterminate(FALSE); iTime->SetValue(time,FALSE); } else { iTime->SetIndeterminate(TRUE); } if (common[1]) { iA->SetIndeterminate(FALSE); iA->SetValue(a,FALSE); } else { iA->SetIndeterminate(TRUE); } if (common[2]) { iB->SetIndeterminate(FALSE); iB->SetValue(b,FALSE); } else { iB->SetIndeterminate(TRUE); } if (common[3]) { iTens->SetIndeterminate(FALSE); iTens->SetValue(ToTCBUI(tens),FALSE); } else { iTens->SetIndeterminate(TRUE); } if (common[4]) { iCont->SetIndeterminate(FALSE); iCont->SetValue(ToTCBUI(cnt),FALSE); } else { iCont->SetIndeterminate(TRUE); } if (common[5]) { iBias->SetIndeterminate(FALSE); iBias->SetValue(ToTCBUI(bias),FALSE); } else { iBias->SetIndeterminate(TRUE); } if (common[6]) { iEaseTo->SetIndeterminate(FALSE); iEaseTo->SetValue(ToEaseUI(easeTo),FALSE); } else { iEaseTo->SetIndeterminate(TRUE); } if (common[7]) { iEaseFrom->SetIndeterminate(FALSE); iEaseFrom->SetValue(ToEaseUI(easeFrom),FALSE); } else { iEaseFrom->SetIndeterminate(TRUE); } if (common[8]) { iFace->SetIndeterminate(FALSE); iFace->SetValue((int)face,FALSE); } else { iFace->SetIndeterminate(TRUE); } } void AttachKeyDlg::SetupUI(HWND hWnd) { this->hWnd = hWnd; iTime = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYTIMESPIN)); iTime->SetLimits(GetAnimStart(),GetAnimEnd(),FALSE); iTime->SetScale(10.0f); iTime->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYTIME),EDITTYPE_TIME); iFace = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYFACESPIN)); iFace->SetLimits(0,0xfffffff,FALSE); iFace->SetScale(0.1f); iFace->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYFACE),EDITTYPE_INT); iA = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYASPIN)); iA->SetLimits(-999999999.0f,999999999.0f,FALSE); iA->SetScale(0.005f); iA->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYA),EDITTYPE_FLOAT); iB = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYBSPIN)); iB->SetLimits(-999999999.0f,999999999.0f,FALSE); iB->SetScale(0.005f); iB->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYB),EDITTYPE_FLOAT); iTens = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYTENSSPIN)); iTens->SetLimits(0.0f,50.0f,FALSE); iTens->SetScale(0.1f); iTens->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYTENS),EDITTYPE_FLOAT); iCont = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYCONTSPIN)); iCont->SetLimits(0.0f,50.0f,FALSE); iCont->SetScale(0.1f); iCont->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYCONT),EDITTYPE_FLOAT); iBias = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYBIASSPIN)); iBias->SetLimits(0.0f,50.0f,FALSE); iBias->SetScale(0.1f); iBias->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYBIAS),EDITTYPE_FLOAT); iEaseTo = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYEASETOSPIN)); iEaseTo->SetLimits(0.0f,50.0f,FALSE); iEaseTo->SetScale(0.1f); iEaseTo->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYEASETO),EDITTYPE_FLOAT); iEaseFrom = GetISpinner(GetDlgItem(hWnd,IDC_ATTACH_KEYEASEFROMSPIN)); iEaseFrom->SetLimits(0.0f,50.0f,FALSE); iEaseFrom->SetScale(0.1f); iEaseFrom->LinkToEdit(GetDlgItem(hWnd,IDC_ATTACH_KEYEASEFROM),EDITTYPE_FLOAT); iPrev = GetICustButton(GetDlgItem(hWnd,IDC_PREVKEY)); iNext = GetICustButton(GetDlgItem(hWnd,IDC_NEXTKEY)); iStat = GetICustStatus(GetDlgItem(hWnd,IDC_KEYNUM)); iStat->SetTextFormat(STATUSTEXT_CENTERED); } RefResult AttachKeyDlg::NotifyRefChanged( Interval iv, RefTargetHandle hTarg, PartID& partID,RefMessage msg) { switch (msg) { case REFMSG_CHANGE: Invalidate(); break; case REFMSG_REF_DELETED: MaybeCloseWindow(); break; } return REF_SUCCEED; } static BOOL CALLBACK AttachDlgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { AttachKeyDlg *dlg = (AttachKeyDlg*)GetWindowLong(hWnd,GWL_USERDATA); switch (msg) { case WM_INITDIALOG: dlg = (AttachKeyDlg*)lParam; SetWindowLong(hWnd,GWL_USERDATA,lParam); dlg->SetupUI(hWnd); CenterWindow(hWnd,GetParent(hWnd)); ShowWindow(hWnd,SW_SHOW); break; case WM_CLOSE: DestroyWindow(hWnd); break; case WM_DESTROY: delete dlg; break; case WM_PAINT: dlg->Update(); return FALSE; case CC_SPINNER_BUTTONDOWN: theHold.Begin(); break; case WM_CUSTEDIT_ENTER: case CC_SPINNER_BUTTONUP: if (HIWORD(wParam)) { theHold.Accept(GetString(IDS_RB_EDITATTACHKEY)); } else { theHold.Cancel(); } break; case CC_SPINNER_CHANGE: dlg->SpinnerChange(LOWORD(wParam)); break; case WM_COMMAND: dlg->Command(LOWORD(wParam),lParam); break; default: return FALSE; } return TRUE; }