Merge branch 'master' into divVerent/farplanedist-sky-fix
[xonotic/netradiant.git] / plugins / textool / ControlPointsManager.cpp
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant 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 General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 //-----------------------------------------------------------------------------
23 //
24 // DESCRIPTION:
25 // a class to handle control points in a 2D view
26 // TODO: this one can be placed under an interface, and provided to the editor as service
27 //
28
29 #include "StdAfx.h"
30
31 void CControlPointsManagerBFace::Init( int iPts, CtrlPts_t *Pts, C2DView *p2DView, int TexSize[2],
32                                                                            _QERFaceData* pFaceData, OpenGLBinding *pQglTable ){
33         ManagerState = Idle;
34         m_NumPoints = iPts;
35         m_pPts = Pts;
36         // store the initial config
37         memcpy( &m_RefPts, Pts, sizeof( CtrlPts_t ) );
38         // init TM
39         memset( m_TM, 0, sizeof( float[2][3] ) );
40         m_TM[0][0] = 1.0f; m_TM[1][1] = 1.0f;
41         m_bGotAnchor = false;
42         m_TransOffset[0] = 0.0f; m_TransOffset[1] = 0.0f;
43         m_TexSize[0] = TexSize[0];
44         m_TexSize[1] = TexSize[1];
45         m_pFaceData = pFaceData;
46
47         CControlPointsManager::Init( p2DView, pQglTable );
48 }
49
50 bool CControlPointsManagerBFace::OnLButtonDown( int xPos, int yPos ){
51         if ( ManagerState == Idle ) {
52                 int i;
53
54                 // scan the point list to see if we selected something
55                 for ( i = 0; i < m_NumPoints; i++ )
56                         if ( m_p2DView->DoesSelect( xPos, yPos, m_pPts->data[i] ) ) {
57                                 m_iDragPoint = i;
58                                 ManagerState = Drag;
59                                 if ( m_bGotAnchor && i == m_iAnchorPoint ) {
60                                         // this means we selected the Anchor, so we'll translate
61                                         m_bGotAnchor = false;
62                                 }
63                                 // perhaps we won't use translation, but we can compute it anyway
64                                 ComputeTransOffset( i );
65                                 if ( m_bGotAnchor ) {
66                                         // we have an Anchor and selected another point
67                                         m_Anchor[0] = m_pPts->data[m_iAnchorPoint][0];
68                                         m_Anchor[1] = m_pPts->data[m_iAnchorPoint][1];
69                                 }
70                         }
71                 // send a repaint message
72                 g_pToolWnd->Redraw();
73                 return true;
74         }
75         return false;
76 }
77
78 bool CControlPointsManagerBFace::OnMouseMove( int xPos, int yPos ){
79         if ( ManagerState == Drag ) {
80                 if ( m_bGotAnchor ) {
81                         // there's an anchor, we are rotating the shape
82                         // we need to work in XY space for orthonormality
83                         float Pt[2];
84                         vec3_t V1,V2;
85                         vec3_t cross;
86                         float c,s;
87                         // used in XY space
88                         float XYTM[2][3];
89                         float XYRefAnchor[2];
90                         float XYAnchor[2];
91                         m_p2DView->GridForWindow( Pt, xPos, yPos );
92                         V2[0] = Pt[0] - m_Anchor[0];
93                         V2[1] = Pt[1] - m_Anchor[1];
94                         V2[2] = 0.0f;
95                         V1[0] = m_RefPts.data[m_iDragPoint][0] - m_RefPts.data[m_iAnchorPoint][0];
96                         V1[1] = m_RefPts.data[m_iDragPoint][1] - m_RefPts.data[m_iAnchorPoint][1];
97                         V1[2] = 0.0f;
98                         // compute transformation from V1 to V2
99                         // we need to work in XY orthonormal space
100                         XYSpaceForSTSpace( V1, V1 );
101                         XYSpaceForSTSpace( V2, V2 );
102                         VectorNormalize( V2, V2 );
103                         VectorNormalize( V1, V1 );
104                         c = DotProduct( V1, V2 );
105                         CrossProduct( V1, V2, cross );
106                         s = VectorLength( cross );
107                         // we compute the transformation matrix in XY space
108                         // reference position of the Anchor in XY space
109                         XYSpaceForSTSpace( XYRefAnchor, m_RefPts.data[m_iAnchorPoint] );
110                         // current position of the Anchor in XY space
111                         XYSpaceForSTSpace( XYAnchor, m_Anchor );
112                         // compute transformation matrix
113                         XYTM[0][0] = c; XYTM[1][1] = c;
114                         if ( cross[2] > 0 ) {
115                                 s *= -1.0f;
116                         }
117                         XYTM[0][1] = s; XYTM[1][0] = -s;
118                         XYTM[0][2] = -c * XYRefAnchor[0] - s * XYRefAnchor[1] + XYAnchor[0];
119                         XYTM[1][2] = s * XYRefAnchor[0] - c * XYRefAnchor[1] + XYAnchor[1];
120                         // express this transformation matrix in ST space
121                         m_TM[0][0] = XYTM[0][0];
122                         m_TM[1][0] = XYTM[1][0] * (float)m_TexSize[0] / (float)m_TexSize[1];
123                         m_TM[0][1] = XYTM[0][1] * (float)m_TexSize[1] / (float)m_TexSize[0];
124                         m_TM[1][1] = XYTM[1][1];
125                         m_TM[0][2] = XYTM[0][2] / (float)m_TexSize[0];
126                         m_TM[1][2] = XYTM[1][2] / (float)m_TexSize[1];
127                         // update all points
128                         UpdateCtrlPts();
129                 }
130                 else
131                 {
132                         // no Anchor point is defined, we translate all points
133                         m_p2DView->GridForWindow( m_pPts->data[m_iDragPoint], xPos, yPos );
134                         m_TM[0][2] = m_pPts->data[m_iDragPoint][0] + m_TransOffset[0];
135                         m_TM[1][2] = m_pPts->data[m_iDragPoint][1] + m_TransOffset[1];
136                         // update all points
137                         UpdateCtrlPts();
138                 }
139                 // send a repaint message
140                 g_pToolWnd->Redraw();
141                 return true;
142         }
143
144         return false;
145 }
146
147 bool CControlPointsManagerBFace::OnLButtonUp( int x, int y ){
148         if ( ManagerState == Drag ) {
149                 // this button is gonna become our Anchor
150                 m_bGotAnchor = true;
151                 m_iAnchorPoint = m_iDragPoint;
152                 // let's get out of Drag mode
153                 ManagerState = Idle;
154                 // send a repaint message
155                 g_pToolWnd->Redraw();
156                 return true;
157         }
158         return false;
159 }
160
161 void CControlPointsManagerBFace::render(){
162         int i;
163
164         m_pQglTable->m_pfn_qglColor3f( 0, 1, 0 );
165         m_pQglTable->m_pfn_qglPointSize( 6 );
166         m_pQglTable->m_pfn_qglBegin( GL_POINTS );
167         for ( i = 0; i < m_NumPoints; i++ )
168         {
169                 if ( ManagerState == Drag && i == m_iDragPoint ) {
170                         m_pQglTable->m_pfn_qglColor3f( 1, 0, 0 );
171                 }
172                 else if ( m_bGotAnchor && i == m_iAnchorPoint ) {
173                         m_pQglTable->m_pfn_qglColor3f( 0, 0, 1 );
174                 }
175                 m_pQglTable->m_pfn_qglVertex2f( m_pPts->data[i][0], m_pPts->data[i][1] );
176                 m_pQglTable->m_pfn_qglColor3f( 0, 1, 0 );
177         }
178         m_pQglTable->m_pfn_qglEnd();
179 }
180
181 void CControlPointsManagerBFace::UpdateCtrlPts(){
182         int i;
183
184         // update all points
185         for ( i = 0; i < m_NumPoints; i++ )
186         {
187                 m_pPts->data[i][0] = m_RefPts.data[i][0] * m_TM[0][0] + m_RefPts.data[i][1] * m_TM[0][1] + m_TM[0][2];
188                 m_pPts->data[i][1] = m_RefPts.data[i][0] * m_TM[1][0] + m_RefPts.data[i][1] * m_TM[1][1] + m_TM[1][2];
189         }
190
191         if ( g_bPrefsUpdateCameraView ) {
192                 Commit();
193                 // tell Radiant to update
194                 // NOTE: little speed optimisation, disable window updates, and only update camera view
195                 g_FuncTable.m_pfnSetScreenUpdate( false );
196                 g_SelectedFaceTable.m_pfnSetFaceInfo( 0, m_pFaceData );
197                 g_FuncTable.m_pfnSetScreenUpdate( true );
198                 g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
199         }
200 }
201
202 //++timo FIXME: we are using a global for the reference data, use a m_pCancelFaceData instead
203 void CControlPointsManagerBFace::Commit(){
204         brushprimit_texdef_t aux;
205         aux.coords[0][0] = m_TM[0][0] * g_CancelFaceData.brushprimit_texdef.coords[0][0] + m_TM[0][1] * g_CancelFaceData.brushprimit_texdef.coords[1][0];
206         aux.coords[0][1] = m_TM[0][0] * g_CancelFaceData.brushprimit_texdef.coords[0][1] + m_TM[0][1] * g_CancelFaceData.brushprimit_texdef.coords[1][1];
207         aux.coords[0][2] = m_TM[0][0] * g_CancelFaceData.brushprimit_texdef.coords[0][2] + m_TM[0][1] * g_CancelFaceData.brushprimit_texdef.coords[1][2] + m_TM[0][2];
208         aux.coords[1][0] = m_TM[1][0] * g_CancelFaceData.brushprimit_texdef.coords[0][0] + m_TM[1][1] * g_CancelFaceData.brushprimit_texdef.coords[1][0];
209         aux.coords[1][1] = m_TM[1][0] * g_CancelFaceData.brushprimit_texdef.coords[0][1] + m_TM[1][1] * g_CancelFaceData.brushprimit_texdef.coords[1][1];
210         aux.coords[1][2] = m_TM[1][0] * g_CancelFaceData.brushprimit_texdef.coords[0][2] + m_TM[1][1] * g_CancelFaceData.brushprimit_texdef.coords[1][2] + m_TM[1][2];
211         memcpy( &m_pFaceData->brushprimit_texdef, &aux, sizeof( brushprimit_texdef_t ) );
212 }
213
214 void CControlPointsManagerBFace::ComputeTransOffset( int i ){
215         // compute the translation offset used to counteract rotation
216         m_TransOffset[0] = -m_TM[0][0] * m_RefPts.data[i][0] - m_TM[0][1] * m_RefPts.data[i][1];
217         m_TransOffset[1] = -m_TM[1][0] * m_RefPts.data[i][0] - m_TM[1][1] * m_RefPts.data[i][1];
218 }
219
220 void CControlPointsManagerBFace::XYSpaceForSTSpace( float xy[2], const float st[2] ){
221         xy[0] = st[0] * (float)m_TexSize[0];
222         xy[1] = st[1] * (float)m_TexSize[1];
223 }
224
225 /*
226    ======================================================================
227    patch manager
228    ======================================================================
229  */
230
231 void CControlPointsManagerPatch::Init( patchMesh_t* pWorkPatch, C2DView *p2DView, OpenGLBinding *pQglTable, patchMesh_t* pPatch ){
232         CControlPointsManager::Init( p2DView, pQglTable );
233         m_pPatch = pPatch;
234         m_pWorkPatch = pWorkPatch;
235 }
236
237 bool CControlPointsManagerPatch::OnLButtonDown( int xPos, int yPos ){
238         if ( ManagerState == Idle ) {
239                 int i,j;
240
241                 // scan the point list to see if we selected something
242                 for ( i = 0; i < m_pPatch->width; i++ )
243                         for ( j = 0; j < m_pPatch->height; j++ )
244                                 if ( m_p2DView->DoesSelect( xPos, yPos, m_pWorkPatch->ctrl[i][j].st ) ) {
245                                         m_iDragPoint[0] = i;
246                                         m_iDragPoint[1] = j;
247                                         ManagerState = Drag;
248                                 }
249                 // send a repaint message
250                 g_pToolWnd->Redraw();
251                 return true;
252         }
253         return false;
254 }
255
256 bool CControlPointsManagerPatch::OnMouseMove( int xPos, int yPos ){
257         if ( ManagerState == Drag ) {
258                 m_p2DView->GridForWindow( m_pWorkPatch->ctrl[ m_iDragPoint[0] ][ m_iDragPoint[1] ].st, xPos, yPos );
259                 if ( g_bPrefsUpdateCameraView ) {
260                         Commit();
261                         // ask to rebuild the patch display data
262                         m_pPatch->bDirty = true;
263                         // send a repaint to the camera window as well
264                         g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
265                 }
266                 // send a repaint message
267                 g_pToolWnd->Redraw();
268                 return true;
269         }
270         return false;
271 }
272
273 bool CControlPointsManagerPatch::OnLButtonUp( int x, int y ){
274         if ( ManagerState == Drag ) {
275                 ManagerState = Idle;
276                 // send a repaint message
277                 g_pToolWnd->Redraw();
278         }
279         return false;
280 }
281
282 void CControlPointsManagerPatch::render(){
283         int i,j;
284
285         m_pQglTable->m_pfn_qglColor3f( 0, 1, 0 );
286         m_pQglTable->m_pfn_qglPointSize( 6 );
287         m_pQglTable->m_pfn_qglBegin( GL_POINTS );
288         for ( i = 0; i < m_pPatch->width; i++ )
289                 for ( j = 0; j < m_pPatch->height; j++ )
290                 {
291                         if ( ManagerState == Drag && i == m_iDragPoint[0] && j == m_iDragPoint[1] ) {
292                                 m_pQglTable->m_pfn_qglColor3f( 1, 0, 0 );
293                         }
294                         m_pQglTable->m_pfn_qglVertex2f( m_pWorkPatch->ctrl[i][j].st[0], m_pWorkPatch->ctrl[i][j].st[1] );
295                         m_pQglTable->m_pfn_qglColor3f( 0, 1, 0 );
296                 }
297         m_pQglTable->m_pfn_qglEnd();
298 }
299
300 void CControlPointsManagerPatch::Commit(){
301         int i,j;
302         for ( i = 0; i < m_pPatch->width; i++ )
303                 for ( j = 0; j < m_pPatch->height; j++ )
304                 {
305                         m_pPatch->ctrl[i][j].st[0] = m_pWorkPatch->ctrl[i][j].st[0];
306                         m_pPatch->ctrl[i][j].st[1] = m_pWorkPatch->ctrl[i][j].st[1];
307                 }
308 }