BoneMgrDialog.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. // BoneMgrDialog.cpp : implementation file
  19. //
  20. #include "StdAfx.H"
  21. #include "W3DView.H"
  22. #include "BoneMgrDialog.H"
  23. #include "HTree.H"
  24. #include "AssetMgr.H"
  25. #include "Utils.H"
  26. #include "MainFrm.H"
  27. #include "W3DViewDoc.H"
  28. #include "DataTreeView.H"
  29. //#include "HModel.H"
  30. #ifdef _DEBUG
  31. #define new DEBUG_NEW
  32. #undef THIS_FILE
  33. static char THIS_FILE[] = __FILE__;
  34. #endif
  35. ///////////////////////////////////////////////////////////////
  36. //
  37. // BoneMgrDialogClass
  38. //
  39. BoneMgrDialogClass::BoneMgrDialogClass
  40. (
  41. RenderObjClass *prender_obj,
  42. CWnd *pparent
  43. )
  44. : m_pBaseModel (prender_obj),
  45. m_pBackupModel (NULL),
  46. m_bAttach (true),
  47. CDialog (BoneMgrDialogClass::IDD, pparent)
  48. {
  49. //{{AFX_DATA_INIT(BoneMgrDialogClass)
  50. // NOTE: the ClassWizard will add member initialization here
  51. //}}AFX_DATA_INIT
  52. return ;
  53. }
  54. ///////////////////////////////////////////////////////////////
  55. //
  56. // DoDataExchange
  57. //
  58. void
  59. BoneMgrDialogClass::DoDataExchange (CDataExchange* pDX)
  60. {
  61. CDialog::DoDataExchange(pDX);
  62. //{{AFX_DATA_MAP(BoneMgrDialogClass)
  63. DDX_Control(pDX, IDC_OBJECT_COMBO, m_ObjectCombo);
  64. DDX_Control(pDX, IDC_BONE_TREE, m_BoneTree);
  65. //}}AFX_DATA_MAP
  66. return ;
  67. }
  68. BEGIN_MESSAGE_MAP(BoneMgrDialogClass, CDialog)
  69. //{{AFX_MSG_MAP(BoneMgrDialogClass)
  70. ON_NOTIFY(TVN_SELCHANGED, IDC_BONE_TREE, OnSelchangedBoneTree)
  71. ON_CBN_SELCHANGE(IDC_OBJECT_COMBO, OnSelchangeObjectCombo)
  72. ON_WM_DESTROY()
  73. ON_BN_CLICKED(IDC_ATTACH_BUTTON, OnAttachButton)
  74. //}}AFX_MSG_MAP
  75. END_MESSAGE_MAP()
  76. ///////////////////////////////////////////////////////////////
  77. //
  78. // OnInitDialog
  79. //
  80. BOOL
  81. BoneMgrDialogClass::OnInitDialog (void)
  82. {
  83. // Allow the base class to process this message
  84. CDialog::OnInitDialog ();
  85. // Make a backup of this hierarchy in case we need to restore it.
  86. m_pBackupModel = m_pBaseModel->Clone ();
  87. // Create an icon imagelist for the tree control
  88. CImageList *pimagelist = new CImageList;
  89. pimagelist->Create (16, 16, ILC_COLOR | ILC_MASK, 2, 2);
  90. // Load this icon and add it to our imagelist
  91. pimagelist->Add ((HICON)::LoadImage (::AfxGetResourceHandle (),
  92. MAKEINTRESOURCE (IDI_FOLDER),
  93. IMAGE_ICON,
  94. 16,
  95. 16,
  96. LR_SHARED));
  97. pimagelist->Add ((HICON)::LoadImage (::AfxGetResourceHandle (),
  98. MAKEINTRESOURCE (IDI_OBJECT),
  99. IMAGE_ICON,
  100. 16,
  101. 16,
  102. LR_SHARED));
  103. m_BoneTree.SetImageList (pimagelist, TVSIL_NORMAL);
  104. // Get the hierarchy tree for this model so we can enumerate bone's
  105. // and subobjects.
  106. HTREEITEM hfirst_item = NULL;
  107. // Loop through all the bones in this model
  108. int bone_count = m_pBaseModel->Get_Num_Bones ();
  109. for (int index = 0; index < bone_count; index ++) {
  110. const char *pbone_name = m_pBaseModel->Get_Bone_Name (index);
  111. // Add this bone to the tree control
  112. HTREEITEM hbone_item = m_BoneTree.InsertItem (pbone_name, 0, 0);
  113. Fill_Bone_Item (hbone_item, index);
  114. // Is this the first item we've added to the tree?
  115. if (hfirst_item == NULL) {
  116. hfirst_item = hbone_item;
  117. }
  118. }
  119. //
  120. // Sort the tree control
  121. //
  122. m_BoneTree.SortChildren (TVI_ROOT);
  123. // Build a list of all the render objects currently loaded
  124. CW3DViewDoc *pdoc = (CW3DViewDoc *)((CMainFrame *)::AfxGetMainWnd())->GetActiveDocument ();
  125. CDataTreeView *pdata_tree = pdoc->GetDataTreeView ();
  126. DynamicVectorClass <CString> asset_list;
  127. pdata_tree->Build_Render_Object_List (asset_list);
  128. // Add this render object list to the combobox
  129. for (index = 0; index < asset_list.Count (); index ++) {
  130. m_ObjectCombo.AddString (asset_list[index]);
  131. }
  132. // Select the first entry in the combobox
  133. m_ObjectCombo.SetCurSel (0);
  134. OnSelchangeObjectCombo ();
  135. // Select the first item in the tree
  136. m_BoneTree.SelectItem (hfirst_item);
  137. return TRUE;
  138. }
  139. ///////////////////////////////////////////////////////////////
  140. //
  141. // Fill_Bone_Item
  142. //
  143. void
  144. BoneMgrDialogClass::Fill_Bone_Item
  145. (
  146. HTREEITEM hbone_item,
  147. int bone_index
  148. )
  149. {
  150. // Create a new instance of the hmodel which we can use
  151. // to compare with the supplied hmodel and determine
  152. // which 'bones-models' are new.
  153. const char *orig_model_name = m_pBaseModel->Get_Base_Model_Name ();
  154. orig_model_name = (orig_model_name == NULL) ? m_pBaseModel->Get_Name () : orig_model_name;
  155. RenderObjClass *porig_model = WW3DAssetManager::Get_Instance()->Create_Render_Obj (orig_model_name);
  156. // Build a list of nodes that are contained in the vanilla model
  157. DynamicVectorClass <RenderObjClass *> orig_node_list;
  158. for (int index = 0;
  159. index < porig_model->Get_Num_Sub_Objects_On_Bone (bone_index);
  160. index ++) {
  161. RenderObjClass *psubobj = porig_model->Get_Sub_Object_On_Bone (index, bone_index);
  162. if (psubobj != NULL) {
  163. orig_node_list.Add (psubobj);
  164. }
  165. }
  166. // Build a list of nodes that are contained in this bone
  167. DynamicVectorClass <RenderObjClass *> node_list;
  168. for (index = 0;
  169. index < m_pBaseModel->Get_Num_Sub_Objects_On_Bone (bone_index);
  170. index ++) {
  171. RenderObjClass *psubobj = m_pBaseModel->Get_Sub_Object_On_Bone (index, bone_index);
  172. if (psubobj != NULL) {
  173. node_list.Add (psubobj);
  174. }
  175. }
  176. if (node_list.Count () > 0) {
  177. // Add the subobjects to the tree control
  178. for (int node_index = 0; node_index < node_list.Count (); node_index ++) {
  179. RenderObjClass *psubobject = node_list[node_index];
  180. ASSERT (psubobject != NULL);
  181. // Is this subobject new? (i.e. not in a 'vanilla' instance?)
  182. if (psubobject != NULL &&
  183. (Is_Object_In_List (psubobject->Get_Name (), orig_node_list) == false)) {
  184. m_BoneTree.InsertItem (psubobject->Get_Name (), 1, 1, hbone_item);
  185. }
  186. }
  187. }
  188. // Free our hold on the render objs in the original node list
  189. for (index = 0; index < orig_node_list.Count (); index ++) {
  190. MEMBER_RELEASE (orig_node_list[index]);
  191. }
  192. // Free our hold on the render objs in the node list
  193. for (index = 0; index < node_list.Count (); index ++) {
  194. MEMBER_RELEASE (node_list[index]);
  195. }
  196. MEMBER_RELEASE (porig_model);
  197. return ;
  198. }
  199. ///////////////////////////////////////////////////////////////////////////////////
  200. //
  201. // Is_Object_In_List
  202. //
  203. bool
  204. BoneMgrDialogClass::Is_Object_In_List
  205. (
  206. const char *passet_name,
  207. DynamicVectorClass <RenderObjClass *> &node_list
  208. )
  209. {
  210. // Assume failure
  211. bool retval = false;
  212. // Loop through the nodes in the list until we've found the one
  213. // were are looking for.
  214. for (int node_index = 0; (node_index < node_list.Count ()) && (retval == false); node_index ++) {
  215. RenderObjClass *prender_obj = node_list[node_index];
  216. // Is this the render object we were looking for?
  217. if (prender_obj != NULL &&
  218. ::lstrcmpi (prender_obj->Get_Name (), passet_name) == 0) {
  219. retval = true;
  220. }
  221. }
  222. // Return the true/false result code
  223. return retval;
  224. }
  225. ///////////////////////////////////////////////////////////////////////////////////
  226. //
  227. // OnSelchangedBoneTree
  228. //
  229. void
  230. BoneMgrDialogClass::OnSelchangedBoneTree
  231. (
  232. NMHDR *pNMHDR,
  233. LRESULT *pResult
  234. )
  235. {
  236. // Make the dialog controls reflect the new selection
  237. NM_TREEVIEW *pNMTreeView = (NM_TREEVIEW *)pNMHDR;
  238. Update_Controls (pNMTreeView->itemNew.hItem);
  239. (*pResult) = 0;
  240. return ;
  241. }
  242. ///////////////////////////////////////////////////////////////////////////////////
  243. //
  244. // OnSelchangeObjectCombo
  245. //
  246. void
  247. BoneMgrDialogClass::OnSelchangeObjectCombo (void)
  248. {
  249. // Get the name of the currently selected render object
  250. CString name;
  251. int index = m_ObjectCombo.GetCurSel ();
  252. m_ObjectCombo.GetLBText (index, name);
  253. // Change the text of the 'Attach' button based on whether or not
  254. // this render object is already attached to the current bone.
  255. if (Is_Render_Obj_Already_Attached (name)) {
  256. SetDlgItemText (IDC_ATTACH_BUTTON, "&Remove");
  257. m_bAttach = false;
  258. } else {
  259. SetDlgItemText (IDC_ATTACH_BUTTON, "&Attach");
  260. m_bAttach = true;
  261. }
  262. return ;
  263. }
  264. ///////////////////////////////////////////////////////////////////////////////////
  265. //
  266. // Is_Render_Obj_Already_Attached
  267. //
  268. bool
  269. BoneMgrDialogClass::Is_Render_Obj_Already_Attached (const CString &name)
  270. {
  271. // Assume not already attached
  272. bool retval = false;
  273. HTREEITEM htree_item = m_BoneTree.GetSelectedItem ();
  274. HTREEITEM hparent_item = m_BoneTree.GetParentItem (htree_item);
  275. htree_item = (hparent_item != NULL) ? hparent_item : htree_item;
  276. if (htree_item != NULL) {
  277. // Loop through all the children of this bone
  278. for (HTREEITEM hchild_item = m_BoneTree.GetChildItem (htree_item);
  279. (hchild_item != NULL) && (retval == false);
  280. hchild_item = m_BoneTree.GetNextSiblingItem (hchild_item)) {
  281. // Is this the render object we were looking for?
  282. CString child_name = m_BoneTree.GetItemText (hchild_item);
  283. if (name.CompareNoCase (child_name) == 0) {
  284. retval = true;
  285. }
  286. }
  287. }
  288. // Return the true/false result code
  289. return retval;
  290. }
  291. ///////////////////////////////////////////////////////////////////////////////////
  292. //
  293. // Update_Controls
  294. //
  295. void
  296. BoneMgrDialogClass::Update_Controls (HTREEITEM selected_item)
  297. {
  298. // Get the name of the currently selected item
  299. CString name = m_BoneTree.GetItemText (selected_item);
  300. bool bis_bone = (m_BoneTree.GetParentItem (selected_item) == NULL);
  301. // Did the user select a bone name?
  302. if (bis_bone) {
  303. m_BoneName = name;
  304. } else {
  305. // Select this render object in the combobox
  306. int index = m_ObjectCombo.FindStringExact (-1, name);
  307. m_ObjectCombo.SetCurSel ((index != CB_ERR) ? index : 0);
  308. // The bone name is the name of the parent item of the selected item.
  309. m_BoneName = m_BoneTree.GetItemText (m_BoneTree.GetParentItem (selected_item));
  310. }
  311. OnSelchangeObjectCombo ();
  312. // Change the text of the group box to reflect the bone name
  313. CString text;
  314. text.Format ("Bone: %s", m_BoneName);
  315. SetDlgItemText (IDC_BONE_GROUPBOX, text);
  316. return ;
  317. }
  318. ///////////////////////////////////////////////////////////////////////////////////
  319. //
  320. // OnDestroy
  321. //
  322. void
  323. BoneMgrDialogClass::OnDestroy (void)
  324. {
  325. // Free the state image list we associated with the control
  326. CImageList *pimagelist = m_BoneTree.GetImageList (TVSIL_NORMAL);
  327. m_BoneTree.SetImageList (NULL, TVSIL_NORMAL);
  328. SAFE_DELETE (pimagelist);
  329. // Allow the base class to process this message
  330. CDialog::OnDestroy ();
  331. return ;
  332. }
  333. ///////////////////////////////////////////////////////////////////////////////////
  334. //
  335. // OnOK
  336. //
  337. void
  338. BoneMgrDialogClass::OnOK (void)
  339. {
  340. // Simply forget about the backup we made
  341. MEMBER_RELEASE (m_pBackupModel);
  342. // Update the hierarchy's cached information to reflect the new settings
  343. CW3DViewDoc *pdoc = (CW3DViewDoc *)((CMainFrame *)::AfxGetMainWnd())->GetActiveDocument ();
  344. pdoc->Update_Aggregate_Prototype (*m_pBaseModel);
  345. // Allow the base class to process this message
  346. CDialog::OnOK ();
  347. return ;
  348. }
  349. ///////////////////////////////////////////////////////////////////////////////////
  350. //
  351. // OnCancel
  352. //
  353. void
  354. BoneMgrDialogClass::OnCancel (void)
  355. {
  356. CWaitCursor wait_cursor;
  357. // Display the backup hierarchy
  358. CW3DViewDoc *pdoc = (CW3DViewDoc *)((CMainFrame *)::AfxGetMainWnd())->GetActiveDocument ();
  359. pdoc->DisplayObject (m_pBackupModel, false, false);
  360. // Allow the base class to process this message
  361. CDialog::OnCancel ();
  362. return ;
  363. }
  364. ///////////////////////////////////////////////////////////////////////////////////
  365. //
  366. // OnAttachButton
  367. //
  368. void
  369. BoneMgrDialogClass::OnAttachButton (void)
  370. {
  371. // Get the name of the currently selected render object
  372. CString name;
  373. int index = m_ObjectCombo.GetCurSel ();
  374. m_ObjectCombo.GetLBText (index, name);
  375. // Lookup the currently selected bone item
  376. HTREEITEM hbone_item = Get_Current_Bone_Item ();
  377. // Should we attach or remove the render object?
  378. if (m_bAttach) {
  379. // Create an instance of the render object and attach it to the bone
  380. RenderObjClass *prender_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj (name);
  381. if (prender_obj != NULL) {
  382. m_pBaseModel->Add_Sub_Object_To_Bone (prender_obj, m_BoneName);
  383. m_BoneTree.InsertItem (name, 1, 1, hbone_item);
  384. MEMBER_RELEASE (prender_obj);
  385. }
  386. } else {
  387. // Loop through all the subobjects on the bone
  388. bool found = false;
  389. int bone_index = m_pBaseModel->Get_Bone_Index (m_BoneName);
  390. int count = m_pBaseModel->Get_Num_Sub_Objects_On_Bone (bone_index);
  391. for (int index = 0; (index < count) && !found; index ++) {
  392. // Is this the subobject we were looking for?
  393. RenderObjClass *psub_obj = m_pBaseModel->Get_Sub_Object_On_Bone (index, bone_index);
  394. if ((psub_obj != NULL) &&
  395. (::lstrcmpi (psub_obj->Get_Name (), name) == 0)) {
  396. // Remove this subobject from the bone
  397. m_pBaseModel->Remove_Sub_Object (psub_obj);
  398. found = true;
  399. }
  400. // Release our hold on this pointer
  401. MEMBER_RELEASE (psub_obj);
  402. }
  403. // Remove the object from our UI
  404. Remove_Object_From_Bone (hbone_item, name);
  405. }
  406. // Refresh the UI state
  407. m_BoneTree.InvalidateRect (NULL, TRUE);
  408. m_BoneTree.UpdateWindow ();
  409. Update_Controls (hbone_item);
  410. return ;
  411. }
  412. ///////////////////////////////////////////////////////////////////////////////////
  413. //
  414. // Get_Current_Bone_Item
  415. //
  416. HTREEITEM
  417. BoneMgrDialogClass::Get_Current_Bone_Item (void)
  418. {
  419. // Get the currently selected item and its parent
  420. HTREEITEM htree_item = m_BoneTree.GetSelectedItem ();
  421. HTREEITEM hparent_item = m_BoneTree.GetParentItem (htree_item);
  422. // Return the bone item
  423. return (hparent_item != NULL) ? hparent_item : htree_item;
  424. }
  425. ///////////////////////////////////////////////////////////////////////////////////
  426. //
  427. // Remove_Object_From_Bone
  428. //
  429. void
  430. BoneMgrDialogClass::Remove_Object_From_Bone
  431. (
  432. HTREEITEM bone_item,
  433. const CString &name
  434. )
  435. {
  436. // Loop through all the children of this bone
  437. for (HTREEITEM hchild_item = m_BoneTree.GetChildItem (bone_item);
  438. (hchild_item != NULL);
  439. hchild_item = m_BoneTree.GetNextSiblingItem (hchild_item)) {
  440. // Is this the render object we were looking for?
  441. CString child_name = m_BoneTree.GetItemText (hchild_item);
  442. if (name.CompareNoCase (child_name) == 0) {
  443. m_BoneTree.DeleteItem (hchild_item);
  444. break ;
  445. }
  446. }
  447. return ;
  448. }