X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fnetradiant.git;a=blobdiff_plain;f=plugins%2Ftextool%2FTexTool.cpp;h=eb519033e1061166787f2973f97379d543514e53;hp=01d88901fc6645a6b3a140a688e3451aada9e344;hb=d5fc00a4f8b5f77808fbedba8221c0cf8b856337;hpb=80378101101ca1762bbf5638a9e3566893096d8a diff --git a/plugins/textool/TexTool.cpp b/plugins/textool/TexTool.cpp index 01d88901..eb519033 100644 --- a/plugins/textool/TexTool.cpp +++ b/plugins/textool/TexTool.cpp @@ -1,962 +1,967 @@ -/* -Copyright (C) 1999-2007 id Software, Inc. and contributors. -For a list of contributors, see the accompanying CONTRIBUTORS file. - -This file is part of GtkRadiant. - -GtkRadiant is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -GtkRadiant is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GtkRadiant; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -//----------------------------------------------------------------------------- -// -// DESCRIPTION: -// main plugin implementation -// texturing tools for Radiant -// - -#include "StdAfx.h" - -static void dialog_button_callback (GtkWidget *widget, gpointer data) -{ - GtkWidget *parent; - int *loop, *ret; - - parent = gtk_widget_get_toplevel (widget); - loop = (int*)g_object_get_data (G_OBJECT (parent), "loop"); - ret = (int*)g_object_get_data (G_OBJECT (parent), "ret"); - - *loop = 0; - *ret = (int)data; -} - -static gint dialog_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data) -{ - int *loop; - - gtk_widget_hide (widget); - loop = (int*)g_object_get_data (G_OBJECT (widget), "loop"); - *loop = 0; - - return TRUE; -} - -int DoMessageBox (const char* lpText, const char* lpCaption, guint32 uType) -{ - GtkWidget *window, *w, *vbox, *hbox; - int mode = (uType & MB_TYPEMASK), ret, loop = 1; - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_signal_connect (GTK_OBJECT (window), "delete_event", - GTK_SIGNAL_FUNC (dialog_delete_callback), NULL); - gtk_signal_connect (GTK_OBJECT (window), "destroy", - GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL); - gtk_window_set_title (GTK_WINDOW (window), lpCaption); - gtk_container_border_width (GTK_CONTAINER (window), 10); - g_object_set_data (G_OBJECT (window), "loop", &loop); - g_object_set_data (G_OBJECT (window), "ret", &ret); - gtk_widget_realize (window); - - vbox = gtk_vbox_new (FALSE, 10); - gtk_container_add (GTK_CONTAINER (window), vbox); - gtk_widget_show (vbox); - - w = gtk_label_new (lpText); - gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2); - gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT); - gtk_widget_show (w); - - w = gtk_hseparator_new (); - gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2); - gtk_widget_show (w); - - hbox = gtk_hbox_new (FALSE, 10); - gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2); - gtk_widget_show (hbox); - - if (mode == MB_OK) - { - w = gtk_button_new_with_label ("Ok"); - gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); - gtk_signal_connect (GTK_OBJECT (w), "clicked", - GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK)); - GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); - gtk_widget_grab_default (w); - gtk_widget_show (w); - ret = IDOK; - } - else if (mode == MB_OKCANCEL) - { - w = gtk_button_new_with_label ("Ok"); - gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); - gtk_signal_connect (GTK_OBJECT (w), "clicked", - GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK)); - GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); - gtk_widget_grab_default (w); - gtk_widget_show (w); - - w = gtk_button_new_with_label ("Cancel"); - gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); - gtk_signal_connect (GTK_OBJECT (w), "clicked", - GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL)); - gtk_widget_show (w); - ret = IDCANCEL; - } - else if (mode == MB_YESNOCANCEL) - { - w = gtk_button_new_with_label ("Yes"); - gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); - gtk_signal_connect (GTK_OBJECT (w), "clicked", - GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES)); - GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); - gtk_widget_grab_default (w); - gtk_widget_show (w); - - w = gtk_button_new_with_label ("No"); - gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); - gtk_signal_connect (GTK_OBJECT (w), "clicked", - GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO)); - gtk_widget_show (w); - - w = gtk_button_new_with_label ("Cancel"); - gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); - gtk_signal_connect (GTK_OBJECT (w), "clicked", - GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL)); - gtk_widget_show (w); - ret = IDCANCEL; - } - else /* if (mode == MB_YESNO) */ - { - w = gtk_button_new_with_label ("Yes"); - gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); - gtk_signal_connect (GTK_OBJECT (w), "clicked", - GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES)); - GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); - gtk_widget_grab_default (w); - gtk_widget_show (w); - - w = gtk_button_new_with_label ("No"); - gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); - gtk_signal_connect (GTK_OBJECT (w), "clicked", - GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO)); - gtk_widget_show (w); - ret = IDNO; - } - - gtk_widget_show (window); - gtk_grab_add (window); - - while (loop) - gtk_main_iteration (); - - gtk_grab_remove (window); - gtk_widget_destroy (window); - - return ret; -} - -// Radiant function table -_QERFuncTable_1 g_FuncTable; - -// plugin name -const char *PLUGIN_NAME = "Q3 Texture Tools"; - -// commands in the menu -const char *PLUGIN_COMMANDS = "About...;Go..."; - -// cast to GtkWidget* -void *g_pMainWnd; -IWindow *g_pToolWnd = NULL; // handle to the window -CWindowListener g_Listen; - -// plugin interfaces --------------------------- -bool g_bQglInitDone = false; -_QERQglTable g_QglTable; -bool g_bSelectedFaceInitDone = false; -_QERSelectedFaceTable g_SelectedFaceTable; -bool g_bUITable = false; -_QERUITable g_UITable; - -// selected face ------------------------------- -// we use this one to commit / read with Radiant -_QERFaceData g_SelectedFaceData; -// g_pSelectedFaceWindings gets allocated with MAX_POINTS_ON_WINDING at plugin startup ( QERPlug_Init ) -winding_t *g_pSelectedFaceWinding = NULL; -const float g_ViewportRatio = 1.2f; -// usefull class to manage the 2D view -C2DView g_2DView; -// control points to move the polygon -CControlPointsManagerBFace g_ControlPointsBFace; -// tells if a face is selected and we have something to render in the TexWindow -bool g_bTexViewReady = false; -// data for texture work -int g_NumPoints; -CtrlPts_t g_WorkWinding; -// reference _QERFaceData we use on Cancel, and for Commit -_QERFaceData g_CancelFaceData; - -// patches ------------------------------------- -bool g_bPatch = false; -//++timo we use this one to grab selected patchMesh_t -// FIXME: update when there's a real interface to read/write patches -bool g_bSurfaceTableInitDone = false; -_QERAppSurfaceTable g_SurfaceTable; -CControlPointsManagerPatch g_ControlPointsPatch; -// data for texture work -patchMesh_t* g_pPatch; -// we only use ctrl[][].st in this one -patchMesh_t g_WorkPatch; -// copy of initial g_pPatch for Cancel situation -patchMesh_t g_CancelPatch; - -// --------------------------------------------- -// holds the manager we are currently using -CControlPointsManager *g_pManager = NULL; - -// --------------------------------------------- -// globals flags for user preferences -//++timo TODO: this should be retrieved from the Editor's .INI prefs in a dedicated interface -// update camera view during manipulation ? -bool g_bPrefsUpdateCameraView = true; - -// misc ---------------------------------------- -bool g_bHelp = false; -//++timo FIXME: used to close the plugin window if InitTexView fails -// it's dirty, only use is to prevent infinite loop in DialogProc -bool g_bClosing = false; - -const char *PLUGIN_ABOUT = "Texture Tools for Radiant\n\n" - "Gtk port by Leonardo Zide (leo@lokigames.com)\n" - "Original version by Timothee \"TTimo\" Besset (timo@qeradiant.com)"; - -extern "C" void* WINAPI QERPlug_GetFuncTable () -{ - return &g_FuncTable; -} - -const char* QERPlug_Init (void* hApp, void *pWidget) -{ - int size; - GtkWidget* pMainWidget = static_cast(pWidget); - - g_pMainWnd = pMainWidget; - memset(&g_FuncTable, 0, sizeof(_QERFuncTable_1)); - g_FuncTable.m_nSize = sizeof(_QERFuncTable_1); - size = (int)((winding_t *)0)->points[MAX_POINTS_ON_WINDING]; - g_pSelectedFaceWinding = (winding_t *)malloc( size ); - memset( g_pSelectedFaceWinding, 0, size ); - return "Texture tools for Radiant"; -} - -const char* QERPlug_GetName() -{ - return (char*)PLUGIN_NAME; -} - -const char* QERPlug_GetCommandList() -{ - return PLUGIN_COMMANDS; -} - -char *TranslateString (char *buf) -{ - static char buf2[32768]; - int i, l; - char *out; - - l = strlen(buf); - out = buf2; - for (i=0 ; igetHeight(); - g_2DView.m_rect.right = hwndDlg->getWidth(); - - // we need to draw this area, now compute a bigger area so the texture scale is the same along X and Y - // compute box shape in XY space, let's say X <-> S we'll get a ratio for Y: - if (!g_bPatch) - { - g_SelectedFaceTable.m_pfnGetTextureSize( 0, TexSize ); - } - else - { - TexSize[0] = g_pPatch->d_texture->width; - TexSize[1] = g_pPatch->d_texture->height; - } - // we want a texture with the same X / Y ratio - // compute XY space / window size ratio - float SSize = (float)fabs( g_2DView.m_Maxs[0] - g_2DView.m_Mins[0] ); - float TSize = (float)fabs( g_2DView.m_Maxs[1] - g_2DView.m_Mins[1] ); - float XSize = TexSize[0] * SSize; - float YSize = TexSize[1] * TSize; - float RatioX = XSize / (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right ); - float RatioY = YSize / (float)abs( g_2DView.m_rect.top - g_2DView.m_rect.bottom ); - if ( RatioX > RatioY ) - { - YSize = (float)abs( g_2DView.m_rect.top - g_2DView.m_rect.bottom ) * RatioX; - TSize = YSize / (float)TexSize[1]; - } - else - { - XSize = (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right ) * RatioY; - SSize = XSize / (float)TexSize[0]; - } - g_2DView.m_Mins[0] = g_2DView.m_Center[0] - 0.5f * SSize; - g_2DView.m_Maxs[0] = g_2DView.m_Center[0] + 0.5f * SSize; - g_2DView.m_Mins[1] = g_2DView.m_Center[1] - 0.5f * TSize; - g_2DView.m_Maxs[1] = g_2DView.m_Center[1] + 0.5f * TSize; -} - -// call this one each time we need to re-init -//++timo TODO: re-init objects state, g_2DView and g_ControlPointsManager -void InitTexView( IWindow* hwndDlg ) -{ - // size of the texture we are working on - int TexSize[2]; - g_bTexViewReady = false; - if (g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 0) - { - g_SelectedFaceTable.m_pfnGetFaceInfo( 0, &g_SelectedFaceData, g_pSelectedFaceWinding ); - g_bPatch = false; - int i; - // we have something selected - // setup: compute BBox for the winding ( in ST space ) - //++timo FIXME: move this in a C2DView member ? used as well for patches - g_2DView.m_Mins[0] = +9999.0f; g_2DView.m_Mins[1] = +9999.0f; - g_2DView.m_Maxs[0] = -9999.0f; g_2DView.m_Maxs[1] = -9999.0f; - for ( i=0; inumpoints; i++ ) - { - if ( g_pSelectedFaceWinding->points[i][3] < g_2DView.m_Mins[0] ) - g_2DView.m_Mins[0] = g_pSelectedFaceWinding->points[i][3]; - if ( g_pSelectedFaceWinding->points[i][3] > g_2DView.m_Maxs[0] ) - g_2DView.m_Maxs[0] = g_pSelectedFaceWinding->points[i][3]; - if ( g_pSelectedFaceWinding->points[i][4] < g_2DView.m_Mins[1] ) - g_2DView.m_Mins[1] = g_pSelectedFaceWinding->points[i][4]; - if ( g_pSelectedFaceWinding->points[i][4] > g_2DView.m_Maxs[1] ) - g_2DView.m_Maxs[1] = g_pSelectedFaceWinding->points[i][4]; - } - // NOTE: FitView will read and init TexSize - FitView( hwndDlg, TexSize ); - // now init the work tables - g_NumPoints = g_pSelectedFaceWinding->numpoints; - for ( i=0; ipoints[i][3]; - g_WorkWinding.data[i][1] = g_pSelectedFaceWinding->points[i][4]; - } - g_ControlPointsBFace.Init( g_NumPoints, &g_WorkWinding, &g_2DView, TexSize, &g_SelectedFaceData, &g_QglTable ); - // init snap-to-grid - float fTexStep[2]; - fTexStep[0] = 1.0f / float(TexSize[0]); - fTexStep[1] = 1.0f / float(TexSize[1]); - g_2DView.SetGrid( fTexStep[0], fTexStep[1] ); - g_pManager = &g_ControlPointsBFace; - // prepare the "Cancel" data - memcpy( &g_CancelFaceData, &g_SelectedFaceData, sizeof(_QERFaceData) ); - // we are done - g_bTexViewReady = true; - } - else if ( g_SurfaceTable.m_pfnAnyPatchesSelected()) - { - g_pPatch = g_SurfaceTable.m_pfnGetSelectedPatch(); - g_bPatch = true; - int i,j; - // compute BBox for all patch points - g_2DView.m_Mins[0] = +9999.0f; g_2DView.m_Mins[1] = +9999.0f; - g_2DView.m_Maxs[0] = -9999.0f; g_2DView.m_Maxs[1] = -9999.0f; - for ( i=0; iwidth; i++ ) - { - for ( j=0; jheight; j++ ) - { - if ( g_pPatch->ctrl[i][j].st[0] < g_2DView.m_Mins[0] ) - g_2DView.m_Mins[0] = g_pPatch->ctrl[i][j].st[0]; - if ( g_pPatch->ctrl[i][j].st[0] > g_2DView.m_Maxs[0] ) - g_2DView.m_Maxs[0] = g_pPatch->ctrl[i][j].st[0]; - if ( g_pPatch->ctrl[i][j].st[1] < g_2DView.m_Mins[1] ) - g_2DView.m_Mins[1] = g_pPatch->ctrl[i][j].st[1]; - if ( g_pPatch->ctrl[i][j].st[1] > g_2DView.m_Maxs[1] ) - g_2DView.m_Maxs[1] = g_pPatch->ctrl[i][j].st[1]; - } - } - FitView( hwndDlg, TexSize); - // init the work tables - g_WorkPatch = *g_pPatch; - g_ControlPointsPatch.Init( &g_WorkPatch, &g_2DView, &g_QglTable, g_pPatch ); - // init snap-to-grid - float fTexStep[2]; - fTexStep[0] = 1.0f / float(TexSize[0]); - fTexStep[1] = 1.0f / float(TexSize[1]); - g_2DView.SetGrid( fTexStep[0], fTexStep[1] ); - g_pManager = &g_ControlPointsPatch; - // prepare the "cancel" data - g_CancelPatch = *g_pPatch; - // we are done - g_bTexViewReady = true; - } -} - -void Textool_Validate() -{ - // validate current situation into the main view - g_pManager->Commit( ); - // for a brush face we have an aditionnal step - if (!g_bPatch) - { - // tell Radiant to update (will also send update windows messages ) - g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_SelectedFaceData ); - } - else - { - // ask to rebuild the patch display data - g_pPatch->bDirty = true; - // send a repaint to the camera window as well - g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA ); - } - // we'll need to update after that as well: - g_bTexViewReady = false; - // send a repaint message - g_pToolWnd->Redraw (); -} - -void Textool_Cancel() -{ - if (!g_bPatch) - { - // tell Radiant to update (will also send update windows messages ) - g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_CancelFaceData ); - } - else - { - *g_pPatch = g_CancelPatch; - g_pPatch->bDirty = true; - g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA ); - } - // do not call destroy, decref it - g_pToolWnd->DecRef(); - g_pToolWnd = NULL; -} - -static void DoExpose () -{ - int i,j; - - g_2DView.PreparePaint(); - g_QglTable.m_pfn_qglColor3f(1, 1, 1); - // draw the texture background - g_QglTable.m_pfn_qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); - if (!g_bPatch) - { - g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_SelectedFaceTable.m_pfnGetTextureNumber(0) ); - } - else - { - g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_pPatch->d_texture->texture_number ); - } - - g_QglTable.m_pfn_qglEnable( GL_TEXTURE_2D ); - g_QglTable.m_pfn_qglBegin( GL_QUADS ); - g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Mins[0], g_2DView.m_Mins[1] ); - g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Mins[0], g_2DView.m_Mins[1] ); - g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Maxs[0], g_2DView.m_Mins[1] ); - g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Maxs[0], g_2DView.m_Mins[1] ); - g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Maxs[0], g_2DView.m_Maxs[1] ); - g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Maxs[0], g_2DView.m_Maxs[1] ); - g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Mins[0], g_2DView.m_Maxs[1] ); - g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Mins[0], g_2DView.m_Maxs[1] ); - g_QglTable.m_pfn_qglEnd(); - g_QglTable.m_pfn_qglDisable( GL_TEXTURE_2D ); - - if (!g_bPatch) - { - g_QglTable.m_pfn_qglBegin( GL_LINE_LOOP ); - for ( i=0; iwidth; i++ ) - for ( j=0; jheight; j++ ) - { - if ( i < g_pPatch->width-1 ) - { - g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j].st[0], g_WorkPatch.ctrl[i][j].st[1] ); - g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i+1][j].st[0], g_WorkPatch.ctrl[i+1][j].st[1] ); - } - - if ( j < g_pPatch->height-1 ) - { - g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j].st[0], g_WorkPatch.ctrl[i][j].st[1] ); - g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j+1].st[0], g_WorkPatch.ctrl[i][j+1].st[1] ); - } - } - g_QglTable.m_pfn_qglEnd(); - } - - // let the control points manager render - g_pManager->Render( ); -} - -static bool CanProcess () -{ - if (!g_bTexViewReady && !g_bClosing) - { - InitTexView (g_pToolWnd); - - if (!g_bTexViewReady) - { - g_bClosing = true; - DoMessageBox ("You must have brush primitives activated in your project settings and\n" - "have a patch or a single face selected to use the TexTool plugin.\n" - "See plugins/TexToolHelp for documentation.", "TexTool plugin", MB_ICONERROR | MB_OK); - // decref, this will destroy - g_pToolWnd->DecRef(); - g_pToolWnd = NULL; - return 0; - } - else - g_bClosing = false; - } - else if (!g_bTexViewReady && g_bClosing) - { - return 0; - } - - return 1; -} - -#if 0 -static void button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) -{ - if (CanProcess ()) - { - switch (event->button) - { - case 1: - g_pManager->OnLButtonDown (event->x, event->y); break; - case 3: - g_2DView.OnRButtonDown (event->x, event->y); break; - } - } -} - -static void button_release (GtkWidget *widget, GdkEventButton *event, gpointer data) -{ - if (CanProcess ()) - { - switch (event->button) - { - case 1: - g_pManager->OnLButtonUp (event->x, event->y); break; - case 3: - g_2DView.OnRButtonUp (event->x, event->y); break; - } - } -} - -static void motion (GtkWidget *widget, GdkEventMotion *event, gpointer data) -{ - if (CanProcess ()) - { - if (g_2DView.OnMouseMove (event->x, event->y)) - return; - - if (g_pManager->OnMouseMove (event->x, event->y)) - return; - } -} - -static gint expose (GtkWidget *widget, GdkEventExpose *event, gpointer data) -{ - if (event->count > 0) - return TRUE; - - if (!CanProcess ()) - return TRUE; - - if (g_bTexViewReady) - { - g_2DView.m_rect.bottom = widget->allocation.height; - g_2DView.m_rect.right = widget->allocation.width; - - if (!g_QglTable.m_pfn_glwidget_make_current (g_pToolWidget)) - { - Sys_Printf("TexTool: glMakeCurrent failed\n"); - return TRUE; - } - - DoExpose (); - - g_QglTable.m_pfn_glwidget_swap_buffers (g_pToolWidget); - } - - return TRUE; -} - -static gint keypress (GtkWidget* widget, GdkEventKey* event, gpointer data) -{ - unsigned int code = gdk_keyval_to_upper(event->keyval); - - if (code == GDK_Escape) - { - gtk_widget_destroy (g_pToolWnd); - g_pToolWnd = NULL; - return TRUE; - } - - if (CanProcess ()) - { - if (g_2DView.OnKeyDown (code)) - return FALSE; - - if (code == GDK_Return) - { - Textool_Validate(); - return FALSE; - } - } - - return TRUE; -} - -static gint close (GtkWidget *widget, GdkEvent* event, gpointer data) -{ - gtk_widget_destroy (widget); - g_pToolWnd = NULL; - - return TRUE; -} - -static GtkWidget* CreateOpenGLWidget () -{ - g_pToolWidget = g_QglTable.m_pfn_glwidget_new (FALSE, g_QglTable.m_pfn_GetQeglobalsGLWidget ()); - - gtk_widget_set_events (g_pToolWidget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK); - - // Connect signal handlers - gtk_signal_connect (GTK_OBJECT (g_pToolWidget), "expose_event", GTK_SIGNAL_FUNC (expose), NULL); - gtk_signal_connect (GTK_OBJECT (g_pToolWidget), "motion_notify_event", - GTK_SIGNAL_FUNC (motion), NULL); - gtk_signal_connect (GTK_OBJECT (g_pToolWidget), "button_press_event", - GTK_SIGNAL_FUNC (button_press), NULL); - gtk_signal_connect (GTK_OBJECT (g_pToolWidget), "button_release_event", - GTK_SIGNAL_FUNC (button_release), NULL); - - gtk_signal_connect (GTK_OBJECT (g_pToolWnd), "delete_event", GTK_SIGNAL_FUNC (close), NULL); - gtk_signal_connect (GTK_OBJECT (g_pToolWnd), "key_press_event", - GTK_SIGNAL_FUNC (keypress), NULL); - - return g_pToolWidget; -} -#endif - -#if 0 -static void DoPaint () -{ - if (!CanProcess ()) - return; - - if (g_bTexViewReady) - { - g_2DView.m_rect.bottom = g_pToolWnd->getHeight(); - g_2DView.m_rect.right = g_pToolWnd->getWidth(); - - // set GL_PROJECTION - g_2DView.PreparePaint(); - // render the objects - // the master is not rendered the same way, draw over a unified texture background - g_2DView.TextureBackground(g_DrawObjects[0].pObject->getTextureNumber()); - if (g_nDrawObjects >= 1) - { - int i; - for (i=1;iPrepareModelView(g_DrawObjects[i].pTopo); - g_DrawObjects[i].pObject->RenderAuxiliary(); - } - } - // draw the polygon outline and control points - g_DrawObjects[0].pObject->PrepareModelView(NULL); - g_DrawObjects[0].pObject->RenderUI(); - } -} -#endif - -bool CWindowListener::OnLButtonDown(guint32 nFlags, double x, double y) -{ - if (CanProcess()) - { - g_pManager->OnLButtonDown((int)x, (int)y); - return true; - } - return false; -} - -bool CWindowListener::OnRButtonDown(guint32 nFlags, double x, double y) -{ - if (CanProcess()) - { - g_2DView.OnRButtonDown ((int)x, (int)y); - return true; - } - return false; -} - -bool CWindowListener::OnLButtonUp(guint32 nFlags, double x, double y) -{ - if (CanProcess()) - { - g_pManager->OnLButtonUp((int)x, (int)y); - return true; - } - return false; -} - -bool CWindowListener::OnRButtonUp(guint32 nFlags, double x, double y) -{ - if (CanProcess()) - { - g_2DView.OnRButtonUp ((int)x, (int)y); - return true; - } - return false; -} - -bool CWindowListener::OnMouseMove(guint32 nFlags, double x, double y) -{ - if (CanProcess ()) - { - if (g_2DView.OnMouseMove ((int)x, (int)y)) - return true; - - g_pManager->OnMouseMove((int)x, (int)y); - return true; - } - return false; -} - -// the widget is closing -void CWindowListener::Close() -{ - g_pToolWnd = NULL; -} - -bool CWindowListener::Paint() -{ - if (!CanProcess ()) - return false; - - if (g_bTexViewReady) - DoExpose(); - - return true; -} - -bool CWindowListener::OnKeyPressed(char *s) -{ - if (!strcmp(s,"Escape")) - { - Textool_Cancel(); - return TRUE; - } - if (CanProcess ()) - { - if (g_2DView.OnKeyDown (s)) - return TRUE; - - if (!strcmp(s,"Return")) - { - Textool_Validate(); - return TRUE; - } - } - return FALSE; -} - -extern "C" void QERPlug_Dispatch(const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush) -{ - #if 0 - // if it's the first call, perhaps we need some additional init steps - if (!g_bQglInitDone) - { - g_QglTable.m_nSize = sizeof(_QERQglTable); - if ( g_FuncTable.m_pfnRequestInterface( QERQglTable_GUID, static_cast(&g_QglTable) ) ) - { - g_bQglInitDone = true; - } - else - { - Sys_Printf("TexTool plugin: _QERQglTable interface request failed\n"); - return; - } - } - - if (!g_bSelectedFaceInitDone) - { - g_SelectedFaceTable.m_nSize = sizeof(_QERSelectedFaceTable); - if (g_FuncTable.m_pfnRequestInterface (QERSelectedFaceTable_GUID, - static_cast(&g_SelectedFaceTable))) - { - g_bSelectedFaceInitDone = true; - } - else - { - Sys_Printf("TexTool plugin: _QERSelectedFaceTable interface request failed\n"); - return; - } - } - - if (!g_bSurfaceTableInitDone) - { - g_SurfaceTable.m_nSize = sizeof(_QERAppSurfaceTable); - if ( g_FuncTable.m_pfnRequestInterface( QERAppSurfaceTable_GUID, static_cast(&g_SurfaceTable) ) ) - { - g_bSurfaceTableInitDone = true; - } - else - { - Sys_Printf("TexTool plugin: _QERAppSurfaceTable interface request failed\n"); - return; - } - } - - if (!g_bUITable) - { - g_UITable.m_nSize = sizeof(_QERUITable); - if ( g_FuncTable.m_pfnRequestInterface( QERUI_GUID, static_cast(&g_UITable) ) ) - { - g_bUITable = true; - } - else - { - Sys_Printf("TexTool plugin: _QERUITable interface request failed\n"); - return; - } - } - #endif - - if (!strcmp(p, "About...")) - { - DoMessageBox (PLUGIN_ABOUT, "About ...", MB_OK ); - } - else if (!strcmp(p, "Go...")) - { - if (!g_pToolWnd) - { - g_pToolWnd = g_UITable.m_pfnCreateGLWindow(); - g_pToolWnd->setSizeParm(300,300); - g_pToolWnd->setName("TexTool"); - // g_Listener is a static class, we need to bump the refCount to avoid premature release problems - g_Listen.IncRef(); - // setListener will incRef on the listener too - g_pToolWnd->setListener(&g_Listen); - if (!g_pToolWnd->Show()) - { - DoMessageBox ("Error creating texture tools window!", "TexTool plugin", MB_ICONERROR | MB_OK); - return; - } - } - - g_bTexViewReady = false; - g_bClosing = false; - } - else if (!strcmp(p, "Help...")) - { - if (!g_bHelp) - DoMessageBox ("Select a brush face (ctrl+shift+left mouse) or a patch, and hit Go...\n" - "See tutorials for more", "TexTool plugin", MB_OK ); - else - DoMessageBox ("Are you kidding me ?", "TexTool plugin", MB_OK ); - g_bHelp = true; - } -} - -// ============================================================================= -// SYNAPSE - -CSynapseServer* g_pSynapseServer = NULL; -CSynapseClientTexTool g_SynapseClient; - -extern "C" CSynapseClient* SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces (const char *version, CSynapseServer *pServer) -{ - if (strcmp(version, SYNAPSE_VERSION)) - { - Syn_Printf("ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version); - return NULL; - } - g_pSynapseServer = pServer; - g_pSynapseServer->IncRef(); - Set_Syn_Printf(g_pSynapseServer->Get_Syn_Printf()); - - g_SynapseClient.AddAPI(PLUGIN_MAJOR, "textool", sizeof(_QERPluginTable)); - g_SynapseClient.AddAPI(RADIANT_MAJOR, NULL, sizeof(g_FuncTable), SYN_REQUIRE, &g_FuncTable); - g_SynapseClient.AddAPI(QGL_MAJOR, NULL, sizeof(g_QglTable), SYN_REQUIRE, &g_QglTable); - g_SynapseClient.AddAPI(SELECTEDFACE_MAJOR, NULL, sizeof(g_SelectedFaceTable), SYN_REQUIRE, &g_SelectedFaceTable); - - return &g_SynapseClient; -} - -bool CSynapseClientTexTool::RequestAPI(APIDescriptor_t *pAPI) -{ - if (!strcmp(pAPI->major_name, PLUGIN_MAJOR)) - { - _QERPluginTable *pTable = static_cast<_QERPluginTable*>(pAPI->mpTable); - pTable->m_pfnQERPlug_Init = QERPlug_Init; - pTable->m_pfnQERPlug_GetName = QERPlug_GetName; - pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList; - pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch; - return true; - } - - Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo()); - return false; -} - -#include "version.h" - -const char* CSynapseClientTexTool::GetInfo() -{ - return "Texture Tools plugin built " __DATE__ " " RADIANT_VERSION; -} +/* +Copyright (C) 1999-2007 id Software, Inc. and contributors. +For a list of contributors, see the accompanying CONTRIBUTORS file. + +This file is part of GtkRadiant. + +GtkRadiant is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +GtkRadiant is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GtkRadiant; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// +// DESCRIPTION: +// main plugin implementation +// texturing tools for Radiant +// + +#include "StdAfx.h" + +static void dialog_button_callback (GtkWidget *widget, gpointer data) +{ + GtkWidget *parent; + int *loop, *ret; + + parent = gtk_widget_get_toplevel (widget); + loop = (int*)g_object_get_data (G_OBJECT (parent), "loop"); + ret = (int*)g_object_get_data (G_OBJECT (parent), "ret"); + + *loop = 0; + *ret = (int)data; +} + +static gint dialog_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data) +{ + int *loop; + + gtk_widget_hide (widget); + loop = (int*)g_object_get_data (G_OBJECT (widget), "loop"); + *loop = 0; + + return TRUE; +} + +int DoMessageBox (const char* lpText, const char* lpCaption, guint32 uType) +{ + GtkWidget *window, *w, *vbox, *hbox; + int mode = (uType & MB_TYPEMASK), ret, loop = 1; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT (window), "delete_event", + GTK_SIGNAL_FUNC (dialog_delete_callback), NULL); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL); + gtk_window_set_title (GTK_WINDOW (window), lpCaption); + gtk_container_border_width (GTK_CONTAINER (window), 10); + g_object_set_data (G_OBJECT (window), "loop", &loop); + g_object_set_data (G_OBJECT (window), "ret", &ret); + gtk_widget_realize (window); + + vbox = gtk_vbox_new (FALSE, 10); + gtk_container_add (GTK_CONTAINER (window), vbox); + gtk_widget_show (vbox); + + w = gtk_label_new (lpText); + gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2); + gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT); + gtk_widget_show (w); + + w = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2); + gtk_widget_show (w); + + hbox = gtk_hbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2); + gtk_widget_show (hbox); + + if (mode == MB_OK) + { + w = gtk_button_new_with_label ("Ok"); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (w), "clicked", + GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK)); + GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); + gtk_widget_grab_default (w); + gtk_widget_show (w); + ret = IDOK; + } + else if (mode == MB_OKCANCEL) + { + w = gtk_button_new_with_label ("Ok"); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (w), "clicked", + GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK)); + GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); + gtk_widget_grab_default (w); + gtk_widget_show (w); + + w = gtk_button_new_with_label ("Cancel"); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (w), "clicked", + GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL)); + gtk_widget_show (w); + ret = IDCANCEL; + } + else if (mode == MB_YESNOCANCEL) + { + w = gtk_button_new_with_label ("Yes"); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (w), "clicked", + GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES)); + GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); + gtk_widget_grab_default (w); + gtk_widget_show (w); + + w = gtk_button_new_with_label ("No"); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (w), "clicked", + GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO)); + gtk_widget_show (w); + + w = gtk_button_new_with_label ("Cancel"); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (w), "clicked", + GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL)); + gtk_widget_show (w); + ret = IDCANCEL; + } + else /* if (mode == MB_YESNO) */ + { + w = gtk_button_new_with_label ("Yes"); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (w), "clicked", + GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES)); + GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT); + gtk_widget_grab_default (w); + gtk_widget_show (w); + + w = gtk_button_new_with_label ("No"); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + gtk_signal_connect (GTK_OBJECT (w), "clicked", + GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO)); + gtk_widget_show (w); + ret = IDNO; + } + + gtk_widget_show (window); + gtk_grab_add (window); + + while (loop) + gtk_main_iteration (); + + gtk_grab_remove (window); + gtk_widget_destroy (window); + + return ret; +} + +// Radiant function table +_QERFuncTable_1 g_FuncTable; + +// plugin name +const char *PLUGIN_NAME = "Q3 Texture Tools"; + +// commands in the menu +const char *PLUGIN_COMMANDS = "About...;Go..."; + +// cast to GtkWidget* +void *g_pMainWnd; +IWindow *g_pToolWnd = NULL; // handle to the window +CWindowListener g_Listen; + +// plugin interfaces --------------------------- +bool g_bQglInitDone = false; +_QERQglTable g_QglTable; +bool g_bSelectedFaceInitDone = false; +_QERSelectedFaceTable g_SelectedFaceTable; +bool g_bUITable = false; +_QERUITable g_UITable; + +// selected face ------------------------------- +// we use this one to commit / read with Radiant +_QERFaceData g_SelectedFaceData; +// g_pSelectedFaceWindings gets allocated with MAX_POINTS_ON_WINDING at plugin startup ( QERPlug_Init ) +winding_t *g_pSelectedFaceWinding = NULL; +const float g_ViewportRatio = 1.2f; +// usefull class to manage the 2D view +C2DView g_2DView; +// control points to move the polygon +CControlPointsManagerBFace g_ControlPointsBFace; +// tells if a face is selected and we have something to render in the TexWindow +bool g_bTexViewReady = false; +// data for texture work +int g_NumPoints; +CtrlPts_t g_WorkWinding; +// reference _QERFaceData we use on Cancel, and for Commit +_QERFaceData g_CancelFaceData; + +// patches ------------------------------------- +bool g_bPatch = false; +//++timo we use this one to grab selected patchMesh_t +// FIXME: update when there's a real interface to read/write patches +bool g_bSurfaceTableInitDone = false; +_QERAppSurfaceTable g_SurfaceTable; +CControlPointsManagerPatch g_ControlPointsPatch; +// data for texture work +patchMesh_t* g_pPatch; +// we only use ctrl[][].st in this one +patchMesh_t g_WorkPatch; +// copy of initial g_pPatch for Cancel situation +patchMesh_t g_CancelPatch; + +// --------------------------------------------- +// holds the manager we are currently using +CControlPointsManager *g_pManager = NULL; + +// --------------------------------------------- +// globals flags for user preferences +//++timo TODO: this should be retrieved from the Editor's .INI prefs in a dedicated interface +// update camera view during manipulation ? +bool g_bPrefsUpdateCameraView = true; + +// misc ---------------------------------------- +bool g_bHelp = false; +//++timo FIXME: used to close the plugin window if InitTexView fails +// it's dirty, only use is to prevent infinite loop in DialogProc +bool g_bClosing = false; + +const char *PLUGIN_ABOUT = "Texture Tools for Radiant\n\n" + "Gtk port by Leonardo Zide (leo@lokigames.com)\n" + "Original version by Timothee \"TTimo\" Besset (timo@qeradiant.com)"; + +extern "C" void* WINAPI QERPlug_GetFuncTable () +{ + return &g_FuncTable; +} + +const char* QERPlug_Init (void* hApp, void *pWidget) +{ + int size; + GtkWidget* pMainWidget = static_cast(pWidget); + + g_pMainWnd = pMainWidget; + memset(&g_FuncTable, 0, sizeof(_QERFuncTable_1)); + g_FuncTable.m_nSize = sizeof(_QERFuncTable_1); + size = (int)((winding_t *)0)->points[MAX_POINTS_ON_WINDING]; + g_pSelectedFaceWinding = (winding_t *)malloc( size ); + memset( g_pSelectedFaceWinding, 0, size ); + return "Texture tools for Radiant"; +} + +const char* QERPlug_GetName() +{ + return (char*)PLUGIN_NAME; +} + +const char* QERPlug_GetCommandList() +{ + return PLUGIN_COMMANDS; +} + +char *TranslateString (char *buf) +{ + static char buf2[32768]; + int i, l; + char *out; + + l = strlen(buf); + out = buf2; + for (i=0 ; igetHeight(); + g_2DView.m_rect.right = hwndDlg->getWidth(); + + // we need to draw this area, now compute a bigger area so the texture scale is the same along X and Y + // compute box shape in XY space, let's say X <-> S we'll get a ratio for Y: + if (!g_bPatch) + { + g_SelectedFaceTable.m_pfnGetTextureSize( 0, TexSize ); + } + else + { + TexSize[0] = g_pPatch->d_texture->width; + TexSize[1] = g_pPatch->d_texture->height; + } + // we want a texture with the same X / Y ratio + // compute XY space / window size ratio + float SSize = (float)fabs( g_2DView.m_Maxs[0] - g_2DView.m_Mins[0] ); + float TSize = (float)fabs( g_2DView.m_Maxs[1] - g_2DView.m_Mins[1] ); + float XSize = TexSize[0] * SSize; + float YSize = TexSize[1] * TSize; + float RatioX = XSize / (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right ); + float RatioY = YSize / (float)abs( g_2DView.m_rect.top - g_2DView.m_rect.bottom ); + if ( RatioX > RatioY ) + { + YSize = (float)abs( g_2DView.m_rect.top - g_2DView.m_rect.bottom ) * RatioX; + TSize = YSize / (float)TexSize[1]; + } + else + { + XSize = (float)abs( g_2DView.m_rect.left - g_2DView.m_rect.right ) * RatioY; + SSize = XSize / (float)TexSize[0]; + } + g_2DView.m_Mins[0] = g_2DView.m_Center[0] - 0.5f * SSize; + g_2DView.m_Maxs[0] = g_2DView.m_Center[0] + 0.5f * SSize; + g_2DView.m_Mins[1] = g_2DView.m_Center[1] - 0.5f * TSize; + g_2DView.m_Maxs[1] = g_2DView.m_Center[1] + 0.5f * TSize; +} + +// call this one each time we need to re-init +//++timo TODO: re-init objects state, g_2DView and g_ControlPointsManager +void InitTexView( IWindow* hwndDlg ) +{ + // size of the texture we are working on + int TexSize[2]; + g_bTexViewReady = false; + if (g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 0) + { + g_SelectedFaceTable.m_pfnGetFaceInfo( 0, &g_SelectedFaceData, g_pSelectedFaceWinding ); + g_bPatch = false; + int i; + // we have something selected + // setup: compute BBox for the winding ( in ST space ) + //++timo FIXME: move this in a C2DView member ? used as well for patches + g_2DView.m_Mins[0] = +9999.0f; g_2DView.m_Mins[1] = +9999.0f; + g_2DView.m_Maxs[0] = -9999.0f; g_2DView.m_Maxs[1] = -9999.0f; + for ( i=0; inumpoints; i++ ) + { + if ( g_pSelectedFaceWinding->points[i][3] < g_2DView.m_Mins[0] ) + g_2DView.m_Mins[0] = g_pSelectedFaceWinding->points[i][3]; + if ( g_pSelectedFaceWinding->points[i][3] > g_2DView.m_Maxs[0] ) + g_2DView.m_Maxs[0] = g_pSelectedFaceWinding->points[i][3]; + if ( g_pSelectedFaceWinding->points[i][4] < g_2DView.m_Mins[1] ) + g_2DView.m_Mins[1] = g_pSelectedFaceWinding->points[i][4]; + if ( g_pSelectedFaceWinding->points[i][4] > g_2DView.m_Maxs[1] ) + g_2DView.m_Maxs[1] = g_pSelectedFaceWinding->points[i][4]; + } + // NOTE: FitView will read and init TexSize + FitView( hwndDlg, TexSize ); + // now init the work tables + g_NumPoints = g_pSelectedFaceWinding->numpoints; + for ( i=0; ipoints[i][3]; + g_WorkWinding.data[i][1] = g_pSelectedFaceWinding->points[i][4]; + } + g_ControlPointsBFace.Init( g_NumPoints, &g_WorkWinding, &g_2DView, TexSize, &g_SelectedFaceData, &g_QglTable ); + // init snap-to-grid + float fTexStep[2]; + fTexStep[0] = 1.0f / float(TexSize[0]); + fTexStep[1] = 1.0f / float(TexSize[1]); + g_2DView.SetGrid( fTexStep[0], fTexStep[1] ); + g_pManager = &g_ControlPointsBFace; + // prepare the "Cancel" data + memcpy( &g_CancelFaceData, &g_SelectedFaceData, sizeof(_QERFaceData) ); + // we are done + g_bTexViewReady = true; + } + else if ( g_SurfaceTable.m_pfnAnyPatchesSelected()) + { + g_pPatch = g_SurfaceTable.m_pfnGetSelectedPatch(); + g_bPatch = true; + int i,j; + // compute BBox for all patch points + g_2DView.m_Mins[0] = +9999.0f; g_2DView.m_Mins[1] = +9999.0f; + g_2DView.m_Maxs[0] = -9999.0f; g_2DView.m_Maxs[1] = -9999.0f; + for ( i=0; iwidth; i++ ) + { + for ( j=0; jheight; j++ ) + { + if ( g_pPatch->ctrl[i][j].st[0] < g_2DView.m_Mins[0] ) + g_2DView.m_Mins[0] = g_pPatch->ctrl[i][j].st[0]; + if ( g_pPatch->ctrl[i][j].st[0] > g_2DView.m_Maxs[0] ) + g_2DView.m_Maxs[0] = g_pPatch->ctrl[i][j].st[0]; + if ( g_pPatch->ctrl[i][j].st[1] < g_2DView.m_Mins[1] ) + g_2DView.m_Mins[1] = g_pPatch->ctrl[i][j].st[1]; + if ( g_pPatch->ctrl[i][j].st[1] > g_2DView.m_Maxs[1] ) + g_2DView.m_Maxs[1] = g_pPatch->ctrl[i][j].st[1]; + } + } + FitView( hwndDlg, TexSize); + // init the work tables + g_WorkPatch = *g_pPatch; + g_ControlPointsPatch.Init( &g_WorkPatch, &g_2DView, &g_QglTable, g_pPatch ); + // init snap-to-grid + float fTexStep[2]; + fTexStep[0] = 1.0f / float(TexSize[0]); + fTexStep[1] = 1.0f / float(TexSize[1]); + g_2DView.SetGrid( fTexStep[0], fTexStep[1] ); + g_pManager = &g_ControlPointsPatch; + // prepare the "cancel" data + g_CancelPatch = *g_pPatch; + // we are done + g_bTexViewReady = true; + } +} + +void Textool_Validate() +{ + // validate current situation into the main view + g_pManager->Commit( ); + // for a brush face we have an aditionnal step + if (!g_bPatch) + { + // tell Radiant to update (will also send update windows messages ) + g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_SelectedFaceData ); + } + else + { + // ask to rebuild the patch display data + g_pPatch->bDirty = true; + // send a repaint to the camera window as well + g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA ); + } + // we'll need to update after that as well: + g_bTexViewReady = false; + // send a repaint message + g_pToolWnd->Redraw (); +} + +void Textool_Cancel() +{ + if (!g_bPatch) + { + // tell Radiant to update (will also send update windows messages ) + g_SelectedFaceTable.m_pfnSetFaceInfo( 0, &g_CancelFaceData ); + } + else + { + *g_pPatch = g_CancelPatch; + g_pPatch->bDirty = true; + g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA ); + } + // do not call destroy, decref it + g_pToolWnd->DecRef(); + g_pToolWnd = NULL; +} + +static void DoExpose () +{ + int i,j; + + g_2DView.PreparePaint(); + g_QglTable.m_pfn_qglColor3f(1, 1, 1); + // draw the texture background + g_QglTable.m_pfn_qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + if (!g_bPatch) + { + g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_SelectedFaceTable.m_pfnGetTextureNumber(0) ); + } + else + { + g_QglTable.m_pfn_qglBindTexture( GL_TEXTURE_2D, g_pPatch->d_texture->texture_number ); + } + + g_QglTable.m_pfn_qglEnable( GL_TEXTURE_2D ); + g_QglTable.m_pfn_qglBegin( GL_QUADS ); + g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Mins[0], g_2DView.m_Mins[1] ); + g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Mins[0], g_2DView.m_Mins[1] ); + g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Maxs[0], g_2DView.m_Mins[1] ); + g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Maxs[0], g_2DView.m_Mins[1] ); + g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Maxs[0], g_2DView.m_Maxs[1] ); + g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Maxs[0], g_2DView.m_Maxs[1] ); + g_QglTable.m_pfn_qglTexCoord2f( g_2DView.m_Mins[0], g_2DView.m_Maxs[1] ); + g_QglTable.m_pfn_qglVertex2f( g_2DView.m_Mins[0], g_2DView.m_Maxs[1] ); + g_QglTable.m_pfn_qglEnd(); + g_QglTable.m_pfn_qglDisable( GL_TEXTURE_2D ); + + if (!g_bPatch) + { + g_QglTable.m_pfn_qglBegin( GL_LINE_LOOP ); + for ( i=0; iwidth; i++ ) + for ( j=0; jheight; j++ ) + { + if ( i < g_pPatch->width-1 ) + { + g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j].st[0], g_WorkPatch.ctrl[i][j].st[1] ); + g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i+1][j].st[0], g_WorkPatch.ctrl[i+1][j].st[1] ); + } + + if ( j < g_pPatch->height-1 ) + { + g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j].st[0], g_WorkPatch.ctrl[i][j].st[1] ); + g_QglTable.m_pfn_qglVertex2f( g_WorkPatch.ctrl[i][j+1].st[0], g_WorkPatch.ctrl[i][j+1].st[1] ); + } + } + g_QglTable.m_pfn_qglEnd(); + } + + // let the control points manager render + g_pManager->Render( ); +} + +static bool CanProcess () +{ + if (!g_bTexViewReady && !g_bClosing) + { + InitTexView (g_pToolWnd); + + if (!g_bTexViewReady) + { + g_bClosing = true; + DoMessageBox ("You must have brush primitives activated in your project settings and\n" + "have a patch or a single face selected to use the TexTool plugin.\n" + "See plugins/TexToolHelp for documentation.", "TexTool plugin", MB_ICONERROR | MB_OK); + // decref, this will destroy + g_pToolWnd->DecRef(); + g_pToolWnd = NULL; + return 0; + } + else + g_bClosing = false; + } + else if (!g_bTexViewReady && g_bClosing) + { + return 0; + } + + return 1; +} + +#if 0 +static void button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) +{ + if (CanProcess ()) + { + switch (event->button) + { + case 1: + g_pManager->OnLButtonDown (event->x, event->y); break; + case 3: + g_2DView.OnRButtonDown (event->x, event->y); break; + } + } +} + +static void button_release (GtkWidget *widget, GdkEventButton *event, gpointer data) +{ + if (CanProcess ()) + { + switch (event->button) + { + case 1: + g_pManager->OnLButtonUp (event->x, event->y); break; + case 3: + g_2DView.OnRButtonUp (event->x, event->y); break; + } + } +} + +static void motion (GtkWidget *widget, GdkEventMotion *event, gpointer data) +{ + if (CanProcess ()) + { + if (g_2DView.OnMouseMove (event->x, event->y)) + return; + + if (g_pManager->OnMouseMove (event->x, event->y)) + return; + } +} + +static gint expose (GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + if (event->count > 0) + return TRUE; + + if (!CanProcess ()) + return TRUE; + + if (g_bTexViewReady) + { + g_2DView.m_rect.bottom = widget->allocation.height; + g_2DView.m_rect.right = widget->allocation.width; + + if (!g_QglTable.m_pfn_glwidget_make_current (g_pToolWidget)) + { + Sys_Printf("TexTool: glMakeCurrent failed\n"); + return TRUE; + } + + DoExpose (); + + g_QglTable.m_pfn_glwidget_swap_buffers (g_pToolWidget); + } + + return TRUE; +} + +static gint keypress (GtkWidget* widget, GdkEventKey* event, gpointer data) +{ + unsigned int code = gdk_keyval_to_upper(event->keyval); + + if (code == GDK_Escape) + { + gtk_widget_destroy (g_pToolWnd); + g_pToolWnd = NULL; + return TRUE; + } + + if (CanProcess ()) + { + if (g_2DView.OnKeyDown (code)) + return FALSE; + + if (code == GDK_Return) + { + Textool_Validate(); + return FALSE; + } + } + + return TRUE; +} + +static gint close (GtkWidget *widget, GdkEvent* event, gpointer data) +{ + gtk_widget_destroy (widget); + g_pToolWnd = NULL; + + return TRUE; +} + +static GtkWidget* CreateOpenGLWidget () +{ + g_pToolWidget = g_QglTable.m_pfn_glwidget_new (FALSE, g_QglTable.m_pfn_GetQeglobalsGLWidget ()); + + gtk_widget_set_events (g_pToolWidget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK); + + // Connect signal handlers + gtk_signal_connect (GTK_OBJECT (g_pToolWidget), "expose_event", GTK_SIGNAL_FUNC (expose), NULL); + gtk_signal_connect (GTK_OBJECT (g_pToolWidget), "motion_notify_event", + GTK_SIGNAL_FUNC (motion), NULL); + gtk_signal_connect (GTK_OBJECT (g_pToolWidget), "button_press_event", + GTK_SIGNAL_FUNC (button_press), NULL); + gtk_signal_connect (GTK_OBJECT (g_pToolWidget), "button_release_event", + GTK_SIGNAL_FUNC (button_release), NULL); + + gtk_signal_connect (GTK_OBJECT (g_pToolWnd), "delete_event", GTK_SIGNAL_FUNC (close), NULL); + gtk_signal_connect (GTK_OBJECT (g_pToolWnd), "key_press_event", + GTK_SIGNAL_FUNC (keypress), NULL); + + return g_pToolWidget; +} +#endif + +#if 0 +static void DoPaint () +{ + if (!CanProcess ()) + return; + + if (g_bTexViewReady) + { + g_2DView.m_rect.bottom = g_pToolWnd->getHeight(); + g_2DView.m_rect.right = g_pToolWnd->getWidth(); + + // set GL_PROJECTION + g_2DView.PreparePaint(); + // render the objects + // the master is not rendered the same way, draw over a unified texture background + g_2DView.TextureBackground(g_DrawObjects[0].pObject->getTextureNumber()); + if (g_nDrawObjects >= 1) + { + int i; + for (i=1;iPrepareModelView(g_DrawObjects[i].pTopo); + g_DrawObjects[i].pObject->RenderAuxiliary(); + } + } + // draw the polygon outline and control points + g_DrawObjects[0].pObject->PrepareModelView(NULL); + g_DrawObjects[0].pObject->RenderUI(); + } +} +#endif + +bool CWindowListener::OnLButtonDown(guint32 nFlags, double x, double y) +{ + if (CanProcess()) + { + g_pManager->OnLButtonDown((int)x, (int)y); + return true; + } + return false; +} + +bool CWindowListener::OnRButtonDown(guint32 nFlags, double x, double y) +{ + if (CanProcess()) + { + g_2DView.OnRButtonDown ((int)x, (int)y); + return true; + } + return false; +} + +bool CWindowListener::OnLButtonUp(guint32 nFlags, double x, double y) +{ + if (CanProcess()) + { + g_pManager->OnLButtonUp((int)x, (int)y); + return true; + } + return false; +} + +bool CWindowListener::OnRButtonUp(guint32 nFlags, double x, double y) +{ + if (CanProcess()) + { + g_2DView.OnRButtonUp ((int)x, (int)y); + return true; + } + return false; +} + +bool CWindowListener::OnMouseMove(guint32 nFlags, double x, double y) +{ + if (CanProcess ()) + { + if (g_2DView.OnMouseMove ((int)x, (int)y)) + return true; + + g_pManager->OnMouseMove((int)x, (int)y); + return true; + } + return false; +} + +// the widget is closing +void CWindowListener::Close() +{ + g_pToolWnd = NULL; +} + +bool CWindowListener::Paint() +{ + if (!CanProcess ()) + return false; + + if (g_bTexViewReady) + DoExpose(); + + return true; +} + +bool CWindowListener::OnKeyPressed(char *s) +{ + if (!strcmp(s,"Escape")) + { + Textool_Cancel(); + return TRUE; + } + if (CanProcess ()) + { + if (g_2DView.OnKeyDown (s)) + return TRUE; + + if (!strcmp(s,"Return")) + { + Textool_Validate(); + return TRUE; + } + } + return FALSE; +} + +extern "C" void QERPlug_Dispatch(const char* p, vec3_t vMin, vec3_t vMax, bool bSingleBrush) +{ + #if 0 + // if it's the first call, perhaps we need some additional init steps + if (!g_bQglInitDone) + { + g_QglTable.m_nSize = sizeof(_QERQglTable); + if ( g_FuncTable.m_pfnRequestInterface( QERQglTable_GUID, static_cast(&g_QglTable) ) ) + { + g_bQglInitDone = true; + } + else + { + Sys_Printf("TexTool plugin: _QERQglTable interface request failed\n"); + return; + } + } + + if (!g_bSelectedFaceInitDone) + { + g_SelectedFaceTable.m_nSize = sizeof(_QERSelectedFaceTable); + if (g_FuncTable.m_pfnRequestInterface (QERSelectedFaceTable_GUID, + static_cast(&g_SelectedFaceTable))) + { + g_bSelectedFaceInitDone = true; + } + else + { + Sys_Printf("TexTool plugin: _QERSelectedFaceTable interface request failed\n"); + return; + } + } + + if (!g_bSurfaceTableInitDone) + { + g_SurfaceTable.m_nSize = sizeof(_QERAppSurfaceTable); + if ( g_FuncTable.m_pfnRequestInterface( QERAppSurfaceTable_GUID, static_cast(&g_SurfaceTable) ) ) + { + g_bSurfaceTableInitDone = true; + } + else + { + Sys_Printf("TexTool plugin: _QERAppSurfaceTable interface request failed\n"); + return; + } + } + + if (!g_bUITable) + { + g_UITable.m_nSize = sizeof(_QERUITable); + if ( g_FuncTable.m_pfnRequestInterface( QERUI_GUID, static_cast(&g_UITable) ) ) + { + g_bUITable = true; + } + else + { + Sys_Printf("TexTool plugin: _QERUITable interface request failed\n"); + return; + } + } + #endif + + if (!strcmp(p, "About...")) + { + DoMessageBox (PLUGIN_ABOUT, "About ...", MB_OK ); + } + else if (!strcmp(p, "Go...")) + { + if (!g_pToolWnd) + { + g_pToolWnd = g_UITable.m_pfnCreateGLWindow(); + g_pToolWnd->setSizeParm(300,300); + g_pToolWnd->setName("TexTool"); + // g_Listener is a static class, we need to bump the refCount to avoid premature release problems + g_Listen.IncRef(); + // setListener will incRef on the listener too + g_pToolWnd->setListener(&g_Listen); + if (!g_pToolWnd->Show()) + { + DoMessageBox ("Error creating texture tools window!", "TexTool plugin", MB_ICONERROR | MB_OK); + return; + } + } + + g_bTexViewReady = false; + g_bClosing = false; + } + else if (!strcmp(p, "Help...")) + { + if (!g_bHelp) + DoMessageBox ("Select a brush face (ctrl+shift+left mouse) or a patch, and hit Go...\n" + "See tutorials for more", "TexTool plugin", MB_OK ); + else + DoMessageBox ("Are you kidding me ?", "TexTool plugin", MB_OK ); + g_bHelp = true; + } +} + +// ============================================================================= +// SYNAPSE + +CSynapseServer* g_pSynapseServer = NULL; +CSynapseClientTexTool g_SynapseClient; + +#if __GNUC__ >= 4 +#pragma GCC visibility push(default) +#endif +extern "C" CSynapseClient* SYNAPSE_DLL_EXPORT Synapse_EnumerateInterfaces( const char *version, CSynapseServer *pServer ) { +#if __GNUC__ >= 4 +#pragma GCC visibility pop +#endif + if (strcmp(version, SYNAPSE_VERSION)) + { + Syn_Printf("ERROR: synapse API version mismatch: should be '" SYNAPSE_VERSION "', got '%s'\n", version); + return NULL; + } + g_pSynapseServer = pServer; + g_pSynapseServer->IncRef(); + Set_Syn_Printf(g_pSynapseServer->Get_Syn_Printf()); + + g_SynapseClient.AddAPI(PLUGIN_MAJOR, "textool", sizeof(_QERPluginTable)); + g_SynapseClient.AddAPI(RADIANT_MAJOR, NULL, sizeof(g_FuncTable), SYN_REQUIRE, &g_FuncTable); + g_SynapseClient.AddAPI(QGL_MAJOR, NULL, sizeof(g_QglTable), SYN_REQUIRE, &g_QglTable); + g_SynapseClient.AddAPI(SELECTEDFACE_MAJOR, NULL, sizeof(g_SelectedFaceTable), SYN_REQUIRE, &g_SelectedFaceTable); + + return &g_SynapseClient; +} + +bool CSynapseClientTexTool::RequestAPI(APIDescriptor_t *pAPI) +{ + if (!strcmp(pAPI->major_name, PLUGIN_MAJOR)) + { + _QERPluginTable *pTable = static_cast<_QERPluginTable*>(pAPI->mpTable); + pTable->m_pfnQERPlug_Init = QERPlug_Init; + pTable->m_pfnQERPlug_GetName = QERPlug_GetName; + pTable->m_pfnQERPlug_GetCommandList = QERPlug_GetCommandList; + pTable->m_pfnQERPlug_Dispatch = QERPlug_Dispatch; + return true; + } + + Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo()); + return false; +} + +#include "version.h" + +const char* CSynapseClientTexTool::GetInfo() +{ + return "Texture Tools plugin built " __DATE__ " " RADIANT_VERSION; +}