/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ // BoneMgrDialog.cpp : implementation file // #include "StdAfx.H" #include "W3DView.H" #include "BoneMgrDialog.H" #include "HTree.H" #include "AssetMgr.H" #include "Utils.H" #include "MainFrm.H" #include "W3DViewDoc.H" #include "DataTreeView.H" //#include "HModel.H" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /////////////////////////////////////////////////////////////// // // BoneMgrDialogClass // BoneMgrDialogClass::BoneMgrDialogClass ( RenderObjClass *prender_obj, CWnd *pparent ) : m_pBaseModel (prender_obj), m_pBackupModel (NULL), m_bAttach (true), CDialog (BoneMgrDialogClass::IDD, pparent) { //{{AFX_DATA_INIT(BoneMgrDialogClass) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT return ; } /////////////////////////////////////////////////////////////// // // DoDataExchange // void BoneMgrDialogClass::DoDataExchange (CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(BoneMgrDialogClass) DDX_Control(pDX, IDC_OBJECT_COMBO, m_ObjectCombo); DDX_Control(pDX, IDC_BONE_TREE, m_BoneTree); //}}AFX_DATA_MAP return ; } BEGIN_MESSAGE_MAP(BoneMgrDialogClass, CDialog) //{{AFX_MSG_MAP(BoneMgrDialogClass) ON_NOTIFY(TVN_SELCHANGED, IDC_BONE_TREE, OnSelchangedBoneTree) ON_CBN_SELCHANGE(IDC_OBJECT_COMBO, OnSelchangeObjectCombo) ON_WM_DESTROY() ON_BN_CLICKED(IDC_ATTACH_BUTTON, OnAttachButton) //}}AFX_MSG_MAP END_MESSAGE_MAP() /////////////////////////////////////////////////////////////// // // OnInitDialog // BOOL BoneMgrDialogClass::OnInitDialog (void) { // Allow the base class to process this message CDialog::OnInitDialog (); // Make a backup of this hierarchy in case we need to restore it. m_pBackupModel = m_pBaseModel->Clone (); // Create an icon imagelist for the tree control CImageList *pimagelist = new CImageList; pimagelist->Create (16, 16, ILC_COLOR | ILC_MASK, 2, 2); // Load this icon and add it to our imagelist pimagelist->Add ((HICON)::LoadImage (::AfxGetResourceHandle (), MAKEINTRESOURCE (IDI_FOLDER), IMAGE_ICON, 16, 16, LR_SHARED)); pimagelist->Add ((HICON)::LoadImage (::AfxGetResourceHandle (), MAKEINTRESOURCE (IDI_OBJECT), IMAGE_ICON, 16, 16, LR_SHARED)); m_BoneTree.SetImageList (pimagelist, TVSIL_NORMAL); // Get the hierarchy tree for this model so we can enumerate bone's // and subobjects. HTREEITEM hfirst_item = NULL; // Loop through all the bones in this model int bone_count = m_pBaseModel->Get_Num_Bones (); for (int index = 0; index < bone_count; index ++) { const char *pbone_name = m_pBaseModel->Get_Bone_Name (index); // Add this bone to the tree control HTREEITEM hbone_item = m_BoneTree.InsertItem (pbone_name, 0, 0); Fill_Bone_Item (hbone_item, index); // Is this the first item we've added to the tree? if (hfirst_item == NULL) { hfirst_item = hbone_item; } } // // Sort the tree control // m_BoneTree.SortChildren (TVI_ROOT); // Build a list of all the render objects currently loaded CW3DViewDoc *pdoc = (CW3DViewDoc *)((CMainFrame *)::AfxGetMainWnd())->GetActiveDocument (); CDataTreeView *pdata_tree = pdoc->GetDataTreeView (); DynamicVectorClass asset_list; pdata_tree->Build_Render_Object_List (asset_list); // Add this render object list to the combobox for (index = 0; index < asset_list.Count (); index ++) { m_ObjectCombo.AddString (asset_list[index]); } // Select the first entry in the combobox m_ObjectCombo.SetCurSel (0); OnSelchangeObjectCombo (); // Select the first item in the tree m_BoneTree.SelectItem (hfirst_item); return TRUE; } /////////////////////////////////////////////////////////////// // // Fill_Bone_Item // void BoneMgrDialogClass::Fill_Bone_Item ( HTREEITEM hbone_item, int bone_index ) { // Create a new instance of the hmodel which we can use // to compare with the supplied hmodel and determine // which 'bones-models' are new. const char *orig_model_name = m_pBaseModel->Get_Base_Model_Name (); orig_model_name = (orig_model_name == NULL) ? m_pBaseModel->Get_Name () : orig_model_name; RenderObjClass *porig_model = WW3DAssetManager::Get_Instance()->Create_Render_Obj (orig_model_name); // Build a list of nodes that are contained in the vanilla model DynamicVectorClass orig_node_list; for (int index = 0; index < porig_model->Get_Num_Sub_Objects_On_Bone (bone_index); index ++) { RenderObjClass *psubobj = porig_model->Get_Sub_Object_On_Bone (index, bone_index); if (psubobj != NULL) { orig_node_list.Add (psubobj); } } // Build a list of nodes that are contained in this bone DynamicVectorClass node_list; for (index = 0; index < m_pBaseModel->Get_Num_Sub_Objects_On_Bone (bone_index); index ++) { RenderObjClass *psubobj = m_pBaseModel->Get_Sub_Object_On_Bone (index, bone_index); if (psubobj != NULL) { node_list.Add (psubobj); } } if (node_list.Count () > 0) { // Add the subobjects to the tree control for (int node_index = 0; node_index < node_list.Count (); node_index ++) { RenderObjClass *psubobject = node_list[node_index]; ASSERT (psubobject != NULL); // Is this subobject new? (i.e. not in a 'vanilla' instance?) if (psubobject != NULL && (Is_Object_In_List (psubobject->Get_Name (), orig_node_list) == false)) { m_BoneTree.InsertItem (psubobject->Get_Name (), 1, 1, hbone_item); } } } // Free our hold on the render objs in the original node list for (index = 0; index < orig_node_list.Count (); index ++) { MEMBER_RELEASE (orig_node_list[index]); } // Free our hold on the render objs in the node list for (index = 0; index < node_list.Count (); index ++) { MEMBER_RELEASE (node_list[index]); } MEMBER_RELEASE (porig_model); return ; } /////////////////////////////////////////////////////////////////////////////////// // // Is_Object_In_List // bool BoneMgrDialogClass::Is_Object_In_List ( const char *passet_name, DynamicVectorClass &node_list ) { // Assume failure bool retval = false; // Loop through the nodes in the list until we've found the one // were are looking for. for (int node_index = 0; (node_index < node_list.Count ()) && (retval == false); node_index ++) { RenderObjClass *prender_obj = node_list[node_index]; // Is this the render object we were looking for? if (prender_obj != NULL && ::lstrcmpi (prender_obj->Get_Name (), passet_name) == 0) { retval = true; } } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////////////////////// // // OnSelchangedBoneTree // void BoneMgrDialogClass::OnSelchangedBoneTree ( NMHDR *pNMHDR, LRESULT *pResult ) { // Make the dialog controls reflect the new selection NM_TREEVIEW *pNMTreeView = (NM_TREEVIEW *)pNMHDR; Update_Controls (pNMTreeView->itemNew.hItem); (*pResult) = 0; return ; } /////////////////////////////////////////////////////////////////////////////////// // // OnSelchangeObjectCombo // void BoneMgrDialogClass::OnSelchangeObjectCombo (void) { // Get the name of the currently selected render object CString name; int index = m_ObjectCombo.GetCurSel (); m_ObjectCombo.GetLBText (index, name); // Change the text of the 'Attach' button based on whether or not // this render object is already attached to the current bone. if (Is_Render_Obj_Already_Attached (name)) { SetDlgItemText (IDC_ATTACH_BUTTON, "&Remove"); m_bAttach = false; } else { SetDlgItemText (IDC_ATTACH_BUTTON, "&Attach"); m_bAttach = true; } return ; } /////////////////////////////////////////////////////////////////////////////////// // // Is_Render_Obj_Already_Attached // bool BoneMgrDialogClass::Is_Render_Obj_Already_Attached (const CString &name) { // Assume not already attached bool retval = false; HTREEITEM htree_item = m_BoneTree.GetSelectedItem (); HTREEITEM hparent_item = m_BoneTree.GetParentItem (htree_item); htree_item = (hparent_item != NULL) ? hparent_item : htree_item; if (htree_item != NULL) { // Loop through all the children of this bone for (HTREEITEM hchild_item = m_BoneTree.GetChildItem (htree_item); (hchild_item != NULL) && (retval == false); hchild_item = m_BoneTree.GetNextSiblingItem (hchild_item)) { // Is this the render object we were looking for? CString child_name = m_BoneTree.GetItemText (hchild_item); if (name.CompareNoCase (child_name) == 0) { retval = true; } } } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////////////////////// // // Update_Controls // void BoneMgrDialogClass::Update_Controls (HTREEITEM selected_item) { // Get the name of the currently selected item CString name = m_BoneTree.GetItemText (selected_item); bool bis_bone = (m_BoneTree.GetParentItem (selected_item) == NULL); // Did the user select a bone name? if (bis_bone) { m_BoneName = name; } else { // Select this render object in the combobox int index = m_ObjectCombo.FindStringExact (-1, name); m_ObjectCombo.SetCurSel ((index != CB_ERR) ? index : 0); // The bone name is the name of the parent item of the selected item. m_BoneName = m_BoneTree.GetItemText (m_BoneTree.GetParentItem (selected_item)); } OnSelchangeObjectCombo (); // Change the text of the group box to reflect the bone name CString text; text.Format ("Bone: %s", m_BoneName); SetDlgItemText (IDC_BONE_GROUPBOX, text); return ; } /////////////////////////////////////////////////////////////////////////////////// // // OnDestroy // void BoneMgrDialogClass::OnDestroy (void) { // Free the state image list we associated with the control CImageList *pimagelist = m_BoneTree.GetImageList (TVSIL_NORMAL); m_BoneTree.SetImageList (NULL, TVSIL_NORMAL); SAFE_DELETE (pimagelist); // Allow the base class to process this message CDialog::OnDestroy (); return ; } /////////////////////////////////////////////////////////////////////////////////// // // OnOK // void BoneMgrDialogClass::OnOK (void) { // Simply forget about the backup we made MEMBER_RELEASE (m_pBackupModel); // Update the hierarchy's cached information to reflect the new settings CW3DViewDoc *pdoc = (CW3DViewDoc *)((CMainFrame *)::AfxGetMainWnd())->GetActiveDocument (); pdoc->Update_Aggregate_Prototype (*m_pBaseModel); // Allow the base class to process this message CDialog::OnOK (); return ; } /////////////////////////////////////////////////////////////////////////////////// // // OnCancel // void BoneMgrDialogClass::OnCancel (void) { CWaitCursor wait_cursor; // Display the backup hierarchy CW3DViewDoc *pdoc = (CW3DViewDoc *)((CMainFrame *)::AfxGetMainWnd())->GetActiveDocument (); pdoc->DisplayObject (m_pBackupModel, false, false); // Allow the base class to process this message CDialog::OnCancel (); return ; } /////////////////////////////////////////////////////////////////////////////////// // // OnAttachButton // void BoneMgrDialogClass::OnAttachButton (void) { // Get the name of the currently selected render object CString name; int index = m_ObjectCombo.GetCurSel (); m_ObjectCombo.GetLBText (index, name); // Lookup the currently selected bone item HTREEITEM hbone_item = Get_Current_Bone_Item (); // Should we attach or remove the render object? if (m_bAttach) { // Create an instance of the render object and attach it to the bone RenderObjClass *prender_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj (name); if (prender_obj != NULL) { m_pBaseModel->Add_Sub_Object_To_Bone (prender_obj, m_BoneName); m_BoneTree.InsertItem (name, 1, 1, hbone_item); MEMBER_RELEASE (prender_obj); } } else { // Loop through all the subobjects on the bone bool found = false; int bone_index = m_pBaseModel->Get_Bone_Index (m_BoneName); int count = m_pBaseModel->Get_Num_Sub_Objects_On_Bone (bone_index); for (int index = 0; (index < count) && !found; index ++) { // Is this the subobject we were looking for? RenderObjClass *psub_obj = m_pBaseModel->Get_Sub_Object_On_Bone (index, bone_index); if ((psub_obj != NULL) && (::lstrcmpi (psub_obj->Get_Name (), name) == 0)) { // Remove this subobject from the bone m_pBaseModel->Remove_Sub_Object (psub_obj); found = true; } // Release our hold on this pointer MEMBER_RELEASE (psub_obj); } // Remove the object from our UI Remove_Object_From_Bone (hbone_item, name); } // Refresh the UI state m_BoneTree.InvalidateRect (NULL, TRUE); m_BoneTree.UpdateWindow (); Update_Controls (hbone_item); return ; } /////////////////////////////////////////////////////////////////////////////////// // // Get_Current_Bone_Item // HTREEITEM BoneMgrDialogClass::Get_Current_Bone_Item (void) { // Get the currently selected item and its parent HTREEITEM htree_item = m_BoneTree.GetSelectedItem (); HTREEITEM hparent_item = m_BoneTree.GetParentItem (htree_item); // Return the bone item return (hparent_item != NULL) ? hparent_item : htree_item; } /////////////////////////////////////////////////////////////////////////////////// // // Remove_Object_From_Bone // void BoneMgrDialogClass::Remove_Object_From_Bone ( HTREEITEM bone_item, const CString &name ) { // Loop through all the children of this bone for (HTREEITEM hchild_item = m_BoneTree.GetChildItem (bone_item); (hchild_item != NULL); hchild_item = m_BoneTree.GetNextSiblingItem (hchild_item)) { // Is this the render object we were looking for? CString child_name = m_BoneTree.GetItemText (hchild_item); if (name.CompareNoCase (child_name) == 0) { m_BoneTree.DeleteItem (hchild_item); break ; } } return ; }