This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Tools/W3DView/BoneMgrDialog.cpp

539 lines
14 KiB
C++

/*
** 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 <http://www.gnu.org/licenses/>.
*/
// 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 <CString> 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 <RenderObjClass *> 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 <RenderObjClass *> 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 <RenderObjClass *> &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 ;
}