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 ){
110 gtk_adjustment_set_step_increment(gtk_spin_button_get_adjustment( spin ), step);
112 GValue gvalue = GValue_default();
113 g_value_init( &gvalue, G_TYPE_DOUBLE );
114 g_value_set_double( &gvalue, step );
115 g_object_set( G_OBJECT( gtk_spin_button_get_adjustment( spin ) ), "step-increment", &gvalue, NULL );
123 ui::SpinButton m_spin;
125 Increment( float& f ) : m_f( f ), m_spin( ui::null ), m_entry( ui::null ){
128 entry_set_float( m_entry, m_f );
130 typedef MemberCaller<Increment, void(), &Increment::cancel> CancelCaller;
132 m_f = static_cast<float>( entry_get_float( m_entry ) );
133 spin_button_set_step( m_spin, m_f );
135 typedef MemberCaller<Increment, void(), &Increment::apply> ApplyCaller;
138 void SurfaceInspector_GridChange();
140 class SurfaceInspector : public Dialog
142 ui::Window BuildDialog();
144 NonModalEntry m_textureEntry;
145 NonModalSpinner m_hshiftSpinner;
146 NonModalEntry m_hshiftEntry;
147 NonModalSpinner m_vshiftSpinner;
148 NonModalEntry m_vshiftEntry;
149 NonModalSpinner m_hscaleSpinner;
150 NonModalEntry m_hscaleEntry;
151 NonModalSpinner m_vscaleSpinner;
152 NonModalEntry m_vscaleEntry;
153 NonModalSpinner m_rotateSpinner;
154 NonModalEntry m_rotateEntry;
158 GtkCheckButton* m_surfaceFlags[32];
159 GtkCheckButton* m_contentFlags[32];
161 NonModalEntry m_valueEntry;
162 ui::Entry m_valueEntryWidget{ui::null};
164 WindowPositionTracker m_positionTracker;
167 float m_fitHorizontal;
170 Increment m_hshiftIncrement;
171 Increment m_vshiftIncrement;
172 Increment m_hscaleIncrement;
173 Increment m_vscaleIncrement;
174 Increment m_rotateIncrement;
175 ui::Entry m_texture{ui::null};
178 m_textureEntry( ApplyShaderCaller( *this ), UpdateCaller( *this ) ),
179 m_hshiftSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
180 m_hshiftEntry( Increment::ApplyCaller( m_hshiftIncrement ), Increment::CancelCaller( m_hshiftIncrement ) ),
181 m_vshiftSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
182 m_vshiftEntry( Increment::ApplyCaller( m_vshiftIncrement ), Increment::CancelCaller( m_vshiftIncrement ) ),
183 m_hscaleSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
184 m_hscaleEntry( Increment::ApplyCaller( m_hscaleIncrement ), Increment::CancelCaller( m_hscaleIncrement ) ),
185 m_vscaleSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
186 m_vscaleEntry( Increment::ApplyCaller( m_vscaleIncrement ), Increment::CancelCaller( m_vscaleIncrement ) ),
187 m_rotateSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
188 m_rotateEntry( Increment::ApplyCaller( m_rotateIncrement ), Increment::CancelCaller( m_rotateIncrement ) ),
189 m_idleDraw( UpdateCaller( *this ) ),
190 m_valueEntry( ApplyFlagsCaller( *this ), UpdateCaller( *this ) ),
191 m_hshiftIncrement( g_si_globals.shift[0] ),
192 m_vshiftIncrement( g_si_globals.shift[1] ),
193 m_hscaleIncrement( g_si_globals.scale[0] ),
194 m_vscaleIncrement( g_si_globals.scale[1] ),
195 m_rotateIncrement( g_si_globals.rotate ){
198 m_positionTracker.setPosition( c_default_window_pos );
201 void constructWindow( ui::Window main_window ){
202 m_parent = main_window;
204 AddGridChangeCallback( FreeCaller<void(), SurfaceInspector_GridChange>() );
206 void destroyWindow(){
210 return GetWidget().visible();
214 m_idleDraw.queueDraw();
219 typedef MemberCaller<SurfaceInspector, void(), &SurfaceInspector::Update> UpdateCaller;
221 typedef MemberCaller<SurfaceInspector, void(), &SurfaceInspector::ApplyShader> ApplyShaderCaller;
223 typedef MemberCaller<SurfaceInspector, void(), &SurfaceInspector::ApplyTexdef> ApplyTexdefCaller;
225 typedef MemberCaller<SurfaceInspector, void(), &SurfaceInspector::ApplyFlags> ApplyFlagsCaller;
230 SurfaceInspector* g_SurfaceInspector;
232 inline SurfaceInspector& getSurfaceInspector(){
233 ASSERT_NOTNULL( g_SurfaceInspector );
234 return *g_SurfaceInspector;
238 void SurfaceInspector_constructWindow( ui::Window main_window ){
239 getSurfaceInspector().constructWindow( main_window );
241 void SurfaceInspector_destroyWindow(){
242 getSurfaceInspector().destroyWindow();
245 void SurfaceInspector_queueDraw(){
246 getSurfaceInspector().queueDraw();
251 CopiedString g_selectedShader;
252 TextureProjection g_selectedTexdef;
253 ContentsFlagsValue g_selectedFlags;
254 size_t g_selectedShaderSize[2];
257 void SurfaceInspector_SetSelectedShader( const char* shader ){
258 g_selectedShader = shader;
259 SurfaceInspector_queueDraw();
262 void SurfaceInspector_SetSelectedTexdef( const TextureProjection& projection ){
263 g_selectedTexdef = projection;
264 SurfaceInspector_queueDraw();
267 void SurfaceInspector_SetSelectedFlags( const ContentsFlagsValue& flags ){
268 g_selectedFlags = flags;
269 SurfaceInspector_queueDraw();
272 static bool s_texture_selection_dirty = false;
274 void SurfaceInspector_updateSelection(){
275 s_texture_selection_dirty = true;
276 SurfaceInspector_queueDraw();
279 if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
280 TexTool::queueDraw();
281 //globalOutputStream() << "textool texture changed..\n";
286 void SurfaceInspector_SelectionChanged( const Selectable& selectable ){
287 SurfaceInspector_updateSelection();
290 void SurfaceInspector_SetCurrent_FromSelected(){
291 if ( s_texture_selection_dirty == true ) {
292 s_texture_selection_dirty = false;
293 if ( !g_SelectedFaceInstances.empty() ) {
294 TextureProjection projection;
295 //This *may* be the point before it fucks up... Let's see!
296 //Yep, there was a call to removeScale in there...
297 Scene_BrushGetTexdef_Component_Selected( GlobalSceneGraph(), projection );
299 SurfaceInspector_SetSelectedTexdef( projection );
301 Scene_BrushGetShaderSize_Component_Selected( GlobalSceneGraph(), g_selectedShaderSize[0], g_selectedShaderSize[1] );
302 g_selectedTexdef.m_brushprimit_texdef.coords[0][2] = float_mod( g_selectedTexdef.m_brushprimit_texdef.coords[0][2], (float)g_selectedShaderSize[0] );
303 g_selectedTexdef.m_brushprimit_texdef.coords[1][2] = float_mod( g_selectedTexdef.m_brushprimit_texdef.coords[1][2], (float)g_selectedShaderSize[1] );
306 Scene_BrushGetShader_Component_Selected( GlobalSceneGraph(), name );
307 if ( string_not_empty( name.c_str() ) ) {
308 SurfaceInspector_SetSelectedShader( name.c_str() );
311 ContentsFlagsValue flags;
312 Scene_BrushGetFlags_Component_Selected( GlobalSceneGraph(), flags );
313 SurfaceInspector_SetSelectedFlags( flags );
317 TextureProjection projection;
318 Scene_BrushGetTexdef_Selected( GlobalSceneGraph(), projection );
319 SurfaceInspector_SetSelectedTexdef( projection );
322 Scene_BrushGetShader_Selected( GlobalSceneGraph(), name );
323 if ( string_empty( name.c_str() ) ) {
324 Scene_PatchGetShader_Selected( GlobalSceneGraph(), name );
326 if ( string_not_empty( name.c_str() ) ) {
327 SurfaceInspector_SetSelectedShader( name.c_str() );
330 ContentsFlagsValue flags( 0, 0, 0, false );
331 Scene_BrushGetFlags_Selected( GlobalSceneGraph(), flags );
332 SurfaceInspector_SetSelectedFlags( flags );
337 const char* SurfaceInspector_GetSelectedShader(){
338 SurfaceInspector_SetCurrent_FromSelected();
339 return g_selectedShader.c_str();
342 const TextureProjection& SurfaceInspector_GetSelectedTexdef(){
343 SurfaceInspector_SetCurrent_FromSelected();
344 return g_selectedTexdef;
347 const ContentsFlagsValue& SurfaceInspector_GetSelectedFlags(){
348 SurfaceInspector_SetCurrent_FromSelected();
349 return g_selectedFlags;
355 ===================================================
359 ===================================================
362 si_globals_t g_si_globals;
365 // make the shift increments match the grid settings
366 // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size
367 // this depends on a scale value if you have selected a particular texture on which you want it to work:
368 // we move the textures in pixels, not world units. (i.e. increment values are in pixel)
369 // depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize()
370 // increment * scale = gridsize
371 // hscale and vscale are optional parameters, if they are zero they will be set to the default scale
372 // NOTE: the default scale depends if you are using BP mode or regular.
373 // For regular it's 0.5f (128 pixels cover 64 world units), for BP it's simply 1.0f
375 void DoSnapTToGrid( float hscale, float vscale ){
376 g_si_globals.shift[0] = static_cast<float>( float_to_integer( static_cast<float>( GetGridSize() ) / hscale ) );
377 g_si_globals.shift[1] = static_cast<float>( float_to_integer( static_cast<float>( GetGridSize() ) / vscale ) );
378 getSurfaceInspector().queueDraw();
381 void SurfaceInspector_GridChange(){
382 if ( g_si_globals.m_bSnapTToGrid ) {
383 DoSnapTToGrid( Texdef_getDefaultTextureScale(), Texdef_getDefaultTextureScale() );
387 // make the shift increments match the grid settings
388 // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size
389 // this depends on the current texture scale used?
390 // we move the textures in pixels, not world units. (i.e. increment values are in pixel)
391 // depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize()
392 // increment * scale = gridsize
393 static void OnBtnMatchGrid( ui::Widget widget, gpointer data ){
394 float hscale, vscale;
395 hscale = static_cast<float>( gtk_spin_button_get_value( getSurfaceInspector().m_hscaleIncrement.m_spin ) );
396 vscale = static_cast<float>( gtk_spin_button_get_value( getSurfaceInspector().m_vscaleIncrement.m_spin ) );
398 if ( hscale == 0.0f || vscale == 0.0f ) {
399 globalOutputStream() << "ERROR: unexpected scale == 0.0f\n";
403 DoSnapTToGrid( hscale, vscale );
406 // DoSurface will always try to show the surface inspector
407 // or update it because something new has been selected
408 // Shamus: It does get called when the SI is hidden, but not when you select something new. ;-)
409 void DoSurface( void ){
410 if ( !getSurfaceInspector().GetWidget() ) {
411 getSurfaceInspector().Create();
414 getSurfaceInspector().Update();
415 getSurfaceInspector().importData();
416 getSurfaceInspector().ShowDlg();
419 void SurfaceInspector_toggleShown(){
420 if ( getSurfaceInspector().visible() ) {
421 getSurfaceInspector().HideDlg();
429 void SurfaceInspector_FitTexture(){
430 UndoableCommand undo( "textureAutoFit" );
431 Select_FitTexture( getSurfaceInspector().m_fitHorizontal, getSurfaceInspector().m_fitVertical );
434 void SurfaceInspector_FitTextureW(){
435 UndoableCommand undo( "textureAutoFitW" );
436 Select_FitTextureW( getSurfaceInspector().m_fitHorizontal, getSurfaceInspector().m_fitVertical );
439 void SurfaceInspector_FitTextureH(){
440 UndoableCommand undo( "textureAutoFitH" );
441 Select_FitTextureH( getSurfaceInspector().m_fitHorizontal, getSurfaceInspector().m_fitVertical );
444 static void OnBtnPatchdetails( ui::Widget widget, gpointer data ){
448 static void OnBtnPatchnatural( ui::Widget widget, gpointer data ){
449 Patch_NaturalTexture();
452 static void OnBtnPatchreset( ui::Widget widget, gpointer data ){
453 Patch_ResetTexture();
456 static void OnBtnPatchFit( ui::Widget widget, gpointer data ){
460 static void OnBtnAxial( ui::Widget widget, gpointer data ){
461 //globalOutputStream() << "--> [OnBtnAxial]...\n";
462 UndoableCommand undo( "textureDefault" );
463 TextureProjection projection;
464 //globalOutputStream() << " TexDef_Construct_Default()...\n";
465 TexDef_Construct_Default( projection );
466 //globalOutputStream() << " Select_SetTexdef()...\n";
471 if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
472 // Scale up texture width/height if in BP mode...
473 //NOTE: This may not be correct any more! :-P
474 if ( !g_SelectedFaceInstances.empty() ) {
475 Face & face = g_SelectedFaceInstances.last().getFace();
476 float x = face.getShader().m_state->getTexture().width;
477 float y = face.getShader().m_state->getTexture().height;
478 projection.m_brushprimit_texdef.coords[0][0] /= x;
479 projection.m_brushprimit_texdef.coords[0][1] /= y;
480 projection.m_brushprimit_texdef.coords[1][0] /= x;
481 projection.m_brushprimit_texdef.coords[1][1] /= y;
486 Select_SetTexdef( projection );
489 static void OnBtnFaceFit( ui::Widget widget, gpointer data ){
490 getSurfaceInspector().exportData();
491 SurfaceInspector_FitTexture();
494 static void OnBtnFaceFitW( GtkWidget *widget, gpointer data ){
495 getSurfaceInspector().exportData();
496 SurfaceInspector_FitTextureW();
499 static void OnBtnFaceFitH( GtkWidget *widget, gpointer data ){
500 getSurfaceInspector().exportData();
501 SurfaceInspector_FitTextureH();
506 typedef const char* FlagName;
508 const FlagName surfaceflagNamesDefault[32] = {
543 const FlagName contentflagNamesDefault[32] = {
578 const char* getSurfaceFlagName( std::size_t bit ){
579 const char* value = g_pGameDescription->getKeyValue( surfaceflagNamesDefault[bit] );
580 if ( string_empty( value ) ) {
581 return surfaceflagNamesDefault[bit];
586 const char* getContentFlagName( std::size_t bit ){
587 const char* value = g_pGameDescription->getKeyValue( contentflagNamesDefault[bit] );
588 if ( string_empty( value ) ) {
589 return contentflagNamesDefault[bit];
595 // =============================================================================
596 // SurfaceInspector class
598 guint togglebutton_connect_toggled( ui::ToggleButton button, const Callback<void()>& callback ){
599 return g_signal_connect_swapped( G_OBJECT( button ), "toggled", G_CALLBACK( callback.getThunk() ), callback.getEnvironment() );
602 ui::Window SurfaceInspector::BuildDialog(){
603 ui::Window window = ui::Window(create_floating_window( "Surface Inspector", m_parent ));
605 m_positionTracker.connect( window );
607 global_accel_connect_window( window );
609 window_connect_focus_in_clear_focus_widget( window );
613 // replaced by only the vbox:
614 auto vbox = ui::VBox( FALSE, 5 );
617 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
620 auto hbox2 = ui::HBox( FALSE, 5 );
622 vbox.pack_start( hbox2, FALSE, FALSE, 0 );
625 ui::Widget label = ui::Label( "Texture" );
627 hbox2.pack_start( label, FALSE, TRUE, 0 );
630 auto entry = ui::Entry(ui::New);
632 hbox2.pack_start( entry, TRUE, TRUE, 0 );
634 m_textureEntry.connect( entry );
635 GlobalTextureEntryCompletion::instance().connect( entry );
641 auto table = ui::Table(6, 4, FALSE);
643 vbox.pack_start( table, FALSE, FALSE, 0 );
644 gtk_table_set_row_spacings(table, 5);
645 gtk_table_set_col_spacings(table, 5);
647 ui::Widget label = ui::Label( "Horizontal shift" );
649 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
650 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
653 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 2 );
654 m_hshiftIncrement.m_spin = spin;
655 m_hshiftSpinner.connect( spin );
657 table.attach(spin, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
658 spin.dimensions(60, -1);
661 ui::Widget label = ui::Label( "Step" );
663 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
664 table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 0});
667 auto entry = ui::Entry(ui::New);
669 table.attach(entry, {3, 4, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
670 entry.dimensions(50, -1);
671 m_hshiftIncrement.m_entry = entry;
672 m_hshiftEntry.connect( entry );
675 ui::Widget label = ui::Label( "Vertical shift" );
677 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
678 table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
681 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 2 );
682 m_vshiftIncrement.m_spin = spin;
683 m_vshiftSpinner.connect( spin );
685 table.attach(spin, {1, 2, 1, 2}, {GTK_FILL, 0});
686 spin.dimensions(60, -1);
689 ui::Widget label = ui::Label( "Step" );
691 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
692 table.attach(label, {2, 3, 1, 2}, {GTK_FILL, 0});
695 auto entry = ui::Entry(ui::New);
697 table.attach(entry, {3, 4, 1, 2}, {GTK_FILL, 0});
698 entry.dimensions(50, -1);
699 m_vshiftIncrement.m_entry = entry;
700 m_vshiftEntry.connect( entry );
703 ui::Widget label = ui::Label( "Horizontal stretch" );
705 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
706 table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0});
709 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 5 );
710 m_hscaleIncrement.m_spin = spin;
711 m_hscaleSpinner.connect( spin );
713 table.attach(spin, {1, 2, 2, 3}, {GTK_FILL, 0});
714 spin.dimensions(60, -1);
717 ui::Widget label = ui::Label( "Step" );
719 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
720 table.attach(label, {2, 3, 2, 3}, {GTK_FILL, 0});
723 auto entry = ui::Entry(ui::New);
725 table.attach(entry, {3, 4, 2, 3}, {GTK_FILL, 0});
726 entry.dimensions(50, -1);
727 m_hscaleIncrement.m_entry = entry;
728 m_hscaleEntry.connect( entry );
731 ui::Widget label = ui::Label( "Vertical stretch" );
733 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
734 table.attach(label, {0, 1, 3, 4}, {GTK_FILL, 0});
737 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 5 );
738 m_vscaleIncrement.m_spin = spin;
739 m_vscaleSpinner.connect( spin );
741 table.attach(spin, {1, 2, 3, 4}, {GTK_FILL, 0});
742 spin.dimensions(60, -1);
745 ui::Widget label = ui::Label( "Step" );
747 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
748 table.attach(label, {2, 3, 3, 4}, {GTK_FILL, 0});
751 auto entry = ui::Entry(ui::New);
753 table.attach(entry, {3, 4, 3, 4}, {GTK_FILL, 0});
754 entry.dimensions(50, -1);
755 m_vscaleIncrement.m_entry = entry;
756 m_vscaleEntry.connect( entry );
759 ui::Widget label = ui::Label( "Rotate" );
761 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
762 table.attach(label, {0, 1, 4, 5}, {GTK_FILL, 0});
765 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 2 );
766 m_rotateIncrement.m_spin = spin;
767 m_rotateSpinner.connect( spin );
769 table.attach(spin, {1, 2, 4, 5}, {GTK_FILL, 0});
770 spin.dimensions(60, -1);
771 gtk_spin_button_set_wrap( spin, TRUE );
774 ui::Widget label = ui::Label( "Step" );
776 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
777 table.attach(label, {2, 3, 4, 5}, {GTK_FILL, 0});
780 auto entry = ui::Entry(ui::New);
782 table.attach(entry, {3, 4, 4, 5}, {GTK_FILL, 0});
783 entry.dimensions(50, -1);
784 m_rotateIncrement.m_entry = entry;
785 m_rotateEntry.connect( entry );
789 ui::Widget button = ui::Button( "Match Grid" );
791 table.attach(button, {2, 4, 5, 6}, {GTK_EXPAND | GTK_FILL, 0});
792 button.connect( "clicked", G_CALLBACK( OnBtnMatchGrid ), 0 );
797 auto frame = ui::Frame( "Texturing" );
799 vbox.pack_start( frame, FALSE, FALSE, 0 );
801 auto table = ui::Table(4, 4, FALSE);
804 gtk_table_set_row_spacings(table, 5);
805 gtk_table_set_col_spacings(table, 5);
806 gtk_container_set_border_width( GTK_CONTAINER( table ), 5 );
808 ui::Widget label = ui::Label( "Brush" );
810 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
813 ui::Widget label = ui::Label( "Patch" );
815 table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0});
818 ui::Widget label = ui::Label( "Width" );
820 table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 0});
821 g_signal_connect( G_OBJECT( button ), "clicked",
822 G_CALLBACK( OnBtnFaceFitW ), 0 );
823 gtk_widget_set_usize( button, 60, -2 );
826 ui::Widget label = ui::Label( "Height" );
828 table.attach(label, {3, 4, 0, 1}, {GTK_FILL, 0});
829 g_signal_connect( G_OBJECT( button ), "clicked",
830 G_CALLBACK( OnBtnFaceFitH ), 0 );
831 gtk_widget_set_usize( button, 60, -2 );
834 ui::Widget button = ui::Button( "Axial" );
836 table.attach(button, {0, 1, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
837 button.connect( "clicked",
838 G_CALLBACK( OnBtnAxial ), 0 );
839 button.dimensions(60, -1);
842 ui::Widget button = ui::Button( "Fit" );
844 table.attach(button, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
845 button.connect( "clicked",
846 G_CALLBACK( OnBtnFaceFit ), 0 );
847 button.dimensions(60, -1);
850 ui::Widget button = ui::Button( "CAP" );
852 table.attach(button, {0, 1, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
853 button.connect( "clicked",
854 G_CALLBACK( OnBtnPatchdetails ), 0 );
855 button.dimensions(60, -1);
858 ui::Widget button = ui::Button( "Set..." );
860 table.attach(button, {1, 2, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
861 button.connect( "clicked",
862 G_CALLBACK( OnBtnPatchreset ), 0 );
863 button.dimensions(60, -1);
866 ui::Widget button = ui::Button( "Natural" );
868 table.attach(button, {2, 3, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
869 button.connect( "clicked",
870 G_CALLBACK( OnBtnPatchnatural ), 0 );
871 button.dimensions(60, -1);
874 ui::Widget button = ui::Button( "Fit" );
876 table.attach(button, {3, 4, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
877 button.connect( "clicked",
878 G_CALLBACK( OnBtnPatchFit ), 0 );
879 button.dimensions(60, -1);
882 auto spin = ui::SpinButton( ui::Adjustment( 1, 0, 1 << 16, 1, 10, 0 ), 0, 6 );
884 table.attach(spin, {2, 3, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
885 spin.dimensions(60, -1);
886 AddDialogData( spin, m_fitHorizontal );
889 auto spin = ui::SpinButton( ui::Adjustment( 1, 0, 1 << 16, 1, 10, 0 ), 0, 6 );
891 table.attach(spin, {3, 4, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
892 spin.dimensions(60, -1);
893 AddDialogData( spin, m_fitVertical );
897 if ( !string_empty( g_pGameDescription->getKeyValue( "si_flags" ) ) ) {
899 auto frame = ui::Frame( "Surface Flags" );
901 vbox.pack_start( frame, TRUE, TRUE, 0 );
903 auto vbox3 = ui::VBox( FALSE, 4 );
904 //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
908 auto table = ui::Table( 8, 4, FALSE );
910 vbox3.pack_start( table, TRUE, TRUE, 0 );
911 gtk_table_set_row_spacings( table, 0 );
912 gtk_table_set_col_spacings( table, 0 );
914 GtkCheckButton** p = m_surfaceFlags;
916 for (unsigned int c = 0; c != 4; ++c)
918 for (unsigned int r = 0; r != 8; ++r)
920 auto check = ui::CheckButton( getSurfaceFlagName( c * 8 + r ) );
922 table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0});
924 guint handler_id = togglebutton_connect_toggled( check, ApplyFlagsCaller( *this ) );
925 g_object_set_data( G_OBJECT( check ), "handler", gint_to_pointer( handler_id ) );
932 auto frame = ui::Frame( "Content Flags" );
934 vbox.pack_start( frame, TRUE, TRUE, 0 );
936 auto vbox3 = ui::VBox( FALSE, 4 );
937 //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
942 auto table = ui::Table( 8, 4, FALSE );
944 vbox3.pack_start( table, TRUE, TRUE, 0 );
945 gtk_table_set_row_spacings( table, 0 );
946 gtk_table_set_col_spacings( table, 0 );
948 GtkCheckButton** p = m_contentFlags;
950 for (unsigned int c = 0; c != 4; ++c)
952 for (unsigned int r = 0; r != 8; ++r)
954 auto check = ui::CheckButton( getContentFlagName( c * 8 + r ) );
956 table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0});
958 guint handler_id = togglebutton_connect_toggled( check, ApplyFlagsCaller( *this ) );
959 g_object_set_data( G_OBJECT( check ), "handler", gint_to_pointer( handler_id ) );
963 // not allowed to modify detail flag using Surface Inspector
964 gtk_widget_set_sensitive( ui::CheckButton::from(m_contentFlags[BRUSH_DETAIL_FLAG]), FALSE );
969 auto frame = ui::Frame( "Value" );
971 vbox.pack_start( frame, TRUE, TRUE, 0 );
973 auto vbox3 = ui::VBox( FALSE, 4 );
974 gtk_container_set_border_width( GTK_CONTAINER( vbox3 ), 4 );
979 auto entry = ui::Entry(ui::New);
981 vbox3.pack_start( entry, TRUE, TRUE, 0 );
982 m_valueEntryWidget = entry;
983 m_valueEntry.connect( entry );
990 if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
991 // Shamus: Textool goodies...
992 ui::Widget frame = ui::Frame( "Textool" );
994 vbox.pack_start( frame , FALSE, FALSE, 0 );
996 //Prolly should make this a member or global var, so the SI can draw on it...
997 TexTool::g_textoolWin = glwidget_new( FALSE );
998 // --> Dunno, but this stuff may be necessary... (Looks like it!)
999 g_object_ref( TexTool::g_textoolWin );
1000 gtk_widget_set_events( TexTool::g_textoolWin, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK );
1001 gtk_widget_set_can_focus( TexTool::g_textoolWin, true );
1003 TexTool::g_textoolWin.show();
1004 TexTool::g_textoolWin.dimensions( -1, 240 ); //Yeah!
1005 frame.add(TexTool::g_textoolWin);
1007 TexTool::g_textoolWin.connect( "size_allocate", G_CALLBACK( TexTool::size_allocate ), NULL );
1008 TexTool::g_textoolWin.connect( "expose_event", G_CALLBACK( TexTool::expose ), NULL );
1009 TexTool::g_textoolWin.connect( "button_press_event", G_CALLBACK( TexTool::button_press ), NULL );
1010 TexTool::g_textoolWin.connect( "button_release_event", G_CALLBACK( TexTool::button_release ), NULL );
1011 TexTool::g_textoolWin.connect( "motion_notify_event", G_CALLBACK( TexTool::motion ), NULL );
1014 ui::Widget hbox = ui::HBox( FALSE, 5 );
1016 vbox.pack_start( hbox , FALSE, FALSE, 0 );
1017 // Checkboxes go here... (Flip X/Y)
1018 ui::Widget flipX = ui::CheckButton( "Flip X axis" );
1019 ui::Widget flipY = ui::CheckButton( "Flip Y axis" );
1022 hbox.pack_start( flipX, FALSE, FALSE, 0 );
1023 hbox.pack_start( flipY, FALSE, FALSE, 0 );
1025 //Instead of this, we probably need to create a vbox to put into the frame, then the
1026 //window, then the hbox. !!! FIX !!!
1029 //Hmm. Do we really need g_object_set_data? Mebbe not... And we don't! :-)
1030 // g_object_set_data(G_OBJECT(flipX), "handler", gint_to_pointer(flipX.connect("toggled", G_CALLBACK(TexTool::flipX), 0)));
1031 // g_object_set_data(G_OBJECT(flipY), "handler", gint_to_pointer(flipY.connect("toggled", G_CALLBACK(TexTool::flipY), 0)));
1033 flipX.connect( "toggled", G_CALLBACK( TexTool::flipX ), NULL );
1034 flipY.connect( "toggled", G_CALLBACK( TexTool::flipY ), NULL );
1047 Set the fields to the current texdef (i.e. map/texdef -> dialog widgets)
1048 if faces selected (instead of brushes) -> will read this face texdef, else current texdef
1049 if only patches selected, will read the patch texdef
1053 void spin_button_set_value_no_signal( ui::SpinButton spin, gdouble value ){
1054 guint handler_id = gpointer_to_int( g_object_get_data( G_OBJECT( spin ), "handler" ) );
1055 g_signal_handler_block( G_OBJECT( gtk_spin_button_get_adjustment( spin ) ), handler_id );
1056 gtk_spin_button_set_value( spin, value );
1057 g_signal_handler_unblock( G_OBJECT( gtk_spin_button_get_adjustment( spin ) ), handler_id );
1060 void spin_button_set_step_increment( ui::SpinButton spin, gdouble value ){
1061 auto adjust = gtk_spin_button_get_adjustment( spin );
1062 gtk_adjustment_set_step_increment(adjust, value);
1065 void SurfaceInspector::Update(){
1066 const char * name = SurfaceInspector_GetSelectedShader();
1068 if ( shader_is_texture( name ) ) {
1069 m_texture.text(shader_get_textureName(name));
1076 texdef_t shiftScaleRotate;
1077 //Shamus: This is where we get into trouble--the BP code tries to convert to a "faked"
1078 //shift, rotate & scale values from the brush face, which seems to screw up for some reason.
1080 /*globalOutputStream() << "--> SI::Update. About to do ShiftScaleRotate_fromFace()...\n";
1081 SurfaceInspector_GetSelectedBPTexdef();
1082 globalOutputStream() << "BP: (" << g_selectedBrushPrimitTexdef.coords[0][0] << ", " << g_selectedBrushPrimitTexdef.coords[0][1] << ")("
1083 << g_selectedBrushPrimitTexdef.coords[1][0] << ", " << g_selectedBrushPrimitTexdef.coords[1][1] << ")("
1084 << g_selectedBrushPrimitTexdef.coords[0][2] << ", " << g_selectedBrushPrimitTexdef.coords[1][2] << ") SurfaceInspector::Update\n";//*/
1085 //Ok, it's screwed up *before* we get here...
1086 ShiftScaleRotate_fromFace( shiftScaleRotate, SurfaceInspector_GetSelectedTexdef() );
1088 // normalize again to hide the ridiculously high scale values that get created when using texlock
1089 shiftScaleRotate.shift[0] = float_mod( shiftScaleRotate.shift[0], (float)g_selectedShaderSize[0] );
1090 shiftScaleRotate.shift[1] = float_mod( shiftScaleRotate.shift[1], (float)g_selectedShaderSize[1] );
1093 spin_button_set_value_no_signal( m_hshiftIncrement.m_spin, shiftScaleRotate.shift[0] );
1094 spin_button_set_step_increment( m_hshiftIncrement.m_spin, g_si_globals.shift[0] );
1095 entry_set_float( m_hshiftIncrement.m_entry, g_si_globals.shift[0] );
1099 spin_button_set_value_no_signal( m_vshiftIncrement.m_spin, shiftScaleRotate.shift[1] );
1100 spin_button_set_step_increment( m_vshiftIncrement.m_spin, g_si_globals.shift[1] );
1101 entry_set_float( m_vshiftIncrement.m_entry, g_si_globals.shift[1] );
1105 spin_button_set_value_no_signal( m_hscaleIncrement.m_spin, shiftScaleRotate.scale[0] );
1106 spin_button_set_step_increment( m_hscaleIncrement.m_spin, g_si_globals.scale[0] );
1107 entry_set_float( m_hscaleIncrement.m_entry, g_si_globals.scale[0] );
1111 spin_button_set_value_no_signal( m_vscaleIncrement.m_spin, shiftScaleRotate.scale[1] );
1112 spin_button_set_step_increment( m_vscaleIncrement.m_spin, g_si_globals.scale[1] );
1113 entry_set_float( m_vscaleIncrement.m_entry, g_si_globals.scale[1] );
1117 spin_button_set_value_no_signal( m_rotateIncrement.m_spin, shiftScaleRotate.rotate );
1118 spin_button_set_step_increment( m_rotateIncrement.m_spin, g_si_globals.rotate );
1119 entry_set_float( m_rotateIncrement.m_entry, g_si_globals.rotate );
1122 if ( !string_empty( g_pGameDescription->getKeyValue( "si_flags" ) ) ) {
1123 ContentsFlagsValue flags( SurfaceInspector_GetSelectedFlags() );
1125 entry_set_int( m_valueEntryWidget, flags.m_value );
1127 for ( GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p )
1129 toggle_button_set_active_no_signal( ui::CheckButton::from( *p ), flags.m_surfaceFlags & ( 1 << ( p - m_surfaceFlags ) ) );
1132 for ( GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p )
1134 toggle_button_set_active_no_signal( ui::CheckButton::from( *p ), flags.m_contentFlags & ( 1 << ( p - m_contentFlags ) ) );
1143 Reads the fields to get the current texdef (i.e. widgets -> MAP)
1144 in brush primitive mode, grab the fake shift scale rot and compute a new texture matrix
1147 void SurfaceInspector::ApplyShader(){
1148 StringOutputStream name( 256 );
1149 name << GlobalTexturePrefix_get() << gtk_entry_get_text( m_texture );
1151 // TTimo: detect and refuse invalid texture names (at least the ones with spaces)
1152 if ( !texdef_name_valid( name.c_str() ) ) {
1153 globalErrorStream() << "invalid texture name '" << name.c_str() << "'\n";
1154 SurfaceInspector_queueDraw();
1158 UndoableCommand undo( "textureNameSetSelected" );
1159 Select_SetShader( name.c_str() );
1162 void SurfaceInspector::ApplyTexdef(){
1163 texdef_t shiftScaleRotate;
1165 shiftScaleRotate.shift[0] = static_cast<float>( gtk_spin_button_get_value( m_hshiftIncrement.m_spin ) );
1166 shiftScaleRotate.shift[1] = static_cast<float>( gtk_spin_button_get_value( m_vshiftIncrement.m_spin ) );
1167 shiftScaleRotate.scale[0] = static_cast<float>( gtk_spin_button_get_value( m_hscaleIncrement.m_spin ) );
1168 shiftScaleRotate.scale[1] = static_cast<float>( gtk_spin_button_get_value( m_vscaleIncrement.m_spin ) );
1169 shiftScaleRotate.rotate = static_cast<float>( gtk_spin_button_get_value( m_rotateIncrement.m_spin ) );
1171 TextureProjection projection;
1172 //Shamus: This is the other place that screws up, it seems, since it doesn't seem to do the
1173 //conversion from the face (I think) and so bogus values end up in the thing... !!! FIX !!!
1174 //This is actually OK. :-P
1175 ShiftScaleRotate_toFace( shiftScaleRotate, projection );
1177 UndoableCommand undo( "textureProjectionSetSelected" );
1178 Select_SetTexdef( projection );
1181 void SurfaceInspector::ApplyFlags(){
1182 unsigned int surfaceflags = 0;
1183 for ( GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p )
1185 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( *p ) ) ) {
1186 surfaceflags |= ( 1 << ( p - m_surfaceFlags ) );
1190 unsigned int contentflags = 0;
1191 for ( GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p )
1193 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( *p ) ) ) {
1194 contentflags |= ( 1 << ( p - m_contentFlags ) );
1198 int value = entry_get_int( m_valueEntryWidget );
1200 UndoableCommand undo( "flagsSetSelected" );
1201 Select_SetFlags( ContentsFlagsValue( surfaceflags, contentflags, value, true ) );
1205 void Face_getTexture( Face& face, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags ){
1206 shader = face.GetShader();
1207 face.GetTexdef( projection );
1208 flags = face.getShader().m_flags;
1210 typedef Function<void(Face&, CopiedString&, TextureProjection&, ContentsFlagsValue&), Face_getTexture> FaceGetTexture;
1212 void Face_setTexture( Face& face, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags ){
1213 face.SetShader( shader );
1214 face.SetTexdef( projection );
1215 face.SetFlags( flags );
1217 typedef Function<void(Face&, const char*, const TextureProjection&, const ContentsFlagsValue&), Face_setTexture> FaceSetTexture;
1220 void Patch_getTexture( Patch& patch, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags ){
1221 shader = patch.GetShader();
1222 projection = TextureProjection( texdef_t(), brushprimit_texdef_t(), Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) );
1223 flags = ContentsFlagsValue( 0, 0, 0, false );
1225 typedef Function<void(Patch&, CopiedString&, TextureProjection&, ContentsFlagsValue&), Patch_getTexture> PatchGetTexture;
1227 void Patch_setTexture( Patch& patch, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags ){
1228 patch.SetShader( shader );
1230 typedef Function<void(Patch&, const char*, const TextureProjection&, const ContentsFlagsValue&), Patch_setTexture> PatchSetTexture;
1233 typedef Callback<void(CopiedString&, TextureProjection&, ContentsFlagsValue&)> GetTextureCallback;
1234 typedef Callback<void(const char*, const TextureProjection&, const ContentsFlagsValue&)> SetTextureCallback;
1238 GetTextureCallback getTexture;
1239 SetTextureCallback setTexture;
1243 void Face_getClosest( Face& face, SelectionTest& test, SelectionIntersection& bestIntersection, Texturable& texturable ){
1244 SelectionIntersection intersection;
1245 face.testSelect( test, intersection );
1246 if ( intersection.valid()
1247 && SelectionIntersection_closer( intersection, bestIntersection ) ) {
1248 bestIntersection = intersection;
1249 texturable.setTexture = makeCallback( FaceSetTexture(), face );
1250 texturable.getTexture = makeCallback( FaceGetTexture(), face );
1255 class OccludeSelector : public Selector
1257 SelectionIntersection& m_bestIntersection;
1260 OccludeSelector( SelectionIntersection& bestIntersection, bool& occluded ) : m_bestIntersection( bestIntersection ), m_occluded( occluded ){
1263 void pushSelectable( Selectable& selectable ){
1265 void popSelectable(){
1267 void addIntersection( const SelectionIntersection& intersection ){
1268 if ( SelectionIntersection_closer( intersection, m_bestIntersection ) ) {
1269 m_bestIntersection = intersection;
1275 class BrushGetClosestFaceVisibleWalker : public scene::Graph::Walker
1277 SelectionTest& m_test;
1278 Texturable& m_texturable;
1279 mutable SelectionIntersection m_bestIntersection;
1281 BrushGetClosestFaceVisibleWalker( SelectionTest& test, Texturable& texturable ) : m_test( test ), m_texturable( texturable ){
1283 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1284 if ( path.top().get().visible() ) {
1285 BrushInstance* brush = Instance_getBrush( instance );
1287 m_test.BeginMesh( brush->localToWorld() );
1289 for ( Brush::const_iterator i = brush->getBrush().begin(); i != brush->getBrush().end(); ++i )
1291 Face_getClosest( *( *i ), m_test, m_bestIntersection, m_texturable );
1296 SelectionTestable* selectionTestable = Instance_getSelectionTestable( instance );
1297 if ( selectionTestable ) {
1299 OccludeSelector selector( m_bestIntersection, occluded );
1300 selectionTestable->testSelect( selector, m_test );
1302 Patch* patch = Node_getPatch( path.top() );
1304 m_texturable.setTexture = makeCallback( PatchSetTexture(), *patch );
1305 m_texturable.getTexture = makeCallback( PatchGetTexture(), *patch );
1309 m_texturable = Texturable();
1319 Texturable Scene_getClosestTexturable( scene::Graph& graph, SelectionTest& test ){
1320 Texturable texturable;
1321 graph.traverse( BrushGetClosestFaceVisibleWalker( test, texturable ) );
1325 bool Scene_getClosestTexture( scene::Graph& graph, SelectionTest& test, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags ){
1326 Texturable texturable = Scene_getClosestTexturable( graph, test );
1327 if ( texturable.getTexture != GetTextureCallback() ) {
1328 texturable.getTexture( shader, projection, flags );
1334 void Scene_setClosestTexture( scene::Graph& graph, SelectionTest& test, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags ){
1335 Texturable texturable = Scene_getClosestTexturable( graph, test );
1336 if ( texturable.setTexture != SetTextureCallback() ) {
1337 texturable.setTexture( shader, projection, flags );
1345 TextureProjection m_projection;
1346 ContentsFlagsValue m_flags;
1349 FaceTexture g_faceTextureClipboard;
1351 void FaceTextureClipboard_setDefault(){
1352 g_faceTextureClipboard.m_flags = ContentsFlagsValue( 0, 0, 0, false );
1353 TexDef_Construct_Default( g_faceTextureClipboard.m_projection );
1356 void TextureClipboard_textureSelected( const char* shader ){
1357 FaceTextureClipboard_setDefault();
1360 class TextureBrowser;
1361 extern TextureBrowser g_TextureBrowser;
1362 void TextureBrowser_SetSelectedShader( TextureBrowser& textureBrowser, const char* shader );
1363 const char* TextureBrowser_GetSelectedShader( TextureBrowser& textureBrowser );
1365 void Scene_copyClosestTexture( SelectionTest& test ){
1366 CopiedString shader;
1367 if ( Scene_getClosestTexture( GlobalSceneGraph(), test, shader, g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags ) ) {
1368 TextureBrowser_SetSelectedShader( g_TextureBrowser, shader.c_str() );
1372 void Scene_applyClosestTexture( SelectionTest& test ){
1373 UndoableCommand command( "facePaintTexture" );
1375 Scene_setClosestTexture( GlobalSceneGraph(), test, TextureBrowser_GetSelectedShader( g_TextureBrowser ), g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags );
1377 SceneChangeNotify();
1384 void SelectedFaces_copyTexture(){
1385 if ( !g_SelectedFaceInstances.empty() ) {
1386 Face& face = g_SelectedFaceInstances.last().getFace();
1387 face.GetTexdef( g_faceTextureClipboard.m_projection );
1388 g_faceTextureClipboard.m_flags = face.getShader().m_flags;
1390 TextureBrowser_SetSelectedShader( g_TextureBrowser, face.getShader().getShader() );
1394 void FaceInstance_pasteTexture( FaceInstance& faceInstance ){
1395 faceInstance.getFace().SetTexdef( g_faceTextureClipboard.m_projection );
1396 faceInstance.getFace().SetShader( TextureBrowser_GetSelectedShader( g_TextureBrowser ) );
1397 faceInstance.getFace().SetFlags( g_faceTextureClipboard.m_flags );
1398 SceneChangeNotify();
1401 bool SelectedFaces_empty(){
1402 return g_SelectedFaceInstances.empty();
1405 void SelectedFaces_pasteTexture(){
1406 UndoableCommand command( "facePasteTexture" );
1407 g_SelectedFaceInstances.foreach( FaceInstance_pasteTexture );
1412 void SurfaceInspector_constructPreferences( PreferencesPage& page ){
1413 page.appendCheckBox( "", "Surface Inspector Increments Match Grid", g_si_globals.m_bSnapTToGrid );
1415 void SurfaceInspector_constructPage( PreferenceGroup& group ){
1416 PreferencesPage page( group.createPage( "Surface Inspector", "Surface Inspector Preferences" ) );
1417 SurfaceInspector_constructPreferences( page );
1419 void SurfaceInspector_registerPreferencesPage(){
1420 PreferencesDialog_addSettingsPage( makeCallbackF(SurfaceInspector_constructPage) );
1423 void SurfaceInspector_registerCommands(){
1424 GlobalCommands_insert( "FitTexture", makeCallbackF(SurfaceInspector_FitTexture), Accelerator( 'B', (GdkModifierType)GDK_SHIFT_MASK ) );
1425 GlobalCommands_insert( "SurfaceInspector", makeCallbackF(SurfaceInspector_toggleShown), Accelerator( 'S' ) );
1427 GlobalCommands_insert( "FaceCopyTexture", makeCallbackF(SelectedFaces_copyTexture) );
1428 GlobalCommands_insert( "FacePasteTexture", makeCallbackF(SelectedFaces_pasteTexture) );
1432 #include "preferencesystem.h"
1435 void SurfaceInspector_Construct(){
1436 g_SurfaceInspector = new SurfaceInspector;
1438 SurfaceInspector_registerCommands();
1440 FaceTextureClipboard_setDefault();
1442 GlobalPreferenceSystem().registerPreference( "SurfaceWnd", make_property<WindowPositionTracker_String>( getSurfaceInspector().m_positionTracker) );
1443 GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Scale1", make_property_string( g_si_globals.scale[0] ) );
1444 GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Scale2", make_property_string( g_si_globals.scale[1] ) );
1445 GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Shift1", make_property_string( g_si_globals.shift[0] ) );
1446 GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Shift2", make_property_string( g_si_globals.shift[1] ) );
1447 GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Rotate", make_property_string( g_si_globals.rotate ) );
1448 GlobalPreferenceSystem().registerPreference( "SnapTToGrid", make_property_string( g_si_globals.m_bSnapTToGrid ) );
1450 typedef FreeCaller<void(const Selectable&), SurfaceInspector_SelectionChanged> SurfaceInspectorSelectionChangedCaller;
1451 GlobalSelectionSystem().addSelectionChangeCallback( SurfaceInspectorSelectionChangedCaller() );
1452 typedef FreeCaller<void(), SurfaceInspector_updateSelection> SurfaceInspectorUpdateSelectionCaller;
1453 Brush_addTextureChangedCallback( SurfaceInspectorUpdateSelectionCaller() );
1454 Patch_addTextureChangedCallback( SurfaceInspectorUpdateSelectionCaller() );
1456 SurfaceInspector_registerPreferencesPage();
1458 void SurfaceInspector_Destroy(){
1459 delete g_SurfaceInspector;
1466 namespace TexTool { // namespace hides these symbols from other object-files
1468 //Shamus: Textool functions, including GTK+ callbacks
1471 //NOTE: Black screen when TT first comes up is caused by an uninitialized Extent... !!! FIX !!!
1472 // But... You can see down below that it *is* initialized! WTF?
1475 float minX, minY, maxX, maxY;
1476 float width( void ) { return fabs( maxX - minX ); }
1477 float height( void ) { return fabs( maxY - minY ); }
1480 //This seems to control the texture scale... (Yep! ;-)
1481 Extent extents = { -2.0f, -2.0f, +2.0f, +2.0f };
1482 brushprimit_texdef_t tm; // Texture transform matrix
1483 Vector2 pts[c_brush_maxFaces];
1487 Vector2 textureSize;
1489 #define VP_PADDING 1.2
1490 #define PI 3.14159265358979
1491 bool lButtonDown = false;
1492 bool rButtonDown = false;
1495 bool haveAnchor = false;
1496 brushprimit_texdef_t currentBP;
1497 brushprimit_texdef_t origBP; // Original brush primitive (before we muck it up)
1498 float controlRadius = 5.0f;
1499 float rotationAngle = 0.0f;
1500 float rotationAngle2 = 0.0f;
1501 float oldRotationAngle;
1502 Vector2 rotationPoint;
1503 bool translatingX = false; // Widget state variables
1504 bool translatingY = false;
1505 bool scalingX = false;
1506 bool scalingY = false;
1507 bool rotating = false;
1508 bool resizingX = false; // Not sure what this means... :-/
1509 bool resizingY = false;
1510 float origAngle, origScaleX, origScaleY;
1514 // Function prototypes (move up to top later...)
1516 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius );
1519 void CopyPointsFromSelectedFace( void ){
1520 // Make sure that there's a face and winding to get!
1522 if ( g_SelectedFaceInstances.empty() ) {
1527 Face & face = g_SelectedFaceInstances.last().getFace();
1528 textureNum = face.getShader().m_state->getTexture().texture_number;
1529 textureSize.x() = face.getShader().m_state->getTexture().width;
1530 textureSize.y() = face.getShader().m_state->getTexture().height;
1531 //globalOutputStream() << "--> Texture #" << textureNum << ": " << textureSize.x() << " x " << textureSize.y() << "...\n";
1533 currentBP = SurfaceInspector_GetSelectedTexdef().m_brushprimit_texdef;
1535 face.EmitTextureCoordinates();
1536 Winding & w = face.getWinding();
1539 for ( Winding::const_iterator i = w.begin(); i != w.end(); i++ )
1541 //globalOutputStream() << (*i).texcoord.x() << " " << (*i).texcoord.y() << ", ";
1542 pts[count].x() = ( *i ).texcoord.x();
1543 pts[count].y() = ( *i ).texcoord.y();
1549 //globalOutputStream() << " ..copied points\n";
1552 brushprimit_texdef_t bp;
1553 //This approach is probably wrongheaded and just not right anyway. So, !!! FIX !!! [DONE]
1554 void CommitChanges( void ){
1555 texdef_t t; // Throwaway, since this is BP only
1557 bp.coords[0][0] = tm.coords[0][0] * origBP.coords[0][0] + tm.coords[0][1] * origBP.coords[1][0];
1558 bp.coords[0][1] = tm.coords[0][0] * origBP.coords[0][1] + tm.coords[0][1] * origBP.coords[1][1];
1559 bp.coords[0][2] = tm.coords[0][0] * origBP.coords[0][2] + tm.coords[0][1] * origBP.coords[1][2] + tm.coords[0][2];
1560 //Ok, this works for translation...
1561 // 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();
1562 bp.coords[1][0] = tm.coords[1][0] * origBP.coords[0][0] + tm.coords[1][1] * origBP.coords[1][0];
1563 bp.coords[1][1] = tm.coords[1][0] * origBP.coords[0][1] + tm.coords[1][1] * origBP.coords[1][1];
1564 bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2];
1565 // 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();
1567 //This doesn't work: g_brush_texture_changed();
1569 //Note: We should only set an undo *after* the button has been released... !!! FIX !!!
1570 //Definitely *should* have an undo, though!
1571 // UndoableCommand undo("textureProjectionSetSelected");
1572 Select_SetTexdef( TextureProjection( t, bp, Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) ) );
1573 //This is working, but for some reason the translate is causing the rest of the SI
1574 //widgets to yield bad readings... !!! FIX !!!
1575 //I.e., click on textool window, translate face wireframe, then controls go crazy. Dunno why.
1576 //It's because there were some uncommented out add/removeScale functions in brush.h and a
1577 //removeScale in brushmanip.cpp... :-/
1578 //Translate isn't working at all now... :-(
1579 //It's because we need to multiply in some scaling factor (prolly the texture width/height)
1583 void UpdateControlPoints( void ){
1586 // Init texture transform matrix
1588 tm.coords[0][0] = 1.0f; tm.coords[0][1] = 0.0f; tm.coords[0][2] = 0.0f;
1589 tm.coords[1][0] = 0.0f; tm.coords[1][1] = 1.0f; tm.coords[1][2] = 0.0f;
1594 For shifting we have:
1597 The code that should provide reasonable defaults, but doesn't for some reason:
1598 It's scaling the BP by 128 for some reason, between the time it's created and the
1599 time we get back to the SI widgets:
1601 static void OnBtnAxial(GtkWidget *widget, gpointer data)
1603 UndoableCommand undo("textureDefault");
1604 TextureProjection projection;
1605 TexDef_Construct_Default(projection);
1606 Select_SetTexdef(projection);
1609 Select_SetTexdef() calls Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection)
1610 which is in brushmanip.h: This eventually calls
1611 Texdef_Assign(m_texdef, texdef, m_brushprimit_texdef, brushprimit_texdef) in class Face...
1612 which just copies from brushpr to m_brushpr...
1615 //Small problem with this thing: It's scaled to the texture which is all screwed up... !!! FIX !!! [DONE]
1616 //Prolly should separate out the grid drawing so that we can draw it behind the polygon.
1617 const float gridWidth = 1.3f; // Let's try an absolute height... WORKS!!!
1618 // NOTE that 2.0 is the height of the viewport. Dunno why... Should make collision
1619 // detection easier...
1620 const float gridRadius = gridWidth * 0.5f;
1622 typedef const float WidgetColor[3];
1623 const WidgetColor widgetColor[10] = {
1624 { 1.0000f, 0.2000f, 0.0000f }, // Red
1625 { 0.9137f, 0.9765f, 0.4980f }, // Yellow
1626 { 0.0000f, 0.6000f, 0.3216f }, // Green
1627 { 0.6157f, 0.7726f, 0.8196f }, // Cyan
1628 { 0.4980f, 0.5000f, 0.4716f }, // Grey
1631 { 1.0000f, 0.6000f, 0.4000f }, // Light Red
1632 { 1.0000f, 1.0000f, 0.8980f }, // Light Yellow
1633 { 0.4000f, 1.0000f, 0.7216f }, // Light Green
1634 { 1.0000f, 1.0000f, 1.0000f }, // Light Cyan
1635 { 0.8980f, 0.9000f, 0.8716f } // Light Grey
1639 #define COLOR_YELLOW 1
1640 #define COLOR_GREEN 2
1641 #define COLOR_CYAN 3
1642 #define COLOR_GREY 4
1643 #define COLOR_LT_RED 5
1644 #define COLOR_LT_YELLOW 6
1645 #define COLOR_LT_GREEN 7
1646 #define COLOR_LT_CYAN 8
1647 #define COLOR_LT_GREY 9
1649 void DrawControlWidgets( void ){
1650 //Note that the grid should go *behind* the face outline... !!! FIX !!!
1652 float xStart = center.x() - ( gridWidth / 2.0f );
1653 float yStart = center.y() - ( gridWidth / 2.0f );
1654 float xScale = ( extents.height() / extents.width() ) * ( textureSize.y() / textureSize.x() );
1657 //Small problem with this approach: Changing the center point in the TX code doesn't seem to
1658 //change anything here--prolly because we load a new identity matrix. A couple of ways to fix
1659 //this would be to get rid of that code, or change the center to a new point by taking into
1660 //account the transforms that we toss with the new identity matrix. Dunno which is better.
1662 glScalef( xScale, 1.0, 1.0 ); // Will that square it up? Yup.
1663 glRotatef( static_cast<float>( radians_to_degrees( atan2( -currentBP.coords[0][1], currentBP.coords[0][0] ) ) ), 0.0, 0.0, -1.0 );
1664 glTranslatef( -center.x(), -center.y(), 0.0 );
1667 glColor3fv( translatingX && translatingY ? widgetColor[COLOR_LT_YELLOW] : widgetColor[COLOR_YELLOW] );
1668 glBegin( GL_LINE_LOOP );
1669 DrawCircularArc( center, 0, 2.0f * PI, gridRadius * 0.16 );
1674 glBegin( GL_LINES );
1675 glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1676 glVertex2f( center.x(), center.y() + ( gridRadius * 0.16 ) );
1677 glVertex2f( center.x(), center.y() + ( gridRadius * 1.00 ) );
1678 glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1679 glVertex2f( center.x() + ( gridRadius * 0.16 ), center.y() );
1680 glVertex2f( center.x() + ( gridRadius * 1.00 ), center.y() );
1684 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1685 glBegin( GL_TRIANGLES );
1686 glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1687 glVertex2f( center.x(), center.y() + ( gridRadius * 1.10 ) );
1688 glVertex2f( center.x() + ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1689 glVertex2f( center.x() - ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1690 glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1691 glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() );
1692 glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() + ( gridRadius * 0.06 ) );
1693 glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() - ( gridRadius * 0.06 ) );
1697 glBegin( GL_LINE_STRIP );
1698 glColor3fv( rotating ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1699 DrawCircularArc( center, 0.03f * PI, 0.47f * PI, gridRadius * 0.90 );
1703 glColor3fv( scalingY && !scalingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1704 glBegin( GL_LINES );
1705 glVertex2f( center.x() + ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1706 glVertex2f( center.x() - ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1708 glBegin( GL_LINE_LOOP );
1709 glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1710 glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1711 glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1712 glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1715 glColor3fv( scalingX && !scalingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1716 glBegin( GL_LINES );
1717 glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 0.20 ) );
1718 glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() - ( gridRadius * 0.20 ) );
1720 glBegin( GL_LINE_LOOP );
1721 glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 0.10 ) );
1722 glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() - ( gridRadius * 0.10 ) );
1723 glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() - ( gridRadius * 0.10 ) );
1724 glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 0.10 ) );
1727 glColor3fv( scalingX && scalingY ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1728 glBegin( GL_LINE_STRIP );
1729 glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.10 ) );
1730 glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.50 ) );
1731 glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() + ( gridRadius * 1.50 ) );
1733 glBegin( GL_LINE_LOOP );
1734 glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.40 ) );
1735 glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.20 ) );
1736 glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.20 ) );
1737 glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.40 ) );
1743 void DrawControlPoints( void ){
1744 glColor3f( 1, 1, 1 );
1745 glBegin( GL_LINE_LOOP );
1747 for ( int i = 0; i < numPts; i++ )
1748 glVertex2f( pts[i].x(), pts[i].y() );
1753 // Note: Setup and all that jazz must be done by the caller!
1755 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius ){
1756 float stepSize = ( 2.0f * PI ) / 200.0f;
1758 for ( float angle = startAngle; angle <= endAngle; angle += stepSize )
1759 glVertex2f( ctr.x() + radius * cos( angle ), ctr.y() + radius * sin( angle ) );
1764 if ( numPts == 0 ) {
1768 // Find selected texture's extents...
1770 extents.minX = extents.maxX = pts[0].x(),
1771 extents.minY = extents.maxY = pts[0].y();
1773 for ( int i = 1; i < numPts; i++ )
1775 if ( pts[i].x() < extents.minX ) {
1776 extents.minX = pts[i].x();
1778 if ( pts[i].x() > extents.maxX ) {
1779 extents.maxX = pts[i].x();
1781 if ( pts[i].y() < extents.minY ) {
1782 extents.minY = pts[i].y();
1784 if ( pts[i].y() > extents.maxY ) {
1785 extents.maxY = pts[i].y();
1789 // Do some viewport fitting stuff...
1791 //globalOutputStream() << "--> Center: " << center.x() << ", " << center.y() << "\n";
1792 //globalOutputStream() << "--> Extents (stage 1): " << extents.minX << ", "
1793 // << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1794 // TTimo: Apply a ratio to get the area we'll draw.
1795 center.x() = 0.5f * ( extents.minX + extents.maxX ),
1796 center.y() = 0.5f * ( extents.minY + extents.maxY );
1797 extents.minX = center.x() + VP_PADDING * ( extents.minX - center.x() ),
1798 extents.minY = center.y() + VP_PADDING * ( extents.minY - center.y() ),
1799 extents.maxX = center.x() + VP_PADDING * ( extents.maxX - center.x() ),
1800 extents.maxY = center.y() + VP_PADDING * ( extents.maxY - center.y() );
1801 //globalOutputStream() << "--> Extents (stage 2): " << extents.minX << ", "
1802 // << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1804 // TTimo: We want a texture with the same X / Y ratio.
1805 // TTimo: Compute XY space / window size ratio.
1806 float SSize = extents.width(), TSize = extents.height();
1807 float ratioX = textureSize.x() * extents.width() / windowSize.x(),
1808 ratioY = textureSize.y() * extents.height() / windowSize.y();
1809 //globalOutputStream() << "--> Texture size: " << textureSize.x() << ", " << textureSize.y() << "\n";
1810 //globalOutputStream() << "--> Window size: " << windowSize.x() << ", " << windowSize.y() << "\n";
1812 if ( ratioX > ratioY ) {
1813 TSize = ( windowSize.y() * ratioX ) / textureSize.y();
1814 // TSize = extents.width() * (windowSize.y() / windowSize.x()) * (textureSize.x() / textureSize.y());
1818 SSize = ( windowSize.x() * ratioY ) / textureSize.x();
1819 // SSize = extents.height() * (windowSize.x() / windowSize.y()) * (textureSize.y() / textureSize.x());
1822 extents.minX = center.x() - 0.5f * SSize, extents.maxX = center.x() + 0.5f * SSize,
1823 extents.minY = center.y() - 0.5f * TSize, extents.maxY = center.y() + 0.5f * TSize;
1824 //globalOutputStream() << "--> Extents (stage 3): " << extents.minX << ", "
1825 // << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1828 gboolean size_allocate( ui::Widget win, GtkAllocation * a, gpointer ){
1829 windowSize.x() = a->width;
1830 windowSize.y() = a->height;
1835 gboolean expose( ui::Widget win, GdkEventExpose * e, gpointer ){
1836 // globalOutputStream() << "--> Textool Window was exposed!\n";
1837 // globalOutputStream() << " (window width/height: " << cc << "/" << e->area.height << ")\n";
1839 // windowSize.x() = e->area.width, windowSize.y() = e->area.height;
1840 //This needs to go elsewhere...
1843 if ( glwidget_make_current( win ) == FALSE ) {
1844 globalOutputStream() << " FAILED to make current! Oh, the agony! :-(\n";
1848 CopyPointsFromSelectedFace();
1850 if ( !lButtonDown ) {
1854 // Probably should init button/anchor states here as well...
1855 // rotationAngle = 0.0f;
1856 glClearColor( 0, 0, 0, 0 );
1857 glViewport( 0, 0, e->area.width, e->area.height );
1858 glMatrixMode( GL_PROJECTION );
1862 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1863 glDisable( GL_DEPTH_TEST );
1864 glDisable( GL_BLEND );
1866 glOrtho( extents.minX, extents.maxX, extents.maxY, extents.minY, -1, 1 );
1868 glColor3f( 1, 1, 1 );
1869 // draw the texture background
1870 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1871 glBindTexture( GL_TEXTURE_2D, textureNum );
1873 glEnable( GL_TEXTURE_2D );
1874 glBegin( GL_QUADS );
1875 glTexCoord2f( extents.minX, extents.minY );
1876 glVertex2f( extents.minX, extents.minY );
1877 glTexCoord2f( extents.maxX, extents.minY );
1878 glVertex2f( extents.maxX, extents.minY );
1879 glTexCoord2f( extents.maxX, extents.maxY );
1880 glVertex2f( extents.maxX, extents.maxY );
1881 glTexCoord2f( extents.minX, extents.maxY );
1882 glVertex2f( extents.minX, extents.maxY );
1884 glDisable( GL_TEXTURE_2D );
1886 // draw the texture-space grid
1887 glColor3fv( widgetColor[COLOR_GREY] );
1888 glBegin( GL_LINES );
1890 const int gridSubdivisions = 8;
1891 const float gridExtents = 4.0f;
1893 for ( int i = 0; i < gridSubdivisions + 1; ++i )
1895 float y = i * ( gridExtents / float(gridSubdivisions) );
1896 float x = i * ( gridExtents / float(gridSubdivisions) );
1898 glVertex2f( gridExtents, y );
1900 glVertex2f( x, gridExtents );
1905 DrawControlPoints();
1906 DrawControlWidgets();
1908 // reset the current texture
1909 // glBindTexture(GL_TEXTURE_2D, 0);
1911 glwidget_swap_buffers( win );
1916 /*int FindSelectedPoint(int x, int y)
1918 for(int i=0; i<numPts; i++)
1920 int nx = (int)(windowSize.x() * (pts[i].x() - extents.minX) / extents.width());
1921 int ny = (int)(windowSize.y() * (pts[i].y() - extents.minY) / extents.height());
1923 if (abs(nx - x) <= 3 && abs(ny - y) <= 3)
1932 Vector2 dragPoint; // Defined in terms of window space (+x/-y)
1934 gboolean button_press( ui::Widget win, GdkEventButton * e, gpointer ){
1935 // globalOutputStream() << "--> Textool button press...\n";
1937 if ( e->button == 1 ) {
1939 GlobalUndoSystem().start();
1943 //globalOutputStream() << "--> Original BP: [" << origBP.coords[0][0] << "][" << origBP.coords[0][1] << "][" << origBP.coords[0][2] << "]\n";
1944 //globalOutputStream() << " [" << origBP.coords[1][0] << "][" << origBP.coords[1][1] << "][" << origBP.coords[1][2] << "]\n";
1945 //float angle = atan2(origBP.coords[0][1], origBP.coords[0][0]) * 180.0f / 3.141592653589f;
1946 origAngle = ( origBP.coords[0][1] > 0 ? PI : -PI ); // Could also be -PI... !!! FIX !!! [DONE]
1948 if ( origBP.coords[0][0] != 0.0f ) {
1949 origAngle = atan( origBP.coords[0][1] / origBP.coords[0][0] );
1952 origScaleX = origBP.coords[0][0] / cos( origAngle );
1953 origScaleY = origBP.coords[1][1] / cos( origAngle );
1954 rotationAngle = origAngle;
1955 oldCenter[0] = oldCenter[1] = 0;
1957 //globalOutputStream() << "--> BP stats: ang=" << origAngle * RAD_TO_DEG << ", scale=" << origScaleX << "/" << origScaleY << "\n";
1958 //Should also set the Flip X/Y checkboxes here as well... !!! FIX !!!
1959 //Also: should reverse texture left/right up/down instead of flipping the points...
1962 //float nx = windowSize.x() * (e->x - extents.minX) / (extents.maxX - extents.minX);
1963 //float ny = windowSize.y() * (e->y - extents.minY) / (extents.maxY - extents.minY);
1965 //But I want it to scroll the texture window, not the points... !!! FIX !!!
1966 //Actually, should scroll the texture window only when mouse is down on no widgets...
1967 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
1968 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
1969 trans.x() = -tm.coords[0][0] * nx - tm.coords[0][1] * ny;
1970 trans.y() = -tm.coords[1][0] * nx - tm.coords[1][1] * ny;
1972 dragPoint.x() = e->x, dragPoint.y() = e->y;
1973 trans2.x() = nx, trans2.y() = ny;
1974 oldRotationAngle = rotationAngle;
1975 // oldTrans.x() = tm.coords[0][2] - nx * textureSize.x();
1976 // oldTrans.y() = tm.coords[1][2] - ny * textureSize.y();
1977 oldTrans.x() = tm.coords[0][2];
1978 oldTrans.y() = tm.coords[1][2];
1979 oldCenter.x() = center.x();
1980 oldCenter.y() = center.y();
1986 /* else if (e->button == 3)
1991 //globalOutputStream() << "(" << (haveAnchor ? "anchor" : "released") << ")\n";
1996 gboolean button_release( ui::Widget win, GdkEventButton * e, gpointer ){
1997 // globalOutputStream() << "--> Textool button release...\n";
1999 if ( e->button == 1 ) {
2000 /* float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2001 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2003 //This prolly should go into the mouse move code...
2004 //Doesn't work correctly anyway...
2005 if (translatingX || translatingY)
2006 center.x() = ptx, center.y() = pty;//*/
2008 lButtonDown = false;
2010 if ( translatingX || translatingY ) {
2011 GlobalUndoSystem().finish( "translateTexture" );
2013 else if ( rotating ) {
2014 GlobalUndoSystem().finish( "rotateTexture" );
2016 else if ( scalingX || scalingY ) {
2017 GlobalUndoSystem().finish( "scaleTexture" );
2019 else if ( resizingX || resizingY ) {
2020 GlobalUndoSystem().finish( "resizeTexture" );
2024 GlobalUndoSystem().finish( "textoolUnknown" );
2027 rotating = translatingX = translatingY = scalingX = scalingY
2028 = resizingX = resizingY = false;
2032 else if ( e->button == 3 ) {
2033 rButtonDown = false;
2040 void C2DView::GridForWindow( float c[2], int x, int y)
2042 SpaceForWindow( c, x, y );
2045 c[0] /= m_GridStep[0];
2046 c[1] /= m_GridStep[1];
2047 c[0] = (float)floor( c[0] + 0.5f );
2048 c[1] = (float)floor( c[1] + 0.5f );
2049 c[0] *= m_GridStep[0];
2050 c[1] *= m_GridStep[1];
2052 void C2DView::SpaceForWindow( float c[2], int x, int y)
2054 c[0] = ((float)(x))/((float)(m_rect.right-m_rect.left))*(m_Maxs[0]-m_Mins[0])+m_Mins[0];
2055 c[1] = ((float)(y))/((float)(m_rect.bottom-m_rect.top))*(m_Maxs[1]-m_Mins[1])+m_Mins[1];
2058 gboolean motion( ui::Widget win, GdkEventMotion * e, gpointer ){
2059 // globalOutputStream() << "--> Textool motion...\n";
2061 if ( lButtonDown ) {
2062 if ( translatingX || translatingY ) {
2063 float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2064 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2066 //Need to fix this to take the rotation angle into account, so that it moves along
2067 //the rotated X/Y axis...
2068 if ( translatingX ) {
2069 // tm.coords[0][2] = (trans.x() + ptx) * textureSize.x();
2070 //This works, but only when the angle is zero. !!! FIX !!! [DONE]
2071 // tm.coords[0][2] = oldCenter.x() + (ptx * textureSize.x());
2072 tm.coords[0][2] = oldTrans.x() + ( ptx - trans2.x() ) * textureSize.x();
2073 // center.x() = oldCenter.x() + (ptx - trans2.x());
2076 if ( translatingY ) {
2077 // tm.coords[1][2] = (trans.y() + pty) * textureSize.y();
2078 // tm.coords[1][2] = oldCenter.y() + (pty * textureSize.y());
2079 tm.coords[1][2] = oldTrans.y() + ( pty - trans2.y() ) * textureSize.y();
2080 // center.y() = oldCenter.y() + (pty - trans2.y());
2083 //Need to update center.x/y() so that the widget translates as well. Also, oldCenter
2084 //is badly named... Should be oldTrans or something like that... !!! FIX !!!
2085 //Changing center.x/y() here doesn't seem to change anything... :-/
2086 UpdateControlPoints();
2088 else if ( rotating ) {
2089 // Shamus: New rotate code
2090 int cx = (int)( windowSize.x() * ( center.x() - extents.minX ) / extents.width() );
2091 int cy = (int)( windowSize.y() * ( center.y() - extents.minY ) / extents.height() );
2092 Vector3 v1( dragPoint.x() - cx, dragPoint.y() - cy, 0 ), v2( e->x - cx, e->y - cy, 0 );
2094 vector3_normalise( v1 );
2095 vector3_normalise( v2 );
2096 float c = vector3_dot( v1, v2 );
2097 Vector3 cross = vector3_cross( v1, v2 );
2098 float s = vector3_length( cross );
2100 if ( cross[2] > 0 ) {
2104 // Problem with this: arcsin/cos seems to only return -90 to 90 and 0 to 180...
2105 // Can't derive angle from that!
2107 //rotationAngle = asin(s);// * 180.0f / 3.141592653589f;
2108 rotationAngle = acos( c );
2109 //rotationAngle2 = asin(s);
2110 if ( cross[2] < 0 ) {
2111 rotationAngle = -rotationAngle;
2114 //NO! DOESN'T WORK! rotationAngle -= 45.0f * DEG_TO_RAD;
2117 /*c = cos(rotationAngle - oldRotationAngle);
2118 s = sin(rotationAngle - oldRotationAngle);
2119 rotationAngle += oldRotationAngle;
2120 //c += cos(oldRotationAngle);
2121 //s += sin(oldRotationAngle);
2122 //rotationAngle += oldRotationAngle;
2125 //rotationAngle %= 2.0 * PI;//*/
2127 //This is wrong... Hmm...
2128 //It seems to shear the texture instead of rotating it... !!! FIX !!!
2129 // Now it rotates correctly. Seems TTimo was overcomplicating things here... ;-)
2131 // Seems like what needs to happen here is multiplying these rotations by tm... !!! FIX !!!
2133 // See brush_primit.cpp line 244 (Texdef_EmitTextureCoordinates()) for where texcoords come from...
2135 tm.coords[0][0] = c;
2136 tm.coords[0][1] = s;
2137 tm.coords[1][0] = -s;
2138 tm.coords[1][1] = c;
2139 //It doesn't work anymore... Dunno why...
2140 //tm.coords[0][2] = -trans.x(); // This works!!! Yeah!!!
2141 //tm.coords[1][2] = -trans.y();
2143 //tm.coords[0][2] = rotationPoint.x(); // This works, but strangely...
2144 //tm.coords[1][2] = rotationPoint.y();
2145 //tm.coords[0][2] = 0;// center.x() / 2.0f;
2146 //tm.coords[1][2] = 0;// center.y() / 2.0f;
2148 //tm.coords[0][2] = -(center.x() * textureSize.x());
2149 //tm.coords[1][2] = -(center.y() * textureSize.y());
2150 //Eh? No, but seems to be getting closer...
2151 /*float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2152 float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2153 tm.coords[0][2] = -c * center.x() - s * center.y() + ptx;
2154 tm.coords[1][2] = s * center.x() - c * center.x() + pty;//*/
2155 //Kinda works, but center drifts around on non-square textures...
2156 /*tm.coords[0][2] = (-c * center.x() - s * center.y()) * textureSize.x();
2157 tm.coords[1][2] = ( s * center.x() - c * center.y()) * textureSize.y();//*/
2158 //Rotates correctly, but not around the actual center of the face's points...
2159 /*tm.coords[0][2] = -c * center.x() * textureSize.x() - s * center.y() * textureSize.y();
2160 tm.coords[1][2] = s * center.x() * textureSize.x() - c * center.y() * textureSize.y();//*/
2162 tm.coords[0][2] = ( -c * center.x() * textureSize.x() - s * center.y() * textureSize.y() ) + center.x() * textureSize.x();
2163 tm.coords[1][2] = ( s * center.x() * textureSize.x() - c * center.y() * textureSize.y() ) + center.y() * textureSize.y(); //*/
2164 //This doesn't work...
2165 //And this is the wrong place for this anyway (I'm pretty sure).
2166 /*tm.coords[0][2] += oldCenter.x();
2167 tm.coords[1][2] += oldCenter.y();//*/
2168 UpdateControlPoints(); // will cause a redraw
2173 else // Check for widget mouseovers
2176 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2177 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2178 // Translate nx/y to the "center" point...
2181 ny = -ny; // Flip Y-axis so that increasing numbers move up
2183 tran.x() = tm.coords[0][0] * nx + tm.coords[0][1] * ny;
2184 tran.y() = tm.coords[1][0] * nx + tm.coords[1][1] * ny;
2185 //This doesn't seem to generate a valid distance from the center--for some reason it
2186 //calculates a fixed number every time
2187 //Look at nx/y above: they're getting fixed there! !!! FIX !!! [DONE]
2188 float dist = sqrt( ( nx * nx ) + ( ny * ny ) );
2189 // Normalize to the 2.0 = height standard (for now)
2190 //globalOutputStream() << "--> Distance before: " << dist;
2191 dist = dist * 2.0f / extents.height();
2192 //globalOutputStream() << ". After: " << dist;
2193 tran.x() = tran.x() * 2.0f / extents.height();
2194 tran.y() = tran.y() * 2.0f / extents.height();
2195 //globalOutputStream() << ". Trans: " << tran.x() << ", " << tran.y() << "\n";
2197 //Let's try this instead...
2198 //Interesting! It seems that e->x/y are rotated
2199 //(no, they're not--the TM above is what's doing it...)
2200 nx = ( ( e->x / windowSize.y() ) * 2.0f ) - ( windowSize.x() / windowSize.y() );
2201 ny = ( ( e->y / windowSize.y() ) * 2.0f ) - ( windowSize.y() / windowSize.y() );
2203 //Cool! It works! Now just need to do rotation...
2205 rotating = translatingX = translatingY = scalingX = scalingY
2206 = resizingX = resizingY = false;
2208 if ( dist < ( gridRadius * 0.16f ) ) {
2209 translatingX = translatingY = true;
2211 else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2212 && fabs( ny ) < ( gridRadius * 0.05f ) && nx > 0 ) {
2213 translatingX = true;
2215 else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2216 && fabs( nx ) < ( gridRadius * 0.05f ) && ny > 0 ) {
2217 translatingY = true;
2219 // Should tighten up the angle on this, or put this test after the axis tests...
2220 else if ( tran.x() > 0 && tran.y() > 0
2221 && ( dist > ( gridRadius * 0.82f ) && dist < ( gridRadius * 0.98f ) ) ) {
2233 //It seems the fake tex coords conversion is screwing this stuff up... !!! FIX !!!
2234 //This is still wrong... Prolly need to do something with the oldScaleX/Y stuff...
2235 void flipX( ui::ToggleButton, gpointer ){
2236 // globalOutputStream() << "--> Flip X...\n";
2238 // SurfaceInspector_GetSelectedBPTexdef(); // Refresh g_selectedBrushPrimitTexdef...
2239 // tm.coords[0][0] = -tm.coords[0][0];
2240 // tm.coords[1][0] = -tm.coords[1][0];
2241 // tm.coords[0][0] = -tm.coords[0][0]; // This should be correct now...Nope.
2242 // tm.coords[1][1] = -tm.coords[1][1];
2243 tm.coords[0][0] = -tm.coords[0][0]; // This should be correct now...
2244 tm.coords[1][0] = -tm.coords[1][0];
2245 // tm.coords[2][0] = -tm.coords[2][0];//wil wok? no.
2246 UpdateControlPoints();
2249 void flipY( ui::ToggleButton, gpointer ){
2250 // globalOutputStream() << "--> Flip Y...\n";
2251 // tm.coords[0][1] = -tm.coords[0][1];
2252 // tm.coords[1][1] = -tm.coords[1][1];
2253 // tm.coords[0][1] = -tm.coords[0][1]; // This should be correct now...Nope.
2254 // tm.coords[1][0] = -tm.coords[1][0];
2255 tm.coords[0][1] = -tm.coords[0][1]; // This should be correct now...
2256 tm.coords[1][1] = -tm.coords[1][1];
2257 // tm.coords[2][1] = -tm.coords[2][1];//wil wok? no.
2258 UpdateControlPoints();
2261 } // end namespace TexTool