2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
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.
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.
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
22 //-----------------------------------------------------------------------------
25 // main plugin implementation
26 // texturing tools for Radiant
31 static void dialog_button_callback( GtkWidget *widget, gpointer data ){
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" );
43 static gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
46 gtk_widget_hide( widget );
47 loop = (int*)g_object_get_data( G_OBJECT( widget ), "loop" );
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;
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 );
68 vbox = gtk_vbox_new( FALSE, 10 );
69 gtk_container_add( GTK_CONTAINER( window ), vbox );
70 gtk_widget_show( vbox );
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 );
77 w = gtk_hseparator_new();
78 gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 2 );
81 hbox = gtk_hbox_new( FALSE, 10 );
82 gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 2 );
83 gtk_widget_show( hbox );
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 );
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 );
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 );
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 );
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 );
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 );
133 else /* if (mode == MB_YESNO) */
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 );
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 );
151 gtk_widget_show( window );
152 gtk_grab_add( window );
155 gtk_main_iteration();
157 gtk_grab_remove( window );
158 gtk_widget_destroy( window );
163 // Radiant function table
164 _QERFuncTable_1 g_FuncTable;
167 const char *PLUGIN_NAME = "Q3 Texture Tools";
169 // commands in the menu
170 const char *PLUGIN_COMMANDS = "About...;Go...";
172 // cast to GtkWidget*
174 IWindow *g_pToolWnd = NULL; // handle to the window
175 CWindowListener g_Listen;
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;
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
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
199 CtrlPts_t g_WorkWinding;
200 // reference _QERFaceData we use on Cancel, and for Commit
201 _QERFaceData g_CancelFaceData;
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;
217 // ---------------------------------------------
218 // holds the manager we are currently using
219 CControlPointsManager *g_pManager = NULL;
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;
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;
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)";
237 extern "C" void* WINAPI QERPlug_GetFuncTable(){
241 const char* QERPlug_Init( void* hApp, void *pWidget ){
243 GtkWidget* pMainWidget = static_cast<GtkWidget*>( pWidget );
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";
252 const char* QERPlug_GetName(){
253 return (char*)PLUGIN_NAME;
256 const char* QERPlug_GetCommandList(){
257 return PLUGIN_COMMANDS;
260 char *TranslateString( char *buf ){
261 static char buf2[32768];
267 for ( i = 0 ; i < l ; i++ )
269 if ( buf[i] == '\n' ) {
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] );
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();
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:
300 g_SelectedFaceTable.m_pfnGetTextureSize( 0, TexSize );
304 TexSize[0] = g_pPatch->d_texture->width;
305 TexSize[1] = g_pPatch->d_texture->height;
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];
321 XSize = (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right ) * RatioY;
322 SSize = XSize / (float)TexSize[0];
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;
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
335 g_bTexViewReady = false;
336 if ( g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 0 ) {
337 g_SelectedFaceTable.m_pfnGetFaceInfo( 0, &g_SelectedFaceData, g_pSelectedFaceWinding );
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++ )
347 if ( g_pSelectedFaceWinding->points[i][3] < g_2DView.m_Mins[0] ) {
348 g_2DView.m_Mins[0] = g_pSelectedFaceWinding->points[i][3];
350 if ( g_pSelectedFaceWinding->points[i][3] > g_2DView.m_Maxs[0] ) {
351 g_2DView.m_Maxs[0] = g_pSelectedFaceWinding->points[i][3];
353 if ( g_pSelectedFaceWinding->points[i][4] < g_2DView.m_Mins[1] ) {
354 g_2DView.m_Mins[1] = g_pSelectedFaceWinding->points[i][4];
356 if ( g_pSelectedFaceWinding->points[i][4] > g_2DView.m_Maxs[1] ) {
357 g_2DView.m_Maxs[1] = g_pSelectedFaceWinding->points[i][4];
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++ )
366 g_WorkWinding.data[i][0] = g_pSelectedFaceWinding->points[i][3];
367 g_WorkWinding.data[i][1] = g_pSelectedFaceWinding->points[i][4];
369 g_ControlPointsBFace.Init( g_NumPoints, &g_WorkWinding, &g_2DView, TexSize, &g_SelectedFaceData, &g_QglTable );
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 ) );
379 g_bTexViewReady = true;
381 else if ( g_SurfaceTable.m_pfnAnyPatchesSelected() ) {
382 g_pPatch = g_SurfaceTable.m_pfnGetSelectedPatch();
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++ )
390 for ( j = 0; j < g_pPatch->height; j++ )
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];
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];
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];
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];
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 );
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;
419 g_bTexViewReady = true;
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
428 // tell Radiant to update (will also send update windows messages )
429 g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_SelectedFaceData );
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 );
438 // we'll need to update after that as well:
439 g_bTexViewReady = false;
440 // send a repaint message
441 g_pToolWnd->Redraw();
444 void Textool_Cancel(){
446 // tell Radiant to update (will also send update windows messages )
447 g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_CancelFaceData );
451 *g_pPatch = g_CancelPatch;
452 g_pPatch->bDirty = true;
453 g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
455 // do not call destroy, decref it
456 g_pToolWnd->DecRef();
460 static void DoExpose(){
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 );
468 g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_SelectedFaceTable.m_pfnGetTextureNumber( 0 ) );
472 g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_pPatch->d_texture->texture_number );
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 );
489 g_QglTable.m_pfn_qglBegin( GL_LINE_LOOP );
490 for ( i = 0; i < g_NumPoints; i++ )
492 g_QglTable.m_pfn_qglVertex2f( g_WorkWinding.data[i][0], g_WorkWinding.data[i][1] );
494 g_QglTable.m_pfn_qglEnd();
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++ )
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] );
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] );
512 g_QglTable.m_pfn_qglEnd();
515 // let the control points manager render
516 g_pManager->Render();
519 static bool CanProcess(){
520 if ( !g_bTexViewReady && !g_bClosing ) {
521 InitTexView( g_pToolWnd );
523 if ( !g_bTexViewReady ) {
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();
537 else if ( !g_bTexViewReady && g_bClosing ) {
545 static void button_press( GtkWidget *widget, GdkEventButton *event, gpointer data ){
546 if ( CanProcess() ) {
547 switch ( event->button )
550 g_pManager->OnLButtonDown( event->x, event->y ); break;
552 g_2DView.OnRButtonDown( event->x, event->y ); break;
557 static void button_release( GtkWidget *widget, GdkEventButton *event, gpointer data ){
558 if ( CanProcess() ) {
559 switch ( event->button )
562 g_pManager->OnLButtonUp( event->x, event->y ); break;
564 g_2DView.OnRButtonUp( event->x, event->y ); break;
569 static void motion( GtkWidget *widget, GdkEventMotion *event, gpointer data ){
570 if ( CanProcess() ) {
571 if ( g_2DView.OnMouseMove( event->x, event->y ) ) {
575 if ( g_pManager->OnMouseMove( event->x, event->y ) ) {
581 static gint expose( GtkWidget *widget, GdkEventExpose *event, gpointer data ){
582 if ( event->count > 0 ) {
586 if ( !CanProcess() ) {
590 if ( g_bTexViewReady ) {
591 g_2DView.m_rect.bottom = widget->allocation.height;
592 g_2DView.m_rect.right = widget->allocation.width;
594 if ( !g_QglTable.m_pfn_glwidget_make_current( g_pToolWidget ) ) {
595 Sys_Printf( "TexTool: glMakeCurrent failed\n" );
601 g_QglTable.m_pfn_glwidget_swap_buffers( g_pToolWidget );
607 static gint keypress( GtkWidget* widget, GdkEventKey* event, gpointer data ){
608 unsigned int code = gdk_keyval_to_upper( event->keyval );
610 if ( code == GDK_Escape ) {
611 gtk_widget_destroy( g_pToolWnd );
616 if ( CanProcess() ) {
617 if ( g_2DView.OnKeyDown( code ) ) {
621 if ( code == GDK_Return ) {
630 static gint close( GtkWidget *widget, GdkEvent* event, gpointer data ){
631 gtk_widget_destroy( widget );
637 static GtkWidget* CreateOpenGLWidget(){
638 g_pToolWidget = g_QglTable.m_pfn_glwidget_new( FALSE, g_QglTable.m_pfn_GetQeglobalsGLWidget() );
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 );
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 );
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 );
656 return g_pToolWidget;
661 static void DoPaint(){
662 if ( !CanProcess() ) {
666 if ( g_bTexViewReady ) {
667 g_2DView.m_rect.bottom = g_pToolWnd->getHeight();
668 g_2DView.m_rect.right = g_pToolWnd->getWidth();
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 ) {
677 for ( i = 1; i < g_nDrawObjects; i++ )
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();
685 // draw the polygon outline and control points
686 g_DrawObjects[0].pObject->PrepareModelView( NULL );
687 g_DrawObjects[0].pObject->RenderUI();
692 bool CWindowListener::OnLButtonDown( guint32 nFlags, double x, double y ){
693 if ( CanProcess() ) {
694 g_pManager->OnLButtonDown( (int)x, (int)y );
700 bool CWindowListener::OnRButtonDown( guint32 nFlags, double x, double y ){
701 if ( CanProcess() ) {
702 g_2DView.OnRButtonDown( (int)x, (int)y );
708 bool CWindowListener::OnLButtonUp( guint32 nFlags, double x, double y ){
709 if ( CanProcess() ) {
710 g_pManager->OnLButtonUp( (int)x, (int)y );
716 bool CWindowListener::OnRButtonUp( guint32 nFlags, double x, double y ){
717 if ( CanProcess() ) {
718 g_2DView.OnRButtonUp( (int)x, (int)y );
724 bool CWindowListener::OnMouseMove( guint32 nFlags, double x, double y ){
725 if ( CanProcess() ) {
726 if ( g_2DView.OnMouseMove( (int)x, (int)y ) ) {
730 g_pManager->OnMouseMove( (int)x, (int)y );
736 // the widget is closing
737 void CWindowListener::Close(){
741 bool CWindowListener::Paint(){
742 if ( !CanProcess() ) {
746 if ( g_bTexViewReady ) {
753 bool CWindowListener::OnKeyPressed( char *s ){
754 if ( !strcmp( s,"Escape" ) ) {
758 if ( CanProcess() ) {
759 if ( g_2DView.OnKeyDown( s ) ) {
763 if ( !strcmp( s,"Return" ) ) {
771 extern "C" void QERPlug_Dispatch( const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush ){
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;
781 Sys_Printf( "TexTool plugin: _QERQglTable interface request failed\n" );
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;
794 Sys_Printf( "TexTool plugin: _QERSelectedFaceTable interface request failed\n" );
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;
806 Sys_Printf( "TexTool plugin: _QERAppSurfaceTable interface request failed\n" );
812 g_UITable.m_nSize = sizeof( _QERUITable );
813 if ( g_FuncTable.m_pfnRequestInterface( QERUI_GUID, static_cast<LPVOID>( &g_UITable ) ) ) {
818 Sys_Printf( "TexTool plugin: _QERUITable interface request failed\n" );
824 if ( !strcmp( p, "About..." ) ) {
825 DoMessageBox( PLUGIN_ABOUT, "About ...", MB_OK );
827 else if ( !strcmp( p, "Go..." ) ) {
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
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 );
842 g_bTexViewReady = false;
845 else if ( !strcmp( p, "Help..." ) ) {
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 );
851 DoMessageBox( "Are you kidding me ?", "TexTool plugin", MB_OK );
857 // =============================================================================
860 CSynapseServer* g_pSynapseServer = NULL;
861 CSynapseClientTexTool g_SynapseClient;
864 #pragma GCC visibility push(default)
866 extern "C" CSynapseClient * SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ) {
868 #pragma GCC visibility pop
870 if ( strcmp( version, SYNAPSE_VERSION ) ) {
871 Syn_Printf( "ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version );
874 g_pSynapseServer = pServer;
875 g_pSynapseServer->IncRef();
876 Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() );
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 );
885 return &g_SynapseClient;
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;
898 Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );
904 const char* CSynapseClientTexTool::GetInfo(){
905 return "Texture Tools plugin built " __DATE__ " " RADIANT_VERSION;