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/>.
21 #include "sound_anim_view.h"
22 #include "sound_anim_dlg.h"
23 #include "object_viewer.h"
25 #include "nel/sound/sound_animation.h"
26 #include "nel/sound/sound_anim_manager.h"
27 #include "nel/sound/sound_anim_marker.h"
30 using namespace NLMISC
;
32 using namespace NLSOUND
;
35 IMPLEMENT_DYNCREATE(CSoundAnimView
, CWnd
)
39 BEGIN_MESSAGE_MAP(CSoundAnimView
, CWnd
)
40 //{{AFX_MSG_MAP(CSoundAnimView)
48 bool CSoundAnimView::_Registered
= false;
49 CString
CSoundAnimView::_WndClass
;
50 uint
CSoundAnimView::_WndId
= 0;
51 const uint
CSoundAnimView::_ZoomCount
= 7;
52 float CSoundAnimView::_ZoomValue
[] = { 0.1f
, 0.25f
, 0.5f
, 1.0f
, 2.0f
, 4.0f
, 10.0f
};
53 const float CSoundAnimView::_Scale
= 200.0f
; // 1 second equals 200 pixels
54 CFont
CSoundAnimView::_Font
;
55 CBrush
CSoundAnimView::_FillBrush
;
56 CBrush
CSoundAnimView::_MarkerBrush
;
57 CBrush
CSoundAnimView::_SelectBrush
;
58 CPen
CSoundAnimView::_RedPen
;
61 // ***************************************************************************
63 bool CSoundAnimView::registerClass()
70 _WndClass
= AfxRegisterWndClass(CS_VREDRAW
| CS_HREDRAW
, ::LoadCursor(NULL
, IDC_ARROW
), (HBRUSH
) ::GetStockObject(WHITE_BRUSH
));
72 // Do some additional initialization of static veriables
73 _Font
.CreateFont(14, 0, 0, 0, FW_NORMAL
, FALSE
, FALSE
, 0, ANSI_CHARSET
, OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
, DEFAULT_QUALITY
, DEFAULT_PITCH
| FF_SWISS
, _T("Arial"));
74 _FillBrush
.CreateSolidBrush(RGB(230, 245, 245));
75 _MarkerBrush
.CreateSolidBrush(RGB(0, 0, 0));
76 _SelectBrush
.CreateSolidBrush(RGB(0, 210, 210));
77 _RedPen
.CreatePen(PS_SOLID
, 1, RGB(255, 0, 0));
85 // ***************************************************************************
87 void CSoundAnimView::Create(CObjectViewer
* objView
, CAnimationDlg
* animDlg
, CSoundAnimDlg
* sndDlg
, const RECT
& rect
)
92 _AnimationDlg
= animDlg
;
93 _SoundAnimDlg
= sndDlg
;
101 _PixelsViewH
= rect
.right
- rect
.left
;
102 _PixelsViewV
= rect
.bottom
- rect
.top
;
107 // Find the zoom index dynamically so we can change the _ZoomValue array
108 // withou risk of using a bad _ZoomIndex
109 for (uint i
= 0; i
< _ZoomCount
; i
++)
111 if (_ZoomValue
[i
] == 1.0f
)
119 CWnd::Create((LPCTSTR
) _WndClass
, _T("Sound Animation"), WS_CHILD
| WS_VISIBLE
, rect
, (CWnd
*) sndDlg
, ++_WndId
);
122 // ********************************************************
124 void CSoundAnimView::changeTimeScale()
126 _PixelsTotal
= timeToPixel(_TimeEnd
- _TimeStart
);
127 _PixelsOffset
= timeToPixel(_TimeOffset
- _TimeStart
);
130 if (_PixelsTotal
< _PixelsViewH
)
133 _TimeOffset
= _TimeStart
;
134 _SoundAnimDlg
->updateScroll(0, 0, 0);
136 else if (_PixelsOffset
+ _PixelsViewH
> _PixelsTotal
)
138 _PixelsOffset
= _PixelsTotal
- _PixelsViewH
;
139 _TimeOffset
= _TimeStart
+ pixelToTime(_PixelsOffset
);
140 _SoundAnimDlg
->updateScroll(_PixelsOffset
, 0, _PixelsTotal
- _PixelsViewH
);
144 _SoundAnimDlg
->updateScroll(_PixelsOffset
, 0, _PixelsTotal
- _PixelsViewH
);
150 // ********************************************************
152 void CSoundAnimView::changeScroll(uint curpos
)
154 _PixelsOffset
= curpos
;
155 _TimeOffset
= _TimeStart
+ pixelToTime(_PixelsOffset
);
159 // ********************************************************
161 void CSoundAnimView::zoomIn()
163 if (_ZoomIndex
< _ZoomCount
- 1)
166 _Zoom
= _ZoomValue
[_ZoomIndex
];
172 // ********************************************************
174 void CSoundAnimView::zoomOut()
179 _Zoom
= _ZoomValue
[_ZoomIndex
];
185 // ********************************************************
187 void CSoundAnimView::setAnimTime(float animStart
, float animEnd
)
189 _TimeStart
= animStart
/ _AnimationDlg
->getSpeed();
190 _TimeEnd
= animEnd
/ _AnimationDlg
->getSpeed();
195 // ********************************************************
197 void CSoundAnimView::mark()
199 insertMarkerAt(_AnimationDlg
->getTime());
202 // ********************************************************
204 void CSoundAnimView::save()
206 CAnimationVector::iterator iter
;
207 for (iter
= _Animations
.begin(); iter
!= _Animations
.end(); iter
++)
209 CSoundAnimationHolder
&h
= *iter
;
210 CSoundAnimation
* anim
= h
._Anim
;
214 string filename
= anim
->getFilename();
216 // Ask user for filename
217 if (filename
.empty())
219 filename
.append(anim
->getName()).append(".sound_anim");
222 TCHAR BASED_CODE szFilter
[] = _T("NeL Sound Animations (*.sound_anim)|*.sound_anim|All Files (*.*)|*.*||");
223 CFileDialog
fileDlg(FALSE
, _T(".sound_anim"), nlUtf8ToTStr(filename
), OFN_HIDEREADONLY
| OFN_OVERWRITEPROMPT
, szFilter
);
225 if (fileDlg
.DoModal() == IDOK
)
227 filename
= tStrToUtf8(fileDlg
.GetPathName());
238 CSoundAnimManager::instance()->saveAnimation(anim
, filename
);
240 catch (const Exception
& e
)
242 MessageBox(nlUtf8ToTStr(e
.what()), _T("NeL object viewer"), MB_OK
| MB_ICONEXCLAMATION
);
251 // ********************************************************
253 void CSoundAnimView::deleteMarker()
255 if (_SelectedMarker
!= 0)
257 _SelectedAnim
._Anim
->removeMarker(_SelectedMarker
);
258 _SelectedAnim
._Anim
->setDirty(true);
263 // ********************************************************
265 bool CSoundAnimView::getAnimationAt(CSoundAnimationHolder
& holder
, float time
)
267 CAnimationVector::iterator iter
;
268 for (iter
= _Animations
.begin(); iter
!= _Animations
.end(); iter
++)
270 CSoundAnimationHolder
&h
= *iter
;
271 if ((h
._AnimStart
<= time
) && (time
< h
._AnimEnd
))
281 // ********************************************************
283 void CSoundAnimView::refresh(BOOL update
)
287 uint selected
= _ObjView
->getEditedObject();
288 if (selected
== 0xffffffff)
293 CSoundAnimManager
* animManager
= CSoundAnimManager::instance();
295 // Make sure the sound anim manager is already instanciated
296 if (animManager
== 0)
301 CInstanceInfo
*instanceInfo
= _ObjView
->getInstance(selected
);
303 // Some animation in the list ?
304 if (!instanceInfo
->Saved
.PlayList
.empty())
310 // Get start time of the animation that starts before the current time
311 for (uint index
= 0; index
< instanceInfo
->Saved
.PlayList
.size(); index
++)
313 // Pointer on the animation
314 string
& name
= instanceInfo
->Saved
.PlayList
[index
];
315 CAnimation
*anim
= instanceInfo
->AnimationSet
.getAnimation (instanceInfo
->AnimationSet
.getAnimationIdByName(name
));
319 endTime
= startTime
+ anim
->getEndTime() - anim
->getBeginTime();
321 CSoundAnimation
* soundAnim
= animManager
->findAnimation(name
);
325 bool needCreate
= false;
328 TSoundAnimId res
= animManager
->loadAnimation(name
);
329 if(res
== CSoundAnimationNoId
)
332 soundAnim
= animManager
->findAnimation(name
);
334 catch (const exception
& e
)
336 nlwarning("Couldn't find sound animation <%s>: %s", name
.c_str(), e
.what());
341 animManager
->createAnimation(name
);
342 soundAnim
= animManager
->findAnimation(name
);
346 CSoundAnimationHolder
holder(soundAnim
, startTime
, endTime
);
348 _Animations
.push_back(holder
);
355 // ********************************************************
357 void CSoundAnimView::updateCursor()
359 sint cursor
= timeToPixel(_AnimationDlg
->getTime());
361 if (cursor
!= _Cursor
)
365 r
.left
= (cursor
< _Cursor
)? cursor
: _Cursor
;
366 r
.right
= (cursor
< _Cursor
)? _Cursor
+ 1 : cursor
+ 1;
368 r
.bottom
= _PixelsViewV
;
370 // correct for offset
371 r
.left
-= _PixelsOffset
;
372 r
.right
-= _PixelsOffset
;
380 // ********************************************************
382 CSoundAnimMarker
* CSoundAnimView::getMarkerAt(CPoint point
)
384 CSoundAnimationHolder holder
;
387 //nldebug("TIME=%f", pixelToTime(_PixelsOffset + point.x));
389 if (getAnimationAt(holder
, pixelToTime(_PixelsOffset
+ point
.x
)))
391 CSoundAnimation
* anim
= holder
._Anim
;
392 float offset
= holder
._AnimStart
;
393 uint32 nmarkers
= anim
->countMarkers();
395 for (uint32 i
= 0; i
< nmarkers
; i
++)
397 CSoundAnimMarker
* marker
= anim
->getMarker(i
);
398 uint pixel
= timeToPixel(offset
+ marker
->getTime()) - _PixelsOffset
;
404 r
.bottom
= _PixelsViewV
;
406 if (r
.PtInRect(point
))
415 // ********************************************************
417 void CSoundAnimView::insertMarkerAt(float time
)
419 CSoundAnimationHolder holder
;
421 if (getAnimationAt(holder
, time
))
423 CSoundAnimation
* anim
= holder
._Anim
;
425 CSoundAnimMarker
* marker
= new CSoundAnimMarker((float)(time
- holder
._AnimStart
));
426 anim
->addMarker(marker
);
427 anim
->setDirty(true);
429 _SelectedMarker
= marker
;
430 _SelectedAnim
= holder
;
431 _SoundAnimDlg
->selectMarker(marker
);
437 // ***************************************************************************
439 void CSoundAnimView::OnLButtonDown(UINT nFlags
, CPoint point
)
441 CSoundAnimMarker
* marker
= getMarkerAt(point
);
445 float time
= pixelToTime(_PixelsOffset
+ point
.x
);
447 _DragStartPoint
= point
;
448 _DragStartTime
= marker
->getTime();
449 _SelectedMarker
= marker
;
450 getAnimationAt(_SelectedAnim
, time
);
459 // ***************************************************************************
461 void CSoundAnimView::OnLButtonUp(UINT nFlags
, CPoint point
)
463 float time
= pixelToTime(point
.x
);
465 if (nFlags
== MK_CONTROL
)
467 insertMarkerAt(time
);
469 else if (nFlags
== 0)
471 _SoundAnimDlg
->selectMarker(_SelectedMarker
);
481 // ********************************************************
483 void CSoundAnimView::OnLButtonDblClk(UINT nFlags
, CPoint point
)
485 float time
= pixelToTime(point
.x
);
486 CSoundAnimationHolder holder
;
488 if (getAnimationAt(holder
, time
))
490 nlwarning("CSoundAnimView::OnLButtonDblClk: x=%d, t=%.3f", point
.x
, time
);
494 // ***************************************************************************
496 void CSoundAnimView::OnMouseMove(UINT nFlags
, CPoint point
)
498 if (_Dragging
&& (_SelectedMarker
!= 0) && (point
.x
!= _DragStartPoint
.x
))
500 sint32 deltaPx
= point
.x
- _DragStartPoint
.x
;
501 float newTime
= _DragStartTime
+ pixelToTime(point
.x
- _DragStartPoint
.x
);
502 clamp(newTime
, 0.0f
, _SelectedAnim
._AnimEnd
- _SelectedAnim
._AnimStart
);
503 _SelectedMarker
->setTime(newTime
);
504 _SelectedAnim
._Anim
->setDirty(true);
509 // ***************************************************************************
511 void CSoundAnimView::OnPaint()
514 CDC
* dc
= BeginPaint(&ps
);
520 // Shift the origin according to the scroll offset
521 CPoint p
= dc
->GetViewportOrg();
522 p
.Offset(- (sint
) _PixelsOffset
, 0);
523 dc
->SetViewportOrg(p
);
526 CAnimationVector::iterator iter
;
529 dc
->MoveTo(0, _PixelsViewV
- 15);
530 dc
->LineTo(_PixelsTotal
, _PixelsViewV
- 15);
532 for (iter
= _Animations
.begin(); iter
!= _Animations
.end(); iter
++)
534 CSoundAnimationHolder
&holder
= *iter
;
536 if (holder
._Anim
== NULL
) continue;
538 dc
->MoveTo(timeToPixel(holder
._AnimStart
), 0);
539 dc
->LineTo(timeToPixel(holder
._AnimStart
), _PixelsViewV
);
541 r
.left
= timeToPixel(holder
._AnimStart
) + 3;
542 r
.right
= timeToPixel(holder
._AnimEnd
) - 3;
546 CGdiObject
* oldFont
= (CGdiObject
*) dc
->SelectObject(&_Font
);
547 _StringBuffer
.erase();
548 _StringBuffer
.append(holder
._Anim
->getName());
549 if (holder
._Anim
->isDirty())
551 _StringBuffer
.append("*");
553 dc
->DrawText(_StringBuffer
.c_str(), &r
, DT_VCENTER
| DT_LEFT
| DT_SINGLELINE
);
554 dc
->SelectObject(oldFont
);
556 lastPixel
= timeToPixel(holder
._AnimEnd
);
558 // Draw the markers of the animation
559 CSoundAnimation
* anim
= holder
._Anim
;
560 float offset
= holder
._AnimStart
;
561 uint32 nmarkers
= anim
->countMarkers();
562 for (uint32 i
= 0; i
< nmarkers
; i
++)
564 CSoundAnimMarker
* marker
= anim
->getMarker(i
);
565 sint pixel
= timeToPixel(offset
+ marker
->getTime());
569 r
.top
= _PixelsViewV
- 20 ;
570 r
.bottom
= _PixelsViewV
- 10;
572 dc
->FillRect(&r
, (_SelectedMarker
== marker
)? &_SelectBrush
: &_MarkerBrush
);
576 dc
->MoveTo(lastPixel
, 0);
577 dc
->LineTo(lastPixel
, _PixelsViewV
);
580 if (lastPixel
< (sint
) _PixelsViewH
)
582 r
.left
= lastPixel
+ 1;
583 r
.right
= _PixelsViewH
- 1;
585 r
.bottom
= _PixelsViewV
- 1;
586 dc
->FillRect(&r
, &_FillBrush
);
589 CPen
* oldPen
= (CPen
*) dc
->SelectObject(&_RedPen
);
590 dc
->MoveTo(_Cursor
, 0);
591 dc
->LineTo(_Cursor
, _PixelsViewV
);
592 dc
->SelectObject(oldPen
);
600 // ***************************************************************************
602 void CSoundAnimView::AssertValid() const
607 // ***************************************************************************
609 void CSoundAnimView::Dump(CDumpContext
& dc
) const