/* ** 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 . */ // AnimReportPage.cpp : implementation file // #include "stdafx.h" #include "w3dview.h" #include "W3DViewDoc.h" #include "AdvancedAnimSheet.h" #include "AnimReportPage.h" #include "HAnim.h" #include "HTree.h" #include "Utils.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CAnimReportPage property page IMPLEMENT_DYNCREATE(CAnimReportPage, CPropertyPage) CAnimReportPage::CAnimReportPage(CAdvancedAnimSheet *sheet) : CPropertyPage(CAnimReportPage::IDD), m_Sheet(sheet) { //{{AFX_DATA_INIT(CAnimReportPage) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT } CAnimReportPage::~CAnimReportPage() { } void CAnimReportPage::DoDataExchange(CDataExchange* pDX) { CPropertyPage::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAnimReportPage) DDX_Control(pDX, IDC_ANIM_REPORT, m_AnimReport); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAnimReportPage, CPropertyPage) //{{AFX_MSG_MAP(CAnimReportPage) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CAnimReportPage message handlers BOOL CAnimReportPage::OnInitDialog() { CPropertyPage::OnInitDialog(); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void CAnimReportPage::FillListControl() { // Add the first column to the animation report. All bone names will // go in this column. m_AnimReport.InsertColumn(0, "Bone Name"); // Get the current render object and it's HTree. If it doesn't have // an HTree, then it's not animating and we're not interested. RenderObjClass *robj = ::GetCurrentDocument()->GetDisplayedObject(); if (robj == NULL) return; const HTreeClass *htree = robj->Get_HTree(); if (htree == NULL) return; // Get a sorted array of animations that affect the currently active object. HAnimClass **anim = m_Sheet->GetAnims(); int anim_count = m_Sheet->GetAnimCount(); /* ** Create a column in the report view for each animation. */ int column_count; // number of columns in the report view (EXCLUDING the bone column) bool indirect; // true if we have one column per animation selected in the mixing page if (m_Sheet->m_SelectedAnims.Count() > 0) { column_count = m_Sheet->m_SelectedAnims.Count(); indirect = true; } else { column_count = anim_count; indirect = false; } int i, j; int anim_idx; // column index, essentially (anim_idx + 1 == column for that anim) for (i = 0; i < column_count; i++) { // Add a new column with the name of this animation as the title. anim_idx = indirect ? m_Sheet->m_SelectedAnims[i] : i; if (m_AnimReport.InsertColumn(anim_idx+1, anim[anim_idx]->Get_Name(), LVCFMT_CENTER) == -1) { // Failed to add a new column to the list control. ASSERT(false); } } /* ** Add a row to the report for each bone in the object affected by ** an animation. It's entry (row/col) will contain the animation ** channels with valid animation data. */ for (i = 0; i < column_count; i++) { // Grab the anim pointer directly. anim_idx = indirect ? m_Sheet->m_SelectedAnims[i] : i; HAnimClass *pAnim = anim[anim_idx]; // Which bones does it affect, and how? int num_bones = robj->Get_Num_Bones(); for (j = 1; j < num_bones; j++) // skip bone 0, which is always the root bone { // Add each bone to the report (regardless of animation status) // if it isn't already there. int idx = FindItem(htree->Get_Bone_Name(j)); if (idx == -1) { // Wasn't present, add a new item for this bone. idx = m_AnimReport.InsertItem(m_AnimReport.GetItemCount(), htree->Get_Bone_Name(j)); } if (pAnim->Is_Node_Motion_Present(j)) { // Add motion channel info to the appropriate column. char channels[6]; // strlen("XYZQV")+1 ZeroMemory(channels, sizeof(channels)); MakeChannelStr(j, pAnim, channels); m_AnimReport.SetItem(idx, i+1, LVIF_TEXT, channels, 0,0,0,0); } } } // Make the columns sized nicely. m_AnimReport.SetColumnWidth(0, LVSCW_AUTOSIZE); for (i = 0; i < column_count; i++) m_AnimReport.SetColumnWidth(i+1, LVSCW_AUTOSIZE_USEHEADER); // All done, the report view is set up. } int CAnimReportPage::FindItem (const char *item_name) { LVFINDINFO lvfi; lvfi.flags = LVFI_STRING; lvfi.psz = item_name; return m_AnimReport.FindItem(&lvfi); } void CAnimReportPage::MakeChannelStr (int bone_idx, HAnimClass *hanim, char *channels) { if (hanim->Has_X_Translation(bone_idx)) strcat(channels, "X"); if (hanim->Has_Y_Translation(bone_idx)) strcat(channels, "Y"); if (hanim->Has_Z_Translation(bone_idx)) strcat(channels, "Z"); if (hanim->Has_Rotation(bone_idx)) strcat(channels, "Q"); if (hanim->Has_Visibility(bone_idx)) strcat(channels, "V"); } BOOL CAnimReportPage::OnSetActive() { // Delete all info in the report view. m_AnimReport.DeleteAllItems(); while (m_AnimReport.DeleteColumn(0)); // Fill the list control each time we're set active so that // a change in selection on the mixing page will have an // effect on this page. FillListControl(); return CPropertyPage::OnSetActive(); }