fix "ExpandPath called without qdir set" as qdir is never set in Xonotic/Nexuiz/any...
[divverent/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 {
34   ManagerState = Idle;
35   m_NumPoints = iPts;
36   m_pPts = Pts;
37   // store the initial config
38   memcpy( &m_RefPts, Pts, sizeof( CtrlPts_t ) );
39   // init TM
40   memset( m_TM, 0, sizeof( float[2][3] ) );
41   m_TM[0][0] = 1.0f; m_TM[1][1] = 1.0f;
42   m_bGotAnchor = false;
43   m_TransOffset[0] = 0.0f; m_TransOffset[1] = 0.0f;
44   m_TexSize[0] = TexSize[0];
45   m_TexSize[1] = TexSize[1];
46   m_pFaceData = pFaceData;
47
48   CControlPointsManager::Init( p2DView, pQglTable );
49 }
50
51 bool CControlPointsManagerBFace::OnLButtonDown (int xPos, int yPos)
52 {
53   if (ManagerState == Idle)
54   {
55     int i;
56
57     // scan the point list to see if we selected something
58     for ( i=0; i<m_NumPoints; i++ )
59       if ( m_p2DView->DoesSelect( xPos, yPos, m_pPts->data[i] ) )
60       {
61         m_iDragPoint = i;
62         ManagerState = Drag;
63         if (m_bGotAnchor && i == m_iAnchorPoint)
64         {
65           // this means we selected the Anchor, so we'll translate
66           m_bGotAnchor = false;
67         }
68         // perhaps we won't use translation, but we can compute it anyway
69         ComputeTransOffset(i);
70         if (m_bGotAnchor)
71         {
72           // we have an Anchor and selected another point
73           m_Anchor[0] = m_pPts->data[m_iAnchorPoint][0];
74           m_Anchor[1] = m_pPts->data[m_iAnchorPoint][1];
75         }
76       }
77     // send a repaint message
78     g_pToolWnd->Redraw ();
79     return true;
80   }
81   return false;
82 }
83
84 bool CControlPointsManagerBFace::OnMouseMove (int xPos, int yPos)
85 {
86   if (ManagerState == Drag)
87   {
88     if (m_bGotAnchor)
89     {
90       // there's an anchor, we are rotating the shape
91       // we need to work in XY space for orthonormality
92       float Pt[2];
93       vec3_t V1,V2;
94       vec3_t cross;
95       float c,s;
96       // used in XY space
97       float XYTM[2][3];
98       float XYRefAnchor[2];
99       float XYAnchor[2];
100       m_p2DView->GridForWindow( Pt, xPos, yPos );
101       V2[0] = Pt[0] - m_Anchor[0];
102       V2[1] = Pt[1] - m_Anchor[1];
103       V2[2] = 0.0f;
104       V1[0] = m_RefPts.data[m_iDragPoint][0] - m_RefPts.data[m_iAnchorPoint][0];
105       V1[1] = m_RefPts.data[m_iDragPoint][1] - m_RefPts.data[m_iAnchorPoint][1];
106       V1[2] = 0.0f;
107       // compute transformation from V1 to V2
108       // we need to work in XY orthonormal space
109       XYSpaceForSTSpace( V1, V1 );
110       XYSpaceForSTSpace( V2, V2 );
111       VectorNormalize( V2, V2 );
112       VectorNormalize( V1, V1 );
113       c = DotProduct( V1, V2 );
114       CrossProduct( V1, V2, cross );
115       s = VectorLength( cross );
116       // we compute the transformation matrix in XY space
117       // reference position of the Anchor in XY space
118       XYSpaceForSTSpace( XYRefAnchor, m_RefPts.data[m_iAnchorPoint] );
119       // current position of the Anchor in XY space
120       XYSpaceForSTSpace( XYAnchor, m_Anchor );
121       // compute transformation matrix
122       XYTM[0][0] = c; XYTM[1][1] = c;
123       if (cross[2]>0)
124         s *= -1.0f;
125       XYTM[0][1] = s; XYTM[1][0] = -s;
126       XYTM[0][2] = -c*XYRefAnchor[0] - s*XYRefAnchor[1] + XYAnchor[0];
127       XYTM[1][2] = s*XYRefAnchor[0] - c*XYRefAnchor[1] + XYAnchor[1];
128       // express this transformation matrix in ST space
129       m_TM[0][0] = XYTM[0][0];
130       m_TM[1][0] = XYTM[1][0] * (float)m_TexSize[0] / (float)m_TexSize[1];
131       m_TM[0][1] = XYTM[0][1] * (float)m_TexSize[1] / (float)m_TexSize[0];
132       m_TM[1][1] = XYTM[1][1];
133       m_TM[0][2] = XYTM[0][2] / (float)m_TexSize[0];
134       m_TM[1][2] = XYTM[1][2] / (float)m_TexSize[1];
135       // update all points
136       UpdateCtrlPts();
137     }
138     else
139     {
140       // no Anchor point is defined, we translate all points
141       m_p2DView->GridForWindow( m_pPts->data[m_iDragPoint], xPos, yPos );
142       m_TM[0][2] = m_pPts->data[m_iDragPoint][0] + m_TransOffset[0];
143       m_TM[1][2] = m_pPts->data[m_iDragPoint][1] + m_TransOffset[1];
144       // update all points
145       UpdateCtrlPts();
146     }
147     // send a repaint message
148     g_pToolWnd->Redraw ();
149     return true;
150   }
151
152   return false;
153 }
154
155 bool CControlPointsManagerBFace::OnLButtonUp (int x, int y)
156 {
157   if (ManagerState == Drag)
158   {
159     // this button is gonna become our Anchor
160     m_bGotAnchor = true;
161     m_iAnchorPoint = m_iDragPoint;
162     // let's get out of Drag mode
163     ManagerState = Idle;
164     // send a repaint message
165     g_pToolWnd->Redraw ();
166     return true;
167   }
168   return false;
169 }
170
171 void CControlPointsManagerBFace::render()
172 {
173   int i;
174
175   m_pQglTable->m_pfn_qglColor3f(0, 1, 0);
176   m_pQglTable->m_pfn_qglPointSize(6);
177   m_pQglTable->m_pfn_qglBegin( GL_POINTS );
178   for ( i=0; i<m_NumPoints; i++ )
179   {
180     if ( ManagerState == Drag && i == m_iDragPoint )
181       m_pQglTable->m_pfn_qglColor3f(1, 0, 0);
182     else if ( m_bGotAnchor && i == m_iAnchorPoint )
183       m_pQglTable->m_pfn_qglColor3f(0, 0, 1);
184     m_pQglTable->m_pfn_qglVertex2f( m_pPts->data[i][0], m_pPts->data[i][1] );
185     m_pQglTable->m_pfn_qglColor3f(0, 1, 0);
186   }
187   m_pQglTable->m_pfn_qglEnd();
188 }
189
190 void CControlPointsManagerBFace::UpdateCtrlPts()
191 {
192   int i;
193
194   // update all points
195   for ( i=0; i<m_NumPoints; i++ )
196   {
197     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];
198     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];
199   }
200
201   if (g_bPrefsUpdateCameraView)
202   {
203     Commit();
204     // tell Radiant to update
205     // NOTE: little speed optimisation, disable window updates, and only update camera view
206     g_FuncTable.m_pfnSetScreenUpdate( false );
207     g_SelectedFaceTable.m_pfnSetFaceInfo( 0, m_pFaceData );
208     g_FuncTable.m_pfnSetScreenUpdate( true );
209     g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
210   }
211 }
212
213 //++timo FIXME: we are using a global for the reference data, use a m_pCancelFaceData instead
214 void CControlPointsManagerBFace::Commit( )
215 {
216   brushprimit_texdef_t aux;
217   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];
218   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];
219   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];
220   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];
221   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];
222   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];
223   memcpy( &m_pFaceData->brushprimit_texdef, &aux, sizeof(brushprimit_texdef_t) );
224 }
225
226 void CControlPointsManagerBFace::ComputeTransOffset(int i)
227 {
228   // compute the translation offset used to counteract rotation
229   m_TransOffset[0] = -m_TM[0][0]*m_RefPts.data[i][0] - m_TM[0][1]*m_RefPts.data[i][1];
230   m_TransOffset[1] = -m_TM[1][0]*m_RefPts.data[i][0] - m_TM[1][1]*m_RefPts.data[i][1];
231 }
232
233 void CControlPointsManagerBFace::XYSpaceForSTSpace( float xy[2], const float st[2] )
234 {
235   xy[0] = st[0] * (float)m_TexSize[0];
236   xy[1] = st[1] * (float)m_TexSize[1];
237 }
238
239 /*
240 ======================================================================
241 patch manager
242 ======================================================================
243 */
244
245 void CControlPointsManagerPatch::Init( patchMesh_t* pWorkPatch, C2DView *p2DView, OpenGLBinding *pQglTable, patchMesh_t* pPatch )
246 {
247   CControlPointsManager::Init( p2DView, pQglTable );
248   m_pPatch = pPatch;
249   m_pWorkPatch = pWorkPatch;
250 }
251
252 bool CControlPointsManagerPatch::OnLButtonDown (int xPos, int yPos)
253 {
254   if (ManagerState == Idle)
255   {
256     int i,j;
257
258     // scan the point list to see if we selected something
259     for ( i=0; i<m_pPatch->width; i++ )
260       for ( j=0; j<m_pPatch->height; j++ )
261         if ( m_p2DView->DoesSelect( xPos, yPos, m_pWorkPatch->ctrl[i][j].st ) )
262         {
263           m_iDragPoint[0] = i;
264           m_iDragPoint[1] = j;
265           ManagerState = Drag;
266         }
267     // send a repaint message
268     g_pToolWnd->Redraw ();
269     return true;
270   }
271   return false;
272 }
273
274 bool CControlPointsManagerPatch::OnMouseMove (int xPos, int yPos)
275 {
276   if (ManagerState == Drag)
277   {
278     m_p2DView->GridForWindow( m_pWorkPatch->ctrl[ m_iDragPoint[0] ][ m_iDragPoint[1] ].st, xPos, yPos );
279     if (g_bPrefsUpdateCameraView)
280     {
281       Commit();
282       // ask to rebuild the patch display data
283       m_pPatch->bDirty = true;
284       // send a repaint to the camera window as well
285       g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
286     }
287     // send a repaint message
288     g_pToolWnd->Redraw ();
289     return true;
290   }
291   return false;
292 }
293
294 bool CControlPointsManagerPatch::OnLButtonUp (int x, int y)
295 {
296   if (ManagerState == Drag)
297   {
298     ManagerState = Idle;
299     // send a repaint message
300     g_pToolWnd->Redraw ();
301   }
302   return false;
303 }
304
305 void CControlPointsManagerPatch::render()
306 {
307   int i,j;
308
309   m_pQglTable->m_pfn_qglColor3f(0, 1, 0);
310   m_pQglTable->m_pfn_qglPointSize(6);
311   m_pQglTable->m_pfn_qglBegin( GL_POINTS );
312   for ( i=0; i<m_pPatch->width; i++ )
313     for ( j=0; j<m_pPatch->height; j++ )
314     {
315       if ( ManagerState == Drag && i == m_iDragPoint[0] && j == m_iDragPoint[1] )
316         m_pQglTable->m_pfn_qglColor3f(1, 0, 0);
317       m_pQglTable->m_pfn_qglVertex2f( m_pWorkPatch->ctrl[i][j].st[0], m_pWorkPatch->ctrl[i][j].st[1] );
318       m_pQglTable->m_pfn_qglColor3f(0, 1, 0);
319     }
320   m_pQglTable->m_pfn_qglEnd();
321 }
322
323 void CControlPointsManagerPatch::Commit()
324 {
325   int i,j;
326   for ( i=0; i<m_pPatch->width; i++ )
327     for ( j=0; j<m_pPatch->height; j++ )
328     {
329       m_pPatch->ctrl[i][j].st[0] = m_pWorkPatch->ctrl[i][j].st[0];
330       m_pPatch->ctrl[i][j].st[1] = m_pWorkPatch->ctrl[i][j].st[1];
331     }
332 }