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.
1427 lines
37 KiB
C++
1427 lines
37 KiB
C++
/**********************************************************************
|
|
*<
|
|
FILE: pathctrl.cpp
|
|
|
|
DESCRIPTION: A controller that moves an object along a spline path
|
|
|
|
CREATED BY: Rolf Berteig
|
|
|
|
HISTORY: created 13 June 1995
|
|
|
|
*> Copyright (c) 1994, All Rights Reserved.
|
|
**********************************************************************/
|
|
|
|
#include "ctrl.h"
|
|
#include "units.h"
|
|
#include "interpik.h"
|
|
#include "spline3d.h"
|
|
#include "istdplug.h"
|
|
|
|
#define PATH_CONTROL_CNAME GetString(IDS_RB_PATH)
|
|
|
|
#define InterpType() ((ConstVel())?SPLINE_INTERP_NORMALIZED:SPLINE_INTERP_SIMPLE)
|
|
|
|
class PathPosition;
|
|
|
|
class PickMouseProc : public MouseCallBack {
|
|
public:
|
|
PathPosition *cont;
|
|
PickMouseProc(PathPosition *c);
|
|
int proc(
|
|
HWND hWnd,
|
|
int msg,
|
|
int point,
|
|
int flags,
|
|
IPoint2 m );
|
|
};
|
|
|
|
class PickCommandMode : public CommandMode {
|
|
PickMouseProc mouseProc;
|
|
PathPosition *cont;
|
|
public:
|
|
PickCommandMode(PathPosition *c);
|
|
int Class() {return MOTION_COMMAND;}
|
|
virtual int ID() {return 0x4532;}
|
|
MouseCallBack *MouseProc(int *numPoints) {*numPoints = 1; return &mouseProc;}
|
|
ChangeForegroundCallback *ChangeFGProc() {return CHANGE_FG_SELECTED;}
|
|
BOOL ChangeFG( CommandMode *oldMode ) {return FALSE;}
|
|
void EnterMode();
|
|
void ExitMode();
|
|
};
|
|
|
|
class PickPathMode :
|
|
public PickModeCallback,
|
|
public PickNodeCallback {
|
|
public:
|
|
PathPosition *cont;
|
|
|
|
PickPathMode(PathPosition *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;}
|
|
};
|
|
|
|
// flag values
|
|
#define PATHFLAG_FOLLOW (1<<0)
|
|
#define PATHFLAG_BANK (1<<1)
|
|
#define PATHFLAG_CLOSED (1<<2)
|
|
#define PATHFLAG_ALLOWFLIP (1<<3)
|
|
#define PATHFLAG_CONSTVEL (1<<4)
|
|
#define PATHFLAG_FLIP (1<<5)
|
|
#define PATHFLAG_YAXIS (1<<6)
|
|
#define PATHFLAG_ZAXIS (1<<7)
|
|
|
|
class PathPosition : public IPathPosition, public TimeChangeCallback {
|
|
public:
|
|
|
|
int GetNumTargets(){return 0;}
|
|
INode* GetNode(int targetNumber) {return NULL;}
|
|
float GetTargetWeight(int targetNumber){return 0.0f;}
|
|
BOOL SetTargetWeight(int targetNumber, float weight){return FALSE;}
|
|
BOOL AppendTarget(INode *target, float weight=50.0){return FALSE;}
|
|
BOOL DeleteTarget(int selection){return FALSE;}
|
|
|
|
void SetLoop(BOOL l){} // AG added
|
|
BOOL GetLoop(){return FALSE;} // AG added
|
|
void SetRelative(BOOL rel){} // AG added
|
|
BOOL GetRelative(){return FALSE;}
|
|
|
|
|
|
Control *percent;
|
|
INode *path;
|
|
DWORD flags;
|
|
float bankAmount, tracking;
|
|
Point3 curval;
|
|
Quat curRot;
|
|
Interval ivalid;
|
|
|
|
static HWND hPathParams;
|
|
static IObjParam *ip;
|
|
static ICustButton *iPick;
|
|
static ISpinnerControl *iBank, *iTrack, *iPerc;
|
|
//static PickCommandMode *pickMode;
|
|
static PickPathMode *pickMode;
|
|
static PathPosition *editCont;
|
|
|
|
PathPosition(const PathPosition &ctrl);
|
|
PathPosition(BOOL loading=FALSE);
|
|
~PathPosition();
|
|
|
|
BOOL SetPathNode(INode *node);
|
|
void SetBankAmount(float a);
|
|
void SetTracking(float t);
|
|
void SetFollow(BOOL f);
|
|
void SetBank(BOOL b);
|
|
void SetAllowFlip(BOOL f);
|
|
void SetConstVel(BOOL cv);
|
|
void SetFlip(BOOL onOff);
|
|
void SetAxis(int axis);
|
|
|
|
BOOL GetFollow() {return Follow();}
|
|
float GetBankAmount();
|
|
BOOL GetBank() {return Bank();}
|
|
float GetTracking();
|
|
BOOL GetAllowFlip() {return AllowFlip();}
|
|
BOOL GetConstVel() {return ConstVel();}
|
|
|
|
BOOL Follow() {return flags&PATHFLAG_FOLLOW?TRUE:FALSE;}
|
|
BOOL Bank() {return flags&PATHFLAG_BANK?TRUE:FALSE;}
|
|
BOOL AllowFlip() {return flags&PATHFLAG_ALLOWFLIP?TRUE:FALSE;}
|
|
BOOL ConstVel() {return flags&PATHFLAG_CONSTVEL?TRUE:FALSE;}
|
|
BOOL GetFlip() {return flags&PATHFLAG_FLIP?TRUE:FALSE;}
|
|
int GetAxis();
|
|
|
|
void Update(TimeValue t);
|
|
float GetPercent(TimeValue t,Interval &valid,BOOL noClip=FALSE);
|
|
Point3 PointOnPath(TimeValue t, ShapeObject *pathOb, Interval &valid);
|
|
Matrix3 CalcRefFrame(TimeValue t,ShapeObject *pathOb);
|
|
|
|
// Animatable methods
|
|
Class_ID ClassID() { return Class_ID(PATH_CONTROL_CLASS_ID,0); }
|
|
SClass_ID SuperClassID() { return CTRL_POSITION_CLASS_ID; }
|
|
|
|
void GetClassName(TSTR& s) {s = PATH_CONTROL_CNAME;}
|
|
void DeleteThis() {delete this;}
|
|
int IsKeyable() {return 0;}
|
|
|
|
int NumSubs() {return 1;}
|
|
Animatable* SubAnim(int i) {return percent;}
|
|
TSTR SubAnimName(int i) {return GetString(IDS_RB_PERCENT);}
|
|
int SubNumToRefNum(int subNum) {if (subNum==0) return 0; else return -1;}
|
|
|
|
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 2; };
|
|
RefTargetHandle GetReference(int i);
|
|
void SetReference(int i, RefTargetHandle rtarg);
|
|
RefResult NotifyRefChanged(Interval, RefTargetHandle, PartID&, RefMessage);
|
|
|
|
IOResult Save(ISave *isave);
|
|
IOResult Load(ILoad *iload);
|
|
|
|
void RescaleWorldUnits(float f) {}
|
|
|
|
// Control methods
|
|
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) {percent->CommitValue(t);}
|
|
void RestoreValue(TimeValue t) {percent->RestoreValue(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 TimeChanged(TimeValue t);
|
|
};
|
|
|
|
class JointParamsPath : public JointParams {
|
|
public:
|
|
JointParamsPath() : JointParams((DWORD)JNT_POS,1,100.0f) {}
|
|
void SpinnerChange(InterpCtrlUI *ui,WORD id,ISpinnerControl *spin,BOOL interactive);
|
|
};
|
|
|
|
|
|
//********************************************************
|
|
// PATH CONTROL
|
|
//********************************************************
|
|
static Class_ID pathControlClassID(PATH_CONTROL_CLASS_ID,0);
|
|
class PathClassDesc:public ClassDesc {
|
|
public:
|
|
int IsPublic() { return 1; }
|
|
void * Create(BOOL loading) { return new PathPosition(loading); }
|
|
const TCHAR * ClassName() { return PATH_CONTROL_CNAME; }
|
|
SClass_ID SuperClassID() { return CTRL_POSITION_CLASS_ID; }
|
|
Class_ID ClassID() { return pathControlClassID; }
|
|
const TCHAR* Category() { return _T(""); }
|
|
};
|
|
static PathClassDesc pathCD;
|
|
ClassDesc* GetPathCtrlDesc() {return &pathCD;}
|
|
|
|
|
|
PathPosition::PathPosition(const PathPosition &ctrl)
|
|
{
|
|
percent = NULL;
|
|
path = NULL;
|
|
if (ctrl.percent) {
|
|
ReplaceReference(PATHPOS_PATH_REF,ctrl.percent);
|
|
} else {
|
|
ReplaceReference(PATHPOS_PATH_REF,NewDefaultFloatController());
|
|
|
|
percent->AddNewKey(GetAnimStart(),ADDKEY_INTERP);
|
|
percent->AddNewKey(GetAnimEnd(),ADDKEY_INTERP);
|
|
SuspendAnimate();
|
|
AnimateOn();
|
|
float val=0.0f;
|
|
percent->SetValue(GetAnimStart(),&val);
|
|
val = 1.0f;
|
|
percent->SetValue(GetAnimEnd(),&val);
|
|
ResumeAnimate();
|
|
}
|
|
if (ctrl.path) {
|
|
ReplaceReference(PATHPOS_PATH_REF,ctrl.path);
|
|
} else {
|
|
path = NULL;
|
|
}
|
|
flags = 0;
|
|
bankAmount = FromBankUI(0.5f);
|
|
tracking = FromTrackUI(0.5f);
|
|
curval = ctrl.curval;
|
|
curRot = ctrl.curRot;
|
|
ivalid = ctrl.ivalid;
|
|
}
|
|
|
|
PathPosition::PathPosition(BOOL loading)
|
|
{
|
|
percent = NULL;
|
|
path = NULL;
|
|
curval = Point3(0,0,0);
|
|
curRot.Identity();
|
|
flags = 0;
|
|
bankAmount = FromBankUI(0.5f);
|
|
tracking = FromTrackUI(0.5f);
|
|
|
|
if (!loading) {
|
|
ReplaceReference(PATHPOS_PATH_REF,NewDefaultFloatController());
|
|
|
|
percent->AddNewKey(GetAnimStart(),ADDKEY_INTERP);
|
|
percent->AddNewKey(GetAnimEnd(),ADDKEY_INTERP);
|
|
SuspendAnimate();
|
|
AnimateOn();
|
|
float val=0.0f;
|
|
percent->SetValue(GetAnimStart(),&val);
|
|
val = 1.0f;
|
|
percent->SetValue(GetAnimEnd(),&val);
|
|
ResumeAnimate();
|
|
}
|
|
ivalid.SetEmpty();
|
|
}
|
|
|
|
PathPosition::~PathPosition()
|
|
{
|
|
DeleteAllRefsFromMe();
|
|
}
|
|
|
|
RefTargetHandle PathPosition::Clone(RemapDir& remap)
|
|
{
|
|
PathPosition *p = new PathPosition(TRUE);
|
|
|
|
if (path) {
|
|
p->ReplaceReference(PATHPOS_PATH_REF,path);
|
|
} else {
|
|
p->path = NULL;
|
|
}
|
|
|
|
if (percent) {
|
|
p->ReplaceReference(PATHPOS_PATH_REF,remap.CloneRef(percent));
|
|
} else {
|
|
p->ReplaceReference(PATHPOS_PATH_REF,NewDefaultFloatController());
|
|
}
|
|
|
|
p->flags = flags;
|
|
p->bankAmount = bankAmount;
|
|
p->tracking = tracking;
|
|
p->curval = curval;
|
|
p->curRot = curRot;
|
|
p->ivalid.SetEmpty();
|
|
return p;
|
|
}
|
|
|
|
|
|
void PathPosition::Copy(Control *from)
|
|
{
|
|
if (from->ClassID()==ClassID()) {
|
|
PathPosition *ctrl = (PathPosition*)from;
|
|
ReplaceReference(PATHPOS_PATH_REF,ctrl->percent);
|
|
ReplaceReference(PATHPOS_PATH_REF,ctrl->path);
|
|
curval = ctrl->curval;
|
|
curRot = ctrl->curRot;
|
|
ivalid = ctrl->ivalid;
|
|
} else {
|
|
from->GetValue(0,&curval,ivalid);
|
|
}
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
|
|
float PathPosition::GetPercent(TimeValue t,Interval &valid,BOOL noClip)
|
|
{
|
|
float per;
|
|
percent->GetValue(t,&per,valid,CTRL_ABSOLUTE);
|
|
if (noClip) return per;
|
|
if (per < 0.0f || per > 1.0f || (flags&PATHFLAG_CLOSED)) {
|
|
per = (float)fmod(per,1.0f);
|
|
}
|
|
if (per<0.0f) per = 1.0f + per;
|
|
return per;
|
|
}
|
|
|
|
Point3 PathPosition::PointOnPath(TimeValue t, ShapeObject *pathOb, Interval &valid)
|
|
{
|
|
return pathOb->InterpCurve3D(t, 0, GetPercent(t,valid), InterpType()) *
|
|
path->GetObjTMAfterWSM(t,&valid);
|
|
}
|
|
|
|
// Reference frame is calculated as the following:
|
|
//
|
|
// X axis = tangent(t)
|
|
// Y axis = WorldZ cross X
|
|
// Z axis = X cross Y
|
|
//
|
|
// or if the 'Allow Upside Down' option us selected:
|
|
//
|
|
// X axis = tangent(t)
|
|
// Z axis = path normal cross X
|
|
// Y axis = Z cross X
|
|
//
|
|
// If roll is on then the reference frame is rotated about
|
|
// the tangent by rollamount * curviture.
|
|
|
|
#define DU 0.001f
|
|
#define BDU 0.01f
|
|
#define NUMSTEPS 5
|
|
|
|
Matrix3 PathPosition::CalcRefFrame(TimeValue t,ShapeObject *pathOb)
|
|
{
|
|
Interval valid;
|
|
Matrix3 tm(1), otm = path->GetObjTMAfterWSM(t);
|
|
float u = GetPercent(t,valid);
|
|
|
|
Point3 pt0, pt1;
|
|
pt0 = pathOb->InterpCurve3D(t, 0, u-DU, InterpType()) * otm;
|
|
pt1 = pathOb->InterpCurve3D(t, 0, u+DU, InterpType()) * otm;
|
|
|
|
Point3 pathNorm(0,0,0);
|
|
if (AllowFlip()) {
|
|
// Compute the normal to the plane of the path by sampling points on the path
|
|
#define NUM_SAMPLES 20
|
|
Point3 v[NUM_SAMPLES], cent(0,0,0);
|
|
for (int i=0; i<NUM_SAMPLES; i++) {
|
|
v[i] = pathOb->InterpCurve3D(t, 0, float(i)/float(NUM_SAMPLES), InterpType()) * otm;
|
|
cent += v[i];
|
|
}
|
|
cent /= float(NUM_SAMPLES);
|
|
for (i=1; i<NUM_SAMPLES; i++) {
|
|
pathNorm += Normalize((v[i]-cent)^(v[i-1]-cent));
|
|
}
|
|
pathNorm = Normalize(pathNorm);
|
|
}
|
|
|
|
// X
|
|
tm.SetRow(0,Normalize(pt1-pt0));
|
|
|
|
if (GetFlip()) tm.SetRow(0,-tm.GetRow(0));
|
|
|
|
if (AllowFlip()) {
|
|
// Choose Z in the plane of the path
|
|
// Z
|
|
tm.SetRow(2,Normalize(tm.GetRow(0)^pathNorm));
|
|
|
|
// Y
|
|
tm.SetRow(1,tm.GetRow(2)^tm.GetRow(0));
|
|
} else {
|
|
// Choose Y in the world XY plane
|
|
// Y
|
|
tm.SetRow(1,Normalize(Point3(0,0,1)^tm.GetRow(0)));
|
|
|
|
// Z
|
|
tm.SetRow(2,tm.GetRow(0)^tm.GetRow(1));
|
|
}
|
|
|
|
// swap axis around
|
|
Point3 tmp;
|
|
switch (GetAxis()) {
|
|
case 1:
|
|
tmp = tm.GetRow(0);
|
|
tm.SetRow(0,-tm.GetRow(1));
|
|
tm.SetRow(1,tmp);
|
|
break;
|
|
case 2:
|
|
tmp = tm.GetRow(0);
|
|
tm.SetRow(0,-tm.GetRow(2));
|
|
tm.SetRow(2,tmp);
|
|
break;
|
|
}
|
|
|
|
|
|
if (Bank()) {
|
|
// Average over NUMSTEPS samples
|
|
Point3 pt0, pt1, pt2, v0, v1;
|
|
float cv = 0.0f;
|
|
u -= float(NUMSTEPS/2+1)*tracking;
|
|
|
|
if (!pathOb->CurveClosed(t,0)) {
|
|
if (u+(NUMSTEPS+2)*tracking > 1.0f) u = 1.0f - (NUMSTEPS+2)*tracking;
|
|
if (u<0.0f) u=0.0f;
|
|
}
|
|
|
|
pt1 = pathOb->InterpCurve3D(t, 0, u, InterpType()) * otm;
|
|
u += tracking;
|
|
pt2 = pathOb->InterpCurve3D(t, 0, u, InterpType()) * otm;
|
|
u += tracking;
|
|
for (int i=0; i<NUMSTEPS; i++) {
|
|
pt0 = pt1;
|
|
pt1 = pt2;
|
|
if (!pathOb->CurveClosed(t,0) && u>1.0f) {
|
|
//pt2 += pt2 - pt1;
|
|
break;
|
|
} else {
|
|
u = (float)fmod(u,1.0f);
|
|
pt2 = pathOb->InterpCurve3D(t, 0, u, InterpType()) * otm;
|
|
}
|
|
v0 = Normalize(pt2-pt1);
|
|
v1 = Normalize(pt1-pt0);
|
|
v0.z = v1.z = 0.0f; // remove Z component.
|
|
cv += (v0^v1).z * bankAmount / Length(pt1-pt0);
|
|
u += tracking;
|
|
}
|
|
if (i) {
|
|
if (GetFlip()) cv = -cv;
|
|
switch (GetAxis()) {
|
|
case 0: tm.PreRotateX(cv/float(i)); break;
|
|
case 1: tm.PreRotateY(cv/float(i)); break;
|
|
case 2: tm.PreRotateZ(cv/float(i)); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return tm;
|
|
}
|
|
|
|
void PathPosition::Update(TimeValue t)
|
|
{
|
|
ShapeObject *pathOb = NULL;
|
|
ivalid = FOREVER;
|
|
|
|
if (path) {
|
|
ObjectState os = path->EvalWorldState(t);
|
|
if (os.obj->SuperClassID()==SHAPE_CLASS_ID) {
|
|
pathOb = (ShapeObject*)os.obj;
|
|
if (!pathOb->NumberOfCurves()) {
|
|
pathOb = NULL;
|
|
}
|
|
|
|
}
|
|
ivalid &= os.Validity(t);
|
|
}
|
|
if (!pathOb) return;
|
|
curval = PointOnPath(t,pathOb,ivalid);
|
|
|
|
if (Follow()) {
|
|
curRot = Quat(CalcRefFrame(t,pathOb));
|
|
} else {
|
|
curRot.Identity();
|
|
}
|
|
|
|
if (pathOb->CurveClosed(t,0)) {
|
|
flags |= PATHFLAG_CLOSED;
|
|
} else {
|
|
flags &= ~PATHFLAG_CLOSED;
|
|
}
|
|
|
|
if (ivalid.Empty()) ivalid.SetInstant(t);
|
|
}
|
|
|
|
void PathPosition::GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method)
|
|
{
|
|
if (!ivalid.InInterval(t)) {
|
|
Update(t);
|
|
}
|
|
valid &= ivalid;
|
|
|
|
if (method==CTRL_RELATIVE) {
|
|
Matrix3 *mat = (Matrix3*)val;
|
|
if (Follow()) {
|
|
curRot.MakeMatrix(*mat);
|
|
}
|
|
mat->SetTrans(curval);
|
|
} else {
|
|
*((Point3*)val) = curval;
|
|
}
|
|
}
|
|
|
|
|
|
RefTargetHandle PathPosition::GetReference(int i)
|
|
{
|
|
if (i==PATHPOS_PATH_REF) {
|
|
return percent;
|
|
} else {
|
|
return path;
|
|
}
|
|
}
|
|
|
|
void PathPosition::SetReference(int i, RefTargetHandle rtarg)
|
|
{
|
|
if (i==PATHPOS_PATH_REF) {
|
|
percent = (Control*)rtarg;
|
|
} else {
|
|
path = (INode*)rtarg;
|
|
}
|
|
}
|
|
|
|
RefResult PathPosition::NotifyRefChanged(
|
|
Interval iv,
|
|
RefTargetHandle hTarg,
|
|
PartID& partID,
|
|
RefMessage msg)
|
|
{
|
|
switch (msg) {
|
|
case REFMSG_CHANGE:
|
|
ivalid.SetEmpty();
|
|
if (hPathParams &&
|
|
GetWindowLong(hPathParams,GWL_USERDATA)==(LONG)this &&
|
|
ip) {
|
|
TimeChanged(ip->GetTime());
|
|
}
|
|
break;
|
|
case REFMSG_TARGET_DELETED:
|
|
if (hTarg == path) {
|
|
path = NULL;
|
|
if (hPathParams && GetWindowLong(hPathParams,GWL_USERDATA)==(LONG)this) {
|
|
SetWindowText(GetDlgItem(hPathParams,IDC_PATHNAME),
|
|
GetString(IDS_RB_NONE));
|
|
}
|
|
} else
|
|
if (hTarg == percent) {
|
|
percent = NULL;
|
|
}
|
|
break;
|
|
}
|
|
return REF_SUCCEED;
|
|
}
|
|
|
|
BOOL PathPosition::AssignController(Animatable *control,int subAnim)
|
|
{
|
|
ReplaceReference(PATHPOS_PATH_REF,(RefTargetHandle)control);
|
|
NotifyDependents(FOREVER,0,REFMSG_CONTROLREF_CHANGE,TREE_VIEW_CLASS_ID,FALSE);
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
class PickPathRestore : public RestoreObj {
|
|
public:
|
|
PathPosition *cont;
|
|
PickPathRestore(PathPosition *c) {cont=c;}
|
|
void Restore(int isUndo) {
|
|
if (cont->editCont == cont && cont->hPathParams) {
|
|
SetWindowText(GetDlgItem(cont->hPathParams,IDC_PATHNAME),
|
|
GetString(IDS_RB_NONE));
|
|
}
|
|
}
|
|
void Redo() {
|
|
if (cont->editCont == cont && cont->hPathParams && cont->path) {
|
|
SetWindowText(GetDlgItem(cont->hPathParams,IDC_PATHNAME),
|
|
cont->path->GetName());
|
|
}
|
|
}
|
|
TSTR Description() { return TSTR(_T("Pick Path")); }
|
|
};
|
|
|
|
BOOL PathPosition::SetPathNode(INode *node)
|
|
{
|
|
if (node->TestForLoop(FOREVER,this)==REF_SUCCEED) {
|
|
theHold.Begin();
|
|
ReplaceReference(PATHPOS_PATH_REF,(RefTargetHandle)node);
|
|
theHold.Put(new PickPathRestore(this));
|
|
theHold.Accept(GetString(IDS_RB_PICKPATH));
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void PathPosition::SetTracking(float t)
|
|
{
|
|
tracking = t;
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
float PathPosition::GetTracking()
|
|
{
|
|
return tracking;
|
|
}
|
|
|
|
void PathPosition::SetBankAmount(float a)
|
|
{
|
|
bankAmount = a;
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
float PathPosition::GetBankAmount()
|
|
{
|
|
return bankAmount;
|
|
}
|
|
|
|
void PathPosition::SetFollow(BOOL f)
|
|
{
|
|
if (f) {
|
|
flags |= PATHFLAG_FOLLOW;
|
|
} else {
|
|
flags &= ~PATHFLAG_FOLLOW;
|
|
}
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
void PathPosition::SetAllowFlip(BOOL f)
|
|
{
|
|
if (f) {
|
|
flags |= PATHFLAG_ALLOWFLIP;
|
|
} else {
|
|
flags &= ~PATHFLAG_ALLOWFLIP;
|
|
}
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
void PathPosition::SetConstVel(BOOL cv)
|
|
{
|
|
if (cv) {
|
|
flags |= PATHFLAG_CONSTVEL;
|
|
} else {
|
|
flags &= ~PATHFLAG_CONSTVEL;
|
|
}
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
void PathPosition::SetBank(BOOL b)
|
|
{
|
|
if (b) {
|
|
flags |= PATHFLAG_BANK;
|
|
} else {
|
|
flags &= ~PATHFLAG_BANK;
|
|
}
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
void PathPosition::SetFlip(BOOL onOff)
|
|
{
|
|
if (onOff) flags |= PATHFLAG_FLIP;
|
|
else flags &= ~PATHFLAG_FLIP;
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
void PathPosition::SetAxis(int axis)
|
|
{
|
|
switch (axis) {
|
|
case 0:
|
|
flags &= ~PATHFLAG_YAXIS;
|
|
flags &= ~PATHFLAG_ZAXIS;
|
|
break;
|
|
case 1:
|
|
flags &= ~PATHFLAG_ZAXIS;
|
|
flags |= PATHFLAG_YAXIS;
|
|
break;
|
|
case 2:
|
|
flags |= PATHFLAG_ZAXIS;
|
|
flags &= ~PATHFLAG_YAXIS;
|
|
break;
|
|
}
|
|
ivalid.SetEmpty();
|
|
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
}
|
|
|
|
int PathPosition::GetAxis()
|
|
{
|
|
if (flags&PATHFLAG_YAXIS) return 1;
|
|
if (flags&PATHFLAG_ZAXIS) return 2;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void PathPosition::TimeChanged(TimeValue t)
|
|
{
|
|
if (iPerc) {
|
|
iPerc->SetValue(100.0f*GetPercent(t,FOREVER,TRUE),FALSE);
|
|
if (percent && percent->IsKeyAtTime(t,0)) {
|
|
iPerc->SetKeyBrackets(TRUE);
|
|
} else {
|
|
iPerc->SetKeyBrackets(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
// PathPosition UI
|
|
|
|
HWND PathPosition::hPathParams = NULL;
|
|
IObjParam *PathPosition::ip = NULL;
|
|
ICustButton *PathPosition::iPick = NULL;
|
|
//PickCommandMode *PathPosition::pickMode = NULL;
|
|
PickPathMode *PathPosition::pickMode = NULL;
|
|
ISpinnerControl *PathPosition::iBank = NULL;
|
|
ISpinnerControl *PathPosition::iTrack = NULL;
|
|
ISpinnerControl *PathPosition::iPerc = NULL;
|
|
PathPosition *PathPosition::editCont = NULL;
|
|
|
|
BOOL PickPathMode::Filter(INode *node)
|
|
{
|
|
if (node) {
|
|
ObjectState os = node->EvalWorldState(0);
|
|
if (os.obj->SuperClassID()==SHAPE_CLASS_ID) {
|
|
SetCursor(cont->ip->GetSysCursor(SYSCUR_SELECT));
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL PickPathMode::HitTest(
|
|
IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags)
|
|
{
|
|
INode *node = cont->ip->PickNode(hWnd,m);
|
|
if (node) {
|
|
ObjectState os = node->EvalWorldState(0);
|
|
if (os.obj->SuperClassID()==SHAPE_CLASS_ID) {
|
|
SetCursor(cont->ip->GetSysCursor(SYSCUR_SELECT));
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL PickPathMode::Pick(IObjParam *ip,ViewExp *vpt)
|
|
{
|
|
INode *node = vpt->GetClosestHit();
|
|
if (node) {
|
|
ObjectState os = node->EvalWorldState(0);
|
|
if (os.obj->SuperClassID()==SHAPE_CLASS_ID) {
|
|
if (cont->SetPathNode(node)) {
|
|
SetWindowText(GetDlgItem(cont->hPathParams,IDC_PATHNAME),
|
|
node->GetName());
|
|
|
|
cont->ip->SetStdCommandMode(CID_OBJMOVE);
|
|
cont->ip->RedrawViews(cont->ip->GetTime());
|
|
} else {
|
|
TSTR buf1 = GetString(IDS_RB_PATHCONTROLLER);
|
|
TSTR buf2 = GetString(IDS_RB_ILLEGALPATH);
|
|
MessageBox(ip->GetMAXHWnd(),buf2,buf1,MB_OK|MB_ICONEXCLAMATION);
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void PickPathMode::EnterMode(IObjParam *ip)
|
|
{cont->iPick->SetCheck(TRUE);}
|
|
|
|
void PickPathMode::ExitMode(IObjParam *ip)
|
|
{cont->iPick->SetCheck(FALSE);}
|
|
|
|
PickMouseProc::PickMouseProc(PathPosition *c) {cont=c;}
|
|
PickCommandMode::PickCommandMode(PathPosition *c) : mouseProc(c) {cont = c;}
|
|
void PickCommandMode::EnterMode() {cont->iPick->SetCheck(TRUE);}
|
|
void PickCommandMode::ExitMode() {cont->iPick->SetCheck(FALSE);}
|
|
|
|
int PickMouseProc::proc(
|
|
HWND hWnd,
|
|
int msg,
|
|
int point,
|
|
int flags,
|
|
IPoint2 m )
|
|
{
|
|
switch (msg) {
|
|
case MOUSE_POINT: {
|
|
INode *node = cont->ip->PickNode(hWnd,m);
|
|
if (node) {
|
|
ObjectState os = node->EvalWorldState(0);
|
|
if (os.obj->SuperClassID()==SHAPE_CLASS_ID) {
|
|
if (cont->SetPathNode(node)) {
|
|
SetWindowText(GetDlgItem(cont->hPathParams,IDC_PATHNAME),
|
|
node->GetName());
|
|
|
|
cont->ip->SetStdCommandMode(CID_OBJMOVE);
|
|
cont->ip->RedrawViews(cont->ip->GetTime());
|
|
} else {
|
|
TSTR buf1 = GetString(IDS_RB_PATHCONTROLLER);
|
|
TSTR buf2 = GetString(IDS_RB_ILLEGALPATH);
|
|
MessageBox(hWnd,buf2,buf1,MB_OK|MB_ICONEXCLAMATION);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MOUSE_FREEMOVE: {
|
|
INode *node = cont->ip->PickNode(hWnd,m);
|
|
if (node) {
|
|
ObjectState os = node->EvalWorldState(0);
|
|
if (os.obj->SuperClassID()==SHAPE_CLASS_ID) {
|
|
SetCursor(cont->ip->GetSysCursor(SYSCUR_SELECT));
|
|
return TRUE;
|
|
}
|
|
}
|
|
SetCursor(LoadCursor(NULL,IDC_ARROW));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void SetControlStates(HWND hDlg)
|
|
{
|
|
BOOL follow = IsDlgButtonChecked(hDlg,IDC_PATH_FOLLOW);
|
|
BOOL bank = IsDlgButtonChecked(hDlg,IDC_PATH_BANK);
|
|
|
|
if (follow) {
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_BANK),TRUE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_ALLOWFLIP),TRUE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_X),TRUE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_Y),TRUE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_Z),TRUE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_FLIP),TRUE);
|
|
} else {
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_BANK),FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_ALLOWFLIP),FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_X),FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_Y),FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_Z),FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_PATH_FLIP),FALSE);
|
|
}
|
|
|
|
if (bank && follow) {
|
|
EnableWindow(GetDlgItem(hDlg,IDC_FOLLOW_BANKLABEL),TRUE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_FOLLOW_BANKSPIN),TRUE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_FOLLOW_TRACKLABEL),TRUE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_FOLLOW_TRACKSPIN),TRUE);
|
|
} else {
|
|
EnableWindow(GetDlgItem(hDlg,IDC_FOLLOW_BANKLABEL),FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_FOLLOW_BANKSPIN),FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_FOLLOW_TRACKLABEL),FALSE);
|
|
EnableWindow(GetDlgItem(hDlg,IDC_FOLLOW_TRACKSPIN),FALSE);
|
|
}
|
|
}
|
|
|
|
BOOL CALLBACK PathParamDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
PathPosition *co = (PathPosition*)GetWindowLong(hDlg,GWL_USERDATA);
|
|
if ( !co && message != WM_INITDIALOG ) return FALSE;
|
|
switch ( message ) {
|
|
case WM_INITDIALOG:
|
|
co = (PathPosition*)lParam;
|
|
SetWindowLong( hDlg, GWL_USERDATA, (LONG)co );
|
|
if (co->path) {
|
|
SetWindowText(GetDlgItem(hDlg,IDC_PATHNAME),
|
|
co->path->GetName());
|
|
} else {
|
|
SetWindowText(GetDlgItem(hDlg,IDC_PATHNAME),
|
|
GetString(IDS_RB_NONE));
|
|
}
|
|
co->iPick = GetICustButton(GetDlgItem(hDlg,IDC_PICKPATH));
|
|
co->iPick->SetType(CBT_CHECK);
|
|
co->iPick->SetHighlightColor(GREEN_WASH);
|
|
|
|
co->iBank = GetISpinner(GetDlgItem(hDlg,IDC_FOLLOW_BANKSPIN));
|
|
co->iBank->SetLimits(-999999,999999,FALSE);
|
|
co->iBank->SetScale(0.01f);
|
|
co->iBank->LinkToEdit(GetDlgItem(hDlg,IDC_FOLLOW_BANK),EDITTYPE_FLOAT);
|
|
co->iBank->SetValue(ToBankUI(co->bankAmount),FALSE);
|
|
|
|
co->iTrack = GetISpinner(GetDlgItem(hDlg,IDC_FOLLOW_TRACKSPIN));
|
|
co->iTrack->SetLimits(0.01f,10.0f,FALSE);
|
|
co->iTrack->SetScale(0.01f);
|
|
co->iTrack->LinkToEdit(GetDlgItem(hDlg,IDC_FOLLOW_TRACK),EDITTYPE_FLOAT);
|
|
co->iTrack->SetValue(ToTrackUI(co->tracking),FALSE);
|
|
|
|
co->iPerc = GetISpinner(GetDlgItem(hDlg,IDC_PATHPERCENTSPIN));
|
|
co->iPerc->SetLimits(float(-999999999),float(999999999),FALSE);
|
|
co->iPerc->SetScale(0.1f);
|
|
co->iPerc->LinkToEdit(GetDlgItem(hDlg,IDC_PATHPERCENT),EDITTYPE_FLOAT);
|
|
co->iPerc->SetValue(100.0f*co->GetPercent(co->ip->GetTime(),FOREVER,TRUE),FALSE);
|
|
|
|
if (co->Follow()) {
|
|
CheckDlgButton(hDlg,IDC_PATH_FOLLOW,TRUE);
|
|
} else {
|
|
CheckDlgButton(hDlg,IDC_PATH_FOLLOW,FALSE);
|
|
}
|
|
|
|
if (co->Bank()) {
|
|
CheckDlgButton(hDlg,IDC_PATH_BANK,TRUE);
|
|
} else {
|
|
CheckDlgButton(hDlg,IDC_PATH_BANK,FALSE);
|
|
}
|
|
|
|
if (co->AllowFlip()) {
|
|
CheckDlgButton(hDlg,IDC_PATH_ALLOWFLIP,TRUE);
|
|
} else {
|
|
CheckDlgButton(hDlg,IDC_PATH_ALLOWFLIP,FALSE);
|
|
}
|
|
|
|
CheckDlgButton(hDlg,IDC_PATH_CONSTVEL,co->ConstVel());
|
|
|
|
CheckDlgButton(hDlg,IDC_PATH_FLIP,co->GetFlip());
|
|
CheckDlgButton(hDlg,IDC_PATH_X,co->GetAxis()==0);
|
|
CheckDlgButton(hDlg,IDC_PATH_Y,co->GetAxis()==1);
|
|
CheckDlgButton(hDlg,IDC_PATH_Z,co->GetAxis()==2);
|
|
|
|
SetControlStates(hDlg);
|
|
return TRUE;
|
|
|
|
case WM_LBUTTONDOWN:case WM_LBUTTONUP: case WM_MOUSEMOVE:
|
|
co->ip->RollupMouseMessage(hDlg,message,wParam,lParam);
|
|
return FALSE;
|
|
|
|
case CC_SPINNER_BUTTONDOWN:
|
|
if (LOWORD(wParam)==IDC_PATHPERCENTSPIN) {
|
|
theHold.Begin();
|
|
}
|
|
break;
|
|
|
|
case WM_CUSTEDIT_ENTER:
|
|
case CC_SPINNER_BUTTONUP:
|
|
if (LOWORD(wParam)==IDC_PATHPERCENTSPIN) {
|
|
if (HIWORD(wParam)) {
|
|
theHold.Accept(GetString(IDS_RB_SETPERCENT));
|
|
} else {
|
|
theHold.Cancel();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CC_SPINNER_CHANGE:
|
|
switch (LOWORD(wParam)) {
|
|
case IDC_PATHPERCENTSPIN: {
|
|
float v = co->iPerc->GetFVal()/100.0f;
|
|
co->percent->SetValue(
|
|
co->ip->GetTime(),&v);
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
}
|
|
|
|
case IDC_FOLLOW_BANKSPIN:
|
|
co->SetBankAmount(FromBankUI(co->iBank->GetFVal()));
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
|
|
case IDC_FOLLOW_TRACKSPIN:
|
|
co->SetTracking(FromTrackUI(co->iTrack->GetFVal()));
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDC_PICKPATH:
|
|
//co->ip->SetCommandMode(co->pickMode);
|
|
co->ip->SetPickMode(co->pickMode);
|
|
break;
|
|
|
|
case IDC_PATH_X:
|
|
co->SetAxis(0);
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
case IDC_PATH_Y:
|
|
co->SetAxis(1);
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
case IDC_PATH_Z:
|
|
co->SetAxis(2);
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
|
|
case IDC_PATH_FLIP:
|
|
co->SetFlip(IsDlgButtonChecked(hDlg,IDC_PATH_FLIP));
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
|
|
case IDC_PATH_CONSTVEL:
|
|
co->SetConstVel(IsDlgButtonChecked(hDlg,IDC_PATH_CONSTVEL));
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
|
|
case IDC_PATH_FOLLOW:
|
|
co->SetFollow(IsDlgButtonChecked(hDlg,IDC_PATH_FOLLOW));
|
|
SetControlStates(hDlg);
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
|
|
case IDC_PATH_ALLOWFLIP:
|
|
co->SetAllowFlip(IsDlgButtonChecked(hDlg,IDC_PATH_ALLOWFLIP));
|
|
SetControlStates(hDlg);
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
|
|
case IDC_PATH_BANK:
|
|
co->SetBank(IsDlgButtonChecked(hDlg,IDC_PATH_BANK));
|
|
SetControlStates(hDlg);
|
|
co->ip->RedrawViews(co->ip->GetTime());
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void PathPosition::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev )
|
|
{
|
|
this->ip = ip;
|
|
editCont = this;
|
|
if (flags&BEGIN_EDIT_HIERARCHY) {
|
|
|
|
// No IK if follow is on
|
|
if (Follow()) return;
|
|
|
|
JointParamsPath *jp = (JointParamsPath*)GetProperty(PROPID_JOINTPARAMS);
|
|
InterpCtrlUI *ui;
|
|
|
|
if (!jp) {
|
|
jp = new JointParamsPath();
|
|
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_PATHJOINTPARAMS),
|
|
(int (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long))JointParamDlgProc,
|
|
GetString(IDS_RB_PATHJOINTPARAMS),
|
|
(LPARAM)new JointDlgData(ui,jp),f);
|
|
}
|
|
|
|
SetProperty(PROPID_INTERPUI,ui);
|
|
} else {
|
|
//pickMode = new PickCommandMode(this);
|
|
pickMode = new PickPathMode(this);
|
|
hPathParams = ip->AddRollupPage(
|
|
hInstance,
|
|
MAKEINTRESOURCE(IDD_PATHPARAMS),
|
|
PathParamDialogProc,
|
|
GetString(IDS_RB_PATHPARAMS),
|
|
(LPARAM)this );
|
|
ip->RegisterDlgWnd(hPathParams);
|
|
ip->RegisterTimeChangeCallback(this);
|
|
}
|
|
SetWindowLong( hPathParams, GWL_USERDATA, (LONG)this );
|
|
}
|
|
|
|
void PathPosition::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next )
|
|
{
|
|
editCont = NULL;
|
|
if (next && next->ClassID()==ClassID() && !hPathParams) return;
|
|
|
|
if (hPathParams) {
|
|
ip->UnRegisterTimeChangeCallback(this);
|
|
ip->ClearPickMode();
|
|
delete pickMode;
|
|
pickMode = NULL;
|
|
ip->UnRegisterDlgWnd(hPathParams);
|
|
ip->DeleteRollupPage(hPathParams);
|
|
hPathParams = 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int PathPosition::SetProperty(ULONG id, void *data)
|
|
{
|
|
if (id==PROPID_JOINTPARAMS) {
|
|
if (!data) {
|
|
int index = aprops.FindProperty(id);
|
|
if (index>=0) {
|
|
aprops.Delete(index,1);
|
|
}
|
|
} else {
|
|
JointParamsPath *jp = (JointParamsPath*)GetProperty(id);
|
|
if (jp) {
|
|
*jp = *((JointParamsPath*)data);
|
|
delete (JointParamsPath*)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* PathPosition::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 JOINTPARAMPATH_CHUNK 0x1001
|
|
#define FOLLOW_CHUNK 0x1002
|
|
#define BANK_CHUNK 0x1003
|
|
#define BANKAMOUNT_CHUNK 0x1004
|
|
#define TRACKING_CHUNK 0x1005
|
|
#define ALLOWFLIP_CHUNK 0x1006
|
|
#define CONSTVEL_CHUNK 0x1007
|
|
#define AXIS_CHUNK 0x1008
|
|
#define FLIP_CHUNK 0x1009
|
|
|
|
IOResult PathPosition::Save(ISave *isave)
|
|
{
|
|
JointParamsPath *jp = (JointParamsPath*)GetProperty(PROPID_JOINTPARAMS);
|
|
ULONG nb;
|
|
|
|
if (jp) {
|
|
isave->BeginChunk(JOINTPARAMPATH_CHUNK);
|
|
jp->Save(isave);
|
|
isave->EndChunk();
|
|
}
|
|
|
|
if (Follow()) {
|
|
isave->BeginChunk(FOLLOW_CHUNK);
|
|
isave->EndChunk();
|
|
}
|
|
if (Bank()) {
|
|
isave->BeginChunk(BANK_CHUNK);
|
|
isave->EndChunk();
|
|
}
|
|
if (AllowFlip()) {
|
|
isave->BeginChunk(ALLOWFLIP_CHUNK);
|
|
isave->EndChunk();
|
|
}
|
|
if (ConstVel()) {
|
|
isave->BeginChunk(CONSTVEL_CHUNK);
|
|
isave->EndChunk();
|
|
}
|
|
if (GetFlip()) {
|
|
isave->BeginChunk(FLIP_CHUNK);
|
|
isave->EndChunk();
|
|
}
|
|
|
|
int axis = GetAxis();
|
|
isave->BeginChunk(AXIS_CHUNK);
|
|
isave->Write(&axis,sizeof(axis),&nb);
|
|
isave->EndChunk();
|
|
|
|
isave->BeginChunk(BANKAMOUNT_CHUNK);
|
|
isave->Write(&bankAmount,sizeof(bankAmount),&nb);
|
|
isave->EndChunk();
|
|
|
|
isave->BeginChunk(TRACKING_CHUNK);
|
|
isave->Write(&tracking,sizeof(tracking),&nb);
|
|
isave->EndChunk();
|
|
|
|
return IO_OK;
|
|
}
|
|
|
|
IOResult PathPosition::Load(ILoad *iload)
|
|
{
|
|
ULONG nb;
|
|
IOResult res = IO_OK;
|
|
while (IO_OK==(res=iload->OpenChunk())) {
|
|
switch (iload->CurChunkID()) {
|
|
case FLIP_CHUNK:
|
|
flags |= PATHFLAG_FLIP;
|
|
break;
|
|
|
|
case AXIS_CHUNK: {
|
|
int axis;
|
|
res=iload->Read(&axis,sizeof(axis),&nb);
|
|
SetAxis(axis);
|
|
break;
|
|
}
|
|
|
|
case CONSTVEL_CHUNK:
|
|
flags |= PATHFLAG_CONSTVEL;
|
|
break;
|
|
|
|
case FOLLOW_CHUNK:
|
|
flags |= PATHFLAG_FOLLOW;
|
|
break;
|
|
|
|
case BANK_CHUNK:
|
|
flags |= PATHFLAG_BANK;
|
|
break;
|
|
|
|
case ALLOWFLIP_CHUNK:
|
|
flags |= PATHFLAG_ALLOWFLIP;
|
|
break;
|
|
|
|
case BANKAMOUNT_CHUNK:
|
|
res=iload->Read(&bankAmount,sizeof(bankAmount),&nb);
|
|
break;
|
|
|
|
case TRACKING_CHUNK:
|
|
res=iload->Read(&tracking,sizeof(tracking),&nb);
|
|
break;
|
|
|
|
case JOINTPARAMPATH_CHUNK: {
|
|
JointParamsPath *jp = new JointParamsPath;
|
|
jp->Load(iload);
|
|
SetProperty(PROPID_JOINTPARAMS,jp);
|
|
break;
|
|
}
|
|
}
|
|
iload->CloseChunk();
|
|
if (res!=IO_OK) return res;
|
|
}
|
|
return IO_OK;
|
|
}
|
|
|
|
//-------------------------------------------------------------
|
|
//
|
|
// IK
|
|
//
|
|
|
|
|
|
void PathPosition::EnumIKParams(IKEnumCallback &callback)
|
|
{
|
|
JointParamsPath *jp = (JointParamsPath*)GetProperty(PROPID_JOINTPARAMS);
|
|
if (jp && jp->Active(0) && !Follow()) {
|
|
callback.proc(this,0);
|
|
}
|
|
}
|
|
|
|
BOOL PathPosition::CompDeriv(TimeValue t,Matrix3& ptm,IKDeriv& derivs,DWORD flags)
|
|
{
|
|
JointParamsPath *jp = (JointParamsPath*)GetProperty(PROPID_JOINTPARAMS);
|
|
if (!jp || !jp->Active(0) || Follow()) return FALSE;
|
|
Point3 zero(0,0,0);
|
|
if (path) {
|
|
ObjectState os = path->EvalWorldState(t);
|
|
if (os.obj->SuperClassID()==SHAPE_CLASS_ID) {
|
|
ShapeObject *pathOb = (ShapeObject*)os.obj;
|
|
if (pathOb->NumberOfCurves()) {
|
|
Point3 p0,p1,d;
|
|
float per,dt=0.01f;
|
|
percent->GetValue(t,&per,ivalid,CTRL_ABSOLUTE);
|
|
per = (float)fmod(per,1.0f);
|
|
if (per<0.0f) per = 1.0f + per;
|
|
if (dt+per>1.0f) dt = -dt;
|
|
p0 = pathOb->InterpCurve3D(t,0,per, InterpType());
|
|
p1 = pathOb->InterpCurve3D(t,0,per+dt, InterpType());
|
|
if (os.GetTM()) {
|
|
p0 = p0 * (*os.GetTM());
|
|
p1 = p1 * (*os.GetTM());
|
|
}
|
|
d = (p1-p0)/dt;
|
|
for (int j=0; j<derivs.NumEndEffectors(); j++) {
|
|
if (flags&POSITION_DERIV) {
|
|
derivs.DP(d,j);
|
|
}
|
|
if (flags&ROTATION_DERIV) {
|
|
derivs.DR(zero,j);
|
|
}
|
|
}
|
|
|
|
derivs.NextDOF();
|
|
ptm.SetTrans(p0);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int j=0; j<derivs.NumEndEffectors(); j++) {
|
|
if (flags&POSITION_DERIV) {
|
|
derivs.DP(zero,j);
|
|
}
|
|
if (flags&ROTATION_DERIV) {
|
|
derivs.DR(zero,j);
|
|
}
|
|
}
|
|
derivs.NextDOF();
|
|
return FALSE;
|
|
}
|
|
|
|
float PathPosition::IncIKParam(TimeValue t,int index,float delta)
|
|
{
|
|
JointParamsPath *jp = (JointParamsPath*)GetProperty(PROPID_JOINTPARAMS);
|
|
float v=0.0f;
|
|
BOOL gotV=FALSE;
|
|
if (fabs(delta) > 0.01f) {
|
|
if (delta<0) delta = -0.01f;
|
|
else delta = 0.01f;
|
|
}
|
|
if (jp) {
|
|
if (jp->Limited(0)) {
|
|
Interval valid;
|
|
percent->GetValue(t,&v,valid,CTRL_ABSOLUTE);
|
|
gotV = TRUE;
|
|
}
|
|
delta = jp->ConstrainInc(0,v,delta);
|
|
}
|
|
// If the path is not closed, do not let it go off the end.
|
|
if (!(flags&PATHFLAG_CLOSED)) {
|
|
if (!gotV) {
|
|
Interval valid;
|
|
percent->GetValue(t,&v,valid,CTRL_ABSOLUTE);
|
|
}
|
|
if (v + delta < 0.0f) delta = -v;
|
|
if (v + delta > 1.0f) delta = 1.0f-v;
|
|
}
|
|
percent->SetValue(t,&delta,FALSE,CTRL_RELATIVE);
|
|
return delta;
|
|
}
|
|
|
|
void PathPosition::ClearIKParam(Interval iv,int index)
|
|
{
|
|
if (percent) percent->DeleteTime(iv,TIME_INCRIGHT|TIME_NOSLIDE);
|
|
}
|
|
|
|
void JointParamsPath::SpinnerChange(
|
|
InterpCtrlUI *ui,WORD id,ISpinnerControl *spin,BOOL interactive)
|
|
{
|
|
float val;
|
|
BOOL set = FALSE;
|
|
|
|
switch (id) {
|
|
case IDC_XFROMSPIN:
|
|
val = min[0] = spin->GetFVal()/scale;
|
|
set = TRUE;
|
|
break;
|
|
case IDC_XTOSPIN:
|
|
val = max[0] = spin->GetFVal()/scale;
|
|
set = TRUE;
|
|
break;
|
|
|
|
case IDC_XDAMPINGSPIN:
|
|
damping[0] = spin->GetFVal(); break;
|
|
}
|
|
|
|
if (set && interactive) {
|
|
PathPosition *c = (PathPosition*)ui->cont;
|
|
c->percent->SetValue(ui->ip->GetTime(),&val,TRUE,CTRL_ABSOLUTE);
|
|
ui->ip->RedrawViews(ui->ip->GetTime(),REDRAW_INTERACTIVE);
|
|
}
|
|
}
|
|
|
|
|