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