311 lines
7.9 KiB
C++
311 lines
7.9 KiB
C++
|
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||
|
//
|
||
|
// This program is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU Affero General Public License as
|
||
|
// published by the Free Software Foundation, either version 3 of the
|
||
|
// License, or (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU Affero General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU Affero General Public License
|
||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
#include "listener_view.h"
|
||
|
#include "sound_document_plugin.h"
|
||
|
#include "nel/misc/debug.h"
|
||
|
#include "nel/misc/path.h"
|
||
|
#include "nel/misc/vector.h"
|
||
|
#include <mmsystem.h>
|
||
|
#include <math.h>
|
||
|
|
||
|
using namespace std;
|
||
|
using namespace NLMISC;
|
||
|
|
||
|
namespace NLGEORGES
|
||
|
{
|
||
|
|
||
|
|
||
|
#undef new
|
||
|
IMPLEMENT_DYNCREATE(CListenerView, CView)
|
||
|
#define new NL_NEW
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CListenerView, CView)
|
||
|
//{{AFX_MSG_MAP(CListenerView)
|
||
|
ON_WM_LBUTTONDOWN()
|
||
|
ON_WM_LBUTTONUP()
|
||
|
ON_WM_MOUSEMOVE()
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
bool CListenerView::_Registered = false;
|
||
|
CString CListenerView::_WndClass;
|
||
|
uint CListenerView::_WndId = 0;
|
||
|
CFont CListenerView::_Font;
|
||
|
CBrush CListenerView::_InnerBrush;
|
||
|
CBrush CListenerView::_OuterBrush;
|
||
|
CBrush CListenerView::_ListenerBrush;
|
||
|
CPen CListenerView::_VolumeCurve;
|
||
|
|
||
|
|
||
|
|
||
|
// ***************************************************************************
|
||
|
|
||
|
bool CListenerView::registerClass()
|
||
|
{
|
||
|
if (_Registered)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
try
|
||
|
{
|
||
|
LPCTSTR className = AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW, ::LoadCursor(NULL, IDC_ARROW), (HBRUSH) ::GetStockObject(WHITE_BRUSH));
|
||
|
_WndClass = className;
|
||
|
}
|
||
|
catch (CResourceException* e)
|
||
|
{
|
||
|
AfxMessageBox("Couldn't register class! (Already registered?)");
|
||
|
e->Delete();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
_OuterBrush.CreateSolidBrush(RGB(255, 0, 0));
|
||
|
_InnerBrush.CreateSolidBrush(RGB(0, 255, 0));
|
||
|
_ListenerBrush.CreateSolidBrush(RGB(0, 0, 0));
|
||
|
_VolumeCurve.CreatePen(PS_SOLID, 1, RGB(255, 0, 128));
|
||
|
|
||
|
_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, "Arial");
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
// ***************************************************************************
|
||
|
|
||
|
void CListenerView::init(CSoundPlugin* plugin, CRect& rect, CWnd* parent)
|
||
|
{
|
||
|
|
||
|
registerClass();
|
||
|
|
||
|
_Plugin = plugin;
|
||
|
_Listener.SetRect(-1, -1, -1, -1);
|
||
|
_Cones.SetRect(0, 0, 0, 0);
|
||
|
_OuterAngle = 360;
|
||
|
_InnerAngle = 360;
|
||
|
|
||
|
if (!Create((LPCTSTR) _WndClass, "Listener", WS_CHILD | WS_VISIBLE, rect, parent, ++_WndId))
|
||
|
{
|
||
|
throw exception("failed to create the listener view");
|
||
|
}
|
||
|
|
||
|
EnableWindow ();
|
||
|
ShowWindow(SW_SHOW);
|
||
|
}
|
||
|
|
||
|
// ***************************************************************************
|
||
|
|
||
|
void CListenerView::OnLButtonDown(UINT nFlags, CPoint point)
|
||
|
{
|
||
|
if (_Listener.PtInRect(point))
|
||
|
{
|
||
|
_DragOrigine = point;
|
||
|
_ListenerOrigine = _Listener.CenterPoint();
|
||
|
_Dragging = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ***************************************************************************
|
||
|
|
||
|
void CListenerView::OnLButtonUp(UINT nFlags, CPoint point)
|
||
|
{
|
||
|
if (_Dragging)
|
||
|
{
|
||
|
_Dragging = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ***************************************************************************
|
||
|
|
||
|
void CListenerView::OnMouseMove(UINT nFlags, CPoint point)
|
||
|
{
|
||
|
if (_Dragging)
|
||
|
{
|
||
|
CVector pos;
|
||
|
CPoint center = _ListenerOrigine + point - _DragOrigine;
|
||
|
CRect updateRect = _Listener;
|
||
|
_Listener.SetRect(center.x - 5, center.y - 5, center.x + 5, center.y + 5);
|
||
|
//updateRect |= _Listener;
|
||
|
//InvalidateRect(&updateRect);
|
||
|
Invalidate();
|
||
|
|
||
|
center = _Cones.CenterPoint();
|
||
|
CPoint listener = _Listener.CenterPoint();
|
||
|
|
||
|
CRect rect;
|
||
|
GetClientRect(&rect);
|
||
|
uint fullHeight = rect.bottom - center.y;
|
||
|
uint fullWidth = rect.right - center.x;
|
||
|
|
||
|
_X = _MaxDist * (listener.x - center.x) / fullHeight;
|
||
|
_Y = (_MaxDist * (listener.y - center.y) / fullHeight);
|
||
|
|
||
|
pos.set(_X, _Y, 0.0f);
|
||
|
|
||
|
//nldebug("position=(%.2f, %.2f)", _X, _Y);
|
||
|
|
||
|
_Plugin->setListenerPos(pos);
|
||
|
_Plugin->setListenerOrientation(CVector(0,1,0), CVector(0,0,1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ***************************************************************************
|
||
|
|
||
|
void CListenerView::OnDraw(CDC* dc)
|
||
|
{
|
||
|
CRect rect;
|
||
|
|
||
|
GetClientRect(&rect);
|
||
|
|
||
|
dc->Rectangle(rect);
|
||
|
|
||
|
// Initialize the position of the rectangle on the first call to draw
|
||
|
if (_Listener.top < 0)
|
||
|
{
|
||
|
CPoint c = rect.CenterPoint();
|
||
|
_Listener.SetRect(c.x - 5, rect.top + 25, c.x + 5, rect.top + 35);
|
||
|
_Cones.SetRect(c.x - 20, rect.top + 10, c.x + 20, rect.top + 50);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (_Activated)
|
||
|
{
|
||
|
CPoint center = _Cones.CenterPoint();
|
||
|
|
||
|
CBrush *oldBrush = dc->SelectObject(&_OuterBrush);
|
||
|
|
||
|
double angle = NLMISC::Pi * (360.0 - _OuterAngle) / 2.0 / 180.0;
|
||
|
dc->Pie(_Cones,
|
||
|
CPoint((int) (center.x - 10.0 * sin(angle)), (int) (center.y - 10.0 * cos(angle))),
|
||
|
CPoint((int) (center.x + 10.0 * sin(angle)), (int) (center.y - 10.0 * cos(angle))));
|
||
|
|
||
|
dc->SelectObject(&_InnerBrush);
|
||
|
|
||
|
angle = NLMISC::Pi * (360.0 - _InnerAngle) / 2.0 / 180.0;
|
||
|
dc->Pie(_Cones,
|
||
|
CPoint((int) (center.x - 15.0 * sin(angle)), (int) (center.y - 15.0 * cos(angle))),
|
||
|
CPoint((int) (center.x + 15.0 * sin(angle)), (int) (center.y - 15.0 * cos(angle))));
|
||
|
|
||
|
dc->SelectObject(&_ListenerBrush);
|
||
|
//dc->Ellipse(_Listener);
|
||
|
|
||
|
CPoint pts[4];
|
||
|
pts[0].x = _Listener.left;
|
||
|
pts[0].y = _Listener.bottom;
|
||
|
|
||
|
pts[1].x = _Listener.right;
|
||
|
pts[1].y = _Listener.bottom;
|
||
|
|
||
|
pts[2].x = (_Listener.left + _Listener.right) / 2;
|
||
|
pts[2].y = _Listener.top;
|
||
|
|
||
|
pts[3].x = _Listener.left;
|
||
|
pts[3].y = _Listener.bottom;
|
||
|
|
||
|
|
||
|
dc->Polygon(pts, 4);
|
||
|
|
||
|
dc->SelectObject(oldBrush);
|
||
|
|
||
|
char s[256];
|
||
|
_snprintf(s, 256, "%.1f, %.1f", _X, _Y);
|
||
|
|
||
|
CFont* oldFont = dc->SelectObject(&_Font);
|
||
|
dc->DrawText(s, rect, DT_VCENTER | DT_SINGLELINE | DT_CENTER);
|
||
|
dc->SelectObject(oldFont);
|
||
|
|
||
|
CPen* oldPen = dc->SelectObject(&_VolumeCurve);
|
||
|
|
||
|
// Draw the volume curve
|
||
|
CPoint c = _Cones.CenterPoint();
|
||
|
uint fullHeight = rect.bottom - c.y;
|
||
|
uint fullWidth = rect.right - c.x;
|
||
|
|
||
|
// Draw the min distance
|
||
|
uint minDistHeight = (uint)(((float) fullHeight * _MinDist) / _MaxDist);
|
||
|
|
||
|
CPoint p = c;
|
||
|
dc->MoveTo(p);
|
||
|
p.y += minDistHeight;
|
||
|
dc->LineTo(p);
|
||
|
|
||
|
|
||
|
if (_ShowAlpha)
|
||
|
{
|
||
|
// calculate the curve in 10 intervals
|
||
|
double dx = (_MaxDist - _MinDist) / 100.0;
|
||
|
double x = _MinDist;
|
||
|
|
||
|
for (uint i = 0; i < 101; i++)
|
||
|
{
|
||
|
double y;
|
||
|
|
||
|
// linearly descending volume on a dB scale
|
||
|
double db1 = -100.0 * (x - _MinDist) / (_MaxDist - _MinDist);
|
||
|
|
||
|
if (_Alpha == 0.0) {
|
||
|
y = db1;
|
||
|
|
||
|
} else if (_Alpha > 0.0) {
|
||
|
double amp2 = 0.0001 + 0.9999 * (_MaxDist - x) / (_MaxDist - _MinDist); // linear amp between 0.00001 and 1.0
|
||
|
double db2 = 20. * log10(amp2);
|
||
|
y = ((1.0 - _Alpha) * db1 + _Alpha * db2);
|
||
|
|
||
|
} else if (_Alpha < 0.0) {
|
||
|
double amp3 = _MinDist / x; // linear amplitude is 1/distance
|
||
|
double db3 = 20.0 * log10(amp3);
|
||
|
y = ((1.0 + _Alpha) * db1 - _Alpha * db3);
|
||
|
}
|
||
|
|
||
|
clamp(y, -100, 0);
|
||
|
|
||
|
p.x = c.x + (uint)(fullWidth * (y / -100.0));
|
||
|
p.y = c.y + (uint)(fullHeight * (x / _MaxDist));
|
||
|
dc->LineTo(p);
|
||
|
|
||
|
x += dx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dc->SelectObject(oldPen);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
|
||
|
// ***************************************************************************
|
||
|
|
||
|
void CListenerView::AssertValid() const
|
||
|
{
|
||
|
CView::AssertValid();
|
||
|
}
|
||
|
|
||
|
// ***************************************************************************
|
||
|
|
||
|
void CListenerView::Dump(CDumpContext& dc) const
|
||
|
{
|
||
|
CView::Dump(dc);
|
||
|
}
|
||
|
|
||
|
#endif //_DEBUG
|
||
|
|
||
|
|
||
|
} // namespace NLGEORGES
|
||
|
|