// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "std_afx.h" #include "object_viewer.h" #include "animation_set_dlg.h" #include "nel/misc/file.h" #include "nel/3d/track_keyframer.h" using namespace NLMISC; using namespace NL3D; ///////////////////////////////////////////////////////////////////////////// // CAnimationSetDlg dialog CAnimationSetDlg::CAnimationSetDlg(CObjectViewer* objView, CWnd* pParent /*=NULL*/) : CDialog(CAnimationSetDlg::IDD, pParent) { //{{AFX_DATA_INIT(CAnimationSetDlg) UseMixer = 0; //}}AFX_DATA_INIT _ObjView=objView; } void CAnimationSetDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAnimationSetDlg) DDX_Control(pDX, IDC_EDITED_OBJECT, EditedObject); DDX_Control(pDX, IDC_PLAYLIST, PlayList); DDX_Control(pDX, IDC_TREE2, SkelTree); DDX_Control(pDX, IDC_TREE, Tree); DDX_Radio(pDX, IDC_USE_LIST, UseMixer); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAnimationSetDlg, CDialog) //{{AFX_MSG_MAP(CAnimationSetDlg) ON_BN_CLICKED(IDC_ADD_ANIMATION, OnAddAnimation) ON_BN_CLICKED(IDC_RESET, OnReset) ON_BN_CLICKED(IDC_ADD_SKEL_WT, OnAddSkelWt) ON_WM_DESTROY() ON_BN_CLICKED(IDC_LIST_INSERT, OnListInsert) ON_BN_CLICKED(IDC_LIST_UP, OnListUp) ON_BN_CLICKED(IDC_LIST_DOWN, OnListDown) ON_BN_CLICKED(IDC_LIST_DELETE, OnListDelete) ON_BN_CLICKED(IDC_SET_ANIM_LENGTH, OnSetAnimLength) ON_BN_CLICKED(IDC_USE_LIST, OnUseList) ON_BN_CLICKED(IDC_USE_MIXER, OnUseMixer) ON_CBN_SELCHANGE(IDC_EDITED_OBJECT, OnSelchangeEditedObject) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CAnimationSetDlg message handlers // *************************************************************************** void CAnimationSetDlg::OnAddAnimation () { uint instance = _ObjView->getEditedObject (); if (instance != 0xffffffff) { // Create a dialog static char BASED_CODE szFilter[] = "NeL Animation Files (*.anim)\0*.anim\0" "All Files (*.*)\0*.*\0\0"; // Filename buffer char buffer[65535]; buffer[0]=0; OPENFILENAME openFile; memset (&openFile, 0, sizeof (OPENFILENAME)); openFile.lStructSize = sizeof (OPENFILENAME); openFile.hwndOwner = this->m_hWnd; openFile.lpstrFilter = szFilter; openFile.nFilterIndex = 0; openFile.lpstrFile = buffer; openFile.nMaxFile = 65535; openFile.Flags = OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_ALLOWMULTISELECT|OFN_ENABLESIZING|OFN_EXPLORER; openFile.lpstrDefExt = "*.anim"; if (GetOpenFileName(&openFile)) { // Open the file try { // Filename pointer char *c=buffer; // Read the path CString path = buffer; if (path.GetLength()>openFile.nFileOffset) { // Double zero at the end c[path.GetLength()+1]=0; // Path is empty path = ""; } else { // Adda slash path += "\\"; // Look for the next string while (*(c++)) {} } // For each file selected while (*c) { // File name char filename[256]; char *ptr=filename; // Read a file name while (*c) { *(ptr++)=*(c++); } *ptr=0; c++; // File name CString name = path + filename; // Load the animation _ObjView->loadAnimation (name, instance); // Touch the channel mixer _ObjView->reinitChannels (); // Update refresh (TRUE); } } catch (Exception& e) { MessageBox (e.what(), "NeL object viewer", MB_OK|MB_ICONEXCLAMATION); } } } } // *************************************************************************** void CAnimationSetDlg::OnAddSkelWt() { // Instance number int instance = _ObjView->getEditedObject (); if (instance != CB_ERR) { // TODO: Add your control notification handler code here static char BASED_CODE szFilter[] = "NeL Skeleton Weight Template Files (*.swt)|*.swt|All Files (*.*)|*.*||"; CFileDialog fileDlg( TRUE, ".swt", "*.swt", OFN_ALLOWMULTISELECT|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, szFilter); if (fileDlg.DoModal()==IDOK) { // Open the file try { // Get first file POSITION pos=fileDlg.GetStartPosition( ); while (pos) { // Get the name CString filename=fileDlg.GetNextPathName(pos); // Load the animation _ObjView->loadSWT (filename, instance); // Touch the channel mixer _ObjView->reinitChannels (); // Update refresh (TRUE); } } catch (Exception& e) { MessageBox (e.what(), "NeL object viewer", MB_OK|MB_ICONEXCLAMATION); } } } } // *************************************************************************** void CAnimationSetDlg::OnReset () { // Instance uint instance = _ObjView->getEditedObject (); if (instance!=0xffffffff) { // Reset the channel mixer slots _ObjView->resetSlots (instance); } } // *************************************************************************** void CAnimationSetDlg::refresh (BOOL update) { if (update) { CDialog::UpdateData (update); // Clear the combo box EditedObject.ResetContent (); // Set edited object list uint i; for (i=0; i<_ObjView->getNumInstance (); i++) { char name[512]; _splitpath (_ObjView->getInstance (i)->Saved.ShapeFilename.c_str(), NULL, NULL, name, NULL); EditedObject.InsertString (-1, name); } // Get edited object uint instance = _ObjView->getEditedObject (); // Selection combo box EditedObject.SetCurSel ((instance==0xffffffff)?-1:(int)instance); // Clear the tree Tree.DeleteAllItems (); // Clear the tree SkelTree.DeleteAllItems (); // Clear the playlist PlayList.ResetContent (); if (instance != 0xffffffff) { // Get the instance CInstanceInfo *object = _ObjView->getInstance (instance); // For all tracks in the animation uint i; for (i=0; i<object->Saved.AnimationFileName.size(); i++) { // Get the animation name char name[512]; _splitpath (object->Saved.AnimationFileName[i].c_str(), NULL, NULL, name, NULL); // Get the animation pointer CAnimation *anim = object->AnimationSet.getAnimation (object->AnimationSet.getAnimationIdByName (name)); // Insert an intem HTREEITEM item=Tree.InsertItem (name); Tree.SetItemData (item, i); nlassert (item!=NULL); // For all tracks in the animation std::set<std::string> setString; anim->getTrackNames (setString); std::set<std::string>::iterator ite=setString.begin(); while (ite!=setString.end()) { // Add this string HTREEITEM newItem = Tree.InsertItem (ite->c_str(), item); Tree.SetItemData (newItem, 0xffffffff); // Get the track ITrack *track=anim->getTrack (anim->getIdTrackByName (*ite)); // Keyframer ? UTrackKeyframer *keyTrack=dynamic_cast<UTrackKeyframer *>(track); if (keyTrack) { // Get number of keys std::vector<TAnimationTime> keys; keyTrack->getKeysInRange (track->getBeginTime ()-1, track->getEndTime ()+1, keys); // Print track info char name[512]; _snprintf (name, 512, "%s (%f - %f) %d keys", typeid(*track).name(), track->getBeginTime (), track->getEndTime (), keys.size()); HTREEITEM keyItem = Tree.InsertItem (name, newItem); Tree.SetItemData (keyItem, 0xffffffff); } else { // Print track info char name[512]; _snprintf (name, 512, "%s (%f - %f)", typeid(*track).name(), track->getBeginTime (), track->getEndTime ()); HTREEITEM keyItem = Tree.InsertItem (name, newItem); Tree.SetItemData (keyItem, 0xffffffff); } ite++; } } // For all tracks in the animation for (i=0; i<object->Saved.SWTFileName.size(); i++) { // Get the animation name char name[512]; _splitpath (object->Saved.SWTFileName[i].c_str(), NULL, NULL, name, NULL); // Get the animation pointer CSkeletonWeight *swt = object->AnimationSet.getSkeletonWeight (object->AnimationSet.getSkeletonWeightIdByName (name)); // Insert an intem HTREEITEM item=SkelTree.InsertItem (name); nlassert (item!=NULL); // Get number of node in this skeleton weight uint numNode=swt->getNumNode (); // Add the nodein the tree for (uint n=0; n<numNode; n++) { char percent[512]; sprintf (percent, "%s (%f%%)", swt->getNodeName (n).c_str(), swt->getNodeWeight(n)*100); // Add this string SkelTree.InsertItem (percent, item); } } // For all tracks in the playlist for (i=0; i<object->Saved.PlayList.size(); i++) { // Insert an intem int item=PlayList.InsertString (-1, object->Saved.PlayList[i].c_str()); nlassert (item!=LB_ERR); } } CDialog::UpdateData (FALSE); } else { CDialog::UpdateData (TRUE); // Get edited object uint instance = _ObjView->getEditedObject (); if (instance != 0xffffffff) { // Get the instance CInstanceInfo *object = _ObjView->getInstance (instance); // Clear the playlist object->Saved.PlayList.resize (PlayList.GetCount ()); // For all tracks in the playlist uint i; for (i=0; i<object->Saved.PlayList.size(); i++) { // Insert an intem char text[512]; PlayList.GetText( i, text); object->Saved.PlayList[i] = text; } CDialog::UpdateData (update); } } _ObjView->refreshAnimationListeners(); } // *************************************************************************** void CAnimationSetDlg::OnDestroy() { setRegisterWindowState (this, REGKEY_OBJ_VIEW_ANIMATION_SET_DLG); CDialog::OnDestroy(); } // *************************************************************************** void CAnimationSetDlg::OnListInsert() { // Get selected animation HTREEITEM item = Tree.GetSelectedItem (); if (item && (Tree.GetItemData (item)!=0xffffffff)) { // Insert the string int itemList = PlayList.InsertString (-1, Tree.GetItemText(item)); PlayList.SetItemData (itemList, Tree.GetItemData (item)); // Reselect the item Tree.SelectItem (item); } // Update back refresh (FALSE); } // *************************************************************************** void CAnimationSetDlg::OnListUp() { // Get selected item int sel = PlayList.GetCurSel (); if ((sel != LB_ERR) && (sel>0)) { // Backup the string CString text; PlayList.GetText (sel, text); DWORD_PTR data = PlayList.GetItemData (sel); // Remove the node PlayList.DeleteString (sel); // Insert the string int pos = PlayList.InsertString (sel-1, text); PlayList.SetItemData (pos, data); PlayList.SetCurSel (pos); } // Update back refresh (FALSE); } // *************************************************************************** void CAnimationSetDlg::OnListDown() { // Get selected item int sel = PlayList.GetCurSel (); if ((sel != LB_ERR) && (sel<PlayList.GetCount()-1)) { // Backup the string CString text; PlayList.GetText (sel, text); DWORD_PTR data = PlayList.GetItemData (sel); // Remove the node PlayList.DeleteString (sel); // Insert the string int pos = PlayList.InsertString (sel+1, text); PlayList.SetItemData (pos, data); PlayList.SetCurSel (pos); } // Update back refresh (FALSE); } // *************************************************************************** void CAnimationSetDlg::OnListDelete() { // Get selected item int sel = PlayList.GetCurSel (); if (sel != LB_ERR) { // Remove the node PlayList.DeleteString (sel); if (sel>=PlayList.GetCount ()) sel--; if (sel>=0) PlayList.SetCurSel (sel); } // Update back refresh (FALSE); } // *************************************************************************** void CAnimationSetDlg::OnSetAnimLength() { // Longest animation float longestLength = 0; // For each instance for (uint instance=0; instance<_ObjView->_ListInstance.size(); instance++) { // Ref on the instance CInstanceInfo &inst = *_ObjView->_ListInstance[instance]; // There is some anim ? if (inst.Saved.PlayList.size()) { // Calculate the length float length = 0; // For each animation in the list for (uint i=0; i<(uint)inst.Saved.PlayList.size(); i++) { // Get the animation CAnimation *anim = inst.AnimationSet.getAnimation (inst.AnimationSet.getAnimationIdByName (inst.Saved.PlayList[i])); // Add the length length += anim->getEndTime () - anim->getBeginTime (); } if (length>longestLength) longestLength=length; } } // Adjuste length if (longestLength>0) _ObjView->setAnimTime (0, longestLength * _ObjView->getFrameRate ()); } // *************************************************************************** void CAnimationSetDlg::OnUseList() { // Disable channels _ObjView->enableChannels (); } // *************************************************************************** void CAnimationSetDlg::OnUseMixer() { // Enable channels _ObjView->enableChannels (); } // *************************************************************************** void CAnimationSetDlg::OnSelchangeEditedObject() { UpdateData (); // Selection combo box int selection = EditedObject.GetCurSel (); _ObjView->setEditedObject ((selection==CB_ERR)?0xffffffff:selection); refresh (TRUE); _ObjView->getSlotDlg ()->refresh (TRUE); }