Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
1148 lines
29 KiB
C++
1148 lines
29 KiB
C++
/**********************************************************************
|
|
*<
|
|
FILE: eulrctrl.cpp
|
|
|
|
DESCRIPTION: An Euler angle rotation controller
|
|
|
|
CREATED BY: Rolf Berteig
|
|
|
|
HISTORY: created 13 June 1995
|
|
|
|
*> Copyright (c) 1994, All Rights Reserved.
|
|
**********************************************************************/
|
|
|
|
#include "ctrl.h"
|
|
#include "interpik.h"
|
|
#include "euler.h"
|
|
|
|
#define EULER_CONTROL_CNAME GetString(IDS_RB_EULERXYZ)
|
|
|
|
#define EULER_X_REF 0
|
|
#define EULER_Y_REF 1
|
|
#define EULER_Z_REF 2
|
|
|
|
class EulerDlg;
|
|
|
|
static DWORD subColor[] = {PAINTCURVE_XCOLOR, PAINTCURVE_YCOLOR, PAINTCURVE_ZCOLOR};
|
|
|
|
class EulerRotation : public Control {
|
|
public:
|
|
Control *rotX;
|
|
Control *rotY;
|
|
Control *rotZ;
|
|
int order;
|
|
Quat curval;
|
|
Interval ivalid;
|
|
|
|
static EulerDlg *dlg;
|
|
static IObjParam *ip;
|
|
static ULONG beginFlags;
|
|
static EulerRotation *editControl; // The one being edited.
|
|
|
|
EulerRotation(const EulerRotation &ctrl);
|
|
EulerRotation(BOOL loading=FALSE);
|
|
~EulerRotation();
|
|
void Update(TimeValue t);
|
|
|
|
// Animatable methods
|
|
Class_ID ClassID() { return Class_ID(EULER_CONTROL_CLASS_ID,0); }
|
|
SClass_ID SuperClassID() { return CTRL_ROTATION_CLASS_ID; }
|
|
|
|
void GetClassName(TSTR& s);
|
|
void DeleteThis() {delete this;}
|
|
int IsKeyable() {return 1;}
|
|
|
|
int NumSubs() {return 3;}
|
|
Animatable* SubAnim(int i);
|
|
TSTR SubAnimName(int i);
|
|
int SubNumToRefNum(int subNum) {return subNum;}
|
|
|
|
DWORD GetSubAnimCurveColor(int subNum) {return subColor[subNum];}
|
|
|
|
ParamDimension* GetParamDimension(int i) {return stdAngleDim;}
|
|
BOOL AssignController(Animatable *control,int subAnim);
|
|
void AddNewKey(TimeValue t,DWORD flags);
|
|
int NumKeys();
|
|
TimeValue GetKeyTime(int index);
|
|
void CopyKeysFromTime(TimeValue src,TimeValue dst,DWORD flags);
|
|
BOOL IsKeyAtTime(TimeValue t,DWORD flags);
|
|
BOOL GetNextKeyTime(TimeValue t,DWORD flags,TimeValue &nt);
|
|
void DeleteKeyAtTime(TimeValue t);
|
|
|
|
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);
|
|
void RescaleWorldUnits(float f) {}
|
|
|
|
IOResult Save(ISave *isave);
|
|
IOResult Load(ILoad *iload);
|
|
|
|
// Control methods
|
|
Control *GetXController() {return rotX;}
|
|
Control *GetYController() {return rotY;}
|
|
Control *GetZController() {return rotZ;}
|
|
void Copy(Control *from);
|
|
RefTargetHandle Clone(RemapDir& remap);
|
|
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);
|
|
void EnableORTs(BOOL enable);
|
|
void MirrorIKConstraints(int axis,int which);
|
|
BOOL CanCopyIKParams(int which);
|
|
IKClipObject *CopyIKParams(int which);
|
|
BOOL CanPasteIKParams(IKClipObject *co,int which);
|
|
void PasteIKParams(IKClipObject *co,int which);
|
|
|
|
void ChangeOrdering(int newOrder);
|
|
};
|
|
|
|
EulerDlg *EulerRotation::dlg = NULL;
|
|
IObjParam *EulerRotation::ip = NULL;
|
|
ULONG EulerRotation::beginFlags = 0;
|
|
EulerRotation *EulerRotation::editControl = NULL;
|
|
|
|
class JointParamsEuler : public JointParams {
|
|
public:
|
|
JointParamsEuler() : JointParams((DWORD)JNT_ROT,3) {flags |= JNT_LIMITEXACT;}
|
|
void SpinnerChange(InterpCtrlUI *ui,WORD id,ISpinnerControl *spin,BOOL interactive);
|
|
};
|
|
|
|
static BOOL CALLBACK EulerParamDialogProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
|
|
|
|
static const int editButs[] = {IDC_EULER_X,IDC_EULER_Y,IDC_EULER_Z};
|
|
|
|
static int eulerIDs[] = {
|
|
IDS_RB_EULERTYPE0,IDS_RB_EULERTYPE1,IDS_RB_EULERTYPE2,
|
|
IDS_RB_EULERTYPE3,IDS_RB_EULERTYPE4,IDS_RB_EULERTYPE5,
|
|
IDS_RB_EULERTYPE6,IDS_RB_EULERTYPE7,IDS_RB_EULERTYPE8};
|
|
|
|
typedef int EAOrdering[3];
|
|
static EAOrdering orderings[] = {
|
|
{0,1,2},
|
|
{0,2,1},
|
|
{1,2,0},
|
|
{1,0,2},
|
|
{2,0,1},
|
|
{2,1,0},
|
|
{0,1,0},
|
|
{1,2,1},
|
|
{2,0,2},
|
|
};
|
|
|
|
static int xyzIDs[] = {IDS_RB_X,IDS_RB_Y,IDS_RB_Z};
|
|
static int xyzRotIDs[] = {IDS_RB_XROTATION,IDS_RB_YROTATION,IDS_RB_ZROTATION};
|
|
static int xyzAxisIDs[] = {IDS_RB_XAXIS,IDS_RB_YAXIS,IDS_RB_ZAXIS};
|
|
|
|
#define EDIT_X 0
|
|
#define EDIT_Y 1
|
|
#define EDIT_Z 2
|
|
|
|
#define EULER_BEGIN 1
|
|
#define EULER_MIDDLE 2
|
|
#define EULER_END 3
|
|
|
|
class EulerDlg {
|
|
public:
|
|
EulerRotation *cont;
|
|
HWND hWnd;
|
|
IObjParam *ip;
|
|
ICustButton *iEdit[3];
|
|
static int cur;
|
|
|
|
EulerDlg(EulerRotation *cont,IObjParam *ip);
|
|
~EulerDlg();
|
|
|
|
void Init();
|
|
void SetButtonText();
|
|
void EndingEdit(EulerRotation *next);
|
|
void BeginingEdit(EulerRotation *cont,IObjParam *ip,EulerRotation *prev);
|
|
void SetCur(int c,int code=EULER_MIDDLE);
|
|
void WMCommand(int id, int notify, HWND hCtrl);
|
|
};
|
|
|
|
int EulerDlg::cur = EDIT_X;
|
|
|
|
EulerDlg::EulerDlg(EulerRotation *cont,IObjParam *ip)
|
|
{
|
|
this->ip = ip;
|
|
this->cont = cont;
|
|
for (int i=0; i<3; i++) {
|
|
iEdit[i] = NULL;
|
|
}
|
|
|
|
hWnd = ip->AddRollupPage(
|
|
hInstance,
|
|
MAKEINTRESOURCE(IDD_EULER_PARAMS),
|
|
EulerParamDialogProc,
|
|
GetString(IDS_RB_EULERPARAMS),
|
|
(LPARAM)this);
|
|
ip->RegisterDlgWnd(hWnd);
|
|
|
|
SetCur(cur,EULER_BEGIN);
|
|
UpdateWindow(hWnd);
|
|
}
|
|
|
|
EulerDlg::~EulerDlg()
|
|
{
|
|
SetCur(cur,EULER_END);
|
|
for (int i=0; i<3; i++) {
|
|
ReleaseICustButton(iEdit[i]);
|
|
}
|
|
ip->UnRegisterDlgWnd(hWnd);
|
|
ip->DeleteRollupPage(hWnd);
|
|
hWnd = NULL;
|
|
}
|
|
|
|
void EulerDlg::EndingEdit(EulerRotation *next)
|
|
{
|
|
switch (cur) {
|
|
case EDIT_X:
|
|
cont->rotX->EndEditParams(ip,0,next->rotX);
|
|
break;
|
|
case EDIT_Y:
|
|
cont->rotY->EndEditParams(ip,0,next->rotY);
|
|
break;
|
|
case EDIT_Z:
|
|
cont->rotZ->EndEditParams(ip,0,next->rotZ);
|
|
break;
|
|
}
|
|
cont = NULL;
|
|
ip = NULL;
|
|
}
|
|
|
|
void EulerDlg::BeginingEdit(EulerRotation *cont,IObjParam *ip,EulerRotation *prev)
|
|
{
|
|
this->ip = ip;
|
|
this->cont = cont;
|
|
switch (cur) {
|
|
case EDIT_X:
|
|
cont->rotX->BeginEditParams(ip,BEGIN_EDIT_MOTION,prev->rotX);
|
|
break;
|
|
case EDIT_Y:
|
|
cont->rotY->BeginEditParams(ip,BEGIN_EDIT_MOTION,prev->rotY);
|
|
break;
|
|
case EDIT_Z:
|
|
cont->rotZ->BeginEditParams(ip,BEGIN_EDIT_MOTION,prev->rotZ);
|
|
break;
|
|
}
|
|
UpdateWindow(hWnd);
|
|
}
|
|
|
|
void EulerDlg::SetButtonText()
|
|
{
|
|
for (int i=0; i<3; i++) {
|
|
iEdit[i]->SetText(GetString(
|
|
xyzIDs[orderings[cont->order][i]]));
|
|
}
|
|
}
|
|
|
|
void EulerDlg::Init()
|
|
{
|
|
for (int i=0; i<3; i++) {
|
|
iEdit[i] = GetICustButton(GetDlgItem(hWnd,editButs[i]));
|
|
iEdit[i]->SetType(CBT_CHECK);
|
|
}
|
|
iEdit[cur]->SetCheck(TRUE);
|
|
SetButtonText();
|
|
|
|
SendDlgItemMessage(hWnd,IDC_EULER_ORDER,CB_RESETCONTENT,0,0);
|
|
for (i=0; i<9; i++) {
|
|
SendDlgItemMessage(hWnd,IDC_EULER_ORDER,CB_ADDSTRING,0,
|
|
(LPARAM)GetString(eulerIDs[i]));
|
|
}
|
|
SendDlgItemMessage(hWnd,IDC_EULER_ORDER,CB_SETCURSEL,cont->order,0);
|
|
}
|
|
|
|
void EulerDlg::SetCur(int c,int code)
|
|
{
|
|
if (c==cur && code==EULER_MIDDLE) return;
|
|
Control *prev = NULL, *next = NULL;
|
|
|
|
if (code!=EULER_END) {
|
|
switch (c) {
|
|
case EDIT_X:
|
|
next = cont->rotX;
|
|
break;
|
|
case EDIT_Y:
|
|
next = cont->rotY;
|
|
break;
|
|
case EDIT_Z:
|
|
next = cont->rotZ;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (code!=EULER_BEGIN) {
|
|
switch (cur) {
|
|
case EDIT_X:
|
|
cont->rotX->EndEditParams(ip,END_EDIT_REMOVEUI,next);
|
|
prev = cont->rotX;
|
|
break;
|
|
case EDIT_Y:
|
|
cont->rotY->EndEditParams(ip,END_EDIT_REMOVEUI,next);
|
|
prev = cont->rotY;
|
|
break;
|
|
case EDIT_Z:
|
|
cont->rotZ->EndEditParams(ip,END_EDIT_REMOVEUI,next);
|
|
prev = cont->rotZ;
|
|
break;
|
|
}
|
|
}
|
|
|
|
cur = c;
|
|
|
|
if (code!=EULER_END) {
|
|
switch (cur) {
|
|
case EDIT_X:
|
|
cont->rotX->BeginEditParams(ip,BEGIN_EDIT_MOTION,prev);
|
|
break;
|
|
case EDIT_Y:
|
|
cont->rotY->BeginEditParams(ip,BEGIN_EDIT_MOTION,prev);
|
|
break;
|
|
case EDIT_Z:
|
|
cont->rotZ->BeginEditParams(ip,BEGIN_EDIT_MOTION,prev);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EulerDlg::WMCommand(int id, int notify, HWND hCtrl)
|
|
{
|
|
switch (id) {
|
|
case IDC_EULER_X:
|
|
SetCur(0);
|
|
iEdit[0]->SetCheck(TRUE);
|
|
iEdit[1]->SetCheck(FALSE);
|
|
iEdit[2]->SetCheck(FALSE);
|
|
break;
|
|
case IDC_EULER_Y:
|
|
SetCur(1);
|
|
iEdit[0]->SetCheck(FALSE);
|
|
iEdit[1]->SetCheck(TRUE);
|
|
iEdit[2]->SetCheck(FALSE);
|
|
break;
|
|
case IDC_EULER_Z:
|
|
SetCur(2);
|
|
iEdit[0]->SetCheck(FALSE);
|
|
iEdit[1]->SetCheck(FALSE);
|
|
iEdit[2]->SetCheck(TRUE);
|
|
break;
|
|
|
|
case IDC_EULER_ORDER:
|
|
if (notify==CBN_SELCHANGE) {
|
|
int res = SendDlgItemMessage(hWnd,IDC_EULER_ORDER,CB_GETCURSEL,0,0);
|
|
if (res!=CB_ERR) {
|
|
cont->ChangeOrdering(res);
|
|
SetButtonText();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static BOOL CALLBACK EulerParamDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
EulerDlg *dlg = (EulerDlg*)GetWindowLong(hDlg,GWL_USERDATA);
|
|
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
dlg = (EulerDlg*)lParam;
|
|
SetWindowLong(hDlg,GWL_USERDATA,lParam);
|
|
dlg->hWnd = hDlg;
|
|
dlg->Init();
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
dlg->WMCommand(LOWORD(wParam),HIWORD(wParam),(HWND)lParam);
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:case WM_LBUTTONUP: case WM_MOUSEMOVE:
|
|
dlg->ip->RollupMouseMessage(hDlg,message,wParam,lParam);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//********************************************************
|
|
// EULER CONTROL
|
|
//********************************************************
|
|
static Class_ID eulerControlClassID(EULER_CONTROL_CLASS_ID,0);
|
|
class EulerClassDesc:public ClassDesc {
|
|
public:
|
|
int IsPublic() { return 1; }
|
|
void * Create(BOOL loading) { return new EulerRotation(loading); }
|
|
const TCHAR * ClassName() { return EULER_CONTROL_CNAME; }
|
|
SClass_ID SuperClassID() { return CTRL_ROTATION_CLASS_ID; }
|
|
Class_ID ClassID() { return eulerControlClassID; }
|
|
const TCHAR* Category() { return _T(""); }
|
|
};
|
|
static EulerClassDesc eulerCD;
|
|
ClassDesc* GetEulerCtrlDesc() {return &eulerCD;}
|
|
|
|
EulerRotation::EulerRotation(const EulerRotation &ctrl)
|
|
{
|
|
order = EULERTYPE_XYZ;
|
|
rotX = NULL;
|
|
rotY = NULL;
|
|
rotZ = NULL;
|
|
|
|
if (ctrl.rotX) {
|
|
ReplaceReference(EULER_X_REF,ctrl.rotX);
|
|
} else {
|
|
ReplaceReference(EULER_X_REF,NewDefaultFloatController());
|
|
}
|
|
if (ctrl.rotY) {
|
|
ReplaceReference(EULER_Y_REF,ctrl.rotY);
|
|
} else {
|
|
ReplaceReference(EULER_Y_REF,NewDefaultFloatController());
|
|
}
|
|
if (ctrl.rotZ) {
|
|
ReplaceReference(EULER_Z_REF,ctrl.rotZ);
|
|
} else {
|
|
ReplaceReference(EULER_Z_REF,NewDefaultFloatController());
|
|
}
|
|
curval = ctrl.curval;
|
|
ivalid = ctrl.ivalid;
|
|
}
|
|
|
|
EulerRotation::EulerRotation(BOOL loading)
|
|
{
|
|
order = EULERTYPE_XYZ;
|
|
rotX = NULL;
|
|
rotY = NULL;
|
|
rotZ = NULL;
|
|
if (!loading) {
|
|
ReplaceReference(EULER_X_REF,NewDefaultFloatController());
|
|
ReplaceReference(EULER_Y_REF,NewDefaultFloatController());
|
|
ReplaceReference(EULER_Z_REF,NewDefaultFloatController());
|
|
ivalid = FOREVER;
|
|
curval.Identity();
|
|
} else {
|
|
ivalid.SetEmpty();
|
|
}
|
|
}
|
|
|
|
RefTargetHandle EulerRotation::Clone(RemapDir& remap)
|
|
{
|
|
EulerRotation *euler = new EulerRotation(TRUE);
|
|
euler->ReplaceReference(EULER_X_REF, remap.CloneRef(rotX));
|
|
euler->ReplaceReference(EULER_Y_REF, remap.CloneRef(rotY));
|
|
euler->ReplaceReference(EULER_Z_REF, remap.CloneRef(rotZ));
|
|
euler->order = order;
|
|
|
|
JointParams *jp = (JointParams*)GetProperty(PROPID_JOINTPARAMS);
|
|
if (jp) {
|
|
JointParams *jp2 = new JointParams(*jp);
|
|
euler->SetProperty(PROPID_JOINTPARAMS,jp2);
|
|
}
|
|
return euler;
|
|
}
|
|
|
|
|
|
|
|
EulerRotation::~EulerRotation()
|
|
{
|
|
DeleteAllRefsFromMe();
|
|
}
|
|
|
|
void EulerRotation::GetClassName(TSTR& s)
|
|
{
|
|
TSTR format(GetString(IDS_RB_EULERNAME));
|
|
s.printf(format,GetString(eulerIDs[order]));
|
|
}
|
|
|
|
void EulerRotation::Copy(Control *from)
|
|
{
|
|
if (from->ClassID()==ClassID()) {
|
|
EulerRotation *ctrl = (EulerRotation*)from;
|
|
ReplaceReference(EULER_X_REF,ctrl->rotX);
|
|
ReplaceReference(EULER_Y_REF,ctrl->rotY);
|
|
ReplaceReference(EULER_Z_REF,ctrl->rotZ);
|
|
curval = ctrl->curval;
|
|
ivalid = ctrl->ivalid;
|
|
order = ctrl->order;
|
|
} else {
|
|
Quat v;
|
|
Interval iv;
|
|
int num;
|
|
if (num=from->NumKeys()) {
|
|
SuspendAnimate();
|
|
AnimateOn();
|
|
for (int i=0; i<num; i++) {
|
|
TimeValue t = from->GetKeyTime(i);
|
|
from->GetValue(t,&v,iv);
|
|
SetValue(t,&v,TRUE,CTRL_ABSOLUTE);
|
|
}
|
|
ResumeAnimate();
|
|
} else {
|
|
from->GetValue(0,&v,ivalid);
|
|
SetValue(0,&v,TRUE,CTRL_ABSOLUTE);
|
|
}
|
|
}
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
void EulerRotation::Update(TimeValue t)
|
|
{
|
|
if (!ivalid.InInterval(t)) {
|
|
ivalid = FOREVER;
|
|
Point3 ang(0,0,0);
|
|
if (rotX) rotX->GetValue(t,&ang.x,ivalid);
|
|
if (rotY) rotY->GetValue(t,&ang.y,ivalid);
|
|
if (rotZ) rotZ->GetValue(t,&ang.z,ivalid);
|
|
|
|
// This could be optimized.
|
|
//Matrix3 tm(1);
|
|
//tm.RotateX(x);
|
|
//tm.RotateY(y);
|
|
//tm.RotateZ(z);
|
|
//curval = Quat(tm);
|
|
Matrix3 tm(1);
|
|
for (int i=0; i<3; i++) {
|
|
switch (orderings[order][i]) {
|
|
case 0: tm.RotateX(ang[i]); break;
|
|
case 1: tm.RotateY(ang[i]); break;
|
|
case 2: tm.RotateZ(ang[i]); break;
|
|
}
|
|
}
|
|
curval = Quat(tm);
|
|
//EulerToQuat(ang, curval, order);
|
|
}
|
|
}
|
|
|
|
void EulerRotation::ChangeOrdering(int newOrder)
|
|
{
|
|
order = newOrder;
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_SUBANIM_STRUCTURE_CHANGED);
|
|
ip->RedrawViews(ip->GetTime());
|
|
}
|
|
|
|
void EulerRotation::SetValue(TimeValue t, void *val, int commit, GetSetMethod method)
|
|
{
|
|
Quat v;
|
|
Update(t);
|
|
if (method==CTRL_RELATIVE) {
|
|
v = curval * Quat(*((AngAxis*)val));
|
|
} else {
|
|
v = *((Quat*)val);
|
|
}
|
|
|
|
float ang[3];
|
|
//QuatToEuler(v,ang);
|
|
Matrix3 tm;
|
|
v.MakeMatrix(tm);
|
|
MatrixToEuler(tm,ang, order);
|
|
|
|
// RB: this gives the incorrect sign sometimes...
|
|
//QuatToEuler(v, ang, order);
|
|
|
|
if (rotX) rotX->SetValue(t,&ang[0]);
|
|
if (rotY) rotY->SetValue(t,&ang[1]);
|
|
if (rotZ) rotZ->SetValue(t,&ang[2]);
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
void EulerRotation::GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method)
|
|
{
|
|
Update(t);
|
|
valid &= ivalid;
|
|
if (method==CTRL_RELATIVE) {
|
|
Matrix3 *mat = (Matrix3*)val;
|
|
PreRotateMatrix(*mat,curval);
|
|
} else {
|
|
*((Quat*)val) = curval;
|
|
}
|
|
}
|
|
|
|
void EulerRotation::CommitValue(TimeValue t)
|
|
{
|
|
if (rotX) rotX->CommitValue(t);
|
|
if (rotY) rotY->CommitValue(t);
|
|
if (rotZ) rotZ->CommitValue(t);
|
|
}
|
|
|
|
void EulerRotation::RestoreValue(TimeValue t)
|
|
{
|
|
if (rotX) rotX->RestoreValue(t);
|
|
if (rotY) rotY->RestoreValue(t);
|
|
if (rotZ) rotZ->RestoreValue(t);
|
|
}
|
|
|
|
RefTargetHandle EulerRotation::GetReference(int i)
|
|
{
|
|
switch (i) {
|
|
case EULER_X_REF: return rotX;
|
|
case EULER_Y_REF: return rotY;
|
|
case EULER_Z_REF: return rotZ;
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
void EulerRotation::SetReference(int i, RefTargetHandle rtarg)
|
|
{
|
|
switch (i) {
|
|
case EULER_X_REF: rotX = (Control*)rtarg; break;
|
|
case EULER_Y_REF: rotY = (Control*)rtarg; break;
|
|
case EULER_Z_REF: rotZ = (Control*)rtarg; break;
|
|
}
|
|
}
|
|
|
|
Animatable* EulerRotation::SubAnim(int i)
|
|
{
|
|
return GetReference(i);
|
|
}
|
|
|
|
TSTR EulerRotation::SubAnimName(int i)
|
|
{
|
|
switch (i) {
|
|
case EULER_X_REF: return GetString(xyzRotIDs[orderings[order][0]]);
|
|
case EULER_Y_REF: return GetString(xyzRotIDs[orderings[order][1]]);
|
|
case EULER_Z_REF: return GetString(xyzRotIDs[orderings[order][2]]);
|
|
default: return _T("");
|
|
}
|
|
}
|
|
|
|
RefResult EulerRotation::NotifyRefChanged(
|
|
Interval iv,
|
|
RefTargetHandle hTarg,
|
|
PartID& partID,
|
|
RefMessage msg)
|
|
{
|
|
switch (msg) {
|
|
case REFMSG_CHANGE:
|
|
ivalid.SetEmpty();
|
|
break;
|
|
case REFMSG_TARGET_DELETED:
|
|
if (rotX == hTarg) rotX = NULL;
|
|
if (rotY == hTarg) rotY = NULL;
|
|
if (rotZ == hTarg) rotZ = NULL;
|
|
break;
|
|
case REFMSG_GET_CONTROL_DIM: {
|
|
ParamDimension **dim = (ParamDimension **)partID;
|
|
assert(dim);
|
|
*dim = stdAngleDim;
|
|
}
|
|
}
|
|
return REF_SUCCEED;
|
|
}
|
|
|
|
BOOL EulerRotation::AssignController(Animatable *control,int subAnim)
|
|
{
|
|
switch (subAnim) {
|
|
case EULER_X_REF:
|
|
ReplaceReference(EULER_X_REF,(RefTargetHandle)control);
|
|
break;
|
|
case EULER_Y_REF:
|
|
ReplaceReference(EULER_Y_REF,(RefTargetHandle)control);
|
|
break;
|
|
case EULER_Z_REF:
|
|
ReplaceReference(EULER_Z_REF,(RefTargetHandle)control);
|
|
break;
|
|
}
|
|
NotifyDependents(FOREVER,0,REFMSG_CONTROLREF_CHANGE,TREE_VIEW_CLASS_ID,FALSE);
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
return TRUE;
|
|
}
|
|
|
|
void EulerRotation::AddNewKey(TimeValue t,DWORD flags)
|
|
{
|
|
if (rotX) rotX->AddNewKey(t,flags);
|
|
if (rotY) rotY->AddNewKey(t,flags);
|
|
if (rotZ) rotZ->AddNewKey(t,flags);
|
|
}
|
|
|
|
int EulerRotation::NumKeys()
|
|
{
|
|
int num = 0;
|
|
if (rotX) num += rotX->NumKeys();
|
|
if (rotY) num += rotY->NumKeys();
|
|
if (rotZ) num += rotZ->NumKeys();
|
|
return num;
|
|
}
|
|
|
|
TimeValue EulerRotation::GetKeyTime(int index)
|
|
{
|
|
int onum,num = 0;
|
|
if (rotX) num += rotX->NumKeys();
|
|
if (index < num) return rotX->GetKeyTime(index);
|
|
onum = num;
|
|
if (rotY) num += rotY->NumKeys();
|
|
if (index < num) return rotY->GetKeyTime(index-onum);
|
|
onum = num;
|
|
if (rotZ) num += rotZ->NumKeys();
|
|
if (index < num) return rotZ->GetKeyTime(index-onum);
|
|
return 0;
|
|
}
|
|
|
|
void EulerRotation::CopyKeysFromTime(TimeValue src,TimeValue dst,DWORD flags)
|
|
{
|
|
if (rotX) rotX->CopyKeysFromTime(src,dst,flags);
|
|
if (rotY) rotY->CopyKeysFromTime(src,dst,flags);
|
|
if (rotZ) rotZ->CopyKeysFromTime(src,dst,flags);
|
|
}
|
|
|
|
BOOL EulerRotation::IsKeyAtTime(TimeValue t,DWORD flags)
|
|
{
|
|
if (rotX && rotX->IsKeyAtTime(t,flags)) return TRUE;
|
|
if (rotY && rotY->IsKeyAtTime(t,flags)) return TRUE;
|
|
if (rotZ && rotZ->IsKeyAtTime(t,flags)) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
void EulerRotation::DeleteKeyAtTime(TimeValue t)
|
|
{
|
|
if (rotX) rotX->DeleteKeyAtTime(t);
|
|
if (rotY) rotY->DeleteKeyAtTime(t);
|
|
if (rotZ) rotZ->DeleteKeyAtTime(t);
|
|
}
|
|
|
|
BOOL EulerRotation::GetNextKeyTime(TimeValue t,DWORD flags,TimeValue &nt)
|
|
{
|
|
TimeValue at,tnear;
|
|
BOOL tnearInit = FALSE;
|
|
|
|
if (rotX && rotX->GetNextKeyTime(t,flags,at)) {
|
|
if (!tnearInit) {
|
|
tnear = at;
|
|
tnearInit = TRUE;
|
|
} else
|
|
if (ABS(at-t) < ABS(tnear-t)) tnear = at;
|
|
}
|
|
|
|
if (rotY && rotY->GetNextKeyTime(t,flags,at)) {
|
|
if (!tnearInit) {
|
|
tnear = at;
|
|
tnearInit = TRUE;
|
|
} else
|
|
if (ABS(at-t) < ABS(tnear-t)) tnear = at;
|
|
}
|
|
|
|
if (rotZ && rotZ->GetNextKeyTime(t,flags,at)) {
|
|
if (!tnearInit) {
|
|
tnear = at;
|
|
tnearInit = TRUE;
|
|
} else
|
|
if (ABS(at-t) < ABS(tnear-t)) tnear = at;
|
|
}
|
|
|
|
if (tnearInit) {
|
|
nt = tnear;
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
void EulerRotation::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev )
|
|
{
|
|
if (flags&BEGIN_EDIT_HIERARCHY) {
|
|
JointParamsEuler *jp = (JointParamsEuler*)GetProperty(PROPID_JOINTPARAMS);
|
|
InterpCtrlUI *ui;
|
|
|
|
if (!jp) {
|
|
jp = new JointParamsEuler();
|
|
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_STDJOINTPARAMS),
|
|
JointParamDlgProc,
|
|
GetString(IDS_RB_ROTJOINTPARAMS),
|
|
(LPARAM)new JointDlgData(ui,jp),f);
|
|
}
|
|
|
|
SetDlgItemText(ui->hParams,IDC_XAXIS_LABEL,
|
|
GetString(xyzAxisIDs[orderings[order][0]]));
|
|
SetDlgItemText(ui->hParams,IDC_YAXIS_LABEL,
|
|
GetString(xyzAxisIDs[orderings[order][1]]));
|
|
SetDlgItemText(ui->hParams,IDC_ZAXIS_LABEL,
|
|
GetString(xyzAxisIDs[orderings[order][2]]));
|
|
|
|
SetProperty(PROPID_INTERPUI,ui);
|
|
editControl = this;
|
|
beginFlags = flags;
|
|
} else
|
|
if (flags&BEGIN_EDIT_MOTION) {
|
|
this->ip = ip;
|
|
|
|
if (dlg) {
|
|
dlg->BeginingEdit(this,ip,(EulerRotation*)prev);
|
|
dlg->Init();
|
|
} else {
|
|
dlg = new EulerDlg(this,ip);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EulerRotation::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next )
|
|
{
|
|
EulerRotation *cont=NULL;
|
|
if (next && next->ClassID()==ClassID()) {
|
|
cont = (EulerRotation*)next;
|
|
}
|
|
|
|
if (dlg) {
|
|
if (cont) {
|
|
dlg->EndingEdit(cont);
|
|
} else {
|
|
delete dlg;
|
|
dlg = NULL;
|
|
}
|
|
} else {
|
|
if (cont) return;
|
|
|
|
editControl = NULL;
|
|
beginFlags = 0;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int EulerRotation::SetProperty(ULONG id, void *data)
|
|
{
|
|
if (id==PROPID_JOINTPARAMS) {
|
|
if (!data) {
|
|
int index = aprops.FindProperty(id);
|
|
if (index>=0) {
|
|
aprops.Delete(index,1);
|
|
}
|
|
} else {
|
|
JointParamsEuler *jp = (JointParamsEuler*)GetProperty(id);
|
|
if (jp) {
|
|
*jp = *((JointParamsEuler*)data);
|
|
delete (JointParamsEuler*)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* EulerRotation::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);
|
|
}
|
|
}
|
|
|
|
|
|
#define JOINTPARAMEULER_CHUNK 0x1002
|
|
#define ORDER_CHUNK 0x1003
|
|
|
|
IOResult EulerRotation::Save(ISave *isave)
|
|
{
|
|
ULONG nb;
|
|
JointParamsEuler *jp = (JointParamsEuler*)GetProperty(PROPID_JOINTPARAMS);
|
|
if (jp) {
|
|
isave->BeginChunk(JOINTPARAMEULER_CHUNK);
|
|
jp->Save(isave);
|
|
isave->EndChunk();
|
|
}
|
|
|
|
isave->BeginChunk(ORDER_CHUNK);
|
|
isave->Write(&order,sizeof(order),&nb);
|
|
isave->EndChunk();
|
|
|
|
return IO_OK;
|
|
}
|
|
|
|
IOResult EulerRotation::Load(ILoad *iload)
|
|
{
|
|
ULONG nb;
|
|
IOResult res = IO_OK;
|
|
while (IO_OK==(res=iload->OpenChunk())) {
|
|
switch (iload->CurChunkID()) {
|
|
case ORDER_CHUNK:
|
|
res=iload->Read(&order,sizeof(order),&nb);
|
|
break;
|
|
|
|
case JOINTPARAMEULER_CHUNK: {
|
|
JointParamsEuler *jp = new JointParamsEuler;
|
|
jp->Load(iload);
|
|
jp->flags |= JNT_LIMITEXACT;
|
|
SetProperty(PROPID_JOINTPARAMS,jp);
|
|
break;
|
|
}
|
|
}
|
|
iload->CloseChunk();
|
|
if (res!=IO_OK) return res;
|
|
}
|
|
return IO_OK;
|
|
}
|
|
|
|
|
|
void EulerRotation::EnumIKParams(IKEnumCallback &callback)
|
|
{
|
|
JointParamsEuler *jp = (JointParamsEuler*)GetProperty(PROPID_JOINTPARAMS);
|
|
for (int i=2; i>=0; i--) {
|
|
if (!jp || jp->Active(i)) {
|
|
callback.proc(this,i);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL EulerRotation::CompDeriv(TimeValue t,Matrix3& ptm,IKDeriv& derivs,DWORD flags)
|
|
{
|
|
JointParamsEuler *jp = (JointParamsEuler*)GetProperty(PROPID_JOINTPARAMS);
|
|
Quat q;
|
|
Interval valid;
|
|
Point3 a(0,0,0);
|
|
|
|
if (rotX) rotX->GetValue(t,&a[0],valid);
|
|
if (rotY) rotY->GetValue(t,&a[1],valid);
|
|
if (rotZ) rotZ->GetValue(t,&a[2],valid);
|
|
|
|
for (int i=2; i>=0; i--) {
|
|
if (!jp || jp->Active(i)) {
|
|
for (int j=0; j<derivs.NumEndEffectors(); j++) {
|
|
Point3 r = derivs.EndEffectorPos(j) - ptm.GetTrans();
|
|
|
|
Point3 axis = ptm.GetRow(orderings[order][i]);
|
|
if (!(ptm.GetIdentFlags()&SCL_IDENT)) {
|
|
axis = Normalize(axis);
|
|
if (ptm.Parity()) axis = -axis;
|
|
}
|
|
|
|
if (flags&POSITION_DERIV) {
|
|
derivs.DP(CrossProd(axis,r),j);
|
|
}
|
|
if (flags&ROTATION_DERIV) {
|
|
derivs.DR(axis,j);
|
|
}
|
|
}
|
|
derivs.NextDOF();
|
|
}
|
|
switch (orderings[order][i]) {
|
|
case 0: ptm.PreRotateX(a[i]); break;
|
|
case 1: ptm.PreRotateY(a[i]); break;
|
|
case 2: ptm.PreRotateZ(a[i]); break;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#define MAX_IKROT DegToRad(4.0f)
|
|
#define SGN(a) (a<0?-1:1)
|
|
|
|
float EulerRotation::IncIKParam(TimeValue t,int index,float delta)
|
|
{
|
|
JointParamsEuler *jp = (JointParamsEuler*)GetProperty(PROPID_JOINTPARAMS);
|
|
if ((float)fabs(delta)>MAX_IKROT) delta = MAX_IKROT * SGN(delta);
|
|
|
|
if (jp) {
|
|
float v=0.0f;
|
|
if (jp->Limited(index) || jp->Spring(index)) {
|
|
Interval valid;
|
|
switch (index) {
|
|
case 0: if (rotX) rotX->GetValue(t,&v,valid); break;
|
|
case 1: if (rotY) rotY->GetValue(t,&v,valid); break;
|
|
case 2: if (rotZ) rotZ->GetValue(t,&v,valid); break;
|
|
}
|
|
}
|
|
delta = jp->ConstrainInc(index,v,delta);
|
|
}
|
|
switch (index) {
|
|
case 0: if (rotX) rotX->SetValue(t,&delta,FALSE,CTRL_RELATIVE); break;
|
|
case 1: if (rotY) rotY->SetValue(t,&delta,FALSE,CTRL_RELATIVE); break;
|
|
case 2: if (rotZ) rotZ->SetValue(t,&delta,FALSE,CTRL_RELATIVE); break;
|
|
}
|
|
return delta;
|
|
}
|
|
|
|
void EulerRotation::ClearIKParam(Interval iv,int index)
|
|
{
|
|
switch (index) {
|
|
case 0: if (rotX) rotX->DeleteTime(iv,TIME_INCRIGHT|TIME_NOSLIDE); break;
|
|
case 1: if (rotY) rotY->DeleteTime(iv,TIME_INCRIGHT|TIME_NOSLIDE); break;
|
|
case 2: if (rotZ) rotZ->DeleteTime(iv,TIME_INCRIGHT|TIME_NOSLIDE); break;
|
|
}
|
|
}
|
|
|
|
void EulerRotation::MirrorIKConstraints(int axis,int which)
|
|
{
|
|
JointParamsEuler *jp = (JointParamsEuler*)GetProperty(PROPID_JOINTPARAMS);
|
|
if (jp) jp->MirrorConstraints(axis);
|
|
}
|
|
|
|
void EulerRotation::EnableORTs(BOOL enable)
|
|
{
|
|
if (rotX) rotX->EnableORTs(enable);
|
|
if (rotY) rotY->EnableORTs(enable);
|
|
if (rotZ) rotZ->EnableORTs(enable);
|
|
}
|
|
|
|
BOOL EulerRotation::CanCopyIKParams(int which)
|
|
{
|
|
return ::CanCopyIKParams(this,which);
|
|
}
|
|
|
|
IKClipObject *EulerRotation::CopyIKParams(int which)
|
|
{
|
|
return ::CopyIKParams(this,which);
|
|
}
|
|
|
|
BOOL EulerRotation::CanPasteIKParams(IKClipObject *co,int which)
|
|
{
|
|
return ::CanPasteIKParams(this,co,which);
|
|
}
|
|
|
|
void EulerRotation::PasteIKParams(IKClipObject *co,int which)
|
|
{
|
|
::PasteIKParams(this,co,which);
|
|
}
|
|
|
|
void JointParamsEuler::SpinnerChange(
|
|
InterpCtrlUI *ui,WORD id,ISpinnerControl *spin,BOOL interactive)
|
|
{
|
|
EulerRotation *c = (EulerRotation*)ui->cont;
|
|
Point3 a(0,0,0);
|
|
BOOL set = FALSE;
|
|
Interval valid;
|
|
|
|
if (c->rotX) c->rotX->GetValue(ui->ip->GetTime(),&a[0],valid);
|
|
if (c->rotY) c->rotY->GetValue(ui->ip->GetTime(),&a[1],valid);
|
|
if (c->rotZ) c->rotZ->GetValue(ui->ip->GetTime(),&a[2],valid);
|
|
|
|
switch (id) {
|
|
case IDC_XFROMSPIN:
|
|
a[0] = min[0] = DegToRad(spin->GetFVal());
|
|
set = TRUE;
|
|
break;
|
|
case IDC_XTOSPIN:
|
|
a[0] = max[0] = DegToRad(spin->GetFVal());
|
|
set = TRUE;
|
|
break;
|
|
case IDC_XSPRINGSPIN:
|
|
a[0] = spring[0] = DegToRad(spin->GetFVal());
|
|
set = TRUE;
|
|
break;
|
|
|
|
case IDC_YFROMSPIN:
|
|
a[1] = min[1] = DegToRad(spin->GetFVal());
|
|
set = TRUE;
|
|
break;
|
|
case IDC_YTOSPIN:
|
|
a[1] = max[1] = DegToRad(spin->GetFVal());
|
|
set = TRUE;
|
|
break;
|
|
case IDC_YSPRINGSPIN:
|
|
a[0] = spring[1] = DegToRad(spin->GetFVal());
|
|
set = TRUE;
|
|
break;
|
|
|
|
case IDC_ZFROMSPIN:
|
|
a[2] = min[2] = DegToRad(spin->GetFVal());
|
|
set = TRUE;
|
|
break;
|
|
case IDC_ZTOSPIN:
|
|
a[2] = max[2] = DegToRad(spin->GetFVal());
|
|
set = TRUE;
|
|
break;
|
|
case IDC_ZSPRINGSPIN:
|
|
a[2] = spring[0] = DegToRad(spin->GetFVal());
|
|
set = TRUE;
|
|
break;
|
|
|
|
case IDC_XDAMPINGSPIN:
|
|
damping[0] = spin->GetFVal(); break;
|
|
case IDC_YDAMPINGSPIN:
|
|
damping[1] = spin->GetFVal(); break;
|
|
case IDC_ZDAMPINGSPIN:
|
|
damping[2] = spin->GetFVal(); break;
|
|
|
|
case IDC_XSPRINGTENSSPIN:
|
|
stens[0] = spin->GetFVal()/SPRINGTENS_UI; break;
|
|
case IDC_YSPRINGTENSSPIN:
|
|
stens[1] = spin->GetFVal()/SPRINGTENS_UI; break;
|
|
case IDC_ZSPRINGTENSSPIN:
|
|
stens[2] = spin->GetFVal()/SPRINGTENS_UI; break;
|
|
}
|
|
|
|
if (set && interactive) {
|
|
if (c->rotX) c->rotX->SetValue(ui->ip->GetTime(),&a[0],TRUE,CTRL_ABSOLUTE);
|
|
if (c->rotY) c->rotY->SetValue(ui->ip->GetTime(),&a[1],TRUE,CTRL_ABSOLUTE);
|
|
if (c->rotZ) c->rotZ->SetValue(ui->ip->GetTime(),&a[2],TRUE,CTRL_ABSOLUTE);
|
|
ui->ip->RedrawViews(ui->ip->GetTime(),REDRAW_INTERACTIVE);
|
|
}
|
|
}
|
|
|
|
|
|
|