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.
1134 lines
30 KiB
C++
1134 lines
30 KiB
C++
/***************************************************************************
|
|
* *
|
|
* 3DS Max Test Plugin *
|
|
* *
|
|
***************************************************************************
|
|
* *
|
|
* Version : 1.0ß, March 11th, 1998 *
|
|
* *
|
|
* Written By Loic Baumann from Fatal Design, specially for Gamasutra *
|
|
* *
|
|
***************************************************************************
|
|
* *
|
|
* This Plugin demonstrates how to write a simple 3DS MAX Plugin, using MFCs *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#ifdef STRICT // Already defined by stdafx,
|
|
#undef STRICT // so we avoid warning linking msg
|
|
#endif
|
|
#ifdef _MBCS // The same as above
|
|
#undef _MBCS
|
|
#endif
|
|
|
|
#include "Replicate.h"
|
|
#include "CvObject.h"
|
|
|
|
#pragma pack(push,8)
|
|
#include <dummy.h>
|
|
#pragma pack(pop)
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
ReplicatePanel g_replicatePanel;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// PlugPanel Dialog Procedure to handle User and System Interactions
|
|
static BOOL CALLBACK ReplicatePanelProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
switch (msg) {
|
|
case WM_INITDIALOG: //Panel's extra initialization
|
|
g_replicatePanel.Init(hWnd);
|
|
break;
|
|
case WM_DESTROY: //Panel's extra destruction
|
|
g_replicatePanel.Destroy(hWnd);
|
|
break;
|
|
case WM_COMMAND :
|
|
switch (LOWORD(wParam)) { //Handle Panel's controls
|
|
case IDC_ABOUT: {
|
|
CAbout dlg;
|
|
dlg.DoModal();
|
|
break;
|
|
}
|
|
case IDC_REPLICATE: {
|
|
g_replicatePanel.Replicate();
|
|
break;
|
|
}
|
|
case IDC_BAKE: {
|
|
g_replicatePanel.Bake();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//The following lines are not necessary for MAX2 Plugins
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_MOUSEMOVE:
|
|
g_replicatePanel.m_ip->RollupMouseMessage(hWnd, msg, wParam, lParam);
|
|
break;
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Display the Plugin's Panel
|
|
void ReplicatePanel::BeginEditParams(Interface* ip, IUtil* iu)
|
|
{
|
|
m_ip = ip; //Get the Plugin's Interface
|
|
m_iu = iu; //Get the Utility Plugin's Interface
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
HINSTANCE loc_hInstance = AfxGetResourceHandle ();
|
|
|
|
//Display the plugin's Rollup Dialog
|
|
m_hPanel = m_ip->AddRollupPage(loc_hInstance, MAKEINTRESOURCE(IDD_REP_PANEL),
|
|
ReplicatePanelProc, GetString(IDS_REPLICATE_NAME),0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Free the Plugins' Panel
|
|
void ReplicatePanel::EndEditParams(Interface* ip, IUtil *iu)
|
|
{
|
|
m_iu = NULL;
|
|
m_ip = NULL;
|
|
|
|
//Remove the Rollup Dialog
|
|
ip->DeleteRollupPage(m_hPanel);
|
|
m_hPanel = NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Panel Initialization callback
|
|
void ReplicatePanel::Init(HWND hWnd)
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Panel Destruction callback
|
|
void ReplicatePanel::Destroy(HWND hWnd)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Called when the Optimize Button is pushed
|
|
void ReplicatePanel::Replicate()
|
|
{
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
// Notes for undo:
|
|
// all I have to do is store the new
|
|
// parent nodes pointer in the hold
|
|
// the delete is already in the hold
|
|
// cuz max is nice like that
|
|
// all I have to do is delete my creation
|
|
// and I can do that by keeping a pointer to
|
|
// the node and just delete it!
|
|
// Sticky point: Interaction between deleting
|
|
// while in restore mode, since deleting actualy
|
|
// stores the undo info? hmmm no idea.
|
|
|
|
INode* theNode;
|
|
|
|
|
|
//Display the HitByName dialog
|
|
TestHitByNameDlgCallback myCallback;
|
|
if (!m_ip->DoHitByNameDialog(&myCallback)) return;
|
|
|
|
//Get the Selected Node Table and the nodes' number
|
|
if (!myCallback.NodeTab.Count()) return;
|
|
theNode = *myCallback.NodeTab.Addr(0);
|
|
|
|
|
|
ThresholdDialog thresh_dialog;
|
|
|
|
if (thresh_dialog.DoModal() == IDOK)
|
|
{
|
|
//Display the Node's Info Dialog
|
|
|
|
// Get the threshold...
|
|
JointEditDialog edit_dialog(theNode, m_ip);
|
|
|
|
if (edit_dialog.DoModal() == IDOK)
|
|
{
|
|
theHold.SuperBegin();
|
|
|
|
// no modal this time!
|
|
// use the Max progress bar... Interface::Progressthingamagiggy
|
|
|
|
|
|
// keyframe reduction is fast enough so for now just use
|
|
// the joint count and how far it has gone.
|
|
|
|
INode *new_copy = CopyMeAndMyChildren(theNode); // the root
|
|
|
|
// copy the animation is now done in the copy routine
|
|
CopyAnimation(theNode, new_copy, &edit_dialog);
|
|
|
|
|
|
// try drawing first...
|
|
// Redraw the display
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_BEGIN);
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_NORMAL);
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_END);
|
|
|
|
|
|
|
|
// strip the temp bones and helpers out...
|
|
StripBones(new_copy);
|
|
|
|
|
|
if (thresh_dialog.m_deleteOriginal)
|
|
{
|
|
//delete original
|
|
DeleteMeAndMyChildren(theNode);
|
|
}
|
|
|
|
|
|
int node_count = JointEditDialog::CountChildren(new_copy);
|
|
|
|
LPVOID arg = NULL;
|
|
m_ip->ProgressStart(_T("Optimizing Joints"), TRUE, fn, arg);
|
|
|
|
int current_node = 0;
|
|
OptimizeAnimation(new_copy, current_node, node_count,&edit_dialog);
|
|
|
|
m_ip->ProgressEnd();
|
|
|
|
|
|
// Move the new node over and tell it's kids to do the same.
|
|
|
|
if (thresh_dialog.m_offsetModelFlag)
|
|
{
|
|
// make sure we are not animating this...oy how emarassing
|
|
SuspendAnimate();
|
|
AnimateOff();
|
|
|
|
|
|
Matrix3 axis(MAT_IDENT);
|
|
Point3 translate(thresh_dialog.m_offsetX, thresh_dialog.m_offsetY, thresh_dialog.m_offsetZ);
|
|
new_copy->Move(0, axis, translate, FALSE, TRUE, PIV_NONE);
|
|
|
|
ResumeAnimate();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close out the undo
|
|
theHold.SuperAccept("Replicate");
|
|
|
|
|
|
// Redraw the display
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_BEGIN);
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_NORMAL);
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_END);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReplicatePanel::DeleteMeAndMyChildren(INode *parent_node)
|
|
{
|
|
|
|
// Recursuvly Delete all the children
|
|
while (parent_node->NumberOfChildren() != 0)
|
|
{
|
|
INode *child_node = parent_node->GetChildNode(0);
|
|
assert(child_node != NULL);
|
|
DeleteMeAndMyChildren(child_node);
|
|
}
|
|
|
|
// delete me!
|
|
parent_node->Delete(m_ip->GetTime(), true);
|
|
|
|
}
|
|
|
|
INode *
|
|
ReplicatePanel::CopyMeAndMyChildren(INode *copy_node)
|
|
{
|
|
|
|
//MessageBox(NULL, copy_node->GetName(), "Just say OK", MB_OK|MB_APPLMODAL );
|
|
|
|
INode *new_node = NULL;
|
|
|
|
if (!copy_node->IsRootNode())
|
|
{
|
|
new_node = CopyNode(copy_node);
|
|
assert(new_node != NULL);
|
|
|
|
//CopyAnimation(copy_node, new_node);
|
|
}
|
|
|
|
|
|
int maxcount = copy_node->NumberOfChildren();
|
|
for (int i = 0; i < maxcount; i++)
|
|
{
|
|
INode *child_node = copy_node->GetChildNode(i);
|
|
assert(child_node != NULL);
|
|
INode *copy_child_node = CopyMeAndMyChildren(child_node);
|
|
assert(copy_child_node != NULL);
|
|
|
|
// attach the copy_child_node to new_node
|
|
if (!copy_node->IsRootNode())
|
|
{
|
|
new_node->AttachChild(copy_child_node, 1);
|
|
}
|
|
}
|
|
|
|
return new_node;
|
|
}
|
|
|
|
INode *
|
|
ReplicatePanel::CopyNode(INode *copy_node)
|
|
{
|
|
|
|
INode *new_node = NULL;
|
|
|
|
SClass_ID SCID;
|
|
Class_ID CID;
|
|
ObjectState os;
|
|
os = copy_node->EvalWorldState(m_ip->GetTime());
|
|
|
|
ViewExp *vpt = m_ip->GetViewport(NULL);
|
|
|
|
// new node = copy_node
|
|
if (os.obj)
|
|
{
|
|
SCID = os.obj->SuperClassID();
|
|
CID = os.obj->ClassID();
|
|
|
|
switch (SCID)
|
|
{
|
|
case GEOMOBJECT_CLASS_ID :
|
|
// We support this!!!
|
|
// I'm going to convert it to a mesh some day
|
|
// but right now go to a dummie with a bound the same size...
|
|
{
|
|
|
|
|
|
GeomObject* obj = (GeomObject*)os.obj;
|
|
GeomObject* cloned_obj = (GeomObject*)obj->Clone();
|
|
|
|
assert(cloned_obj != NULL);
|
|
new_node = m_ip->CreateObjectNode(cloned_obj);
|
|
|
|
// clone the refrences...
|
|
// damn this is a bitch...
|
|
|
|
// Transfer the material.. All of the other refs are copied
|
|
|
|
if (copy_node->GetMtl() != NULL)
|
|
{
|
|
new_node->SetMtl(copy_node->GetMtl());
|
|
}
|
|
// Transfer the wireframe color...
|
|
new_node->SetWireColor(copy_node->GetWireColor());
|
|
|
|
TSTR name(copy_node->GetName());
|
|
name = "_"+name;
|
|
//m_ip->MakeNameUnique(name);
|
|
|
|
new_node->SetName(name);
|
|
|
|
//Set the object offset transform
|
|
Point3 offset_trans = copy_node->GetObjOffsetPos();
|
|
Quat offset_rot_quat = copy_node->GetObjOffsetRot();
|
|
|
|
new_node->SetObjOffsetPos(offset_trans);
|
|
new_node->SetObjOffsetRot(offset_rot_quat);
|
|
|
|
// Set world transform
|
|
|
|
TimeValue t(0);
|
|
|
|
Matrix3 position = copy_node->GetNodeTM(t);
|
|
new_node->SetNodeTM(t, position);
|
|
|
|
}
|
|
break;
|
|
|
|
//case SHAPE_CLASS_ID:
|
|
// 2D Shapes not supported right now
|
|
// break;
|
|
|
|
case HELPER_CLASS_ID:
|
|
{
|
|
|
|
// copy dummies and turn Bones into dummies
|
|
// We support this!!!
|
|
// I'm going to convert it to a mesh some day
|
|
// but right now go to a dummie with a bound the same size...
|
|
|
|
// We only support bones and dummies...
|
|
|
|
if (CID == Class_ID(DUMMY_CLASS_ID,0) || CID == Class_ID(BONE_CLASS_ID, 0))
|
|
{
|
|
|
|
DummyObject *dummy = (DummyObject *)m_ip->CreateInstance(
|
|
HELPER_CLASS_ID,
|
|
Class_ID(DUMMY_CLASS_ID, 0)
|
|
);
|
|
|
|
assert(dummy != NULL);
|
|
new_node = m_ip->CreateObjectNode(dummy);
|
|
|
|
dummy->EnableDisplay();
|
|
|
|
GeomObject* obj = (GeomObject*)os.obj;
|
|
|
|
Box3 bound_box;
|
|
obj->GetLocalBoundBox(m_ip->GetTime(), copy_node, vpt, bound_box);
|
|
|
|
dummy->SetBox(bound_box);
|
|
|
|
}
|
|
if (CID == SITEOBJECT_CLASS_ID)
|
|
{
|
|
|
|
|
|
Object* obj = (Object*)os.obj;
|
|
Object* cloned_obj = (Object*)obj->Clone();
|
|
|
|
assert(cloned_obj != NULL);
|
|
new_node = m_ip->CreateObjectNode(cloned_obj);
|
|
|
|
|
|
// clone the refrences...
|
|
// damn this is a bitch...
|
|
|
|
// Transfer the material.. All of the other refs are copied
|
|
|
|
if (copy_node->GetMtl() != NULL)
|
|
{
|
|
new_node->SetMtl(copy_node->GetMtl());
|
|
}
|
|
// Transfer the wireframe color...
|
|
new_node->SetWireColor(copy_node->GetWireColor());
|
|
|
|
}
|
|
if (CID == CVOBJECT_CLASS_ID)
|
|
{
|
|
|
|
|
|
CvObject* obj = (CvObject*)os.obj;
|
|
CvObject* cloned_obj = (CvObject*)obj->Clone();
|
|
|
|
assert(cloned_obj != NULL);
|
|
new_node = m_ip->CreateObjectNode(cloned_obj);
|
|
|
|
|
|
// clone the refrences...
|
|
// damn this is a bitch...
|
|
|
|
// Transfer the material.. All of the other refs are copied
|
|
|
|
if (copy_node->GetMtl() != NULL)
|
|
{
|
|
new_node->SetMtl(copy_node->GetMtl());
|
|
}
|
|
// Transfer the wireframe color...
|
|
new_node->SetWireColor(copy_node->GetWireColor());
|
|
|
|
|
|
}
|
|
|
|
TSTR name(copy_node->GetName());
|
|
name = "_"+name;
|
|
//m_ip->MakeNameUnique(name);
|
|
|
|
new_node->SetName(name);
|
|
|
|
//Set the object offset transform
|
|
Point3 offset_trans = copy_node->GetObjOffsetPos();
|
|
Quat offset_rot_quat = copy_node->GetObjOffsetRot();
|
|
|
|
new_node->SetObjOffsetPos(offset_trans);
|
|
new_node->SetObjOffsetRot(offset_rot_quat);
|
|
|
|
// Set world transform
|
|
|
|
TimeValue t(0);
|
|
|
|
Matrix3 position = copy_node->GetNodeTM(t);
|
|
new_node->SetNodeTM(t, position);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
Verify(new_node != NULL);
|
|
|
|
// copy the pref data over...
|
|
JointEditDialog::CopyJointPrefrence(copy_node, new_node);
|
|
|
|
return new_node;
|
|
}
|
|
|
|
void
|
|
ReplicatePanel::StripBones(INode * /*parent_node*/)
|
|
{
|
|
|
|
// strip bones...
|
|
|
|
// Only strip helper if it has geometry that can sub for it and that geometry doesn't
|
|
// have it's own animation. If there is more than one geometry set directly attached to
|
|
// the bone, don't disolve the bone since it is an optimization to animate two nodes with
|
|
// one channel...Or combine the nodes, but only if they don't have animation.
|
|
|
|
// Maybe don't disolve the bones if the artist is planing on having animation on both the bones
|
|
// and the geometry attached to the bone. Is that possible to do? I'm not sure.
|
|
|
|
// Anyway. To disolve a bone, detach the geometry from the bone and attach it to it's parent
|
|
// Here is the tricky part. Redefining the animation and Transform Matrix.
|
|
// Try not to change the animation, IE: attach the geometry to the same TM as the bone TM, then
|
|
// make the object offset TM be the original objects offset transform times
|
|
// the original objects local origin!!!! =) HEHEHEHEHE
|
|
|
|
// IF not then we have to do weird stuff to the animation to make it correct again.
|
|
|
|
|
|
}
|
|
|
|
static void SetKeys(Control *control,DWORD tantype)
|
|
{
|
|
Control *con = control->GetXController();
|
|
IKeyControl *ikc = NULL;
|
|
if (con) {
|
|
ikc = GetKeyControlInterface(con);
|
|
if (ikc)
|
|
{
|
|
int maxkeys=ikc->GetNumKeys();
|
|
|
|
for (int x=0;x<maxkeys;x++)
|
|
{
|
|
IKey *key=(IKey *)new IBezFloatKey;
|
|
ikc->GetKey(x,key);
|
|
SetInTanType(key->flags,tantype);
|
|
SetOutTanType(key->flags,tantype);
|
|
ikc->SetKey(x,key);
|
|
}
|
|
}
|
|
}
|
|
con = control->GetYController();
|
|
if (con) {
|
|
ikc = GetKeyControlInterface(con);
|
|
if (ikc)
|
|
{
|
|
int maxkeys=ikc->GetNumKeys();
|
|
|
|
for (int x=0;x<maxkeys;x++)
|
|
{
|
|
IKey *key=(IKey *)new IBezFloatKey;
|
|
ikc->GetKey(x,key);
|
|
SetInTanType(key->flags,tantype);
|
|
SetOutTanType(key->flags,tantype);
|
|
ikc->SetKey(x,key);
|
|
}
|
|
}
|
|
}
|
|
con = control->GetZController();
|
|
if (con) {
|
|
ikc = GetKeyControlInterface(con);
|
|
if (ikc)
|
|
{
|
|
int maxkeys=ikc->GetNumKeys();
|
|
|
|
for (int x=0;x<maxkeys;x++)
|
|
{
|
|
IKey *key=(IKey *)new IBezFloatKey;
|
|
ikc->GetKey(x,key);
|
|
SetInTanType(key->flags,tantype);
|
|
SetOutTanType(key->flags,tantype);
|
|
ikc->SetKey(x,key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ReplicatePanel::CopyAnimation(INode * source_node, INode * target_node, JointEditDialog *dialog,int converttype)
|
|
{
|
|
// Copyies controlers in order
|
|
// If the controler is procedural or this is a bone
|
|
// turn it into something we know and add the controler
|
|
|
|
// Simplify the keyframes future should ask the user which joints to optimize
|
|
// or optimize procedural/bones and anything that has a one for one rate
|
|
|
|
//afxDump << "Copying Animation : " << source_node->GetName() << "\n";
|
|
//MessageBox(NULL, source_node->GetName(), "COPY ANIM", MB_OK|MB_APPLMODAL );
|
|
|
|
|
|
// copy this anim...
|
|
|
|
//If everything is known than just clone this puppy...
|
|
|
|
JointPreference *pref = JointEditDialog::GetJointPrefrence(source_node);
|
|
if (pref)
|
|
{
|
|
Verify(pref != NULL);
|
|
|
|
|
|
|
|
Control *tm_controller = source_node->GetTMController();
|
|
|
|
int include_joint = true;
|
|
|
|
if (pref->m_excludeJoint == JointPreference::ExcludeJoint)
|
|
{
|
|
include_joint = false;
|
|
}
|
|
|
|
if (pref->m_excludeJoint == JointPreference::AutoJoint)
|
|
{
|
|
if(!dialog->NodeAnimated(source_node))
|
|
{
|
|
include_joint = false;
|
|
}
|
|
}
|
|
|
|
if (tm_controller != NULL && include_joint)
|
|
{
|
|
|
|
if (pref->m_convertRotationJoint)
|
|
{
|
|
|
|
theHold.Begin();
|
|
// replace controler pointer with new controller
|
|
|
|
// if the type is known but the sub type isn't
|
|
// then convert that controller.
|
|
|
|
// Example is RotationXYZ with a sub controler of SineWave or Noise
|
|
|
|
Control *rotation_controler = NULL;
|
|
|
|
if (converttype)
|
|
{
|
|
switch(pref->m_rotationConvertType)
|
|
{
|
|
case JointPreference::LinearKeyframeType:
|
|
rotation_controler = (Control *)m_ip->CreateInstance(
|
|
CTRL_ROTATION_CLASS_ID,
|
|
Class_ID(LININTERP_ROTATION_CLASS_ID, 0)
|
|
);
|
|
break;
|
|
case JointPreference::EulerXYZKeyframeType:
|
|
rotation_controler = (Control *)m_ip->CreateInstance(
|
|
CTRL_ROTATION_CLASS_ID,
|
|
Class_ID(EULER_CONTROL_CLASS_ID, 0)
|
|
);
|
|
break;
|
|
case JointPreference::PositionXYZKeyframeType:
|
|
rotation_controler = (Control *)m_ip->CreateInstance(
|
|
CTRL_ROTATION_CLASS_ID,
|
|
Class_ID(EULER_CONTROL_CLASS_ID, 0)
|
|
);
|
|
break;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rotation_controler = (Control *)m_ip->CreateInstance(
|
|
CTRL_ROTATION_CLASS_ID,
|
|
Class_ID(EULER_CONTROL_CLASS_ID, 0));
|
|
}
|
|
|
|
SuspendAnimate();
|
|
AnimateOn();
|
|
|
|
{
|
|
|
|
Interval iv;
|
|
int ticks_per_frame = GetTicksPerFrame();
|
|
int num_of_frames = 1;
|
|
|
|
TimeValue start =m_ip->GetAnimRange().Start();
|
|
TimeValue end =m_ip->GetAnimRange().End();
|
|
int delta = GetTicksPerFrame();
|
|
|
|
for (int time_of_key=start; time_of_key<=end; time_of_key+=delta)
|
|
{
|
|
|
|
Quat rot;
|
|
Point3 pos;
|
|
|
|
// Get the pos and rot from the frame.
|
|
|
|
// Compute the relative TM
|
|
Matrix3 parentTM, nodeTM, localTM;
|
|
nodeTM = source_node->GetNodeTM(time_of_key);
|
|
parentTM = source_node->GetParentTM(time_of_key);
|
|
localTM = nodeTM*Inverse(parentTM);
|
|
|
|
AffineParts ap;
|
|
decomp_affine(localTM, &ap);
|
|
pos = ap.t;
|
|
rot = ap.q;
|
|
|
|
rotation_controler->SetValue(time_of_key,&rot,TRUE,CTRL_ABSOLUTE);
|
|
}
|
|
}
|
|
|
|
ResumeAnimate();
|
|
theHold.Accept(0);
|
|
|
|
Control *target_master_control;
|
|
target_master_control = target_node->GetTMController();
|
|
target_master_control->SetRotationController(rotation_controler);
|
|
SetKeys(rotation_controler,BEZKEY_LINEAR);
|
|
}
|
|
else
|
|
{
|
|
// clone the animation
|
|
// Get the source control
|
|
Control *source_master_control;
|
|
Control *source_new_rotation_control = NULL;
|
|
|
|
source_master_control = source_node->GetTMController();
|
|
source_new_rotation_control = source_master_control->GetRotationController();
|
|
assert(source_new_rotation_control != NULL);
|
|
|
|
// Clone the source to the targets
|
|
Control *target_master_control;
|
|
Control *target_new_rotation_control = NULL;
|
|
|
|
target_master_control = target_node->GetTMController();
|
|
target_new_rotation_control = (Control *)source_new_rotation_control->Clone();
|
|
|
|
|
|
// Paste them in
|
|
// The help file said I didn't have to delete the old ones, but it doesn't
|
|
// say if I should dereference them . I'm assuming, for now, that the new
|
|
// position control is being deleted by the derefrencing method ( ie, nothing
|
|
// is left looking at them so they are deleted ). But it doesn't say if I am
|
|
// the one to have to delete it. I guess they are and I don't have to.
|
|
assert(target_new_rotation_control != NULL);
|
|
target_master_control->SetRotationController(target_new_rotation_control);
|
|
|
|
}
|
|
|
|
|
|
if (pref->m_convertTranslationJoint)
|
|
{
|
|
theHold.Begin();
|
|
// replace controler pointer with new controller
|
|
|
|
// if the type is known but the sub type isn't
|
|
// then convert that controller.
|
|
|
|
// Example is PositionXYZ with a sub controler of SineWave or Noise
|
|
|
|
Control *translation_controler = NULL;
|
|
|
|
Control *posc = tm_controller->GetPositionController();
|
|
if (posc)
|
|
{
|
|
Control *c=posc->GetXController();
|
|
if (c)
|
|
Class_ID cl = c->ClassID();
|
|
Class_ID cl2 = posc->ClassID();
|
|
}
|
|
|
|
Class_ID cl1 = Class_ID(0x118f7e02,0xffee238a);
|
|
|
|
if (converttype)
|
|
{
|
|
switch(pref->m_translationConvertType)
|
|
{
|
|
case JointPreference::LinearKeyframeType:
|
|
translation_controler = (Control *)m_ip->CreateInstance(
|
|
CTRL_POSITION_CLASS_ID,
|
|
Class_ID(LININTERP_POSITION_CLASS_ID, 0)
|
|
);
|
|
break;
|
|
case JointPreference::EulerXYZKeyframeType:
|
|
translation_controler =
|
|
(Control *)m_ip->CreateInstance(
|
|
CTRL_POSITION_CLASS_ID,
|
|
cl1
|
|
);
|
|
break;
|
|
case JointPreference::PositionXYZKeyframeType:
|
|
//translation_controler = NewDefaultPoint3Controller();
|
|
translation_controler = (Control *)m_ip->CreateInstance(
|
|
CTRL_POSITION_CLASS_ID,
|
|
cl1
|
|
);
|
|
break;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
translation_controler =
|
|
(Control *)m_ip->CreateInstance(
|
|
CTRL_POSITION_CLASS_ID,
|
|
cl1);
|
|
}
|
|
|
|
SuspendAnimate();
|
|
AnimateOn();
|
|
|
|
{
|
|
|
|
Interval iv;
|
|
int ticks_per_frame = GetTicksPerFrame();
|
|
int num_of_frames = 1;
|
|
|
|
TimeValue start =m_ip->GetAnimRange().Start();
|
|
TimeValue end =m_ip->GetAnimRange().End();
|
|
int delta = GetTicksPerFrame();
|
|
|
|
for (int time_of_key=start; time_of_key<=end; time_of_key+=delta)
|
|
{
|
|
|
|
Quat rot;
|
|
Point3 pos;
|
|
|
|
// Get the pos and rot from the frame.
|
|
|
|
// Compute the relative TM
|
|
Matrix3 parentTM, nodeTM, localTM;
|
|
nodeTM = source_node->GetNodeTM(time_of_key);
|
|
parentTM = source_node->GetParentTM(time_of_key);
|
|
localTM = nodeTM*Inverse(parentTM);
|
|
|
|
AffineParts ap;
|
|
decomp_affine(localTM, &ap);
|
|
pos = ap.t;
|
|
rot = ap.q;
|
|
|
|
translation_controler->SetValue(time_of_key,&pos,TRUE,CTRL_ABSOLUTE);
|
|
}
|
|
}
|
|
|
|
ResumeAnimate();
|
|
theHold.Accept(0);
|
|
|
|
Control *target_master_control;
|
|
target_master_control = target_node->GetTMController();
|
|
target_master_control->SetPositionController(translation_controler);
|
|
SetKeys(translation_controler,BEZKEY_LINEAR);
|
|
}
|
|
else
|
|
{
|
|
// clone the animation
|
|
// Get the source control
|
|
Control *source_master_control;
|
|
Control *source_new_position_control = NULL;
|
|
|
|
source_master_control = source_node->GetTMController();
|
|
source_new_position_control = source_master_control->GetPositionController();
|
|
assert(source_new_position_control != NULL);
|
|
|
|
|
|
// Clone the source to the targets
|
|
Control *target_master_control;
|
|
Control *target_new_position_control = NULL;
|
|
|
|
target_master_control = target_node->GetTMController();
|
|
target_new_position_control = (Control *)source_new_position_control->Clone();
|
|
|
|
|
|
// Paste them in
|
|
// The help file said I didn't have to delete the old ones, but it doesn't
|
|
// say if I should dereference them . I'm assuming, for now, that the new
|
|
// position control is being deleted by the derefrencing method ( ie, nothing
|
|
// is left looking at them so they are deleted ). But it doesn't say if I am
|
|
// the one to have to delete it. I guess they are and I don't have to.
|
|
assert(target_new_position_control != NULL);
|
|
target_master_control->SetPositionController(target_new_position_control);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
// do my children..
|
|
assert(source_node->NumberOfChildren() == target_node->NumberOfChildren());
|
|
|
|
for (int i = 0; i < source_node->NumberOfChildren(); i++)
|
|
{
|
|
INode *child_node_source = source_node->GetChildNode(i);
|
|
assert(child_node_source != NULL);
|
|
|
|
INode *child_node_target = target_node->GetChildNode(i);
|
|
assert(child_node_target != NULL);
|
|
|
|
CopyAnimation(child_node_source, child_node_target, dialog,converttype);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
ReplicatePanel::OptimizeAnimation(INode *copy_node, int ¤t_node, int total_nodes, JointEditDialog *dialog)
|
|
{
|
|
|
|
|
|
Control *position_control = NULL;
|
|
Control *rotation_control = NULL;
|
|
|
|
Control *tm_controller = copy_node->GetTMController();
|
|
|
|
if (tm_controller != NULL)
|
|
{
|
|
|
|
position_control = tm_controller->GetPositionController();
|
|
rotation_control = tm_controller->GetRotationController();
|
|
|
|
JointPreference *pref = JointEditDialog::GetJointPrefrence(copy_node);
|
|
Verify(pref != NULL);
|
|
|
|
int include_joint = true;
|
|
|
|
if (pref->m_excludeJoint == JointPreference::ExcludeJoint)
|
|
{
|
|
include_joint = false;
|
|
}
|
|
|
|
if (pref->m_excludeJoint == JointPreference::AutoJoint)
|
|
{
|
|
if(!dialog->NodeAnimated(copy_node))
|
|
{
|
|
include_joint = false;
|
|
}
|
|
}
|
|
|
|
if (include_joint)
|
|
{
|
|
if (pref->m_optimizeRotation)
|
|
{
|
|
if (rotation_control != NULL)
|
|
{
|
|
int num_keys = rotation_control->NumKeys();
|
|
|
|
if (num_keys > 0)
|
|
{
|
|
|
|
TimeValue start = rotation_control->GetKeyTime(0);
|
|
TimeValue end = rotation_control->GetKeyTime(num_keys-1);
|
|
|
|
if (pref->m_rotationRange)
|
|
{
|
|
start = (TimeValue)pref->m_rotationSampleStartFrame * GetTicksPerFrame();
|
|
end = (TimeValue)pref->m_rotationSampleEndFrame * GetTicksPerFrame();
|
|
}
|
|
|
|
Interval range;
|
|
start = (rotation_control->GetKeyTime(0) > start) ? rotation_control->GetKeyTime(0):start;
|
|
end = (rotation_control->GetKeyTime(num_keys-1) < end) ? rotation_control->GetKeyTime(num_keys-1):end;
|
|
|
|
|
|
|
|
// adjust the optimization to not include the last frame... tehehehehehe
|
|
if ((end - GetTicksPerFrame()) > start)
|
|
{
|
|
end -= GetTicksPerFrame();
|
|
}
|
|
range.Set(start , end);
|
|
|
|
RepReduceStatus rot_progress;
|
|
|
|
ApplyKeyReduction_MW4(
|
|
rotation_control,
|
|
range,
|
|
pref->m_rotationThreshold * Stuff::Radians_Per_Degree,
|
|
GetTicksPerFrame(),
|
|
&rot_progress
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pref->m_optimizeTranslation)
|
|
{
|
|
if (position_control != NULL)
|
|
{
|
|
int num_keys = position_control->NumKeys();
|
|
|
|
if (num_keys > 0)
|
|
{
|
|
TimeValue start = position_control->GetKeyTime(0);
|
|
TimeValue end = position_control->GetKeyTime(num_keys-1);
|
|
|
|
if (pref->m_translationRange)
|
|
{
|
|
start = (TimeValue)pref->m_translationSampleStartFrame * GetTicksPerFrame();
|
|
end = (TimeValue)pref->m_translationSampleEndFrame * GetTicksPerFrame();
|
|
}
|
|
|
|
Interval range;
|
|
start = (position_control->GetKeyTime(0) > start) ? position_control->GetKeyTime(0):start;
|
|
end = (position_control->GetKeyTime(num_keys-1) < end) ? position_control->GetKeyTime(num_keys-1):end;
|
|
|
|
// adjust the optimization to not include the last frame... tehehehehehe
|
|
if ((end - GetTicksPerFrame()) > start)
|
|
{
|
|
end -= GetTicksPerFrame();
|
|
}
|
|
range.Set(start , end);
|
|
|
|
RepReduceStatus tran_progress;
|
|
|
|
ApplyKeyReduction_MW4(
|
|
position_control,
|
|
range,
|
|
pref->m_translationThreshold,
|
|
GetTicksPerFrame(),
|
|
&tran_progress
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
++current_node;
|
|
|
|
int total_done = (int)(((float)current_node/(float)total_nodes)*100.0f);
|
|
m_ip->ProgressUpdate(total_done);
|
|
|
|
|
|
for (int i = 0; i < copy_node->NumberOfChildren(); i++)
|
|
{
|
|
INode *child_node = copy_node->GetChildNode(i);
|
|
assert(child_node != NULL);
|
|
OptimizeAnimation(child_node, current_node, total_nodes, dialog);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void FixNames(INode *pNode)
|
|
{
|
|
|
|
CString str = pNode->GetName();
|
|
if ('_' == str[0])
|
|
{
|
|
str.Delete(0,1);
|
|
pNode->SetName((char *)(LPCTSTR)str);
|
|
}
|
|
for (int x=0;x<pNode->NumberOfChildren();x++)
|
|
{
|
|
INode *pChild = pNode->GetChildNode(x);
|
|
FixNames(pChild);
|
|
}
|
|
}
|
|
|
|
void ReplicatePanel::Bake()
|
|
{
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
INode* theNode =m_ip->GetRootNode();
|
|
|
|
|
|
|
|
// ThresholdDialog thresh_dialog;
|
|
|
|
// if (thresh_dialog.DoModal() == IDOK)
|
|
{
|
|
//Display the Node's Info Dialog
|
|
|
|
// Get the threshold...
|
|
// JointEditDialog edit_dialog(theNode, m_ip);
|
|
|
|
// if (edit_dialog.DoModal() == IDOK)
|
|
{
|
|
theHold.SuperBegin();
|
|
|
|
// no modal this time!
|
|
// use the Max progress bar... Interface::Progressthingamagiggy
|
|
|
|
|
|
// keyframe reduction is fast enough so for now just use
|
|
// the joint count and how far it has gone.
|
|
|
|
int count = theNode->NumberOfChildren();
|
|
for (int x=0;x<count;x++)
|
|
{
|
|
INode *pChild = theNode->GetChildNode(x);
|
|
JointEditDialog edit_dialog(theNode, m_ip);
|
|
edit_dialog.AddJointPreference(pChild);
|
|
|
|
INode *new_copy = CopyMeAndMyChildren(pChild); // the root
|
|
|
|
// copy the animation is now done in the copy routine
|
|
//CopyAnimation(theNode, new_copy, &edit_dialog);
|
|
CopyAnimation(pChild, new_copy, &edit_dialog,0);
|
|
|
|
|
|
// try drawing first...
|
|
// Redraw the display
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_BEGIN);
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_NORMAL);
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_END);
|
|
|
|
|
|
|
|
// strip the temp bones and helpers out...
|
|
StripBones(new_copy);
|
|
|
|
|
|
// if (thresh_dialog.deleteOriginal)
|
|
{
|
|
//delete original
|
|
DeleteMeAndMyChildren(pChild);
|
|
}
|
|
|
|
FixNames(new_copy);
|
|
}
|
|
// TODO: Rename the nodes to get the names right
|
|
|
|
// Move the new node over and tell it's kids to do the same.
|
|
/*
|
|
if (thresh_dialog.offsetModelFlag)
|
|
{
|
|
// make sure we are not animating this...oy how emarassing
|
|
SuspendAnimate();
|
|
AnimateOff();
|
|
|
|
|
|
Matrix3 axis(MAT_IDENT);
|
|
Point3 translate(thresh_dialog.offsetX, thresh_dialog.offsetY, thresh_dialog.offsetZ);
|
|
new_copy->Move(0, axis, translate, FALSE, TRUE, PIV_NONE);
|
|
|
|
ResumeAnimate();
|
|
|
|
}
|
|
*/
|
|
|
|
// Close out the undo
|
|
theHold.SuperAccept("Replicate");
|
|
|
|
|
|
// Redraw the display
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_BEGIN);
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_NORMAL);
|
|
m_ip->RedrawViews(m_ip->GetTime(),REDRAW_END);
|
|
}
|
|
}
|
|
} |