/********************************************************************** *< FILE: surfctrl.cpp DESCRIPTION: A controller that moves an object along a surface CREATED BY: Rolf Berteig HISTORY: created 2/13/97 *> Copyright (c) 1997, All Rights Reserved. **********************************************************************/ #include "ctrl.h" #include "units.h" #include "interpik.h" #include "istdplug.h" #define SURF_CONTROL_CNAME GetString(IDS_RB_SURF) #define U_REF 0 #define V_REF 1 #define SURF_REF 2 class SurfPosition; class PickSurfMode : public PickModeCallback, public PickNodeCallback { public: SurfPosition *cont; PickSurfMode(SurfPosition *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;} }; class SurfPosition : public ISurfPosition, public TimeChangeCallback { public: Control *uCont, *vCont; INode *surf; int align; BOOL flip; Point3 curval; Quat curRot; Interval ivalid; static HWND hWnd; static IObjParam *ip; static ICustButton *iPick; static PickSurfMode *pickMode; static ISpinnerControl *iU, *iV; static SurfPosition *editCont; SurfPosition(); ~SurfPosition(); // Animatable methods Class_ID ClassID() {return SURF_CONTROL_CLASSID;} SClass_ID SuperClassID() { return CTRL_POSITION_CLASS_ID; } void GetClassName(TSTR& s) {s = SURF_CONTROL_CNAME;} void DeleteThis() {delete this;} int IsKeyable() {return 0;} int NumSubs() {return 2;} Animatable* SubAnim(int i); TSTR SubAnimName(int i); int SubNumToRefNum(int subNum); ParamDimension* GetParamDimension(int i) {return stdPercentDim;} BOOL AssignController(Animatable *control,int subAnim); void BeginEditParams(IObjParam *ip, ULONG flags,Animatable *prev); void EndEditParams(IObjParam *ip, ULONG flags,Animatable *next); int SetProperty(ULONG id, void *data); void *GetProperty(ULONG id); // Reference methods int NumRefs() {return 3;}; RefTargetHandle GetReference(int i); void SetReference(int i, RefTargetHandle rtarg); RefResult NotifyRefChanged(Interval, RefTargetHandle, PartID&, RefMessage); RefTargetHandle Clone(RemapDir& remap); IOResult Save(ISave *isave); IOResult Load(ILoad *iload); // Control methods void Copy(Control *from); BOOL IsLeaf() {return FALSE;} void GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method); void SetValue(TimeValue t, void *val, int commit, GetSetMethod method) {} void CommitValue(TimeValue t); void RestoreValue(TimeValue t); void EnumIKParams(IKEnumCallback &callback); BOOL CompDeriv(TimeValue t,Matrix3& ptm,IKDeriv& derivs,DWORD flags); float IncIKParam(TimeValue t,int index,float delta); void ClearIKParam(Interval iv,int index); // TimeChangeCallback methods void TimeChanged(TimeValue t); // Local methods void SetSurface(INode *node); void SetupDialog(HWND hWnd); void DestroyDialog(); void UpdateDialog(); void GetUV(TimeValue t,float &u, float &v,Interval *iv=NULL); Point3 DU(TimeValue t,float u, float v); Point3 DV(TimeValue t,float u, float v); Point3 Norm(TimeValue t,float u, float v); Point3 Pos(TimeValue t,float u, float v,Interval *iv=NULL); int GetAlign() {return align;} void SetAlign(int a); BOOL GetFlip() {return flip;} void SetFlip(BOOL f); }; class JointParamsSurf : public JointParams { public: JointParamsSurf() : JointParams((DWORD)JNT_POS,2,1.0f) {} void SpinnerChange(InterpCtrlUI *ui,WORD id,ISpinnerControl *spin,BOOL interactive); }; //--- ClassDesc and class vars ------------------------------------- class SurfClassDesc:public ClassDesc { public: int IsPublic() {return 1;} void * Create(BOOL loading) { return new SurfPosition();} const TCHAR * ClassName() {return SURF_CONTROL_CNAME;} SClass_ID SuperClassID() {return CTRL_POSITION_CLASS_ID;} Class_ID ClassID() {return SURF_CONTROL_CLASSID;} const TCHAR* Category() {return _T("");} }; static SurfClassDesc surfCD; ClassDesc* GetSurfCtrlDesc() {return &surfCD;} HWND SurfPosition::hWnd = NULL; IObjParam *SurfPosition::ip = NULL; ICustButton *SurfPosition::iPick = NULL; ISpinnerControl *SurfPosition::iU = NULL; ISpinnerControl *SurfPosition::iV = NULL; PickSurfMode *SurfPosition::pickMode = NULL; SurfPosition *SurfPosition::editCont = NULL; static BOOL CALLBACK SurfParamDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); //--- SurfPosition methods ------------------------------------------ SurfPosition::SurfPosition() { uCont = vCont = NULL; surf = NULL; ReplaceReference(U_REF,NewDefaultFloatController()); ReplaceReference(V_REF,NewDefaultFloatController()); align = IDC_ALIGN_NONE; flip = FALSE; ivalid.SetEmpty(); } SurfPosition::~SurfPosition() { DeleteAllRefsFromMe(); } Animatable* SurfPosition::SubAnim(int i) { switch (i) { case 0: return uCont; break; case 1: return vCont; break; }; return NULL; } TSTR SurfPosition::SubAnimName(int i) { switch (i) { case 0: return GetString(IDS_RB_U); break; case 1: return GetString(IDS_RB_V); break; } return _T(""); } int SurfPosition::SubNumToRefNum(int subNum) { switch (subNum) { case 0: return 0; case 1: return 1; default: return -1; } } BOOL SurfPosition::AssignController( Animatable *control,int subAnim) { ReplaceReference(subAnim,(ReferenceTarget*)control); ivalid.SetEmpty(); NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED); NotifyDependents(FOREVER,0,REFMSG_CHANGE); return TRUE; } void SurfPosition::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev) { this->ip = ip; editCont = this; if (flags&BEGIN_EDIT_HIERARCHY) { if (align!=IDC_ALIGN_NONE) return; JointParamsSurf *jp = (JointParamsSurf*)GetProperty(PROPID_JOINTPARAMS); InterpCtrlUI *ui; if (!jp) { jp = new JointParamsSurf(); SetProperty(PROPID_JOINTPARAMS,jp); } if (prev && prev->ClassID()==ClassID() && (ui = (InterpCtrlUI*)prev->GetProperty(PROPID_INTERPUI))) { JointParams *prevjp = (JointParams*)prev->GetProperty(PROPID_JOINTPARAMS); prevjp->EndDialog(ui); ui->cont = this; ui->ip = ip; prev->SetProperty(PROPID_INTERPUI,NULL); JointDlgData *jd = (JointDlgData*)GetWindowLong(ui->hParams,GWL_USERDATA); jd->jp = jp; jp->InitDialog(ui); } else { ui = new InterpCtrlUI(NULL,ip,this); DWORD f=0; if (!jp || !jp->RollupOpen()) f = APPENDROLL_CLOSED; ui->hParams = ip->AddRollupPage( hInstance, MAKEINTRESOURCE(IDD_SURFJOINTPARAMS), (int (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long))JointParamDlgProc, GetString(IDS_RB_SURFJOINTPARAMS), (LPARAM)new JointDlgData(ui,jp),f); } SetProperty(PROPID_INTERPUI,ui); } else { pickMode = new PickSurfMode(this); hWnd = ip->AddRollupPage( hInstance, MAKEINTRESOURCE(IDD_SURFPARAMS), SurfParamDlgProc, GetString(IDS_RB_SURFPARAMS), (LPARAM)this ); ip->RegisterTimeChangeCallback(this); } } void SurfPosition::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next) { editCont = NULL; this->ip = NULL; if (next && next->ClassID()==ClassID() && !hWnd) return; if (hWnd) { ip->UnRegisterTimeChangeCallback(this); ip->ClearPickMode(); delete pickMode; pickMode = NULL; ip->DeleteRollupPage(hWnd); hWnd = NULL; } else { int index = aprops.FindProperty(PROPID_INTERPUI); if (index>=0) { InterpCtrlUI *ui = (InterpCtrlUI*)aprops[index]; if (ui->hParams) { ip->UnRegisterDlgWnd(ui->hParams); ip->DeleteRollupPage(ui->hParams); } index = aprops.FindProperty(PROPID_INTERPUI); if (index>=0) { delete aprops[index]; aprops.Delete(index,1); } } } } RefTargetHandle SurfPosition::GetReference(int i) { switch (i) { case U_REF: return uCont; case V_REF: return vCont; case SURF_REF: return surf; } return NULL; } void SurfPosition::SetReference(int i, RefTargetHandle rtarg) { switch (i) { case U_REF: uCont = (Control*)rtarg; break; case V_REF: vCont = (Control*)rtarg; break; case SURF_REF: surf = (INode*)rtarg; break; } } RefResult SurfPosition::NotifyRefChanged( Interval iv, RefTargetHandle hTarg, PartID &partID, RefMessage msg) { switch (msg) { case REFMSG_CHANGE: ivalid.SetEmpty(); break; case REFMSG_TARGET_DELETED: if (hTarg == surf) { surf = NULL; if (hWnd && editCont == this) { SetWindowText(GetDlgItem(hWnd,IDC_SURFNAME), GetString(IDS_RB_NONE)); } } break; } return REF_SUCCEED; } void SurfPosition::SetAlign(int a) { align = a; NotifyDependents(FOREVER,0,REFMSG_CHANGE); } void SurfPosition::SetFlip(BOOL f) { flip = f; NotifyDependents(FOREVER,0,REFMSG_CHANGE); } RefTargetHandle SurfPosition::Clone(RemapDir& remap) { SurfPosition *cont = new SurfPosition; cont->ReplaceReference(U_REF,remap.CloneRef(uCont)); cont->ReplaceReference(V_REF,remap.CloneRef(vCont)); cont->ReplaceReference(SURF_REF,surf); cont->align = align; cont->flip = flip; return cont; } #define ALIGN_CHUNK 0x0100 #define FLIP_CHUNK 0x0110 #define JOINTPARAMSURF_CHUNK 0x0200 IOResult SurfPosition::Save(ISave *isave) { JointParamsSurf *jp = (JointParamsSurf*)GetProperty(PROPID_JOINTPARAMS); ULONG nb; if (jp) { isave->BeginChunk(JOINTPARAMSURF_CHUNK); jp->Save(isave); isave->EndChunk(); } isave->BeginChunk(ALIGN_CHUNK); isave->Write(&align,sizeof(align),&nb); isave->EndChunk(); if (flip) { isave->BeginChunk(FLIP_CHUNK); isave->EndChunk(); } return IO_OK; } IOResult SurfPosition::Load(ILoad *iload) { ULONG nb; IOResult res = IO_OK; while (IO_OK==(res=iload->OpenChunk())) { switch (iload->CurChunkID()) { case ALIGN_CHUNK: res=iload->Read(&align,sizeof(align),&nb); break; case JOINTPARAMSURF_CHUNK: { JointParamsSurf *jp = new JointParamsSurf; jp->Load(iload); SetProperty(PROPID_JOINTPARAMS,jp); break; } case FLIP_CHUNK: flip = TRUE; break; } iload->CloseChunk(); if (res!=IO_OK) return res; } return IO_OK; } void SurfPosition::Copy(Control *from) { if (from->ClassID()==ClassID()) { SurfPosition *cont = (SurfPosition*)from; ReplaceReference(U_REF,cont->uCont->Clone()); ReplaceReference(V_REF,cont->vCont->Clone()); ReplaceReference(SURF_REF,cont->surf); align = cont->align; flip = cont->flip; } } void SurfPosition::GetUV( TimeValue t,float &u, float &v,Interval *iv) { Interval valid; if (!iv) iv = &valid; uCont->GetValue(t,&u,*iv); vCont->GetValue(t,&v,*iv); } Point3 SurfPosition::DU(TimeValue t,float u, float v) { return (Pos(t,u+0.01f,v)-Pos(t,u,v))/0.01f; } Point3 SurfPosition::DV(TimeValue t,float u, float v) { return (Pos(t,u,v+0.01f)-Pos(t,u,v))/0.01f; } Point3 SurfPosition::Norm(TimeValue t,float u, float v) { Point3 du = DU(t,u,v); Point3 dv = DV(t,u,v); return Normalize(du^dv); } Point3 SurfPosition::Pos( TimeValue t,float u, float v,Interval *iv) { Interval valid; if (!iv) iv = &valid; if (surf) { ObjectState os = surf->EvalWorldState(0); return os.obj->GetSurfacePoint(t,u,v,*iv) * surf->GetObjTMBeforeWSM(t,iv); } else { return Point3(0,0,0); } } void SurfPosition::GetValue( TimeValue t, void *val, Interval &valid, GetSetMethod method) { if (!ivalid.InInterval(t)) { float u, v; ivalid = FOREVER; GetUV(t,u,v,&ivalid); curval = Pos(t,u,v,&ivalid); if (align!=IDC_ALIGN_NONE) { Matrix3 tm(1); Point3 norm = Norm(t,u,v); tm.SetRow(2,flip?-norm:norm); if (align==IDC_ALIGN_U) tm.SetRow(0,Normalize(DU(t,u,v))); else tm.SetRow(0,Normalize(DV(t,u,v))); tm.SetRow(1,Normalize(tm.GetRow(2)^tm.GetRow(0))); curRot = Quat(tm); } } if (method==CTRL_RELATIVE) { Matrix3 *mat = (Matrix3*)val; if (align!=IDC_ALIGN_NONE) { curRot.MakeMatrix(*mat); } mat->SetTrans(curval); } else { *((Point3*)val) = curval; } valid &= ivalid; } void SurfPosition::CommitValue(TimeValue t) { uCont->CommitValue(t); vCont->CommitValue(t); } void SurfPosition::RestoreValue(TimeValue t) { uCont->RestoreValue(t); vCont->RestoreValue(t); } void SurfPosition::TimeChanged(TimeValue t) { UpdateDialog(); } void SurfPosition::SetSurface(INode *node) { ivalid.SetEmpty(); ReplaceReference(SURF_REF,node); NotifyDependents(FOREVER,0,REFMSG_CHANGE); UpdateDialog(); } //--- PickSurfMode methods ----------------------------------------- BOOL PickSurfMode::HitTest( IObjParam *ip,HWND hWnd,ViewExp *vpt, IPoint2 m,int flags) { INode *node = cont->ip->PickNode(hWnd,m); return node?TRUE:FALSE; } BOOL PickSurfMode::Pick(IObjParam *ip,ViewExp *vpt) { INode *node = vpt->GetClosestHit(); if (node) { cont->SetSurface(node); ip->RedrawViews(ip->GetTime()); } return TRUE; } void PickSurfMode::EnterMode(IObjParam *ip) { cont->iPick->SetCheck(TRUE); } void PickSurfMode::ExitMode(IObjParam *ip) { cont->iPick->SetCheck(FALSE); } BOOL PickSurfMode::Filter(INode *node) { if (node) { ObjectState os = node->EvalWorldState(0); if (os.obj->IsParamSurface()) return TRUE; } return FALSE; } //--- UI ------------------------------------------------------------- static BOOL CALLBACK SurfParamDlgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { SurfPosition *co = (SurfPosition*)GetWindowLong(hWnd,GWL_USERDATA); switch (msg) { case WM_INITDIALOG: co = (SurfPosition*)lParam; SetWindowLong(hWnd, GWL_USERDATA, (LONG)co); co->SetupDialog(hWnd); break; case CC_SPINNER_BUTTONDOWN: theHold.Begin(); break; case WM_CUSTEDIT_ENTER: case CC_SPINNER_BUTTONUP: if (HIWORD(wParam)) { theHold.Accept(GetString(IDS_RB_CHANGEUV)); } else { theHold.Cancel(); } co->ip->RedrawViews(co->ip->GetTime()); break; case CC_SPINNER_CHANGE: { float f; switch (LOWORD(wParam)) { case IDC_SURF_USPIN: f = co->iU->GetFVal(); co->uCont->SetValue(co->ip->GetTime(),&f); break; case IDC_SURF_VSPIN: f = co->iV->GetFVal(); co->vCont->SetValue(co->ip->GetTime(),&f); break; } co->ip->RedrawViews(co->ip->GetTime()); break; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_ALIGN_FLIP: co->flip = IsDlgButtonChecked(hWnd,IDC_ALIGN_FLIP); co->ivalid.SetEmpty(); co->NotifyDependents(FOREVER,0,REFMSG_CHANGE); co->ip->RedrawViews(co->ip->GetTime()); break; case IDC_ALIGN_NONE: case IDC_ALIGN_U: case IDC_ALIGN_V: co->align = LOWORD(wParam); co->ivalid.SetEmpty(); co->NotifyDependents(FOREVER,0,REFMSG_CHANGE); co->ip->RedrawViews(co->ip->GetTime()); co->UpdateDialog(); break; case IDC_PICKSURF: co->ip->SetPickMode(co->pickMode); break; } break; case WM_LBUTTONDOWN:case WM_LBUTTONUP: case WM_MOUSEMOVE: co->ip->RollupMouseMessage(hWnd,msg,wParam,lParam); return FALSE; case WM_DESTROY: co->DestroyDialog(); break; default: return FALSE; } return TRUE; } void SurfPosition::SetupDialog(HWND hWnd) { this->hWnd = hWnd; iPick = GetICustButton(GetDlgItem(hWnd,IDC_PICKSURF)); iPick->SetType(CBT_CHECK); iPick->SetHighlightColor(GREEN_WASH); iU = GetISpinner(GetDlgItem(hWnd,IDC_SURF_USPIN)); iU->SetLimits(-999999999.0f,999999999.0f,FALSE); iU->SetAutoScale(); iU->LinkToEdit(GetDlgItem(hWnd,IDC_SURF_U),EDITTYPE_FLOAT); iV = GetISpinner(GetDlgItem(hWnd,IDC_SURF_VSPIN)); iV->SetLimits(-999999999.0f,999999999.0f,FALSE); iV->SetAutoScale(); iV->LinkToEdit(GetDlgItem(hWnd,IDC_SURF_V),EDITTYPE_FLOAT); UpdateDialog(); } void SurfPosition::UpdateDialog() { TimeValue t = ip->GetTime(); float f; uCont->GetValue(t,&f,FOREVER); iU->SetValue(f,FALSE); vCont->GetValue(t,&f,FOREVER); iV->SetValue(f,FALSE); CheckDlgButton(hWnd,align,TRUE); CheckDlgButton(hWnd,IDC_ALIGN_FLIP,flip); if (align!=IDC_ALIGN_NONE) EnableWindow(GetDlgItem(hWnd,IDC_ALIGN_FLIP),TRUE); else EnableWindow(GetDlgItem(hWnd,IDC_ALIGN_FLIP),FALSE); if (surf) { SetWindowText(GetDlgItem(hWnd,IDC_SURFNAME), surf->GetName()); } else { SetWindowText(GetDlgItem(hWnd,IDC_SURFNAME), GetString(IDS_RB_NONE)); } } void SurfPosition::DestroyDialog() { ReleaseISpinner(iU); iU = NULL; ReleaseISpinner(iV); iV = NULL; ReleaseICustButton(iPick); iPick = NULL; } //--- IK ----------------------------------------------------------- int SurfPosition::SetProperty(ULONG id, void *data) { if (id==PROPID_JOINTPARAMS) { if (!data) { int index = aprops.FindProperty(id); if (index>=0) { aprops.Delete(index,1); } } else { JointParamsSurf *jp = (JointParamsSurf*)GetProperty(id); if (jp) { *jp = *((JointParamsSurf*)data); delete (JointParamsSurf*)data; } else { aprops.Append(1,(AnimProperty**)&data); } } return 1; } else if (id==PROPID_INTERPUI) { if (!data) { int index = aprops.FindProperty(id); if (index>=0) { aprops.Delete(index,1); } } else { InterpCtrlUI *ui = (InterpCtrlUI*)GetProperty(id); if (ui) { *ui = *((InterpCtrlUI*)data); } else { aprops.Append(1,(AnimProperty**)&data); } } return 1; } else { return Animatable::SetProperty(id,data); } } void* SurfPosition::GetProperty(ULONG id) { if (id==PROPID_INTERPUI || id==PROPID_JOINTPARAMS) { int index = aprops.FindProperty(id); if (index>=0) { return aprops[index]; } else { return NULL; } } else { return Animatable::GetProperty(id); } } void JointParamsSurf::SpinnerChange( InterpCtrlUI *ui,WORD id,ISpinnerControl *spin, BOOL interactive) { float val; int set = 0; switch (id) { case IDC_XFROMSPIN: val = min[0] = spin->GetFVal()/scale; set = 1; break; case IDC_XTOSPIN: val = max[0] = spin->GetFVal()/scale; set = 1; break; case IDC_XDAMPINGSPIN: damping[0] = spin->GetFVal(); break; case IDC_YFROMSPIN: val = min[1] = spin->GetFVal()/scale; set = 2; break; case IDC_YTOSPIN: val = max[1] = spin->GetFVal()/scale; set = 2; break; case IDC_YDAMPINGSPIN: damping[1] = spin->GetFVal(); break; } if (set && interactive) { SurfPosition *c = (SurfPosition*)ui->cont; if (set==1) c->uCont->SetValue( ui->ip->GetTime(),&val,TRUE,CTRL_ABSOLUTE); else c->vCont->SetValue( ui->ip->GetTime(),&val,TRUE,CTRL_ABSOLUTE); ui->ip->RedrawViews(ui->ip->GetTime(),REDRAW_INTERACTIVE); } } void SurfPosition::EnumIKParams(IKEnumCallback &callback) { JointParamsSurf *jp = (JointParamsSurf*)GetProperty(PROPID_JOINTPARAMS); if (jp && align==IDC_ALIGN_NONE) { if (jp->Active(0)) callback.proc(this,0); if (jp->Active(1)) callback.proc(this,1); } } BOOL SurfPosition::CompDeriv( TimeValue t,Matrix3& ptm,IKDeriv& derivs,DWORD flags) { JointParamsSurf *jp = (JointParamsSurf*)GetProperty(PROPID_JOINTPARAMS); if (!jp || align!=IDC_ALIGN_NONE) return FALSE; float u, v; GetUV(t,u,v); Point3 zero(0,0,0); if (jp->Active(0)) { Point3 d = DU(t,u,v); for (int j=0; jActive(1)) { Point3 d = DV(t,u,v); for (int j=0; j 0.01f) { if (delta<0) delta = -0.01f; else delta = 0.01f; } if (jp) { float u, v; if (index==0) { if (jp->Limited(0)) { uCont->GetValue(t,&u,FOREVER); } delta = jp->ConstrainInc(0,u,delta); uCont->SetValue(t,&delta,FALSE,CTRL_RELATIVE); } else { if (jp->Limited(1)) { vCont->GetValue(t,&v,FOREVER); } delta = jp->ConstrainInc(0,v,delta); vCont->SetValue(t,&delta,FALSE,CTRL_RELATIVE); } } return delta; } void SurfPosition::ClearIKParam(Interval iv,int index) { uCont->DeleteTime(iv,TIME_INCRIGHT|TIME_NOSLIDE); vCont->DeleteTime(iv,TIME_INCRIGHT|TIME_NOSLIDE); }