2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\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
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
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
22 //-----------------------------------------------------------------------------
\r
25 // main plugin implementation
\r
26 // texturing tools for Radiant
\r
31 static void dialog_button_callback (GtkWidget *widget, gpointer data)
\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
44 static gint dialog_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
\r
48 gtk_widget_hide (widget);
\r
49 loop = (int*)g_object_get_data (G_OBJECT (widget), "loop");
\r
55 int DoMessageBox (const char* lpText, const char* lpCaption, guint32 uType)
\r
57 GtkWidget *window, *w, *vbox, *hbox;
\r
58 int mode = (uType & MB_TYPEMASK), ret, loop = 1;
\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
71 vbox = gtk_vbox_new (FALSE, 10);
\r
72 gtk_container_add (GTK_CONTAINER (window), vbox);
\r
73 gtk_widget_show (vbox);
\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
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
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
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
99 else if (mode == MB_OKCANCEL)
\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
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
116 else if (mode == MB_YESNOCANCEL)
\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
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
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
139 else /* if (mode == MB_YESNO) */
\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
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
157 gtk_widget_show (window);
\r
158 gtk_grab_add (window);
\r
161 gtk_main_iteration ();
\r
163 gtk_grab_remove (window);
\r
164 gtk_widget_destroy (window);
\r
169 // Radiant function table
\r
170 _QERFuncTable_1 g_FuncTable;
\r
173 const char *PLUGIN_NAME = "Q3 Texture Tools";
\r
175 // commands in the menu
\r
176 const char *PLUGIN_COMMANDS = "About...;Go...";
\r
178 // cast to GtkWidget*
\r
180 IWindow *g_pToolWnd = NULL; // handle to the window
\r
181 CWindowListener g_Listen;
\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
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
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
205 CtrlPts_t g_WorkWinding;
\r
206 // reference _QERFaceData we use on Cancel, and for Commit
\r
207 _QERFaceData g_CancelFaceData;
\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
223 // ---------------------------------------------
\r
224 // holds the manager we are currently using
\r
225 CControlPointsManager *g_pManager = NULL;
\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
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
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
243 extern "C" void* WINAPI QERPlug_GetFuncTable ()
\r
245 return &g_FuncTable;
\r
248 const char* QERPlug_Init (void* hApp, void *pWidget)
\r
251 GtkWidget* pMainWidget = static_cast<GtkWidget*>(pWidget);
\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
262 const char* QERPlug_GetName()
\r
264 return (char*)PLUGIN_NAME;
\r
267 const char* QERPlug_GetCommandList()
\r
269 return PLUGIN_COMMANDS;
\r
272 char *TranslateString (char *buf)
\r
274 static char buf2[32768];
\r
280 for (i=0 ; i<l ; i++)
\r
282 if (buf[i] == '\n')
\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
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
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
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
315 g_SelectedFaceTable.m_pfnGetTextureSize( 0, TexSize );
\r
319 TexSize[0] = g_pPatch->d_texture->width;
\r
320 TexSize[1] = g_pPatch->d_texture->height;
\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
332 YSize = (float)abs( g_2DView.m_rect.top - g_2DView.m_rect.bottom ) * RatioX;
\r
333 TSize = YSize / (float)TexSize[1];
\r
337 XSize = (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right ) * RatioY;
\r
338 SSize = XSize / (float)TexSize[0];
\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
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
350 // size of the texture we are working on
\r
352 g_bTexViewReady = false;
\r
353 if (g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 0)
\r
355 g_SelectedFaceTable.m_pfnGetFaceInfo( 0, &g_SelectedFaceData, g_pSelectedFaceWinding );
\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
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
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
380 g_WorkWinding.data[i][0] = g_pSelectedFaceWinding->points[i][3];
\r
381 g_WorkWinding.data[i][1] = g_pSelectedFaceWinding->points[i][4];
\r
383 g_ControlPointsBFace.Init( g_NumPoints, &g_WorkWinding, &g_2DView, TexSize, &g_SelectedFaceData, &g_QglTable );
\r
384 // init snap-to-grid
\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
393 g_bTexViewReady = true;
\r
395 else if ( g_SurfaceTable.m_pfnAnyPatchesSelected())
\r
397 g_pPatch = g_SurfaceTable.m_pfnGetSelectedPatch();
\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
405 for ( j=0; j<g_pPatch->height; j++ )
\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
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
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
430 g_bTexViewReady = true;
\r
434 void Textool_Validate()
\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
441 // tell Radiant to update (will also send update windows messages )
\r
442 g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_SelectedFaceData );
\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
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
457 void Textool_Cancel()
\r
461 // tell Radiant to update (will also send update windows messages )
\r
462 g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_CancelFaceData );
\r
466 *g_pPatch = g_CancelPatch;
\r
467 g_pPatch->bDirty = true;
\r
468 g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
\r
470 // do not call destroy, decref it
\r
471 g_pToolWnd->DecRef();
\r
475 static void DoExpose ()
\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
485 g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_SelectedFaceTable.m_pfnGetTextureNumber(0) );
\r
489 g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_pPatch->d_texture->texture_number );
\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
507 g_QglTable.m_pfn_qglBegin( GL_LINE_LOOP );
\r
508 for ( i=0; i<g_NumPoints; i++ )
\r
510 g_QglTable.m_pfn_qglVertex2f( g_WorkWinding.data[i][0], g_WorkWinding.data[i][1] );
\r
512 g_QglTable.m_pfn_qglEnd();
\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
520 if ( i < g_pPatch->width-1 )
\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
526 if ( j < g_pPatch->height-1 )
\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
532 g_QglTable.m_pfn_qglEnd();
\r
535 // let the control points manager render
\r
536 g_pManager->Render( );
\r
539 static bool CanProcess ()
\r
541 if (!g_bTexViewReady && !g_bClosing)
\r
543 InitTexView (g_pToolWnd);
\r
545 if (!g_bTexViewReady)
\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
557 g_bClosing = false;
\r
559 else if (!g_bTexViewReady && g_bClosing)
\r
568 static void button_press (GtkWidget *widget, GdkEventButton *event, gpointer data)
\r
572 switch (event->button)
\r
575 g_pManager->OnLButtonDown (event->x, event->y); break;
\r
577 g_2DView.OnRButtonDown (event->x, event->y); break;
\r
582 static void button_release (GtkWidget *widget, GdkEventButton *event, gpointer data)
\r
586 switch (event->button)
\r
589 g_pManager->OnLButtonUp (event->x, event->y); break;
\r
591 g_2DView.OnRButtonUp (event->x, event->y); break;
\r
596 static void motion (GtkWidget *widget, GdkEventMotion *event, gpointer data)
\r
600 if (g_2DView.OnMouseMove (event->x, event->y))
\r
603 if (g_pManager->OnMouseMove (event->x, event->y))
\r
608 static gint expose (GtkWidget *widget, GdkEventExpose *event, gpointer data)
\r
610 if (event->count > 0)
\r
613 if (!CanProcess ())
\r
616 if (g_bTexViewReady)
\r
618 g_2DView.m_rect.bottom = widget->allocation.height;
\r
619 g_2DView.m_rect.right = widget->allocation.width;
\r
621 if (!g_QglTable.m_pfn_glwidget_make_current (g_pToolWidget))
\r
623 Sys_Printf("TexTool: glMakeCurrent failed\n");
\r
629 g_QglTable.m_pfn_glwidget_swap_buffers (g_pToolWidget);
\r
635 static gint keypress (GtkWidget* widget, GdkEventKey* event, gpointer data)
\r
637 unsigned int code = gdk_keyval_to_upper(event->keyval);
\r
639 if (code == GDK_Escape)
\r
641 gtk_widget_destroy (g_pToolWnd);
\r
648 if (g_2DView.OnKeyDown (code))
\r
651 if (code == GDK_Return)
\r
653 Textool_Validate();
\r
661 static gint close (GtkWidget *widget, GdkEvent* event, gpointer data)
\r
663 gtk_widget_destroy (widget);
\r
669 static GtkWidget* CreateOpenGLWidget ()
\r
671 g_pToolWidget = g_QglTable.m_pfn_glwidget_new (FALSE, g_QglTable.m_pfn_GetQeglobalsGLWidget ());
\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
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
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
689 return g_pToolWidget;
\r
694 static void DoPaint ()
\r
696 if (!CanProcess ())
\r
699 if (g_bTexViewReady)
\r
701 g_2DView.m_rect.bottom = g_pToolWnd->getHeight();
\r
702 g_2DView.m_rect.right = g_pToolWnd->getWidth();
\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
712 for (i=1;i<g_nDrawObjects;i++)
\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
720 // draw the polygon outline and control points
\r
721 g_DrawObjects[0].pObject->PrepareModelView(NULL);
\r
722 g_DrawObjects[0].pObject->RenderUI();
\r
727 bool CWindowListener::OnLButtonDown(guint32 nFlags, double x, double y)
\r
731 g_pManager->OnLButtonDown((int)x, (int)y);
\r
737 bool CWindowListener::OnRButtonDown(guint32 nFlags, double x, double y)
\r
741 g_2DView.OnRButtonDown ((int)x, (int)y);
\r
747 bool CWindowListener::OnLButtonUp(guint32 nFlags, double x, double y)
\r
751 g_pManager->OnLButtonUp((int)x, (int)y);
\r
757 bool CWindowListener::OnRButtonUp(guint32 nFlags, double x, double y)
\r
761 g_2DView.OnRButtonUp ((int)x, (int)y);
\r
767 bool CWindowListener::OnMouseMove(guint32 nFlags, double x, double y)
\r
771 if (g_2DView.OnMouseMove ((int)x, (int)y))
\r
774 g_pManager->OnMouseMove((int)x, (int)y);
\r
780 // the widget is closing
\r
781 void CWindowListener::Close()
\r
786 bool CWindowListener::Paint()
\r
788 if (!CanProcess ())
\r
791 if (g_bTexViewReady)
\r
797 bool CWindowListener::OnKeyPressed(char *s)
\r
799 if (!strcmp(s,"Escape"))
\r
806 if (g_2DView.OnKeyDown (s))
\r
809 if (!strcmp(s,"Return"))
\r
811 Textool_Validate();
\r
818 extern "C" void QERPlug_Dispatch(const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush)
\r
821 // if it's the first call, perhaps we need some additional init steps
\r
822 if (!g_bQglInitDone)
\r
824 g_QglTable.m_nSize = sizeof(_QERQglTable);
\r
825 if ( g_FuncTable.m_pfnRequestInterface( QERQglTable_GUID, static_cast<LPVOID>(&g_QglTable) ) )
\r
827 g_bQglInitDone = true;
\r
831 Sys_Printf("TexTool plugin: _QERQglTable interface request failed\n");
\r
836 if (!g_bSelectedFaceInitDone)
\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
842 g_bSelectedFaceInitDone = true;
\r
846 Sys_Printf("TexTool plugin: _QERSelectedFaceTable interface request failed\n");
\r
851 if (!g_bSurfaceTableInitDone)
\r
853 g_SurfaceTable.m_nSize = sizeof(_QERAppSurfaceTable);
\r
854 if ( g_FuncTable.m_pfnRequestInterface( QERAppSurfaceTable_GUID, static_cast<LPVOID>(&g_SurfaceTable) ) )
\r
856 g_bSurfaceTableInitDone = true;
\r
860 Sys_Printf("TexTool plugin: _QERAppSurfaceTable interface request failed\n");
\r
867 g_UITable.m_nSize = sizeof(_QERUITable);
\r
868 if ( g_FuncTable.m_pfnRequestInterface( QERUI_GUID, static_cast<LPVOID>(&g_UITable) ) )
\r
874 Sys_Printf("TexTool plugin: _QERUITable interface request failed\n");
\r
880 if (!strcmp(p, "About..."))
\r
882 DoMessageBox (PLUGIN_ABOUT, "About ...", MB_OK );
\r
884 else if (!strcmp(p, "Go..."))
\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
893 // setListener will incRef on the listener too
\r
894 g_pToolWnd->setListener(&g_Listen);
\r
895 if (!g_pToolWnd->Show())
\r
897 DoMessageBox ("Error creating texture tools window!", "TexTool plugin", MB_ICONERROR | MB_OK);
\r
902 g_bTexViewReady = false;
\r
903 g_bClosing = false;
\r
905 else if (!strcmp(p, "Help..."))
\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
911 DoMessageBox ("Are you kidding me ?", "TexTool plugin", MB_OK );
\r
916 // =============================================================================
\r
919 CSynapseServer* g_pSynapseServer = NULL;
\r
920 CSynapseClientTexTool g_SynapseClient;
\r
923 #pragma GCC visibility push(default)
\r
925 extern "C" CSynapseClient* SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ) {
\r
927 #pragma GCC visibility pop
\r
929 if (strcmp(version, SYNAPSE_VERSION))
\r
931 Syn_Printf("ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version);
\r
934 g_pSynapseServer = pServer;
\r
935 g_pSynapseServer->IncRef();
\r
936 Set_Syn_Printf(g_pSynapseServer->Get_Syn_Printf());
\r
938 g_SynapseClient.AddAPI(PLUGIN_MAJOR, "textool", sizeof(_QERPluginTable));
\r
939 g_SynapseClient.AddAPI(RADIANT_MAJOR, NULL, sizeof(g_FuncTable), SYN_REQUIRE, &g_FuncTable);
\r
940 g_SynapseClient.AddAPI(QGL_MAJOR, NULL, sizeof(g_QglTable), SYN_REQUIRE, &g_QglTable);
\r
941 g_SynapseClient.AddAPI(SELECTEDFACE_MAJOR, NULL, sizeof(g_SelectedFaceTable), SYN_REQUIRE, &g_SelectedFaceTable);
\r
943 return &g_SynapseClient;
\r
946 bool CSynapseClientTexTool::RequestAPI(APIDescriptor_t *pAPI)
\r
948 if (!strcmp(pAPI->major_name, PLUGIN_MAJOR))
\r
950 _QERPluginTable *pTable = static_cast<_QERPluginTable*>(pAPI->mpTable);
\r
951 pTable->m_pfnQERPlug_Init = QERPlug_Init;
\r
952 pTable->m_pfnQERPlug_GetName = QERPlug_GetName;
\r
953 pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList;
\r
954 pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch;
\r
958 Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());
\r
962 #include "version.h"
\r
964 const char* CSynapseClientTexTool::GetInfo()
\r
966 return "Texture Tools plugin built " __DATE__ " " RADIANT_VERSION;
\r