1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 // skeleton_scale_dlg.cpp : implementation file
24 #include "object_viewer.h"
25 #include "skeleton_scale_dlg.h"
26 #include "nel/3d/skeleton_shape.h"
27 #include "nel/misc/algo.h"
28 #include "main_frame.h"
31 // ***************************************************************************
32 #define NL_SSD_SLIDER_SIZE 1000
33 #define NL_SSD_SLIDER_THRESHOLD 0.03f
34 #define NL_SSD_SLIDER_SCALE 2
37 /////////////////////////////////////////////////////////////////////////////
38 // CSkeletonScaleDlg dialog
41 CSkeletonScaleDlg::CSkeletonScaleDlg(CObjectViewer
*viewer
, CWnd
* pParent
/*=NULL*/)
42 : CDialog(CSkeletonScaleDlg::IDD
, pParent
) ,_ObjViewer(viewer
)
44 //{{AFX_DATA_INIT(CSkeletonScaleDlg)
45 _StaticFileName
= _T("");
54 // Init Scale Sliders ptrs
55 nlassert(SidCount
==6);
56 _ScaleSliders
[SidBoneX
]= &_SliderBoneX
;
57 _ScaleSliders
[SidBoneY
]= &_SliderBoneY
;
58 _ScaleSliders
[SidBoneZ
]= &_SliderBoneZ
;
59 _ScaleSliders
[SidSkinX
]= &_SliderSkinX
;
60 _ScaleSliders
[SidSkinY
]= &_SliderSkinY
;
61 _ScaleSliders
[SidSkinZ
]= &_SliderSkinZ
;
62 _ScaleEdits
[SidBoneX
]= &_EditBoneSX
;
63 _ScaleEdits
[SidBoneY
]= &_EditBoneSY
;
64 _ScaleEdits
[SidBoneZ
]= &_EditBoneSZ
;
65 _ScaleEdits
[SidSkinX
]= &_EditSkinSX
;
66 _ScaleEdits
[SidSkinY
]= &_EditSkinSY
;
67 _ScaleEdits
[SidSkinZ
]= &_EditSkinSZ
;
68 _StaticScaleMarkers
[SidBoneX
]= &_StaticScaleMarkerBoneSX
;
69 _StaticScaleMarkers
[SidBoneY
]= &_StaticScaleMarkerBoneSY
;
70 _StaticScaleMarkers
[SidBoneZ
]= &_StaticScaleMarkerBoneSZ
;
71 _StaticScaleMarkers
[SidSkinX
]= &_StaticScaleMarkerSkinSX
;
72 _StaticScaleMarkers
[SidSkinY
]= &_StaticScaleMarkerSkinSY
;
73 _StaticScaleMarkers
[SidSkinZ
]= &_StaticScaleMarkerSkinSZ
;
76 _SliderEdited
= SidNone
;
80 _UndoQueue
.resize(MaxUndoRedo
);
81 _RedoQueue
.resize(MaxUndoRedo
);
85 _BoneBBoxNeedRecompute
= false;
88 CSkeletonScaleDlg::~CSkeletonScaleDlg()
93 void CSkeletonScaleDlg::DoDataExchange(CDataExchange
* pDX
)
95 CDialog::DoDataExchange(pDX
);
96 //{{AFX_DATA_MAP(CSkeletonScaleDlg)
97 DDX_Control(pDX
, IDC_SSD_SLIDER_SKIN_SZ
, _SliderSkinZ
);
98 DDX_Control(pDX
, IDC_SSD_SLIDER_SKIN_SY
, _SliderSkinY
);
99 DDX_Control(pDX
, IDC_SSD_SLIDER_SKIN_SX
, _SliderSkinX
);
100 DDX_Control(pDX
, IDC_SSD_SLIDER_BONE_SZ
, _SliderBoneZ
);
101 DDX_Control(pDX
, IDC_SSD_SLIDER_BONE_SY
, _SliderBoneY
);
102 DDX_Control(pDX
, IDC_SSD_SLIDER_BONE_SX
, _SliderBoneX
);
103 DDX_Control(pDX
, IDC_SSD_LIST
, _BoneList
);
104 DDX_Text(pDX
, IDC_SSD_STATIC_FILENAME
, _StaticFileName
);
105 DDX_Text(pDX
, IDC_SSD_EDIT_BONE_SX
, _EditBoneSX
);
106 DDX_Text(pDX
, IDC_SSD_EDIT_BONE_SY
, _EditBoneSY
);
107 DDX_Text(pDX
, IDC_SSD_EDIT_BONE_SZ
, _EditBoneSZ
);
108 DDX_Text(pDX
, IDC_SSD_EDIT_SKIN_SX
, _EditSkinSX
);
109 DDX_Text(pDX
, IDC_SSD_EDIT_SKIN_SY
, _EditSkinSY
);
110 DDX_Text(pDX
, IDC_SSD_EDIT_SKIN_SZ
, _EditSkinSZ
);
111 DDX_Control(pDX
, IDC_SSD_STATIC_SKIN_SZ
, _StaticScaleMarkerSkinSZ
);
112 DDX_Control(pDX
, IDC_SSD_STATIC_SKIN_SY
, _StaticScaleMarkerSkinSY
);
113 DDX_Control(pDX
, IDC_SSD_STATIC_SKIN_SX
, _StaticScaleMarkerSkinSX
);
114 DDX_Control(pDX
, IDC_SSD_STATIC_BONE_SZ
, _StaticScaleMarkerBoneSZ
);
115 DDX_Control(pDX
, IDC_SSD_STATIC_BONE_SY
, _StaticScaleMarkerBoneSY
);
116 DDX_Control(pDX
, IDC_SSD_STATIC_BONE_SX
, _StaticScaleMarkerBoneSX
);
121 BEGIN_MESSAGE_MAP(CSkeletonScaleDlg
, CDialog
)
122 //{{AFX_MSG_MAP(CSkeletonScaleDlg)
125 ON_NOTIFY(NM_RELEASEDCAPTURE
, IDC_SSD_SLIDER_BONE_SX
, OnReleasedcaptureSsdSliderBoneSx
)
126 ON_NOTIFY(NM_RELEASEDCAPTURE
, IDC_SSD_SLIDER_BONE_SY
, OnReleasedcaptureSsdSliderBoneSy
)
127 ON_NOTIFY(NM_RELEASEDCAPTURE
, IDC_SSD_SLIDER_BONE_SZ
, OnReleasedcaptureSsdSliderBoneSz
)
128 ON_NOTIFY(NM_RELEASEDCAPTURE
, IDC_SSD_SLIDER_SKIN_SX
, OnReleasedcaptureSsdSliderSkinSx
)
129 ON_NOTIFY(NM_RELEASEDCAPTURE
, IDC_SSD_SLIDER_SKIN_SY
, OnReleasedcaptureSsdSliderSkinSy
)
130 ON_NOTIFY(NM_RELEASEDCAPTURE
, IDC_SSD_SLIDER_SKIN_SZ
, OnReleasedcaptureSsdSliderSkinSz
)
131 ON_EN_CHANGE(IDC_SSD_EDIT_BONE_SX
, OnChangeSsdEditBoneSx
)
132 ON_EN_CHANGE(IDC_SSD_EDIT_BONE_SY
, OnChangeSsdEditBoneSy
)
133 ON_EN_CHANGE(IDC_SSD_EDIT_BONE_SZ
, OnChangeSsdEditBoneSz
)
134 ON_EN_CHANGE(IDC_SSD_EDIT_SKIN_SX
, OnChangeSsdEditSkinSx
)
135 ON_EN_CHANGE(IDC_SSD_EDIT_SKIN_SY
, OnChangeSsdEditSkinSy
)
136 ON_EN_CHANGE(IDC_SSD_EDIT_SKIN_SZ
, OnChangeSsdEditSkinSz
)
137 ON_EN_KILLFOCUS(IDC_SSD_EDIT_BONE_SX
, OnKillfocusSsdEditBoneSx
)
138 ON_EN_KILLFOCUS(IDC_SSD_EDIT_BONE_SY
, OnKillfocusSsdEditBoneSy
)
139 ON_EN_KILLFOCUS(IDC_SSD_EDIT_BONE_SZ
, OnKillfocusSsdEditBoneSz
)
140 ON_EN_KILLFOCUS(IDC_SSD_EDIT_SKIN_SX
, OnKillfocusSsdEditSkinSx
)
141 ON_EN_KILLFOCUS(IDC_SSD_EDIT_SKIN_SY
, OnKillfocusSsdEditSkinSy
)
142 ON_EN_KILLFOCUS(IDC_SSD_EDIT_SKIN_SZ
, OnKillfocusSsdEditSkinSz
)
143 ON_EN_SETFOCUS(IDC_SSD_EDIT_BONE_SX
, OnSetfocusSsdEditBoneSx
)
144 ON_EN_SETFOCUS(IDC_SSD_EDIT_BONE_SY
, OnSetfocusSsdEditBoneSy
)
145 ON_EN_SETFOCUS(IDC_SSD_EDIT_BONE_SZ
, OnSetfocusSsdEditBoneSz
)
146 ON_EN_SETFOCUS(IDC_SSD_EDIT_SKIN_SX
, OnSetfocusSsdEditSkinSx
)
147 ON_EN_SETFOCUS(IDC_SSD_EDIT_SKIN_SY
, OnSetfocusSsdEditSkinSy
)
148 ON_EN_SETFOCUS(IDC_SSD_EDIT_SKIN_SZ
, OnSetfocusSsdEditSkinSz
)
149 ON_LBN_SELCHANGE(IDC_SSD_LIST
, OnSelchangeSsdList
)
150 ON_BN_CLICKED(IDC_SSD_BUTTON_UNDO
, OnSsdButtonUndo
)
151 ON_BN_CLICKED(IDC_SSD_BUTTON_REDO
, OnSsdButtonRedo
)
152 ON_BN_CLICKED(IDC_SSD_BUTTON_SAVE
, OnSsdButtonSave
)
153 ON_BN_CLICKED(IDC_SSD_BUTTON_SAVEAS
, OnSsdButtonSaveas
)
154 ON_BN_CLICKED(IDC_SSD_BUTTON_MIRROR
, OnSsdButtonMirror
)
155 ON_BN_CLICKED(IDC_SSD_BUTTON_SAVE_SCALE
, OnSsdButtonSaveScale
)
156 ON_BN_CLICKED(IDC_SSD_BUTTON_LOAD_SCALE
, OnSsdButtonLoadScale
)
161 /////////////////////////////////////////////////////////////////////////////
162 // CSkeletonScaleDlg message handlers
164 void CSkeletonScaleDlg::OnDestroy()
166 setRegisterWindowState (this, REGKEY_SKELETON_SCALE_DLG
);
167 CDialog::OnDestroy();
172 // ***************************************************************************
173 void CSkeletonScaleDlg::setSkeletonToEdit(NL3D::CSkeletonModel
*skel
, const std::string
&fileName
)
177 _SkeletonModel
= skel
;
178 _SkeletonFileName
= fileName
;
181 // **** Setup File name
182 _StaticFileName
= fileName
.c_str();
184 // **** Setup Bone mirror
188 _Bones
.resize(_SkeletonModel
->Bones
.size());
189 // copy from skel to mirror
190 applySkeletonToMirror();
193 // **** reset bone bbox here
195 _BoneBBoxes
.resize(_Bones
.size());
196 // delegate to drawSelection(), cause skins not still bound
197 _BoneBBoxNeedRecompute
= true;
199 // **** Setup Bone List
200 _BoneList
.ResetContent();
203 for(uint i
=0;i
<_SkeletonModel
->Bones
.size();i
++)
205 const std::string tabStr
= " ";
206 std::string name
= _SkeletonModel
->Bones
[i
].getBoneName();
208 // append a tab for easy hierarchy
211 while((boneId
=_SkeletonModel
->Bones
[boneId
].getFatherId())!=-1)
212 name
= tabStr
+ name
;
214 // append to the list
215 _BoneList
.AddString(nlUtf8ToTStr(name
));
220 // **** unselect all widgets
221 for(i
=0;i
<SidCount
;i
++)
223 _ScaleSliders
[i
]->SetRange(0, NL_SSD_SLIDER_SIZE
);
224 _ScaleSliders
[i
]->SetPos(NL_SSD_SLIDER_SIZE
/2);
225 _ScaleEdits
[i
]->Empty();
226 _StaticScaleMarkers
[i
]->SetWindowText(_T("100%"));
228 // ensure no problem with scale and ALT-TAB
229 _SliderEdited
= SidNone
;
232 // **** clean undo/redo
235 refreshUndoRedoView();
238 // **** clear save button
248 // ***************************************************************************
249 BOOL
CSkeletonScaleDlg::OnInitDialog()
251 CDialog::OnInitDialog();
253 setSkeletonToEdit(NULL
, "");
255 return TRUE
; // return TRUE unless you set the focus to a control
256 // EXCEPTION: OCX Property Pages should return FALSE
261 // ***************************************************************************
262 void CSkeletonScaleDlg::OnVScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
264 CSliderCtrl
*sliderCtrl
= (CSliderCtrl
*)pScrollBar
;
265 TScaleId sliderId
= getScaleIdFromSliderCtrl(sliderCtrl
);
266 if(sliderId
!=SidNone
&& nSBCode
==SB_THUMBPOSITION
|| nSBCode
==SB_THUMBTRACK
)
268 // If the user press ALT-Tab while dragging an old slider, the old slider is not released.
269 // ThereFore, release the old one now
270 if(_SliderEdited
!=SidNone
&& _SliderEdited
!=sliderId
)
272 onSliderReleased(_SliderEdited
);
275 // if begin of slide, bkup state
276 if(_SliderEdited
==SidNone
)
278 _SliderEdited
= sliderId
;
279 // Bkup all scales (dont bother selected bones or which scale is edited...)
284 applyScaleSlider(nPos
);
287 CDialog::OnVScroll(nSBCode
, nPos
, pScrollBar
);
290 // ***************************************************************************
291 void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderBoneSx(NMHDR
* pNMHDR
, LRESULT
* pResult
)
293 onSliderReleased(SidBoneX
);
296 void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderBoneSy(NMHDR
* pNMHDR
, LRESULT
* pResult
)
298 onSliderReleased(SidBoneY
);
301 void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderBoneSz(NMHDR
* pNMHDR
, LRESULT
* pResult
)
303 onSliderReleased(SidBoneZ
);
306 void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderSkinSx(NMHDR
* pNMHDR
, LRESULT
* pResult
)
308 onSliderReleased(SidSkinX
);
311 void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderSkinSy(NMHDR
* pNMHDR
, LRESULT
* pResult
)
313 onSliderReleased(SidSkinY
);
316 void CSkeletonScaleDlg::OnReleasedcaptureSsdSliderSkinSz(NMHDR
* pNMHDR
, LRESULT
* pResult
)
318 onSliderReleased(SidSkinZ
);
322 void CSkeletonScaleDlg::onSliderReleased(TScaleId sid
)
324 // Get value from slider
325 sint value
= _ScaleSliders
[sid
]->GetPos();
327 // If the user press ALT-Tab while dragging an old slider, the old slider is not released.
328 // ThereFore, release the old one now
329 if(_SliderEdited
!=SidNone
&& _SliderEdited
!=sid
)
331 onSliderReleased(_SliderEdited
);
335 if(_SliderEdited
==SidNone
)
338 // Bkup all scales (dont bother selected bones or which scale is edited...)
342 // apply the final value
343 applyScaleSlider(value
);
345 // And reset the slider
346 _ScaleSliders
[_SliderEdited
]->SetPos(NL_SSD_SLIDER_SIZE
/2);
347 _StaticScaleMarkers
[_SliderEdited
]->SetWindowText(_T("100%"));
348 _SliderEdited
= SidNone
;
350 // push an undo/redo only at release of slide. push the value of scale before slide
351 pushUndoState(_BkupBones
, true);
355 // ***************************************************************************
356 void CSkeletonScaleDlg::applyScaleSlider(sint scrollValue
)
358 // get scale beetween -1 and 1.
359 float scale
= (NL_SSD_SLIDER_SIZE
/2-scrollValue
)/(float)(NL_SSD_SLIDER_SIZE
/2);
360 NLMISC::clamp(scale
, -1.f
, 1.f
);
364 if(fabs(scale
)<NL_SSD_SLIDER_THRESHOLD
)
369 float minv
= 1.0f
/ NL_SSD_SLIDER_SCALE
;
370 factor
= minv
*(-scale
) + 1.0f
*(1+scale
);
375 float maxv
= NL_SSD_SLIDER_SCALE
;
376 factor
= maxv
*(scale
) + 1.0f
*(1-scale
);
379 // Apply the noise to each selected bones
380 for(uint i
=0;i
<_Bones
.size();i
++)
382 CBoneMirror
&bone
= _Bones
[i
];
383 CBoneMirror
&bkup
= _BkupBones
[i
];
386 // apply to the scaled component
387 switch(_SliderEdited
)
389 case SidBoneX
: bone
.BoneScale
.x
= bkup
.BoneScale
.x
*factor
; break;
390 case SidBoneY
: bone
.BoneScale
.y
= bkup
.BoneScale
.y
*factor
; break;
391 case SidBoneZ
: bone
.BoneScale
.z
= bkup
.BoneScale
.z
*factor
; break;
392 case SidSkinX
: bone
.SkinScale
.x
= bkup
.SkinScale
.x
*factor
; break;
393 case SidSkinY
: bone
.SkinScale
.y
= bkup
.SkinScale
.y
*factor
; break;
394 case SidSkinZ
: bone
.SkinScale
.z
= bkup
.SkinScale
.z
*factor
; break;
397 roundClampScale(bone
.BoneScale
);
398 roundClampScale(bone
.SkinScale
);
402 // update the skeleton view
403 applyMirrorToSkeleton();
405 // refresh text views
408 // update marker text
409 std::string str
= NLMISC::toString("%d%%", (sint
)(factor
*100));
410 _StaticScaleMarkers
[_SliderEdited
]->SetWindowText(nlUtf8ToTStr(str
));
413 // ***************************************************************************
414 void CSkeletonScaleDlg::applyMirrorToSkeleton()
418 nlassert(_SkeletonModel
->Bones
.size()==_Bones
.size());
419 for(uint i
=0;i
<_Bones
.size();i
++)
421 // unmul from precision
422 _SkeletonModel
->Bones
[i
].setScale(_Bones
[i
].BoneScale
/ NL_SSD_SCALE_PRECISION
);
423 _SkeletonModel
->Bones
[i
].setSkinScale(_Bones
[i
].SkinScale
/ NL_SSD_SCALE_PRECISION
);
429 // ***************************************************************************
430 void CSkeletonScaleDlg::applySkeletonToMirror()
434 nlassert(_SkeletonModel
->Bones
.size()==_Bones
.size());
435 for(uint i
=0;i
<_SkeletonModel
->Bones
.size();i
++)
437 // mul by precision, and round
438 _Bones
[i
].SkinScale
= _SkeletonModel
->Bones
[i
].getSkinScale() * NL_SSD_SCALE_PRECISION
;
439 _Bones
[i
].BoneScale
= _SkeletonModel
->Bones
[i
].getScale() * NL_SSD_SCALE_PRECISION
;
440 roundClampScale(_Bones
[i
].SkinScale
);
441 roundClampScale(_Bones
[i
].BoneScale
);
447 // ***************************************************************************
448 void CSkeletonScaleDlg::refreshTextViews()
450 // 1.f for each component if multiple selection is different, else 0.f
451 NLMISC::CVector boneScaleDiff
= NLMISC::CVector::Null
;
452 NLMISC::CVector skinScaleDiff
= NLMISC::CVector::Null
;
453 // valid if scale of each bone component is same
454 NLMISC::CVector boneScaleAll
= NLMISC::CVector::Null
;
455 NLMISC::CVector skinScaleAll
= NLMISC::CVector::Null
;
456 bool someSelected
= false;
458 // For all bones selected
459 for(uint i
=0;i
<_Bones
.size();i
++)
461 CBoneMirror
&bone
= _Bones
[i
];
468 boneScaleAll
= bone
.BoneScale
;
469 skinScaleAll
= bone
.SkinScale
;
473 // compare each component, if different, flag
474 if(boneScaleAll
.x
!= bone
.BoneScale
.x
) boneScaleDiff
.x
= 1.f
;
475 if(boneScaleAll
.y
!= bone
.BoneScale
.y
) boneScaleDiff
.y
= 1.f
;
476 if(boneScaleAll
.z
!= bone
.BoneScale
.z
) boneScaleDiff
.z
= 1.f
;
477 if(skinScaleAll
.x
!= bone
.SkinScale
.x
) skinScaleDiff
.x
= 1.f
;
478 if(skinScaleAll
.y
!= bone
.SkinScale
.y
) skinScaleDiff
.y
= 1.f
;
479 if(skinScaleAll
.z
!= bone
.SkinScale
.z
) skinScaleDiff
.z
= 1.f
;
484 // if none selected, force empty text
487 boneScaleDiff
.set(1.f
,1.f
,1.f
);
488 skinScaleDiff
.set(1.f
,1.f
,1.f
);
491 // All component refresh or only one refresh?
492 nlassert(SidCount
==6);
493 refreshTextViewWithScale(SidBoneX
, boneScaleAll
.x
, boneScaleDiff
.x
);
494 refreshTextViewWithScale(SidBoneY
, boneScaleAll
.y
, boneScaleDiff
.y
);
495 refreshTextViewWithScale(SidBoneZ
, boneScaleAll
.z
, boneScaleDiff
.z
);
496 refreshTextViewWithScale(SidSkinX
, skinScaleAll
.x
, skinScaleDiff
.x
);
497 refreshTextViewWithScale(SidSkinY
, skinScaleAll
.y
, skinScaleDiff
.y
);
498 refreshTextViewWithScale(SidSkinZ
, skinScaleAll
.z
, skinScaleDiff
.z
);
505 // ***************************************************************************
506 void CSkeletonScaleDlg::refreshTextViewWithScale(TScaleId sid
, float scale
, float diff
)
508 // if different values selected, diff
511 _ScaleEdits
[sid
]->Empty();
517 // ensure correct display with no precision problem
518 sint value
= uint(floor(scale
+0.5f
));
519 sint whole
= value
*100/NL_SSD_SCALE_PRECISION
;
520 sint decimal
= value
- whole
*(NL_SSD_SCALE_PRECISION
/100);
521 sprintf(str
, "%d.%d", whole
, decimal
);
522 *_ScaleEdits
[sid
]= str
;
527 // ***************************************************************************
528 void CSkeletonScaleDlg::roundClampScale(NLMISC::CVector
&v
) const
533 v
.x
= (float)floor(v
.x
);
534 v
.y
= (float)floor(v
.y
);
535 v
.z
= (float)floor(v
.z
);
536 // Minimum is 1 (avoid 0 scale)
537 v
.maxof(v
, NLMISC::CVector(1.f
,1.f
,1.f
));
540 // ***************************************************************************
541 CSkeletonScaleDlg::TScaleId
CSkeletonScaleDlg::getScaleIdFromSliderCtrl(CSliderCtrl
*sliderCtrl
) const
543 for(uint i
=0;i
<SidCount
;i
++)
545 if(sliderCtrl
==_ScaleSliders
[i
])
553 // ***************************************************************************
554 CSkeletonScaleDlg::TScaleId
CSkeletonScaleDlg::getScaleIdFromEditId(UINT ctrlId
) const
556 nlctassert(SidCount
==6);
557 if(ctrlId
==IDC_SSD_EDIT_BONE_SX
) return SidBoneX
;
558 if(ctrlId
==IDC_SSD_EDIT_BONE_SY
) return SidBoneY
;
559 if(ctrlId
==IDC_SSD_EDIT_BONE_SZ
) return SidBoneZ
;
560 if(ctrlId
==IDC_SSD_EDIT_SKIN_SX
) return SidSkinX
;
561 if(ctrlId
==IDC_SSD_EDIT_SKIN_SY
) return SidSkinY
;
562 if(ctrlId
==IDC_SSD_EDIT_SKIN_SZ
) return SidSkinZ
;
568 // ***************************************************************************
569 void CSkeletonScaleDlg::OnChangeSsdEditBoneSx()
571 onChangeEditText(IDC_SSD_EDIT_BONE_SX
);
573 void CSkeletonScaleDlg::OnChangeSsdEditBoneSy()
575 onChangeEditText(IDC_SSD_EDIT_BONE_SY
);
577 void CSkeletonScaleDlg::OnChangeSsdEditBoneSz()
579 onChangeEditText(IDC_SSD_EDIT_BONE_SZ
);
581 void CSkeletonScaleDlg::OnChangeSsdEditSkinSx()
583 onChangeEditText(IDC_SSD_EDIT_SKIN_SX
);
585 void CSkeletonScaleDlg::OnChangeSsdEditSkinSy()
587 onChangeEditText(IDC_SSD_EDIT_SKIN_SY
);
589 void CSkeletonScaleDlg::OnChangeSsdEditSkinSz()
591 onChangeEditText(IDC_SSD_EDIT_SKIN_SZ
);
595 static void concatEdit2Lines(CEdit
&edit
)
597 const uint lineLen
= 1000;
599 // retrieve the 2 lines.
600 TCHAR tmp0
[2*lineLen
];
602 n
= edit
.GetLine(0, tmp0
, lineLen
); tmp0
[n
]= 0;
603 n
= edit
.GetLine(1, tmp1
, lineLen
); tmp1
[n
]= 0;
604 // concat and update the CEdit.
605 edit
.SetWindowText(_tcscat(tmp0
, tmp1
));
609 void CSkeletonScaleDlg::onChangeEditText(UINT ctrlId
)
611 CEdit
*ce
= (CEdit
*)GetDlgItem(ctrlId
);
615 // Trick to track "Enter" keypress: CEdit are multiline. If GetLineCount()>1, then
616 // user has press enter.
617 if(ce
->GetLineCount()>1)
619 // must ccat 2 lines of the CEdit.
620 concatEdit2Lines(*ce
);
621 // update text members
625 // update scale values from the CEdit
626 updateScalesFromText(ctrlId
);
628 // then re-update CEdit from scale values
635 // ***************************************************************************
636 void CSkeletonScaleDlg::OnKillfocusSsdEditBoneSx()
638 onQuitEditText(IDC_SSD_EDIT_BONE_SX
);
640 void CSkeletonScaleDlg::OnKillfocusSsdEditBoneSy()
642 onQuitEditText(IDC_SSD_EDIT_BONE_SY
);
644 void CSkeletonScaleDlg::OnKillfocusSsdEditBoneSz()
646 onQuitEditText(IDC_SSD_EDIT_BONE_SZ
);
648 void CSkeletonScaleDlg::OnKillfocusSsdEditSkinSx()
650 onQuitEditText(IDC_SSD_EDIT_SKIN_SX
);
652 void CSkeletonScaleDlg::OnKillfocusSsdEditSkinSy()
654 onQuitEditText(IDC_SSD_EDIT_SKIN_SY
);
656 void CSkeletonScaleDlg::OnKillfocusSsdEditSkinSz()
658 onQuitEditText(IDC_SSD_EDIT_SKIN_SZ
);
662 void CSkeletonScaleDlg::onQuitEditText(UINT ctrlId
)
664 CEdit
*ce
= (CEdit
*)GetDlgItem(ctrlId
);
669 // update scale values from the CEdit
670 updateScalesFromText(ctrlId
);
672 // then re-update CEdit from scale values
678 // ***************************************************************************
679 void CSkeletonScaleDlg::OnSetfocusSsdEditBoneSx()
681 onSelectEditText(IDC_SSD_EDIT_BONE_SX
);
683 void CSkeletonScaleDlg::OnSetfocusSsdEditBoneSy()
685 onSelectEditText(IDC_SSD_EDIT_BONE_SY
);
687 void CSkeletonScaleDlg::OnSetfocusSsdEditBoneSz()
689 onSelectEditText(IDC_SSD_EDIT_BONE_SZ
);
691 void CSkeletonScaleDlg::OnSetfocusSsdEditSkinSx()
693 onSelectEditText(IDC_SSD_EDIT_SKIN_SX
);
695 void CSkeletonScaleDlg::OnSetfocusSsdEditSkinSy()
697 onSelectEditText(IDC_SSD_EDIT_SKIN_SY
);
699 void CSkeletonScaleDlg::OnSetfocusSsdEditSkinSz()
701 onSelectEditText(IDC_SSD_EDIT_SKIN_SZ
);
704 void CSkeletonScaleDlg::onSelectEditText(UINT ctrlId
)
706 CEdit
*ce
= (CEdit
*)GetDlgItem(ctrlId
);
709 ce
->PostMessage(EM_SETSEL
, 0, -1);
715 // ***************************************************************************
716 void CSkeletonScaleDlg::updateScalesFromText(UINT ctrlId
)
718 TScaleId sid
= getScaleIdFromEditId(ctrlId
);
722 // get the scale info
723 std::string str
= NLMISC::tStrToUtf8(*_ScaleEdits
[sid
]);
727 sscanf(str
.c_str(), "%f", &f
);
729 f
*= NL_SSD_SCALE_PRECISION
/100;
730 f
= (float)floor(f
+0.5f
);
733 static TBoneMirrorArray precState
;
736 // For all bones selected, set the edited value
737 for(uint i
=0;i
<_Bones
.size();i
++)
739 CBoneMirror
&bone
= _Bones
[i
];
744 case SidBoneX
: bone
.BoneScale
.x
= f
; break;
745 case SidBoneY
: bone
.BoneScale
.y
= f
; break;
746 case SidBoneZ
: bone
.BoneScale
.z
= f
; break;
747 case SidSkinX
: bone
.SkinScale
.x
= f
; break;
748 case SidSkinY
: bone
.SkinScale
.y
= f
; break;
749 case SidSkinZ
: bone
.SkinScale
.z
= f
; break;
752 roundClampScale(bone
.BoneScale
);
753 roundClampScale(bone
.SkinScale
);
757 // change => bkup for undo
758 pushUndoState(precState
, true);
760 // mirror changed => update skeleton
761 applyMirrorToSkeleton();
766 // ***************************************************************************
767 void CSkeletonScaleDlg::OnSelchangeSsdList()
771 // **** Retrieve List of selected bones.
772 uint count
= _BoneList
.GetSelCount();
773 std::vector
<int> items
;
777 _BoneList
.GetSelItems(count
, &items
[0]);
780 // **** update the Selection array
782 static TBoneMirrorArray precState
;
785 // identify selected items in a set
786 std::set
<int> selSet
;
789 selSet
.insert(items
[i
]);
791 // change selection of Bones
792 for(i
=0;i
<_Bones
.size();i
++)
794 if(selSet
.find(i
)!=selSet
.end())
795 _Bones
[i
].Selected
= true;
797 _Bones
[i
].Selected
= false;
801 // selection change => no need to dirt save
802 pushUndoState(precState
, false);
804 // **** refresh text views
809 // ***************************************************************************
810 void CSkeletonScaleDlg::OnSsdButtonUndo()
815 void CSkeletonScaleDlg::OnSsdButtonRedo()
821 // ***************************************************************************
822 void CSkeletonScaleDlg::OnSsdButtonSave()
824 // if no skeleton edited, quit
830 if( f
.open(_SkeletonFileName
) )
832 if(saveCurrentInStream(f
))
834 // no more need to save (done)
841 MessageBox(_T("Failed to open file for write!"));
845 void CSkeletonScaleDlg::OnSsdButtonSaveas()
847 // if no skeleton edited, quit
852 CFileDialog
fd(FALSE
, _T("skel"), nlUtf8ToTStr(_SkeletonFileName
), OFN_OVERWRITEPROMPT
, _T("SkelFiles (*.skel)|*.skel|All Files (*.*)|*.*||"), this);
853 fd
.m_ofn
.lpstrTitle
= _T("Save As Skeleton");
854 if (fd
.DoModal() == IDOK
)
858 if (f
.open(NLMISC::tStrToUtf8(fd
.GetPathName())))
860 if(saveCurrentInStream(f
))
862 // no more need to save (done)
867 // bkup the valid fileName (new file edited)
868 _SkeletonFileName
= NLMISC::tStrToUtf8(fd
.GetPathName());
869 _StaticFileName
= _SkeletonFileName
.c_str();
874 MessageBox(_T("Failed to open file for write!"));
879 // ***************************************************************************
880 bool CSkeletonScaleDlg::saveCurrentInStream(NLMISC::IStream
&f
)
884 nlassert(_SkeletonModel
);
885 nlassert(_SkeletonModel
->Shape
);
887 // Retrieve boneBase definition from the current skeleton
888 std::vector
<NL3D::CBoneBase
> boneBases
;
889 (NLMISC::safe_cast
<NL3D::CSkeletonShape
*>((NL3D::IShape
*)_SkeletonModel
->Shape
))->retrieve(boneBases
);
891 // Copies bone scales from the model
892 nlassert(boneBases
.size()==_SkeletonModel
->Bones
.size());
893 for(uint i
=0;i
<_SkeletonModel
->Bones
.size();i
++)
895 NL3D::CBone
&bone
= _SkeletonModel
->Bones
[i
];
896 NL3D::CBoneBase
&boneBase
= boneBases
[i
];
898 boneBase
.SkinScale
= bone
.getSkinScale();
899 boneBase
.DefaultScale
= bone
.getScale();
902 // build a new Skeleton shape
903 NL3D::CSkeletonShape
*skelShape
= new NL3D::CSkeletonShape
;
904 skelShape
->build(boneBases
);
907 // save the vegetable
908 NL3D::CShapeStream ss
;
909 ss
.setShapePointer(skelShape
);
913 catch(const NLMISC::EStream
&)
915 MessageBox(_T("Failed to save file!"));
923 // ***************************************************************************
924 void CSkeletonScaleDlg::pushUndoState(const TBoneMirrorArray
&precState
, bool dirtSave
)
926 // **** test if real change
927 nlassert(precState
.size()==_Bones
.size());
929 for(uint i
=0;i
<_Bones
.size();i
++)
931 if( _Bones
[i
].BoneScale
!=precState
[i
].BoneScale
||
932 _Bones
[i
].SkinScale
!=precState
[i
].SkinScale
||
933 _Bones
[i
].Selected
!=precState
[i
].Selected
)
944 // **** then bkup for undo
945 // change => the redo list is lost
948 // if not enough space, the last undo is lost
949 if(_UndoQueue
.size()==MaxUndoRedo
)
950 _UndoQueue
.pop_front();
952 // add the precedent state to the undo queue
953 _UndoQueue
.push_back(precState
);
956 refreshUndoRedoView();
958 // refresh save button
959 if(dirtSave
&& !_SaveDirty
)
966 // ***************************************************************************
967 void CSkeletonScaleDlg::undo()
969 nlassert(_UndoQueue
.size()+_RedoQueue
.size()<=MaxUndoRedo
);
972 if(_UndoQueue
.size())
974 // current goes into the redo queue
975 _RedoQueue
.push_front(_Bones
);
977 _Bones
= _UndoQueue
.back();
979 _UndoQueue
.pop_back();
982 applyMirrorToSkeleton();
984 applySelectionToView();
987 refreshUndoRedoView();
989 // change => must save
995 // ***************************************************************************
996 void CSkeletonScaleDlg::redo()
998 nlassert(_UndoQueue
.size()+_RedoQueue
.size()<=MaxUndoRedo
);
1001 if(_RedoQueue
.size())
1003 // current goes into the undo queue
1004 _UndoQueue
.push_back(_Bones
);
1006 _Bones
= _RedoQueue
.front();
1008 _RedoQueue
.pop_front();
1011 applyMirrorToSkeleton();
1013 applySelectionToView();
1016 refreshUndoRedoView();
1018 // change => must save
1020 refreshSaveButton();
1025 // ***************************************************************************
1026 void CSkeletonScaleDlg::applySelectionToView()
1028 // update list box selection according to state
1029 nlassert(_Bones
.size()==(uint
)_BoneList
.GetCount());
1030 for(uint i
=0;i
<_Bones
.size();i
++)
1032 _BoneList
.SetSel(i
, _Bones
[i
].Selected
);
1037 // ***************************************************************************
1038 void CSkeletonScaleDlg::refreshUndoRedoView()
1041 wnd
= GetDlgItem(IDC_SSD_BUTTON_UNDO
);
1043 wnd
->EnableWindow(!_UndoQueue
.empty());
1044 wnd
= GetDlgItem(IDC_SSD_BUTTON_REDO
);
1046 wnd
->EnableWindow(!_RedoQueue
.empty());
1050 // ***************************************************************************
1051 void CSkeletonScaleDlg::refreshSaveButton()
1053 // SaveAs is always available
1054 CWnd
*wnd
= GetDlgItem(IDC_SSD_BUTTON_SAVE
);
1056 wnd
->EnableWindow(_SaveDirty
);
1060 // ***************************************************************************
1061 sint
CSkeletonScaleDlg::getBoneForMirror(uint boneId
, std::string
&mirrorName
)
1064 std::string::size_type pos
;
1065 nlassert(_SkeletonModel
&& boneId
<_SkeletonModel
->Bones
.size());
1066 mirrorName
= _SkeletonModel
->Bones
[boneId
].getBoneName();
1068 if((pos
= mirrorName
.find(" R "))!=std::string::npos
)
1071 mirrorName
[pos
+1]= 'L';
1073 else if((pos
= mirrorName
.find(" L "))!=std::string::npos
)
1076 mirrorName
[pos
+1]= 'R';
1083 // ***************************************************************************
1084 void CSkeletonScaleDlg::OnSsdButtonMirror()
1087 static TBoneMirrorArray precState
;
1089 nlassert(_SkeletonModel
);
1091 // for each bone selected
1093 for(uint i
=0;i
<_Bones
.size();i
++)
1095 CBoneMirror
&bone
= _Bones
[i
];
1098 // get the bone side and mirrored name
1099 std::string mirrorName
;
1100 sint side
= getBoneForMirror(i
, mirrorName
);
1101 // if not a "centered" bone
1104 // get the bone with mirrored name
1105 sint mirrorId
= _SkeletonModel
->getBoneIdByName(mirrorName
);
1108 nlinfo("MirrorScale: Didn't found %s", mirrorName
.c_str());
1112 // copy setup from the dest bone
1113 nlassert(mirrorId
<(sint
)_Bones
.size());
1114 _Bones
[mirrorId
].BoneScale
= bone
.BoneScale
;
1115 _Bones
[mirrorId
].SkinScale
= bone
.SkinScale
;
1122 applyMirrorToSkeleton();
1125 // if some change, bkup for undo/redo
1126 pushUndoState(precState
, true);
1131 // ***************************************************************************
1132 void CSkeletonScaleDlg::drawSelection()
1137 nlassert(_SkeletonModel
->Bones
.size()==_Bones
.size());
1139 // **** Need recompute Bones bbox?
1140 if(_BoneBBoxNeedRecompute
)
1142 _BoneBBoxNeedRecompute
= false;
1145 for(uint i
=0;i
<_SkeletonModel
->Bones
.size();i
++)
1147 NLMISC::CAABBox boneBBox
;
1150 // for all instances skinned
1151 const std::set
<NL3D::CTransform
*> &skins
= _SkeletonModel
->getSkins();
1152 std::set
<NL3D::CTransform
*>::const_iterator it
;
1153 for(it
=skins
.begin();it
!=skins
.end();it
++)
1155 NL3D::CTransform
*skin
= *it
;
1156 NLMISC::CAABBox skinBoneBBox
;
1157 // if the skin is skinned to this bone
1158 if(skin
->getSkinBoneBBox(skinBoneBBox
, i
))
1160 // set or enlarge the bone bbox
1164 boneBBox
= skinBoneBBox
;
1168 boneBBox
= NLMISC::CAABBox::computeAABBoxUnion(boneBBox
, skinBoneBBox
);
1173 // if there is no skin influence on this bone, still display a tiny bbox, to see the scale
1174 const float defSize
= 0.05f
;
1177 boneBBox
.setCenter(NLMISC::CVector::Null
);
1178 boneBBox
.setSize(NLMISC::CVector(defSize
, defSize
, defSize
));
1182 _BoneBBoxes
[i
]= boneBBox
;
1187 // **** Draw each selected bone
1188 for(uint i
=0;i
<_SkeletonModel
->Bones
.size();i
++)
1190 // if bone not selected, skip
1191 if(!_Bones
[i
].Selected
)
1194 // get the bone "Skin Matrix"
1195 NL3D::CBone
&bone
= _SkeletonModel
->Bones
[i
];
1196 // force compute of this bone
1197 if(!_SkeletonModel
->isBoneComputed(i
))
1198 _SkeletonModel
->forceComputeBone(i
);
1199 NLMISC::CMatrix worldSkinMat
= bone
.getWorldMatrix();
1200 // scale with skin scale, because the localskeleton and world matrix do not have this scale
1201 worldSkinMat
.scale(bone
.getSkinScale());
1203 // Transform the local bbox in its associated matrix
1204 NLMISC::CMatrix matBBox
;
1205 NLMISC::CAABBox bbox
= _BoneBBoxes
[i
];
1206 matBBox
.setPos(bbox
.getCenter());
1208 NLMISC::CVector::I
* bbox
.getSize().x
,
1209 NLMISC::CVector::J
* bbox
.getSize().y
,
1210 NLMISC::CVector::K
* bbox
.getSize().z
);
1211 NLMISC::CMatrix finalMat
;
1212 finalMat
.setMulMatrixNoProj(worldSkinMat
, matBBox
);
1214 //Draw a wired bbox with this bone
1215 NLMISC::CVector corner
= finalMat
.getPos() - finalMat
.getI()/2 - finalMat
.getJ()/2 - finalMat
.getK()/2;
1216 NL3D::IDriver
*driver
= NL3D::CNELU::Driver
;
1217 driver
->setupModelMatrix(NLMISC::CMatrix::Identity
);
1218 NL3D::CDRU::drawWiredBox(corner
, finalMat
.getI(), finalMat
.getJ(), finalMat
.getK(), NLMISC::CRGBA::White
, *driver
);
1223 // ***************************************************************************
1224 void CSkeletonScaleDlg::OnSsdButtonSaveScale()
1226 // if no skeleton edited, quit
1231 std::string defaultFileName
= _SkeletonFileName
;
1232 NLMISC::strFindReplace(defaultFileName
, ".skel", ".scale");
1234 CFileDialog
fd(FALSE
, _T("scale"), nlUtf8ToTStr(defaultFileName
), OFN_OVERWRITEPROMPT
, _T("SkelScaleFiles (*.scale)|*.scale|All Files (*.*)|*.*||"), this);
1235 fd
.m_ofn
.lpstrTitle
= _T("Save As Skeleton Scale File");
1237 if (fd
.DoModal() == IDOK
)
1240 if (f
.open(NLMISC::tStrToUtf8(fd
.GetPathName())))
1242 saveSkelScaleInStream(f
);
1246 MessageBox(_T("Failed to open file for write!"));
1251 // ***************************************************************************
1252 void CSkeletonScaleDlg::OnSsdButtonLoadScale()
1254 // if no skeleton edited, quit
1259 std::string defaultFileName
= _SkeletonFileName
;
1260 NLMISC::strFindReplace(defaultFileName
, ".skel", ".scale");
1262 CFileDialog
fd(TRUE
, _T("scale"), nlUtf8ToTStr(defaultFileName
), 0, _T("SkelScaleFiles (*.scale)|*.scale|All Files (*.*)|*.*||"), this);
1263 fd
.m_ofn
.lpstrTitle
= _T("Load a Skeleton Scale File");
1265 if (fd
.DoModal() == IDOK
)
1268 if (f
.open(NLMISC::tStrToUtf8(fd
.GetPathName())))
1270 loadSkelScaleFromStream(f
);
1274 MessageBox(_T("Failed to open file for read!"));
1280 // ***************************************************************************
1281 struct CBoneScaleInfo
1284 NLMISC::CVector Scale
;
1285 NLMISC::CVector SkinScale
;
1287 void serial(NLMISC::IStream
&f
)
1289 sint32 ver
= f
.serialVersion(0);
1290 f
.serial(Name
, Scale
, SkinScale
);
1294 // ***************************************************************************
1295 bool CSkeletonScaleDlg::saveSkelScaleInStream(NLMISC::IStream
&f
)
1299 nlassert(_SkeletonModel
);
1301 // Copies bone scales from the model
1302 std::vector
<CBoneScaleInfo
> boneScales
;
1303 boneScales
.resize(_SkeletonModel
->Bones
.size());
1304 for(uint i
=0;i
<boneScales
.size();i
++)
1306 NL3D::CBone
&bone
= _SkeletonModel
->Bones
[i
];
1307 CBoneScaleInfo
&boneScale
= boneScales
[i
];
1309 // get scale info from current edited skeleton
1310 boneScale
.Name
= bone
.getBoneName();
1311 boneScale
.Scale
= bone
.getScale();
1312 boneScale
.SkinScale
= bone
.getSkinScale();
1316 sint32 ver
= f
.serialVersion(0);
1317 f
.serialCont(boneScales
);
1319 catch(const NLMISC::EStream
&)
1321 MessageBox(_T("Failed to save file!"));
1328 // ***************************************************************************
1329 bool CSkeletonScaleDlg::loadSkelScaleFromStream(NLMISC::IStream
&f
)
1333 nlassert(_SkeletonModel
);
1336 sint32 ver
= f
.serialVersion(0);
1337 std::vector
<CBoneScaleInfo
> boneScales
;
1338 f
.serialCont(boneScales
);
1340 // apply to the current skeleton
1341 for(uint i
=0;i
<boneScales
.size();i
++)
1343 sint32 boneId
= _SkeletonModel
->getBoneIdByName(boneScales
[i
].Name
);
1344 if(boneId
>=0 && boneId
<(sint32
)_SkeletonModel
->Bones
.size())
1346 CBoneScaleInfo
&boneScale
= boneScales
[i
];
1347 _SkeletonModel
->Bones
[boneId
].setScale(boneScale
.Scale
);
1348 _SkeletonModel
->Bones
[boneId
].setSkinScale(boneScale
.SkinScale
);
1352 // Bkup _Bones, for undo
1353 static TBoneMirrorArray precState
;
1356 // Then reapply to the mirror
1357 applySkeletonToMirror();
1359 // change => must save
1360 pushUndoState(precState
, true);
1362 // and update display
1365 catch(const NLMISC::EStream
&)
1367 MessageBox(_T("Failed to save file!"));
1375 void CSkeletonScaleDlg::OnClose()
1377 _ObjViewer
->getMainFrame()->OnWindowSkeletonScale();