1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "object_viewer.h"
19 #include "basis_edit.h"
20 #include "nel/misc/matrix.h"
21 #include "nel/misc/vector.h"
23 /////////////////////////////////////////////////////////////////////////////
27 CBasisEdit::CBasisEdit(CWnd
* pParent
/*=NULL*/)
29 //{{AFX_DATA_INIT(CBasisEdit)
37 void CBasisEdit::DoDataExchange(CDataExchange
* pDX
)
39 CDialog::DoDataExchange(pDX
);
40 //{{AFX_DATA_MAP(CBasisEdit)
41 DDX_Control(pDX
, IDC_PHI_SCROLLBAR
, m_PhiCtrl
);
42 DDX_Control(pDX
, IDC_PSI_SCROLLBAR
, m_PsiCtrl
);
43 DDX_Control(pDX
, IDC_THETA_SCROLLBAR
, m_ThetaCtrl
);
44 DDX_Text(pDX
, IDC_PSI_VALUE
, m_Psi
);
45 DDX_Text(pDX
, IDC_THETA_VALUE
, m_Theta
);
46 DDX_Text(pDX
, IDC_PHI_VALUE
, m_Phi
);
51 BEGIN_MESSAGE_MAP(CBasisEdit
, CDialog
)
52 //{{AFX_MSG_MAP(CBasisEdit)
58 /////////////////////////////////////////////////////////////////////////////
59 // CBasisEdit message handlers
65 void CBasisEdit::init(uint32 x
, uint32 y
, CWnd
*pParent
)
67 nlassert(_Wrapper
); // _Wrapper should have been set before init !!
68 Create(IDD_BASIS_EDIT
, pParent
);
72 m_PsiCtrl
.SetScrollRange(0, 359);
73 m_ThetaCtrl
.SetScrollRange(0, 359);
74 m_PhiCtrl
.SetScrollRange(0, 359);
76 MoveWindow(x
, y
, r
.right
, r
.bottom
);
77 updateAnglesFromReader();
86 // build an euler matrix
87 NLMISC::CMatrix
BuildEulerMatrix(float psi
, float theta
, float phi
)
89 float ca
= cosf(psi
), sa
= sinf(psi
)
90 , cb
= cosf(theta
), sb
= sinf(theta
)
91 , cc
= cosf(phi
), sc
= sinf(phi
);
94 m
.setRot(NLMISC::CVector(ca
* cb
* cc
- sa
* sc
, -cc
* sa
- ca
* cb
*sc
, ca
* sb
)
95 ,NLMISC::CVector(cb
* cc
* sa
+ ca
* sc
, ca
* cc
- cb
* sa
* sc
, sa
*sb
)
96 ,NLMISC::CVector(-cc
* sb
, sb
* sc
, cb
)
101 // get back the euler angles from a matrix
102 NLMISC::CVector
GetEulerAngles(const NLMISC::CMatrix
&mat
)
105 // we got cos theta = m33
106 NLMISC::CVector v
[3];
107 mat
.getRot(v
[0], v
[1], v
[2]);
108 for (uint l
= 0; l
< 3; ++l
)
110 m
[0][l
] = v
[l
].x
; m
[1][l
] = v
[l
].y
; m
[2][l
] = v
[l
].z
;
113 // there are eight triplet that may satisfy the equation
114 // we compute them all, and test them against the matrix
116 float b0
, b1
, a0
, a1
, a2
, a3
, c0
, c1
, c2
, c3
;
118 b1
= (float) NLMISC::Pi
- b0
;
119 float sb0
= sinf(b0
), sb1
= sinf(b1
);
120 if (fabsf(sb0
) > 10E-6)
129 if (fabs(sb1
) > 10E-6)
140 a2
= (float) NLMISC::Pi
- a0
;
141 a3
= (float) NLMISC::Pi
- a1
;
144 c2
= (float) NLMISC::Pi
- c0
;
145 c3
= (float) NLMISC::Pi
- c1
;
147 NLMISC::CVector sol
[] =
149 NLMISC::CVector(b0
, a0
, c0
)
150 ,NLMISC::CVector(b0
, a2
, c0
)
151 ,NLMISC::CVector(b0
, a0
, c2
)
152 ,NLMISC::CVector(b0
, a2
, c2
)
153 ,NLMISC::CVector(b1
, a1
, c1
)
154 ,NLMISC::CVector(b1
, a3
, c1
)
155 ,NLMISC::CVector(b1
, a1
, c3
)
156 ,NLMISC::CVector(b1
, a3
, c3
)
159 // now we take the triplet that fit best the 6 other equations
164 for (uint k
= 0; k
< 8; ++k
)
166 float ca
= cosf(sol
[k
].x
), sa
= sinf(sol
[k
].x
)
167 , cb
= cosf(sol
[k
].y
), sb
= sinf(sol
[k
].y
)
168 , cc
= cosf(sol
[k
].z
), sc
= sinf(sol
[k
].z
);
170 float gap
= fabsf(m
[0][0] - ca
* cb
* cc
+ sa
* sc
);
171 gap
+= fabsf(m
[1][0] + cc
* sa
+ ca
* cb
*sc
);
172 gap
+= fabsf(m
[0][1] - cb
* cc
* sa
- ca
* sc
);
173 gap
+= fabsf(m
[0][1] - cb
* cc
* sa
- ca
* sc
);
174 gap
+= fabsf(m
[1][1] - ca
* cc
+ cb
* sa
* sc
);
175 gap
+= fabsf(m
[2][1] - sb
*ca
);
176 gap
+= fabsf(m
[0][2] + cc
* sb
);
178 if (k
== 0 || gap
< bestGap
)
185 return sol
[bestIndex
];
190 void CBasisEdit::updateAnglesFromReader()
192 nlassert(_Wrapper
); // _Wrapper should have been set before init !!
195 NL3D::CPlaneBasis pb
= _Wrapper
->get();
197 mat
.setRot(pb
.X
, pb
.Y
, pb
.X
^ pb
.Y
);
198 NLMISC::CVector angles
= GetEulerAngles(mat
);
199 m_PsiCtrl
.SetScrollPos((uint
) (360.f
* angles
.x
/ (2.f
* (float) NLMISC::Pi
)));
200 m_ThetaCtrl
.SetScrollPos((uint
) (360.f
* angles
.y
/ (2.f
* (float) NLMISC::Pi
)));
201 m_PhiCtrl
.SetScrollPos((uint
) (360.f
* angles
.z
/ (2.f
* (float) NLMISC::Pi
)));
209 // just project a vector with orthogonal proj
210 static CPoint
BasisVectXForm(NLMISC::CVector
&v
, float size
)
212 const float sq
= sqrtf(2.f
) / 2.f
;
213 return CPoint((int) (size
* (sq
* (v
.x
+ v
.y
) )), (int) (size
* (-v
.z
+ sq
* (v
.x
- v
.y
))));
216 // draw a basis using the given colors, and hte given dc
218 void DrawBasisInDC(const CPoint
¢er
, float size
, const NLMISC::CMatrix
&m
, CDC
&dc
, NLMISC::CRGBA col
[3])
221 CPoint px
= center
+ BasisVectXForm(m
.getI(), size
);
222 CPoint py
= center
+ BasisVectXForm(m
.getJ(), size
);
223 CPoint pz
= center
+ BasisVectXForm(m
.getK(), size
);
227 p
[0].CreatePen(PS_SOLID
, 1, col
[0].R
+ (col
[0].G
<< 8) + (col
[0].B
<< 16) );
228 p
[1].CreatePen(PS_SOLID
, 1, col
[1].R
+ (col
[1].G
<< 8) + (col
[1].B
<< 16) );
229 p
[2].CreatePen(PS_SOLID
, 1, col
[2].R
+ (col
[2].G
<< 8) + (col
[2].B
<< 16) );
236 // draw letters indicating each axis
238 CPen
*old
= dc
.SelectObject(&p
[0]);
241 dc
.MoveTo(px
+ CPoint(2, 2));
242 dc
.LineTo(px
+ CPoint(5, 5));
243 dc
.MoveTo(px
+ CPoint(4, 2));
244 dc
.LineTo(px
+ CPoint(3, 5));
248 dc
.SelectObject(&p
[1]);
252 dc
.MoveTo(py
+ CPoint(2, 2));
253 dc
.LineTo(py
+ CPoint(4, 4));
254 dc
.MoveTo(py
+ CPoint(4, 2));
255 dc
.LineTo(py
+ CPoint(1, 5));
258 dc
.SelectObject(&p
[2]);
262 dc
.MoveTo(pz
+ CPoint(2, 2));
263 dc
.LineTo(pz
+ CPoint(5, 2));
264 dc
.MoveTo(pz
+ CPoint(4, 2));
265 dc
.LineTo(pz
+ CPoint(1, 5));
266 dc
.MoveTo(pz
+ CPoint(2, 4));
267 dc
.LineTo(pz
+ CPoint(5, 4));
269 dc
.SelectObject(old
);
273 void CBasisEdit::OnPaint()
275 CPaintDC
dc(this); // device context for painting
277 NLMISC::CRGBA c1
[] ={ NLMISC::CRGBA::White
, NLMISC::CRGBA::White
, NLMISC::CRGBA::White
};
278 NLMISC::CRGBA c2
[] ={ NLMISC::CRGBA::Green
, NLMISC::CRGBA::Green
, NLMISC::CRGBA::Red
};
284 NL3D::CPlaneBasis pb
= _Wrapper
->get();
286 CPoint
center(20, 20);
287 // draw a white box on the left
288 dc
.FillSolidRect(0, 0, 39, 39, 0x777777);
293 DrawBasisInDC(center
, 18, m
, dc
, c1
);
294 m
.setRot(pb
.X
, pb
.Y
, pb
.X
^ pb
.Y
);
295 DrawBasisInDC(center
, 18, m
, dc
, c2
);
299 // Do not call CDialog::OnPaint() for painting messages
302 void CBasisEdit::OnHScroll(UINT nSBCode
, UINT nPos
, CScrollBar
* pScrollBar
)
305 if (nSBCode
== SB_THUMBPOSITION
|| nSBCode
== SB_THUMBTRACK
)
307 NLMISC::CVector
angles(2.f
* (float) NLMISC::Pi
* m_PsiCtrl
.GetScrollPos() / 360.f
308 , 2.f
* (float) NLMISC::Pi
* m_ThetaCtrl
.GetScrollPos() / 360.f
309 , 2.f
* (float) NLMISC::Pi
* m_PhiCtrl
.GetScrollPos() / 360.f
311 if (pScrollBar
== &m_PsiCtrl
)
313 angles
.x
= 2.f
* (float) NLMISC::Pi
* nPos
/ 360.f
;
314 m_PsiCtrl
.SetScrollPos(nPos
);
317 if (pScrollBar
== &m_ThetaCtrl
)
319 angles
.y
= 2.f
* (float) NLMISC::Pi
* nPos
/ 360.f
;
320 m_ThetaCtrl
.SetScrollPos(nPos
);
323 if (pScrollBar
== &m_PhiCtrl
)
325 angles
.z
= 2.f
* (float) NLMISC::Pi
* nPos
/ 360.f
;
326 m_PhiCtrl
.SetScrollPos(nPos
);
329 NLMISC::CMatrix mat
= BuildEulerMatrix(angles
.x
, angles
.y
, angles
.z
);
330 NL3D::CPlaneBasis pb
;
333 _Wrapper
->setAndUpdateModifiedFlag(pb
);
336 CDialog::OnHScroll(nSBCode
, nPos
, pScrollBar
);