2013-10-23 17:08:09 +00:00
|
|
|
// 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/>.
|
|
|
|
|
|
|
|
// skeleton_scale_dlg.cpp : implementation file
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "std_afx.h"
|
|
|
|
#include "object_viewer.h"
|
|
|
|
#include "skeleton_scale_dlg.h"
|
|
|
|
#include "nel/3d/skeleton_shape.h"
|
|
|
|
#include "nel/misc/algo.h"
|
|
|
|
#include "main_frame.h"
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
#define NL_SSD_SLIDER_SIZE 1000
|
|
|
|
#define NL_SSD_SLIDER_THRESHOLD 0.03f
|
|
|
|
#define NL_SSD_SLIDER_SCALE 2
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CSkeletonScaleDlg dialog
|
|
|
|
|
|
|
|
|
|
|
|
CSkeletonScaleDlg::CSkeletonScaleDlg(CObjectViewer *viewer, CWnd* pParent /*=NULL*/)
|
|
|
|
: CDialog(CSkeletonScaleDlg::IDD, pParent) ,_ObjViewer(viewer)
|
|
|
|
{
|
|
|
|
//{{AFX_DATA_INIT(CSkeletonScaleDlg)
|
|
|
|
_StaticFileName = _T("");
|
|
|
|
_EditBoneSX = _T("");
|
|
|
|
_EditBoneSY = _T("");
|
|
|
|
_EditBoneSZ = _T("");
|
|
|
|
_EditSkinSX = _T("");
|
|
|
|
_EditSkinSY = _T("");
|
|
|
|
_EditSkinSZ = _T("");
|
|
|
|
//}}AFX_DATA_INIT
|
|
|
|
|
|
|
|
// Init Scale Sliders ptrs
|
|
|
|
nlassert(SidCount==6);
|
|
|
|
_ScaleSliders[SidBoneX]= &_SliderBoneX;
|
|
|
|
_ScaleSliders[SidBoneY]= &_SliderBoneY;
|
|
|
|
_ScaleSliders[SidBoneZ]= &_SliderBoneZ;
|
|
|
|
_ScaleSliders[SidSkinX]= &_SliderSkinX;
|
|
|
|
_ScaleSliders[SidSkinY]= &_SliderSkinY;
|
|
|
|
_ScaleSliders[SidSkinZ]= &_SliderSkinZ;
|
|
|
|
_ScaleEdits[SidBoneX]= &_EditBoneSX;
|
|
|
|
_ScaleEdits[SidBoneY]= &_EditBoneSY;
|
|
|
|
_ScaleEdits[SidBoneZ]= &_EditBoneSZ;
|
|
|
|
_ScaleEdits[SidSkinX]= &_EditSkinSX;
|
|
|
|
_ScaleEdits[SidSkinY]= &_EditSkinSY;
|
|
|
|
_ScaleEdits[SidSkinZ]= &_EditSkinSZ;
|
|
|
|
_StaticScaleMarkers[SidBoneX]= &_StaticScaleMarkerBoneSX;
|
|
|
|
_StaticScaleMarkers[SidBoneY]= &_StaticScaleMarkerBoneSY;
|
|
|
|
_StaticScaleMarkers[SidBoneZ]= &_StaticScaleMarkerBoneSZ;
|
|
|
|
_StaticScaleMarkers[SidSkinX]= &_StaticScaleMarkerSkinSX;
|
|
|
|
_StaticScaleMarkers[SidSkinY]= &_StaticScaleMarkerSkinSY;
|
|
|
|
_StaticScaleMarkers[SidSkinZ]= &_StaticScaleMarkerSkinSZ;
|
|
|
|
|
|
|
|
|
|
|
|
_SliderEdited= SidNone;
|
|
|
|
_SaveDirty= false;
|
|
|
|
|
|
|
|
// avoid realloc
|
|
|
|
_UndoQueue.resize(MaxUndoRedo);
|
|
|
|
_RedoQueue.resize(MaxUndoRedo);
|
|
|
|
_UndoQueue.clear();
|
|
|
|
_RedoQueue.clear();
|
|
|
|
|
|
|
|
_BoneBBoxNeedRecompute= false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CSkeletonScaleDlg::~CSkeletonScaleDlg()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CSkeletonScaleDlg::DoDataExchange(CDataExchange* pDX)
|
|
|
|
{
|
|
|
|
CDialog::DoDataExchange(pDX);
|
|
|
|
//{{AFX_DATA_MAP(CSkeletonScaleDlg)
|
|
|
|
DDX_Control(pDX, IDC_SSD_SLIDER_SKIN_SZ, _SliderSkinZ);
|
|
|
|
DDX_Control(pDX, IDC_SSD_SLIDER_SKIN_SY, _SliderSkinY);
|
|
|
|
DDX_Control(pDX, IDC_SSD_SLIDER_SKIN_SX, _SliderSkinX);
|
|
|
|
DDX_Control(pDX, IDC_SSD_SLIDER_BONE_SZ, _SliderBoneZ);
|
|
|
|
DDX_Control(pDX, IDC_SSD_SLIDER_BONE_SY, _SliderBoneY);
|
|
|
|
DDX_Control(pDX, IDC_SSD_SLIDER_BONE_SX, _SliderBoneX);
|
|
|
|
DDX_Control(pDX, IDC_SSD_LIST, _BoneList);
|
|
|
|
DDX_Text(pDX, IDC_SSD_STATIC_FILENAME, _StaticFileName);
|
|
|
|
DDX_Text(pDX, IDC_SSD_EDIT_BONE_SX, _EditBoneSX);
|
|
|
|
DDX_Text(pDX, IDC_SSD_EDIT_BONE_SY, _EditBoneSY);
|
|
|
|
DDX_Text(pDX, IDC_SSD_EDIT_BONE_SZ, _EditBoneSZ);
|
|
|
|
DDX_Text(pDX, IDC_SSD_EDIT_SKIN_SX, _EditSkinSX);
|
|
|
|
DDX_Text(pDX, IDC_SSD_EDIT_SKIN_SY, _EditSkinSY);
|
|
|
|
DDX_Text(pDX, IDC_SSD_EDIT_SKIN_SZ, _EditSkinSZ);
|
|
|
|
DDX_Control(pDX, IDC_SSD_STATIC_SKIN_SZ, _StaticScaleMarkerSkinSZ);
|
|
|
|
DDX_Control(pDX, IDC_SSD_STATIC_SKIN_SY, _StaticScaleMarkerSkinSY);
|
|
|
|
DDX_Control(pDX, IDC_SSD_STATIC_SKIN_SX, _StaticScaleMarkerSkinSX);
|
|
|
|
DDX_Control(pDX, IDC_SSD_STATIC_BONE_SZ, _StaticScaleMarkerBoneSZ);
|
|
|
|
DDX_Control(pDX, IDC_SSD_STATIC_BONE_SY, _StaticScaleMarkerBoneSY);
|
|
|
|
DDX_Control(pDX, IDC_SSD_STATIC_BONE_SX, _StaticScaleMarkerBoneSX);
|
|
|
|
//}}AFX_DATA_MAP
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CSkeletonScaleDlg, CDialog)
|
|
|
|
//{{AFX_MSG_MAP(CSkeletonScaleDlg)
|
|
|
|
ON_WM_DESTROY()
|
|
|
|
ON_WM_VSCROLL()
|
|
|
|
ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SSD_SLIDER_BONE_SX, OnReleasedcaptureSsdSliderBoneSx)
|
|
|
|
ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SSD_SLIDER_BONE_SY, OnReleasedcaptureSsdSliderBoneSy)
|
|
|
|
ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SSD_SLIDER_BONE_SZ, OnReleasedcaptureSsdSliderBoneSz)
|
|
|
|
ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SSD_SLIDER_SKIN_SX, OnReleasedcaptureSsdSliderSkinSx)
|
|
|
|
ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SSD_SLIDER_SKIN_SY, OnReleasedcaptureSsdSliderSkinSy)
|
|
|
|
ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SSD_SLIDER_SKIN_SZ, OnReleasedcaptureSsdSliderSkinSz)
|
|
|
|
ON_EN_CHANGE(IDC_SSD_EDIT_BONE_SX, OnChangeSsdEditBoneSx)
|
|
|
|
ON_EN_CHANGE(IDC_SSD_EDIT_BONE_SY, OnChangeSsdEditBoneSy)
|
|
|
|
ON_EN_CHANGE(IDC_SSD_EDIT_BONE_SZ, OnChangeSsdEditBoneSz)
|
|
|
|
ON_EN_CHANGE(IDC_SSD_EDIT_SKIN_SX, OnChangeSsdEditSkinSx)
|
|
|
|
ON_EN_CHANGE(IDC_SSD_EDIT_SKIN_SY, OnChangeSsdEditSkinSy)
|
|
|
|
ON_EN_CHANGE(IDC_SSD_EDIT_SKIN_SZ, OnChangeSsdEditSkinSz)
|
|
|
|
ON_EN_KILLFOCUS(IDC_SSD_EDIT_BONE_SX, OnKillfocusSsdEditBoneSx)
|
|
|
|
ON_EN_KILLFOCUS(IDC_SSD_EDIT_BONE_SY, OnKillfocusSsdEditBoneSy)
|
|
|
|
ON_EN_KILLFOCUS(IDC_SSD_EDIT_BONE_SZ, OnKillfocusSsdEditBoneSz)
|
|
|
|
ON_EN_KILLFOCUS(IDC_SSD_EDIT_SKIN_SX, OnKillfocusSsdEditSkinSx)
|
|
|
|
ON_EN_KILLFOCUS(IDC_SSD_EDIT_SKIN_SY, OnKillfocusSsdEditSkinSy)
|
|
|
|
ON_EN_KILLFOCUS(IDC_SSD_EDIT_SKIN_SZ, OnKillfocusSsdEditSkinSz)
|
|
|
|
ON_EN_SETFOCUS(IDC_SSD_EDIT_BONE_SX, OnSetfocusSsdEditBoneSx)
|
|
|
|
ON_EN_SETFOCUS(IDC_SSD_EDIT_BONE_SY, OnSetfocusSsdEditBoneSy)
|
|
|
|
ON_EN_SETFOCUS(IDC_SSD_EDIT_BONE_SZ, OnSetfocusSsdEditBoneSz)
|
|
|
|
ON_EN_SETFOCUS(IDC_SSD_EDIT_SKIN_SX, OnSetfocusSsdEditSkinSx)
|
|
|
|
ON_EN_SETFOCUS(IDC_SSD_EDIT_SKIN_SY, OnSetfocusSsdEditSkinSy)
|
|
|
|
ON_EN_SETFOCUS(IDC_SSD_EDIT_SKIN_SZ, OnSetfocusSsdEditSkinSz)
|
|
|
|
ON_LBN_SELCHANGE(IDC_SSD_LIST, OnSelchangeSsdList)
|
|
|
|
ON_BN_CLICKED(IDC_SSD_BUTTON_UNDO, OnSsdButtonUndo)
|
|
|
|
ON_BN_CLICKED(IDC_SSD_BUTTON_REDO, OnSsdButtonRedo)
|
|
|
|
ON_BN_CLICKED(IDC_SSD_BUTTON_SAVE, OnSsdButtonSave)
|
|
|
|
ON_BN_CLICKED(IDC_SSD_BUTTON_SAVEAS, OnSsdButtonSaveas)
|
|
|
|
ON_BN_CLICKED(IDC_SSD_BUTTON_MIRROR, OnSsdButtonMirror)
|
|
|
|
ON_BN_CLICKED(IDC_SSD_BUTTON_SAVE_SCALE, OnSsdButtonSaveScale)
|
|
|
|
ON_BN_CLICKED(IDC_SSD_BUTTON_LOAD_SCALE, OnSsdButtonLoadScale)
|
|
|
|
ON_WM_CLOSE()
|
|
|
|
//}}AFX_MSG_MAP
|
|
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CSkeletonScaleDlg message handlers
|
|
|
|
|
|
|
|
void CSkeletonScaleDlg::OnDestroy()
|
|
|
|
{
|
|
|
|
setRegisterWindowState (this, REGKEY_SKELETON_SCALE_DLG);
|
|
|
|
CDialog::OnDestroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::setSkeletonToEdit(NL3D::CSkeletonModel *skel, const std::string &fileName)
|
|
|
|
{
|
|
|
|
uint i;
|
|
|
|
|
|
|
|
_SkeletonModel= skel;
|
|
|
|
_SkeletonFileName= fileName;
|
|
|
|
|
|
|
|
|
|
|
|
// **** Setup File name
|
|
|
|
_StaticFileName= fileName.c_str();
|
|
|
|
|
|
|
|
// **** Setup Bone mirror
|
|
|
|
_Bones.clear();
|
|
|
|
if(_SkeletonModel)
|
|
|
|
{
|
|
|
|
_Bones.resize(_SkeletonModel->Bones.size());
|
|
|
|
// copy from skel to mirror
|
|
|
|
applySkeletonToMirror();
|
|
|
|
}
|
|
|
|
|
|
|
|
// **** reset bone bbox here
|
|
|
|
_BoneBBoxes.clear();
|
|
|
|
_BoneBBoxes.resize(_Bones.size());
|
|
|
|
// delegate to drawSelection(), cause skins not still bound
|
|
|
|
_BoneBBoxNeedRecompute= true;
|
|
|
|
|
|
|
|
// **** Setup Bone List
|
|
|
|
_BoneList.ResetContent();
|
|
|
|
if(_SkeletonModel)
|
|
|
|
{
|
|
|
|
for(uint i=0;i<_SkeletonModel->Bones.size();i++)
|
|
|
|
{
|
|
|
|
const std::string tabStr= " ";
|
|
|
|
std::string name= _SkeletonModel->Bones[i].getBoneName();
|
|
|
|
// append a tab for easy hierarchy
|
|
|
|
uint boneId= i;
|
|
|
|
while((boneId=_SkeletonModel->Bones[boneId].getFatherId())!=-1)
|
|
|
|
name= tabStr + name;
|
|
|
|
// append to the list
|
|
|
|
_BoneList.AddString(name.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// **** unselect all widgets
|
|
|
|
for(i=0;i<SidCount;i++)
|
|
|
|
{
|
|
|
|
_ScaleSliders[i]->SetRange(0, NL_SSD_SLIDER_SIZE);
|
|
|
|
_ScaleSliders[i]->SetPos(NL_SSD_SLIDER_SIZE/2);
|
|
|
|
*_ScaleEdits[i]= "";
|
|
|
|
_StaticScaleMarkers[i]->SetWindowText("100%");
|
|
|
|
}
|
|
|
|
// ensure no problem with scale and ALT-TAB
|
|
|
|
_SliderEdited= SidNone;
|
|
|
|
|
|
|
|
|
|
|
|
// **** clean undo/redo
|
|
|
|
_UndoQueue.clear();
|
|
|
|
_RedoQueue.clear();
|
|
|
|
refreshUndoRedoView();
|
|
|
|
|
|
|
|
|
|
|
|
// **** clear save button
|
|
|
|
_SaveDirty= false;
|
|
|
|
refreshSaveButton();
|
|
|
|
|
|
|
|
|
|
|
|
// refresh
|
|
|
|
UpdateData(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
BOOL CSkeletonScaleDlg::OnInitDialog()
|
|
|
|
{
|
|
|
|
CDialog::OnInitDialog();
|
|
|
|
|
|
|
|
setSkeletonToEdit(NULL, "");
|
|
|
|
|
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
|
|
// EXCEPTION: OCX Property Pages should return FALSE
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
|
|
|
{
|
|
|
|
CSliderCtrl *sliderCtrl= (CSliderCtrl*)pScrollBar;
|
|
|
|
TScaleId sliderId= getScaleIdFromSliderCtrl(sliderCtrl);
|
|
|
|
if(sliderId!=SidNone && nSBCode==SB_THUMBPOSITION || nSBCode==SB_THUMBTRACK)
|
|
|
|
{
|
|
|
|
// If the user press ALT-Tab while dragging an old slider, the old slider is not released.
|
|
|
|
// ThereFore, release the old one now
|
|
|
|
if(_SliderEdited!=SidNone && _SliderEdited!=sliderId)
|
|
|
|
{
|
|
|
|
onSliderReleased(_SliderEdited);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if begin of slide, bkup state
|
|
|
|
if(_SliderEdited==SidNone)
|
|
|
|
{
|
|
|
|
_SliderEdited= sliderId;
|
|
|
|
// Bkup all scales (dont bother selected bones or which scale is edited...)
|
|
|
|
_BkupBones= _Bones;
|
|
|
|
}
|
|
|
|
|
|
|
|
//applyScale
|
|
|
|
applyScaleSlider(nPos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderBoneSx(NMHDR* pNMHDR, LRESULT* pResult)
|
|
|
|
{
|
|
|
|
onSliderReleased(SidBoneX);
|
|
|
|
*pResult = 0;
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderBoneSy(NMHDR* pNMHDR, LRESULT* pResult)
|
|
|
|
{
|
|
|
|
onSliderReleased(SidBoneY);
|
|
|
|
*pResult = 0;
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderBoneSz(NMHDR* pNMHDR, LRESULT* pResult)
|
|
|
|
{
|
|
|
|
onSliderReleased(SidBoneZ);
|
|
|
|
*pResult = 0;
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderSkinSx(NMHDR* pNMHDR, LRESULT* pResult)
|
|
|
|
{
|
|
|
|
onSliderReleased(SidSkinX);
|
|
|
|
*pResult = 0;
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderSkinSy(NMHDR* pNMHDR, LRESULT* pResult)
|
|
|
|
{
|
|
|
|
onSliderReleased(SidSkinY);
|
|
|
|
*pResult = 0;
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderSkinSz(NMHDR* pNMHDR, LRESULT* pResult)
|
|
|
|
{
|
|
|
|
onSliderReleased(SidSkinZ);
|
|
|
|
*pResult = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSkeletonScaleDlg::onSliderReleased(TScaleId sid)
|
|
|
|
{
|
|
|
|
// Get value from slider
|
|
|
|
sint value= _ScaleSliders[sid]->GetPos();
|
|
|
|
|
|
|
|
// If the user press ALT-Tab while dragging an old slider, the old slider is not released.
|
|
|
|
// ThereFore, release the old one now
|
|
|
|
if(_SliderEdited!=SidNone && _SliderEdited!=sid)
|
|
|
|
{
|
|
|
|
onSliderReleased(_SliderEdited);
|
|
|
|
}
|
|
|
|
|
|
|
|
//applyScale
|
|
|
|
if(_SliderEdited==SidNone)
|
|
|
|
{
|
|
|
|
_SliderEdited= sid;
|
|
|
|
// Bkup all scales (dont bother selected bones or which scale is edited...)
|
|
|
|
_BkupBones= _Bones;
|
|
|
|
}
|
|
|
|
|
|
|
|
// apply the final value
|
|
|
|
applyScaleSlider(value);
|
|
|
|
|
|
|
|
// And reset the slider
|
|
|
|
_ScaleSliders[_SliderEdited]->SetPos(NL_SSD_SLIDER_SIZE/2);
|
|
|
|
_StaticScaleMarkers[_SliderEdited]->SetWindowText("100%");
|
|
|
|
_SliderEdited= SidNone;
|
|
|
|
|
|
|
|
// push an undo/redo only at release of slide. push the value of scale before slide
|
|
|
|
pushUndoState(_BkupBones, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::applyScaleSlider(sint scrollValue)
|
|
|
|
{
|
|
|
|
// get scale beetween -1 and 1.
|
|
|
|
float scale= (NL_SSD_SLIDER_SIZE/2-scrollValue)/(float)(NL_SSD_SLIDER_SIZE/2);
|
|
|
|
NLMISC::clamp(scale, -1.f, 1.f);
|
|
|
|
float factor;
|
|
|
|
|
|
|
|
// no scale
|
|
|
|
if(fabs(scale)<NL_SSD_SLIDER_THRESHOLD)
|
|
|
|
factor=1;
|
|
|
|
// scale down
|
|
|
|
else if(scale<0)
|
|
|
|
{
|
|
|
|
float minv= 1.0f / NL_SSD_SLIDER_SCALE;
|
|
|
|
factor= minv*(-scale) + 1.0f*(1+scale);
|
|
|
|
}
|
|
|
|
// scale up
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float maxv= NL_SSD_SLIDER_SCALE;
|
|
|
|
factor= maxv*(scale) + 1.0f*(1-scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the noise to each selected bones
|
|
|
|
for(uint i=0;i<_Bones.size();i++)
|
|
|
|
{
|
|
|
|
CBoneMirror &bone= _Bones[i];
|
|
|
|
CBoneMirror &bkup= _BkupBones[i];
|
|
|
|
if(bone.Selected)
|
|
|
|
{
|
|
|
|
// apply to the scaled component
|
|
|
|
switch(_SliderEdited)
|
|
|
|
{
|
|
|
|
case SidBoneX: bone.BoneScale.x= bkup.BoneScale.x *factor; break;
|
|
|
|
case SidBoneY: bone.BoneScale.y= bkup.BoneScale.y *factor; break;
|
|
|
|
case SidBoneZ: bone.BoneScale.z= bkup.BoneScale.z *factor; break;
|
|
|
|
case SidSkinX: bone.SkinScale.x= bkup.SkinScale.x *factor; break;
|
|
|
|
case SidSkinY: bone.SkinScale.y= bkup.SkinScale.y *factor; break;
|
|
|
|
case SidSkinZ: bone.SkinScale.z= bkup.SkinScale.z *factor; break;
|
|
|
|
};
|
|
|
|
// round result
|
|
|
|
roundClampScale(bone.BoneScale);
|
|
|
|
roundClampScale(bone.SkinScale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// update the skeleton view
|
|
|
|
applyMirrorToSkeleton();
|
|
|
|
|
|
|
|
// refresh text views
|
|
|
|
refreshTextViews();
|
|
|
|
|
|
|
|
// update marker text
|
|
|
|
char str[256];
|
|
|
|
sprintf(str, "%d%%", (sint)(factor*100));
|
|
|
|
_StaticScaleMarkers[_SliderEdited]->SetWindowText(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::applyMirrorToSkeleton()
|
|
|
|
{
|
|
|
|
if(_SkeletonModel)
|
|
|
|
{
|
|
|
|
nlassert(_SkeletonModel->Bones.size()==_Bones.size());
|
|
|
|
for(uint i=0;i<_Bones.size();i++)
|
|
|
|
{
|
|
|
|
// unmul from precision
|
|
|
|
_SkeletonModel->Bones[i].setScale(_Bones[i].BoneScale / NL_SSD_SCALE_PRECISION);
|
|
|
|
_SkeletonModel->Bones[i].setSkinScale(_Bones[i].SkinScale / NL_SSD_SCALE_PRECISION);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::applySkeletonToMirror()
|
|
|
|
{
|
|
|
|
if(_SkeletonModel)
|
|
|
|
{
|
|
|
|
nlassert(_SkeletonModel->Bones.size()==_Bones.size());
|
|
|
|
for(uint i=0;i<_SkeletonModel->Bones.size();i++)
|
|
|
|
{
|
|
|
|
// mul by precision, and round
|
|
|
|
_Bones[i].SkinScale= _SkeletonModel->Bones[i].getSkinScale() * NL_SSD_SCALE_PRECISION;
|
|
|
|
_Bones[i].BoneScale= _SkeletonModel->Bones[i].getScale() * NL_SSD_SCALE_PRECISION;
|
|
|
|
roundClampScale(_Bones[i].SkinScale);
|
|
|
|
roundClampScale(_Bones[i].BoneScale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::refreshTextViews()
|
|
|
|
{
|
|
|
|
// 1.f for each component if multiple selection is different, else 0.f
|
|
|
|
NLMISC::CVector boneScaleDiff= NLMISC::CVector::Null;
|
|
|
|
NLMISC::CVector skinScaleDiff= NLMISC::CVector::Null;
|
|
|
|
// valid if scale of each bone component is same
|
|
|
|
NLMISC::CVector boneScaleAll= NLMISC::CVector::Null;
|
|
|
|
NLMISC::CVector skinScaleAll= NLMISC::CVector::Null;
|
|
|
|
bool someSelected= false;
|
|
|
|
|
|
|
|
// For all bones selected
|
|
|
|
for(uint i=0;i<_Bones.size();i++)
|
|
|
|
{
|
|
|
|
CBoneMirror &bone= _Bones[i];
|
|
|
|
if(bone.Selected)
|
|
|
|
{
|
|
|
|
if(!someSelected)
|
|
|
|
{
|
|
|
|
someSelected= true;
|
|
|
|
// just bkup
|
|
|
|
boneScaleAll= bone.BoneScale;
|
|
|
|
skinScaleAll= bone.SkinScale;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// compare each component, if different, flag
|
|
|
|
if(boneScaleAll.x!= bone.BoneScale.x) boneScaleDiff.x= 1.f;
|
|
|
|
if(boneScaleAll.y!= bone.BoneScale.y) boneScaleDiff.y= 1.f;
|
|
|
|
if(boneScaleAll.z!= bone.BoneScale.z) boneScaleDiff.z= 1.f;
|
|
|
|
if(skinScaleAll.x!= bone.SkinScale.x) skinScaleDiff.x= 1.f;
|
|
|
|
if(skinScaleAll.y!= bone.SkinScale.y) skinScaleDiff.y= 1.f;
|
|
|
|
if(skinScaleAll.z!= bone.SkinScale.z) skinScaleDiff.z= 1.f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if none selected, force empty text
|
|
|
|
if(!someSelected)
|
|
|
|
{
|
|
|
|
boneScaleDiff.set(1.f,1.f,1.f);
|
|
|
|
skinScaleDiff.set(1.f,1.f,1.f);
|
|
|
|
}
|
|
|
|
|
|
|
|
// All component refresh or only one refresh?
|
|
|
|
nlassert(SidCount==6);
|
|
|
|
refreshTextViewWithScale(SidBoneX, boneScaleAll.x, boneScaleDiff.x);
|
|
|
|
refreshTextViewWithScale(SidBoneY, boneScaleAll.y, boneScaleDiff.y);
|
|
|
|
refreshTextViewWithScale(SidBoneZ, boneScaleAll.z, boneScaleDiff.z);
|
|
|
|
refreshTextViewWithScale(SidSkinX, skinScaleAll.x, skinScaleDiff.x);
|
|
|
|
refreshTextViewWithScale(SidSkinY, skinScaleAll.y, skinScaleDiff.y);
|
|
|
|
refreshTextViewWithScale(SidSkinZ, skinScaleAll.z, skinScaleDiff.z);
|
|
|
|
|
|
|
|
// refresh
|
|
|
|
UpdateData(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::refreshTextViewWithScale(TScaleId sid, float scale, float diff)
|
|
|
|
{
|
|
|
|
// if different values selected, diff
|
|
|
|
if(diff)
|
|
|
|
{
|
|
|
|
*_ScaleEdits[sid]= "";
|
|
|
|
}
|
|
|
|
// else display text
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char str[256];
|
|
|
|
// ensure correct display with no precision problem
|
|
|
|
sint value= uint(floor(scale+0.5f));
|
|
|
|
sint whole= value*100/NL_SSD_SCALE_PRECISION;
|
|
|
|
sint decimal= value - whole*(NL_SSD_SCALE_PRECISION/100);
|
|
|
|
sprintf(str, "%d.%d", whole, decimal);
|
|
|
|
*_ScaleEdits[sid]= str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::roundClampScale(NLMISC::CVector &v) const
|
|
|
|
{
|
|
|
|
v.x+= 0.5f;
|
|
|
|
v.y+= 0.5f;
|
|
|
|
v.z+= 0.5f;
|
|
|
|
v.x= (float)floor(v.x);
|
|
|
|
v.y= (float)floor(v.y);
|
|
|
|
v.z= (float)floor(v.z);
|
|
|
|
// Minimum is 1 (avoid 0 scale)
|
|
|
|
v.maxof(v, NLMISC::CVector(1.f,1.f,1.f));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
CSkeletonScaleDlg::TScaleId CSkeletonScaleDlg::getScaleIdFromSliderCtrl(CSliderCtrl *sliderCtrl) const
|
|
|
|
{
|
|
|
|
for(uint i=0;i<SidCount;i++)
|
|
|
|
{
|
|
|
|
if(sliderCtrl==_ScaleSliders[i])
|
|
|
|
return (TScaleId)i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SidNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
CSkeletonScaleDlg::TScaleId CSkeletonScaleDlg::getScaleIdFromEditId(UINT ctrlId) const
|
|
|
|
{
|
|
|
|
nlctassert(SidCount==6);
|
|
|
|
if(ctrlId==IDC_SSD_EDIT_BONE_SX) return SidBoneX;
|
|
|
|
if(ctrlId==IDC_SSD_EDIT_BONE_SY) return SidBoneY;
|
|
|
|
if(ctrlId==IDC_SSD_EDIT_BONE_SZ) return SidBoneZ;
|
|
|
|
if(ctrlId==IDC_SSD_EDIT_SKIN_SX) return SidSkinX;
|
|
|
|
if(ctrlId==IDC_SSD_EDIT_SKIN_SY) return SidSkinY;
|
|
|
|
if(ctrlId==IDC_SSD_EDIT_SKIN_SZ) return SidSkinZ;
|
|
|
|
|
|
|
|
return SidNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnChangeSsdEditBoneSx()
|
|
|
|
{
|
|
|
|
onChangeEditText(IDC_SSD_EDIT_BONE_SX);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnChangeSsdEditBoneSy()
|
|
|
|
{
|
|
|
|
onChangeEditText(IDC_SSD_EDIT_BONE_SY);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnChangeSsdEditBoneSz()
|
|
|
|
{
|
|
|
|
onChangeEditText(IDC_SSD_EDIT_BONE_SZ);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnChangeSsdEditSkinSx()
|
|
|
|
{
|
|
|
|
onChangeEditText(IDC_SSD_EDIT_SKIN_SX);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnChangeSsdEditSkinSy()
|
|
|
|
{
|
|
|
|
onChangeEditText(IDC_SSD_EDIT_SKIN_SY);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnChangeSsdEditSkinSz()
|
|
|
|
{
|
|
|
|
onChangeEditText(IDC_SSD_EDIT_SKIN_SZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void concatEdit2Lines(CEdit &edit)
|
|
|
|
{
|
|
|
|
const uint lineLen= 1000;
|
|
|
|
uint n;
|
|
|
|
// retrieve the 2 lines.
|
|
|
|
char tmp0[2*lineLen];
|
|
|
|
char tmp1[lineLen];
|
|
|
|
n= edit.GetLine(0, tmp0, lineLen); tmp0[n]= 0;
|
|
|
|
n= edit.GetLine(1, tmp1, lineLen); tmp1[n]= 0;
|
|
|
|
// concat and update the CEdit.
|
|
|
|
edit.SetWindowText(strcat(tmp0, tmp1));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CSkeletonScaleDlg::onChangeEditText(UINT ctrlId)
|
|
|
|
{
|
|
|
|
CEdit *ce = (CEdit*)GetDlgItem(ctrlId);
|
|
|
|
if(ce)
|
|
|
|
{
|
|
|
|
UpdateData();
|
|
|
|
// Trick to track "Enter" keypress: CEdit are multiline. If GetLineCount()>1, then
|
|
|
|
// user has press enter.
|
|
|
|
if(ce->GetLineCount()>1)
|
|
|
|
{
|
|
|
|
// must ccat 2 lines of the CEdit.
|
|
|
|
concatEdit2Lines(*ce);
|
|
|
|
// update text members
|
|
|
|
UpdateData(FALSE);
|
|
|
|
UpdateData();
|
|
|
|
|
|
|
|
// update scale values from the CEdit
|
|
|
|
updateScalesFromText(ctrlId);
|
|
|
|
|
|
|
|
// then re-update CEdit from scale values
|
|
|
|
refreshTextViews();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnKillfocusSsdEditBoneSx()
|
|
|
|
{
|
|
|
|
onQuitEditText(IDC_SSD_EDIT_BONE_SX);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnKillfocusSsdEditBoneSy()
|
|
|
|
{
|
|
|
|
onQuitEditText(IDC_SSD_EDIT_BONE_SY);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnKillfocusSsdEditBoneSz()
|
|
|
|
{
|
|
|
|
onQuitEditText(IDC_SSD_EDIT_BONE_SZ);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnKillfocusSsdEditSkinSx()
|
|
|
|
{
|
|
|
|
onQuitEditText(IDC_SSD_EDIT_SKIN_SX);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnKillfocusSsdEditSkinSy()
|
|
|
|
{
|
|
|
|
onQuitEditText(IDC_SSD_EDIT_SKIN_SY);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnKillfocusSsdEditSkinSz()
|
|
|
|
{
|
|
|
|
onQuitEditText(IDC_SSD_EDIT_SKIN_SZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CSkeletonScaleDlg::onQuitEditText(UINT ctrlId)
|
|
|
|
{
|
|
|
|
CEdit *ce = (CEdit*)GetDlgItem(ctrlId);
|
|
|
|
if(ce)
|
|
|
|
{
|
|
|
|
UpdateData();
|
|
|
|
|
|
|
|
// update scale values from the CEdit
|
|
|
|
updateScalesFromText(ctrlId);
|
|
|
|
|
|
|
|
// then re-update CEdit from scale values
|
|
|
|
refreshTextViews();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnSetfocusSsdEditBoneSx()
|
|
|
|
{
|
|
|
|
onSelectEditText(IDC_SSD_EDIT_BONE_SX);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnSetfocusSsdEditBoneSy()
|
|
|
|
{
|
|
|
|
onSelectEditText(IDC_SSD_EDIT_BONE_SY);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnSetfocusSsdEditBoneSz()
|
|
|
|
{
|
|
|
|
onSelectEditText(IDC_SSD_EDIT_BONE_SZ);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnSetfocusSsdEditSkinSx()
|
|
|
|
{
|
|
|
|
onSelectEditText(IDC_SSD_EDIT_SKIN_SX);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnSetfocusSsdEditSkinSy()
|
|
|
|
{
|
|
|
|
onSelectEditText(IDC_SSD_EDIT_SKIN_SY);
|
|
|
|
}
|
|
|
|
void CSkeletonScaleDlg::OnSetfocusSsdEditSkinSz()
|
|
|
|
{
|
|
|
|
onSelectEditText(IDC_SSD_EDIT_SKIN_SZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSkeletonScaleDlg::onSelectEditText(UINT ctrlId)
|
|
|
|
{
|
|
|
|
CEdit *ce = (CEdit*)GetDlgItem(ctrlId);
|
|
|
|
if(ce)
|
|
|
|
{
|
|
|
|
ce->PostMessage(EM_SETSEL, 0, -1);
|
|
|
|
ce->Invalidate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::updateScalesFromText(UINT ctrlId)
|
|
|
|
{
|
|
|
|
TScaleId sid= getScaleIdFromEditId(ctrlId);
|
|
|
|
if(sid==SidNone)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// get the scale info
|
|
|
|
std::string str= (const char*)(*_ScaleEdits[sid]);
|
|
|
|
if(str.empty())
|
|
|
|
return;
|
|
|
|
float f;
|
|
|
|
sscanf(str.c_str(), "%f", &f);
|
|
|
|
// edit a %age
|
|
|
|
f*= NL_SSD_SCALE_PRECISION/100;
|
|
|
|
f= (float)floor(f+0.5f);
|
|
|
|
|
|
|
|
// bkup for undo
|
|
|
|
static TBoneMirrorArray precState;
|
|
|
|
precState= _Bones;
|
|
|
|
|
|
|
|
// For all bones selected, set the edited value
|
|
|
|
for(uint i=0;i<_Bones.size();i++)
|
|
|
|
{
|
|
|
|
CBoneMirror &bone= _Bones[i];
|
|
|
|
if(bone.Selected)
|
|
|
|
{
|
|
|
|
switch(sid)
|
|
|
|
{
|
|
|
|
case SidBoneX: bone.BoneScale.x= f; break;
|
|
|
|
case SidBoneY: bone.BoneScale.y= f; break;
|
|
|
|
case SidBoneZ: bone.BoneScale.z= f; break;
|
|
|
|
case SidSkinX: bone.SkinScale.x= f; break;
|
|
|
|
case SidSkinY: bone.SkinScale.y= f; break;
|
|
|
|
case SidSkinZ: bone.SkinScale.z= f; break;
|
|
|
|
};
|
|
|
|
// normalize
|
|
|
|
roundClampScale(bone.BoneScale);
|
|
|
|
roundClampScale(bone.SkinScale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// change => bkup for undo
|
|
|
|
pushUndoState(precState, true);
|
|
|
|
|
|
|
|
// mirror changed => update skeleton
|
|
|
|
applyMirrorToSkeleton();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnSelchangeSsdList()
|
|
|
|
{
|
|
|
|
UpdateData();
|
|
|
|
|
|
|
|
// **** Retrieve List of selected bones.
|
|
|
|
uint count= _BoneList.GetSelCount();
|
|
|
|
std::vector<int> items;
|
|
|
|
if(count)
|
|
|
|
{
|
|
|
|
items.resize(count);
|
|
|
|
_BoneList.GetSelItems(count, &items[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// **** update the Selection array
|
|
|
|
// bkup for undo
|
|
|
|
static TBoneMirrorArray precState;
|
|
|
|
precState= _Bones;
|
|
|
|
|
|
|
|
// identify selected items in a set
|
|
|
|
std::set<int> selSet;
|
|
|
|
uint i;
|
|
|
|
for(i=0;i<count;i++)
|
|
|
|
selSet.insert(items[i]);
|
|
|
|
|
|
|
|
// change selection of Bones
|
|
|
|
for(i=0;i<_Bones.size();i++)
|
|
|
|
{
|
|
|
|
if(selSet.find(i)!=selSet.end())
|
|
|
|
_Bones[i].Selected= true;
|
|
|
|
else
|
|
|
|
_Bones[i].Selected= false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// **** undo-redo
|
|
|
|
// selection change => no need to dirt save
|
|
|
|
pushUndoState(precState, false);
|
|
|
|
|
|
|
|
// **** refresh text views
|
|
|
|
refreshTextViews();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnSsdButtonUndo()
|
|
|
|
{
|
|
|
|
undo();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSkeletonScaleDlg::OnSsdButtonRedo()
|
|
|
|
{
|
|
|
|
redo();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnSsdButtonSave()
|
|
|
|
{
|
|
|
|
// if no skeleton edited, quit
|
|
|
|
if(!_SkeletonModel)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// save the file
|
|
|
|
NLMISC::COFile f;
|
|
|
|
if( f.open(_SkeletonFileName) )
|
|
|
|
{
|
|
|
|
if(saveCurrentInStream(f))
|
|
|
|
{
|
|
|
|
// no more need to save (done)
|
|
|
|
_SaveDirty= false;
|
|
|
|
refreshSaveButton();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MessageBox("Failed to open file for write!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSkeletonScaleDlg::OnSsdButtonSaveas()
|
|
|
|
{
|
|
|
|
// if no skeleton edited, quit
|
|
|
|
if(!_SkeletonModel)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// choose the file
|
|
|
|
CFileDialog fd(FALSE, "skel", _SkeletonFileName.c_str(), OFN_OVERWRITEPROMPT, "SkelFiles (*.skel)|*.skel|All Files (*.*)|*.*||", this) ;
|
|
|
|
fd.m_ofn.lpstrTitle= "Save As Skeleton";
|
|
|
|
if (fd.DoModal() == IDOK)
|
|
|
|
{
|
|
|
|
NLMISC::COFile f;
|
|
|
|
|
|
|
|
if( f.open((const char*)fd.GetPathName()) )
|
|
|
|
{
|
|
|
|
if(saveCurrentInStream(f))
|
|
|
|
{
|
|
|
|
// no more need to save (done)
|
|
|
|
_SaveDirty= false;
|
|
|
|
refreshSaveButton();
|
|
|
|
}
|
|
|
|
|
|
|
|
// bkup the valid fileName (new file edited)
|
|
|
|
_SkeletonFileName= (const char*)fd.GetPathName();
|
|
|
|
_StaticFileName= _SkeletonFileName.c_str();
|
|
|
|
UpdateData(FALSE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MessageBox("Failed to open file for write!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
bool CSkeletonScaleDlg::saveCurrentInStream(NLMISC::IStream &f)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
nlassert(_SkeletonModel);
|
|
|
|
nlassert(_SkeletonModel->Shape);
|
|
|
|
|
|
|
|
// Retrieve boneBase definition from the current skeleton
|
|
|
|
std::vector<NL3D::CBoneBase> boneBases;
|
|
|
|
(NLMISC::safe_cast<NL3D::CSkeletonShape*>((NL3D::IShape*)_SkeletonModel->Shape))->retrieve(boneBases);
|
|
|
|
|
|
|
|
// Copies bone scales from the model
|
|
|
|
nlassert(boneBases.size()==_SkeletonModel->Bones.size());
|
|
|
|
for(uint i=0;i<_SkeletonModel->Bones.size();i++)
|
|
|
|
{
|
|
|
|
NL3D::CBone &bone= _SkeletonModel->Bones[i];
|
|
|
|
NL3D::CBoneBase &boneBase= boneBases[i];
|
|
|
|
|
|
|
|
boneBase.SkinScale= bone.getSkinScale();
|
|
|
|
boneBase.DefaultScale= bone.getScale();
|
|
|
|
}
|
|
|
|
|
|
|
|
// build a new Skeleton shape
|
|
|
|
NL3D::CSkeletonShape *skelShape= new NL3D::CSkeletonShape;
|
|
|
|
skelShape->build(boneBases);
|
|
|
|
|
|
|
|
|
|
|
|
// save the vegetable
|
|
|
|
NL3D::CShapeStream ss;
|
|
|
|
ss.setShapePointer(skelShape);
|
|
|
|
ss.serial(f);
|
|
|
|
delete skelShape;
|
|
|
|
}
|
|
|
|
catch(NLMISC::EStream &)
|
|
|
|
{
|
|
|
|
MessageBox("Failed to save file!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::pushUndoState(const TBoneMirrorArray &precState, bool dirtSave)
|
|
|
|
{
|
|
|
|
// **** test if real change
|
|
|
|
nlassert(precState.size()==_Bones.size());
|
|
|
|
bool change= false;
|
|
|
|
for(uint i=0;i<_Bones.size();i++)
|
|
|
|
{
|
|
|
|
if( _Bones[i].BoneScale!=precState[i].BoneScale ||
|
|
|
|
_Bones[i].SkinScale!=precState[i].SkinScale ||
|
|
|
|
_Bones[i].Selected!=precState[i].Selected)
|
|
|
|
{
|
|
|
|
change= true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// no change? no op
|
|
|
|
if(!change)
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// **** then bkup for undo
|
|
|
|
// change => the redo list is lost
|
|
|
|
_RedoQueue.clear();
|
|
|
|
|
|
|
|
// if not enough space, the last undo is lost
|
|
|
|
if(_UndoQueue.size()==MaxUndoRedo)
|
|
|
|
_UndoQueue.pop_front();
|
|
|
|
|
|
|
|
// add the precedent state to the undo queue
|
|
|
|
_UndoQueue.push_back(precState);
|
|
|
|
|
|
|
|
// refresh buttons
|
|
|
|
refreshUndoRedoView();
|
|
|
|
|
|
|
|
// refresh save button
|
|
|
|
if(dirtSave && !_SaveDirty)
|
|
|
|
{
|
|
|
|
_SaveDirty= true;
|
|
|
|
refreshSaveButton();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::undo()
|
|
|
|
{
|
|
|
|
nlassert(_UndoQueue.size()+_RedoQueue.size()<=MaxUndoRedo);
|
|
|
|
|
|
|
|
// is some undoable
|
|
|
|
if(_UndoQueue.size())
|
|
|
|
{
|
|
|
|
// current goes into the redo queue
|
|
|
|
_RedoQueue.push_front(_Bones);
|
|
|
|
// restore
|
|
|
|
_Bones= _UndoQueue.back();
|
|
|
|
// pop
|
|
|
|
_UndoQueue.pop_back();
|
|
|
|
|
|
|
|
// refresh display
|
|
|
|
applyMirrorToSkeleton();
|
|
|
|
refreshTextViews();
|
|
|
|
applySelectionToView();
|
|
|
|
|
|
|
|
// refresh buttons
|
|
|
|
refreshUndoRedoView();
|
|
|
|
|
|
|
|
// change => must save
|
|
|
|
_SaveDirty= true;
|
|
|
|
refreshSaveButton();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::redo()
|
|
|
|
{
|
|
|
|
nlassert(_UndoQueue.size()+_RedoQueue.size()<=MaxUndoRedo);
|
|
|
|
|
|
|
|
// is some redoable
|
|
|
|
if(_RedoQueue.size())
|
|
|
|
{
|
|
|
|
// current goes into the undo queue
|
|
|
|
_UndoQueue.push_back(_Bones);
|
|
|
|
// restore
|
|
|
|
_Bones= _RedoQueue.front();
|
|
|
|
// pop
|
|
|
|
_RedoQueue.pop_front();
|
|
|
|
|
|
|
|
// refresh display
|
|
|
|
applyMirrorToSkeleton();
|
|
|
|
refreshTextViews();
|
|
|
|
applySelectionToView();
|
|
|
|
|
|
|
|
// refresh buttons
|
|
|
|
refreshUndoRedoView();
|
|
|
|
|
|
|
|
// change => must save
|
|
|
|
_SaveDirty= true;
|
|
|
|
refreshSaveButton();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::applySelectionToView()
|
|
|
|
{
|
|
|
|
// update list box selection according to state
|
|
|
|
nlassert(_Bones.size()==(uint)_BoneList.GetCount());
|
|
|
|
for(uint i=0;i<_Bones.size();i++)
|
|
|
|
{
|
|
|
|
_BoneList.SetSel(i, _Bones[i].Selected);
|
|
|
|
}
|
|
|
|
UpdateData(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::refreshUndoRedoView()
|
|
|
|
{
|
|
|
|
CWnd *wnd;
|
|
|
|
wnd= GetDlgItem(IDC_SSD_BUTTON_UNDO);
|
|
|
|
if(wnd)
|
|
|
|
wnd->EnableWindow(!_UndoQueue.empty());
|
|
|
|
wnd= GetDlgItem(IDC_SSD_BUTTON_REDO);
|
|
|
|
if(wnd)
|
|
|
|
wnd->EnableWindow(!_RedoQueue.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::refreshSaveButton()
|
|
|
|
{
|
|
|
|
// SaveAs is always available
|
|
|
|
CWnd *wnd= GetDlgItem(IDC_SSD_BUTTON_SAVE);
|
|
|
|
if(wnd)
|
|
|
|
wnd->EnableWindow(_SaveDirty);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
sint CSkeletonScaleDlg::getBoneForMirror(uint boneId, std::string &mirrorName)
|
|
|
|
{
|
|
|
|
sint side= 0;
|
|
|
|
std::string::size_type pos;
|
|
|
|
nlassert(_SkeletonModel && boneId<_SkeletonModel->Bones.size());
|
|
|
|
mirrorName= _SkeletonModel->Bones[boneId].getBoneName();
|
|
|
|
|
|
|
|
if((pos= mirrorName.find(" R "))!=std::string::npos)
|
|
|
|
{
|
|
|
|
side= 1;
|
|
|
|
mirrorName[pos+1]= 'L';
|
|
|
|
}
|
|
|
|
else if((pos= mirrorName.find(" L "))!=std::string::npos)
|
|
|
|
{
|
|
|
|
side= -1;
|
|
|
|
mirrorName[pos+1]= 'R';
|
|
|
|
}
|
|
|
|
|
|
|
|
return side;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnSsdButtonMirror()
|
|
|
|
{
|
|
|
|
// bkup for undo
|
|
|
|
static TBoneMirrorArray precState;
|
|
|
|
precState= _Bones;
|
|
|
|
nlassert(_SkeletonModel);
|
|
|
|
|
|
|
|
// for each bone selected
|
|
|
|
bool change= false;
|
|
|
|
for(uint i=0;i<_Bones.size();i++)
|
|
|
|
{
|
|
|
|
CBoneMirror &bone= _Bones[i];
|
|
|
|
if(bone.Selected)
|
|
|
|
{
|
|
|
|
// get the bone side and mirrored name
|
|
|
|
std::string mirrorName;
|
|
|
|
sint side= getBoneForMirror(i, mirrorName);
|
|
|
|
// if not a "centered" bone
|
|
|
|
if(side!=0)
|
|
|
|
{
|
|
|
|
// get the bone with mirrored name
|
|
|
|
sint mirrorId= _SkeletonModel->getBoneIdByName(mirrorName);
|
|
|
|
if(mirrorId<0)
|
|
|
|
{
|
|
|
|
nlinfo("MirrorScale: Didn't found %s", mirrorName.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// copy setup from the dest bone
|
|
|
|
nlassert(mirrorId<(sint)_Bones.size());
|
|
|
|
_Bones[mirrorId].BoneScale= bone.BoneScale;
|
|
|
|
_Bones[mirrorId].SkinScale= bone.SkinScale;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// refresh display
|
|
|
|
applyMirrorToSkeleton();
|
|
|
|
refreshTextViews();
|
|
|
|
|
|
|
|
// if some change, bkup for undo/redo
|
|
|
|
pushUndoState(precState, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::drawSelection()
|
|
|
|
{
|
|
|
|
if(!_SkeletonModel)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nlassert(_SkeletonModel->Bones.size()==_Bones.size());
|
|
|
|
|
|
|
|
// **** Need recompute Bones bbox?
|
|
|
|
if(_BoneBBoxNeedRecompute)
|
|
|
|
{
|
|
|
|
_BoneBBoxNeedRecompute= false;
|
|
|
|
|
|
|
|
// for all bones
|
|
|
|
for(uint i=0;i<_SkeletonModel->Bones.size();i++)
|
|
|
|
{
|
|
|
|
NLMISC::CAABBox boneBBox;
|
|
|
|
bool empty= true;
|
|
|
|
|
|
|
|
// for all instances skinned
|
|
|
|
const std::set<NL3D::CTransform*> &skins= _SkeletonModel->getSkins();
|
|
|
|
std::set<NL3D::CTransform*>::const_iterator it;
|
|
|
|
for(it=skins.begin();it!=skins.end();it++)
|
|
|
|
{
|
|
|
|
NL3D::CTransform *skin= *it;
|
|
|
|
NLMISC::CAABBox skinBoneBBox;
|
|
|
|
// if the skin is skinned to this bone
|
|
|
|
if(skin->getSkinBoneBBox(skinBoneBBox, i))
|
|
|
|
{
|
|
|
|
// set or enlarge the bone bbox
|
|
|
|
if(empty)
|
|
|
|
{
|
|
|
|
empty= false;
|
|
|
|
boneBBox= skinBoneBBox;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
boneBBox= NLMISC::CAABBox::computeAABBoxUnion(boneBBox, skinBoneBBox);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there is no skin influence on this bone, still display a tiny bbox, to see the scale
|
|
|
|
const float defSize= 0.05f;
|
|
|
|
if(empty)
|
|
|
|
{
|
|
|
|
boneBBox.setCenter(NLMISC::CVector::Null);
|
|
|
|
boneBBox.setSize(NLMISC::CVector(defSize, defSize, defSize));
|
|
|
|
}
|
|
|
|
|
|
|
|
// assign
|
|
|
|
_BoneBBoxes[i]= boneBBox;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// **** Draw each selected bone
|
|
|
|
for(uint i=0;i<_SkeletonModel->Bones.size();i++)
|
|
|
|
{
|
|
|
|
// if bone not selected, skip
|
|
|
|
if(!_Bones[i].Selected)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// get the bone "Skin Matrix"
|
|
|
|
NL3D::CBone &bone= _SkeletonModel->Bones[i];
|
|
|
|
// force compute of this bone
|
|
|
|
if(!_SkeletonModel->isBoneComputed(i))
|
|
|
|
_SkeletonModel->forceComputeBone(i);
|
|
|
|
NLMISC::CMatrix worldSkinMat= bone.getWorldMatrix();
|
|
|
|
// scale with skin scale, because the localskeleton and world matrix do not have this scale
|
|
|
|
worldSkinMat.scale(bone.getSkinScale());
|
|
|
|
|
|
|
|
// Transform the local bbox in its associated matrix
|
|
|
|
NLMISC::CMatrix matBBox;
|
|
|
|
NLMISC::CAABBox bbox= _BoneBBoxes[i];
|
|
|
|
matBBox.setPos(bbox.getCenter());
|
|
|
|
matBBox.setRot(
|
|
|
|
NLMISC::CVector::I * bbox.getSize().x,
|
|
|
|
NLMISC::CVector::J * bbox.getSize().y,
|
|
|
|
NLMISC::CVector::K * bbox.getSize().z);
|
|
|
|
NLMISC::CMatrix finalMat;
|
|
|
|
finalMat.setMulMatrixNoProj(worldSkinMat, matBBox);
|
|
|
|
|
|
|
|
//Draw a wired bbox with this bone
|
|
|
|
NLMISC::CVector corner= finalMat.getPos() - finalMat.getI()/2 - finalMat.getJ()/2 - finalMat.getK()/2;
|
|
|
|
NL3D::IDriver *driver= NL3D::CNELU::Driver;
|
|
|
|
driver->setupModelMatrix(NLMISC::CMatrix::Identity);
|
|
|
|
NL3D::CDRU::drawWiredBox(corner, finalMat.getI(), finalMat.getJ(), finalMat.getK(), NLMISC::CRGBA::White, *driver);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnSsdButtonSaveScale()
|
|
|
|
{
|
|
|
|
// if no skeleton edited, quit
|
|
|
|
if(!_SkeletonModel)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// choose the file
|
|
|
|
std::string defaultFileName= _SkeletonFileName;
|
|
|
|
NLMISC::strFindReplace(defaultFileName, ".skel", ".scale");
|
|
|
|
CFileDialog fd(FALSE, "scale", defaultFileName.c_str(), OFN_OVERWRITEPROMPT, "SkelScaleFiles (*.scale)|*.scale|All Files (*.*)|*.*||", this) ;
|
|
|
|
fd.m_ofn.lpstrTitle= "Save As Skeleton Scale File";
|
|
|
|
if (fd.DoModal() == IDOK)
|
|
|
|
{
|
|
|
|
NLMISC::COFile f;
|
|
|
|
if( f.open((const char*)fd.GetPathName()) )
|
|
|
|
{
|
|
|
|
saveSkelScaleInStream(f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MessageBox("Failed to open file for write!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CSkeletonScaleDlg::OnSsdButtonLoadScale()
|
|
|
|
{
|
|
|
|
// if no skeleton edited, quit
|
|
|
|
if(!_SkeletonModel)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// choose the file
|
|
|
|
std::string defaultFileName= _SkeletonFileName;
|
|
|
|
NLMISC::strFindReplace(defaultFileName, ".skel", ".scale");
|
|
|
|
CFileDialog fd(TRUE, "scale", defaultFileName.c_str(), 0, "SkelScaleFiles (*.scale)|*.scale|All Files (*.*)|*.*||", this) ;
|
|
|
|
fd.m_ofn.lpstrTitle= "Load a Skeleton Scale File";
|
|
|
|
if (fd.DoModal() == IDOK)
|
|
|
|
{
|
|
|
|
NLMISC::CIFile f;
|
|
|
|
if( f.open((const char*)fd.GetPathName()) )
|
|
|
|
{
|
|
|
|
loadSkelScaleFromStream(f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MessageBox("Failed to open file for read!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
struct CBoneScaleInfo
|
|
|
|
{
|
|
|
|
std::string Name;
|
|
|
|
NLMISC::CVector Scale;
|
|
|
|
NLMISC::CVector SkinScale;
|
|
|
|
|
|
|
|
void serial(NLMISC::IStream &f)
|
|
|
|
{
|
|
|
|
sint32 ver= f.serialVersion(0);
|
|
|
|
f.serial(Name, Scale, SkinScale);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
bool CSkeletonScaleDlg::saveSkelScaleInStream(NLMISC::IStream &f)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
nlassert(_SkeletonModel);
|
|
|
|
|
|
|
|
// Copies bone scales from the model
|
|
|
|
std::vector<CBoneScaleInfo> boneScales;
|
|
|
|
boneScales.resize(_SkeletonModel->Bones.size());
|
|
|
|
for(uint i=0;i<boneScales.size();i++)
|
|
|
|
{
|
|
|
|
NL3D::CBone &bone= _SkeletonModel->Bones[i];
|
|
|
|
CBoneScaleInfo &boneScale= boneScales[i];
|
|
|
|
|
|
|
|
// get scale info from current edited skeleton
|
|
|
|
boneScale.Name= bone.getBoneName();
|
|
|
|
boneScale.Scale= bone.getScale();
|
|
|
|
boneScale.SkinScale= bone.getSkinScale();
|
|
|
|
}
|
|
|
|
|
|
|
|
// save the file
|
|
|
|
sint32 ver= f.serialVersion(0);
|
|
|
|
f.serialCont(boneScales);
|
|
|
|
}
|
|
|
|
catch(NLMISC::EStream &)
|
|
|
|
{
|
|
|
|
MessageBox("Failed to save file!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
bool CSkeletonScaleDlg::loadSkelScaleFromStream(NLMISC::IStream &f)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
nlassert(_SkeletonModel);
|
|
|
|
|
|
|
|
// load the file
|
|
|
|
sint32 ver= f.serialVersion(0);
|
|
|
|
std::vector<CBoneScaleInfo> boneScales;
|
|
|
|
f.serialCont(boneScales);
|
|
|
|
|
|
|
|
// apply to the current skeleton
|
|
|
|
for(uint i=0;i<boneScales.size();i++)
|
|
|
|
{
|
|
|
|
sint32 boneId= _SkeletonModel->getBoneIdByName(boneScales[i].Name);
|
|
|
|
if(boneId>=0 && boneId<(sint32)_SkeletonModel->Bones.size())
|
|
|
|
{
|
|
|
|
CBoneScaleInfo &boneScale= boneScales[i];
|
|
|
|
_SkeletonModel->Bones[boneId].setScale(boneScale.Scale);
|
|
|
|
_SkeletonModel->Bones[boneId].setSkinScale(boneScale.SkinScale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bkup _Bones, for undo
|
|
|
|
static TBoneMirrorArray precState;
|
|
|
|
precState= _Bones;
|
|
|
|
|
|
|
|
// Then reapply to the mirror
|
|
|
|
applySkeletonToMirror();
|
|
|
|
|
|
|
|
// change => must save
|
|
|
|
pushUndoState(precState, true);
|
|
|
|
|
|
|
|
// and update display
|
|
|
|
refreshTextViews();
|
|
|
|
}
|
|
|
|
catch(NLMISC::EStream &)
|
|
|
|
{
|
|
|
|
MessageBox("Failed to save file!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CSkeletonScaleDlg::OnClose()
|
|
|
|
{
|
|
|
|
_ObjViewer->getMainFrame()->OnWindowSkeletonScale();
|
|
|
|
}
|