/*************************************************************************** * * * 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 #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;xGetKey(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;xGetKey(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;xGetKey(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;xNumberOfChildren();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;xGetChildNode(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); } } }