xrefutil.cpp
Go to the documentation of this file.00001 // Copyright (C) 2000 by Autodesk, Inc. 00002 // 00003 // Permission to use, copy, modify, and distribute this software in 00004 // object code form for any purpose and without fee is hereby granted, 00005 // provided that the above copyright notice appears in all copies and 00006 // that both that copyright notice and the limited warranty and 00007 // restricted rights notice below appear in all supporting 00008 // documentation. 00009 // 00010 // AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. 00011 // AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF 00012 // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. 00013 // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE 00014 // UNINTERRUPTED OR ERROR FREE. 00015 // 00016 // Use, duplication, or disclosure by the U.S. Government is subject to 00017 // restrictions set forth in FAR 52.227-19 (Commercial Computer 00018 // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) 00019 // (Rights in Technical Data and Computer Software), as applicable. 00021 // XREFUTIL.CPP 00022 // 00023 // DESCR: 00024 // Simple Utility plug-in sample that shows how to do some of the MAX 00025 // XRef Scene and Object functionality via SDK APIs. 00026 // 00027 // Does not do: 00028 // -- undo/redo handling 00029 // -- extensive error checking 00030 // 00031 // 00032 // CHANGE LOG: 00033 // 03/2000 : DY : Created 00034 // 00036 00037 #include "xrefutil.h" 00038 00039 #define ERROR_TITLE "XRef Util Error" 00040 00042 // XrefutilClassDesc -- our required ClassDesc 00044 00045 class XrefutilClassDesc:public ClassDesc2 00046 { 00047 public: 00048 int IsPublic() { return 1; } 00049 void * Create(BOOL loading = FALSE) { return &theXrefutil; } 00050 const TCHAR * ClassName() { return GetString(IDS_CLASS_NAME); } 00051 SClass_ID SuperClassID() { return UTILITY_CLASS_ID; } 00052 Class_ID ClassID() { return XREFUTIL_CLASS_ID; } 00053 const TCHAR * Category() { return GetString(IDS_CATEGORY); } 00054 const TCHAR * InternalName() { return _T("Xrefutil"); } 00055 HINSTANCE HInstance() { return hInstance; } 00056 }; 00057 00058 static XrefutilClassDesc XrefutilDesc; 00059 00060 ClassDesc2* GetXrefutilDesc() 00061 { 00062 return &XrefutilDesc; 00063 } 00064 00065 static BOOL CALLBACK XrefutilDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 00066 { 00067 switch (msg) { 00068 case WM_INITDIALOG: 00069 theXrefutil.Init(hWnd); 00070 break; 00071 00072 case WM_DESTROY: 00073 theXrefutil.Destroy(hWnd); 00074 break; 00075 00076 case WM_COMMAND: 00077 switch (LOWORD(wParam)) { 00078 00079 case IDC_CMD_XS_ADD: 00080 // -- Add a new XRef Scene into the current scene -- 00081 theXrefutil.AddNewXrefScene(hWnd); 00082 break; 00083 00084 case IDC_CMD_XS_CNV: 00085 // -- Convert selected nodes into an XRef'd scene -- 00086 theXrefutil.ConvertSelectedToXrefScene(hWnd); 00087 break; 00088 00089 case IDC_CMD_XS_RFS: 00090 // -- Refresh/reload all scene xrefs -- 00091 theXrefutil.RefreshAllXrefScenes(hWnd); 00092 break; 00093 00094 case IDC_CMD_XS_MRG: 00095 // -- merge in all scene xrefs -- 00096 theXrefutil.MergeAllXrefScenes(hWnd); 00097 break; 00098 00099 00100 case IDC_CMD_XO_ADD: 00101 // -- Add a new XRef Object into the current scene -- 00102 theXrefutil.AddNewXrefObject(hWnd); 00103 break; 00104 00105 case IDC_CMD_XO_CNVSEL: 00106 // -- Convert selected object into an XRef Object -- 00107 theXrefutil.ConvertSelectedToXrefObject(hWnd); 00108 break; 00109 00110 case IDC_CMD_XO_EXP: 00111 // -- fake "export" of all xref objects in scene -- 00112 theXrefutil.ExportXrefObjects(hWnd); 00113 break; 00114 00115 00116 case IDC_CLOSE: 00117 theXrefutil.m_pUtil->CloseUtility(); 00118 break; 00119 00120 } 00121 00122 00123 break; 00124 00125 default: 00126 return FALSE; 00127 } 00128 return TRUE; 00129 } 00130 00132 // some more (Xrefutil specific) NodeEnumerator derivations 00134 00135 class UnflaggedNodeNamer : public NodeEnumerator 00136 { 00137 public: 00138 Tab<TSTR *> * m_namelist; 00139 00140 UnflaggedNodeNamer() : m_namelist(NULL) {}; 00141 virtual bool Proc(INode * pNode); 00142 }; 00143 00144 00145 bool UnflaggedNodeNamer::Proc(INode * pNode) 00146 { 00147 if (!m_namelist) return false; 00148 // otherwise, grab the list of node names and stick them in namelist 00149 if (! pNode->TestAFlag(A_WORK1)) { 00150 TSTR * pName = new TSTR(pNode->GetName()); // consumer must delete 00151 m_namelist->Append(1, &pName, 5); 00152 } 00153 return true; 00154 } 00155 00156 class UnflaggedNodeDeleter : public NodeEnumerator 00157 { 00158 public: 00159 virtual bool Proc(INode * pNode); 00160 }; 00161 00162 00163 bool UnflaggedNodeDeleter::Proc(INode * pNode) 00164 { 00165 // NOTE: should be called with Enumerate(procfirst = false) ! 00166 // Always do safety check so that we don't delete root accidentally 00167 if (!pNode->IsRootNode() && !pNode->TestAFlag(A_WORK1)) { 00168 // pNode->DeleteThis(); 00169 // theXrefutil.m_pInterface->DeleteNode(pnode, FALSE); 00170 pNode->Delete(0,TRUE); 00171 } 00172 00173 return true; 00174 } 00175 00176 class XRefObjFinder : public NodeEnumerator 00177 { 00178 public: 00179 TSTR * m_buffer; 00180 00181 XRefObjFinder() : m_buffer(NULL) {}; 00182 virtual bool Proc(INode * pNode); 00183 }; 00184 00185 bool XRefObjFinder::Proc(INode * pNode) 00186 { 00187 TSTR workstring; 00188 00189 // NOTE: We dump all info into one big string 00190 // Not super realistic, and probably won't work with 00191 // a scene with lots of XRef objects, but in a real exporter 00192 // situation, you'd be dumping out to a file pointer anyway 00193 00194 if (!pNode || !m_buffer) return false; 00195 Object *obj = pNode->GetObjectRef(); 00196 if (obj && obj->SuperClassID()==SYSTEM_CLASS_ID && 00197 obj->ClassID()==Class_ID(XREFOBJ_CLASS_ID,0)) { 00198 IXRefObject *ix = (IXRefObject *)obj; 00199 // if pNode refs an XRef object, let's pull some info out of it 00200 workstring.printf("Node <%s> XREF <%s> filename:<%s> proxy:<%s> anim-off:<%s> update-mats:<%s> \x0D\x0A", 00201 pNode->GetName(), ix->GetCurObjName(), ix->GetCurFileName(), 00202 (ix->GetUseProxy()) ? "T" : "F", 00203 (ix->GetIgnoreAnim()) ? "T" : "F", 00204 (ix->GetUpdateMats()) ? "T" : "F"); 00205 m_buffer->append(workstring); 00206 } 00207 return true; 00208 } 00209 00210 00211 // Helper class derived from AnimEnum used to delete (keyframed) animation info 00212 00213 class DeleteAllAnimEnum : public AnimEnum 00214 { 00215 public: 00216 BOOL delAtFrame0; 00217 00218 DeleteAllAnimEnum(BOOL delAt0) : AnimEnum(SCOPE_ALL) {delAtFrame0 = delAt0;} 00219 int proc(Animatable *anim, Animatable *client, int subNum) { 00220 if (delAtFrame0) { 00221 // Evaluate the controller at frame 0 before 00222 // deleting. This will cause its cache to take on 00223 // its value at frame 0. 00224 Control *cont = GetControlInterface(anim); 00225 if (cont) { 00226 Point3 p; 00227 Quat q; 00228 ScaleValue s; 00229 float f; 00230 Interval valid; 00231 switch (cont->SuperClassID()) { 00232 case CTRL_FLOAT_CLASS_ID: 00233 cont->GetValue(0,&f,valid); break; 00234 case CTRL_POSITION_CLASS_ID: 00235 cont->GetValue(0,&p,valid); break; 00236 case CTRL_ROTATION_CLASS_ID: 00237 cont->GetValue(0,&q,valid); break; 00238 case CTRL_SCALE_CLASS_ID: 00239 cont->GetValue(0,&s,valid); break; 00240 case CTRL_POINT3_CLASS_ID: 00241 cont->GetValue(0,&p,valid); break; 00242 } 00243 cont->SetORT(ORT_CONSTANT,ORT_BEFORE); 00244 cont->SetORT(ORT_CONSTANT,ORT_AFTER); 00245 } 00246 } 00247 anim->DeleteTime(FOREVER,0); 00248 anim->EditTimeRange(Interval(0,0),EDITRANGE_LINKTOKEYS); 00249 return ANIM_ENUM_PROCEED; 00250 } 00251 }; 00252 00253 00255 // Xrefutil class implementation -- our main utility plugin class 00257 00259 // Construction/destruction 00260 00261 Xrefutil::Xrefutil() 00262 : m_pUtil(NULL) 00263 , m_pInterface(NULL) 00264 , m_hPanel(NULL) 00265 , m_proxyholder(false) 00266 , m_ignoreanimholder(false) 00267 { 00268 } 00269 00270 Xrefutil::~Xrefutil() 00271 { 00272 00273 } 00274 00276 // UtilityObj overrides 00277 00278 void Xrefutil::BeginEditParams(Interface *ip,IUtil *iu) 00279 { 00280 m_pUtil = iu; 00281 m_pInterface = ip; 00282 m_hPanel = ip->AddRollupPage(hInstance, 00283 MAKEINTRESOURCE(IDD_PANEL), 00284 XrefutilDlgProc, 00285 GetString(IDS_PARAMS), 00286 0); 00287 } 00288 00289 void Xrefutil::EndEditParams(Interface *ip,IUtil *iu) 00290 { 00291 m_pUtil = NULL; 00292 m_pInterface = NULL; 00293 ip->DeleteRollupPage(m_hPanel); 00294 m_hPanel = NULL; 00295 } 00296 00297 void Xrefutil::DeleteThis() 00298 { 00299 // since there's only one static instance of the 00300 // UtilityObj, we don't do anything here 00301 } 00302 00303 00305 // utility methods 00306 00307 void Xrefutil::Init(HWND hWnd) 00308 { 00309 } 00310 00311 void Xrefutil::Destroy(HWND hWnd) 00312 { 00313 } 00314 00315 bool Xrefutil::DoOpenSaveDialog(TSTR &fileName, bool bOpen) 00316 { 00317 00318 // Does a standard Win32 CommonDlg Save-As or Open dialog 00319 // for a .MAX file 00320 // 00321 // (doesn't use registered custom dlg in MAX, as 00322 // in truth, only MAX can access this cache currently, 00323 // although you'll get any registered dialog if 00324 // you call Interface::FileSave or something similar, 00325 // but we just want to get a filename, not save yet) 00326 00327 OPENFILENAME ofn; 00328 TCHAR szFilter[]=__TEXT("3D Studio MAX (*.MAX)\0*.MAX\0\0"); 00329 TCHAR fname[512]; 00330 00331 _tcscpy(fname,fileName); 00332 00333 // set up that OPENFILENAME struct 00334 ::memset(&ofn, 0, sizeof(OPENFILENAME)); 00335 ofn.lStructSize = sizeof(OPENFILENAME); 00336 ofn.hwndOwner = m_hPanel; 00337 ofn.nFilterIndex = 1L; 00338 ofn.lpstrFilter = szFilter; 00339 ofn.lpstrCustomFilter = (LPTSTR)NULL; 00340 ofn.lpstrFile = fname; 00341 ofn.nMaxFile = sizeof(fname) / sizeof(TCHAR); 00342 ofn.lpstrFileTitle = NULL; 00343 ofn.nMaxFileTitle = 0; 00344 ofn.lpstrInitialDir = m_pInterface->GetDir(APP_SCENE_DIR); 00345 ofn.lpstrTitle = (LPCSTR)NULL; 00346 if (bOpen) { 00347 ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_LONGNAMES; 00348 } else { 00349 ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_LONGNAMES | OFN_OVERWRITEPROMPT; 00350 } 00351 ofn.lpstrDefExt = _TEXT("MAX"); 00352 00353 if (bOpen) { 00354 if (GetOpenFileName(&ofn)) { 00355 00356 // NOTE: More error checking needs to be done for this 00357 // to be practical -- e.g. we shouldn't allow the user to 00358 // select the currently open file. 00359 00360 fileName = TSTR(ofn.lpstrFile); // full path and file 00361 return true; // success 00362 00363 } else { 00364 // user canceled 00365 return false; 00366 } 00367 00368 } else { 00369 if (GetSaveFileName(&ofn)) { 00370 00371 // NOTE: More error checking needs to be done for this 00372 // to be practical -- e.g. we shouldn't allow the user to 00373 // select the currently open file. 00374 00375 fileName = TSTR(ofn.lpstrFile); // full path and file 00376 return true; // success 00377 00378 } else { 00379 // user canceled 00380 return false; 00381 } 00382 } 00383 return false; // failure 00384 } 00385 00386 00387 extern HINSTANCE hInstance; 00388 00389 static BOOL CALLBACK PickObjDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 00390 { 00391 int i, ix; 00392 UINT chkval; 00393 00394 switch (message) { 00395 00396 case WM_INITDIALOG: 00397 // (populate the listbox) 00398 SendDlgItemMessage(hWnd,IDC_LIST_NODES,LB_RESETCONTENT,0,0); 00399 for (i = 0; i < theXrefutil.m_objnamesholder.Count(); i++) { 00400 TSTR oname = *(theXrefutil.m_objnamesholder[i]); 00401 ix = SendDlgItemMessage(hWnd,IDC_LIST_NODES,LB_ADDSTRING,0,(LPARAM)(TCHAR*)oname); 00402 SendDlgItemMessage(hWnd,IDC_LIST_NODES,LB_SETITEMDATA,ix,i); 00403 } 00404 CenterWindow(hWnd,GetParent(hWnd)); 00405 theXrefutil.m_picknameholder = ""; 00406 break; 00407 00408 case WM_COMMAND: 00409 00410 switch (LOWORD(wParam)) { 00411 00412 case IDCANCEL: 00413 theXrefutil.m_picknameholder = ""; 00414 EndDialog(hWnd,0); 00415 break; 00416 00417 case IDOK: 00418 // on okay, we just copy over user-picked info into our 00419 // utility object -- slightly unusual, but it's a modal dialog 00420 // and the sample continues the xref object logic fully in 00421 // AddNewXrefObject 00422 ix = ::SendDlgItemMessage(hWnd,IDC_LIST_NODES,LB_GETCURSEL,0,0); 00423 if (ix != LB_ERR) { 00424 theXrefutil.m_picknameholder = *(theXrefutil.m_objnamesholder[ix]); 00425 } 00426 chkval = ::IsDlgButtonChecked(hWnd, IDC_CHK_PROXY); 00427 if (chkval == BST_CHECKED) theXrefutil.m_proxyholder = true; 00428 else theXrefutil.m_proxyholder = false; 00429 chkval = ::IsDlgButtonChecked(hWnd, IDC_CHK_NOANIM); 00430 if (chkval == BST_CHECKED) theXrefutil.m_ignoreanimholder = true; 00431 else theXrefutil.m_ignoreanimholder = false; 00432 00433 EndDialog(hWnd,1); 00434 break; 00435 00436 default: 00437 return FALSE; 00438 00439 } 00440 00441 default: 00442 return FALSE; 00443 } 00444 00445 return TRUE; 00446 00447 } 00448 00449 00450 bool Xrefutil::DoPickObjDialog() 00451 { 00452 // populate IDD_PICKOBJ IDC_LIST_NODES with objnames, 00453 // show dialog modal, get and return results 00454 int dlgres = 0; 00455 00456 dlgres = DialogBox(hInstance, 00457 MAKEINTRESOURCE(IDD_PICKOBJ), 00458 m_hPanel, 00459 PickObjDlgProc); 00460 if (dlgres == 1) return true; 00461 return false; // failure 00462 } 00463 00464 00465 static BOOL CALLBACK ExportDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 00466 { 00467 TSTR * contents = NULL; 00468 00469 switch (message) { 00470 00471 case WM_INITDIALOG: 00472 // (populate the editbox) 00473 contents = (TSTR *) lParam; 00474 ::SetDlgItemText(hWnd, IDC_EDIT_EXPRES, (TCHAR*)(*contents)); 00475 CenterWindow(hWnd,GetParent(hWnd)); 00476 break; 00477 00478 case WM_COMMAND: 00479 00480 switch (LOWORD(wParam)) { 00481 00482 case IDOK: 00483 EndDialog(hWnd,1); 00484 break; 00485 00486 default: 00487 return FALSE; 00488 00489 } 00490 00491 default: 00492 return FALSE; 00493 } 00494 00495 return TRUE; 00496 00497 } 00498 00499 00501 // xref methods 00502 00503 // (XRef Scene methods) 00504 00505 void Xrefutil::AddNewXrefScene(HWND hWnd) 00506 { 00507 // *** AddNewXrefScene *** 00508 // 00509 // Add new XRef'd Scene to current scene -- simply get the 00510 // name of the .MAX file, and use RootNode::AddNewXRefFile 00511 // to hook that xref'd scene into the current scene hierarchy 00512 // 00513 // Note: Don't confuse XRef scenes with XRef objects. 00514 // XRef scenes live as special children of the (single) root 00515 // node of the current scene, basically as "XRef'd root nodes" 00516 // of the external scene. Note that merged-in XRef Root Nodes 00517 // can come from original scenes that already had another scene 00518 // xref'd in, and so-on. See the SDK documentation on the 00519 // various XRef Scene APIs on the INode object, keeping in mind 00520 // that these APIs only function when the INode is in fact the 00521 // root node of the scene. 00522 // 00523 00524 TSTR filename = ""; 00525 INode * pRootNode = m_pInterface->GetRootNode(); 00526 if (!pRootNode) { 00527 // well, this is actually _really_ bad, but we just exit 00528 return; 00529 } 00530 if (!DoOpenSaveDialog(filename, true)) { 00531 // either cancel or fail, just return 00532 return; 00533 } 00534 pRootNode->AddNewXRefFile(filename, TRUE); 00535 m_pInterface->RedrawViews(m_pInterface->GetTime()); 00536 } 00537 00538 00539 void Xrefutil::ConvertSelectedToXrefScene(HWND hWnd) 00540 { 00541 // *** ConvertSelectedToXrefScene *** 00542 // 00543 // Relatively simple -- take the selected nodes, save them out 00544 // to a new .MAX file, delete the selected nodes from the 00545 // current scene, and then do a RootNode->AddNewXRefFile 00546 // with the just-saved MAX file. 00547 00548 INode * pNode = NULL; 00549 TSTR filename = ""; 00550 int i; 00551 00552 INode * pRootNode = m_pInterface->GetRootNode(); 00553 if (!pRootNode) { 00554 // well, this is actually _really_ bad, but we just exit 00555 return; 00556 } 00557 if (m_pInterface->GetSelNodeCount() == 0) { 00558 ::MessageBox(hWnd, GetString(IDS_ERR3), ERROR_TITLE, MB_ICONSTOP | MB_OK); 00559 return; 00560 } 00561 00562 Tab<INode *> nodetab; 00563 nodetab.ZeroCount(); 00564 nodetab.Shrink(); 00565 for (i = 0; i < m_pInterface->GetSelNodeCount(); i++) { 00566 pNode = m_pInterface->GetSelNode(i); 00567 nodetab.Append(1, &pNode, 5); 00568 } 00569 00570 if (!DoOpenSaveDialog(filename)) { 00571 // either cancel or fail, just return 00572 return; 00573 } 00574 00575 m_pInterface->FileSaveSelected(filename); 00576 00577 // delete selected nodes, don't refresh yet 00578 for (i = 0; i < nodetab.Count(); i++) { 00579 nodetab[i]->Delete(0,TRUE); 00580 } 00581 00582 // add in the nodes we saved out as an xref'd scene 00583 pRootNode->AddNewXRefFile(filename, TRUE); 00584 00585 m_pInterface->RedrawViews(m_pInterface->GetTime()); 00586 00587 } 00588 00589 00590 void Xrefutil::RefreshAllXrefScenes(HWND hWnd) 00591 { 00592 // *** RefreshAllXrefScenes *** 00593 // 00594 // Refreshes all Xref'd Scenes -- mimics 00595 // MAX's "Update Now" button on XRef Scene dialog, 00596 // with all xref'd scenes selected. 00597 // 00598 // Simply walk through all XRef'd scenes (hanging off root) 00599 // and use RootNode::ReloadXRef() to reload each one. 00600 00601 INode * pNode = NULL; 00602 int i, numxrefscenes; 00603 00604 INode * pRootNode = m_pInterface->GetRootNode(); 00605 if (!pRootNode) { 00606 // well, this is actually _really_ bad, but we just exit 00607 return; 00608 } 00609 numxrefscenes = pRootNode->GetXRefFileCount(); 00610 for (i = 0; i < numxrefscenes; i++) { 00611 pRootNode->ReloadXRef(i); 00612 } 00613 } 00614 00615 00616 void Xrefutil::MergeAllXrefScenes(HWND hWnd) 00617 { 00618 // *** MergeAllXrefScenes *** 00619 // 00620 // Merges ("binds") all XRef Scenes into the 00621 // current scene. Just using RootNode::BindXRefFile 00622 // will merge in the real scene objects from the xref'd 00623 // scene as real modifiable objects, and delete the 00624 // xref scene link from the scene hierarchy. 00625 00626 INode * pNode = NULL; 00627 int i, numxrefscenes; 00628 00629 INode * pRootNode = m_pInterface->GetRootNode(); 00630 if (!pRootNode) { 00631 // well, this is actually _really_ bad, but we just exit 00632 return; 00633 } 00634 numxrefscenes = pRootNode->GetXRefFileCount(); 00635 for (i = 0; i < numxrefscenes; i++) { 00636 pRootNode->BindXRefFile(i); 00637 } 00638 00639 } 00640 00641 00642 // (XRef Object methods) 00643 00644 00645 void Xrefutil::NodeToXref(INode * pNode, TSTR &filename, bool bProxy, bool bIgnoreAnim) 00646 { 00647 IXRefObject * pXRef = (IXRefObject *)m_pInterface->CreateInstance(SYSTEM_CLASS_ID, 00648 Class_ID(XREFOBJ_CLASS_ID,0)); 00649 TSTR obName = TSTR(pNode->GetName()); 00650 pXRef->Init(filename, obName, pNode->GetObjectRef(), bProxy); 00651 pNode->SetObjectRef(pXRef); 00652 // also, set visual queue that we're ignoring anim if we did 00653 if (bIgnoreAnim) 00654 pXRef->SetIgnoreAnim(TRUE,FALSE); 00655 } 00656 00657 void Xrefutil::DeleteAllAnimation(ReferenceTarget *ref) 00658 { 00659 DeleteAllAnimEnum en(TRUE); 00660 ref->EnumAnimTree(&en,NULL,0); 00661 } 00662 00663 00664 void Xrefutil::AddNewXrefObject(HWND hWnd) 00665 { 00666 // *** AddNewXrefObject *** 00667 // 00668 // Tries to mimic the functionality of MAX's Add New XRef Object 00669 // 00670 // Does the following: 00671 // 1) Gets a source .MAX filename from user to get the XRef Object from 00672 // 1.5) Flags current nodes for later 00673 // 2) Merges in this file, but doesn't display end results (yet) 00674 // 3) Walks list of file nodes (not flagged), and lets user pick one to XRef 00675 // 4) Blows away all merged (not flagged) nodes except picked node. Converts 00676 // the chosen node into an XRef node (see ConvertSelectedToXrefObject below) 00677 // with default settings 00678 00679 INode * pNode = NULL; 00680 TSTR filename = ""; 00681 TSTR pickedname = ""; 00682 TSTR * workname = NULL; 00683 00684 INode * pRootNode = m_pInterface->GetRootNode(); 00685 if (!pRootNode) { 00686 // well, this is actually _really_ bad, but we just exit 00687 return; 00688 } 00689 if (!DoOpenSaveDialog(filename, true)) { 00690 // either cancel or fail, just return 00691 return; 00692 } 00693 00694 // in preparation for merge, flag current scene nodes 00695 NodeFlagger newFlagger(A_WORK1); 00696 newFlagger.Enumerate(pRootNode); 00697 00698 // merge in user-picked file into current scene 00699 // NOTE: We just skip anything in xref'd file that has the same name 00700 // as an object in the current scene 00701 if (! m_pInterface->MergeFromFile(filename, TRUE, FALSE, FALSE, MERGE_DUPS_SKIP, NULL)) { 00702 // error, merge failed 00703 newFlagger.set_clear(true); 00704 newFlagger.Enumerate(pRootNode); 00705 return; 00706 } 00707 00708 // walk scene and build list of non-flagged nodes 00709 m_objnamesholder.ZeroCount(); 00710 m_objnamesholder.Shrink(); 00711 UnflaggedNodeNamer newNamer; 00712 newNamer.m_namelist = &m_objnamesholder; 00713 newNamer.Enumerate(pRootNode); 00714 UnflaggedNodeDeleter newDeleter; 00715 00716 // present list of nodes to user, sep. modal dialog 00717 if (DoPickObjDialog() && m_picknameholder.length() > 0) { 00718 pNode = m_pInterface->GetINodeByName(m_picknameholder); 00719 if (pNode) { 00720 if (m_ignoreanimholder && pNode->IsAnimated()) { 00721 // if animation is ignored, we basically go through 00722 // the node and delete all the keys for the node's controllers 00723 // Note that this won't remove animation for procedural controllers 00724 DeleteAllAnimation(pNode); 00725 } 00726 NodeToXref(pNode, filename, m_proxyholder, m_ignoreanimholder); 00727 // flag this converted node so we keep it 00728 pNode->SetAFlag(A_WORK1); 00729 } 00730 } 00731 00732 // deleted non-flagged nodes, un-flag original nodes, and return 00733 newDeleter.Enumerate(pRootNode); 00734 newFlagger.set_clear(true); 00735 newFlagger.Enumerate(pRootNode); 00736 for (int delme = 0; delme < m_objnamesholder.Count(); delme++) { 00737 // (clean up TSTRs) 00738 delete m_objnamesholder[delme]; 00739 } 00740 00741 m_pInterface->RedrawViews(m_pInterface->GetTime()); 00742 00743 } 00744 00745 00746 void Xrefutil::ConvertSelectedToXrefObject(HWND hWnd) 00747 { 00748 // *** ConvertSelectedToXrefObject *** 00749 // 00750 // Takes single selection of scene object and 00751 // "converts" it to XRef Object with default settings. This is 00752 // done via the following steps: 00753 // 00754 // 1) Save selected object into new MAX file of user's specification 00755 // 2) Create a new XRefObject 00756 // 3) XRefObject makes reference to the objectref of the selected node (OSM-level only) 00757 // 4) INode object reference changed to XRefObject 00758 // 00759 // converting multiple selected objects is left as an exercise 00760 // but should be quite straight-forward... 00761 00762 INode * pNode = NULL; 00763 TSTR filename = ""; 00764 00765 if (m_pInterface->GetSelNodeCount() != 1) { 00766 ::MessageBox(hWnd, GetString(IDS_ERR1), ERROR_TITLE, MB_ICONSTOP | MB_OK); 00767 return; 00768 } 00769 pNode = m_pInterface->GetSelNode(0); 00770 if (!pNode) { 00771 ::MessageBox(hWnd, GetString(IDS_ERR2), ERROR_TITLE, MB_ICONSTOP | MB_OK); 00772 return; 00773 } 00774 00775 if (!DoOpenSaveDialog(filename)) { 00776 // either cancel or fail, just return 00777 return; 00778 } 00779 m_pInterface->FileSaveSelected(filename); 00780 00781 // One caveat : If the object (not node) has any ReferenceMakers watching it 00782 // that are expecting geom pipeline msgs, you may need to remove/reset these 00783 // references. After the conversion, the original object is effectively 00784 // hidden from the pipeline. 00785 00786 NodeToXref(pNode, filename, false); 00787 00788 // leave all XRef settings at defaults for this sample 00789 00790 m_pInterface->RedrawViews(m_pInterface->GetTime()); 00791 00792 } 00793 00794 00795 void Xrefutil::ExportXrefObjects(HWND hWnd) 00796 { 00797 // *** ExportXrefObjects *** 00798 // 00799 // Does a "fake" export of all the XRef objects in the scene. 00800 // Basically, walks the scene looking for XRef objects, pulls 00801 // some information from them, and spits this out into a text 00802 // format, which is then displayed in a modal dialog. 00803 // 00804 // In reality, this sort of process would be more applicable 00805 // to a MAX Exporter plug-in. During SceneExport::DoExport 00806 // the INodes would be walked, looking for XRef objects, and 00807 // the exporter could then export whatever was needed from 00808 // them. 00809 // 00810 // The important distinction between XRef scenes and XRef 00811 // objects is again shown here -- XRef objects, to be manipulated 00812 // must be found in the scene via ClassID (like any typical 00813 // scene object), and then treated as IXRefObject pointers. 00814 // Do NOT use the XRef methods off of INode, since these only 00815 // apply to xref scenes and only work from the scene root INode. 00816 00817 TSTR * resbuffer = NULL; 00818 00819 // walk the scene starting at the root, looking for xref objects 00820 INode * pRootNode = m_pInterface->GetRootNode(); 00821 if (!pRootNode) { 00822 // well, this is actually _really_ bad, but we just exit 00823 return; 00824 } 00825 resbuffer = new TSTR; 00826 if (!resbuffer) return; 00827 00828 XRefObjFinder newfinder; 00829 newfinder.m_buffer = resbuffer; 00830 // (see XRefObjFinder::Proc above for details) 00831 newfinder.Enumerate(pRootNode); 00832 00833 // finally, display results in dialog 00834 int dlgres = DialogBoxParam(hInstance, 00835 MAKEINTRESOURCE(IDD_DLGEXPORT), 00836 m_hPanel, 00837 ExportDlgProc, 00838 (LPARAM)resbuffer); 00839 00840 delete resbuffer; 00841 }
Generated at Mon Nov 6 14:11:58 2000 by 1.2.3 written by Dimitri van Heesch, © 1997-2000