]> git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/textool/TexTool.cpp
uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / plugins / textool / TexTool.cpp
1 /*
2    Copyright (C) 1999-2007 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 // main plugin implementation
26 // texturing tools for Radiant
27 //
28
29 #include "StdAfx.h"
30
31 static void dialog_button_callback( GtkWidget *widget, gpointer data ){
32         GtkWidget *parent;
33         int *loop, *ret;
34
35         parent = gtk_widget_get_toplevel( widget );
36         loop = (int*)g_object_get_data( G_OBJECT( parent ), "loop" );
37         ret = (int*)g_object_get_data( G_OBJECT( parent ), "ret" );
38
39         *loop = 0;
40         *ret = (int)data;
41 }
42
43 static gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
44         int *loop;
45
46         gtk_widget_hide( widget );
47         loop = (int*)g_object_get_data( G_OBJECT( widget ), "loop" );
48         *loop = 0;
49
50         return TRUE;
51 }
52
53 int DoMessageBox( const char* lpText, const char* lpCaption, guint32 uType ){
54         GtkWidget *window, *w, *vbox, *hbox;
55         int mode = ( uType & MB_TYPEMASK ), ret, loop = 1;
56
57         window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
58         gtk_signal_connect( GTK_OBJECT( window ), "delete_event",
59                                                 GTK_SIGNAL_FUNC( dialog_delete_callback ), NULL );
60         gtk_signal_connect( GTK_OBJECT( window ), "destroy",
61                                                 GTK_SIGNAL_FUNC( gtk_widget_destroy ), NULL );
62         gtk_window_set_title( GTK_WINDOW( window ), lpCaption );
63         gtk_container_border_width( GTK_CONTAINER( window ), 10 );
64         g_object_set_data( G_OBJECT( window ), "loop", &loop );
65         g_object_set_data( G_OBJECT( window ), "ret", &ret );
66         gtk_widget_realize( window );
67
68         vbox = gtk_vbox_new( FALSE, 10 );
69         gtk_container_add( GTK_CONTAINER( window ), vbox );
70         gtk_widget_show( vbox );
71
72         w = gtk_label_new( lpText );
73         gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 2 );
74         gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT );
75         gtk_widget_show( w );
76
77         w = gtk_hseparator_new();
78         gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 2 );
79         gtk_widget_show( w );
80
81         hbox = gtk_hbox_new( FALSE, 10 );
82         gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 2 );
83         gtk_widget_show( hbox );
84
85         if ( mode == MB_OK ) {
86                 w = gtk_button_new_with_label( "Ok" );
87                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
88                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
89                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
90                 GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
91                 gtk_widget_grab_default( w );
92                 gtk_widget_show( w );
93                 ret = IDOK;
94         }
95         else if ( mode ==  MB_OKCANCEL ) {
96                 w = gtk_button_new_with_label( "Ok" );
97                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
98                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
99                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
100                 GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
101                 gtk_widget_grab_default( w );
102                 gtk_widget_show( w );
103
104                 w = gtk_button_new_with_label( "Cancel" );
105                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
106                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
107                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
108                 gtk_widget_show( w );
109                 ret = IDCANCEL;
110         }
111         else if ( mode == MB_YESNOCANCEL ) {
112                 w = gtk_button_new_with_label( "Yes" );
113                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
114                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
115                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
116                 GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
117                 gtk_widget_grab_default( w );
118                 gtk_widget_show( w );
119
120                 w = gtk_button_new_with_label( "No" );
121                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
122                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
123                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
124                 gtk_widget_show( w );
125
126                 w = gtk_button_new_with_label( "Cancel" );
127                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
128                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
129                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
130                 gtk_widget_show( w );
131                 ret = IDCANCEL;
132         }
133         else /* if (mode == MB_YESNO) */
134         {
135                 w = gtk_button_new_with_label( "Yes" );
136                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
137                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
138                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
139                 GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
140                 gtk_widget_grab_default( w );
141                 gtk_widget_show( w );
142
143                 w = gtk_button_new_with_label( "No" );
144                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
145                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
146                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
147                 gtk_widget_show( w );
148                 ret = IDNO;
149         }
150
151         gtk_widget_show( window );
152         gtk_grab_add( window );
153
154         while ( loop )
155                 gtk_main_iteration();
156
157         gtk_grab_remove( window );
158         gtk_widget_destroy( window );
159
160         return ret;
161 }
162
163 // Radiant function table
164 _QERFuncTable_1 g_FuncTable;
165
166 // plugin name
167 const char *PLUGIN_NAME = "Q3 Texture Tools";
168
169 // commands in the menu
170 const char *PLUGIN_COMMANDS = "About...;Go...";
171
172 // cast to GtkWidget*
173 void *g_pMainWnd;
174 IWindow *g_pToolWnd = NULL; // handle to the window
175 CWindowListener g_Listen;
176
177 // plugin interfaces ---------------------------
178 bool g_bQglInitDone = false;
179 _QERQglTable g_QglTable;
180 bool g_bSelectedFaceInitDone = false;
181 _QERSelectedFaceTable g_SelectedFaceTable;
182 bool g_bUITable = false;
183 _QERUITable g_UITable;
184
185 // selected face -------------------------------
186 // we use this one to commit / read with Radiant
187 _QERFaceData g_SelectedFaceData;
188 // g_pSelectedFaceWindings gets allocated with MAX_POINTS_ON_WINDING at plugin startup ( QERPlug_Init )
189 winding_t               *g_pSelectedFaceWinding = NULL;
190 const float g_ViewportRatio = 1.2f;
191 // usefull class to manage the 2D view
192 C2DView g_2DView;
193 // control points to move the polygon
194 CControlPointsManagerBFace g_ControlPointsBFace;
195 // tells if a face is selected and we have something to render in the TexWindow
196 bool g_bTexViewReady = false;
197 // data for texture work
198 int g_NumPoints;
199 CtrlPts_t g_WorkWinding;
200 // reference _QERFaceData we use on Cancel, and for Commit
201 _QERFaceData g_CancelFaceData;
202
203 // patches -------------------------------------
204 bool g_bPatch = false;
205 //++timo we use this one to grab selected patchMesh_t
206 // FIXME: update when there's a real interface to read/write patches
207 bool g_bSurfaceTableInitDone = false;
208 _QERAppSurfaceTable g_SurfaceTable;
209 CControlPointsManagerPatch g_ControlPointsPatch;
210 // data for texture work
211 patchMesh_t*            g_pPatch;
212 // we only use ctrl[][].st in this one
213 patchMesh_t g_WorkPatch;
214 // copy of initial g_pPatch for Cancel situation
215 patchMesh_t g_CancelPatch;
216
217 // ---------------------------------------------
218 // holds the manager we are currently using
219 CControlPointsManager   *g_pManager = NULL;
220
221 // ---------------------------------------------
222 // globals flags for user preferences
223 //++timo TODO: this should be retrieved from the Editor's .INI prefs in a dedicated interface
224 // update camera view during manipulation ?
225 bool g_bPrefsUpdateCameraView = true;
226
227 // misc ----------------------------------------
228 bool g_bHelp = false;
229 //++timo FIXME: used to close the plugin window if InitTexView fails
230 // it's dirty, only use is to prevent infinite loop in DialogProc
231 bool g_bClosing = false;
232
233 const char *PLUGIN_ABOUT = "Texture Tools for Radiant\n\n"
234                                                    "Gtk port by Leonardo Zide (leo@lokigames.com)\n"
235                                                    "Original version by Timothee \"TTimo\" Besset (timo@qeradiant.com)";
236
237 extern "C" void* WINAPI QERPlug_GetFuncTable(){
238         return &g_FuncTable;
239 }
240
241 const char* QERPlug_Init( void* hApp, void *pWidget ){
242         int size;
243         GtkWidget* pMainWidget = static_cast<GtkWidget*>( pWidget );
244
245         g_pMainWnd = pMainWidget;
246         size = (int)( (winding_t *)0 )->points[MAX_POINTS_ON_WINDING];
247         g_pSelectedFaceWinding = (winding_t *)malloc( size );
248         memset( g_pSelectedFaceWinding, 0, size );
249         return "Texture tools for Radiant";
250 }
251
252 const char* QERPlug_GetName(){
253         return (char*)PLUGIN_NAME;
254 }
255
256 const char* QERPlug_GetCommandList(){
257         return PLUGIN_COMMANDS;
258 }
259
260 char *TranslateString( char *buf ){
261         static char buf2[32768];
262         int i, l;
263         char  *out;
264
265         l = strlen( buf );
266         out = buf2;
267         for ( i = 0 ; i < l ; i++ )
268         {
269                 if ( buf[i] == '\n' ) {
270                         *out++ = '\r';
271                         *out++ = '\n';
272                 }
273                 else{
274                         *out++ = buf[i];
275                 }
276         }
277         *out++ = 0;
278
279         return buf2;
280 }
281
282 // called by InitTexView to fit the view against the bounding box of control points
283 void FitView( IWindow* hwndDlg, int TexSize[2] ){
284         // apply a ratio to get the area we'll draw
285         g_2DView.m_Center[0] = 0.5f * ( g_2DView.m_Mins[0] + g_2DView.m_Maxs[0] );
286         g_2DView.m_Center[1] = 0.5f * ( g_2DView.m_Mins[1] + g_2DView.m_Maxs[1] );
287         g_2DView.m_Mins[0] = g_2DView.m_Center[0] + g_ViewportRatio * ( g_2DView.m_Mins[0] - g_2DView.m_Center[0] );
288         g_2DView.m_Mins[1] = g_2DView.m_Center[1] + g_ViewportRatio * ( g_2DView.m_Mins[1] - g_2DView.m_Center[1] );
289         g_2DView.m_Maxs[0] = g_2DView.m_Center[0] + g_ViewportRatio * ( g_2DView.m_Maxs[0] - g_2DView.m_Center[0] );
290         g_2DView.m_Maxs[1] = g_2DView.m_Center[1] + g_ViewportRatio * ( g_2DView.m_Maxs[1] - g_2DView.m_Center[1] );
291
292         g_2DView.m_rect.left = 0;
293         g_2DView.m_rect.top = 0;
294         g_2DView.m_rect.bottom = hwndDlg->getHeight();
295         g_2DView.m_rect.right = hwndDlg->getWidth();
296
297         // we need to draw this area, now compute a bigger area so the texture scale is the same along X and Y
298         // compute box shape in XY space, let's say X <-> S we'll get a ratio for Y:
299         if ( !g_bPatch ) {
300                 g_SelectedFaceTable.m_pfnGetTextureSize( 0, TexSize );
301         }
302         else
303         {
304                 TexSize[0] = g_pPatch->d_texture->width;
305                 TexSize[1] = g_pPatch->d_texture->height;
306         }
307         // we want a texture with the same X / Y ratio
308         // compute XY space / window size ratio
309         float SSize = (float)fabs( g_2DView.m_Maxs[0] - g_2DView.m_Mins[0] );
310         float TSize = (float)fabs( g_2DView.m_Maxs[1] - g_2DView.m_Mins[1] );
311         float XSize = TexSize[0] * SSize;
312         float YSize = TexSize[1] * TSize;
313         float RatioX = XSize / (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right );
314         float RatioY = YSize / (float)abs( g_2DView.m_rect.top - g_2DView.m_rect.bottom );
315         if ( RatioX > RatioY ) {
316                 YSize = (float)abs( g_2DView.m_rect.top - g_2DView.m_rect.bottom ) * RatioX;
317                 TSize = YSize / (float)TexSize[1];
318         }
319         else
320         {
321                 XSize = (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right ) * RatioY;
322                 SSize = XSize / (float)TexSize[0];
323         }
324         g_2DView.m_Mins[0] = g_2DView.m_Center[0] - 0.5f * SSize;
325         g_2DView.m_Maxs[0] = g_2DView.m_Center[0] + 0.5f * SSize;
326         g_2DView.m_Mins[1] = g_2DView.m_Center[1] - 0.5f * TSize;
327         g_2DView.m_Maxs[1] = g_2DView.m_Center[1] + 0.5f * TSize;
328 }
329
330 // call this one each time we need to re-init
331 //++timo TODO: re-init objects state, g_2DView and g_ControlPointsManager
332 void InitTexView( IWindow* hwndDlg ){
333         // size of the texture we are working on
334         int TexSize[2];
335         g_bTexViewReady = false;
336         if ( g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 0 ) {
337                 g_SelectedFaceTable.m_pfnGetFaceInfo( 0, &g_SelectedFaceData, g_pSelectedFaceWinding );
338                 g_bPatch = false;
339                 int i;
340                 // we have something selected
341                 // setup: compute BBox for the winding ( in ST space )
342                 //++timo FIXME: move this in a C2DView member ? used as well for patches
343                 g_2DView.m_Mins[0] = +9999.0f; g_2DView.m_Mins[1] = +9999.0f;
344                 g_2DView.m_Maxs[0] = -9999.0f; g_2DView.m_Maxs[1] = -9999.0f;
345                 for ( i = 0; i < g_pSelectedFaceWinding->numpoints; i++ )
346                 {
347                         if ( g_pSelectedFaceWinding->points[i][3] < g_2DView.m_Mins[0] ) {
348                                 g_2DView.m_Mins[0] = g_pSelectedFaceWinding->points[i][3];
349                         }
350                         if ( g_pSelectedFaceWinding->points[i][3] > g_2DView.m_Maxs[0] ) {
351                                 g_2DView.m_Maxs[0] = g_pSelectedFaceWinding->points[i][3];
352                         }
353                         if ( g_pSelectedFaceWinding->points[i][4] < g_2DView.m_Mins[1] ) {
354                                 g_2DView.m_Mins[1] = g_pSelectedFaceWinding->points[i][4];
355                         }
356                         if ( g_pSelectedFaceWinding->points[i][4] > g_2DView.m_Maxs[1] ) {
357                                 g_2DView.m_Maxs[1] = g_pSelectedFaceWinding->points[i][4];
358                         }
359                 }
360                 // NOTE: FitView will read and init TexSize
361                 FitView( hwndDlg, TexSize );
362                 // now init the work tables
363                 g_NumPoints = g_pSelectedFaceWinding->numpoints;
364                 for ( i = 0; i < g_NumPoints; i++ )
365                 {
366                         g_WorkWinding.data[i][0] = g_pSelectedFaceWinding->points[i][3];
367                         g_WorkWinding.data[i][1] = g_pSelectedFaceWinding->points[i][4];
368                 }
369                 g_ControlPointsBFace.Init( g_NumPoints, &g_WorkWinding, &g_2DView, TexSize, &g_SelectedFaceData, &g_QglTable );
370                 // init snap-to-grid
371                 float fTexStep[2];
372                 fTexStep[0] = 1.0f / float(TexSize[0]);
373                 fTexStep[1] = 1.0f / float(TexSize[1]);
374                 g_2DView.SetGrid( fTexStep[0], fTexStep[1] );
375                 g_pManager = &g_ControlPointsBFace;
376                 // prepare the "Cancel" data
377                 memcpy( &g_CancelFaceData, &g_SelectedFaceData, sizeof( _QERFaceData ) );
378                 // we are done
379                 g_bTexViewReady = true;
380         }
381         else if ( g_SurfaceTable.m_pfnAnyPatchesSelected() ) {
382                 g_pPatch = g_SurfaceTable.m_pfnGetSelectedPatch();
383                 g_bPatch = true;
384                 int i,j;
385                 // compute BBox for all patch points
386                 g_2DView.m_Mins[0] = +9999.0f; g_2DView.m_Mins[1] = +9999.0f;
387                 g_2DView.m_Maxs[0] = -9999.0f; g_2DView.m_Maxs[1] = -9999.0f;
388                 for ( i = 0; i < g_pPatch->width; i++ )
389                 {
390                         for ( j = 0; j < g_pPatch->height; j++ )
391                         {
392                                 if ( g_pPatch->ctrl[i][j].st[0] < g_2DView.m_Mins[0] ) {
393                                         g_2DView.m_Mins[0] = g_pPatch->ctrl[i][j].st[0];
394                                 }
395                                 if ( g_pPatch->ctrl[i][j].st[0] > g_2DView.m_Maxs[0] ) {
396                                         g_2DView.m_Maxs[0] = g_pPatch->ctrl[i][j].st[0];
397                                 }
398                                 if ( g_pPatch->ctrl[i][j].st[1] < g_2DView.m_Mins[1] ) {
399                                         g_2DView.m_Mins[1] = g_pPatch->ctrl[i][j].st[1];
400                                 }
401                                 if ( g_pPatch->ctrl[i][j].st[1] > g_2DView.m_Maxs[1] ) {
402                                         g_2DView.m_Maxs[1] = g_pPatch->ctrl[i][j].st[1];
403                                 }
404                         }
405                 }
406                 FitView( hwndDlg, TexSize );
407                 // init the work tables
408                 g_WorkPatch = *g_pPatch;
409                 g_ControlPointsPatch.Init( &g_WorkPatch, &g_2DView, &g_QglTable, g_pPatch );
410                 // init snap-to-grid
411                 float fTexStep[2];
412                 fTexStep[0] = 1.0f / float(TexSize[0]);
413                 fTexStep[1] = 1.0f / float(TexSize[1]);
414                 g_2DView.SetGrid( fTexStep[0], fTexStep[1] );
415                 g_pManager = &g_ControlPointsPatch;
416                 // prepare the "cancel" data
417                 g_CancelPatch = *g_pPatch;
418                 // we are done
419                 g_bTexViewReady = true;
420         }
421 }
422
423 void Textool_Validate(){
424         // validate current situation into the main view
425         g_pManager->Commit();
426         // for a brush face we have an aditionnal step
427         if ( !g_bPatch ) {
428                 // tell Radiant to update (will also send update windows messages )
429                 g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_SelectedFaceData );
430         }
431         else
432         {
433                 // ask to rebuild the patch display data
434                 g_pPatch->bDirty = true;
435                 // send a repaint to the camera window as well
436                 g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
437         }
438         // we'll need to update after that as well:
439         g_bTexViewReady = false;
440         // send a repaint message
441         g_pToolWnd->Redraw();
442 }
443
444 void Textool_Cancel(){
445         if ( !g_bPatch ) {
446                 // tell Radiant to update (will also send update windows messages )
447                 g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_CancelFaceData );
448         }
449         else
450         {
451                 *g_pPatch = g_CancelPatch;
452                 g_pPatch->bDirty = true;
453                 g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
454         }
455         // do not call destroy, decref it
456         g_pToolWnd->DecRef();
457         g_pToolWnd = NULL;
458 }
459
460 static void DoExpose(){
461         int i,j;
462
463         g_2DView.PreparePaint();
464         g_QglTable.m_pfn_qglColor3f( 1, 1, 1 );
465         // draw the texture background
466         g_QglTable.m_pfn_qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
467         if ( !g_bPatch ) {
468                 g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_SelectedFaceTable.m_pfnGetTextureNumber( 0 ) );
469         }
470         else
471         {
472                 g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_pPatch->d_texture->texture_number );
473         }
474
475         g_QglTable.m_pfn_qglEnable( GL_TEXTURE_2D );
476         g_QglTable.m_pfn_qglBegin( GL_QUADS );
477         g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Mins[0], g_2DView.m_Mins[1] );
478         g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Mins[0], g_2DView.m_Mins[1] );
479         g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Maxs[0], g_2DView.m_Mins[1] );
480         g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Maxs[0], g_2DView.m_Mins[1] );
481         g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Maxs[0], g_2DView.m_Maxs[1] );
482         g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Maxs[0], g_2DView.m_Maxs[1] );
483         g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Mins[0], g_2DView.m_Maxs[1] );
484         g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Mins[0], g_2DView.m_Maxs[1] );
485         g_QglTable.m_pfn_qglEnd();
486         g_QglTable.m_pfn_qglDisable( GL_TEXTURE_2D );
487
488         if ( !g_bPatch ) {
489                 g_QglTable.m_pfn_qglBegin( GL_LINE_LOOP );
490                 for ( i = 0; i < g_NumPoints; i++ )
491                 {
492                         g_QglTable.m_pfn_qglVertex2f( g_WorkWinding.data[i][0], g_WorkWinding.data[i][1] );
493                 }
494                 g_QglTable.m_pfn_qglEnd();
495         }
496         else
497         {
498                 g_QglTable.m_pfn_qglBegin( GL_LINES );
499                 for ( i = 0; i < g_pPatch->width; i++ )
500                         for ( j = 0; j < g_pPatch->height; j++ )
501                         {
502                                 if ( i < g_pPatch->width - 1 ) {
503                                         g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j].st[0], g_WorkPatch.ctrl[i][j].st[1] );
504                                         g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i + 1][j].st[0], g_WorkPatch.ctrl[i + 1][j].st[1] );
505                                 }
506
507                                 if ( j < g_pPatch->height - 1 ) {
508                                         g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j].st[0], g_WorkPatch.ctrl[i][j].st[1] );
509                                         g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j + 1].st[0], g_WorkPatch.ctrl[i][j + 1].st[1] );
510                                 }
511                         }
512                 g_QglTable.m_pfn_qglEnd();
513         }
514
515         // let the control points manager render
516         g_pManager->Render();
517 }
518
519 static bool CanProcess(){
520         if ( !g_bTexViewReady && !g_bClosing ) {
521                 InitTexView( g_pToolWnd );
522
523                 if ( !g_bTexViewReady ) {
524                         g_bClosing = true;
525                         DoMessageBox( "You must have brush primitives activated in your project settings and\n"
526                                                   "have a patch or a single face selected to use the TexTool plugin.\n"
527                                                   "See plugins/TexToolHelp for documentation.", "TexTool plugin", MB_ICONERROR | MB_OK );
528                         // decref, this will destroy
529                         g_pToolWnd->DecRef();
530                         g_pToolWnd = NULL;
531                         return 0;
532                 }
533                 else{
534                         g_bClosing = false;
535                 }
536         }
537         else if ( !g_bTexViewReady && g_bClosing ) {
538                 return 0;
539         }
540
541         return 1;
542 }
543
544 #if 0
545 static void button_press( GtkWidget *widget, GdkEventButton *event, gpointer data ){
546         if ( CanProcess() ) {
547                 switch ( event->button )
548                 {
549                 case 1:
550                         g_pManager->OnLButtonDown( event->x, event->y ); break;
551                 case 3:
552                         g_2DView.OnRButtonDown( event->x, event->y ); break;
553                 }
554         }
555 }
556
557 static void button_release( GtkWidget *widget, GdkEventButton *event, gpointer data ){
558         if ( CanProcess() ) {
559                 switch ( event->button )
560                 {
561                 case 1:
562                         g_pManager->OnLButtonUp( event->x, event->y ); break;
563                 case 3:
564                         g_2DView.OnRButtonUp( event->x, event->y ); break;
565                 }
566         }
567 }
568
569 static void motion( GtkWidget *widget, GdkEventMotion *event, gpointer data ){
570         if ( CanProcess() ) {
571                 if ( g_2DView.OnMouseMove( event->x, event->y ) ) {
572                         return;
573                 }
574
575                 if ( g_pManager->OnMouseMove( event->x, event->y ) ) {
576                         return;
577                 }
578         }
579 }
580
581 static gint expose( GtkWidget *widget, GdkEventExpose *event, gpointer data ){
582         if ( event->count > 0 ) {
583                 return TRUE;
584         }
585
586         if ( !CanProcess() ) {
587                 return TRUE;
588         }
589
590         if ( g_bTexViewReady ) {
591                 g_2DView.m_rect.bottom = widget->allocation.height;
592                 g_2DView.m_rect.right = widget->allocation.width;
593
594                 if ( !g_QglTable.m_pfn_glwidget_make_current( g_pToolWidget ) ) {
595                         Sys_Printf( "TexTool: glMakeCurrent failed\n" );
596                         return TRUE;
597                 }
598
599                 DoExpose();
600
601                 g_QglTable.m_pfn_glwidget_swap_buffers( g_pToolWidget );
602         }
603
604         return TRUE;
605 }
606
607 static gint keypress( GtkWidget* widget, GdkEventKey* event, gpointer data ){
608         unsigned int code = gdk_keyval_to_upper( event->keyval );
609
610         if ( code == GDK_Escape ) {
611                 gtk_widget_destroy( g_pToolWnd );
612                 g_pToolWnd = NULL;
613                 return TRUE;
614         }
615
616         if ( CanProcess() ) {
617                 if ( g_2DView.OnKeyDown( code ) ) {
618                         return FALSE;
619                 }
620
621                 if ( code == GDK_Return ) {
622                         Textool_Validate();
623                         return FALSE;
624                 }
625         }
626
627         return TRUE;
628 }
629
630 static gint close( GtkWidget *widget, GdkEvent* event, gpointer data ){
631         gtk_widget_destroy( widget );
632         g_pToolWnd = NULL;
633
634         return TRUE;
635 }
636
637 static GtkWidget* CreateOpenGLWidget(){
638         g_pToolWidget = g_QglTable.m_pfn_glwidget_new( FALSE, g_QglTable.m_pfn_GetQeglobalsGLWidget() );
639
640         gtk_widget_set_events( g_pToolWidget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
641                                                    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK );
642
643         // Connect signal handlers
644         gtk_signal_connect( GTK_OBJECT( g_pToolWidget ), "expose_event", GTK_SIGNAL_FUNC( expose ), NULL );
645         gtk_signal_connect( GTK_OBJECT( g_pToolWidget ), "motion_notify_event",
646                                                 GTK_SIGNAL_FUNC( motion ), NULL );
647         gtk_signal_connect( GTK_OBJECT( g_pToolWidget ), "button_press_event",
648                                                 GTK_SIGNAL_FUNC( button_press ), NULL );
649         gtk_signal_connect( GTK_OBJECT( g_pToolWidget ), "button_release_event",
650                                                 GTK_SIGNAL_FUNC( button_release ), NULL );
651
652         gtk_signal_connect( GTK_OBJECT( g_pToolWnd ), "delete_event", GTK_SIGNAL_FUNC( close ), NULL );
653         gtk_signal_connect( GTK_OBJECT( g_pToolWnd ), "key_press_event",
654                                                 GTK_SIGNAL_FUNC( keypress ), NULL );
655
656         return g_pToolWidget;
657 }
658 #endif
659
660 #if 0
661 static void DoPaint(){
662         if ( !CanProcess() ) {
663                 return;
664         }
665
666         if ( g_bTexViewReady ) {
667                 g_2DView.m_rect.bottom = g_pToolWnd->getHeight();
668                 g_2DView.m_rect.right = g_pToolWnd->getWidth();
669
670                 // set GL_PROJECTION
671                 g_2DView.PreparePaint();
672                 // render the objects
673                 // the master is not rendered the same way, draw over a unified texture background
674                 g_2DView.TextureBackground( g_DrawObjects[0].pObject->getTextureNumber() );
675                 if ( g_nDrawObjects >= 1 ) {
676                         int i;
677                         for ( i = 1; i < g_nDrawObjects; i++ )
678                         {
679                                 // we use a first step to the GL_MODELVIEW for the master object
680                                 // GL_MODELVIEW will be altered in RenderAuxiliary too
681                                 g_DrawObjects[0].pObject->PrepareModelView( g_DrawObjects[i].pTopo );
682                                 g_DrawObjects[i].pObject->RenderAuxiliary();
683                         }
684                 }
685                 // draw the polygon outline and control points
686                 g_DrawObjects[0].pObject->PrepareModelView( NULL );
687                 g_DrawObjects[0].pObject->RenderUI();
688         }
689 }
690 #endif
691
692 bool CWindowListener::OnLButtonDown( guint32 nFlags, double x, double y ){
693         if ( CanProcess() ) {
694                 g_pManager->OnLButtonDown( (int)x, (int)y );
695                 return true;
696         }
697         return false;
698 }
699
700 bool CWindowListener::OnRButtonDown( guint32 nFlags, double x, double y ){
701         if ( CanProcess() ) {
702                 g_2DView.OnRButtonDown( (int)x, (int)y );
703                 return true;
704         }
705         return false;
706 }
707
708 bool CWindowListener::OnLButtonUp( guint32 nFlags, double x, double y ){
709         if ( CanProcess() ) {
710                 g_pManager->OnLButtonUp( (int)x, (int)y );
711                 return true;
712         }
713         return false;
714 }
715
716 bool CWindowListener::OnRButtonUp( guint32 nFlags, double x, double y ){
717         if ( CanProcess() ) {
718                 g_2DView.OnRButtonUp( (int)x, (int)y );
719                 return true;
720         }
721         return false;
722 }
723
724 bool CWindowListener::OnMouseMove( guint32 nFlags, double x, double y ){
725         if ( CanProcess() ) {
726                 if ( g_2DView.OnMouseMove( (int)x, (int)y ) ) {
727                         return true;
728                 }
729
730                 g_pManager->OnMouseMove( (int)x, (int)y );
731                 return true;
732         }
733         return false;
734 }
735
736 // the widget is closing
737 void CWindowListener::Close(){
738         g_pToolWnd = NULL;
739 }
740
741 bool CWindowListener::Paint(){
742         if ( !CanProcess() ) {
743                 return false;
744         }
745
746         if ( g_bTexViewReady ) {
747                 DoExpose();
748         }
749
750         return true;
751 }
752
753 bool CWindowListener::OnKeyPressed( char *s ){
754         if ( !strcmp( s,"Escape" ) ) {
755                 Textool_Cancel();
756                 return TRUE;
757         }
758         if ( CanProcess() ) {
759                 if ( g_2DView.OnKeyDown( s ) ) {
760                         return TRUE;
761                 }
762
763                 if ( !strcmp( s,"Return" ) ) {
764                         Textool_Validate();
765                         return TRUE;
766                 }
767         }
768         return FALSE;
769 }
770
771 extern "C" void QERPlug_Dispatch( const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush ){
772   #if 0
773         // if it's the first call, perhaps we need some additional init steps
774         if ( !g_bQglInitDone ) {
775                 g_QglTable.m_nSize = sizeof( _QERQglTable );
776                 if ( g_FuncTable.m_pfnRequestInterface( QERQglTable_GUID, static_cast<LPVOID>( &g_QglTable ) ) ) {
777                         g_bQglInitDone = true;
778                 }
779                 else
780                 {
781                         Sys_Printf( "TexTool plugin: _QERQglTable interface request failed\n" );
782                         return;
783                 }
784         }
785
786         if ( !g_bSelectedFaceInitDone ) {
787                 g_SelectedFaceTable.m_nSize = sizeof( _QERSelectedFaceTable );
788                 if ( g_FuncTable.m_pfnRequestInterface( QERSelectedFaceTable_GUID,
789                                                                                                 static_cast<LPVOID>( &g_SelectedFaceTable ) ) ) {
790                         g_bSelectedFaceInitDone = true;
791                 }
792                 else
793                 {
794                         Sys_Printf( "TexTool plugin: _QERSelectedFaceTable interface request failed\n" );
795                         return;
796                 }
797         }
798
799         if ( !g_bSurfaceTableInitDone ) {
800                 g_SurfaceTable.m_nSize = sizeof( _QERAppSurfaceTable );
801                 if ( g_FuncTable.m_pfnRequestInterface( QERAppSurfaceTable_GUID, static_cast<LPVOID>( &g_SurfaceTable ) ) ) {
802                         g_bSurfaceTableInitDone = true;
803                 }
804                 else
805                 {
806                         Sys_Printf( "TexTool plugin: _QERAppSurfaceTable interface request failed\n" );
807                         return;
808                 }
809         }
810
811         if ( !g_bUITable ) {
812                 g_UITable.m_nSize = sizeof( _QERUITable );
813                 if ( g_FuncTable.m_pfnRequestInterface( QERUI_GUID, static_cast<LPVOID>( &g_UITable ) ) ) {
814                         g_bUITable = true;
815                 }
816                 else
817                 {
818                         Sys_Printf( "TexTool plugin: _QERUITable interface request failed\n" );
819                         return;
820                 }
821         }
822   #endif
823
824         if ( !strcmp( p, "About..." ) ) {
825                 DoMessageBox( PLUGIN_ABOUT, "About ...", MB_OK );
826         }
827         else if ( !strcmp( p, "Go..." ) ) {
828                 if ( !g_pToolWnd ) {
829                         g_pToolWnd = g_UITable.m_pfnCreateGLWindow();
830                         g_pToolWnd->setSizeParm( 300,300 );
831                         g_pToolWnd->setName( "TexTool" );
832                         // g_Listener is a static class, we need to bump the refCount to avoid premature release problems
833                         g_Listen.IncRef();
834                         // setListener will incRef on the listener too
835                         g_pToolWnd->setListener( &g_Listen );
836                         if ( !g_pToolWnd->Show() ) {
837                                 DoMessageBox( "Error creating texture tools window!", "TexTool plugin", MB_ICONERROR | MB_OK );
838                                 return;
839                         }
840                 }
841
842                 g_bTexViewReady = false;
843                 g_bClosing = false;
844         }
845         else if ( !strcmp( p, "Help..." ) ) {
846                 if ( !g_bHelp ) {
847                         DoMessageBox( "Select a brush face (ctrl+shift+left mouse) or a patch, and hit Go...\n"
848                                                   "See tutorials for more", "TexTool plugin", MB_OK );
849                 }
850                 else{
851                         DoMessageBox( "Are you kidding me ?", "TexTool plugin", MB_OK );
852                 }
853                 g_bHelp = true;
854         }
855 }
856
857 // =============================================================================
858 // SYNAPSE
859
860 CSynapseServer* g_pSynapseServer = NULL;
861 CSynapseClientTexTool g_SynapseClient;
862
863 #if __GNUC__ >= 4
864 #pragma GCC visibility push(default)
865 #endif
866 extern "C" CSynapseClient * SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ) {
867 #if __GNUC__ >= 4
868 #pragma GCC visibility pop
869 #endif
870         if ( strcmp( version, SYNAPSE_VERSION ) ) {
871                 Syn_Printf( "ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version );
872                 return NULL;
873         }
874         g_pSynapseServer = pServer;
875         g_pSynapseServer->IncRef();
876         Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() );
877
878         g_SynapseClient.AddAPI( PLUGIN_MAJOR, "textool", sizeof( _QERPluginTable ) );
879         g_SynapseClient.AddAPI( RADIANT_MAJOR, NULL, sizeof( g_FuncTable ), SYN_REQUIRE, &g_FuncTable );
880         g_SynapseClient.AddAPI( QGL_MAJOR, NULL, sizeof( g_QglTable ), SYN_REQUIRE, &g_QglTable );
881         g_SynapseClient.AddAPI( SELECTEDFACE_MAJOR, NULL, sizeof( g_SelectedFaceTable ), SYN_REQUIRE, &g_SelectedFaceTable );
882         g_SynapseClient.AddAPI( UI_MAJOR, NULL, sizeof( _QERUITable ), SYN_REQUIRE, &g_UITable );
883         g_SynapseClient.AddAPI( APPSURFACEDIALOG_MAJOR, NULL, sizeof( _QERAppSurfaceTable ), SYN_REQUIRE, &g_SurfaceTable );
884
885         return &g_SynapseClient;
886 }
887
888 bool CSynapseClientTexTool::RequestAPI( APIDescriptor_t *pAPI ){
889         if ( !strcmp( pAPI->major_name, PLUGIN_MAJOR ) ) {
890                 _QERPluginTable *pTable = static_cast<_QERPluginTable*>( pAPI->mpTable );
891                 pTable->m_pfnQERPlug_Init = QERPlug_Init;
892                 pTable->m_pfnQERPlug_GetName = QERPlug_GetName;
893                 pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList;
894                 pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch;
895                 return true;
896         }
897
898         Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );
899         return false;
900 }
901
902 #include "version.h"
903
904 const char* CSynapseClientTexTool::GetInfo(){
905         return "Texture Tools plugin built " __DATE__ " " RADIANT_VERSION;
906 }