339 lines
8.3 KiB
C++
339 lines
8.3 KiB
C++
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||
|
//
|
||
|
// This program is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU Affero General Public License as
|
||
|
// published by the Free Software Foundation, either version 3 of the
|
||
|
// License, or (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU Affero General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU Affero General Public License
|
||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
#include "std_afx.h"
|
||
|
#include "object_viewer.h"
|
||
|
#include "basis_edit.h"
|
||
|
#include "nel/misc/matrix.h"
|
||
|
#include "nel/misc/vector.h"
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CBasisEdit dialog
|
||
|
|
||
|
|
||
|
CBasisEdit::CBasisEdit(CWnd* pParent /*=NULL*/)
|
||
|
{
|
||
|
//{{AFX_DATA_INIT(CBasisEdit)
|
||
|
m_Psi = 0;
|
||
|
m_Theta = 0;
|
||
|
m_Phi = 0;
|
||
|
//}}AFX_DATA_INIT
|
||
|
}
|
||
|
|
||
|
|
||
|
void CBasisEdit::DoDataExchange(CDataExchange* pDX)
|
||
|
{
|
||
|
CDialog::DoDataExchange(pDX);
|
||
|
//{{AFX_DATA_MAP(CBasisEdit)
|
||
|
DDX_Control(pDX, IDC_PHI_SCROLLBAR, m_PhiCtrl);
|
||
|
DDX_Control(pDX, IDC_PSI_SCROLLBAR, m_PsiCtrl);
|
||
|
DDX_Control(pDX, IDC_THETA_SCROLLBAR, m_ThetaCtrl);
|
||
|
DDX_Text(pDX, IDC_PSI_VALUE, m_Psi);
|
||
|
DDX_Text(pDX, IDC_THETA_VALUE, m_Theta);
|
||
|
DDX_Text(pDX, IDC_PHI_VALUE, m_Phi);
|
||
|
//}}AFX_DATA_MAP
|
||
|
}
|
||
|
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CBasisEdit, CDialog)
|
||
|
//{{AFX_MSG_MAP(CBasisEdit)
|
||
|
ON_WM_PAINT()
|
||
|
ON_WM_HSCROLL()
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CBasisEdit message handlers
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void CBasisEdit::init(uint32 x, uint32 y, CWnd *pParent)
|
||
|
{
|
||
|
nlassert(_Wrapper); // _Wrapper should have been set before init !!
|
||
|
Create(IDD_BASIS_EDIT, pParent);
|
||
|
RECT r;
|
||
|
GetClientRect(&r);
|
||
|
|
||
|
m_PsiCtrl.SetScrollRange(0, 359);
|
||
|
m_ThetaCtrl.SetScrollRange(0, 359);
|
||
|
m_PhiCtrl.SetScrollRange(0, 359);
|
||
|
|
||
|
MoveWindow(x, y, r.right, r.bottom);
|
||
|
updateAnglesFromReader();
|
||
|
ShowWindow(SW_SHOW);
|
||
|
UpdateData(FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// build an euler matrix
|
||
|
NLMISC::CMatrix BuildEulerMatrix(float psi, float theta, float phi)
|
||
|
{
|
||
|
float ca = cosf(psi), sa = sinf(psi)
|
||
|
, cb = cosf(theta), sb = sinf(theta)
|
||
|
, cc = cosf(phi), sc = sinf(phi);
|
||
|
NLMISC::CMatrix m;
|
||
|
m.identity();
|
||
|
m.setRot(NLMISC::CVector(ca * cb * cc - sa * sc, -cc * sa - ca * cb *sc, ca * sb)
|
||
|
,NLMISC::CVector(cb * cc * sa + ca * sc, ca * cc - cb * sa * sc, sa *sb)
|
||
|
,NLMISC::CVector(-cc * sb, sb * sc, cb)
|
||
|
);
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
// get back the euler angles from a matrix
|
||
|
NLMISC::CVector GetEulerAngles(const NLMISC::CMatrix &mat)
|
||
|
{
|
||
|
float m[3][3];
|
||
|
// we got cos theta = m33
|
||
|
NLMISC::CVector v[3];
|
||
|
mat.getRot(v[0], v[1], v[2]);
|
||
|
for (uint l = 0; l < 3; ++l)
|
||
|
{
|
||
|
m[0][l] = v[l].x; m[1][l] = v[l].y; m[2][l] = v[l].z;
|
||
|
}
|
||
|
|
||
|
// there are eight triplet that may satisfy the equation
|
||
|
// we compute them all, and test them against the matrix
|
||
|
|
||
|
float b0, b1, a0, a1, a2, a3, c0, c1, c2, c3;
|
||
|
b0 = acosf(m[2][2]);
|
||
|
b1 = (float) NLMISC::Pi - b0;
|
||
|
float sb0 = sinf(b0), sb1 = sinf(b1);
|
||
|
if (fabsf(sb0) > 10E-6)
|
||
|
{
|
||
|
a0 = m[2][0] / sb0;
|
||
|
c0 = m[1][2] / sb0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
a0 = c0 = 1.f;
|
||
|
}
|
||
|
if (fabs(sb1) > 10E-6)
|
||
|
{
|
||
|
a1 = m[2][0] / sb1;
|
||
|
c1 = m[1][2] / sb1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
a1 = c1 = 1.f;
|
||
|
}
|
||
|
|
||
|
|
||
|
a2 = (float) NLMISC::Pi - a0;
|
||
|
a3 = (float) NLMISC::Pi - a1;
|
||
|
|
||
|
|
||
|
c2 = (float) NLMISC::Pi - c0;
|
||
|
c3 = (float) NLMISC::Pi - c1;
|
||
|
|
||
|
NLMISC::CVector sol[] =
|
||
|
{
|
||
|
NLMISC::CVector(b0, a0, c0)
|
||
|
,NLMISC::CVector(b0, a2, c0)
|
||
|
,NLMISC::CVector(b0, a0, c2)
|
||
|
,NLMISC::CVector(b0, a2, c2)
|
||
|
,NLMISC::CVector(b1, a1, c1)
|
||
|
,NLMISC::CVector(b1, a3, c1)
|
||
|
,NLMISC::CVector(b1, a1, c3)
|
||
|
,NLMISC::CVector(b1, a3, c3)
|
||
|
};
|
||
|
|
||
|
// now we take the triplet that fit best the 6 other equations
|
||
|
|
||
|
float bestGap = 0.f;
|
||
|
uint bestIndex;
|
||
|
|
||
|
for (uint k = 0; k < 8; ++k)
|
||
|
{
|
||
|
float ca = cosf(sol[k].x), sa = sinf(sol[k].x)
|
||
|
, cb = cosf(sol[k].y), sb = sinf(sol[k].y)
|
||
|
, cc = cosf(sol[k].z), sc = sinf(sol[k].z);
|
||
|
|
||
|
float gap = fabsf(m[0][0] - ca * cb * cc + sa * sc);
|
||
|
gap += fabsf(m[1][0] + cc * sa + ca * cb *sc);
|
||
|
gap += fabsf(m[0][1] - cb * cc * sa - ca * sc);
|
||
|
gap += fabsf(m[0][1] - cb * cc * sa - ca * sc);
|
||
|
gap += fabsf(m[1][1] - ca * cc + cb * sa * sc);
|
||
|
gap += fabsf(m[2][1] - sb *ca);
|
||
|
gap += fabsf(m[0][2] + cc * sb);
|
||
|
|
||
|
if (k == 0 || gap < bestGap)
|
||
|
{
|
||
|
bestGap = gap;
|
||
|
bestIndex = k;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
return sol[bestIndex];
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void CBasisEdit::updateAnglesFromReader()
|
||
|
{
|
||
|
nlassert(_Wrapper); // _Wrapper should have been set before init !!
|
||
|
|
||
|
// read plane basis
|
||
|
NL3D::CPlaneBasis pb = _Wrapper->get();
|
||
|
NLMISC::CMatrix mat;
|
||
|
mat.setRot(pb.X, pb.Y, pb.X ^ pb.Y);
|
||
|
NLMISC::CVector angles = GetEulerAngles(mat);
|
||
|
m_PsiCtrl.SetScrollPos((uint) (360.f * angles.x / (2.f * (float) NLMISC::Pi)));
|
||
|
m_ThetaCtrl.SetScrollPos((uint) (360.f * angles.y / (2.f * (float) NLMISC::Pi)));
|
||
|
m_PhiCtrl.SetScrollPos((uint) (360.f * angles.z / (2.f * (float) NLMISC::Pi)));
|
||
|
UpdateData(FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// just project a vector with orthogonal proj
|
||
|
static CPoint BasisVectXForm(NLMISC::CVector &v, float size)
|
||
|
{
|
||
|
const float sq = sqrtf(2.f) / 2.f;
|
||
|
return CPoint((int) (size * (sq * (v.x + v.y) )), (int) (size * (-v.z + sq * (v.x - v.y))));
|
||
|
}
|
||
|
|
||
|
// draw a basis using the given colors, and hte given dc
|
||
|
|
||
|
void DrawBasisInDC(const CPoint ¢er, float size, const NLMISC::CMatrix &m, CDC &dc, NLMISC::CRGBA col[3])
|
||
|
{
|
||
|
// draw the basis
|
||
|
CPoint px = center + BasisVectXForm(m.getI(), size);
|
||
|
CPoint py = center + BasisVectXForm(m.getJ(), size);
|
||
|
CPoint pz = center + BasisVectXForm(m.getK(), size);
|
||
|
|
||
|
|
||
|
CPen p[3];
|
||
|
p[0].CreatePen(PS_SOLID, 1, col[0].R + (col[0].G << 8) + (col[0].B << 16) );
|
||
|
p[1].CreatePen(PS_SOLID, 1, col[1].R + (col[1].G << 8) + (col[1].B << 16) );
|
||
|
p[2].CreatePen(PS_SOLID, 1, col[2].R + (col[2].G << 8) + (col[2].B << 16) );
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// draw letters indicating each axis
|
||
|
// X
|
||
|
CPen *old = dc.SelectObject(&p[0]);
|
||
|
dc.MoveTo(center);
|
||
|
dc.LineTo(px);
|
||
|
dc.MoveTo(px + CPoint(2, 2));
|
||
|
dc.LineTo(px + CPoint(5, 5));
|
||
|
dc.MoveTo(px + CPoint(4, 2));
|
||
|
dc.LineTo(px + CPoint(3, 5));
|
||
|
|
||
|
|
||
|
// Y
|
||
|
dc.SelectObject(&p[1]);
|
||
|
dc.MoveTo(center);
|
||
|
dc.LineTo(py);
|
||
|
|
||
|
dc.MoveTo(py + CPoint(2, 2));
|
||
|
dc.LineTo(py + CPoint(4, 4));
|
||
|
dc.MoveTo(py + CPoint(4, 2));
|
||
|
dc.LineTo(py + CPoint(1, 5));
|
||
|
|
||
|
// Z
|
||
|
dc.SelectObject(&p[2]);
|
||
|
dc.MoveTo(center);
|
||
|
dc.LineTo(pz);
|
||
|
|
||
|
dc.MoveTo(pz + CPoint(2, 2));
|
||
|
dc.LineTo(pz + CPoint(5, 2));
|
||
|
dc.MoveTo(pz + CPoint(4, 2));
|
||
|
dc.LineTo(pz + CPoint(1, 5));
|
||
|
dc.MoveTo(pz + CPoint(2, 4));
|
||
|
dc.LineTo(pz + CPoint(5, 4));
|
||
|
|
||
|
dc.SelectObject(old);
|
||
|
|
||
|
}
|
||
|
|
||
|
void CBasisEdit::OnPaint()
|
||
|
{
|
||
|
CPaintDC dc(this); // device context for painting
|
||
|
|
||
|
NLMISC::CRGBA c1[] ={ NLMISC::CRGBA::White, NLMISC::CRGBA::White, NLMISC::CRGBA::White };
|
||
|
NLMISC::CRGBA c2[] ={ NLMISC::CRGBA::Green, NLMISC::CRGBA::Green, NLMISC::CRGBA::Red };
|
||
|
|
||
|
if (_Wrapper)
|
||
|
{
|
||
|
|
||
|
// read plane basis
|
||
|
NL3D::CPlaneBasis pb = _Wrapper->get();
|
||
|
|
||
|
CPoint center(20, 20);
|
||
|
// draw a white box on the left
|
||
|
dc.FillSolidRect(0, 0, 39, 39, 0x777777);
|
||
|
|
||
|
|
||
|
NLMISC::CMatrix m;
|
||
|
m.identity();
|
||
|
DrawBasisInDC(center, 18, m, dc, c1);
|
||
|
m.setRot(pb.X, pb.Y, pb.X ^ pb.Y);
|
||
|
DrawBasisInDC(center, 18, m, dc, c2);
|
||
|
|
||
|
}
|
||
|
|
||
|
// Do not call CDialog::OnPaint() for painting messages
|
||
|
}
|
||
|
|
||
|
void CBasisEdit::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
||
|
{
|
||
|
UpdateData();
|
||
|
if (nSBCode == SB_THUMBPOSITION || nSBCode == SB_THUMBTRACK)
|
||
|
{
|
||
|
NLMISC::CVector angles(2.f * (float) NLMISC::Pi * m_PsiCtrl.GetScrollPos() / 360.f
|
||
|
, 2.f * (float) NLMISC::Pi * m_ThetaCtrl.GetScrollPos() / 360.f
|
||
|
, 2.f * (float) NLMISC::Pi * m_PhiCtrl.GetScrollPos() / 360.f
|
||
|
);
|
||
|
if (pScrollBar == &m_PsiCtrl)
|
||
|
{
|
||
|
angles.x = 2.f * (float) NLMISC::Pi * nPos / 360.f;
|
||
|
m_PsiCtrl.SetScrollPos(nPos);
|
||
|
}
|
||
|
|
||
|
if (pScrollBar == &m_ThetaCtrl)
|
||
|
{
|
||
|
angles.y = 2.f * (float) NLMISC::Pi * nPos / 360.f;
|
||
|
m_ThetaCtrl.SetScrollPos(nPos);
|
||
|
}
|
||
|
|
||
|
if (pScrollBar == &m_PhiCtrl)
|
||
|
{
|
||
|
angles.z = 2.f * (float) NLMISC::Pi * nPos / 360.f;
|
||
|
m_PhiCtrl.SetScrollPos(nPos);
|
||
|
}
|
||
|
|
||
|
NLMISC::CMatrix mat = BuildEulerMatrix(angles.x, angles.y, angles.z);
|
||
|
NL3D::CPlaneBasis pb;
|
||
|
pb.X = mat.getI();
|
||
|
pb.Y = mat.getJ();
|
||
|
_Wrapper->setAndUpdateModifiedFlag(pb);
|
||
|
Invalidate();
|
||
|
}
|
||
|
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
|
||
|
UpdateData(FALSE);
|
||
|
}
|