2 Copyright (C) 1999-2006 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 ){
34 auto parent = widget.window();
35 loop = (int*)g_object_get_data( G_OBJECT( parent ), "loop" );
36 ret = (int*)g_object_get_data( G_OBJECT( parent ), "ret" );
39 *ret = gpointer_to_int( data );
42 static gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
45 gtk_widget_hide( widget );
46 loop = (int*)g_object_get_data( G_OBJECT( widget ), "loop" );
52 int DoMessageBox( const char* lpText, const char* lpCaption, guint32 uType ){
53 GtkWidget *w, *vbox, *hbox;
54 int mode = ( uType & MB_TYPEMASK ), ret, loop = 1;
56 auto window = ui::Window( ui::window_type::TOP );
57 window.connect( "delete_event",
58 G_CALLBACK( dialog_delete_callback ), NULL );
59 window.connect( "destroy",
60 G_CALLBACK( gtk_widget_destroy ), NULL );
61 gtk_window_set_title( window, lpCaption );
62 gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );
63 g_object_set_data( G_OBJECT( window ), "loop", &loop );
64 g_object_set_data( G_OBJECT( window ), "ret", &ret );
65 gtk_widget_realize( window );
67 vbox = ui::VBox( FALSE, 10 );
71 w = ui::Label( lpText );
72 vbox.pack_start( w, FALSE, FALSE, 2 );
73 gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT );
76 w = gtk_hseparator_new();
77 vbox.pack_start( w, FALSE, FALSE, 2 );
80 hbox = ui::HBox( FALSE, 10 );
81 vbox.pack_start( hbox, FALSE, FALSE, 2 );
84 if ( mode == MB_OK ) {
85 w = ui::Button( "Ok" );
86 hbox.pack_start( w, TRUE, TRUE, 0 );
88 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
89 gtk_widget_set_can_default( w, true );
90 gtk_widget_grab_default( w );
94 else if ( mode == MB_OKCANCEL ) {
95 w = ui::Button( "Ok" );
96 hbox.pack_start( w, TRUE, TRUE, 0 );
98 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
99 gtk_widget_set_can_default( w, true );
100 gtk_widget_grab_default( w );
103 w = ui::Button( "Cancel" );
104 hbox.pack_start( w, TRUE, TRUE, 0 );
105 w.connect( "clicked",
106 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
110 else if ( mode == MB_YESNOCANCEL ) {
111 w = ui::Button( "Yes" );
112 hbox.pack_start( w, TRUE, TRUE, 0 );
113 w.connect( "clicked",
114 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
115 gtk_widget_set_can_default( w, true );
116 gtk_widget_grab_default( w );
119 w = ui::Button( "No" );
120 hbox.pack_start( w, TRUE, TRUE, 0 );
121 w.connect( "clicked",
122 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
125 w = ui::Button( "Cancel" );
126 hbox.pack_start( w, TRUE, TRUE, 0 );
127 w.connect( "clicked",
128 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
132 else /* if (mode == MB_YESNO) */
134 w = ui::Button( "Yes" );
135 hbox.pack_start( w, TRUE, TRUE, 0 );
136 w.connect( "clicked",
137 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
138 gtk_widget_set_can_default( w, true );
139 gtk_widget_grab_default( w );
142 w = ui::Button( "No" );
143 hbox.pack_start( w, TRUE, TRUE, 0 );
144 w.connect( "clicked",
145 G_CALLBACK( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
151 gtk_grab_add( window );
154 gtk_main_iteration();
156 gtk_grab_remove( window );
157 gtk_widget_destroy( window );
162 // Radiant function table
163 _QERFuncTable_1 g_FuncTable;
166 const char *PLUGIN_NAME = "Q3 Texture Tools";
168 // commands in the menu
169 const char *PLUGIN_COMMANDS = "About...;Go...";
171 // cast to GtkWidget*
173 IWindow *g_pToolWnd = NULL; // handle to the window
174 CWindowListener g_Listen;
176 // plugin interfaces ---------------------------
177 bool g_bQglInitDone = false;
178 OpenGLBinding g_QglTable;
179 bool g_bSelectedFaceInitDone = false;
180 _QERSelectedFaceTable g_SelectedFaceTable;
181 bool g_bUITable = false;
182 _QERUITable g_UITable;
184 // selected face -------------------------------
185 // we use this one to commit / read with Radiant
186 _QERFaceData g_SelectedFaceData;
187 // g_pSelectedFaceWindings gets allocated with MAX_POINTS_ON_WINDING at plugin startup ( QERPlug_Init )
188 winding_t *g_pSelectedFaceWinding = NULL;
189 const float g_ViewportRatio = 1.2f;
190 // usefull class to manage the 2D view
192 // control points to move the polygon
193 CControlPointsManagerBFace g_ControlPointsBFace;
194 // tells if a face is selected and we have something to render in the TexWindow
195 bool g_bTexViewReady = false;
196 // data for texture work
198 CtrlPts_t g_WorkWinding;
199 // reference _QERFaceData we use on Cancel, and for Commit
200 _QERFaceData g_CancelFaceData;
202 // patches -------------------------------------
203 bool g_bPatch = false;
204 //++timo we use this one to grab selected patchMesh_t
205 // FIXME: update when there's a real interface to read/write patches
206 bool g_bSurfaceTableInitDone = false;
207 _QERAppSurfaceTable g_SurfaceTable;
208 CControlPointsManagerPatch g_ControlPointsPatch;
209 // data for texture work
210 patchMesh_t* g_pPatch;
211 // we only use ctrl[][].st in this one
212 patchMesh_t g_WorkPatch;
213 // copy of initial g_pPatch for Cancel situation
214 patchMesh_t g_CancelPatch;
216 // ---------------------------------------------
217 // holds the manager we are currently using
218 CControlPointsManager *g_pManager = NULL;
220 // ---------------------------------------------
221 // globals flags for user preferences
222 //++timo TODO: this should be retrieved from the Editor's .INI prefs in a dedicated interface
223 // update camera view during manipulation ?
224 bool g_bPrefsUpdateCameraView = true;
226 // misc ----------------------------------------
227 bool g_bHelp = false;
228 //++timo FIXME: used to close the plugin window if InitTexView fails
229 // it's dirty, only use is to prevent infinite loop in DialogProc
230 bool g_bClosing = false;
232 const char *PLUGIN_ABOUT = "Texture Tools for Radiant\n\n"
233 "Gtk port by Leonardo Zide (leo@lokigames.com)\n"
234 "Original version by Timothee \"TTimo\" Besset (timo@qeradiant.com)";
236 extern "C" void* WINAPI QERPlug_GetFuncTable(){
240 const char* QERPlug_Init( void* hApp, void *pWidget ){
242 GtkWidget* pMainWidget = static_cast<GtkWidget*>( pWidget );
244 g_pMainWnd = pMainWidget;
245 memset( &g_FuncTable, 0, sizeof( _QERFuncTable_1 ) );
246 g_FuncTable.m_nSize = sizeof( _QERFuncTable_1 );
247 size = (int)( (winding_t *)0 )->points[MAX_POINTS_ON_WINDING];
248 g_pSelectedFaceWinding = (winding_t *)malloc( size );
249 memset( g_pSelectedFaceWinding, 0, size );
250 return "Texture tools for Radiant";
253 const char* QERPlug_GetName(){
254 return (char*)PLUGIN_NAME;
257 const char* QERPlug_GetCommandList(){
258 return PLUGIN_COMMANDS;
261 char *TranslateString( char *buf ){
262 static char buf2[32768];
268 for ( i = 0 ; i < l ; i++ )
270 if ( buf[i] == '\n' ) {
283 // called by InitTexView to fit the view against the bounding box of control points
284 void FitView( IWindow* hwndDlg, int TexSize[2] ){
285 // apply a ratio to get the area we'll draw
286 g_2DView.m_Center[0] = 0.5f * ( g_2DView.m_Mins[0] + g_2DView.m_Maxs[0] );
287 g_2DView.m_Center[1] = 0.5f * ( g_2DView.m_Mins[1] + g_2DView.m_Maxs[1] );
288 g_2DView.m_Mins[0] = g_2DView.m_Center[0] + g_ViewportRatio * ( g_2DView.m_Mins[0] - g_2DView.m_Center[0] );
289 g_2DView.m_Mins[1] = g_2DView.m_Center[1] + g_ViewportRatio * ( g_2DView.m_Mins[1] - g_2DView.m_Center[1] );
290 g_2DView.m_Maxs[0] = g_2DView.m_Center[0] + g_ViewportRatio * ( g_2DView.m_Maxs[0] - g_2DView.m_Center[0] );
291 g_2DView.m_Maxs[1] = g_2DView.m_Center[1] + g_ViewportRatio * ( g_2DView.m_Maxs[1] - g_2DView.m_Center[1] );
293 g_2DView.m_rect.left = 0;
294 g_2DView.m_rect.top = 0;
295 g_2DView.m_rect.bottom = hwndDlg->getHeight();
296 g_2DView.m_rect.right = hwndDlg->getWidth();
298 // we need to draw this area, now compute a bigger area so the texture scale is the same along X and Y
299 // compute box shape in XY space, let's say X <-> S we'll get a ratio for Y:
301 g_SelectedFaceTable.m_pfnGetTextureSize( 0, TexSize );
305 TexSize[0] = g_pPatch->d_texture->width;
306 TexSize[1] = g_pPatch->d_texture->height;
308 // we want a texture with the same X / Y ratio
309 // compute XY space / window size ratio
310 float SSize = (float)fabs( g_2DView.m_Maxs[0] - g_2DView.m_Mins[0] );
311 float TSize = (float)fabs( g_2DView.m_Maxs[1] - g_2DView.m_Mins[1] );
312 float XSize = TexSize[0] * SSize;
313 float YSize = TexSize[1] * TSize;
314 float RatioX = XSize / (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right );
315 float RatioY = YSize / (float)abs( g_2DView.m_rect.top - g_2DView.m_rect.bottom );
316 if ( RatioX > RatioY ) {
317 YSize = (float)abs( g_2DView.m_rect.top - g_2DView.m_rect.bottom ) * RatioX;
318 TSize = YSize / (float)TexSize[1];
322 XSize = (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right ) * RatioY;
323 SSize = XSize / (float)TexSize[0];
325 g_2DView.m_Mins[0] = g_2DView.m_Center[0] - 0.5f * SSize;
326 g_2DView.m_Maxs[0] = g_2DView.m_Center[0] + 0.5f * SSize;
327 g_2DView.m_Mins[1] = g_2DView.m_Center[1] - 0.5f * TSize;
328 g_2DView.m_Maxs[1] = g_2DView.m_Center[1] + 0.5f * TSize;
331 // call this one each time we need to re-init
332 //++timo TODO: re-init objects state, g_2DView and g_ControlPointsManager
333 void InitTexView( IWindow* hwndDlg ){
334 // size of the texture we are working on
336 g_bTexViewReady = false;
337 if ( g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 0 ) {
338 g_SelectedFaceTable.m_pfnGetFaceInfo( 0, &g_SelectedFaceData, g_pSelectedFaceWinding );
341 // we have something selected
342 // setup: compute BBox for the winding ( in ST space )
343 //++timo FIXME: move this in a C2DView member ? used as well for patches
344 g_2DView.m_Mins[0] = +9999.0f; g_2DView.m_Mins[1] = +9999.0f;
345 g_2DView.m_Maxs[0] = -9999.0f; g_2DView.m_Maxs[1] = -9999.0f;
346 for ( i = 0; i < g_pSelectedFaceWinding->numpoints; i++ )
348 if ( g_pSelectedFaceWinding->points[i][3] < g_2DView.m_Mins[0] ) {
349 g_2DView.m_Mins[0] = g_pSelectedFaceWinding->points[i][3];
351 if ( g_pSelectedFaceWinding->points[i][3] > g_2DView.m_Maxs[0] ) {
352 g_2DView.m_Maxs[0] = g_pSelectedFaceWinding->points[i][3];
354 if ( g_pSelectedFaceWinding->points[i][4] < g_2DView.m_Mins[1] ) {
355 g_2DView.m_Mins[1] = g_pSelectedFaceWinding->points[i][4];
357 if ( g_pSelectedFaceWinding->points[i][4] > g_2DView.m_Maxs[1] ) {
358 g_2DView.m_Maxs[1] = g_pSelectedFaceWinding->points[i][4];
361 // NOTE: FitView will read and init TexSize
362 FitView( hwndDlg, TexSize );
363 // now init the work tables
364 g_NumPoints = g_pSelectedFaceWinding->numpoints;
365 for ( i = 0; i < g_NumPoints; i++ )
367 g_WorkWinding.data[i][0] = g_pSelectedFaceWinding->points[i][3];
368 g_WorkWinding.data[i][1] = g_pSelectedFaceWinding->points[i][4];
370 g_ControlPointsBFace.Init( g_NumPoints, &g_WorkWinding, &g_2DView, TexSize, &g_SelectedFaceData, &g_QglTable );
373 fTexStep[0] = 1.0f / float(TexSize[0]);
374 fTexStep[1] = 1.0f / float(TexSize[1]);
375 g_2DView.SetGrid( fTexStep[0], fTexStep[1] );
376 g_pManager = &g_ControlPointsBFace;
377 // prepare the "Cancel" data
378 memcpy( &g_CancelFaceData, &g_SelectedFaceData, sizeof( _QERFaceData ) );
380 g_bTexViewReady = true;
382 else if ( g_SurfaceTable.m_pfnAnyPatchesSelected() ) {
383 g_pPatch = g_SurfaceTable.m_pfnGetSelectedPatch();
386 // compute BBox for all patch points
387 g_2DView.m_Mins[0] = +9999.0f; g_2DView.m_Mins[1] = +9999.0f;
388 g_2DView.m_Maxs[0] = -9999.0f; g_2DView.m_Maxs[1] = -9999.0f;
389 for ( i = 0; i < g_pPatch->width; i++ )
391 for ( j = 0; j < g_pPatch->height; j++ )
393 if ( g_pPatch->ctrl[i][j].st[0] < g_2DView.m_Mins[0] ) {
394 g_2DView.m_Mins[0] = g_pPatch->ctrl[i][j].st[0];
396 if ( g_pPatch->ctrl[i][j].st[0] > g_2DView.m_Maxs[0] ) {
397 g_2DView.m_Maxs[0] = g_pPatch->ctrl[i][j].st[0];
399 if ( g_pPatch->ctrl[i][j].st[1] < g_2DView.m_Mins[1] ) {
400 g_2DView.m_Mins[1] = g_pPatch->ctrl[i][j].st[1];
402 if ( g_pPatch->ctrl[i][j].st[1] > g_2DView.m_Maxs[1] ) {
403 g_2DView.m_Maxs[1] = g_pPatch->ctrl[i][j].st[1];
407 FitView( hwndDlg, TexSize );
408 // init the work tables
409 g_WorkPatch = *g_pPatch;
410 g_ControlPointsPatch.Init( &g_WorkPatch, &g_2DView, &g_QglTable, g_pPatch );
413 fTexStep[0] = 1.0f / float(TexSize[0]);
414 fTexStep[1] = 1.0f / float(TexSize[1]);
415 g_2DView.SetGrid( fTexStep[0], fTexStep[1] );
416 g_pManager = &g_ControlPointsPatch;
417 // prepare the "cancel" data
418 g_CancelPatch = *g_pPatch;
420 g_bTexViewReady = true;
424 void Textool_Validate(){
425 // validate current situation into the main view
426 g_pManager->Commit();
427 // for a brush face we have an aditionnal step
429 // tell Radiant to update (will also send update windows messages )
430 g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_SelectedFaceData );
434 // ask to rebuild the patch display data
435 g_pPatch->bDirty = true;
436 // send a repaint to the camera window as well
437 g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
439 // we'll need to update after that as well:
440 g_bTexViewReady = false;
441 // send a repaint message
442 g_pToolWnd->Redraw();
445 void Textool_Cancel(){
447 // tell Radiant to update (will also send update windows messages )
448 g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_CancelFaceData );
452 *g_pPatch = g_CancelPatch;
453 g_pPatch->bDirty = true;
454 g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
456 // do not call destroy, decref it
457 g_pToolWnd->DecRef();
461 static void DoExpose(){
464 g_2DView.PreparePaint();
465 g_QglTable.m_pfn_qglColor3f( 1, 1, 1 );
466 // draw the texture background
467 g_QglTable.m_pfn_qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
469 g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_SelectedFaceTable.m_pfnGetTextureNumber( 0 ) );
473 g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_pPatch->d_texture->texture_number );
476 g_QglTable.m_pfn_qglEnable( GL_TEXTURE_2D );
477 g_QglTable.m_pfn_qglBegin( GL_QUADS );
478 g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Mins[0], g_2DView.m_Mins[1] );
479 g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Mins[0], g_2DView.m_Mins[1] );
480 g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Maxs[0], g_2DView.m_Mins[1] );
481 g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Maxs[0], g_2DView.m_Mins[1] );
482 g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Maxs[0], g_2DView.m_Maxs[1] );
483 g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Maxs[0], g_2DView.m_Maxs[1] );
484 g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Mins[0], g_2DView.m_Maxs[1] );
485 g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Mins[0], g_2DView.m_Maxs[1] );
486 g_QglTable.m_pfn_qglEnd();
487 g_QglTable.m_pfn_qglDisable( GL_TEXTURE_2D );
490 g_QglTable.m_pfn_qglBegin( GL_LINE_LOOP );
491 for ( i = 0; i < g_NumPoints; i++ )
493 g_QglTable.m_pfn_qglVertex2f( g_WorkWinding.data[i][0], g_WorkWinding.data[i][1] );
495 g_QglTable.m_pfn_qglEnd();
499 g_QglTable.m_pfn_qglBegin( GL_LINES );
500 for ( i = 0; i < g_pPatch->width; i++ )
501 for ( j = 0; j < g_pPatch->height; j++ )
503 if ( i < g_pPatch->width - 1 ) {
504 g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j].st[0], g_WorkPatch.ctrl[i][j].st[1] );
505 g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i + 1][j].st[0], g_WorkPatch.ctrl[i + 1][j].st[1] );
508 if ( j < g_pPatch->height - 1 ) {
509 g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j].st[0], g_WorkPatch.ctrl[i][j].st[1] );
510 g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j + 1].st[0], g_WorkPatch.ctrl[i][j + 1].st[1] );
513 g_QglTable.m_pfn_qglEnd();
516 // let the control points manager render
517 g_pManager->render();
520 static bool CanProcess(){
521 if ( !g_bTexViewReady && !g_bClosing ) {
522 InitTexView( g_pToolWnd );
524 if ( !g_bTexViewReady ) {
526 DoMessageBox( "You must have brush primitives activated in your project settings and\n"
527 "have a patch or a single face selected to use the TexTool plugin.\n"
528 "See plugins/TexToolHelp for documentation.", "TexTool plugin", MB_ICONERROR | MB_OK );
529 // decref, this will destroy
530 g_pToolWnd->DecRef();
538 else if ( !g_bTexViewReady && g_bClosing ) {
546 static void button_press( GtkWidget *widget, GdkEventButton *event, gpointer data ){
547 if ( CanProcess() ) {
548 switch ( event->button )
551 g_pManager->OnLButtonDown( event->x, event->y ); break;
553 g_2DView.OnRButtonDown( event->x, event->y ); break;
558 static void button_release( GtkWidget *widget, GdkEventButton *event, gpointer data ){
559 if ( CanProcess() ) {
560 switch ( event->button )
563 g_pManager->OnLButtonUp( event->x, event->y ); break;
565 g_2DView.OnRButtonUp( event->x, event->y ); break;
570 static void motion( GtkWidget *widget, GdkEventMotion *event, gpointer data ){
571 if ( CanProcess() ) {
572 if ( g_2DView.OnMouseMove( event->x, event->y ) ) {
576 if ( g_pManager->OnMouseMove( event->x, event->y ) ) {
582 static gint expose( GtkWidget *widget, GdkEventExpose *event, gpointer data ){
583 if ( event->count > 0 ) {
587 if ( !CanProcess() ) {
591 if ( g_bTexViewReady ) {
592 g_2DView.m_rect.bottom = widget->allocation.height;
593 g_2DView.m_rect.right = widget->allocation.width;
595 if ( !g_QglTable.m_pfn_glwidget_make_current( g_pToolWidget ) ) {
596 Sys_Printf( "TexTool: glMakeCurrent failed\n" );
602 g_QglTable.m_pfn_glwidget_swap_buffers( g_pToolWidget );
608 static gint keypress( GtkWidget* widget, GdkEventKey* event, gpointer data ){
609 unsigned int code = gdk_keyval_to_upper( event->keyval );
611 if ( code == GDK_Escape ) {
612 gtk_widget_destroy( g_pToolWnd );
617 if ( CanProcess() ) {
618 if ( g_2DView.OnKeyDown( code ) ) {
622 if ( code == GDK_Return ) {
631 static gint close( GtkWidget *widget, GdkEvent* event, gpointer data ){
632 gtk_widget_destroy( widget );
638 static GtkWidget* CreateOpenGLWidget(){
639 g_pToolWidget = g_QglTable.m_pfn_glwidget_new( FALSE, g_QglTable.m_pfn_GetQeglobalsGLWidget() );
641 gtk_widget_set_events( g_pToolWidget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
642 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK );
644 // Connect signal handlers
645 g_pToolWidget.connect( "expose_event", G_CALLBACK( expose ), NULL );
646 g_pToolWidget.connect( "motion_notify_event",
647 G_CALLBACK( motion ), NULL );
648 g_pToolWidget.connect( "button_press_event",
649 G_CALLBACK( button_press ), NULL );
650 g_pToolWidget.connect( "button_release_event",
651 G_CALLBACK( button_release ), NULL );
653 g_pToolWnd.connect( "delete_event", G_CALLBACK( close ), NULL );
654 g_pToolWnd.connect( "key_press_event",
655 G_CALLBACK( keypress ), NULL );
657 return g_pToolWidget;
662 static void DoPaint(){
663 if ( !CanProcess() ) {
667 if ( g_bTexViewReady ) {
668 g_2DView.m_rect.bottom = g_pToolWnd->getHeight();
669 g_2DView.m_rect.right = g_pToolWnd->getWidth();
672 g_2DView.PreparePaint();
673 // render the objects
674 // the master is not rendered the same way, draw over a unified texture background
675 g_2DView.TextureBackground( g_DrawObjects[0].pObject->getTextureNumber() );
676 if ( g_nDrawObjects >= 1 ) {
678 for ( i = 1; i < g_nDrawObjects; i++ )
680 // we use a first step to the GL_MODELVIEW for the master object
681 // GL_MODELVIEW will be altered in RenderAuxiliary too
682 g_DrawObjects[0].pObject->PrepareModelView( g_DrawObjects[i].pTopo );
683 g_DrawObjects[i].pObject->RenderAuxiliary();
686 // draw the polygon outline and control points
687 g_DrawObjects[0].pObject->PrepareModelView( NULL );
688 g_DrawObjects[0].pObject->RenderUI();
693 bool CWindowListener::OnLButtonDown( guint32 nFlags, double x, double y ){
694 if ( CanProcess() ) {
695 g_pManager->OnLButtonDown( (int)x, (int)y );
701 bool CWindowListener::OnRButtonDown( guint32 nFlags, double x, double y ){
702 if ( CanProcess() ) {
703 g_2DView.OnRButtonDown( (int)x, (int)y );
709 bool CWindowListener::OnLButtonUp( guint32 nFlags, double x, double y ){
710 if ( CanProcess() ) {
711 g_pManager->OnLButtonUp( (int)x, (int)y );
717 bool CWindowListener::OnRButtonUp( guint32 nFlags, double x, double y ){
718 if ( CanProcess() ) {
719 g_2DView.OnRButtonUp( (int)x, (int)y );
725 bool CWindowListener::OnMouseMove( guint32 nFlags, double x, double y ){
726 if ( CanProcess() ) {
727 if ( g_2DView.OnMouseMove( (int)x, (int)y ) ) {
731 g_pManager->OnMouseMove( (int)x, (int)y );
737 // the widget is closing
738 void CWindowListener::Close(){
742 bool CWindowListener::Paint(){
743 if ( !CanProcess() ) {
747 if ( g_bTexViewReady ) {
754 bool CWindowListener::OnKeyPressed( char *s ){
755 if ( !strcmp( s,"Escape" ) ) {
759 if ( CanProcess() ) {
760 if ( g_2DView.OnKeyDown( s ) ) {
764 if ( !strcmp( s,"Return" ) ) {
772 extern "C" void QERPlug_Dispatch( const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush ){
774 // if it's the first call, perhaps we need some additional init steps
775 if ( !g_bQglInitDone ) {
776 g_QglTable.m_nSize = sizeof( OpenGLBinding );
777 if ( g_FuncTable.m_pfnRequestInterface( QERQglTable_GUID, static_cast<LPVOID>( &g_QglTable ) ) ) {
778 g_bQglInitDone = true;
782 Sys_Printf( "TexTool plugin: OpenGLBinding interface request failed\n" );
787 if ( !g_bSelectedFaceInitDone ) {
788 g_SelectedFaceTable.m_nSize = sizeof( _QERSelectedFaceTable );
789 if ( g_FuncTable.m_pfnRequestInterface( QERSelectedFaceTable_GUID,
790 static_cast<LPVOID>( &g_SelectedFaceTable ) ) ) {
791 g_bSelectedFaceInitDone = true;
795 Sys_Printf( "TexTool plugin: _QERSelectedFaceTable interface request failed\n" );
800 if ( !g_bSurfaceTableInitDone ) {
801 g_SurfaceTable.m_nSize = sizeof( _QERAppSurfaceTable );
802 if ( g_FuncTable.m_pfnRequestInterface( QERAppSurfaceTable_GUID, static_cast<LPVOID>( &g_SurfaceTable ) ) ) {
803 g_bSurfaceTableInitDone = true;
807 Sys_Printf( "TexTool plugin: _QERAppSurfaceTable interface request failed\n" );
813 g_UITable.m_nSize = sizeof( _QERUITable );
814 if ( g_FuncTable.m_pfnRequestInterface( QERUI_GUID, static_cast<LPVOID>( &g_UITable ) ) ) {
819 Sys_Printf( "TexTool plugin: _QERUITable interface request failed\n" );
825 if ( !strcmp( p, "About..." ) ) {
826 DoMessageBox( PLUGIN_ABOUT, "About ...", MB_OK );
828 else if ( !strcmp( p, "Go..." ) ) {
830 g_pToolWnd = g_UITable.m_pfnCreateGLWindow();
831 g_pToolWnd->setSizeParm( 300,300 );
832 g_pToolWnd->setName( "TexTool" );
833 // g_Listener is a static class, we need to bump the refCount to avoid premature release problems
835 // setListener will incRef on the listener too
836 g_pToolWnd->setListener( &g_Listen );
837 if ( !g_pToolWnd->Show() ) {
838 DoMessageBox( "Error creating texture tools window!", "TexTool plugin", MB_ICONERROR | MB_OK );
843 g_bTexViewReady = false;
846 else if ( !strcmp( p, "Help..." ) ) {
848 DoMessageBox( "Select a brush face (ctrl+shift+left mouse) or a patch, and hit Go...\n"
849 "See tutorials for more", "TexTool plugin", MB_OK );
852 DoMessageBox( "Are you kidding me ?", "TexTool plugin", MB_OK );
858 // =============================================================================
861 CSynapseServer* g_pSynapseServer = NULL;
862 CSynapseClientTexTool g_SynapseClient;
864 extern "C" CSynapseClient * SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ){
865 if ( strcmp( version, SYNAPSE_VERSION ) ) {
866 Syn_Printf( "ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version );
869 g_pSynapseServer = pServer;
870 g_pSynapseServer->IncRef();
871 Set_Syn_Printf( g_pSynapseServer->Get_Syn_Printf() );
873 g_SynapseClient.AddAPI( PLUGIN_MAJOR, "textool", sizeof( _QERPluginTable ) );
874 g_SynapseClient.AddAPI( RADIANT_MAJOR, NULL, sizeof( g_FuncTable ), SYN_REQUIRE, &g_FuncTable );
875 g_SynapseClient.AddAPI( QGL_MAJOR, NULL, sizeof( g_QglTable ), SYN_REQUIRE, &g_QglTable );
876 g_SynapseClient.AddAPI( SELECTEDFACE_MAJOR, NULL, sizeof( g_SelectedFaceTable ), SYN_REQUIRE, &g_SelectedFaceTable );
878 return &g_SynapseClient;
881 bool CSynapseClientTexTool::RequestAPI( APIDescriptor_t *pAPI ){
882 if ( !strcmp( pAPI->major_name, PLUGIN_MAJOR ) ) {
883 _QERPluginTable *pTable = static_cast<_QERPluginTable*>( pAPI->mpTable );
884 pTable->m_pfnQERPlug_Init = QERPlug_Init;
885 pTable->m_pfnQERPlug_GetName = QERPlug_GetName;
886 pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList;
887 pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch;
891 Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );
897 const char* CSynapseClientTexTool::GetInfo(){
898 return "Texture Tools plugin built " __DATE__ " " RADIANT_VERSION;