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