X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=radiant%2Fsurfacedialog.cpp;h=b9926714e472eda7844fb7bb15696548f0a30ad3;hb=ea169c8056f5d0ea00a844b0612c9097e5fbefbc;hp=6df9e693b0d18c9664b407bb2c1f4b26aa3cca38;hpb=33efc9089296fc4e5f54d43581a0db81576ba848;p=xonotic%2Fnetradiant.git diff --git a/radiant/surfacedialog.cpp b/radiant/surfacedialog.cpp index 6df9e693..b9926714 100644 --- a/radiant/surfacedialog.cpp +++ b/radiant/surfacedialog.cpp @@ -1,23 +1,23 @@ /* -Copyright (C) 1999-2007 id Software, Inc. and contributors. -For a list of contributors, see the accompanying CONTRIBUTORS file. + Copyright (C) 1999-2006 Id Software, Inc. and contributors. + For a list of contributors, see the accompanying CONTRIBUTORS file. -This file is part of GtkRadiant. + 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 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. + 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 -*/ + 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 + */ // // Surface Dialog @@ -25,338 +25,382 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // Leonardo Zide (leo@lokigames.com) // -#include -#include "stdafx.h" #include "surfacedialog.h" -SurfaceDlg g_dlgSurface; +#include -///////////////////////////////////////////////////////////////////////////// -// surface properties plugin +#include "debugging/debugging.h" +#include "warnings.h" -/* -=================================================== - - SURFACE INSPECTOR - -=================================================== -*/ - -// the texdef to switch back to when the OnCancel is called -texdef_t g_old_texdef; -// when != NULL, this thing means the surface inspector is currently being displayed -// NOTE a boolean flag would have been more explicit, this is totally so ugly -GtkWidget* g_surfwin = NULL; -// turn on/off processing of the "changed" "value_changed" messages -// (need to turn off when we are feeding data in) -bool g_bListenChanged = true; -// the struct used to store the increments (saved in registry) -texdef_t *l_pIncrement = &g_qeglobals.d_savedinfo.m_SIIncrement; -// turn on/off listening of the update messages -bool g_bListenUpdate = true; - -#ifdef _DEBUG -// experimental stuff, work directly on BP -static void OnTest(GtkWidget *widget, gpointer data) +#include "iscenegraph.h" +#include "itexdef.h" +#include "iundo.h" +#include "iselection.h" + +#include + +#include "signal/isignal.h" +#include "generic/object.h" +#include "math/vector.h" +#include "texturelib.h" +#include "shaderlib.h" +#include "stringio.h" + +#include "gtkutil/idledraw.h" +#include "gtkutil/dialog.h" +#include "gtkutil/entry.h" +#include "gtkutil/nonmodal.h" +#include "gtkutil/pointer.h" +#include "gtkutil/glwidget.h" //Shamus: For Textool +#include "gtkutil/button.h" +#include "map.h" +#include "select.h" +#include "patchmanip.h" +#include "brushmanip.h" +#include "patchdialog.h" +#include "preferences.h" +#include "brush_primit.h" +#include "xywindow.h" +#include "mainframe.h" +#include "gtkdlgs.h" +#include "dialog.h" +#include "brush.h" //Shamus: for Textool +#include "patch.h" +#include "commands.h" +#include "stream/stringstream.h" +#include "grid.h" +#include "textureentry.h" + +//NOTE: Proper functioning of Textool currently requires that the "#if 1" lines in +// brush_primit.h be changed to "#if 0". add/removeScale screws this up ATM. :-) +// Plus, Radiant seems to work just fine without that stuff. ;-) + +#define TEXTOOL_ENABLED 0 + +#if TEXTOOL_ENABLED + +namespace TexTool { - if (!g_qeglobals.m_bBrushPrimitMode) - { - Sys_FPrintf(SYS_WRN, "BP mode required\n"); - return; - } - if (g_ptrSelectedFaces.GetSize() != 1) - { - Sys_FPrintf(SYS_WRN, "Expected single face selection\n"); - return; - } - brush_t *b = reinterpret_cast(g_ptrSelectedFaceBrushes.GetAt(0)); - face_t *selFace = reinterpret_cast(g_ptrSelectedFaces.GetAt(0)); - // get the ST axis base for the face - vec3_t texS,texT; - ComputeAxisBase(selFace->plane.normal, texS, texT); - // find ST coordinates for the center of the face - float Os=0,Ot=0; - int i; - for (i=0; iface_winding->numpoints; i++) - { - Os += DotProduct(selFace->face_winding->points[i],texS); - Ot += DotProduct(selFace->face_winding->points[i],texT); - } - Os /= selFace->face_winding->numpoints; - Ot /= selFace->face_winding->numpoints; - brushprimit_texdef_t *pBP = &selFace->brushprimit_texdef; - - // (FIXME: initial version, before axis base change optimize) - - // we need to compute our BP matrix in this new axis base (O,texS,texT) - // the general case if BPO = M * BP * M^-1 - // where BPO is transformation expressed in (O,texS,texT) - // M is the axis base change from (origin,texS,texT) to (O,texS,texT) - // here we have a special case, M is a translation and it's inverse is easy - vec_t BPO[2][3]; - vec_t aux[2][3]; - vec_t m[2][3]; - memset(&m, 0, sizeof(vec_t)*6); - m[0][0] = 1; m[1][1] = 1; m[0][2] = -Os; m[1][2] = -Ot; - BPMatMul(m, pBP->coords, aux); - m[0][2] = Os; m[1][2] = Ot; // now M^-1 - BPMatMul(aux, m, BPO); - -#if 0 - // apply a scaling - // scale factors against S and T axis, we apply on top of the existing matrix - // <1 will decrease the texel/world resolution, >1 will increase - float sS = 1.025,sT = 1.025; - BPMatScale(BPO,sS,sT); -#endif -#if 0 - // apply a rotation - float theta = 5; - BPMatRotate(BPO,theta); -#endif -#if 0 - // read the scale - ConvertTexMatWithQTexture(BPO, selFace->d_texture, aux, NULL); - // reset the scale (normalize the matrix) - vec_t v1,v2; - v1 = sqrt(aux[0][0]*aux[0][0]+aux[1][0]*aux[1][0]); - v2 = sqrt(aux[0][1]*aux[0][1]+aux[1][1]*aux[1][1]); - // if reading the scale values, we have them here: - Sys_Printf("Current Scale: S: %g T: %g\n", v1, v2); - return; + +//Shamus: Textool function prototypes +gboolean size_allocate( ui::Widget, GtkAllocation *, gpointer ); +gboolean expose( ui::Widget, GdkEventExpose *, gpointer ); +gboolean button_press( ui::Widget, GdkEventButton *, gpointer ); +gboolean button_release( ui::Widget, GdkEventButton *, gpointer ); +gboolean motion( ui::Widget, GdkEventMotion *, gpointer ); +void flipX( ui::ToggleButton, gpointer ); +void flipY( ui::ToggleButton, gpointer ); + +//End Textool function prototypes + +//Shamus: Textool globals +ui::Widget g_textoolWin; +//End Textool globals + +void queueDraw(){ + gtk_widget_queue_draw( g_textoolWin ); +} + +} + #endif + +inline void spin_button_set_step(ui::SpinButton spin, gfloat step) +{ #if 1 - // apply a given scale (on S and T) - ConvertTexMatWithQTexture(BPO, selFace->d_texture, aux, NULL); - // reset the scale (normalize the matrix) - vec_t v1,v2; - v1 = sqrt(aux[0][0]*aux[0][0]+aux[1][0]*aux[1][0]); - v2 = sqrt(aux[0][1]*aux[0][1]+aux[1][1]*aux[1][1]); - vec_t sS,sT; - // put the values for scale on S and T here: - sS = 1.2 / v1; - sT = 0.8 / v2; - aux[0][0] *= sS; aux[1][0] *= sS; - aux[0][1] *= sT; aux[1][1] *= sT; - ConvertTexMatWithQTexture(aux, NULL, BPO, selFace->d_texture); + gtk_adjustment_set_step_increment(gtk_spin_button_get_adjustment(spin), step); +#else + GValue gvalue = GValue_default(); + g_value_init( &gvalue, G_TYPE_DOUBLE ); + g_value_set_double( &gvalue, step ); + g_object_set( G_OBJECT( gtk_spin_button_get_adjustment( spin ) ), "step-increment", &gvalue, NULL ); #endif +} - // now BPO must be expressed back in (origin,texS,texT) axis base BP = M^-1 * BPO * M - BPMatMul(m, BPO, aux); // m is M^-1 - m[0][2] = -Os; m[1][2] = -Ot; - BPMatMul(aux, m, pBP->coords); +class Increment { + float &m_f; +public: + ui::SpinButton m_spin; + ui::Entry m_entry; - // now emit the coordinates on the winding - EmitBrushPrimitTextureCoordinates(selFace, selFace->face_winding); - Sys_UpdateWindows(W_CAMERA); -} + Increment(float &f) : m_f(f), m_spin(ui::null), m_entry(ui::null) + { + } -/* - FIXME: try again, there must be a silly mistake in the formula expansion - // we need to compute our BP matrix in this new axis base (O,texS,texT) - // the general case is BPO = M * BP * M^-1 - // where BPO is transformation expressed in (O,texS,texT) - // M is the axis base change from (origin,texS,texT) to (O,texS,texT) - // here we have a special case, M is a translation and it's inverse is easy - // the M * BP * M^-1 formula can be expanded and simplified - vec_t BPO[2][3]; - memcpy(&BPO, &pBP->coords, sizeof(vec_t)*6); - BPO[0][2] = Os*(pBP->coords[0][0]-1.0) + Ot*pBP->coords[0][1] + pBP->coords[0][2]; - BPO[1][2] = Os*pBP->coords[1][0] + Ot*(pBP->coords[1][1]-1.0) + Ot*pBP->coords[1][2]; - - // apply a scaling - // scale factors against S and T axis, we apply on top of the existing matrix - // <1 will decrease the texel/world resolution, >1 will increase - float sS = 1.025,sT = 1.025; - BPMatScale(BPO,sS,sT); - - // now BPO must be expressed back in (origin,texS,texT) axis base BP = M^-1 * BPO * M - // same expanded formula as above - memcpy(&pBP->coords, &BPO, sizeof(vec_t)*6); - pBP->coords[0][2] = Os*(1.0-BPO[0][0]) - Ot*BPO[0][1] + BPO[0][2]; - pBP->coords[1][2] = -Os*BPO[1][0] + Ot*(1.0-BPO[1][1]) + BPO[1][2]; -*/ + void cancel() + { + entry_set_float(m_entry, m_f); + } -/* - // initial version, before axis base change optimize - - // we need to compute our BP matrix in this new axis base (O,texS,texT) - // the general case if BPO = M * BP * M^-1 - // where BPO is transformation expressed in (O,texS,texT) - // M is the axis base change from (origin,texS,texT) to (O,texS,texT) - // here we have a special case, M is a translation and it's inverse is easy - vec_t BPO[2][3]; - vec_t aux[2][3]; - vec_t m[2][3]; - memset(&m, 0, sizeof(vec_t)*6); - m[0][0] = 1; m[1][1] = 1; m[0][2] = -Os; m[1][2] = -Ot; - BPMatMul(m, pBP->coords, aux); - m[0][2] = Os; m[1][2] = Ot; // now M^-1 - BPMatMul(aux, m, BPO); - - // apply a scaling - // scale factors against S and T axis, we apply on top of the existing matrix - // <1 will decrease the texel/world resolution, >1 will increase - float sS = 1.025,sT = 1.025; - BPMatScale(BPO,sS,sT); - - // now BPO must be expressed back in (origin,texS,texT) axis base BP = M^-1 * BPO * M - BPMatMul(m, BPO, aux); // m is M^-1 - m[0][2] = -Os; m[1][2] = -Ot; - BPMatMul(aux, m, pBP->coords); -*/ -#endif + typedef MemberCaller CancelCaller; + + void apply() + { + m_f = static_cast( entry_get_float(m_entry)); + spin_button_set_step(m_spin, m_f); + } + + typedef MemberCaller ApplyCaller; +}; + +void SurfaceInspector_GridChange(); + +class SurfaceInspector : public Dialog { + ui::Window BuildDialog(); + + NonModalEntry m_textureEntry; + NonModalSpinner m_hshiftSpinner; + NonModalEntry m_hshiftEntry; + NonModalSpinner m_vshiftSpinner; + NonModalEntry m_vshiftEntry; + NonModalSpinner m_hscaleSpinner; + NonModalEntry m_hscaleEntry; + NonModalSpinner m_vscaleSpinner; + NonModalEntry m_vscaleEntry; + NonModalSpinner m_rotateSpinner; + NonModalEntry m_rotateEntry; + + IdleDraw m_idleDraw; + + GtkCheckButton *m_surfaceFlags[32]; + GtkCheckButton *m_contentFlags[32]; + + NonModalEntry m_valueEntry; + ui::Entry m_valueEntryWidget{ui::null}; +public: + WindowPositionTracker m_positionTracker; + +// Dialog Data + float m_fitHorizontal; + float m_fitVertical; + + Increment m_hshiftIncrement; + Increment m_vshiftIncrement; + Increment m_hscaleIncrement; + Increment m_vscaleIncrement; + Increment m_rotateIncrement; + ui::Entry m_texture{ui::null}; + + SurfaceInspector() : + m_textureEntry(ApplyShaderCaller(*this), UpdateCaller(*this)), + m_hshiftSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)), + m_hshiftEntry(Increment::ApplyCaller(m_hshiftIncrement), Increment::CancelCaller(m_hshiftIncrement)), + m_vshiftSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)), + m_vshiftEntry(Increment::ApplyCaller(m_vshiftIncrement), Increment::CancelCaller(m_vshiftIncrement)), + m_hscaleSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)), + m_hscaleEntry(Increment::ApplyCaller(m_hscaleIncrement), Increment::CancelCaller(m_hscaleIncrement)), + m_vscaleSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)), + m_vscaleEntry(Increment::ApplyCaller(m_vscaleIncrement), Increment::CancelCaller(m_vscaleIncrement)), + m_rotateSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)), + m_rotateEntry(Increment::ApplyCaller(m_rotateIncrement), Increment::CancelCaller(m_rotateIncrement)), + m_idleDraw(UpdateCaller(*this)), + m_valueEntry(ApplyFlagsCaller(*this), UpdateCaller(*this)), + m_hshiftIncrement(g_si_globals.shift[0]), + m_vshiftIncrement(g_si_globals.shift[1]), + m_hscaleIncrement(g_si_globals.scale[0]), + m_vscaleIncrement(g_si_globals.scale[1]), + m_rotateIncrement(g_si_globals.rotate) + { + m_fitVertical = 1; + m_fitHorizontal = 1; + m_positionTracker.setPosition(c_default_window_pos); + } + + void constructWindow(ui::Window main_window) + { + m_parent = main_window; + Create(); + AddGridChangeCallback(FreeCaller()); + } + + void destroyWindow() + { + Destroy(); + } + + bool visible() + { + return GetWidget().visible(); + } + + void queueDraw() + { + if (visible()) { + m_idleDraw.queueDraw(); + } + } -static void OnDone(GtkWidget *widget, gpointer data) + void Update(); + + typedef MemberCaller UpdateCaller; + + void ApplyShader(); + + typedef MemberCaller ApplyShaderCaller; + + void ApplyTexdef(); + + typedef MemberCaller ApplyTexdefCaller; + + void ApplyFlags(); + + typedef MemberCaller ApplyFlagsCaller; +}; + +namespace { + SurfaceInspector *g_SurfaceInspector; + + inline SurfaceInspector &getSurfaceInspector() + { + ASSERT_NOTNULL(g_SurfaceInspector); + return *g_SurfaceInspector; + } +} + +void SurfaceInspector_constructWindow(ui::Window main_window) { - g_dlgSurface.GetTexMods(); - g_dlgSurface.HideDlg (); - Sys_UpdateWindows(W_ALL); + getSurfaceInspector().constructWindow(main_window); } -// OnUpdate is called when something is changed in the dialog -// and must be reflected in the views. But it's not a change -// so important, so the system will try to undo our last do before applying the new changes -static void OnUpdate (GtkWidget *widget, gpointer data) +void SurfaceInspector_destroyWindow() { - if (!g_bListenChanged) - return; + getSurfaceInspector().destroyWindow(); +} - if (OnlyPatchesSelected()) - { - //++timo possible bug or misfeature in our gtk_MessageBox here.. -// gtk_MessageBox("The surface inspector doesn't work for patches, use the patch inspector instead (Shift+S)", "Surface Inspector", MB_OK ); - Sys_Printf("The surface inspector doesn't work for patches, use the patch inspector instead (Shift+S)\n"); - return; - } +void SurfaceInspector_queueDraw() +{ + getSurfaceInspector().queueDraw(); +} - // avoid long delays on slow computers - while (gtk_events_pending ()) - gtk_main_iteration (); +namespace { + CopiedString g_selectedShader; + TextureProjection g_selectedTexdef; + ContentsFlagsValue g_selectedFlags; + size_t g_selectedShaderSize[2]; +} - g_dlgSurface.GetTexMods (); - Sys_UpdateWindows(W_CAMERA); +void SurfaceInspector_SetSelectedShader(const char *shader) +{ + g_selectedShader = shader; + SurfaceInspector_queueDraw(); } -// reflect the current changes in the views, and make sure -// the changes are stored in the undo. -static void OnApply (GtkWidget *widget, gpointer data) +void SurfaceInspector_SetSelectedTexdef(const TextureProjection &projection) { - if (!g_bListenChanged) - return; + g_selectedTexdef = projection; + SurfaceInspector_queueDraw(); +} - g_dlgSurface.GetTexMods (); - g_dlgSurface.m_nUndoId = 0; // that way we are sure we won't call undo - Sys_UpdateWindows(W_CAMERA); +void SurfaceInspector_SetSelectedFlags(const ContentsFlagsValue &flags) +{ + g_selectedFlags = flags; + SurfaceInspector_queueDraw(); } -// we use OnTextureKey to detect when the user edits something in the texture widget -// in which case next 'Enter' will be interpreted as a OnApply instead of a OnDone -static gint OnTextureKey (GtkWidget* widget, GdkEventKey* event, gpointer data) +static bool s_texture_selection_dirty = false; + +void SurfaceInspector_updateSelection() { -#ifdef DBG_SI - Sys_Printf("OnTextureKey\n"); + s_texture_selection_dirty = true; + SurfaceInspector_queueDraw(); + +#if TEXTOOL_ENABLED + if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) { + TexTool::queueDraw(); + //globalOutputStream() << "textool texture changed..\n"; + } #endif - if (event->keyval != GDK_Return) - g_dlgSurface.m_bEditingTextureWidget = true; - return FALSE; } -static void OnCancel(GtkWidget *widget, gpointer data) +void SurfaceInspector_SelectionChanged(const Selectable &selectable) { - g_qeglobals.d_texturewin.texdef = g_old_texdef; - // cancel the last do if we own it - if (g_dlgSurface.m_nUndoId == Undo_GetUndoId()) - { -#ifdef DBG_SI - Sys_Printf("OnCancel calling Undo_Undo\n"); -#endif - g_bListenUpdate = false; - Undo_Undo(); - g_bListenUpdate = true; - g_dlgSurface.m_nUndoId = 0; - } - g_dlgSurface.HideDlg (); + SurfaceInspector_updateSelection(); } -static gint OnDialogKey (GtkWidget* widget, GdkEventKey* event, gpointer data) +void SurfaceInspector_SetCurrent_FromSelected() { - if (g_surfwin) - { - if (event->keyval == GDK_Return) - { - if (g_dlgSurface.m_bEditingTextureWidget) - { - OnApply (NULL, NULL); - g_dlgSurface.m_bEditingTextureWidget = false; - } - else - { - OnDone (NULL, NULL); - } - return TRUE; - } - if (event->keyval == GDK_Escape) - { - OnCancel (NULL, NULL); - return TRUE; + if (s_texture_selection_dirty == true) { + s_texture_selection_dirty = false; + if (!g_SelectedFaceInstances.empty()) { + TextureProjection projection; +//This *may* be the point before it fucks up... Let's see! +//Yep, there was a call to removeScale in there... + Scene_BrushGetTexdef_Component_Selected(GlobalSceneGraph(), projection); + + SurfaceInspector_SetSelectedTexdef(projection); + + Scene_BrushGetShaderSize_Component_Selected(GlobalSceneGraph(), g_selectedShaderSize[0], + g_selectedShaderSize[1]); + g_selectedTexdef.m_brushprimit_texdef.coords[0][2] = float_mod( + g_selectedTexdef.m_brushprimit_texdef.coords[0][2], (float) g_selectedShaderSize[0]); + g_selectedTexdef.m_brushprimit_texdef.coords[1][2] = float_mod( + g_selectedTexdef.m_brushprimit_texdef.coords[1][2], (float) g_selectedShaderSize[1]); + + CopiedString name; + Scene_BrushGetShader_Component_Selected(GlobalSceneGraph(), name); + if (string_not_empty(name.c_str())) { + SurfaceInspector_SetSelectedShader(name.c_str()); + } + + ContentsFlagsValue flags; + Scene_BrushGetFlags_Component_Selected(GlobalSceneGraph(), flags); + SurfaceInspector_SetSelectedFlags(flags); + } else { + TextureProjection projection; + Scene_BrushGetTexdef_Selected(GlobalSceneGraph(), projection); + SurfaceInspector_SetSelectedTexdef(projection); + + CopiedString name; + Scene_BrushGetShader_Selected(GlobalSceneGraph(), name); + if (string_empty(name.c_str())) { + Scene_PatchGetShader_Selected(GlobalSceneGraph(), name); + } + if (string_not_empty(name.c_str())) { + SurfaceInspector_SetSelectedShader(name.c_str()); + } + + ContentsFlagsValue flags(0, 0, 0, false); + Scene_BrushGetFlags_Selected(GlobalSceneGraph(), flags); + SurfaceInspector_SetSelectedFlags(flags); + } } - } - return FALSE; } -// the widget can be one of hshift, vshift, hscale, vscale, rotate -// we use the g_bListenChanged flag to ignore when changing stuff ourselves -static void OnIncrementChanged(GtkWidget *widget, gpointer data) +const char *SurfaceInspector_GetSelectedShader() { - if (!g_bListenChanged) - return; + SurfaceInspector_SetCurrent_FromSelected(); + return g_selectedShader.c_str(); +} -#ifdef DBG_SI - Sys_Printf("OnIncrementChanged\n"); -#endif +const TextureProjection &SurfaceInspector_GetSelectedTexdef() +{ + SurfaceInspector_SetCurrent_FromSelected(); + return g_selectedTexdef; +} - gfloat val = 0; - sscanf( gtk_entry_get_text (GTK_ENTRY (widget)), "%g", &val); - // now push it into the appropriate spin button - GtkAdjustment * adjust; - if (widget == g_dlgSurface.GetDlgWidget ("hshift_inc")) - { - l_pIncrement->shift[0] = val; - adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (g_dlgSurface.GetDlgWidget ("hshift"))); - adjust->step_increment = l_pIncrement->shift[0]; - } - else if (widget == g_dlgSurface.GetDlgWidget ("vshift_inc")) - { - l_pIncrement->shift[1] = val; - adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (g_dlgSurface.GetDlgWidget ("vshift"))); - adjust->step_increment = l_pIncrement->shift[1]; - } - else if (widget == g_dlgSurface.GetDlgWidget ("hscale_inc")) - { - l_pIncrement->scale[0] = val; - adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (g_dlgSurface.GetDlgWidget ("hscale"))); - adjust->step_increment = l_pIncrement->scale[0]; - } - else if (widget == g_dlgSurface.GetDlgWidget ("vscale_inc")) - { - l_pIncrement->scale[1] = val; - adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (g_dlgSurface.GetDlgWidget ("vscale"))); - adjust->step_increment = l_pIncrement->scale[1]; - } - else if (widget == g_dlgSurface.GetDlgWidget ("rotate_inc")) - { - l_pIncrement->rotate = val; - adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (g_dlgSurface.GetDlgWidget ("rotate"))); - adjust->step_increment = l_pIncrement->rotate; - } +const ContentsFlagsValue &SurfaceInspector_GetSelectedFlags() +{ + SurfaceInspector_SetCurrent_FromSelected(); + return g_selectedFlags; } + +/* + =================================================== + + SURFACE INSPECTOR + + =================================================== + */ + +si_globals_t g_si_globals; + + // make the shift increments match the grid settings // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size // this depends on a scale value if you have selected a particular texture on which you want it to work: // we move the textures in pixels, not world units. (i.e. increment values are in pixel) -// depending on the texture scale it doesn't take the same amount of pixels to move of g_qeglobals.d_gridsize +// depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize() // increment * scale = gridsize // hscale and vscale are optional parameters, if they are zero they will be set to the default scale // NOTE: the default scale depends if you are using BP mode or regular. @@ -364,771 +408,1919 @@ static void OnIncrementChanged(GtkWidget *widget, gpointer data) // see fenris #2810 void DoSnapTToGrid(float hscale, float vscale) { - if (hscale == 0.0f) - { - (g_qeglobals.m_bBrushPrimitMode) ? hscale = 1.0f : hscale = 0.5f; - } - if (vscale == 0.0f) - { - (g_qeglobals.m_bBrushPrimitMode) ? vscale = 1.0f : vscale = 0.5f; - } -#ifdef _DEBUG - Sys_Printf ("DoSnapTToGrid: hscale %g vscale %g\n", hscale, vscale); -#endif - l_pIncrement->shift[0] = (int) ( (float)g_qeglobals.d_gridsize / hscale ); - l_pIncrement->shift[1] = (int) ( (float)g_qeglobals.d_gridsize / vscale ); - // now some update work - // FIXME: doesn't look good here, seems to be called several times - g_dlgSurface.SetTexMods(); + g_si_globals.shift[0] = static_cast( float_to_integer(static_cast( GetGridSize()) / hscale)); + g_si_globals.shift[1] = static_cast( float_to_integer(static_cast( GetGridSize()) / vscale)); + getSurfaceInspector().queueDraw(); +} + +void SurfaceInspector_GridChange() +{ + if (g_si_globals.m_bSnapTToGrid) { + DoSnapTToGrid(Texdef_getDefaultTextureScale(), Texdef_getDefaultTextureScale()); + } } // make the shift increments match the grid settings // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size // this depends on the current texture scale used? // we move the textures in pixels, not world units. (i.e. increment values are in pixel) -// depending on the texture scale it doesn't take the same amount of pixels to move of g_qeglobals.d_gridsize +// depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize() // increment * scale = gridsize -static void OnBtnMatchGrid(GtkWidget *widget, gpointer data) +static void OnBtnMatchGrid(ui::Widget widget, gpointer data) { - float hscale, vscale; - hscale = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (g_dlgSurface.GetDlgWidget ("hscale"))); - vscale = gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (g_dlgSurface.GetDlgWidget ("vscale"))); - if (hscale == 0.0f || vscale == 0.0f) - { - Sys_Printf("ERROR: unexpected scale == 0.0f\n"); - return; - } - DoSnapTToGrid (hscale, vscale); -} + float hscale, vscale; + hscale = static_cast( gtk_spin_button_get_value(getSurfaceInspector().m_hscaleIncrement.m_spin)); + vscale = static_cast( gtk_spin_button_get_value(getSurfaceInspector().m_vscaleIncrement.m_spin)); -void UpdateSurfaceDialog() -{ - if (!g_bListenUpdate) - return; + if (hscale == 0.0f || vscale == 0.0f) { + globalOutputStream() << "ERROR: unexpected scale == 0.0f\n"; + return; + } - g_SurfaceTable.m_pfnUpdateSurfaceDialog(); + DoSnapTToGrid(hscale, vscale); } // DoSurface will always try to show the surface inspector // or update it because something new has been selected -void DoSurface (void) +// Shamus: It does get called when the SI is hidden, but not when you select something new. ;-) +void DoSurface(void) { -#ifdef DBG_SI - Sys_Printf("DoSurface\n"); -#endif - g_SurfaceTable.m_pfnDoSurface(); - return; + if (!getSurfaceInspector().GetWidget()) { + getSurfaceInspector().Create(); + + } + getSurfaceInspector().Update(); + getSurfaceInspector().importData(); + getSurfaceInspector().ShowDlg(); } -void ToggleSurface() +void SurfaceInspector_toggleShown() { - g_SurfaceTable.m_pfnToggleSurface(); - return; + if (getSurfaceInspector().visible()) { + getSurfaceInspector().HideDlg(); + } else { + DoSurface(); + } } -// NOTE: will raise and show the Surface inspector and exec fit for patches and brushes -void SurfaceDlgFitAll() +void SurfaceInspector_FitTexture() { - g_SurfaceTable.m_pfnSurfaceDlgFitAll(); - return; + UndoableCommand undo("textureAutoFit"); + Select_FitTexture(getSurfaceInspector().m_fitHorizontal, getSurfaceInspector().m_fitVertical); } -static void OnBtnPatchdetails(GtkWidget *widget, gpointer data) +static void OnBtnPatchdetails(ui::Widget widget, gpointer data) { - Patch_NaturalizeSelected(true); - Sys_UpdateWindows(W_ALL); + Patch_CapTexture(); } -static void OnBtnPatchnatural(GtkWidget *widget, gpointer data) +static void OnBtnPatchnatural(ui::Widget widget, gpointer data) { - Patch_NaturalizeSelected(); - Sys_UpdateWindows(W_ALL); + Patch_NaturalTexture(); } -static void OnBtnPatchreset(GtkWidget *widget, gpointer data) +static void OnBtnPatchreset(ui::Widget widget, gpointer data) { - float fx, fy; + Patch_ResetTexture(); +} - if (DoTextureLayout (&fx, &fy) == IDOK) - Patch_ResetTexturing (fx, fy); - Sys_UpdateWindows(W_ALL); +static void OnBtnPatchFit(ui::Widget widget, gpointer data) +{ + Patch_FitTexture(); } -static void OnBtnPatchFit(GtkWidget *widget, gpointer data) +static void OnBtnAxial(ui::Widget widget, gpointer data) { - Patch_ResetTexturing(1.0, 1.0); - Sys_UpdateWindows(W_ALL); +//globalOutputStream() << "--> [OnBtnAxial]...\n"; + UndoableCommand undo("textureDefault"); + TextureProjection projection; +//globalOutputStream() << " TexDef_Construct_Default()...\n"; + TexDef_Construct_Default(projection); +//globalOutputStream() << " Select_SetTexdef()...\n"; + +#if TEXTOOL_ENABLED + + //Shamus: + if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) { + // Scale up texture width/height if in BP mode... +//NOTE: This may not be correct any more! :-P + if ( !g_SelectedFaceInstances.empty() ) { + Face & face = g_SelectedFaceInstances.last().getFace(); + float x = face.getShader().m_state->getTexture().width; + float y = face.getShader().m_state->getTexture().height; + projection.m_brushprimit_texdef.coords[0][0] /= x; + projection.m_brushprimit_texdef.coords[0][1] /= y; + projection.m_brushprimit_texdef.coords[1][0] /= x; + projection.m_brushprimit_texdef.coords[1][1] /= y; + } + } +#endif + + Select_SetTexdef(projection); } -static void OnBtnAxial(GtkWidget *widget, gpointer data) +static void OnBtnFaceFit(ui::Widget widget, gpointer data) { - Select_SetTexture (&g_qeglobals.d_texturewin.texdef, &g_qeglobals.d_texturewin.brushprimit_texdef, true); - g_dlgSurface.SetTexMods(); - Sys_UpdateWindows(W_ALL); + getSurfaceInspector().exportData(); + SurfaceInspector_FitTexture(); } -static void OnBtnFaceFit(GtkWidget *widget, gpointer data) +typedef const char *FlagName; + +const FlagName surfaceflagNamesDefault[32] = { + "surf1", + "surf2", + "surf3", + "surf4", + "surf5", + "surf6", + "surf7", + "surf8", + "surf9", + "surf10", + "surf11", + "surf12", + "surf13", + "surf14", + "surf15", + "surf16", + "surf17", + "surf18", + "surf19", + "surf20", + "surf21", + "surf22", + "surf23", + "surf24", + "surf25", + "surf26", + "surf27", + "surf28", + "surf29", + "surf30", + "surf31", + "surf32" +}; + +const FlagName contentflagNamesDefault[32] = { + "cont1", + "cont2", + "cont3", + "cont4", + "cont5", + "cont6", + "cont7", + "cont8", + "cont9", + "cont10", + "cont11", + "cont12", + "cont13", + "cont14", + "cont15", + "cont16", + "cont17", + "cont18", + "cont19", + "cont20", + "cont21", + "cont22", + "cont23", + "cont24", + "cont25", + "cont26", + "cont27", + "cont28", + "cont29", + "cont30", + "cont31", + "cont32" +}; + +const char *getSurfaceFlagName(std::size_t bit) { - g_dlgSurface.UpdateData(TRUE); - if (g_ptrSelectedFaces.GetSize() == 0) - { - brush_t *b; - for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) - { - for (face_t* pFace = b->brush_faces; pFace; pFace = pFace->next) - { - g_ptrSelectedFaces.Add(pFace); - g_ptrSelectedFaceBrushes.Add(b); - } + const char *value = g_pGameDescription->getKeyValue(surfaceflagNamesDefault[bit]); + if (string_empty(value)) { + return surfaceflagNamesDefault[bit]; } - Select_FitTexture(g_dlgSurface.m_nHeight, g_dlgSurface.m_nWidth); - g_dlgSurface.SetTexMods(); - g_ptrSelectedFaces.RemoveAll(); - } - else - { - Select_FitTexture(g_dlgSurface.m_nHeight, g_dlgSurface.m_nWidth); - g_dlgSurface.SetTexMods(); - } - Sys_UpdateWindows(W_ALL); + return value; } +const char *getContentFlagName(std::size_t bit) +{ + const char *value = g_pGameDescription->getKeyValue(contentflagNamesDefault[bit]); + if (string_empty(value)) { + return contentflagNamesDefault[bit]; + } + return value; +} + + // ============================================================================= -// SurfaceDialog class +// SurfaceInspector class + +guint togglebutton_connect_toggled(ui::ToggleButton button, const Callback &callback) +{ + return g_signal_connect_swapped(G_OBJECT(button), "toggled", G_CALLBACK(callback.getThunk()), + callback.getEnvironment()); +} -SurfaceDlg::SurfaceDlg () +ui::Window SurfaceInspector::BuildDialog() { - m_nHeight = 1; - m_nWidth = 1; - m_nUndoId = 0; + ui::Window window = ui::Window(create_floating_window("Surface Inspector", m_parent)); + + m_positionTracker.connect(window); + + global_accel_connect_window(window); + + window_connect_focus_in_clear_focus_widget(window); + + + { + // replaced by only the vbox: + auto vbox = ui::VBox(FALSE, 5); + vbox.show(); + window.add(vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 5); + + { + auto hbox2 = ui::HBox(FALSE, 5); + hbox2.show(); + vbox.pack_start(hbox2, FALSE, FALSE, 0); + + { + ui::Widget label = ui::Label("Texture"); + label.show(); + hbox2.pack_start(label, FALSE, TRUE, 0); + } + { + auto entry = ui::Entry(ui::New); + entry.show(); + hbox2.pack_start(entry, TRUE, TRUE, 0); + m_texture = entry; + m_textureEntry.connect(entry); + GlobalTextureEntryCompletion::instance().connect(entry); + } + } + + + { + auto table = ui::Table(6, 4, FALSE); + table.show(); + vbox.pack_start(table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(table, 5); + gtk_table_set_col_spacings(table, 5); + { + ui::Widget label = ui::Label("Horizontal shift"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0}); + } + { + auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 2); + m_hshiftIncrement.m_spin = spin; + m_hshiftSpinner.connect(spin); + spin.show(); + table.attach(spin, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0}); + spin.dimensions(60, -1); + } + { + ui::Widget label = ui::Label("Step"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 0}); + } + { + auto entry = ui::Entry(ui::New); + entry.show(); + table.attach(entry, {3, 4, 0, 1}, {GTK_EXPAND | GTK_FILL, 0}); + entry.dimensions(50, -1); + m_hshiftIncrement.m_entry = entry; + m_hshiftEntry.connect(entry); + } + { + ui::Widget label = ui::Label("Vertical shift"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0}); + } + { + auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 2); + m_vshiftIncrement.m_spin = spin; + m_vshiftSpinner.connect(spin); + spin.show(); + table.attach(spin, {1, 2, 1, 2}, {GTK_FILL, 0}); + spin.dimensions(60, -1); + } + { + ui::Widget label = ui::Label("Step"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {2, 3, 1, 2}, {GTK_FILL, 0}); + } + { + auto entry = ui::Entry(ui::New); + entry.show(); + table.attach(entry, {3, 4, 1, 2}, {GTK_FILL, 0}); + entry.dimensions(50, -1); + m_vshiftIncrement.m_entry = entry; + m_vshiftEntry.connect(entry); + } + { + ui::Widget label = ui::Label("Horizontal stretch"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0}); + } + { + auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 5); + m_hscaleIncrement.m_spin = spin; + m_hscaleSpinner.connect(spin); + spin.show(); + table.attach(spin, {1, 2, 2, 3}, {GTK_FILL, 0}); + spin.dimensions(60, -1); + } + { + ui::Widget label = ui::Label("Step"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {2, 3, 2, 3}, {GTK_FILL, 0}); + } + { + auto entry = ui::Entry(ui::New); + entry.show(); + table.attach(entry, {3, 4, 2, 3}, {GTK_FILL, 0}); + entry.dimensions(50, -1); + m_hscaleIncrement.m_entry = entry; + m_hscaleEntry.connect(entry); + } + { + ui::Widget label = ui::Label("Vertical stretch"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {0, 1, 3, 4}, {GTK_FILL, 0}); + } + { + auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 5); + m_vscaleIncrement.m_spin = spin; + m_vscaleSpinner.connect(spin); + spin.show(); + table.attach(spin, {1, 2, 3, 4}, {GTK_FILL, 0}); + spin.dimensions(60, -1); + } + { + ui::Widget label = ui::Label("Step"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {2, 3, 3, 4}, {GTK_FILL, 0}); + } + { + auto entry = ui::Entry(ui::New); + entry.show(); + table.attach(entry, {3, 4, 3, 4}, {GTK_FILL, 0}); + entry.dimensions(50, -1); + m_vscaleIncrement.m_entry = entry; + m_vscaleEntry.connect(entry); + } + { + ui::Widget label = ui::Label("Rotate"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {0, 1, 4, 5}, {GTK_FILL, 0}); + } + { + auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 2); + m_rotateIncrement.m_spin = spin; + m_rotateSpinner.connect(spin); + spin.show(); + table.attach(spin, {1, 2, 4, 5}, {GTK_FILL, 0}); + spin.dimensions(60, -1); + gtk_spin_button_set_wrap(spin, TRUE); + } + { + ui::Widget label = ui::Label("Step"); + label.show(); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + table.attach(label, {2, 3, 4, 5}, {GTK_FILL, 0}); + } + { + auto entry = ui::Entry(ui::New); + entry.show(); + table.attach(entry, {3, 4, 4, 5}, {GTK_FILL, 0}); + entry.dimensions(50, -1); + m_rotateIncrement.m_entry = entry; + m_rotateEntry.connect(entry); + } + { + // match grid button + ui::Widget button = ui::Button("Match Grid"); + button.show(); + table.attach(button, {2, 4, 5, 6}, {GTK_EXPAND | GTK_FILL, 0}); + button.connect("clicked", G_CALLBACK(OnBtnMatchGrid), 0); + } + } + + { + auto frame = ui::Frame("Texturing"); + frame.show(); + vbox.pack_start(frame, FALSE, FALSE, 0); + { + auto table = ui::Table(4, 4, FALSE); + table.show(); + frame.add(table); + gtk_table_set_row_spacings(table, 5); + gtk_table_set_col_spacings(table, 5); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + { + ui::Widget label = ui::Label("Brush"); + label.show(); + table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0}); + } + { + ui::Widget label = ui::Label("Patch"); + label.show(); + table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0}); + } + { + ui::Widget label = ui::Label("Width"); + label.show(); + table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 0}); + } + { + ui::Widget label = ui::Label("Height"); + label.show(); + table.attach(label, {3, 4, 0, 1}, {GTK_FILL, 0}); + } + { + ui::Widget button = ui::Button("Axial"); + button.show(); + table.attach(button, {0, 1, 1, 2}, {GTK_EXPAND | GTK_FILL, 0}); + button.connect("clicked", + G_CALLBACK(OnBtnAxial), 0); + button.dimensions(60, -1); + } + { + ui::Widget button = ui::Button("Fit"); + button.show(); + table.attach(button, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0}); + button.connect("clicked", + G_CALLBACK(OnBtnFaceFit), 0); + button.dimensions(60, -1); + } + { + ui::Widget button = ui::Button("CAP"); + button.show(); + table.attach(button, {0, 1, 3, 4}, {GTK_EXPAND | GTK_FILL, 0}); + button.connect("clicked", + G_CALLBACK(OnBtnPatchdetails), 0); + button.dimensions(60, -1); + } + { + ui::Widget button = ui::Button("Set..."); + button.show(); + table.attach(button, {1, 2, 3, 4}, {GTK_EXPAND | GTK_FILL, 0}); + button.connect("clicked", + G_CALLBACK(OnBtnPatchreset), 0); + button.dimensions(60, -1); + } + { + ui::Widget button = ui::Button("Natural"); + button.show(); + table.attach(button, {2, 3, 3, 4}, {GTK_EXPAND | GTK_FILL, 0}); + button.connect("clicked", + G_CALLBACK(OnBtnPatchnatural), 0); + button.dimensions(60, -1); + } + { + ui::Widget button = ui::Button("Fit"); + button.show(); + table.attach(button, {3, 4, 3, 4}, {GTK_EXPAND | GTK_FILL, 0}); + button.connect("clicked", + G_CALLBACK(OnBtnPatchFit), 0); + button.dimensions(60, -1); + } + { + auto spin = ui::SpinButton(ui::Adjustment(1, 0, 1 << 16, 1, 10, 0), 0, 6); + spin.show(); + table.attach(spin, {2, 3, 1, 2}, {GTK_EXPAND | GTK_FILL, 0}); + spin.dimensions(60, -1); + AddDialogData(spin, m_fitHorizontal); + } + { + auto spin = ui::SpinButton(ui::Adjustment(1, 0, 1 << 16, 1, 10, 0), 0, 6); + spin.show(); + table.attach(spin, {3, 4, 1, 2}, {GTK_EXPAND | GTK_FILL, 0}); + spin.dimensions(60, -1); + AddDialogData(spin, m_fitVertical); + } + } + } + if (!string_empty(g_pGameDescription->getKeyValue("si_flags"))) { + { + auto frame = ui::Frame("Surface Flags"); + frame.show(); + vbox.pack_start(frame, TRUE, TRUE, 0); + { + auto vbox3 = ui::VBox(FALSE, 4); + //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4); + vbox3.show(); + frame.add(vbox3); + { + auto table = ui::Table(8, 4, FALSE); + table.show(); + vbox3.pack_start(table, TRUE, TRUE, 0); + gtk_table_set_row_spacings(table, 0); + gtk_table_set_col_spacings(table, 0); + + GtkCheckButton **p = m_surfaceFlags; + + for (unsigned int c = 0; c != 4; ++c) { + for (unsigned int r = 0; r != 8; ++r) { + auto check = ui::CheckButton(getSurfaceFlagName(c * 8 + r)); + check.show(); + table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0}); + *p++ = check; + guint handler_id = togglebutton_connect_toggled(check, ApplyFlagsCaller(*this)); + g_object_set_data(G_OBJECT(check), "handler", gint_to_pointer(handler_id)); + } + } + } + } + } + { + auto frame = ui::Frame("Content Flags"); + frame.show(); + vbox.pack_start(frame, TRUE, TRUE, 0); + { + auto vbox3 = ui::VBox(FALSE, 4); + //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4); + vbox3.show(); + frame.add(vbox3); + { + + auto table = ui::Table(8, 4, FALSE); + table.show(); + vbox3.pack_start(table, TRUE, TRUE, 0); + gtk_table_set_row_spacings(table, 0); + gtk_table_set_col_spacings(table, 0); + + GtkCheckButton **p = m_contentFlags; + + for (unsigned int c = 0; c != 4; ++c) { + for (unsigned int r = 0; r != 8; ++r) { + auto check = ui::CheckButton(getContentFlagName(c * 8 + r)); + check.show(); + table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0}); + *p++ = check; + guint handler_id = togglebutton_connect_toggled(check, ApplyFlagsCaller(*this)); + g_object_set_data(G_OBJECT(check), "handler", gint_to_pointer(handler_id)); + } + } + + // not allowed to modify detail flag using Surface Inspector + gtk_widget_set_sensitive(ui::CheckButton::from(m_contentFlags[BRUSH_DETAIL_FLAG]), FALSE); + } + } + } + { + auto frame = ui::Frame("Value"); + frame.show(); + vbox.pack_start(frame, TRUE, TRUE, 0); + { + auto vbox3 = ui::VBox(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4); + vbox3.show(); + frame.add(vbox3); + + { + auto entry = ui::Entry(ui::New); + entry.show(); + vbox3.pack_start(entry, TRUE, TRUE, 0); + m_valueEntryWidget = entry; + m_valueEntry.connect(entry); + } + } + } + } + +#if TEXTOOL_ENABLED + if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) { +// Shamus: Textool goodies... + ui::Widget frame = ui::Frame( "Textool" ); + frame.show(); + vbox.pack_start( frame , FALSE, FALSE, 0 ); + { + //Prolly should make this a member or global var, so the SI can draw on it... + TexTool::g_textoolWin = glwidget_new( FALSE ); + // --> Dunno, but this stuff may be necessary... (Looks like it!) + g_object_ref( TexTool::g_textoolWin ); + gtk_widget_set_events( TexTool::g_textoolWin, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK ); + gtk_widget_set_can_focus( TexTool::g_textoolWin, true ); + // <-- end stuff... + TexTool::g_textoolWin.show(); + TexTool::g_textoolWin.dimensions( -1, 240 ); //Yeah! + frame.add(TexTool::g_textoolWin); + + TexTool::g_textoolWin.connect( "size_allocate", G_CALLBACK( TexTool::size_allocate ), NULL ); + TexTool::g_textoolWin.connect( "expose_event", G_CALLBACK( TexTool::expose ), NULL ); + TexTool::g_textoolWin.connect( "button_press_event", G_CALLBACK( TexTool::button_press ), NULL ); + TexTool::g_textoolWin.connect( "button_release_event", G_CALLBACK( TexTool::button_release ), NULL ); + TexTool::g_textoolWin.connect( "motion_notify_event", G_CALLBACK( TexTool::motion ), NULL ); + } + { + ui::Widget hbox = ui::HBox( FALSE, 5 ); + hbox.show(); + vbox.pack_start( hbox , FALSE, FALSE, 0 ); + // Checkboxes go here... (Flip X/Y) + ui::Widget flipX = ui::CheckButton( "Flip X axis" ); + ui::Widget flipY = ui::CheckButton( "Flip Y axis" ); + flipX.show(); + flipY.show(); + hbox.pack_start( flipX, FALSE, FALSE, 0 ); + hbox.pack_start( flipY, FALSE, FALSE, 0 ); + +//Instead of this, we probably need to create a vbox to put into the frame, then the +//window, then the hbox. !!! FIX !!! +// frame.add(hbox); + +//Hmm. Do we really need g_object_set_data? Mebbe not... And we don't! :-) +// g_object_set_data(G_OBJECT(flipX), "handler", gint_to_pointer(flipX.connect("toggled", G_CALLBACK(TexTool::flipX), 0))); +// g_object_set_data(G_OBJECT(flipY), "handler", gint_to_pointer(flipY.connect("toggled", G_CALLBACK(TexTool::flipY), 0))); +//Instead, just do: + flipX.connect( "toggled", G_CALLBACK( TexTool::flipX ), NULL ); + flipY.connect( "toggled", G_CALLBACK( TexTool::flipY ), NULL ); + } + } +#endif + } + + return window; } -void SurfaceDlg::ShowDlg() +/* + ============== + Update + + Set the fields to the current texdef (i.e. map/texdef -> dialog widgets) + if faces selected (instead of brushes) -> will read this face texdef, else current texdef + if only patches selected, will read the patch texdef + =============== + */ + +void spin_button_set_value_no_signal(ui::SpinButton spin, gdouble value) +{ + guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(spin), "handler")); + g_signal_handler_block(G_OBJECT(gtk_spin_button_get_adjustment(spin)), handler_id); + gtk_spin_button_set_value(spin, value); + g_signal_handler_unblock(G_OBJECT(gtk_spin_button_get_adjustment(spin)), handler_id); +} + +void spin_button_set_step_increment(ui::SpinButton spin, gdouble value) { - Dialog::ShowDlg(); - if(GetWidget() == NULL) - Create(); - g_surfwin = GetWidget (); + auto adjust = gtk_spin_button_get_adjustment(spin); + gtk_adjustment_set_step_increment(adjust, value); +} + +void SurfaceInspector::Update() +{ + const char *name = SurfaceInspector_GetSelectedShader(); + + if (shader_is_texture(name)) { + m_texture.text(shader_get_textureName(name)); + } else { + m_texture.text(""); + } + + texdef_t shiftScaleRotate; +//Shamus: This is where we get into trouble--the BP code tries to convert to a "faked" +//shift, rotate & scale values from the brush face, which seems to screw up for some reason. +//!!! FIX !!! +/*globalOutputStream() << "--> SI::Update. About to do ShiftScaleRotate_fromFace()...\n"; + SurfaceInspector_GetSelectedBPTexdef(); + globalOutputStream() << "BP: (" << g_selectedBrushPrimitTexdef.coords[0][0] << ", " << g_selectedBrushPrimitTexdef.coords[0][1] << ")(" + << g_selectedBrushPrimitTexdef.coords[1][0] << ", " << g_selectedBrushPrimitTexdef.coords[1][1] << ")(" + << g_selectedBrushPrimitTexdef.coords[0][2] << ", " << g_selectedBrushPrimitTexdef.coords[1][2] << ") SurfaceInspector::Update\n";//*/ +//Ok, it's screwed up *before* we get here... + ShiftScaleRotate_fromFace(shiftScaleRotate, SurfaceInspector_GetSelectedTexdef()); + + // normalize again to hide the ridiculously high scale values that get created when using texlock + shiftScaleRotate.shift[0] = float_mod(shiftScaleRotate.shift[0], (float) g_selectedShaderSize[0]); + shiftScaleRotate.shift[1] = float_mod(shiftScaleRotate.shift[1], (float) g_selectedShaderSize[1]); + + { + spin_button_set_value_no_signal(m_hshiftIncrement.m_spin, shiftScaleRotate.shift[0]); + spin_button_set_step_increment(m_hshiftIncrement.m_spin, g_si_globals.shift[0]); + entry_set_float(m_hshiftIncrement.m_entry, g_si_globals.shift[0]); + } + + { + spin_button_set_value_no_signal(m_vshiftIncrement.m_spin, shiftScaleRotate.shift[1]); + spin_button_set_step_increment(m_vshiftIncrement.m_spin, g_si_globals.shift[1]); + entry_set_float(m_vshiftIncrement.m_entry, g_si_globals.shift[1]); + } + + { + spin_button_set_value_no_signal(m_hscaleIncrement.m_spin, shiftScaleRotate.scale[0]); + spin_button_set_step_increment(m_hscaleIncrement.m_spin, g_si_globals.scale[0]); + entry_set_float(m_hscaleIncrement.m_entry, g_si_globals.scale[0]); + } + + { + spin_button_set_value_no_signal(m_vscaleIncrement.m_spin, shiftScaleRotate.scale[1]); + spin_button_set_step_increment(m_vscaleIncrement.m_spin, g_si_globals.scale[1]); + entry_set_float(m_vscaleIncrement.m_entry, g_si_globals.scale[1]); + } + + { + spin_button_set_value_no_signal(m_rotateIncrement.m_spin, shiftScaleRotate.rotate); + spin_button_set_step_increment(m_rotateIncrement.m_spin, g_si_globals.rotate); + entry_set_float(m_rotateIncrement.m_entry, g_si_globals.rotate); + } + + if (!string_empty(g_pGameDescription->getKeyValue("si_flags"))) { + ContentsFlagsValue flags(SurfaceInspector_GetSelectedFlags()); + + entry_set_int(m_valueEntryWidget, flags.m_value); + + for (GtkCheckButton **p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p) { + toggle_button_set_active_no_signal(ui::CheckButton::from(*p), + flags.m_surfaceFlags & (1 << (p - m_surfaceFlags))); + } + + for (GtkCheckButton **p = m_contentFlags; p != m_contentFlags + 32; ++p) { + toggle_button_set_active_no_signal(ui::CheckButton::from(*p), + flags.m_contentFlags & (1 << (p - m_contentFlags))); + } + } } -void SurfaceDlg::HideDlg() + +/* + ============== + Apply + + Reads the fields to get the current texdef (i.e. widgets -> MAP) + in brush primitive mode, grab the fake shift scale rot and compute a new texture matrix + =============== + */ +void SurfaceInspector::ApplyShader() { - g_surfwin = NULL; - Dialog::HideDlg(); + StringOutputStream name(256); + name << GlobalTexturePrefix_get() << gtk_entry_get_text(m_texture); + + // TTimo: detect and refuse invalid texture names (at least the ones with spaces) + if (!texdef_name_valid(name.c_str())) { + globalErrorStream() << "invalid texture name '" << name.c_str() << "'\n"; + SurfaceInspector_queueDraw(); + return; + } + + UndoableCommand undo("textureNameSetSelected"); + Select_SetShader(name.c_str()); } -GtkWidget* SurfaceDlg::GetWidget() +void SurfaceInspector::ApplyTexdef() { - return g_SurfaceTable.m_pfnGet_SI_Module_Widget(); + texdef_t shiftScaleRotate; + + shiftScaleRotate.shift[0] = static_cast( gtk_spin_button_get_value(m_hshiftIncrement.m_spin)); + shiftScaleRotate.shift[1] = static_cast( gtk_spin_button_get_value(m_vshiftIncrement.m_spin)); + shiftScaleRotate.scale[0] = static_cast( gtk_spin_button_get_value(m_hscaleIncrement.m_spin)); + shiftScaleRotate.scale[1] = static_cast( gtk_spin_button_get_value(m_vscaleIncrement.m_spin)); + shiftScaleRotate.rotate = static_cast( gtk_spin_button_get_value(m_rotateIncrement.m_spin)); + + TextureProjection projection; +//Shamus: This is the other place that screws up, it seems, since it doesn't seem to do the +//conversion from the face (I think) and so bogus values end up in the thing... !!! FIX !!! +//This is actually OK. :-P + ShiftScaleRotate_toFace(shiftScaleRotate, projection); + + UndoableCommand undo("textureProjectionSetSelected"); + Select_SetTexdef(projection); } -// set default values for increments (shift scale and rot) -// this is called by the prefs code if can't find the values -void SurfaceDlg::InitDefaultIncrement(texdef_t *tex) +void SurfaceInspector::ApplyFlags() { - tex->SetName("foo"); - tex->shift[0] = 8; - tex->shift[1] = 8; - tex->scale[0] = 0.25; - tex->scale[1] = 0.25; - tex->rotate = 10; + unsigned int surfaceflags = 0; + for (GtkCheckButton **p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p) { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*p))) { + surfaceflags |= (1 << (p - m_surfaceFlags)); + } + } + + unsigned int contentflags = 0; + for (GtkCheckButton **p = m_contentFlags; p != m_contentFlags + 32; ++p) { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*p))) { + contentflags |= (1 << (p - m_contentFlags)); + } + } + + int value = entry_get_int(m_valueEntryWidget); + + UndoableCommand undo("flagsSetSelected"); + Select_SetFlags(ContentsFlagsValue(surfaceflags, contentflags, value, true)); } -void SurfaceDlg::BuildDialog () + +void Face_getTexture(Face &face, CopiedString &shader, TextureProjection &projection, ContentsFlagsValue &flags) { - GtkWidget *dlg, *vbox, *hbox2, *frame, *table, *label; - GtkWidget *button, *entry, *spin; - - dlg = m_pWidget; - - load_window_pos (dlg, g_PrefsDlg.mWindowInfo.posSurfaceWnd); - - gtk_window_set_title (GTK_WINDOW (dlg), "Surface inspector"); - //g_signal_connect (G_OBJECT (dlg), "delete_event", G_CALLBACK (OnCancel), NULL); - // we catch 'Enter' and interpret is as OnDone - gtk_signal_connect (GTK_OBJECT (dlg), "key_press_event", GTK_SIGNAL_FUNC (OnDialogKey), NULL); - gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (g_pParentWnd->m_pWidget)); - - // replaced by only the vbox: - vbox = gtk_vbox_new (FALSE, 5); - gtk_widget_show (vbox); - gtk_container_add (GTK_CONTAINER (dlg), vbox); - gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); - - hbox2 = gtk_hbox_new (FALSE, 5); - gtk_widget_show (hbox2); - gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, TRUE, 0); - - label = gtk_label_new ("Texture"); - gtk_widget_show (label); - gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, TRUE, 0); - - entry = gtk_entry_new (); - gtk_signal_connect (GTK_OBJECT (entry), "key_press_event", GTK_SIGNAL_FUNC (OnTextureKey), NULL); - gtk_widget_show (entry); - gtk_box_pack_start (GTK_BOX (hbox2), entry, TRUE, TRUE, 0); - g_object_set_data (G_OBJECT (m_pWidget), "texture", entry); - -// table = gtk_table_new (5, 4, FALSE); - table = gtk_table_new (6, 4, FALSE); - gtk_widget_show (table); - gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0); - gtk_table_set_row_spacings (GTK_TABLE (table), 5); - gtk_table_set_col_spacings (GTK_TABLE (table), 5); - - label = gtk_label_new ("Horizontal shift"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - spin = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (0, -8192, 8192, 2, 8, 8)), 0, 0); - g_object_set_data (G_OBJECT (dlg), "hshift", spin); - gtk_signal_connect (GTK_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin))), "value_changed", - GTK_SIGNAL_FUNC (OnUpdate), NULL); - gtk_widget_show (spin); - gtk_table_attach (GTK_TABLE (table), spin, 1, 2, 0, 1, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (spin, 60, -2); - - label = gtk_label_new ("Step"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - entry = gtk_entry_new (); - g_object_set_data (G_OBJECT (dlg), "hshift_inc", entry); - gtk_signal_connect (GTK_OBJECT (entry), "changed", - GTK_SIGNAL_FUNC (OnIncrementChanged), NULL); - gtk_widget_show (entry); - gtk_table_attach (GTK_TABLE (table), entry, 3, 4, 0, 1, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (entry, 50, -2); - - label = gtk_label_new ("Vertical shift"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - spin = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (0, -8192, 8192, 2, 8, 8)), 0, 0); - g_object_set_data (G_OBJECT (dlg), "vshift", spin); - gtk_signal_connect (GTK_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin))), "value_changed", - GTK_SIGNAL_FUNC (OnUpdate), NULL); - gtk_widget_show (spin); - gtk_table_attach (GTK_TABLE (table), spin, 1, 2, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (spin, 60, -2); - - label = gtk_label_new ("Step"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - entry = gtk_entry_new (); - g_object_set_data (G_OBJECT (dlg), "vshift_inc", entry); - gtk_signal_connect (GTK_OBJECT (entry), "changed", - GTK_SIGNAL_FUNC (OnIncrementChanged), NULL); - gtk_widget_show (entry); - gtk_table_attach (GTK_TABLE (table), entry, 3, 4, 1, 2, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (entry, 50, -2); - - label = gtk_label_new ("Horizontal stretch"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - spin = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (0, -1000, 1000, 1, 10, 10)), 0, 0); - g_object_set_data (G_OBJECT (dlg), "hscale", spin); - gtk_signal_connect (GTK_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin))), "value_changed", - GTK_SIGNAL_FUNC (OnUpdate), NULL); - gtk_widget_show (spin); - gtk_table_attach (GTK_TABLE (table), spin, 1, 2, 2, 3, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (spin, 60, -2); - - label = gtk_label_new ("Step"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 2, 3, 2, 3, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 2, 3); - - entry = gtk_entry_new (); - g_object_set_data (G_OBJECT (dlg), "hscale_inc", entry); - gtk_signal_connect (GTK_OBJECT (entry), "changed", - GTK_SIGNAL_FUNC (OnIncrementChanged), NULL); - gtk_widget_show (entry); - gtk_table_attach (GTK_TABLE (table), entry, 3, 4, 2, 3, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 2, 3); - gtk_widget_set_usize (entry, 50, -2); - - label = gtk_label_new ("Vertical stretch"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - spin = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (0, -1000, 1000, 1, 10, 10)), 0, 0); - g_object_set_data (G_OBJECT (dlg), "vscale", spin); - gtk_signal_connect (GTK_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin))), "value_changed", - GTK_SIGNAL_FUNC (OnUpdate), NULL); - gtk_widget_show (spin); - gtk_table_attach (GTK_TABLE (table), spin, 1, 2, 3, 4, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (spin, 60, -2); - - label = gtk_label_new ("Step"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 2, 3, 3, 4, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - entry = gtk_entry_new (); - g_object_set_data (G_OBJECT (dlg), "vscale_inc", entry); - gtk_signal_connect (GTK_OBJECT (entry), "changed", - GTK_SIGNAL_FUNC (OnIncrementChanged), NULL); - gtk_widget_show (entry); - gtk_table_attach (GTK_TABLE (table), entry, 3, 4, 3, 4, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (entry, 50, -2); - - label = gtk_label_new ("Rotate"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - spin = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (0, -360, 360, 1, 10, 10)), 1, 0); - g_object_set_data (G_OBJECT (dlg), "rotate", spin); - gtk_signal_connect (GTK_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin))), "value_changed", - GTK_SIGNAL_FUNC (OnUpdate), NULL); - gtk_widget_show (spin); - gtk_table_attach (GTK_TABLE (table), spin, 1, 2, 4, 5, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (spin, 60, -2); - gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spin), TRUE); - - label = gtk_label_new ("Step"); - gtk_widget_show (label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0); - gtk_table_attach (GTK_TABLE (table), label, 2, 3, 4, 5, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - entry = gtk_entry_new (); - g_object_set_data (G_OBJECT (dlg), "rotate_inc", entry); - gtk_signal_connect (GTK_OBJECT (entry), "changed", - GTK_SIGNAL_FUNC (OnIncrementChanged), NULL); - gtk_widget_show (entry); - gtk_table_attach (GTK_TABLE (table), entry, 3, 4, 4, 5, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (entry, 50, -2); - - // match grid button - button = gtk_button_new_with_label ("Match Grid"); - gtk_widget_show (button); - gtk_table_attach (GTK_TABLE (table), button, 2, 4, 5, 6, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnBtnMatchGrid), NULL); - - frame = gtk_frame_new ("Texturing"); - gtk_widget_show (frame); - gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0); - - table = gtk_table_new (4, 4, FALSE); - gtk_widget_show (table); - gtk_container_add (GTK_CONTAINER (frame), table); - gtk_table_set_row_spacings (GTK_TABLE (table), 5); - gtk_table_set_col_spacings (GTK_TABLE (table), 5); - gtk_container_set_border_width (GTK_CONTAINER (table), 5); - - label = gtk_label_new ("Brush"); - gtk_widget_show (label); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - label = gtk_label_new ("Patch"); - gtk_widget_show (label); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - label = gtk_label_new ("Width"); - gtk_widget_show (label); - gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - label = gtk_label_new ("Height"); - gtk_widget_show (label); - gtk_table_attach (GTK_TABLE (table), label, 3, 4, 0, 1, - (GtkAttachOptions) (GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - - button = gtk_button_new_with_label ("Axial"); - gtk_widget_show (button); - gtk_table_attach (GTK_TABLE (table), button, 0, 1, 1, 2, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnBtnAxial), NULL); - gtk_widget_set_usize (button, 60, -2); - - button = gtk_button_new_with_label ("Fit"); - gtk_widget_show (button); - gtk_table_attach (GTK_TABLE (table), button, 1, 2, 1, 2, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnBtnFaceFit), NULL); - gtk_widget_set_usize (button, 60, -2); - - button = gtk_button_new_with_label ("CAP"); - gtk_widget_show (button); - gtk_table_attach (GTK_TABLE (table), button, 0, 1, 3, 4, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnBtnPatchdetails), NULL); - gtk_widget_set_usize (button, 60, -2); - - button = gtk_button_new_with_label ("Set..."); - gtk_widget_show (button); - gtk_table_attach (GTK_TABLE (table), button, 1, 2, 3, 4, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnBtnPatchreset), NULL); - gtk_widget_set_usize (button, 60, -2); - - button = gtk_button_new_with_label ("Natural"); - gtk_widget_show (button); - gtk_table_attach (GTK_TABLE (table), button, 2, 3, 3, 4, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnBtnPatchnatural), NULL); - gtk_widget_set_usize (button, 60, -2); - - button = gtk_button_new_with_label ("Fit"); - gtk_widget_show (button); - gtk_table_attach (GTK_TABLE (table), button, 3, 4, 3, 4, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnBtnPatchFit), NULL); - gtk_widget_set_usize (button, 60, -2); - - spin = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (1, 1, 32, 1, 10, 10)), 1, 0); - gtk_widget_show (spin); - gtk_table_attach (GTK_TABLE (table), spin, 2, 3, 1, 2, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (spin, 60, -2); - AddDialogData (spin, &m_nWidth, DLG_SPIN_INT); - - spin = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (1, 1, 32, 1, 10, 10)), 1, 0); - gtk_widget_show (spin); - gtk_table_attach (GTK_TABLE (table), spin, 3, 4, 1, 2, - (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), - (GtkAttachOptions) (0), 0, 0); - gtk_widget_set_usize (spin, 60, -2); - AddDialogData (spin, &m_nHeight, DLG_SPIN_INT); - - hbox2 = gtk_hbox_new (FALSE, 5); - gtk_widget_show (hbox2); - gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, TRUE, 0); - - button = gtk_button_new_with_label ("Done"); - gtk_widget_show (button); - gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, FALSE, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnDone), NULL); - gtk_widget_set_usize (button, 60, -2); - - button = gtk_button_new_with_label ("Apply"); - gtk_widget_show (button); - gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, FALSE, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnApply), NULL); - gtk_widget_set_usize (button, 60, -2); - - button = gtk_button_new_with_label ("Cancel"); - gtk_widget_show (button); - gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, FALSE, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnCancel), NULL); - gtk_widget_set_usize (button, 60, -2); - - // that's a bit of trashy stuff from Textool-v2 branch -#ifdef _DEBUG - // FIXME: testing only, scaling in BP mode - button = gtk_button_new_with_label ("Test"); - gtk_widget_show (button); - gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE, FALSE, 0); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (OnTest), NULL); - gtk_widget_set_usize (button, 60, -2); -#endif + shader = face.GetShader(); + face.GetTexdef(projection); + flags = face.getShader().m_flags; +} - // Initialize - SetTexMods (); +typedef Function FaceGetTexture; + +void +Face_setTexture(Face &face, const char *shader, const TextureProjection &projection, const ContentsFlagsValue &flags) +{ + face.SetShader(shader); + face.SetTexdef(projection); + face.SetFlags(flags); } -/* -============== -SetTexMods +typedef Function FaceSetTexture; -Set the fields to the current texdef (i.e. map/texdef -> dialog widgets) -if faces selected (instead of brushes) -> will read this face texdef, else current texdef -if only patches selected, will read the patch texdef -=============== -*/ -void SurfaceDlg::SetTexMods() +void Patch_getTexture(Patch &patch, CopiedString &shader, TextureProjection &projection, ContentsFlagsValue &flags) { - texdef_t *pt; - brushprimit_texdef_t *bpt; - // local copy if a width=2 height=2 qtetxture_t is needed - brushprimit_texdef_t local_bp; + shader = patch.GetShader(); + projection = TextureProjection(texdef_t(), brushprimit_texdef_t(), Vector3(0, 0, 0), Vector3(0, 0, 0)); + flags = ContentsFlagsValue(0, 0, 0, false); +} -#ifdef DBG_SI - Sys_Printf("SurfaceDlg::SetTexMods\n"); -#endif +typedef Function PatchGetTexture; - if (!g_surfwin) - return; - - if (g_ptrSelectedFaces.GetSize() > 0) - { - face_t *selFace = reinterpret_cast(g_ptrSelectedFaces.GetAt(0)); - pt = &selFace->texdef; - if (g_qeglobals.m_bBrushPrimitMode) +void +Patch_setTexture(Patch &patch, const char *shader, const TextureProjection &projection, const ContentsFlagsValue &flags) +{ + patch.SetShader(shader); +} + +typedef Function PatchSetTexture; + + +typedef Callback GetTextureCallback; +typedef Callback SetTextureCallback; + +struct Texturable { + GetTextureCallback getTexture; + SetTextureCallback setTexture; +}; + + +void Face_getClosest(Face &face, SelectionTest &test, SelectionIntersection &bestIntersection, Texturable &texturable) +{ + SelectionIntersection intersection; + face.testSelect(test, intersection); + if (intersection.valid() + && SelectionIntersection_closer(intersection, bestIntersection)) { + bestIntersection = intersection; + texturable.setTexture = makeCallback(FaceSetTexture(), face); + texturable.getTexture = makeCallback(FaceGetTexture(), face); + } +} + + +class OccludeSelector : public Selector { + SelectionIntersection &m_bestIntersection; + bool &m_occluded; +public: + OccludeSelector(SelectionIntersection &bestIntersection, bool &occluded) : m_bestIntersection(bestIntersection), + m_occluded(occluded) + { + m_occluded = false; + } + + void pushSelectable(Selectable &selectable) { - // compute a texture matrix related to the default matrix width=2 height=2 - ConvertTexMatWithQTexture( &selFace->brushprimit_texdef, selFace->d_texture, &local_bp, NULL ); - bpt = &local_bp; } - } - else - { - pt = &g_qeglobals.d_texturewin.texdef; - if (g_qeglobals.m_bBrushPrimitMode) + + void popSelectable() { - bpt = &g_qeglobals.d_texturewin.brushprimit_texdef; } - } - // brush primitive mode : compute fake shift scale rot representation - if (g_qeglobals.m_bBrushPrimitMode) - TexMatToFakeTexCoords( bpt->coords, m_shift, &m_rotate, m_scale ); - - g_bListenChanged = false; - - if(strncmp(pt->GetName(), "textures/", 9) != 0) - pt->SetName(SHADER_NOT_FOUND); - gtk_entry_set_text (GTK_ENTRY (GetDlgWidget ("texture")), pt->GetName()+9); - - GtkSpinButton *spin; - spin = GTK_SPIN_BUTTON (GetDlgWidget ("hshift")); - gtk_spin_button_set_digits (spin, 2); - if (g_qeglobals.m_bBrushPrimitMode) - gtk_spin_button_set_value (spin, m_shift[0]); - else - gtk_spin_button_set_value (spin, pt->shift[0]); - GtkAdjustment *adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin)); - adjust->step_increment = l_pIncrement->shift[0]; - char buf[10]; // got into snprintf paranoia after BoundChecker detected a stack overrun -#ifdef _WIN32 - // TTimo: THIS IS UGLY -#define snprintf _snprintf -#endif - snprintf (buf, 10, "%g", l_pIncrement->shift[0]); - gtk_entry_set_text (GTK_ENTRY (GetDlgWidget ("hshift_inc")), buf); - spin = GTK_SPIN_BUTTON (GetDlgWidget ("vshift")); - gtk_spin_button_set_digits (spin, 2); - if (g_qeglobals.m_bBrushPrimitMode) - gtk_spin_button_set_value (spin, m_shift[1]); - else - gtk_spin_button_set_value (spin, pt->shift[1]); - adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin)); - adjust->step_increment = l_pIncrement->shift[1]; - snprintf (buf, 10, "%g", l_pIncrement->shift[1]); - gtk_entry_set_text (GTK_ENTRY (GetDlgWidget ("vshift_inc")), buf); - - spin = GTK_SPIN_BUTTON (GetDlgWidget ("hscale")); - gtk_spin_button_set_digits (spin, 5); - gtk_spin_button_set_value (spin, g_qeglobals.m_bBrushPrimitMode ? m_scale[0] : pt->scale[0]); - - adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin)); - adjust->step_increment = l_pIncrement->scale[0]; - snprintf (buf, 10, "%g", l_pIncrement->scale[0]); - gtk_entry_set_text (GTK_ENTRY (GetDlgWidget ("hscale_inc")), buf); - - spin = GTK_SPIN_BUTTON (GetDlgWidget ("vscale")); - gtk_spin_button_set_digits (spin, 5); - gtk_spin_button_set_value (spin, g_qeglobals.m_bBrushPrimitMode ? m_scale[1] : pt->scale[1]); - - adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin)); - adjust->step_increment = l_pIncrement->scale[1]; - snprintf (buf, 10, "%g", l_pIncrement->scale[1]); - gtk_entry_set_text (GTK_ENTRY (GetDlgWidget ("vscale_inc")), buf); - - //++timo compute BProtate as int .. - spin = GTK_SPIN_BUTTON (GetDlgWidget ("rotate")); - gtk_spin_button_set_digits (spin, 2); - gtk_spin_button_set_value (spin, g_qeglobals.m_bBrushPrimitMode ? m_rotate : pt->rotate); - - adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin)); - adjust->step_increment = l_pIncrement->rotate; - snprintf (buf, 10, "%g", l_pIncrement->rotate); - gtk_entry_set_text (GTK_ENTRY (GetDlgWidget ("rotate_inc")), buf); - - g_bListenChanged = true; - - // undo tricks: set the undo id to zero so we don't attempt to undo something that does not belong to us - m_nUndoId = 0; - // store the current texdef as our escape route if user hits OnCancel - g_old_texdef = g_qeglobals.d_texturewin.texdef; - // reset the Enter key behaviour flag - m_bEditingTextureWidget = false; + void addIntersection(const SelectionIntersection &intersection) + { + if (SelectionIntersection_closer(intersection, m_bestIntersection)) { + m_bestIntersection = intersection; + m_occluded = true; + } + } +}; + +class BrushGetClosestFaceVisibleWalker : public scene::Graph::Walker { + SelectionTest &m_test; + Texturable &m_texturable; + mutable SelectionIntersection m_bestIntersection; +public: + BrushGetClosestFaceVisibleWalker(SelectionTest &test, Texturable &texturable) : m_test(test), + m_texturable(texturable) + { + } + + bool pre(const scene::Path &path, scene::Instance &instance) const + { + if (path.top().get().visible()) { + BrushInstance *brush = Instance_getBrush(instance); + if (brush != 0) { + m_test.BeginMesh(brush->localToWorld()); + + for (Brush::const_iterator i = brush->getBrush().begin(); i != brush->getBrush().end(); ++i) { + Face_getClosest(*(*i), m_test, m_bestIntersection, m_texturable); + } + } else { + SelectionTestable *selectionTestable = Instance_getSelectionTestable(instance); + if (selectionTestable) { + bool occluded; + OccludeSelector selector(m_bestIntersection, occluded); + selectionTestable->testSelect(selector, m_test); + if (occluded) { + Patch *patch = Node_getPatch(path.top()); + if (patch != 0) { + m_texturable.setTexture = makeCallback(PatchSetTexture(), *patch); + m_texturable.getTexture = makeCallback(PatchGetTexture(), *patch); + } else { + m_texturable = Texturable(); + } + } + } + } + } + return true; + } +}; + +Texturable Scene_getClosestTexturable(scene::Graph &graph, SelectionTest &test) +{ + Texturable texturable; + graph.traverse(BrushGetClosestFaceVisibleWalker(test, texturable)); + return texturable; } -/* -============== -GetTexMods - -Reads the fields to get the current texdef (i.e. widgets -> MAP) -in brush primitive mode, grab the fake shift scale rot and compute a new texture matrix -=============== -*/ -void SurfaceDlg::GetTexMods() +bool +Scene_getClosestTexture(scene::Graph &graph, SelectionTest &test, CopiedString &shader, TextureProjection &projection, + ContentsFlagsValue &flags) +{ + Texturable texturable = Scene_getClosestTexturable(graph, test); + if (texturable.getTexture != GetTextureCallback()) { + texturable.getTexture(shader, projection, flags); + return true; + } + return false; +} + +void Scene_setClosestTexture(scene::Graph &graph, SelectionTest &test, const char *shader, + const TextureProjection &projection, const ContentsFlagsValue &flags) { - char buffer[1024]; - texdef_t *pt; + Texturable texturable = Scene_getClosestTexturable(graph, test); + if (texturable.setTexture != SetTextureCallback()) { + texturable.setTexture(shader, projection, flags); + } +} -#ifdef DBG_SI - Sys_Printf("SurfaceDlg::GetTexMods\n"); -#endif - - if (g_ptrSelectedFaces.GetSize() > 0) - { - //++timo just a test, we disable the undo when working on selected faces - m_nUndoId=0; - face_t *selFace = reinterpret_cast(g_ptrSelectedFaces.GetAt(0)); - g_qeglobals.d_texturewin.texdef = selFace->texdef; -#ifdef DBG_SI - Sys_Printf("g_qeglobals.d_texturewin.texdef = selFace->texdef\n"); -#endif - } -// else -// { - pt = &g_qeglobals.d_texturewin.texdef; -#ifdef DBG_SI - Sys_Printf("pt = &g_qeglobals.d_texturewin.texdef\n"); -#endif -// } - - const char* text = gtk_entry_get_text (GTK_ENTRY (GetDlgWidget ("texture"))); -#ifdef DBG_SI - Sys_Printf("pt->SetName(%s)\n", text ); -#endif - - // TTimo: detect and refuse invalid texture names (at least the ones with spaces) - if (text[0] <= ' ' || strchr(text, ' ')) - { - Sys_FPrintf(SYS_WRN, "WARNING: spaces in shader names are not allowed, ignoring '%s'\n", text); - pt->SetName(SHADER_NOT_FOUND); - gtk_entry_set_text (GTK_ENTRY (GetDlgWidget ("texture")), pt->GetName()); - } - else - { - strcpy(buffer, "textures/"); - strcpy(buffer+9, text); - pt->SetName(buffer); - } - - - (g_qeglobals.m_bBrushPrimitMode ? m_shift[0] : pt->shift[0]) = - gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (GetDlgWidget ("hshift"))); - (g_qeglobals.m_bBrushPrimitMode ? m_shift[1] : pt->shift[1]) = - gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (GetDlgWidget ("vshift"))); - (g_qeglobals.m_bBrushPrimitMode ? m_scale[0] : pt->scale[0]) = - gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (GetDlgWidget ("hscale"))); - (g_qeglobals.m_bBrushPrimitMode ? m_scale[1] : pt->scale[1]) = - gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (GetDlgWidget ("vscale"))); - (g_qeglobals.m_bBrushPrimitMode ? m_rotate : pt->rotate) = - gtk_spin_button_get_value_as_float (GTK_SPIN_BUTTON (GetDlgWidget ("rotate"))); - - // a local copy of the texture matrix, given for a qtexture_t with width=2 height=2 - brushprimit_texdef_t local_bp; - brushprimit_texdef_t *bpt; - if (g_qeglobals.m_bBrushPrimitMode) - { - face_t *selFace = NULL; - if (g_ptrSelectedFaces.GetSize() > 0) +class FaceTexture { +public: + TextureProjection m_projection; + ContentsFlagsValue m_flags; +}; + +FaceTexture g_faceTextureClipboard; + +void FaceTextureClipboard_setDefault() +{ + g_faceTextureClipboard.m_flags = ContentsFlagsValue(0, 0, 0, false); + TexDef_Construct_Default(g_faceTextureClipboard.m_projection); +} + +void TextureClipboard_textureSelected(const char *shader) +{ + FaceTextureClipboard_setDefault(); +} + +class TextureBrowser; + +extern TextureBrowser g_TextureBrowser; + +void TextureBrowser_SetSelectedShader(TextureBrowser &textureBrowser, const char *shader); + +const char *TextureBrowser_GetSelectedShader(TextureBrowser &textureBrowser); + +void Scene_copyClosestTexture(SelectionTest &test) +{ + CopiedString shader; + if (Scene_getClosestTexture(GlobalSceneGraph(), test, shader, g_faceTextureClipboard.m_projection, + g_faceTextureClipboard.m_flags)) { + TextureBrowser_SetSelectedShader(g_TextureBrowser, shader.c_str()); + } +} + +void Scene_applyClosestTexture(SelectionTest &test) +{ + UndoableCommand command("facePaintTexture"); + + Scene_setClosestTexture(GlobalSceneGraph(), test, TextureBrowser_GetSelectedShader(g_TextureBrowser), + g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags); + + SceneChangeNotify(); +} + + +void SelectedFaces_copyTexture() +{ + if (!g_SelectedFaceInstances.empty()) { + Face &face = g_SelectedFaceInstances.last().getFace(); + face.GetTexdef(g_faceTextureClipboard.m_projection); + g_faceTextureClipboard.m_flags = face.getShader().m_flags; + + TextureBrowser_SetSelectedShader(g_TextureBrowser, face.getShader().getShader()); + } +} + +void FaceInstance_pasteTexture(FaceInstance &faceInstance) +{ + faceInstance.getFace().SetTexdef(g_faceTextureClipboard.m_projection); + faceInstance.getFace().SetShader(TextureBrowser_GetSelectedShader(g_TextureBrowser)); + faceInstance.getFace().SetFlags(g_faceTextureClipboard.m_flags); + SceneChangeNotify(); +} + +bool SelectedFaces_empty() +{ + return g_SelectedFaceInstances.empty(); +} + +void SelectedFaces_pasteTexture() +{ + UndoableCommand command("facePasteTexture"); + g_SelectedFaceInstances.foreach(FaceInstance_pasteTexture); +} + + +void SurfaceInspector_constructPreferences(PreferencesPage &page) +{ + page.appendCheckBox("", "Surface Inspector Increments Match Grid", g_si_globals.m_bSnapTToGrid); +} + +void SurfaceInspector_constructPage(PreferenceGroup &group) +{ + PreferencesPage page(group.createPage("Surface Inspector", "Surface Inspector Preferences")); + SurfaceInspector_constructPreferences(page); +} + +void SurfaceInspector_registerPreferencesPage() +{ + PreferencesDialog_addSettingsPage(makeCallbackF(SurfaceInspector_constructPage)); +} + +void SurfaceInspector_registerCommands() +{ + GlobalCommands_insert("FitTexture", makeCallbackF(SurfaceInspector_FitTexture), + Accelerator('B', (GdkModifierType) GDK_SHIFT_MASK)); + GlobalCommands_insert("SurfaceInspector", makeCallbackF(SurfaceInspector_toggleShown), Accelerator('S')); + + GlobalCommands_insert("FaceCopyTexture", makeCallbackF(SelectedFaces_copyTexture)); + GlobalCommands_insert("FacePasteTexture", makeCallbackF(SelectedFaces_pasteTexture)); +} + + +#include "preferencesystem.h" + + +void SurfaceInspector_Construct() +{ + g_SurfaceInspector = new SurfaceInspector; + + SurfaceInspector_registerCommands(); + + FaceTextureClipboard_setDefault(); + + GlobalPreferenceSystem().registerPreference("SurfaceWnd", make_property( + getSurfaceInspector().m_positionTracker)); + GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale1", make_property_string(g_si_globals.scale[0])); + GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale2", make_property_string(g_si_globals.scale[1])); + GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift1", make_property_string(g_si_globals.shift[0])); + GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift2", make_property_string(g_si_globals.shift[1])); + GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Rotate", make_property_string(g_si_globals.rotate)); + GlobalPreferenceSystem().registerPreference("SnapTToGrid", make_property_string(g_si_globals.m_bSnapTToGrid)); + + typedef FreeCaller SurfaceInspectorSelectionChangedCaller; + GlobalSelectionSystem().addSelectionChangeCallback(SurfaceInspectorSelectionChangedCaller()); + typedef FreeCaller SurfaceInspectorUpdateSelectionCaller; + Brush_addTextureChangedCallback(SurfaceInspectorUpdateSelectionCaller()); + Patch_addTextureChangedCallback(SurfaceInspectorUpdateSelectionCaller()); + + SurfaceInspector_registerPreferencesPage(); +} + +void SurfaceInspector_Destroy() +{ + delete g_SurfaceInspector; +} + + +#if TEXTOOL_ENABLED + +namespace TexTool { // namespace hides these symbols from other object-files +// +//Shamus: Textool functions, including GTK+ callbacks +// + +//NOTE: Black screen when TT first comes up is caused by an uninitialized Extent... !!! FIX !!! +// But... You can see down below that it *is* initialized! WTF? +struct Extent +{ + float minX, minY, maxX, maxY; + float width( void ) { return fabs( maxX - minX ); } + float height( void ) { return fabs( maxY - minY ); } +}; + +//This seems to control the texture scale... (Yep! ;-) +Extent extents = { -2.0f, -2.0f, +2.0f, +2.0f }; +brushprimit_texdef_t tm; // Texture transform matrix +Vector2 pts[c_brush_maxFaces]; +Vector2 center; +int numPts; +int textureNum; +Vector2 textureSize; +Vector2 windowSize; +#define VP_PADDING 1.2 +#define PI 3.14159265358979 +bool lButtonDown = false; +bool rButtonDown = false; +//int dragPoint; +//int anchorPoint; +bool haveAnchor = false; +brushprimit_texdef_t currentBP; +brushprimit_texdef_t origBP; // Original brush primitive (before we muck it up) +float controlRadius = 5.0f; +float rotationAngle = 0.0f; +float rotationAngle2 = 0.0f; +float oldRotationAngle; +Vector2 rotationPoint; +bool translatingX = false; // Widget state variables +bool translatingY = false; +bool scalingX = false; +bool scalingY = false; +bool rotating = false; +bool resizingX = false; // Not sure what this means... :-/ +bool resizingY = false; +float origAngle, origScaleX, origScaleY; +Vector2 oldCenter; + + +// Function prototypes (move up to top later...) + +void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius ); + + +void CopyPointsFromSelectedFace( void ){ + // Make sure that there's a face and winding to get! + + if ( g_SelectedFaceInstances.empty() ) { + numPts = 0; + return; + } + + Face & face = g_SelectedFaceInstances.last().getFace(); + textureNum = face.getShader().m_state->getTexture().texture_number; + textureSize.x() = face.getShader().m_state->getTexture().width; + textureSize.y() = face.getShader().m_state->getTexture().height; +//globalOutputStream() << "--> Texture #" << textureNum << ": " << textureSize.x() << " x " << textureSize.y() << "...\n"; + + currentBP = SurfaceInspector_GetSelectedTexdef().m_brushprimit_texdef; + + face.EmitTextureCoordinates(); + Winding & w = face.getWinding(); + int count = 0; + + for ( Winding::const_iterator i = w.begin(); i != w.end(); i++ ) + { + //globalOutputStream() << (*i).texcoord.x() << " " << (*i).texcoord.y() << ", "; + pts[count].x() = ( *i ).texcoord.x(); + pts[count].y() = ( *i ).texcoord.y(); + count++; + } + + numPts = count; + + //globalOutputStream() << " ..copied points\n"; +} + +brushprimit_texdef_t bp; +//This approach is probably wrongheaded and just not right anyway. So, !!! FIX !!! [DONE] +void CommitChanges( void ){ + texdef_t t; // Throwaway, since this is BP only + + bp.coords[0][0] = tm.coords[0][0] * origBP.coords[0][0] + tm.coords[0][1] * origBP.coords[1][0]; + bp.coords[0][1] = tm.coords[0][0] * origBP.coords[0][1] + tm.coords[0][1] * origBP.coords[1][1]; + bp.coords[0][2] = tm.coords[0][0] * origBP.coords[0][2] + tm.coords[0][1] * origBP.coords[1][2] + tm.coords[0][2]; +//Ok, this works for translation... +// bp.coords[0][2] = tm.coords[0][0] * origBP.coords[0][2] + tm.coords[0][1] * origBP.coords[1][2] + tm.coords[0][2] * textureSize.x(); + bp.coords[1][0] = tm.coords[1][0] * origBP.coords[0][0] + tm.coords[1][1] * origBP.coords[1][0]; + bp.coords[1][1] = tm.coords[1][0] * origBP.coords[0][1] + tm.coords[1][1] * origBP.coords[1][1]; + bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2]; +// bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2] * textureSize.y(); + +//This doesn't work: g_brush_texture_changed(); +// Let's try this: +//Note: We should only set an undo *after* the button has been released... !!! FIX !!! +//Definitely *should* have an undo, though! +// UndoableCommand undo("textureProjectionSetSelected"); + Select_SetTexdef( TextureProjection( t, bp, Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) ) ); +//This is working, but for some reason the translate is causing the rest of the SI +//widgets to yield bad readings... !!! FIX !!! +//I.e., click on textool window, translate face wireframe, then controls go crazy. Dunno why. +//It's because there were some uncommented out add/removeScale functions in brush.h and a +//removeScale in brushmanip.cpp... :-/ +//Translate isn't working at all now... :-( +//It's because we need to multiply in some scaling factor (prolly the texture width/height) +//Yep. :-P +} + +void UpdateControlPoints( void ){ + CommitChanges(); + + // Init texture transform matrix + + tm.coords[0][0] = 1.0f; tm.coords[0][1] = 0.0f; tm.coords[0][2] = 0.0f; + tm.coords[1][0] = 0.0f; tm.coords[1][1] = 1.0f; tm.coords[1][2] = 0.0f; +} + + +/* + For shifting we have: + */ +/* + The code that should provide reasonable defaults, but doesn't for some reason: + It's scaling the BP by 128 for some reason, between the time it's created and the + time we get back to the SI widgets: + + static void OnBtnAxial(GtkWidget *widget, gpointer data) + { + UndoableCommand undo("textureDefault"); + TextureProjection projection; + TexDef_Construct_Default(projection); + Select_SetTexdef(projection); + } + + Select_SetTexdef() calls Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection) + which is in brushmanip.h: This eventually calls + Texdef_Assign(m_texdef, texdef, m_brushprimit_texdef, brushprimit_texdef) in class Face... + which just copies from brushpr to m_brushpr... + */ + +//Small problem with this thing: It's scaled to the texture which is all screwed up... !!! FIX !!! [DONE] +//Prolly should separate out the grid drawing so that we can draw it behind the polygon. +const float gridWidth = 1.3f; // Let's try an absolute height... WORKS!!! +// NOTE that 2.0 is the height of the viewport. Dunno why... Should make collision +// detection easier... +const float gridRadius = gridWidth * 0.5f; + +typedef const float WidgetColor[3]; +const WidgetColor widgetColor[10] = { + { 1.0000f, 0.2000f, 0.0000f }, // Red + { 0.9137f, 0.9765f, 0.4980f }, // Yellow + { 0.0000f, 0.6000f, 0.3216f }, // Green + { 0.6157f, 0.7726f, 0.8196f }, // Cyan + { 0.4980f, 0.5000f, 0.4716f }, // Grey + + // Highlight colors + { 1.0000f, 0.6000f, 0.4000f }, // Light Red + { 1.0000f, 1.0000f, 0.8980f }, // Light Yellow + { 0.4000f, 1.0000f, 0.7216f }, // Light Green + { 1.0000f, 1.0000f, 1.0000f }, // Light Cyan + { 0.8980f, 0.9000f, 0.8716f } // Light Grey +}; + +#define COLOR_RED 0 +#define COLOR_YELLOW 1 +#define COLOR_GREEN 2 +#define COLOR_CYAN 3 +#define COLOR_GREY 4 +#define COLOR_LT_RED 5 +#define COLOR_LT_YELLOW 6 +#define COLOR_LT_GREEN 7 +#define COLOR_LT_CYAN 8 +#define COLOR_LT_GREY 9 + +void DrawControlWidgets( void ){ +//Note that the grid should go *behind* the face outline... !!! FIX !!! + // Grid + float xStart = center.x() - ( gridWidth / 2.0f ); + float yStart = center.y() - ( gridWidth / 2.0f ); + float xScale = ( extents.height() / extents.width() ) * ( textureSize.y() / textureSize.x() ); + + glPushMatrix(); +//Small problem with this approach: Changing the center point in the TX code doesn't seem to +//change anything here--prolly because we load a new identity matrix. A couple of ways to fix +//this would be to get rid of that code, or change the center to a new point by taking into +//account the transforms that we toss with the new identity matrix. Dunno which is better. + glLoadIdentity(); + glScalef( xScale, 1.0, 1.0 ); // Will that square it up? Yup. + glRotatef( static_cast( radians_to_degrees( atan2( -currentBP.coords[0][1], currentBP.coords[0][0] ) ) ), 0.0, 0.0, -1.0 ); + glTranslatef( -center.x(), -center.y(), 0.0 ); + + // Circle + glColor3fv( translatingX && translatingY ? widgetColor[COLOR_LT_YELLOW] : widgetColor[COLOR_YELLOW] ); + glBegin( GL_LINE_LOOP ); + DrawCircularArc( center, 0, 2.0f * PI, gridRadius * 0.16 ); + + glEnd(); + + // Axes + glBegin( GL_LINES ); + glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] ); + glVertex2f( center.x(), center.y() + ( gridRadius * 0.16 ) ); + glVertex2f( center.x(), center.y() + ( gridRadius * 1.00 ) ); + glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] ); + glVertex2f( center.x() + ( gridRadius * 0.16 ), center.y() ); + glVertex2f( center.x() + ( gridRadius * 1.00 ), center.y() ); + glEnd(); + + // Arrowheads + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + glBegin( GL_TRIANGLES ); + glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] ); + glVertex2f( center.x(), center.y() + ( gridRadius * 1.10 ) ); + glVertex2f( center.x() + ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) ); + glVertex2f( center.x() - ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) ); + glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] ); + glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() ); + glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() + ( gridRadius * 0.06 ) ); + glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() - ( gridRadius * 0.06 ) ); + glEnd(); + + // Arc + glBegin( GL_LINE_STRIP ); + glColor3fv( rotating ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] ); + DrawCircularArc( center, 0.03f * PI, 0.47f * PI, gridRadius * 0.90 ); + glEnd(); + + // Boxes + glColor3fv( scalingY && !scalingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] ); + glBegin( GL_LINES ); + glVertex2f( center.x() + ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) ); + glVertex2f( center.x() - ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) ); + glEnd(); + glBegin( GL_LINE_LOOP ); + glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) ); + glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) ); + glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) ); + glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) ); + glEnd(); + + glColor3fv( scalingX && !scalingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] ); + glBegin( GL_LINES ); + glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 0.20 ) ); + glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() - ( gridRadius * 0.20 ) ); + glEnd(); + glBegin( GL_LINE_LOOP ); + glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 0.10 ) ); + glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() - ( gridRadius * 0.10 ) ); + glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() - ( gridRadius * 0.10 ) ); + glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 0.10 ) ); + glEnd(); + + glColor3fv( scalingX && scalingY ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] ); + glBegin( GL_LINE_STRIP ); + glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.10 ) ); + glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.50 ) ); + glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() + ( gridRadius * 1.50 ) ); + glEnd(); + glBegin( GL_LINE_LOOP ); + glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.40 ) ); + glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.20 ) ); + glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.20 ) ); + glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.40 ) ); + glEnd(); + + glPopMatrix(); +} + +void DrawControlPoints( void ){ + glColor3f( 1, 1, 1 ); + glBegin( GL_LINE_LOOP ); + + for ( int i = 0; i < numPts; i++ ) + glVertex2f( pts[i].x(), pts[i].y() ); + + glEnd(); +} + +// Note: Setup and all that jazz must be done by the caller! + +void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius ){ + float stepSize = ( 2.0f * PI ) / 200.0f; + + for ( float angle = startAngle; angle <= endAngle; angle += stepSize ) + glVertex2f( ctr.x() + radius * cos( angle ), ctr.y() + radius * sin( angle ) ); +} + + +void focus(){ + if ( numPts == 0 ) { + return; + } + + // Find selected texture's extents... + + extents.minX = extents.maxX = pts[0].x(), + extents.minY = extents.maxY = pts[0].y(); + + for ( int i = 1; i < numPts; i++ ) { - selFace = reinterpret_cast(g_ptrSelectedFaces.GetAt(0)); - bpt = &selFace->brushprimit_texdef; + if ( pts[i].x() < extents.minX ) { + extents.minX = pts[i].x(); + } + if ( pts[i].x() > extents.maxX ) { + extents.maxX = pts[i].x(); + } + if ( pts[i].y() < extents.minY ) { + extents.minY = pts[i].y(); + } + if ( pts[i].y() > extents.maxY ) { + extents.maxY = pts[i].y(); + } + } + + // Do some viewport fitting stuff... + +//globalOutputStream() << "--> Center: " << center.x() << ", " << center.y() << "\n"; +//globalOutputStream() << "--> Extents (stage 1): " << extents.minX << ", " +// << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n"; + // TTimo: Apply a ratio to get the area we'll draw. + center.x() = 0.5f * ( extents.minX + extents.maxX ), + center.y() = 0.5f * ( extents.minY + extents.maxY ); + extents.minX = center.x() + VP_PADDING * ( extents.minX - center.x() ), + extents.minY = center.y() + VP_PADDING * ( extents.minY - center.y() ), + extents.maxX = center.x() + VP_PADDING * ( extents.maxX - center.x() ), + extents.maxY = center.y() + VP_PADDING * ( extents.maxY - center.y() ); +//globalOutputStream() << "--> Extents (stage 2): " << extents.minX << ", " +// << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n"; + + // TTimo: We want a texture with the same X / Y ratio. + // TTimo: Compute XY space / window size ratio. + float SSize = extents.width(), TSize = extents.height(); + float ratioX = textureSize.x() * extents.width() / windowSize.x(), + ratioY = textureSize.y() * extents.height() / windowSize.y(); +//globalOutputStream() << "--> Texture size: " << textureSize.x() << ", " << textureSize.y() << "\n"; +//globalOutputStream() << "--> Window size: " << windowSize.x() << ", " << windowSize.y() << "\n"; + + if ( ratioX > ratioY ) { + TSize = ( windowSize.y() * ratioX ) / textureSize.y(); +// TSize = extents.width() * (windowSize.y() / windowSize.x()) * (textureSize.x() / textureSize.y()); } else { - bpt = &g_qeglobals.d_texturewin.brushprimit_texdef; + SSize = ( windowSize.x() * ratioY ) / textureSize.x(); +// SSize = extents.height() * (windowSize.x() / windowSize.y()) * (textureSize.y() / textureSize.x()); + } + + extents.minX = center.x() - 0.5f * SSize, extents.maxX = center.x() + 0.5f * SSize, + extents.minY = center.y() - 0.5f * TSize, extents.maxY = center.y() + 0.5f * TSize; +//globalOutputStream() << "--> Extents (stage 3): " << extents.minX << ", " +// << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n"; +} + +gboolean size_allocate( ui::Widget win, GtkAllocation * a, gpointer ){ + windowSize.x() = a->width; + windowSize.y() = a->height; + queueDraw(); + return false; +} + +gboolean expose( ui::Widget win, GdkEventExpose * e, gpointer ){ +// globalOutputStream() << "--> Textool Window was exposed!\n"; +// globalOutputStream() << " (window width/height: " << cc << "/" << e->area.height << ")\n"; + +// windowSize.x() = e->area.width, windowSize.y() = e->area.height; +//This needs to go elsewhere... +// InitTextool(); + + if ( glwidget_make_current( win ) == FALSE ) { + globalOutputStream() << " FAILED to make current! Oh, the agony! :-(\n"; + return true; + } + + CopyPointsFromSelectedFace(); + + if ( !lButtonDown ) { + focus(); } - // compute texture matrix - // the matrix returned must be understood as a qtexture_t with width=2 height=2 - FakeTexCoordsToTexMat( m_shift, m_rotate, m_scale, local_bp.coords ); - // copy the texture matrix in the global struct - // fit the qtexture if we have a face selected, otherwise g_qeglobals.d_texturewin.brushprimit_texdef uses the basic qtexture_t with width=2 height=2 - - ConvertTexMatWithQTexture( &local_bp, NULL, bpt, ( (selFace) ? selFace->d_texture : NULL ) ); - } - // we are gonna do stuff, if we own the last do we undo it first - if (m_nUndoId != 0) - { - // check the do we're about to undo is the one we pushed earlier - if (m_nUndoId == Undo_GetUndoId()) + + // Probably should init button/anchor states here as well... +// rotationAngle = 0.0f; + glClearColor( 0, 0, 0, 0 ); + glViewport( 0, 0, e->area.width, e->area.height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + +//??? + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_BLEND ); + + glOrtho( extents.minX, extents.maxX, extents.maxY, extents.minY, -1, 1 ); + + glColor3f( 1, 1, 1 ); + // draw the texture background + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + glBindTexture( GL_TEXTURE_2D, textureNum ); + + glEnable( GL_TEXTURE_2D ); + glBegin( GL_QUADS ); + glTexCoord2f( extents.minX, extents.minY ); + glVertex2f( extents.minX, extents.minY ); + glTexCoord2f( extents.maxX, extents.minY ); + glVertex2f( extents.maxX, extents.minY ); + glTexCoord2f( extents.maxX, extents.maxY ); + glVertex2f( extents.maxX, extents.maxY ); + glTexCoord2f( extents.minX, extents.maxY ); + glVertex2f( extents.minX, extents.maxY ); + glEnd(); + glDisable( GL_TEXTURE_2D ); + + // draw the texture-space grid + glColor3fv( widgetColor[COLOR_GREY] ); + glBegin( GL_LINES ); + + const int gridSubdivisions = 8; + const float gridExtents = 4.0f; + + for ( int i = 0; i < gridSubdivisions + 1; ++i ) { -#ifdef DBG_SI - Sys_Printf("GetTexMods calling Undo_Undo (silent)\n"); -#endif - g_bListenUpdate=false; - Undo_Undo(true); - g_bListenUpdate=true; + float y = i * ( gridExtents / float(gridSubdivisions) ); + float x = i * ( gridExtents / float(gridSubdivisions) ); + glVertex2f( 0, y ); + glVertex2f( gridExtents, y ); + glVertex2f( x, 0 ); + glVertex2f( x, gridExtents ); } - } - Select_SetTexture(pt,&local_bp); - m_nUndoId = Undo_GetUndoId(); + + glEnd(); + + DrawControlPoints(); + DrawControlWidgets(); +//??? + // reset the current texture +// glBindTexture(GL_TEXTURE_2D, 0); +// glFinish(); + glwidget_swap_buffers( win ); + + return false; } -void SurfaceDlg::FitAll() -{ - OnBtnFaceFit(NULL, NULL); - OnBtnPatchFit(NULL, NULL); +/*int FindSelectedPoint(int x, int y) + { + for(int i=0; i Textool button press...\n"; + + if ( e->button == 1 ) { + lButtonDown = true; + GlobalUndoSystem().start(); + + origBP = currentBP; + + //globalOutputStream() << "--> Original BP: [" << origBP.coords[0][0] << "][" << origBP.coords[0][1] << "][" << origBP.coords[0][2] << "]\n"; + //globalOutputStream() << " [" << origBP.coords[1][0] << "][" << origBP.coords[1][1] << "][" << origBP.coords[1][2] << "]\n"; + //float angle = atan2(origBP.coords[0][1], origBP.coords[0][0]) * 180.0f / 3.141592653589f; + origAngle = ( origBP.coords[0][1] > 0 ? PI : -PI ); // Could also be -PI... !!! FIX !!! [DONE] + + if ( origBP.coords[0][0] != 0.0f ) { + origAngle = atan( origBP.coords[0][1] / origBP.coords[0][0] ); + } + + origScaleX = origBP.coords[0][0] / cos( origAngle ); + origScaleY = origBP.coords[1][1] / cos( origAngle ); + rotationAngle = origAngle; + oldCenter[0] = oldCenter[1] = 0; + + //globalOutputStream() << "--> BP stats: ang=" << origAngle * RAD_TO_DEG << ", scale=" << origScaleX << "/" << origScaleY << "\n"; + //Should also set the Flip X/Y checkboxes here as well... !!! FIX !!! + //Also: should reverse texture left/right up/down instead of flipping the points... + +//disnowok +//float nx = windowSize.x() * (e->x - extents.minX) / (extents.maxX - extents.minX); +//float ny = windowSize.y() * (e->y - extents.minY) / (extents.maxY - extents.minY); +//disdoes... +//But I want it to scroll the texture window, not the points... !!! FIX !!! +//Actually, should scroll the texture window only when mouse is down on no widgets... + float nx = e->x / windowSize.x() * extents.width() + extents.minX; + float ny = e->y / windowSize.y() * extents.height() + extents.minY; + trans.x() = -tm.coords[0][0] * nx - tm.coords[0][1] * ny; + trans.y() = -tm.coords[1][0] * nx - tm.coords[1][1] * ny; + + dragPoint.x() = e->x, dragPoint.y() = e->y; + trans2.x() = nx, trans2.y() = ny; + oldRotationAngle = rotationAngle; +// oldTrans.x() = tm.coords[0][2] - nx * textureSize.x(); +// oldTrans.y() = tm.coords[1][2] - ny * textureSize.y(); + oldTrans.x() = tm.coords[0][2]; + oldTrans.y() = tm.coords[1][2]; + oldCenter.x() = center.x(); + oldCenter.y() = center.y(); + + queueDraw(); + + return true; + } +/* else if (e->button == 3) + { + rButtonDown = true; + }//*/ + +//globalOutputStream() << "(" << (haveAnchor ? "anchor" : "released") << ")\n"; + + return false; +} + +gboolean button_release( ui::Widget win, GdkEventButton * e, gpointer ){ +// globalOutputStream() << "--> Textool button release...\n"; + + if ( e->button == 1 ) { +/* float ptx = e->x / windowSize.x() * extents.width() + extents.minX; + float pty = e->y / windowSize.y() * extents.height() + extents.minY; + + //This prolly should go into the mouse move code... + //Doesn't work correctly anyway... + if (translatingX || translatingY) + center.x() = ptx, center.y() = pty;//*/ + + lButtonDown = false; + + if ( translatingX || translatingY ) { + GlobalUndoSystem().finish( "translateTexture" ); + } + else if ( rotating ) { + GlobalUndoSystem().finish( "rotateTexture" ); + } + else if ( scalingX || scalingY ) { + GlobalUndoSystem().finish( "scaleTexture" ); + } + else if ( resizingX || resizingY ) { + GlobalUndoSystem().finish( "resizeTexture" ); + } + else + { + GlobalUndoSystem().finish( "textoolUnknown" ); + } + + rotating = translatingX = translatingY = scalingX = scalingY + = resizingX = resizingY = false; + + queueDraw(); + } + else if ( e->button == 3 ) { + rButtonDown = false; + } + + return true; } + +/* + void C2DView::GridForWindow( float c[2], int x, int y) + { + SpaceForWindow( c, x, y ); + if ( !m_bDoGrid ) + return; + c[0] /= m_GridStep[0]; + c[1] /= m_GridStep[1]; + c[0] = (float)floor( c[0] + 0.5f ); + c[1] = (float)floor( c[1] + 0.5f ); + c[0] *= m_GridStep[0]; + c[1] *= m_GridStep[1]; + } + void C2DView::SpaceForWindow( float c[2], int x, int y) + { + c[0] = ((float)(x))/((float)(m_rect.right-m_rect.left))*(m_Maxs[0]-m_Mins[0])+m_Mins[0]; + c[1] = ((float)(y))/((float)(m_rect.bottom-m_rect.top))*(m_Maxs[1]-m_Mins[1])+m_Mins[1]; + } + */ +gboolean motion( ui::Widget win, GdkEventMotion * e, gpointer ){ +// globalOutputStream() << "--> Textool motion...\n"; + + if ( lButtonDown ) { + if ( translatingX || translatingY ) { + float ptx = e->x / windowSize.x() * extents.width() + extents.minX; + float pty = e->y / windowSize.y() * extents.height() + extents.minY; + +//Need to fix this to take the rotation angle into account, so that it moves along +//the rotated X/Y axis... + if ( translatingX ) { +// tm.coords[0][2] = (trans.x() + ptx) * textureSize.x(); +//This works, but only when the angle is zero. !!! FIX !!! [DONE] +// tm.coords[0][2] = oldCenter.x() + (ptx * textureSize.x()); + tm.coords[0][2] = oldTrans.x() + ( ptx - trans2.x() ) * textureSize.x(); +// center.x() = oldCenter.x() + (ptx - trans2.x()); + } + + if ( translatingY ) { +// tm.coords[1][2] = (trans.y() + pty) * textureSize.y(); +// tm.coords[1][2] = oldCenter.y() + (pty * textureSize.y()); + tm.coords[1][2] = oldTrans.y() + ( pty - trans2.y() ) * textureSize.y(); +// center.y() = oldCenter.y() + (pty - trans2.y()); + } + +//Need to update center.x/y() so that the widget translates as well. Also, oldCenter +//is badly named... Should be oldTrans or something like that... !!! FIX !!! +//Changing center.x/y() here doesn't seem to change anything... :-/ + UpdateControlPoints(); + } + else if ( rotating ) { + // Shamus: New rotate code + int cx = (int)( windowSize.x() * ( center.x() - extents.minX ) / extents.width() ); + int cy = (int)( windowSize.y() * ( center.y() - extents.minY ) / extents.height() ); + Vector3 v1( dragPoint.x() - cx, dragPoint.y() - cy, 0 ), v2( e->x - cx, e->y - cy, 0 ); + + vector3_normalise( v1 ); + vector3_normalise( v2 ); + float c = vector3_dot( v1, v2 ); + Vector3 cross = vector3_cross( v1, v2 ); + float s = vector3_length( cross ); + + if ( cross[2] > 0 ) { + s = -s; + } + +// Problem with this: arcsin/cos seems to only return -90 to 90 and 0 to 180... +// Can't derive angle from that! + +//rotationAngle = asin(s);// * 180.0f / 3.141592653589f; + rotationAngle = acos( c ); +//rotationAngle2 = asin(s); + if ( cross[2] < 0 ) { + rotationAngle = -rotationAngle; + } + +//NO! DOESN'T WORK! rotationAngle -= 45.0f * DEG_TO_RAD; +//Let's try this: +//No wok. +/*c = cos(rotationAngle - oldRotationAngle); + s = sin(rotationAngle - oldRotationAngle); + rotationAngle += oldRotationAngle; + //c += cos(oldRotationAngle); + //s += sin(oldRotationAngle); + //rotationAngle += oldRotationAngle; + //c %= 2.0 * PI; + //s %= 2.0 * PI; + //rotationAngle %= 2.0 * PI;//*/ + +//This is wrong... Hmm... +//It seems to shear the texture instead of rotating it... !!! FIX !!! +// Now it rotates correctly. Seems TTimo was overcomplicating things here... ;-) + +// Seems like what needs to happen here is multiplying these rotations by tm... !!! FIX !!! + +// See brush_primit.cpp line 244 (Texdef_EmitTextureCoordinates()) for where texcoords come from... + + tm.coords[0][0] = c; + tm.coords[0][1] = s; + tm.coords[1][0] = -s; + tm.coords[1][1] = c; +//It doesn't work anymore... Dunno why... +//tm.coords[0][2] = -trans.x(); // This works!!! Yeah!!! +//tm.coords[1][2] = -trans.y(); +//nope. +//tm.coords[0][2] = rotationPoint.x(); // This works, but strangely... +//tm.coords[1][2] = rotationPoint.y(); +//tm.coords[0][2] = 0;// center.x() / 2.0f; +//tm.coords[1][2] = 0;// center.y() / 2.0f; +//No. +//tm.coords[0][2] = -(center.x() * textureSize.x()); +//tm.coords[1][2] = -(center.y() * textureSize.y()); +//Eh? No, but seems to be getting closer... +/*float ptx = e->x / windowSize.x() * extents.width() + extents.minX; + float pty = e->y / windowSize.y() * extents.height() + extents.minY; + tm.coords[0][2] = -c * center.x() - s * center.y() + ptx; + tm.coords[1][2] = s * center.x() - c * center.x() + pty;//*/ +//Kinda works, but center drifts around on non-square textures... +/*tm.coords[0][2] = (-c * center.x() - s * center.y()) * textureSize.x(); + tm.coords[1][2] = ( s * center.x() - c * center.y()) * textureSize.y();//*/ +//Rotates correctly, but not around the actual center of the face's points... +/*tm.coords[0][2] = -c * center.x() * textureSize.x() - s * center.y() * textureSize.y(); + tm.coords[1][2] = s * center.x() * textureSize.x() - c * center.y() * textureSize.y();//*/ +//Yes!!! + tm.coords[0][2] = ( -c * center.x() * textureSize.x() - s * center.y() * textureSize.y() ) + center.x() * textureSize.x(); + tm.coords[1][2] = ( s * center.x() * textureSize.x() - c * center.y() * textureSize.y() ) + center.y() * textureSize.y(); //*/ +//This doesn't work... +//And this is the wrong place for this anyway (I'm pretty sure). +/*tm.coords[0][2] += oldCenter.x(); + tm.coords[1][2] += oldCenter.y();//*/ + UpdateControlPoints(); // will cause a redraw + } + + return true; + } + else // Check for widget mouseovers + { + Vector2 tran; + float nx = e->x / windowSize.x() * extents.width() + extents.minX; + float ny = e->y / windowSize.y() * extents.height() + extents.minY; + // Translate nx/y to the "center" point... + nx -= center.x(); + ny -= center.y(); + ny = -ny; // Flip Y-axis so that increasing numbers move up + + tran.x() = tm.coords[0][0] * nx + tm.coords[0][1] * ny; + tran.y() = tm.coords[1][0] * nx + tm.coords[1][1] * ny; +//This doesn't seem to generate a valid distance from the center--for some reason it +//calculates a fixed number every time +//Look at nx/y above: they're getting fixed there! !!! FIX !!! [DONE] + float dist = sqrt( ( nx * nx ) + ( ny * ny ) ); + // Normalize to the 2.0 = height standard (for now) +//globalOutputStream() << "--> Distance before: " << dist; + dist = dist * 2.0f / extents.height(); +//globalOutputStream() << ". After: " << dist; + tran.x() = tran.x() * 2.0f / extents.height(); + tran.y() = tran.y() * 2.0f / extents.height(); +//globalOutputStream() << ". Trans: " << tran.x() << ", " << tran.y() << "\n"; + +//Let's try this instead... +//Interesting! It seems that e->x/y are rotated +//(no, they're not--the TM above is what's doing it...) + nx = ( ( e->x / windowSize.y() ) * 2.0f ) - ( windowSize.x() / windowSize.y() ); + ny = ( ( e->y / windowSize.y() ) * 2.0f ) - ( windowSize.y() / windowSize.y() ); + ny = -ny; +//Cool! It works! Now just need to do rotation... + + rotating = translatingX = translatingY = scalingX = scalingY + = resizingX = resizingY = false; + + if ( dist < ( gridRadius * 0.16f ) ) { + translatingX = translatingY = true; + } + else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f ) + && fabs( ny ) < ( gridRadius * 0.05f ) && nx > 0 ) { + translatingX = true; + } + else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f ) + && fabs( nx ) < ( gridRadius * 0.05f ) && ny > 0 ) { + translatingY = true; + } + // Should tighten up the angle on this, or put this test after the axis tests... + else if ( tran.x() > 0 && tran.y() > 0 + && ( dist > ( gridRadius * 0.82f ) && dist < ( gridRadius * 0.98f ) ) ) { + rotating = true; + } + + queueDraw(); + + return true; + } + + return false; +} + +//It seems the fake tex coords conversion is screwing this stuff up... !!! FIX !!! +//This is still wrong... Prolly need to do something with the oldScaleX/Y stuff... +void flipX( ui::ToggleButton, gpointer ){ +// globalOutputStream() << "--> Flip X...\n"; + //Shamus: +// SurfaceInspector_GetSelectedBPTexdef(); // Refresh g_selectedBrushPrimitTexdef... +// tm.coords[0][0] = -tm.coords[0][0]; +// tm.coords[1][0] = -tm.coords[1][0]; +// tm.coords[0][0] = -tm.coords[0][0]; // This should be correct now...Nope. +// tm.coords[1][1] = -tm.coords[1][1]; + tm.coords[0][0] = -tm.coords[0][0]; // This should be correct now... + tm.coords[1][0] = -tm.coords[1][0]; +// tm.coords[2][0] = -tm.coords[2][0];//wil wok? no. + UpdateControlPoints(); +} + +void flipY( ui::ToggleButton, gpointer ){ +// globalOutputStream() << "--> Flip Y...\n"; +// tm.coords[0][1] = -tm.coords[0][1]; +// tm.coords[1][1] = -tm.coords[1][1]; +// tm.coords[0][1] = -tm.coords[0][1]; // This should be correct now...Nope. +// tm.coords[1][0] = -tm.coords[1][0]; + tm.coords[0][1] = -tm.coords[0][1]; // This should be correct now... + tm.coords[1][1] = -tm.coords[1][1]; +// tm.coords[2][1] = -tm.coords[2][1];//wil wok? no. + UpdateControlPoints(); +} + +} // end namespace TexTool + +#endif