2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 // Leonardo Zide (leo@lokigames.com)
28 #include "surfacedialog.h"
32 #include "debugging/debugging.h"
35 #include "iscenegraph.h"
38 #include "iselection.h"
40 #include <gdk/gdkkeysyms.h>
42 #include "signal/isignal.h"
43 #include "generic/object.h"
44 #include "math/vector.h"
45 #include "texturelib.h"
46 #include "shaderlib.h"
49 #include "gtkutil/idledraw.h"
50 #include "gtkutil/dialog.h"
51 #include "gtkutil/entry.h"
52 #include "gtkutil/nonmodal.h"
53 #include "gtkutil/pointer.h"
54 #include "gtkutil/glwidget.h" //Shamus: For Textool
55 #include "gtkutil/button.h"
58 #include "patchmanip.h"
59 #include "brushmanip.h"
60 #include "patchdialog.h"
61 #include "preferences.h"
62 #include "brush_primit.h"
64 #include "mainframe.h"
67 #include "brush.h" //Shamus: for Textool
70 #include "stream/stringstream.h"
72 #include "textureentry.h"
74 //NOTE: Proper functioning of Textool currently requires that the "#if 1" lines in
75 // brush_primit.h be changed to "#if 0". add/removeScale screws this up ATM. :-)
76 // Plus, Radiant seems to work just fine without that stuff. ;-)
78 #define TEXTOOL_ENABLED 0
85 //Shamus: Textool function prototypes
86 gboolean size_allocate( ui::Widget, GtkAllocation *, gpointer );
87 gboolean expose( ui::Widget, GdkEventExpose *, gpointer );
88 gboolean button_press( ui::Widget, GdkEventButton *, gpointer );
89 gboolean button_release( ui::Widget, GdkEventButton *, gpointer );
90 gboolean motion( ui::Widget, GdkEventMotion *, gpointer );
91 void flipX( ui::ToggleButton, gpointer );
92 void flipY( ui::ToggleButton, gpointer );
94 //End Textool function prototypes
96 //Shamus: Textool globals
97 ui::Widget g_textoolWin;
101 gtk_widget_queue_draw( g_textoolWin );
108 inline void spin_button_set_step(ui::SpinButton spin, gfloat step)
111 gtk_adjustment_set_step_increment(gtk_spin_button_get_adjustment(spin), step);
113 GValue gvalue = GValue_default();
114 g_value_init( &gvalue, G_TYPE_DOUBLE );
115 g_value_set_double( &gvalue, step );
116 g_object_set( G_OBJECT( gtk_spin_button_get_adjustment( spin ) ), "step-increment", &gvalue, NULL );
123 ui::SpinButton m_spin;
126 Increment(float &f) : m_f(f), m_spin(ui::null), m_entry(ui::null)
132 entry_set_float(m_entry, m_f);
135 typedef MemberCaller<Increment, void(), &Increment::cancel> CancelCaller;
139 m_f = static_cast<float>( entry_get_float(m_entry));
140 spin_button_set_step(m_spin, m_f);
143 typedef MemberCaller<Increment, void(), &Increment::apply> ApplyCaller;
146 void SurfaceInspector_GridChange();
148 class SurfaceInspector : public Dialog {
149 ui::Window BuildDialog();
151 NonModalEntry m_textureEntry;
152 NonModalSpinner m_hshiftSpinner;
153 NonModalEntry m_hshiftEntry;
154 NonModalSpinner m_vshiftSpinner;
155 NonModalEntry m_vshiftEntry;
156 NonModalSpinner m_hscaleSpinner;
157 NonModalEntry m_hscaleEntry;
158 NonModalSpinner m_vscaleSpinner;
159 NonModalEntry m_vscaleEntry;
160 NonModalSpinner m_rotateSpinner;
161 NonModalEntry m_rotateEntry;
165 GtkCheckButton *m_surfaceFlags[32];
166 GtkCheckButton *m_contentFlags[32];
168 NonModalEntry m_valueEntry;
169 ui::Entry m_valueEntryWidget{ui::null};
171 WindowPositionTracker m_positionTracker;
174 float m_fitHorizontal;
177 Increment m_hshiftIncrement;
178 Increment m_vshiftIncrement;
179 Increment m_hscaleIncrement;
180 Increment m_vscaleIncrement;
181 Increment m_rotateIncrement;
182 ui::Entry m_texture{ui::null};
185 m_textureEntry(ApplyShaderCaller(*this), UpdateCaller(*this)),
186 m_hshiftSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
187 m_hshiftEntry(Increment::ApplyCaller(m_hshiftIncrement), Increment::CancelCaller(m_hshiftIncrement)),
188 m_vshiftSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
189 m_vshiftEntry(Increment::ApplyCaller(m_vshiftIncrement), Increment::CancelCaller(m_vshiftIncrement)),
190 m_hscaleSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
191 m_hscaleEntry(Increment::ApplyCaller(m_hscaleIncrement), Increment::CancelCaller(m_hscaleIncrement)),
192 m_vscaleSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
193 m_vscaleEntry(Increment::ApplyCaller(m_vscaleIncrement), Increment::CancelCaller(m_vscaleIncrement)),
194 m_rotateSpinner(ApplyTexdefCaller(*this), UpdateCaller(*this)),
195 m_rotateEntry(Increment::ApplyCaller(m_rotateIncrement), Increment::CancelCaller(m_rotateIncrement)),
196 m_idleDraw(UpdateCaller(*this)),
197 m_valueEntry(ApplyFlagsCaller(*this), UpdateCaller(*this)),
198 m_hshiftIncrement(g_si_globals.shift[0]),
199 m_vshiftIncrement(g_si_globals.shift[1]),
200 m_hscaleIncrement(g_si_globals.scale[0]),
201 m_vscaleIncrement(g_si_globals.scale[1]),
202 m_rotateIncrement(g_si_globals.rotate)
206 m_positionTracker.setPosition(c_default_window_pos);
209 void constructWindow(ui::Window main_window)
211 m_parent = main_window;
213 AddGridChangeCallback(FreeCaller<void(), SurfaceInspector_GridChange>());
223 return GetWidget().visible();
229 m_idleDraw.queueDraw();
235 typedef MemberCaller<SurfaceInspector, void(), &SurfaceInspector::Update> UpdateCaller;
239 typedef MemberCaller<SurfaceInspector, void(), &SurfaceInspector::ApplyShader> ApplyShaderCaller;
243 typedef MemberCaller<SurfaceInspector, void(), &SurfaceInspector::ApplyTexdef> ApplyTexdefCaller;
247 typedef MemberCaller<SurfaceInspector, void(), &SurfaceInspector::ApplyFlags> ApplyFlagsCaller;
251 SurfaceInspector *g_SurfaceInspector;
253 inline SurfaceInspector &getSurfaceInspector()
255 ASSERT_NOTNULL(g_SurfaceInspector);
256 return *g_SurfaceInspector;
260 void SurfaceInspector_constructWindow(ui::Window main_window)
262 getSurfaceInspector().constructWindow(main_window);
265 void SurfaceInspector_destroyWindow()
267 getSurfaceInspector().destroyWindow();
270 void SurfaceInspector_queueDraw()
272 getSurfaceInspector().queueDraw();
276 CopiedString g_selectedShader;
277 TextureProjection g_selectedTexdef;
278 ContentsFlagsValue g_selectedFlags;
279 size_t g_selectedShaderSize[2];
282 void SurfaceInspector_SetSelectedShader(const char *shader)
284 g_selectedShader = shader;
285 SurfaceInspector_queueDraw();
288 void SurfaceInspector_SetSelectedTexdef(const TextureProjection &projection)
290 g_selectedTexdef = projection;
291 SurfaceInspector_queueDraw();
294 void SurfaceInspector_SetSelectedFlags(const ContentsFlagsValue &flags)
296 g_selectedFlags = flags;
297 SurfaceInspector_queueDraw();
300 static bool s_texture_selection_dirty = false;
302 void SurfaceInspector_updateSelection()
304 s_texture_selection_dirty = true;
305 SurfaceInspector_queueDraw();
308 if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
309 TexTool::queueDraw();
310 //globalOutputStream() << "textool texture changed..\n";
315 void SurfaceInspector_SelectionChanged(const Selectable &selectable)
317 SurfaceInspector_updateSelection();
320 void SurfaceInspector_SetCurrent_FromSelected()
322 if (s_texture_selection_dirty == true) {
323 s_texture_selection_dirty = false;
324 if (!g_SelectedFaceInstances.empty()) {
325 TextureProjection projection;
326 //This *may* be the point before it fucks up... Let's see!
327 //Yep, there was a call to removeScale in there...
328 Scene_BrushGetTexdef_Component_Selected(GlobalSceneGraph(), projection);
330 SurfaceInspector_SetSelectedTexdef(projection);
332 Scene_BrushGetShaderSize_Component_Selected(GlobalSceneGraph(), g_selectedShaderSize[0],
333 g_selectedShaderSize[1]);
334 g_selectedTexdef.m_brushprimit_texdef.coords[0][2] = float_mod(
335 g_selectedTexdef.m_brushprimit_texdef.coords[0][2], (float) g_selectedShaderSize[0]);
336 g_selectedTexdef.m_brushprimit_texdef.coords[1][2] = float_mod(
337 g_selectedTexdef.m_brushprimit_texdef.coords[1][2], (float) g_selectedShaderSize[1]);
340 Scene_BrushGetShader_Component_Selected(GlobalSceneGraph(), name);
341 if (string_not_empty(name.c_str())) {
342 SurfaceInspector_SetSelectedShader(name.c_str());
345 ContentsFlagsValue flags;
346 Scene_BrushGetFlags_Component_Selected(GlobalSceneGraph(), flags);
347 SurfaceInspector_SetSelectedFlags(flags);
349 TextureProjection projection;
350 Scene_BrushGetTexdef_Selected(GlobalSceneGraph(), projection);
351 SurfaceInspector_SetSelectedTexdef(projection);
354 Scene_BrushGetShader_Selected(GlobalSceneGraph(), name);
355 if (string_empty(name.c_str())) {
356 Scene_PatchGetShader_Selected(GlobalSceneGraph(), name);
358 if (string_not_empty(name.c_str())) {
359 SurfaceInspector_SetSelectedShader(name.c_str());
362 ContentsFlagsValue flags(0, 0, 0, false);
363 Scene_BrushGetFlags_Selected(GlobalSceneGraph(), flags);
364 SurfaceInspector_SetSelectedFlags(flags);
369 const char *SurfaceInspector_GetSelectedShader()
371 SurfaceInspector_SetCurrent_FromSelected();
372 return g_selectedShader.c_str();
375 const TextureProjection &SurfaceInspector_GetSelectedTexdef()
377 SurfaceInspector_SetCurrent_FromSelected();
378 return g_selectedTexdef;
381 const ContentsFlagsValue &SurfaceInspector_GetSelectedFlags()
383 SurfaceInspector_SetCurrent_FromSelected();
384 return g_selectedFlags;
389 ===================================================
393 ===================================================
396 si_globals_t g_si_globals;
399 // make the shift increments match the grid settings
400 // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size
401 // this depends on a scale value if you have selected a particular texture on which you want it to work:
402 // we move the textures in pixels, not world units. (i.e. increment values are in pixel)
403 // depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize()
404 // increment * scale = gridsize
405 // hscale and vscale are optional parameters, if they are zero they will be set to the default scale
406 // NOTE: the default scale depends if you are using BP mode or regular.
407 // For regular it's 0.5f (128 pixels cover 64 world units), for BP it's simply 1.0f
409 void DoSnapTToGrid(float hscale, float vscale)
411 g_si_globals.shift[0] = static_cast<float>( float_to_integer(static_cast<float>( GetGridSize()) / hscale));
412 g_si_globals.shift[1] = static_cast<float>( float_to_integer(static_cast<float>( GetGridSize()) / vscale));
413 getSurfaceInspector().queueDraw();
416 void SurfaceInspector_GridChange()
418 if (g_si_globals.m_bSnapTToGrid) {
419 DoSnapTToGrid(Texdef_getDefaultTextureScale(), Texdef_getDefaultTextureScale());
423 // make the shift increments match the grid settings
424 // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size
425 // this depends on the current texture scale used?
426 // we move the textures in pixels, not world units. (i.e. increment values are in pixel)
427 // depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize()
428 // increment * scale = gridsize
429 static void OnBtnMatchGrid(ui::Widget widget, gpointer data)
431 float hscale, vscale;
432 hscale = static_cast<float>( gtk_spin_button_get_value(getSurfaceInspector().m_hscaleIncrement.m_spin));
433 vscale = static_cast<float>( gtk_spin_button_get_value(getSurfaceInspector().m_vscaleIncrement.m_spin));
435 if (hscale == 0.0f || vscale == 0.0f) {
436 globalOutputStream() << "ERROR: unexpected scale == 0.0f\n";
440 DoSnapTToGrid(hscale, vscale);
443 // DoSurface will always try to show the surface inspector
444 // or update it because something new has been selected
445 // Shamus: It does get called when the SI is hidden, but not when you select something new. ;-)
448 if (!getSurfaceInspector().GetWidget()) {
449 getSurfaceInspector().Create();
452 getSurfaceInspector().Update();
453 getSurfaceInspector().importData();
454 getSurfaceInspector().ShowDlg();
457 void SurfaceInspector_toggleShown()
459 if (getSurfaceInspector().visible()) {
460 getSurfaceInspector().HideDlg();
466 void SurfaceInspector_FitTexture()
468 UndoableCommand undo("textureAutoFit");
469 Select_FitTexture(getSurfaceInspector().m_fitHorizontal, getSurfaceInspector().m_fitVertical);
472 static void OnBtnPatchdetails(ui::Widget widget, gpointer data)
477 static void OnBtnPatchnatural(ui::Widget widget, gpointer data)
479 Patch_NaturalTexture();
482 static void OnBtnPatchreset(ui::Widget widget, gpointer data)
484 Patch_ResetTexture();
487 static void OnBtnPatchFit(ui::Widget widget, gpointer data)
492 static void OnBtnAxial(ui::Widget widget, gpointer data)
494 //globalOutputStream() << "--> [OnBtnAxial]...\n";
495 UndoableCommand undo("textureDefault");
496 TextureProjection projection;
497 //globalOutputStream() << " TexDef_Construct_Default()...\n";
498 TexDef_Construct_Default(projection);
499 //globalOutputStream() << " Select_SetTexdef()...\n";
504 if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
505 // Scale up texture width/height if in BP mode...
506 //NOTE: This may not be correct any more! :-P
507 if ( !g_SelectedFaceInstances.empty() ) {
508 Face & face = g_SelectedFaceInstances.last().getFace();
509 float x = face.getShader().m_state->getTexture().width;
510 float y = face.getShader().m_state->getTexture().height;
511 projection.m_brushprimit_texdef.coords[0][0] /= x;
512 projection.m_brushprimit_texdef.coords[0][1] /= y;
513 projection.m_brushprimit_texdef.coords[1][0] /= x;
514 projection.m_brushprimit_texdef.coords[1][1] /= y;
519 Select_SetTexdef(projection);
522 static void OnBtnFaceFit(ui::Widget widget, gpointer data)
524 getSurfaceInspector().exportData();
525 SurfaceInspector_FitTexture();
528 typedef const char *FlagName;
530 const FlagName surfaceflagNamesDefault[32] = {
565 const FlagName contentflagNamesDefault[32] = {
600 const char *getSurfaceFlagName(std::size_t bit)
602 const char *value = g_pGameDescription->getKeyValue(surfaceflagNamesDefault[bit]);
603 if (string_empty(value)) {
604 return surfaceflagNamesDefault[bit];
609 const char *getContentFlagName(std::size_t bit)
611 const char *value = g_pGameDescription->getKeyValue(contentflagNamesDefault[bit]);
612 if (string_empty(value)) {
613 return contentflagNamesDefault[bit];
619 // =============================================================================
620 // SurfaceInspector class
622 guint togglebutton_connect_toggled(ui::ToggleButton button, const Callback<void()> &callback)
624 return g_signal_connect_swapped(G_OBJECT(button), "toggled", G_CALLBACK(callback.getThunk()),
625 callback.getEnvironment());
628 ui::Window SurfaceInspector::BuildDialog()
630 ui::Window window = ui::Window(create_floating_window("Surface Inspector", m_parent));
632 m_positionTracker.connect(window);
634 global_accel_connect_window(window);
636 window_connect_focus_in_clear_focus_widget(window);
640 // replaced by only the vbox:
641 auto vbox = ui::VBox(FALSE, 5);
644 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
647 auto hbox2 = ui::HBox(FALSE, 5);
649 vbox.pack_start(hbox2, FALSE, FALSE, 0);
652 ui::Widget label = ui::Label("Texture");
654 hbox2.pack_start(label, FALSE, TRUE, 0);
657 auto entry = ui::Entry(ui::New);
659 hbox2.pack_start(entry, TRUE, TRUE, 0);
661 m_textureEntry.connect(entry);
662 GlobalTextureEntryCompletion::instance().connect(entry);
668 auto table = ui::Table(6, 4, FALSE);
670 vbox.pack_start(table, FALSE, FALSE, 0);
671 gtk_table_set_row_spacings(table, 5);
672 gtk_table_set_col_spacings(table, 5);
674 ui::Widget label = ui::Label("Horizontal shift");
676 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
677 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
680 auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 2);
681 m_hshiftIncrement.m_spin = spin;
682 m_hshiftSpinner.connect(spin);
684 table.attach(spin, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
685 spin.dimensions(60, -1);
688 ui::Widget label = ui::Label("Step");
690 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
691 table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 0});
694 auto entry = ui::Entry(ui::New);
696 table.attach(entry, {3, 4, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
697 entry.dimensions(50, -1);
698 m_hshiftIncrement.m_entry = entry;
699 m_hshiftEntry.connect(entry);
702 ui::Widget label = ui::Label("Vertical shift");
704 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
705 table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
708 auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 2);
709 m_vshiftIncrement.m_spin = spin;
710 m_vshiftSpinner.connect(spin);
712 table.attach(spin, {1, 2, 1, 2}, {GTK_FILL, 0});
713 spin.dimensions(60, -1);
716 ui::Widget label = ui::Label("Step");
718 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
719 table.attach(label, {2, 3, 1, 2}, {GTK_FILL, 0});
722 auto entry = ui::Entry(ui::New);
724 table.attach(entry, {3, 4, 1, 2}, {GTK_FILL, 0});
725 entry.dimensions(50, -1);
726 m_vshiftIncrement.m_entry = entry;
727 m_vshiftEntry.connect(entry);
730 ui::Widget label = ui::Label("Horizontal stretch");
732 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
733 table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0});
736 auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 5);
737 m_hscaleIncrement.m_spin = spin;
738 m_hscaleSpinner.connect(spin);
740 table.attach(spin, {1, 2, 2, 3}, {GTK_FILL, 0});
741 spin.dimensions(60, -1);
744 ui::Widget label = ui::Label("Step");
746 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
747 table.attach(label, {2, 3, 2, 3}, {GTK_FILL, 0});
750 auto entry = ui::Entry(ui::New);
752 table.attach(entry, {3, 4, 2, 3}, {GTK_FILL, 0});
753 entry.dimensions(50, -1);
754 m_hscaleIncrement.m_entry = entry;
755 m_hscaleEntry.connect(entry);
758 ui::Widget label = ui::Label("Vertical stretch");
760 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
761 table.attach(label, {0, 1, 3, 4}, {GTK_FILL, 0});
764 auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 5);
765 m_vscaleIncrement.m_spin = spin;
766 m_vscaleSpinner.connect(spin);
768 table.attach(spin, {1, 2, 3, 4}, {GTK_FILL, 0});
769 spin.dimensions(60, -1);
772 ui::Widget label = ui::Label("Step");
774 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
775 table.attach(label, {2, 3, 3, 4}, {GTK_FILL, 0});
778 auto entry = ui::Entry(ui::New);
780 table.attach(entry, {3, 4, 3, 4}, {GTK_FILL, 0});
781 entry.dimensions(50, -1);
782 m_vscaleIncrement.m_entry = entry;
783 m_vscaleEntry.connect(entry);
786 ui::Widget label = ui::Label("Rotate");
788 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
789 table.attach(label, {0, 1, 4, 5}, {GTK_FILL, 0});
792 auto spin = ui::SpinButton(ui::Adjustment(0, -8192, 8192, 2, 8, 0), 0, 2);
793 m_rotateIncrement.m_spin = spin;
794 m_rotateSpinner.connect(spin);
796 table.attach(spin, {1, 2, 4, 5}, {GTK_FILL, 0});
797 spin.dimensions(60, -1);
798 gtk_spin_button_set_wrap(spin, TRUE);
801 ui::Widget label = ui::Label("Step");
803 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
804 table.attach(label, {2, 3, 4, 5}, {GTK_FILL, 0});
807 auto entry = ui::Entry(ui::New);
809 table.attach(entry, {3, 4, 4, 5}, {GTK_FILL, 0});
810 entry.dimensions(50, -1);
811 m_rotateIncrement.m_entry = entry;
812 m_rotateEntry.connect(entry);
816 ui::Widget button = ui::Button("Match Grid");
818 table.attach(button, {2, 4, 5, 6}, {GTK_EXPAND | GTK_FILL, 0});
819 button.connect("clicked", G_CALLBACK(OnBtnMatchGrid), 0);
824 auto frame = ui::Frame("Texturing");
826 vbox.pack_start(frame, FALSE, FALSE, 0);
828 auto table = ui::Table(4, 4, FALSE);
831 gtk_table_set_row_spacings(table, 5);
832 gtk_table_set_col_spacings(table, 5);
833 gtk_container_set_border_width(GTK_CONTAINER(table), 5);
835 ui::Widget label = ui::Label("Brush");
837 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
840 ui::Widget label = ui::Label("Patch");
842 table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0});
845 ui::Widget label = ui::Label("Width");
847 table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 0});
850 ui::Widget label = ui::Label("Height");
852 table.attach(label, {3, 4, 0, 1}, {GTK_FILL, 0});
855 ui::Widget button = ui::Button("Axial");
857 table.attach(button, {0, 1, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
858 button.connect("clicked",
859 G_CALLBACK(OnBtnAxial), 0);
860 button.dimensions(60, -1);
863 ui::Widget button = ui::Button("Fit");
865 table.attach(button, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
866 button.connect("clicked",
867 G_CALLBACK(OnBtnFaceFit), 0);
868 button.dimensions(60, -1);
871 ui::Widget button = ui::Button("CAP");
873 table.attach(button, {0, 1, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
874 button.connect("clicked",
875 G_CALLBACK(OnBtnPatchdetails), 0);
876 button.dimensions(60, -1);
879 ui::Widget button = ui::Button("Set...");
881 table.attach(button, {1, 2, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
882 button.connect("clicked",
883 G_CALLBACK(OnBtnPatchreset), 0);
884 button.dimensions(60, -1);
887 ui::Widget button = ui::Button("Natural");
889 table.attach(button, {2, 3, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
890 button.connect("clicked",
891 G_CALLBACK(OnBtnPatchnatural), 0);
892 button.dimensions(60, -1);
895 ui::Widget button = ui::Button("Fit");
897 table.attach(button, {3, 4, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
898 button.connect("clicked",
899 G_CALLBACK(OnBtnPatchFit), 0);
900 button.dimensions(60, -1);
903 auto spin = ui::SpinButton(ui::Adjustment(1, 0, 1 << 16, 1, 10, 0), 0, 6);
905 table.attach(spin, {2, 3, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
906 spin.dimensions(60, -1);
907 AddDialogData(spin, m_fitHorizontal);
910 auto spin = ui::SpinButton(ui::Adjustment(1, 0, 1 << 16, 1, 10, 0), 0, 6);
912 table.attach(spin, {3, 4, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
913 spin.dimensions(60, -1);
914 AddDialogData(spin, m_fitVertical);
918 if (!string_empty(g_pGameDescription->getKeyValue("si_flags"))) {
920 auto frame = ui::Frame("Surface Flags");
922 vbox.pack_start(frame, TRUE, TRUE, 0);
924 auto vbox3 = ui::VBox(FALSE, 4);
925 //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
929 auto table = ui::Table(8, 4, FALSE);
931 vbox3.pack_start(table, TRUE, TRUE, 0);
932 gtk_table_set_row_spacings(table, 0);
933 gtk_table_set_col_spacings(table, 0);
935 GtkCheckButton **p = m_surfaceFlags;
937 for (unsigned int c = 0; c != 4; ++c) {
938 for (unsigned int r = 0; r != 8; ++r) {
939 auto check = ui::CheckButton(getSurfaceFlagName(c * 8 + r));
941 table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0});
943 guint handler_id = togglebutton_connect_toggled(check, ApplyFlagsCaller(*this));
944 g_object_set_data(G_OBJECT(check), "handler", gint_to_pointer(handler_id));
951 auto frame = ui::Frame("Content Flags");
953 vbox.pack_start(frame, TRUE, TRUE, 0);
955 auto vbox3 = ui::VBox(FALSE, 4);
956 //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
961 auto table = ui::Table(8, 4, FALSE);
963 vbox3.pack_start(table, TRUE, TRUE, 0);
964 gtk_table_set_row_spacings(table, 0);
965 gtk_table_set_col_spacings(table, 0);
967 GtkCheckButton **p = m_contentFlags;
969 for (unsigned int c = 0; c != 4; ++c) {
970 for (unsigned int r = 0; r != 8; ++r) {
971 auto check = ui::CheckButton(getContentFlagName(c * 8 + r));
973 table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0});
975 guint handler_id = togglebutton_connect_toggled(check, ApplyFlagsCaller(*this));
976 g_object_set_data(G_OBJECT(check), "handler", gint_to_pointer(handler_id));
980 // not allowed to modify detail flag using Surface Inspector
981 gtk_widget_set_sensitive(ui::CheckButton::from(m_contentFlags[BRUSH_DETAIL_FLAG]), FALSE);
986 auto frame = ui::Frame("Value");
988 vbox.pack_start(frame, TRUE, TRUE, 0);
990 auto vbox3 = ui::VBox(FALSE, 4);
991 gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
996 auto entry = ui::Entry(ui::New);
998 vbox3.pack_start(entry, TRUE, TRUE, 0);
999 m_valueEntryWidget = entry;
1000 m_valueEntry.connect(entry);
1007 if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
1008 // Shamus: Textool goodies...
1009 ui::Widget frame = ui::Frame( "Textool" );
1011 vbox.pack_start( frame , FALSE, FALSE, 0 );
1013 //Prolly should make this a member or global var, so the SI can draw on it...
1014 TexTool::g_textoolWin = glwidget_new( FALSE );
1015 // --> Dunno, but this stuff may be necessary... (Looks like it!)
1016 g_object_ref( TexTool::g_textoolWin );
1017 gtk_widget_set_events( TexTool::g_textoolWin, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK );
1018 gtk_widget_set_can_focus( TexTool::g_textoolWin, true );
1020 TexTool::g_textoolWin.show();
1021 TexTool::g_textoolWin.dimensions( -1, 240 ); //Yeah!
1022 frame.add(TexTool::g_textoolWin);
1024 TexTool::g_textoolWin.connect( "size_allocate", G_CALLBACK( TexTool::size_allocate ), NULL );
1025 TexTool::g_textoolWin.connect( "expose_event", G_CALLBACK( TexTool::expose ), NULL );
1026 TexTool::g_textoolWin.connect( "button_press_event", G_CALLBACK( TexTool::button_press ), NULL );
1027 TexTool::g_textoolWin.connect( "button_release_event", G_CALLBACK( TexTool::button_release ), NULL );
1028 TexTool::g_textoolWin.connect( "motion_notify_event", G_CALLBACK( TexTool::motion ), NULL );
1031 ui::Widget hbox = ui::HBox( FALSE, 5 );
1033 vbox.pack_start( hbox , FALSE, FALSE, 0 );
1034 // Checkboxes go here... (Flip X/Y)
1035 ui::Widget flipX = ui::CheckButton( "Flip X axis" );
1036 ui::Widget flipY = ui::CheckButton( "Flip Y axis" );
1039 hbox.pack_start( flipX, FALSE, FALSE, 0 );
1040 hbox.pack_start( flipY, FALSE, FALSE, 0 );
1042 //Instead of this, we probably need to create a vbox to put into the frame, then the
1043 //window, then the hbox. !!! FIX !!!
1046 //Hmm. Do we really need g_object_set_data? Mebbe not... And we don't! :-)
1047 // g_object_set_data(G_OBJECT(flipX), "handler", gint_to_pointer(flipX.connect("toggled", G_CALLBACK(TexTool::flipX), 0)));
1048 // g_object_set_data(G_OBJECT(flipY), "handler", gint_to_pointer(flipY.connect("toggled", G_CALLBACK(TexTool::flipY), 0)));
1050 flipX.connect( "toggled", G_CALLBACK( TexTool::flipX ), NULL );
1051 flipY.connect( "toggled", G_CALLBACK( TexTool::flipY ), NULL );
1064 Set the fields to the current texdef (i.e. map/texdef -> dialog widgets)
1065 if faces selected (instead of brushes) -> will read this face texdef, else current texdef
1066 if only patches selected, will read the patch texdef
1070 void spin_button_set_value_no_signal(ui::SpinButton spin, gdouble value)
1072 guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(spin), "handler"));
1073 g_signal_handler_block(G_OBJECT(gtk_spin_button_get_adjustment(spin)), handler_id);
1074 gtk_spin_button_set_value(spin, value);
1075 g_signal_handler_unblock(G_OBJECT(gtk_spin_button_get_adjustment(spin)), handler_id);
1078 void spin_button_set_step_increment(ui::SpinButton spin, gdouble value)
1080 auto adjust = gtk_spin_button_get_adjustment(spin);
1081 gtk_adjustment_set_step_increment(adjust, value);
1084 void SurfaceInspector::Update()
1086 const char *name = SurfaceInspector_GetSelectedShader();
1088 if (shader_is_texture(name)) {
1089 m_texture.text(shader_get_textureName(name));
1094 texdef_t shiftScaleRotate;
1095 //Shamus: This is where we get into trouble--the BP code tries to convert to a "faked"
1096 //shift, rotate & scale values from the brush face, which seems to screw up for some reason.
1098 /*globalOutputStream() << "--> SI::Update. About to do ShiftScaleRotate_fromFace()...\n";
1099 SurfaceInspector_GetSelectedBPTexdef();
1100 globalOutputStream() << "BP: (" << g_selectedBrushPrimitTexdef.coords[0][0] << ", " << g_selectedBrushPrimitTexdef.coords[0][1] << ")("
1101 << g_selectedBrushPrimitTexdef.coords[1][0] << ", " << g_selectedBrushPrimitTexdef.coords[1][1] << ")("
1102 << g_selectedBrushPrimitTexdef.coords[0][2] << ", " << g_selectedBrushPrimitTexdef.coords[1][2] << ") SurfaceInspector::Update\n";//*/
1103 //Ok, it's screwed up *before* we get here...
1104 ShiftScaleRotate_fromFace(shiftScaleRotate, SurfaceInspector_GetSelectedTexdef());
1106 // normalize again to hide the ridiculously high scale values that get created when using texlock
1107 shiftScaleRotate.shift[0] = float_mod(shiftScaleRotate.shift[0], (float) g_selectedShaderSize[0]);
1108 shiftScaleRotate.shift[1] = float_mod(shiftScaleRotate.shift[1], (float) g_selectedShaderSize[1]);
1111 spin_button_set_value_no_signal(m_hshiftIncrement.m_spin, shiftScaleRotate.shift[0]);
1112 spin_button_set_step_increment(m_hshiftIncrement.m_spin, g_si_globals.shift[0]);
1113 entry_set_float(m_hshiftIncrement.m_entry, g_si_globals.shift[0]);
1117 spin_button_set_value_no_signal(m_vshiftIncrement.m_spin, shiftScaleRotate.shift[1]);
1118 spin_button_set_step_increment(m_vshiftIncrement.m_spin, g_si_globals.shift[1]);
1119 entry_set_float(m_vshiftIncrement.m_entry, g_si_globals.shift[1]);
1123 spin_button_set_value_no_signal(m_hscaleIncrement.m_spin, shiftScaleRotate.scale[0]);
1124 spin_button_set_step_increment(m_hscaleIncrement.m_spin, g_si_globals.scale[0]);
1125 entry_set_float(m_hscaleIncrement.m_entry, g_si_globals.scale[0]);
1129 spin_button_set_value_no_signal(m_vscaleIncrement.m_spin, shiftScaleRotate.scale[1]);
1130 spin_button_set_step_increment(m_vscaleIncrement.m_spin, g_si_globals.scale[1]);
1131 entry_set_float(m_vscaleIncrement.m_entry, g_si_globals.scale[1]);
1135 spin_button_set_value_no_signal(m_rotateIncrement.m_spin, shiftScaleRotate.rotate);
1136 spin_button_set_step_increment(m_rotateIncrement.m_spin, g_si_globals.rotate);
1137 entry_set_float(m_rotateIncrement.m_entry, g_si_globals.rotate);
1140 if (!string_empty(g_pGameDescription->getKeyValue("si_flags"))) {
1141 ContentsFlagsValue flags(SurfaceInspector_GetSelectedFlags());
1143 entry_set_int(m_valueEntryWidget, flags.m_value);
1145 for (GtkCheckButton **p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p) {
1146 toggle_button_set_active_no_signal(ui::CheckButton::from(*p),
1147 flags.m_surfaceFlags & (1 << (p - m_surfaceFlags)));
1150 for (GtkCheckButton **p = m_contentFlags; p != m_contentFlags + 32; ++p) {
1151 toggle_button_set_active_no_signal(ui::CheckButton::from(*p),
1152 flags.m_contentFlags & (1 << (p - m_contentFlags)));
1161 Reads the fields to get the current texdef (i.e. widgets -> MAP)
1162 in brush primitive mode, grab the fake shift scale rot and compute a new texture matrix
1165 void SurfaceInspector::ApplyShader()
1167 StringOutputStream name(256);
1168 name << GlobalTexturePrefix_get() << gtk_entry_get_text(m_texture);
1170 // TTimo: detect and refuse invalid texture names (at least the ones with spaces)
1171 if (!texdef_name_valid(name.c_str())) {
1172 globalErrorStream() << "invalid texture name '" << name.c_str() << "'\n";
1173 SurfaceInspector_queueDraw();
1177 UndoableCommand undo("textureNameSetSelected");
1178 Select_SetShader(name.c_str());
1181 void SurfaceInspector::ApplyTexdef()
1183 texdef_t shiftScaleRotate;
1185 shiftScaleRotate.shift[0] = static_cast<float>( gtk_spin_button_get_value(m_hshiftIncrement.m_spin));
1186 shiftScaleRotate.shift[1] = static_cast<float>( gtk_spin_button_get_value(m_vshiftIncrement.m_spin));
1187 shiftScaleRotate.scale[0] = static_cast<float>( gtk_spin_button_get_value(m_hscaleIncrement.m_spin));
1188 shiftScaleRotate.scale[1] = static_cast<float>( gtk_spin_button_get_value(m_vscaleIncrement.m_spin));
1189 shiftScaleRotate.rotate = static_cast<float>( gtk_spin_button_get_value(m_rotateIncrement.m_spin));
1191 TextureProjection projection;
1192 //Shamus: This is the other place that screws up, it seems, since it doesn't seem to do the
1193 //conversion from the face (I think) and so bogus values end up in the thing... !!! FIX !!!
1194 //This is actually OK. :-P
1195 ShiftScaleRotate_toFace(shiftScaleRotate, projection);
1197 UndoableCommand undo("textureProjectionSetSelected");
1198 Select_SetTexdef(projection);
1201 void SurfaceInspector::ApplyFlags()
1203 unsigned int surfaceflags = 0;
1204 for (GtkCheckButton **p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p) {
1205 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*p))) {
1206 surfaceflags |= (1 << (p - m_surfaceFlags));
1210 unsigned int contentflags = 0;
1211 for (GtkCheckButton **p = m_contentFlags; p != m_contentFlags + 32; ++p) {
1212 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*p))) {
1213 contentflags |= (1 << (p - m_contentFlags));
1217 int value = entry_get_int(m_valueEntryWidget);
1219 UndoableCommand undo("flagsSetSelected");
1220 Select_SetFlags(ContentsFlagsValue(surfaceflags, contentflags, value, true));
1224 void Face_getTexture(Face &face, CopiedString &shader, TextureProjection &projection, ContentsFlagsValue &flags)
1226 shader = face.GetShader();
1227 face.GetTexdef(projection);
1228 flags = face.getShader().m_flags;
1231 typedef Function<void(Face &, CopiedString &, TextureProjection &,
1232 ContentsFlagsValue &), Face_getTexture> FaceGetTexture;
1235 Face_setTexture(Face &face, const char *shader, const TextureProjection &projection, const ContentsFlagsValue &flags)
1237 face.SetShader(shader);
1238 face.SetTexdef(projection);
1239 face.SetFlags(flags);
1242 typedef Function<void(Face &, const char *, const TextureProjection &,
1243 const ContentsFlagsValue &), Face_setTexture> FaceSetTexture;
1246 void Patch_getTexture(Patch &patch, CopiedString &shader, TextureProjection &projection, ContentsFlagsValue &flags)
1248 shader = patch.GetShader();
1249 projection = TextureProjection(texdef_t(), brushprimit_texdef_t(), Vector3(0, 0, 0), Vector3(0, 0, 0));
1250 flags = ContentsFlagsValue(0, 0, 0, false);
1253 typedef Function<void(Patch &, CopiedString &, TextureProjection &,
1254 ContentsFlagsValue &), Patch_getTexture> PatchGetTexture;
1257 Patch_setTexture(Patch &patch, const char *shader, const TextureProjection &projection, const ContentsFlagsValue &flags)
1259 patch.SetShader(shader);
1262 typedef Function<void(Patch &, const char *, const TextureProjection &,
1263 const ContentsFlagsValue &), Patch_setTexture> PatchSetTexture;
1266 typedef Callback<void(CopiedString &, TextureProjection &, ContentsFlagsValue &)> GetTextureCallback;
1267 typedef Callback<void(const char *, const TextureProjection &, const ContentsFlagsValue &)> SetTextureCallback;
1270 GetTextureCallback getTexture;
1271 SetTextureCallback setTexture;
1275 void Face_getClosest(Face &face, SelectionTest &test, SelectionIntersection &bestIntersection, Texturable &texturable)
1277 SelectionIntersection intersection;
1278 face.testSelect(test, intersection);
1279 if (intersection.valid()
1280 && SelectionIntersection_closer(intersection, bestIntersection)) {
1281 bestIntersection = intersection;
1282 texturable.setTexture = makeCallback(FaceSetTexture(), face);
1283 texturable.getTexture = makeCallback(FaceGetTexture(), face);
1288 class OccludeSelector : public Selector {
1289 SelectionIntersection &m_bestIntersection;
1292 OccludeSelector(SelectionIntersection &bestIntersection, bool &occluded) : m_bestIntersection(bestIntersection),
1293 m_occluded(occluded)
1298 void pushSelectable(Selectable &selectable)
1302 void popSelectable()
1306 void addIntersection(const SelectionIntersection &intersection)
1308 if (SelectionIntersection_closer(intersection, m_bestIntersection)) {
1309 m_bestIntersection = intersection;
1315 class BrushGetClosestFaceVisibleWalker : public scene::Graph::Walker {
1316 SelectionTest &m_test;
1317 Texturable &m_texturable;
1318 mutable SelectionIntersection m_bestIntersection;
1320 BrushGetClosestFaceVisibleWalker(SelectionTest &test, Texturable &texturable) : m_test(test),
1321 m_texturable(texturable)
1325 bool pre(const scene::Path &path, scene::Instance &instance) const
1327 if (path.top().get().visible()) {
1328 BrushInstance *brush = Instance_getBrush(instance);
1330 m_test.BeginMesh(brush->localToWorld());
1332 for (Brush::const_iterator i = brush->getBrush().begin(); i != brush->getBrush().end(); ++i) {
1333 Face_getClosest(*(*i), m_test, m_bestIntersection, m_texturable);
1336 SelectionTestable *selectionTestable = Instance_getSelectionTestable(instance);
1337 if (selectionTestable) {
1339 OccludeSelector selector(m_bestIntersection, occluded);
1340 selectionTestable->testSelect(selector, m_test);
1342 Patch *patch = Node_getPatch(path.top());
1344 m_texturable.setTexture = makeCallback(PatchSetTexture(), *patch);
1345 m_texturable.getTexture = makeCallback(PatchGetTexture(), *patch);
1347 m_texturable = Texturable();
1357 Texturable Scene_getClosestTexturable(scene::Graph &graph, SelectionTest &test)
1359 Texturable texturable;
1360 graph.traverse(BrushGetClosestFaceVisibleWalker(test, texturable));
1365 Scene_getClosestTexture(scene::Graph &graph, SelectionTest &test, CopiedString &shader, TextureProjection &projection,
1366 ContentsFlagsValue &flags)
1368 Texturable texturable = Scene_getClosestTexturable(graph, test);
1369 if (texturable.getTexture != GetTextureCallback()) {
1370 texturable.getTexture(shader, projection, flags);
1376 void Scene_setClosestTexture(scene::Graph &graph, SelectionTest &test, const char *shader,
1377 const TextureProjection &projection, const ContentsFlagsValue &flags)
1379 Texturable texturable = Scene_getClosestTexturable(graph, test);
1380 if (texturable.setTexture != SetTextureCallback()) {
1381 texturable.setTexture(shader, projection, flags);
1388 TextureProjection m_projection;
1389 ContentsFlagsValue m_flags;
1392 FaceTexture g_faceTextureClipboard;
1394 void FaceTextureClipboard_setDefault()
1396 g_faceTextureClipboard.m_flags = ContentsFlagsValue(0, 0, 0, false);
1397 TexDef_Construct_Default(g_faceTextureClipboard.m_projection);
1400 void TextureClipboard_textureSelected(const char *shader)
1402 FaceTextureClipboard_setDefault();
1405 class TextureBrowser;
1407 extern TextureBrowser g_TextureBrowser;
1409 void TextureBrowser_SetSelectedShader(TextureBrowser &textureBrowser, const char *shader);
1411 const char *TextureBrowser_GetSelectedShader(TextureBrowser &textureBrowser);
1413 void Scene_copyClosestTexture(SelectionTest &test)
1415 CopiedString shader;
1416 if (Scene_getClosestTexture(GlobalSceneGraph(), test, shader, g_faceTextureClipboard.m_projection,
1417 g_faceTextureClipboard.m_flags)) {
1418 TextureBrowser_SetSelectedShader(g_TextureBrowser, shader.c_str());
1422 void Scene_applyClosestTexture(SelectionTest &test)
1424 UndoableCommand command("facePaintTexture");
1426 Scene_setClosestTexture(GlobalSceneGraph(), test, TextureBrowser_GetSelectedShader(g_TextureBrowser),
1427 g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags);
1429 SceneChangeNotify();
1433 void SelectedFaces_copyTexture()
1435 if (!g_SelectedFaceInstances.empty()) {
1436 Face &face = g_SelectedFaceInstances.last().getFace();
1437 face.GetTexdef(g_faceTextureClipboard.m_projection);
1438 g_faceTextureClipboard.m_flags = face.getShader().m_flags;
1440 TextureBrowser_SetSelectedShader(g_TextureBrowser, face.getShader().getShader());
1444 void FaceInstance_pasteTexture(FaceInstance &faceInstance)
1446 faceInstance.getFace().SetTexdef(g_faceTextureClipboard.m_projection);
1447 faceInstance.getFace().SetShader(TextureBrowser_GetSelectedShader(g_TextureBrowser));
1448 faceInstance.getFace().SetFlags(g_faceTextureClipboard.m_flags);
1449 SceneChangeNotify();
1452 bool SelectedFaces_empty()
1454 return g_SelectedFaceInstances.empty();
1457 void SelectedFaces_pasteTexture()
1459 UndoableCommand command("facePasteTexture");
1460 g_SelectedFaceInstances.foreach(FaceInstance_pasteTexture);
1464 void SurfaceInspector_constructPreferences(PreferencesPage &page)
1466 page.appendCheckBox("", "Surface Inspector Increments Match Grid", g_si_globals.m_bSnapTToGrid);
1469 void SurfaceInspector_constructPage(PreferenceGroup &group)
1471 PreferencesPage page(group.createPage("Surface Inspector", "Surface Inspector Preferences"));
1472 SurfaceInspector_constructPreferences(page);
1475 void SurfaceInspector_registerPreferencesPage()
1477 PreferencesDialog_addSettingsPage(makeCallbackF(SurfaceInspector_constructPage));
1480 void SurfaceInspector_registerCommands()
1482 GlobalCommands_insert("FitTexture", makeCallbackF(SurfaceInspector_FitTexture),
1483 Accelerator('B', (GdkModifierType) GDK_SHIFT_MASK));
1484 GlobalCommands_insert("SurfaceInspector", makeCallbackF(SurfaceInspector_toggleShown), Accelerator('S'));
1486 GlobalCommands_insert("FaceCopyTexture", makeCallbackF(SelectedFaces_copyTexture));
1487 GlobalCommands_insert("FacePasteTexture", makeCallbackF(SelectedFaces_pasteTexture));
1491 #include "preferencesystem.h"
1494 void SurfaceInspector_Construct()
1496 g_SurfaceInspector = new SurfaceInspector;
1498 SurfaceInspector_registerCommands();
1500 FaceTextureClipboard_setDefault();
1502 GlobalPreferenceSystem().registerPreference("SurfaceWnd", make_property<WindowPositionTracker_String>(
1503 getSurfaceInspector().m_positionTracker));
1504 GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale1", make_property_string(g_si_globals.scale[0]));
1505 GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Scale2", make_property_string(g_si_globals.scale[1]));
1506 GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift1", make_property_string(g_si_globals.shift[0]));
1507 GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Shift2", make_property_string(g_si_globals.shift[1]));
1508 GlobalPreferenceSystem().registerPreference("SI_SurfaceTexdef_Rotate", make_property_string(g_si_globals.rotate));
1509 GlobalPreferenceSystem().registerPreference("SnapTToGrid", make_property_string(g_si_globals.m_bSnapTToGrid));
1511 typedef FreeCaller<void(
1512 const Selectable &), SurfaceInspector_SelectionChanged> SurfaceInspectorSelectionChangedCaller;
1513 GlobalSelectionSystem().addSelectionChangeCallback(SurfaceInspectorSelectionChangedCaller());
1514 typedef FreeCaller<void(), SurfaceInspector_updateSelection> SurfaceInspectorUpdateSelectionCaller;
1515 Brush_addTextureChangedCallback(SurfaceInspectorUpdateSelectionCaller());
1516 Patch_addTextureChangedCallback(SurfaceInspectorUpdateSelectionCaller());
1518 SurfaceInspector_registerPreferencesPage();
1521 void SurfaceInspector_Destroy()
1523 delete g_SurfaceInspector;
1529 namespace TexTool { // namespace hides these symbols from other object-files
1531 //Shamus: Textool functions, including GTK+ callbacks
1534 //NOTE: Black screen when TT first comes up is caused by an uninitialized Extent... !!! FIX !!!
1535 // But... You can see down below that it *is* initialized! WTF?
1538 float minX, minY, maxX, maxY;
1539 float width( void ) { return fabs( maxX - minX ); }
1540 float height( void ) { return fabs( maxY - minY ); }
1543 //This seems to control the texture scale... (Yep! ;-)
1544 Extent extents = { -2.0f, -2.0f, +2.0f, +2.0f };
1545 brushprimit_texdef_t tm; // Texture transform matrix
1546 Vector2 pts[c_brush_maxFaces];
1550 Vector2 textureSize;
1552 #define VP_PADDING 1.2
1553 #define PI 3.14159265358979
1554 bool lButtonDown = false;
1555 bool rButtonDown = false;
1558 bool haveAnchor = false;
1559 brushprimit_texdef_t currentBP;
1560 brushprimit_texdef_t origBP; // Original brush primitive (before we muck it up)
1561 float controlRadius = 5.0f;
1562 float rotationAngle = 0.0f;
1563 float rotationAngle2 = 0.0f;
1564 float oldRotationAngle;
1565 Vector2 rotationPoint;
1566 bool translatingX = false; // Widget state variables
1567 bool translatingY = false;
1568 bool scalingX = false;
1569 bool scalingY = false;
1570 bool rotating = false;
1571 bool resizingX = false; // Not sure what this means... :-/
1572 bool resizingY = false;
1573 float origAngle, origScaleX, origScaleY;
1577 // Function prototypes (move up to top later...)
1579 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius );
1582 void CopyPointsFromSelectedFace( void ){
1583 // Make sure that there's a face and winding to get!
1585 if ( g_SelectedFaceInstances.empty() ) {
1590 Face & face = g_SelectedFaceInstances.last().getFace();
1591 textureNum = face.getShader().m_state->getTexture().texture_number;
1592 textureSize.x() = face.getShader().m_state->getTexture().width;
1593 textureSize.y() = face.getShader().m_state->getTexture().height;
1594 //globalOutputStream() << "--> Texture #" << textureNum << ": " << textureSize.x() << " x " << textureSize.y() << "...\n";
1596 currentBP = SurfaceInspector_GetSelectedTexdef().m_brushprimit_texdef;
1598 face.EmitTextureCoordinates();
1599 Winding & w = face.getWinding();
1602 for ( Winding::const_iterator i = w.begin(); i != w.end(); i++ )
1604 //globalOutputStream() << (*i).texcoord.x() << " " << (*i).texcoord.y() << ", ";
1605 pts[count].x() = ( *i ).texcoord.x();
1606 pts[count].y() = ( *i ).texcoord.y();
1612 //globalOutputStream() << " ..copied points\n";
1615 brushprimit_texdef_t bp;
1616 //This approach is probably wrongheaded and just not right anyway. So, !!! FIX !!! [DONE]
1617 void CommitChanges( void ){
1618 texdef_t t; // Throwaway, since this is BP only
1620 bp.coords[0][0] = tm.coords[0][0] * origBP.coords[0][0] + tm.coords[0][1] * origBP.coords[1][0];
1621 bp.coords[0][1] = tm.coords[0][0] * origBP.coords[0][1] + tm.coords[0][1] * origBP.coords[1][1];
1622 bp.coords[0][2] = tm.coords[0][0] * origBP.coords[0][2] + tm.coords[0][1] * origBP.coords[1][2] + tm.coords[0][2];
1623 //Ok, this works for translation...
1624 // 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();
1625 bp.coords[1][0] = tm.coords[1][0] * origBP.coords[0][0] + tm.coords[1][1] * origBP.coords[1][0];
1626 bp.coords[1][1] = tm.coords[1][0] * origBP.coords[0][1] + tm.coords[1][1] * origBP.coords[1][1];
1627 bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2];
1628 // 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();
1630 //This doesn't work: g_brush_texture_changed();
1632 //Note: We should only set an undo *after* the button has been released... !!! FIX !!!
1633 //Definitely *should* have an undo, though!
1634 // UndoableCommand undo("textureProjectionSetSelected");
1635 Select_SetTexdef( TextureProjection( t, bp, Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) ) );
1636 //This is working, but for some reason the translate is causing the rest of the SI
1637 //widgets to yield bad readings... !!! FIX !!!
1638 //I.e., click on textool window, translate face wireframe, then controls go crazy. Dunno why.
1639 //It's because there were some uncommented out add/removeScale functions in brush.h and a
1640 //removeScale in brushmanip.cpp... :-/
1641 //Translate isn't working at all now... :-(
1642 //It's because we need to multiply in some scaling factor (prolly the texture width/height)
1646 void UpdateControlPoints( void ){
1649 // Init texture transform matrix
1651 tm.coords[0][0] = 1.0f; tm.coords[0][1] = 0.0f; tm.coords[0][2] = 0.0f;
1652 tm.coords[1][0] = 0.0f; tm.coords[1][1] = 1.0f; tm.coords[1][2] = 0.0f;
1657 For shifting we have:
1660 The code that should provide reasonable defaults, but doesn't for some reason:
1661 It's scaling the BP by 128 for some reason, between the time it's created and the
1662 time we get back to the SI widgets:
1664 static void OnBtnAxial(GtkWidget *widget, gpointer data)
1666 UndoableCommand undo("textureDefault");
1667 TextureProjection projection;
1668 TexDef_Construct_Default(projection);
1669 Select_SetTexdef(projection);
1672 Select_SetTexdef() calls Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection)
1673 which is in brushmanip.h: This eventually calls
1674 Texdef_Assign(m_texdef, texdef, m_brushprimit_texdef, brushprimit_texdef) in class Face...
1675 which just copies from brushpr to m_brushpr...
1678 //Small problem with this thing: It's scaled to the texture which is all screwed up... !!! FIX !!! [DONE]
1679 //Prolly should separate out the grid drawing so that we can draw it behind the polygon.
1680 const float gridWidth = 1.3f; // Let's try an absolute height... WORKS!!!
1681 // NOTE that 2.0 is the height of the viewport. Dunno why... Should make collision
1682 // detection easier...
1683 const float gridRadius = gridWidth * 0.5f;
1685 typedef const float WidgetColor[3];
1686 const WidgetColor widgetColor[10] = {
1687 { 1.0000f, 0.2000f, 0.0000f }, // Red
1688 { 0.9137f, 0.9765f, 0.4980f }, // Yellow
1689 { 0.0000f, 0.6000f, 0.3216f }, // Green
1690 { 0.6157f, 0.7726f, 0.8196f }, // Cyan
1691 { 0.4980f, 0.5000f, 0.4716f }, // Grey
1694 { 1.0000f, 0.6000f, 0.4000f }, // Light Red
1695 { 1.0000f, 1.0000f, 0.8980f }, // Light Yellow
1696 { 0.4000f, 1.0000f, 0.7216f }, // Light Green
1697 { 1.0000f, 1.0000f, 1.0000f }, // Light Cyan
1698 { 0.8980f, 0.9000f, 0.8716f } // Light Grey
1702 #define COLOR_YELLOW 1
1703 #define COLOR_GREEN 2
1704 #define COLOR_CYAN 3
1705 #define COLOR_GREY 4
1706 #define COLOR_LT_RED 5
1707 #define COLOR_LT_YELLOW 6
1708 #define COLOR_LT_GREEN 7
1709 #define COLOR_LT_CYAN 8
1710 #define COLOR_LT_GREY 9
1712 void DrawControlWidgets( void ){
1713 //Note that the grid should go *behind* the face outline... !!! FIX !!!
1715 float xStart = center.x() - ( gridWidth / 2.0f );
1716 float yStart = center.y() - ( gridWidth / 2.0f );
1717 float xScale = ( extents.height() / extents.width() ) * ( textureSize.y() / textureSize.x() );
1720 //Small problem with this approach: Changing the center point in the TX code doesn't seem to
1721 //change anything here--prolly because we load a new identity matrix. A couple of ways to fix
1722 //this would be to get rid of that code, or change the center to a new point by taking into
1723 //account the transforms that we toss with the new identity matrix. Dunno which is better.
1725 glScalef( xScale, 1.0, 1.0 ); // Will that square it up? Yup.
1726 glRotatef( static_cast<float>( radians_to_degrees( atan2( -currentBP.coords[0][1], currentBP.coords[0][0] ) ) ), 0.0, 0.0, -1.0 );
1727 glTranslatef( -center.x(), -center.y(), 0.0 );
1730 glColor3fv( translatingX && translatingY ? widgetColor[COLOR_LT_YELLOW] : widgetColor[COLOR_YELLOW] );
1731 glBegin( GL_LINE_LOOP );
1732 DrawCircularArc( center, 0, 2.0f * PI, gridRadius * 0.16 );
1737 glBegin( GL_LINES );
1738 glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1739 glVertex2f( center.x(), center.y() + ( gridRadius * 0.16 ) );
1740 glVertex2f( center.x(), center.y() + ( gridRadius * 1.00 ) );
1741 glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1742 glVertex2f( center.x() + ( gridRadius * 0.16 ), center.y() );
1743 glVertex2f( center.x() + ( gridRadius * 1.00 ), center.y() );
1747 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1748 glBegin( GL_TRIANGLES );
1749 glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1750 glVertex2f( center.x(), center.y() + ( gridRadius * 1.10 ) );
1751 glVertex2f( center.x() + ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1752 glVertex2f( center.x() - ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1753 glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1754 glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() );
1755 glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() + ( gridRadius * 0.06 ) );
1756 glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() - ( gridRadius * 0.06 ) );
1760 glBegin( GL_LINE_STRIP );
1761 glColor3fv( rotating ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1762 DrawCircularArc( center, 0.03f * PI, 0.47f * PI, gridRadius * 0.90 );
1766 glColor3fv( scalingY && !scalingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1767 glBegin( GL_LINES );
1768 glVertex2f( center.x() + ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1769 glVertex2f( center.x() - ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1771 glBegin( GL_LINE_LOOP );
1772 glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1773 glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1774 glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1775 glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1778 glColor3fv( scalingX && !scalingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1779 glBegin( GL_LINES );
1780 glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 0.20 ) );
1781 glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() - ( gridRadius * 0.20 ) );
1783 glBegin( GL_LINE_LOOP );
1784 glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 0.10 ) );
1785 glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() - ( gridRadius * 0.10 ) );
1786 glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() - ( gridRadius * 0.10 ) );
1787 glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 0.10 ) );
1790 glColor3fv( scalingX && scalingY ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1791 glBegin( GL_LINE_STRIP );
1792 glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.10 ) );
1793 glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.50 ) );
1794 glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() + ( gridRadius * 1.50 ) );
1796 glBegin( GL_LINE_LOOP );
1797 glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.40 ) );
1798 glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.20 ) );
1799 glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.20 ) );
1800 glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.40 ) );
1806 void DrawControlPoints( void ){
1807 glColor3f( 1, 1, 1 );
1808 glBegin( GL_LINE_LOOP );
1810 for ( int i = 0; i < numPts; i++ )
1811 glVertex2f( pts[i].x(), pts[i].y() );
1816 // Note: Setup and all that jazz must be done by the caller!
1818 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius ){
1819 float stepSize = ( 2.0f * PI ) / 200.0f;
1821 for ( float angle = startAngle; angle <= endAngle; angle += stepSize )
1822 glVertex2f( ctr.x() + radius * cos( angle ), ctr.y() + radius * sin( angle ) );
1827 if ( numPts == 0 ) {
1831 // Find selected texture's extents...
1833 extents.minX = extents.maxX = pts[0].x(),
1834 extents.minY = extents.maxY = pts[0].y();
1836 for ( int i = 1; i < numPts; i++ )
1838 if ( pts[i].x() < extents.minX ) {
1839 extents.minX = pts[i].x();
1841 if ( pts[i].x() > extents.maxX ) {
1842 extents.maxX = pts[i].x();
1844 if ( pts[i].y() < extents.minY ) {
1845 extents.minY = pts[i].y();
1847 if ( pts[i].y() > extents.maxY ) {
1848 extents.maxY = pts[i].y();
1852 // Do some viewport fitting stuff...
1854 //globalOutputStream() << "--> Center: " << center.x() << ", " << center.y() << "\n";
1855 //globalOutputStream() << "--> Extents (stage 1): " << extents.minX << ", "
1856 // << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1857 // TTimo: Apply a ratio to get the area we'll draw.
1858 center.x() = 0.5f * ( extents.minX + extents.maxX ),
1859 center.y() = 0.5f * ( extents.minY + extents.maxY );
1860 extents.minX = center.x() + VP_PADDING * ( extents.minX - center.x() ),
1861 extents.minY = center.y() + VP_PADDING * ( extents.minY - center.y() ),
1862 extents.maxX = center.x() + VP_PADDING * ( extents.maxX - center.x() ),
1863 extents.maxY = center.y() + VP_PADDING * ( extents.maxY - center.y() );
1864 //globalOutputStream() << "--> Extents (stage 2): " << extents.minX << ", "
1865 // << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1867 // TTimo: We want a texture with the same X / Y ratio.
1868 // TTimo: Compute XY space / window size ratio.
1869 float SSize = extents.width(), TSize = extents.height();
1870 float ratioX = textureSize.x() * extents.width() / windowSize.x(),
1871 ratioY = textureSize.y() * extents.height() / windowSize.y();
1872 //globalOutputStream() << "--> Texture size: " << textureSize.x() << ", " << textureSize.y() << "\n";
1873 //globalOutputStream() << "--> Window size: " << windowSize.x() << ", " << windowSize.y() << "\n";
1875 if ( ratioX > ratioY ) {
1876 TSize = ( windowSize.y() * ratioX ) / textureSize.y();
1877 // TSize = extents.width() * (windowSize.y() / windowSize.x()) * (textureSize.x() / textureSize.y());
1881 SSize = ( windowSize.x() * ratioY ) / textureSize.x();
1882 // SSize = extents.height() * (windowSize.x() / windowSize.y()) * (textureSize.y() / textureSize.x());
1885 extents.minX = center.x() - 0.5f * SSize, extents.maxX = center.x() + 0.5f * SSize,
1886 extents.minY = center.y() - 0.5f * TSize, extents.maxY = center.y() + 0.5f * TSize;
1887 //globalOutputStream() << "--> Extents (stage 3): " << extents.minX << ", "
1888 // << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1891 gboolean size_allocate( ui::Widget win, GtkAllocation * a, gpointer ){
1892 windowSize.x() = a->width;
1893 windowSize.y() = a->height;
1898 gboolean expose( ui::Widget win, GdkEventExpose * e, gpointer ){
1899 // globalOutputStream() << "--> Textool Window was exposed!\n";
1900 // globalOutputStream() << " (window width/height: " << cc << "/" << e->area.height << ")\n";
1902 // windowSize.x() = e->area.width, windowSize.y() = e->area.height;
1903 //This needs to go elsewhere...
1906 if ( glwidget_make_current( win ) == FALSE ) {
1907 globalOutputStream() << " FAILED to make current! Oh, the agony! :-(\n";
1911 CopyPointsFromSelectedFace();
1913 if ( !lButtonDown ) {
1917 // Probably should init button/anchor states here as well...
1918 // rotationAngle = 0.0f;
1919 glClearColor( 0, 0, 0, 0 );
1920 glViewport( 0, 0, e->area.width, e->area.height );
1921 glMatrixMode( GL_PROJECTION );
1925 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1926 glDisable( GL_DEPTH_TEST );
1927 glDisable( GL_BLEND );
1929 glOrtho( extents.minX, extents.maxX, extents.maxY, extents.minY, -1, 1 );
1931 glColor3f( 1, 1, 1 );
1932 // draw the texture background
1933 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1934 glBindTexture( GL_TEXTURE_2D, textureNum );
1936 glEnable( GL_TEXTURE_2D );
1937 glBegin( GL_QUADS );
1938 glTexCoord2f( extents.minX, extents.minY );
1939 glVertex2f( extents.minX, extents.minY );
1940 glTexCoord2f( extents.maxX, extents.minY );
1941 glVertex2f( extents.maxX, extents.minY );
1942 glTexCoord2f( extents.maxX, extents.maxY );
1943 glVertex2f( extents.maxX, extents.maxY );
1944 glTexCoord2f( extents.minX, extents.maxY );
1945 glVertex2f( extents.minX, extents.maxY );
1947 glDisable( GL_TEXTURE_2D );
1949 // draw the texture-space grid
1950 glColor3fv( widgetColor[COLOR_GREY] );
1951 glBegin( GL_LINES );
1953 const int gridSubdivisions = 8;
1954 const float gridExtents = 4.0f;
1956 for ( int i = 0; i < gridSubdivisions + 1; ++i )
1958 float y = i * ( gridExtents / float(gridSubdivisions) );
1959 float x = i * ( gridExtents / float(gridSubdivisions) );
1961 glVertex2f( gridExtents, y );
1963 glVertex2f( x, gridExtents );
1968 DrawControlPoints();
1969 DrawControlWidgets();
1971 // reset the current texture
1972 // glBindTexture(GL_TEXTURE_2D, 0);
1974 glwidget_swap_buffers( win );
1979 /*int FindSelectedPoint(int x, int y)
1981 for(int i=0; i<numPts; i++)
1983 int nx = (int)(windowSize.x() * (pts[i].x() - extents.minX) / extents.width());
1984 int ny = (int)(windowSize.y() * (pts[i].y() - extents.minY) / extents.height());
1986 if (abs(nx - x) <= 3 && abs(ny - y) <= 3)
1995 Vector2 dragPoint; // Defined in terms of window space (+x/-y)
1997 gboolean button_press( ui::Widget win, GdkEventButton * e, gpointer ){
1998 // globalOutputStream() << "--> Textool button press...\n";
2000 if ( e->button == 1 ) {
2002 GlobalUndoSystem().start();
2006 //globalOutputStream() << "--> Original BP: [" << origBP.coords[0][0] << "][" << origBP.coords[0][1] << "][" << origBP.coords[0][2] << "]\n";
2007 //globalOutputStream() << " [" << origBP.coords[1][0] << "][" << origBP.coords[1][1] << "][" << origBP.coords[1][2] << "]\n";
2008 //float angle = atan2(origBP.coords[0][1], origBP.coords[0][0]) * 180.0f / 3.141592653589f;
2009 origAngle = ( origBP.coords[0][1] > 0 ? PI : -PI ); // Could also be -PI... !!! FIX !!! [DONE]
2011 if ( origBP.coords[0][0] != 0.0f ) {
2012 origAngle = atan( origBP.coords[0][1] / origBP.coords[0][0] );
2015 origScaleX = origBP.coords[0][0] / cos( origAngle );
2016 origScaleY = origBP.coords[1][1] / cos( origAngle );
2017 rotationAngle = origAngle;
2018 oldCenter[0] = oldCenter[1] = 0;
2020 //globalOutputStream() << "--> BP stats: ang=" << origAngle * RAD_TO_DEG << ", scale=" << origScaleX << "/" << origScaleY << "\n";
2021 //Should also set the Flip X/Y checkboxes here as well... !!! FIX !!!
2022 //Also: should reverse texture left/right up/down instead of flipping the points...
2025 //float nx = windowSize.x() * (e->x - extents.minX) / (extents.maxX - extents.minX);
2026 //float ny = windowSize.y() * (e->y - extents.minY) / (extents.maxY - extents.minY);
2028 //But I want it to scroll the texture window, not the points... !!! FIX !!!
2029 //Actually, should scroll the texture window only when mouse is down on no widgets...
2030 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2031 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2032 trans.x() = -tm.coords[0][0] * nx - tm.coords[0][1] * ny;
2033 trans.y() = -tm.coords[1][0] * nx - tm.coords[1][1] * ny;
2035 dragPoint.x() = e->x, dragPoint.y() = e->y;
2036 trans2.x() = nx, trans2.y() = ny;
2037 oldRotationAngle = rotationAngle;
2038 // oldTrans.x() = tm.coords[0][2] - nx * textureSize.x();
2039 // oldTrans.y() = tm.coords[1][2] - ny * textureSize.y();
2040 oldTrans.x() = tm.coords[0][2];
2041 oldTrans.y() = tm.coords[1][2];
2042 oldCenter.x() = center.x();
2043 oldCenter.y() = center.y();
2049 /* else if (e->button == 3)
2054 //globalOutputStream() << "(" << (haveAnchor ? "anchor" : "released") << ")\n";
2059 gboolean button_release( ui::Widget win, GdkEventButton * e, gpointer ){
2060 // globalOutputStream() << "--> Textool button release...\n";
2062 if ( e->button == 1 ) {
2063 /* float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2064 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2066 //This prolly should go into the mouse move code...
2067 //Doesn't work correctly anyway...
2068 if (translatingX || translatingY)
2069 center.x() = ptx, center.y() = pty;//*/
2071 lButtonDown = false;
2073 if ( translatingX || translatingY ) {
2074 GlobalUndoSystem().finish( "translateTexture" );
2076 else if ( rotating ) {
2077 GlobalUndoSystem().finish( "rotateTexture" );
2079 else if ( scalingX || scalingY ) {
2080 GlobalUndoSystem().finish( "scaleTexture" );
2082 else if ( resizingX || resizingY ) {
2083 GlobalUndoSystem().finish( "resizeTexture" );
2087 GlobalUndoSystem().finish( "textoolUnknown" );
2090 rotating = translatingX = translatingY = scalingX = scalingY
2091 = resizingX = resizingY = false;
2095 else if ( e->button == 3 ) {
2096 rButtonDown = false;
2103 void C2DView::GridForWindow( float c[2], int x, int y)
2105 SpaceForWindow( c, x, y );
2108 c[0] /= m_GridStep[0];
2109 c[1] /= m_GridStep[1];
2110 c[0] = (float)floor( c[0] + 0.5f );
2111 c[1] = (float)floor( c[1] + 0.5f );
2112 c[0] *= m_GridStep[0];
2113 c[1] *= m_GridStep[1];
2115 void C2DView::SpaceForWindow( float c[2], int x, int y)
2117 c[0] = ((float)(x))/((float)(m_rect.right-m_rect.left))*(m_Maxs[0]-m_Mins[0])+m_Mins[0];
2118 c[1] = ((float)(y))/((float)(m_rect.bottom-m_rect.top))*(m_Maxs[1]-m_Mins[1])+m_Mins[1];
2121 gboolean motion( ui::Widget win, GdkEventMotion * e, gpointer ){
2122 // globalOutputStream() << "--> Textool motion...\n";
2124 if ( lButtonDown ) {
2125 if ( translatingX || translatingY ) {
2126 float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2127 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2129 //Need to fix this to take the rotation angle into account, so that it moves along
2130 //the rotated X/Y axis...
2131 if ( translatingX ) {
2132 // tm.coords[0][2] = (trans.x() + ptx) * textureSize.x();
2133 //This works, but only when the angle is zero. !!! FIX !!! [DONE]
2134 // tm.coords[0][2] = oldCenter.x() + (ptx * textureSize.x());
2135 tm.coords[0][2] = oldTrans.x() + ( ptx - trans2.x() ) * textureSize.x();
2136 // center.x() = oldCenter.x() + (ptx - trans2.x());
2139 if ( translatingY ) {
2140 // tm.coords[1][2] = (trans.y() + pty) * textureSize.y();
2141 // tm.coords[1][2] = oldCenter.y() + (pty * textureSize.y());
2142 tm.coords[1][2] = oldTrans.y() + ( pty - trans2.y() ) * textureSize.y();
2143 // center.y() = oldCenter.y() + (pty - trans2.y());
2146 //Need to update center.x/y() so that the widget translates as well. Also, oldCenter
2147 //is badly named... Should be oldTrans or something like that... !!! FIX !!!
2148 //Changing center.x/y() here doesn't seem to change anything... :-/
2149 UpdateControlPoints();
2151 else if ( rotating ) {
2152 // Shamus: New rotate code
2153 int cx = (int)( windowSize.x() * ( center.x() - extents.minX ) / extents.width() );
2154 int cy = (int)( windowSize.y() * ( center.y() - extents.minY ) / extents.height() );
2155 Vector3 v1( dragPoint.x() - cx, dragPoint.y() - cy, 0 ), v2( e->x - cx, e->y - cy, 0 );
2157 vector3_normalise( v1 );
2158 vector3_normalise( v2 );
2159 float c = vector3_dot( v1, v2 );
2160 Vector3 cross = vector3_cross( v1, v2 );
2161 float s = vector3_length( cross );
2163 if ( cross[2] > 0 ) {
2167 // Problem with this: arcsin/cos seems to only return -90 to 90 and 0 to 180...
2168 // Can't derive angle from that!
2170 //rotationAngle = asin(s);// * 180.0f / 3.141592653589f;
2171 rotationAngle = acos( c );
2172 //rotationAngle2 = asin(s);
2173 if ( cross[2] < 0 ) {
2174 rotationAngle = -rotationAngle;
2177 //NO! DOESN'T WORK! rotationAngle -= 45.0f * DEG_TO_RAD;
2180 /*c = cos(rotationAngle - oldRotationAngle);
2181 s = sin(rotationAngle - oldRotationAngle);
2182 rotationAngle += oldRotationAngle;
2183 //c += cos(oldRotationAngle);
2184 //s += sin(oldRotationAngle);
2185 //rotationAngle += oldRotationAngle;
2188 //rotationAngle %= 2.0 * PI;//*/
2190 //This is wrong... Hmm...
2191 //It seems to shear the texture instead of rotating it... !!! FIX !!!
2192 // Now it rotates correctly. Seems TTimo was overcomplicating things here... ;-)
2194 // Seems like what needs to happen here is multiplying these rotations by tm... !!! FIX !!!
2196 // See brush_primit.cpp line 244 (Texdef_EmitTextureCoordinates()) for where texcoords come from...
2198 tm.coords[0][0] = c;
2199 tm.coords[0][1] = s;
2200 tm.coords[1][0] = -s;
2201 tm.coords[1][1] = c;
2202 //It doesn't work anymore... Dunno why...
2203 //tm.coords[0][2] = -trans.x(); // This works!!! Yeah!!!
2204 //tm.coords[1][2] = -trans.y();
2206 //tm.coords[0][2] = rotationPoint.x(); // This works, but strangely...
2207 //tm.coords[1][2] = rotationPoint.y();
2208 //tm.coords[0][2] = 0;// center.x() / 2.0f;
2209 //tm.coords[1][2] = 0;// center.y() / 2.0f;
2211 //tm.coords[0][2] = -(center.x() * textureSize.x());
2212 //tm.coords[1][2] = -(center.y() * textureSize.y());
2213 //Eh? No, but seems to be getting closer...
2214 /*float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2215 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2216 tm.coords[0][2] = -c * center.x() - s * center.y() + ptx;
2217 tm.coords[1][2] = s * center.x() - c * center.x() + pty;//*/
2218 //Kinda works, but center drifts around on non-square textures...
2219 /*tm.coords[0][2] = (-c * center.x() - s * center.y()) * textureSize.x();
2220 tm.coords[1][2] = ( s * center.x() - c * center.y()) * textureSize.y();//*/
2221 //Rotates correctly, but not around the actual center of the face's points...
2222 /*tm.coords[0][2] = -c * center.x() * textureSize.x() - s * center.y() * textureSize.y();
2223 tm.coords[1][2] = s * center.x() * textureSize.x() - c * center.y() * textureSize.y();//*/
2225 tm.coords[0][2] = ( -c * center.x() * textureSize.x() - s * center.y() * textureSize.y() ) + center.x() * textureSize.x();
2226 tm.coords[1][2] = ( s * center.x() * textureSize.x() - c * center.y() * textureSize.y() ) + center.y() * textureSize.y(); //*/
2227 //This doesn't work...
2228 //And this is the wrong place for this anyway (I'm pretty sure).
2229 /*tm.coords[0][2] += oldCenter.x();
2230 tm.coords[1][2] += oldCenter.y();//*/
2231 UpdateControlPoints(); // will cause a redraw
2236 else // Check for widget mouseovers
2239 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2240 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2241 // Translate nx/y to the "center" point...
2244 ny = -ny; // Flip Y-axis so that increasing numbers move up
2246 tran.x() = tm.coords[0][0] * nx + tm.coords[0][1] * ny;
2247 tran.y() = tm.coords[1][0] * nx + tm.coords[1][1] * ny;
2248 //This doesn't seem to generate a valid distance from the center--for some reason it
2249 //calculates a fixed number every time
2250 //Look at nx/y above: they're getting fixed there! !!! FIX !!! [DONE]
2251 float dist = sqrt( ( nx * nx ) + ( ny * ny ) );
2252 // Normalize to the 2.0 = height standard (for now)
2253 //globalOutputStream() << "--> Distance before: " << dist;
2254 dist = dist * 2.0f / extents.height();
2255 //globalOutputStream() << ". After: " << dist;
2256 tran.x() = tran.x() * 2.0f / extents.height();
2257 tran.y() = tran.y() * 2.0f / extents.height();
2258 //globalOutputStream() << ". Trans: " << tran.x() << ", " << tran.y() << "\n";
2260 //Let's try this instead...
2261 //Interesting! It seems that e->x/y are rotated
2262 //(no, they're not--the TM above is what's doing it...)
2263 nx = ( ( e->x / windowSize.y() ) * 2.0f ) - ( windowSize.x() / windowSize.y() );
2264 ny = ( ( e->y / windowSize.y() ) * 2.0f ) - ( windowSize.y() / windowSize.y() );
2266 //Cool! It works! Now just need to do rotation...
2268 rotating = translatingX = translatingY = scalingX = scalingY
2269 = resizingX = resizingY = false;
2271 if ( dist < ( gridRadius * 0.16f ) ) {
2272 translatingX = translatingY = true;
2274 else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2275 && fabs( ny ) < ( gridRadius * 0.05f ) && nx > 0 ) {
2276 translatingX = true;
2278 else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2279 && fabs( nx ) < ( gridRadius * 0.05f ) && ny > 0 ) {
2280 translatingY = true;
2282 // Should tighten up the angle on this, or put this test after the axis tests...
2283 else if ( tran.x() > 0 && tran.y() > 0
2284 && ( dist > ( gridRadius * 0.82f ) && dist < ( gridRadius * 0.98f ) ) ) {
2296 //It seems the fake tex coords conversion is screwing this stuff up... !!! FIX !!!
2297 //This is still wrong... Prolly need to do something with the oldScaleX/Y stuff...
2298 void flipX( ui::ToggleButton, gpointer ){
2299 // globalOutputStream() << "--> Flip X...\n";
2301 // SurfaceInspector_GetSelectedBPTexdef(); // Refresh g_selectedBrushPrimitTexdef...
2302 // tm.coords[0][0] = -tm.coords[0][0];
2303 // tm.coords[1][0] = -tm.coords[1][0];
2304 // tm.coords[0][0] = -tm.coords[0][0]; // This should be correct now...Nope.
2305 // tm.coords[1][1] = -tm.coords[1][1];
2306 tm.coords[0][0] = -tm.coords[0][0]; // This should be correct now...
2307 tm.coords[1][0] = -tm.coords[1][0];
2308 // tm.coords[2][0] = -tm.coords[2][0];//wil wok? no.
2309 UpdateControlPoints();
2312 void flipY( ui::ToggleButton, gpointer ){
2313 // globalOutputStream() << "--> Flip Y...\n";
2314 // tm.coords[0][1] = -tm.coords[0][1];
2315 // tm.coords[1][1] = -tm.coords[1][1];
2316 // tm.coords[0][1] = -tm.coords[0][1]; // This should be correct now...Nope.
2317 // tm.coords[1][0] = -tm.coords[1][0];
2318 tm.coords[0][1] = -tm.coords[0][1]; // This should be correct now...
2319 tm.coords[1][1] = -tm.coords[1][1];
2320 // tm.coords[2][1] = -tm.coords[2][1];//wil wok? no.
2321 UpdateControlPoints();
2324 } // end namespace TexTool