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)
35 auto parent = widget.window();
36 loop = (int *) g_object_get_data(G_OBJECT(parent), "loop");
37 ret = (int *) g_object_get_data(G_OBJECT(parent), "ret");
40 *ret = gpointer_to_int(data);
43 static gint dialog_delete_callback(GtkWidget *widget, GdkEvent *event, gpointer data)
47 gtk_widget_hide(widget);
48 loop = (int *) g_object_get_data(G_OBJECT(widget), "loop");
54 int DoMessageBox(const char *lpText, const char *lpCaption, guint32 uType)
56 GtkWidget *w, *vbox, *hbox;
57 int mode = (uType & MB_TYPEMASK), ret, loop = 1;
59 auto window = ui::Window(ui::window_type::TOP);
60 window.connect("delete_event",
61 G_CALLBACK(dialog_delete_callback), NULL);
62 window.connect("destroy",
63 G_CALLBACK(gtk_widget_destroy), NULL);
64 gtk_window_set_title(window, lpCaption);
65 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
66 g_object_set_data(G_OBJECT(window), "loop", &loop);
67 g_object_set_data(G_OBJECT(window), "ret", &ret);
68 gtk_widget_realize(window);
70 vbox = ui::VBox(FALSE, 10);
74 w = ui::Label(lpText);
75 vbox.pack_start(w, FALSE, FALSE, 2);
76 gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
79 w = gtk_hseparator_new();
80 vbox.pack_start(w, FALSE, FALSE, 2);
83 hbox = ui::HBox(FALSE, 10);
84 vbox.pack_start(hbox, FALSE, FALSE, 2);
89 hbox.pack_start(w, TRUE, TRUE, 0);
91 G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDOK));
92 gtk_widget_set_can_default(w, true);
93 gtk_widget_grab_default(w);
96 } else if (mode == MB_OKCANCEL) {
98 hbox.pack_start(w, TRUE, TRUE, 0);
100 G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDOK));
101 gtk_widget_set_can_default(w, true);
102 gtk_widget_grab_default(w);
105 w = ui::Button("Cancel");
106 hbox.pack_start(w, TRUE, TRUE, 0);
108 G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDCANCEL));
111 } else if (mode == MB_YESNOCANCEL) {
112 w = ui::Button("Yes");
113 hbox.pack_start(w, TRUE, TRUE, 0);
115 G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDYES));
116 gtk_widget_set_can_default(w, true);
117 gtk_widget_grab_default(w);
120 w = ui::Button("No");
121 hbox.pack_start(w, TRUE, TRUE, 0);
123 G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDNO));
126 w = ui::Button("Cancel");
127 hbox.pack_start(w, TRUE, TRUE, 0);
129 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);
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);
145 G_CALLBACK(dialog_button_callback), GINT_TO_POINTER(IDNO));
151 gtk_grab_add(window);
154 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 OpenGLBinding 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
239 QERPlug_GetFuncTable()
244 const char *QERPlug_Init(void *hApp, void *pWidget)
247 GtkWidget *pMainWidget = static_cast<GtkWidget *>( pWidget );
249 g_pMainWnd = pMainWidget;
250 memset(&g_FuncTable, 0, sizeof(_QERFuncTable_1));
251 g_FuncTable.m_nSize = sizeof(_QERFuncTable_1);
252 size = (int) ((winding_t *) 0)->points[MAX_POINTS_ON_WINDING];
253 g_pSelectedFaceWinding = (winding_t *) malloc(size);
254 memset(g_pSelectedFaceWinding, 0, size);
255 return "Texture tools for Radiant";
258 const char *QERPlug_GetName()
260 return (char *) PLUGIN_NAME;
263 const char *QERPlug_GetCommandList()
265 return PLUGIN_COMMANDS;
268 char *TranslateString(char *buf)
270 static char buf2[32768];
276 for (i = 0; i < l; i++) {
277 if (buf[i] == '\n') {
289 // called by InitTexView to fit the view against the bounding box of control points
290 void FitView(IWindow *hwndDlg, int TexSize[2])
292 // apply a ratio to get the area we'll draw
293 g_2DView.m_Center[0] = 0.5f * (g_2DView.m_Mins[0] + g_2DView.m_Maxs[0]);
294 g_2DView.m_Center[1] = 0.5f * (g_2DView.m_Mins[1] + g_2DView.m_Maxs[1]);
295 g_2DView.m_Mins[0] = g_2DView.m_Center[0] + g_ViewportRatio * (g_2DView.m_Mins[0] - g_2DView.m_Center[0]);
296 g_2DView.m_Mins[1] = g_2DView.m_Center[1] + g_ViewportRatio * (g_2DView.m_Mins[1] - g_2DView.m_Center[1]);
297 g_2DView.m_Maxs[0] = g_2DView.m_Center[0] + g_ViewportRatio * (g_2DView.m_Maxs[0] - g_2DView.m_Center[0]);
298 g_2DView.m_Maxs[1] = g_2DView.m_Center[1] + g_ViewportRatio * (g_2DView.m_Maxs[1] - g_2DView.m_Center[1]);
300 g_2DView.m_rect.left = 0;
301 g_2DView.m_rect.top = 0;
302 g_2DView.m_rect.bottom = hwndDlg->getHeight();
303 g_2DView.m_rect.right = hwndDlg->getWidth();
305 // we need to draw this area, now compute a bigger area so the texture scale is the same along X and Y
306 // compute box shape in XY space, let's say X <-> S we'll get a ratio for Y:
308 g_SelectedFaceTable.m_pfnGetTextureSize(0, TexSize);
310 TexSize[0] = g_pPatch->d_texture->width;
311 TexSize[1] = g_pPatch->d_texture->height;
313 // we want a texture with the same X / Y ratio
314 // compute XY space / window size ratio
315 float SSize = (float) fabs(g_2DView.m_Maxs[0] - g_2DView.m_Mins[0]);
316 float TSize = (float) fabs(g_2DView.m_Maxs[1] - g_2DView.m_Mins[1]);
317 float XSize = TexSize[0] * SSize;
318 float YSize = TexSize[1] * TSize;
319 float RatioX = XSize / (float) abs(g_2DView.m_rect.left - g_2DView.m_rect.right);
320 float RatioY = YSize / (float) abs(g_2DView.m_rect.top - g_2DView.m_rect.bottom);
321 if (RatioX > RatioY) {
322 YSize = (float) abs(g_2DView.m_rect.top - g_2DView.m_rect.bottom) * RatioX;
323 TSize = YSize / (float) TexSize[1];
325 XSize = (float) abs(g_2DView.m_rect.left - g_2DView.m_rect.right) * RatioY;
326 SSize = XSize / (float) TexSize[0];
328 g_2DView.m_Mins[0] = g_2DView.m_Center[0] - 0.5f * SSize;
329 g_2DView.m_Maxs[0] = g_2DView.m_Center[0] + 0.5f * SSize;
330 g_2DView.m_Mins[1] = g_2DView.m_Center[1] - 0.5f * TSize;
331 g_2DView.m_Maxs[1] = g_2DView.m_Center[1] + 0.5f * TSize;
334 // call this one each time we need to re-init
335 //++timo TODO: re-init objects state, g_2DView and g_ControlPointsManager
336 void InitTexView(IWindow *hwndDlg)
338 // size of the texture we are working on
340 g_bTexViewReady = false;
341 if (g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 0) {
342 g_SelectedFaceTable.m_pfnGetFaceInfo(0, &g_SelectedFaceData, g_pSelectedFaceWinding);
345 // we have something selected
346 // setup: compute BBox for the winding ( in ST space )
347 //++timo FIXME: move this in a C2DView member ? used as well for patches
348 g_2DView.m_Mins[0] = +9999.0f;
349 g_2DView.m_Mins[1] = +9999.0f;
350 g_2DView.m_Maxs[0] = -9999.0f;
351 g_2DView.m_Maxs[1] = -9999.0f;
352 for (i = 0; i < g_pSelectedFaceWinding->numpoints; i++) {
353 if (g_pSelectedFaceWinding->points[i][3] < g_2DView.m_Mins[0]) {
354 g_2DView.m_Mins[0] = g_pSelectedFaceWinding->points[i][3];
356 if (g_pSelectedFaceWinding->points[i][3] > g_2DView.m_Maxs[0]) {
357 g_2DView.m_Maxs[0] = g_pSelectedFaceWinding->points[i][3];
359 if (g_pSelectedFaceWinding->points[i][4] < g_2DView.m_Mins[1]) {
360 g_2DView.m_Mins[1] = g_pSelectedFaceWinding->points[i][4];
362 if (g_pSelectedFaceWinding->points[i][4] > g_2DView.m_Maxs[1]) {
363 g_2DView.m_Maxs[1] = g_pSelectedFaceWinding->points[i][4];
366 // NOTE: FitView will read and init TexSize
367 FitView(hwndDlg, TexSize);
368 // now init the work tables
369 g_NumPoints = g_pSelectedFaceWinding->numpoints;
370 for (i = 0; i < g_NumPoints; i++) {
371 g_WorkWinding.data[i][0] = g_pSelectedFaceWinding->points[i][3];
372 g_WorkWinding.data[i][1] = g_pSelectedFaceWinding->points[i][4];
374 g_ControlPointsBFace.Init(g_NumPoints, &g_WorkWinding, &g_2DView, TexSize, &g_SelectedFaceData, &g_QglTable);
377 fTexStep[0] = 1.0f / float(TexSize[0]);
378 fTexStep[1] = 1.0f / float(TexSize[1]);
379 g_2DView.SetGrid(fTexStep[0], fTexStep[1]);
380 g_pManager = &g_ControlPointsBFace;
381 // prepare the "Cancel" data
382 memcpy(&g_CancelFaceData, &g_SelectedFaceData, sizeof(_QERFaceData));
384 g_bTexViewReady = true;
385 } else if (g_SurfaceTable.m_pfnAnyPatchesSelected()) {
386 g_pPatch = g_SurfaceTable.m_pfnGetSelectedPatch();
389 // compute BBox for all patch points
390 g_2DView.m_Mins[0] = +9999.0f;
391 g_2DView.m_Mins[1] = +9999.0f;
392 g_2DView.m_Maxs[0] = -9999.0f;
393 g_2DView.m_Maxs[1] = -9999.0f;
394 for (i = 0; i < g_pPatch->width; i++) {
395 for (j = 0; j < g_pPatch->height; j++) {
396 if (g_pPatch->ctrl[i][j].st[0] < g_2DView.m_Mins[0]) {
397 g_2DView.m_Mins[0] = g_pPatch->ctrl[i][j].st[0];
399 if (g_pPatch->ctrl[i][j].st[0] > g_2DView.m_Maxs[0]) {
400 g_2DView.m_Maxs[0] = g_pPatch->ctrl[i][j].st[0];
402 if (g_pPatch->ctrl[i][j].st[1] < g_2DView.m_Mins[1]) {
403 g_2DView.m_Mins[1] = g_pPatch->ctrl[i][j].st[1];
405 if (g_pPatch->ctrl[i][j].st[1] > g_2DView.m_Maxs[1]) {
406 g_2DView.m_Maxs[1] = g_pPatch->ctrl[i][j].st[1];
410 FitView(hwndDlg, TexSize);
411 // init the work tables
412 g_WorkPatch = *g_pPatch;
413 g_ControlPointsPatch.Init(&g_WorkPatch, &g_2DView, &g_QglTable, g_pPatch);
416 fTexStep[0] = 1.0f / float(TexSize[0]);
417 fTexStep[1] = 1.0f / float(TexSize[1]);
418 g_2DView.SetGrid(fTexStep[0], fTexStep[1]);
419 g_pManager = &g_ControlPointsPatch;
420 // prepare the "cancel" data
421 g_CancelPatch = *g_pPatch;
423 g_bTexViewReady = true;
427 void Textool_Validate()
429 // validate current situation into the main view
430 g_pManager->Commit();
431 // for a brush face we have an aditionnal step
433 // tell Radiant to update (will also send update windows messages )
434 g_SelectedFaceTable.m_pfnSetFaceInfo(0, &g_SelectedFaceData);
436 // ask to rebuild the patch display data
437 g_pPatch->bDirty = true;
438 // send a repaint to the camera window as well
439 g_FuncTable.m_pfnSysUpdateWindows(W_CAMERA);
441 // we'll need to update after that as well:
442 g_bTexViewReady = false;
443 // send a repaint message
444 g_pToolWnd->Redraw();
447 void Textool_Cancel()
450 // tell Radiant to update (will also send update windows messages )
451 g_SelectedFaceTable.m_pfnSetFaceInfo(0, &g_CancelFaceData);
453 *g_pPatch = g_CancelPatch;
454 g_pPatch->bDirty = true;
455 g_FuncTable.m_pfnSysUpdateWindows(W_CAMERA);
457 // do not call destroy, decref it
458 g_pToolWnd->DecRef();
462 static void DoExpose()
466 g_2DView.PreparePaint();
467 g_QglTable.m_pfn_qglColor3f(1, 1, 1);
468 // draw the texture background
469 g_QglTable.m_pfn_qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
471 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++) {
492 g_QglTable.m_pfn_qglVertex2f(g_WorkWinding.data[i][0], g_WorkWinding.data[i][1]);
494 g_QglTable.m_pfn_qglEnd();
496 g_QglTable.m_pfn_qglBegin(GL_LINES);
497 for (i = 0; i < g_pPatch->width; i++) {
498 for (j = 0; j < g_pPatch->height; j++) {
499 if (i < g_pPatch->width - 1) {
500 g_QglTable.m_pfn_qglVertex2f(g_WorkPatch.ctrl[i][j].st[0], g_WorkPatch.ctrl[i][j].st[1]);
501 g_QglTable.m_pfn_qglVertex2f(g_WorkPatch.ctrl[i + 1][j].st[0], g_WorkPatch.ctrl[i + 1][j].st[1]);
504 if (j < g_pPatch->height - 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][j + 1].st[0], g_WorkPatch.ctrl[i][j + 1].st[1]);
510 g_QglTable.m_pfn_qglEnd();
513 // let the control points manager render
514 g_pManager->render();
517 static bool CanProcess()
519 if (!g_bTexViewReady && !g_bClosing) {
520 InitTexView(g_pToolWnd);
522 if (!g_bTexViewReady) {
524 DoMessageBox("You must have brush primitives activated in your project settings and\n"
525 "have a patch or a single face selected to use the TexTool plugin.\n"
526 "See plugins/TexToolHelp for documentation.", "TexTool plugin", MB_ICONERROR | MB_OK);
527 // decref, this will destroy
528 g_pToolWnd->DecRef();
534 } else if (!g_bTexViewReady && g_bClosing) {
542 static void button_press( GtkWidget *widget, GdkEventButton *event, gpointer data ){
543 if ( CanProcess() ) {
544 switch ( event->button )
547 g_pManager->OnLButtonDown( event->x, event->y ); break;
549 g_2DView.OnRButtonDown( event->x, event->y ); break;
554 static void button_release( GtkWidget *widget, GdkEventButton *event, gpointer data ){
555 if ( CanProcess() ) {
556 switch ( event->button )
559 g_pManager->OnLButtonUp( event->x, event->y ); break;
561 g_2DView.OnRButtonUp( event->x, event->y ); break;
566 static void motion( GtkWidget *widget, GdkEventMotion *event, gpointer data ){
567 if ( CanProcess() ) {
568 if ( g_2DView.OnMouseMove( event->x, event->y ) ) {
572 if ( g_pManager->OnMouseMove( event->x, event->y ) ) {
578 static gint expose( GtkWidget *widget, GdkEventExpose *event, gpointer data ){
579 if ( event->count > 0 ) {
583 if ( !CanProcess() ) {
587 if ( g_bTexViewReady ) {
588 g_2DView.m_rect.bottom = widget->allocation.height;
589 g_2DView.m_rect.right = widget->allocation.width;
591 if ( !g_QglTable.m_pfn_glwidget_make_current( g_pToolWidget ) ) {
592 Sys_Printf( "TexTool: glMakeCurrent failed\n" );
598 g_QglTable.m_pfn_glwidget_swap_buffers( g_pToolWidget );
604 static gint keypress( GtkWidget* widget, GdkEventKey* event, gpointer data ){
605 unsigned int code = gdk_keyval_to_upper( event->keyval );
607 if ( code == GDK_Escape ) {
608 gtk_widget_destroy( g_pToolWnd );
613 if ( CanProcess() ) {
614 if ( g_2DView.OnKeyDown( code ) ) {
618 if ( code == GDK_Return ) {
627 static gint close( GtkWidget *widget, GdkEvent* event, gpointer data ){
628 gtk_widget_destroy( widget );
634 static GtkWidget* CreateOpenGLWidget(){
635 g_pToolWidget = g_QglTable.m_pfn_glwidget_new( FALSE, g_QglTable.m_pfn_GetQeglobalsGLWidget() );
637 gtk_widget_set_events( g_pToolWidget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
638 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK );
640 // Connect signal handlers
641 g_pToolWidget.connect( "expose_event", G_CALLBACK( expose ), NULL );
642 g_pToolWidget.connect( "motion_notify_event",
643 G_CALLBACK( motion ), NULL );
644 g_pToolWidget.connect( "button_press_event",
645 G_CALLBACK( button_press ), NULL );
646 g_pToolWidget.connect( "button_release_event",
647 G_CALLBACK( button_release ), NULL );
649 g_pToolWnd.connect( "delete_event", G_CALLBACK( close ), NULL );
650 g_pToolWnd.connect( "key_press_event",
651 G_CALLBACK( keypress ), NULL );
653 return g_pToolWidget;
658 static void DoPaint(){
659 if ( !CanProcess() ) {
663 if ( g_bTexViewReady ) {
664 g_2DView.m_rect.bottom = g_pToolWnd->getHeight();
665 g_2DView.m_rect.right = g_pToolWnd->getWidth();
668 g_2DView.PreparePaint();
669 // render the objects
670 // the master is not rendered the same way, draw over a unified texture background
671 g_2DView.TextureBackground( g_DrawObjects[0].pObject->getTextureNumber() );
672 if ( g_nDrawObjects >= 1 ) {
674 for ( i = 1; i < g_nDrawObjects; i++ )
676 // we use a first step to the GL_MODELVIEW for the master object
677 // GL_MODELVIEW will be altered in RenderAuxiliary too
678 g_DrawObjects[0].pObject->PrepareModelView( g_DrawObjects[i].pTopo );
679 g_DrawObjects[i].pObject->RenderAuxiliary();
682 // draw the polygon outline and control points
683 g_DrawObjects[0].pObject->PrepareModelView( NULL );
684 g_DrawObjects[0].pObject->RenderUI();
689 bool CWindowListener::OnLButtonDown(guint32 nFlags, double x, double y)
692 g_pManager->OnLButtonDown((int) x, (int) y);
698 bool CWindowListener::OnRButtonDown(guint32 nFlags, double x, double y)
701 g_2DView.OnRButtonDown((int) x, (int) y);
707 bool CWindowListener::OnLButtonUp(guint32 nFlags, double x, double y)
710 g_pManager->OnLButtonUp((int) x, (int) y);
716 bool CWindowListener::OnRButtonUp(guint32 nFlags, double x, double y)
719 g_2DView.OnRButtonUp((int) x, (int) y);
725 bool CWindowListener::OnMouseMove(guint32 nFlags, double x, double y)
728 if (g_2DView.OnMouseMove((int) x, (int) y)) {
732 g_pManager->OnMouseMove((int) x, (int) y);
738 // the widget is closing
739 void CWindowListener::Close()
744 bool CWindowListener::Paint()
750 if (g_bTexViewReady) {
757 bool CWindowListener::OnKeyPressed(char *s)
759 if (!strcmp(s, "Escape")) {
764 if (g_2DView.OnKeyDown(s)) {
768 if (!strcmp(s, "Return")) {
776 extern "C" void QERPlug_Dispatch(const char *p, vec3_t vMin, vec3_t vMax, bool bSingleBrush)
779 // if it's the first call, perhaps we need some additional init steps
780 if ( !g_bQglInitDone ) {
781 g_QglTable.m_nSize = sizeof( OpenGLBinding );
782 if ( g_FuncTable.m_pfnRequestInterface( QERQglTable_GUID, static_cast<LPVOID>( &g_QglTable ) ) ) {
783 g_bQglInitDone = true;
787 Sys_Printf( "TexTool plugin: OpenGLBinding interface request failed\n" );
792 if ( !g_bSelectedFaceInitDone ) {
793 g_SelectedFaceTable.m_nSize = sizeof( _QERSelectedFaceTable );
794 if ( g_FuncTable.m_pfnRequestInterface( QERSelectedFaceTable_GUID,
795 static_cast<LPVOID>( &g_SelectedFaceTable ) ) ) {
796 g_bSelectedFaceInitDone = true;
800 Sys_Printf( "TexTool plugin: _QERSelectedFaceTable interface request failed\n" );
805 if ( !g_bSurfaceTableInitDone ) {
806 g_SurfaceTable.m_nSize = sizeof( _QERAppSurfaceTable );
807 if ( g_FuncTable.m_pfnRequestInterface( QERAppSurfaceTable_GUID, static_cast<LPVOID>( &g_SurfaceTable ) ) ) {
808 g_bSurfaceTableInitDone = true;
812 Sys_Printf( "TexTool plugin: _QERAppSurfaceTable interface request failed\n" );
818 g_UITable.m_nSize = sizeof( _QERUITable );
819 if ( g_FuncTable.m_pfnRequestInterface( QERUI_GUID, static_cast<LPVOID>( &g_UITable ) ) ) {
824 Sys_Printf( "TexTool plugin: _QERUITable interface request failed\n" );
830 if (!strcmp(p, "About...")) {
831 DoMessageBox(PLUGIN_ABOUT, "About ...", MB_OK);
832 } else if (!strcmp(p, "Go...")) {
834 g_pToolWnd = g_UITable.m_pfnCreateGLWindow();
835 g_pToolWnd->setSizeParm(300, 300);
836 g_pToolWnd->setName("TexTool");
837 // g_Listener is a static class, we need to bump the refCount to avoid premature release problems
839 // setListener will incRef on the listener too
840 g_pToolWnd->setListener(&g_Listen);
841 if (!g_pToolWnd->Show()) {
842 DoMessageBox("Error creating texture tools window!", "TexTool plugin", MB_ICONERROR | MB_OK);
847 g_bTexViewReady = false;
849 } else if (!strcmp(p, "Help...")) {
851 DoMessageBox("Select a brush face (ctrl+shift+left mouse) or a patch, and hit Go...\n"
852 "See tutorials for more", "TexTool plugin", MB_OK);
854 DoMessageBox("Are you kidding me ?", "TexTool plugin", MB_OK);
860 // =============================================================================
863 CSynapseServer *g_pSynapseServer = NULL;
864 CSynapseClientTexTool g_SynapseClient;
866 extern "C" CSynapseClient *SYNAPSE_DLL_EXPORT
868 Synapse_EnumerateInterfaces(const char *version, CSynapseServer *pServer)
870 if (strcmp(version, SYNAPSE_VERSION)) {
871 Syn_Printf("ERROR: synapse API version mismatch: should be '"
873 "', got '%s'\n", version );
876 g_pSynapseServer = pServer;
877 g_pSynapseServer->IncRef();
878 Set_Syn_Printf(g_pSynapseServer->Get_Syn_Printf());
880 g_SynapseClient.AddAPI(PLUGIN_MAJOR, "textool", sizeof(_QERPluginTable));
881 g_SynapseClient.AddAPI(RADIANT_MAJOR, NULL, sizeof(g_FuncTable), SYN_REQUIRE, &g_FuncTable);
882 g_SynapseClient.AddAPI(QGL_MAJOR, NULL, sizeof(g_QglTable), SYN_REQUIRE, &g_QglTable);
883 g_SynapseClient.AddAPI(SELECTEDFACE_MAJOR, NULL, sizeof(g_SelectedFaceTable), SYN_REQUIRE, &g_SelectedFaceTable);
885 return &g_SynapseClient;
888 bool CSynapseClientTexTool::RequestAPI(APIDescriptor_t *pAPI)
890 if (!strcmp(pAPI->major_name, PLUGIN_MAJOR)) {
891 _QERPluginTable *pTable = static_cast<_QERPluginTable *>( pAPI->mpTable );
892 pTable->m_pfnQERPlug_Init = QERPlug_Init;
893 pTable->m_pfnQERPlug_GetName = QERPlug_GetName;
894 pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList;
895 pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch;
899 Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());
905 const char *CSynapseClientTexTool::GetInfo()
907 return "Texture Tools plugin built " __DATE__ " "