]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/surfacedialog.cpp
22e83b27e7db3c64029eef5a1fdfdefaab7ed361
[xonotic/netradiant.git] / radiant / surfacedialog.cpp
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22 //
23 // Surface Dialog
24 //
25 // Leonardo Zide (leo@lokigames.com)
26 //
27
28 #include "surfacedialog.h"
29
30 #include <gtk/gtk.h>
31
32 #include "debugging/debugging.h"
33 #include "warnings.h"
34
35 #include "iscenegraph.h"
36 #include "itexdef.h"
37 #include "iundo.h"
38 #include "iselection.h"
39
40 #include <gdk/gdkkeysyms.h>
41
42 #include "signal/isignal.h"
43 #include "generic/object.h"
44 #include "math/vector.h"
45 #include "texturelib.h"
46 #include "shaderlib.h"
47 #include "stringio.h"
48
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"
56 #include "map.h"
57 #include "select.h"
58 #include "patchmanip.h"
59 #include "brushmanip.h"
60 #include "patchdialog.h"
61 #include "preferences.h"
62 #include "brush_primit.h"
63 #include "xywindow.h"
64 #include "mainframe.h"
65 #include "gtkdlgs.h"
66 #include "dialog.h"
67 #include "brush.h"              //Shamus: for Textool
68 #include "patch.h"
69 #include "commands.h"
70 #include "stream/stringstream.h"
71 #include "grid.h"
72 #include "textureentry.h"
73
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. ;-)
77
78 #define TEXTOOL_ENABLED 0
79
80 #if TEXTOOL_ENABLED
81
82 namespace TexTool
83 {
84
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( GtkToggleButton *, gpointer );
92 void flipY( GtkToggleButton *, gpointer );
93
94 //End Textool function prototypes
95
96 //Shamus: Textool globals
97 ui::Widget g_textoolWin;
98 //End Textool globals
99
100 void queueDraw(){
101         gtk_widget_queue_draw( g_textoolWin );
102 }
103
104 }
105
106 #endif
107
108 inline void spin_button_set_step( GtkSpinButton* spin, gfloat step ){
109 #if 1
110     gtk_adjustment_set_step_increment(gtk_spin_button_get_adjustment( spin ), step);
111 #else
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 );
116 #endif
117 }
118
119 class Increment
120 {
121 float& m_f;
122 public:
123 GtkSpinButton* m_spin;
124 ui::Entry m_entry;
125 Increment( float& f ) : m_f( f ), m_spin( 0 ), m_entry( ui::null ){
126 }
127 void cancel(){
128         entry_set_float( m_entry, m_f );
129 }
130 typedef MemberCaller<Increment, &Increment::cancel> CancelCaller;
131 void apply(){
132         m_f = static_cast<float>( entry_get_float( m_entry ) );
133         spin_button_set_step( m_spin, m_f );
134 }
135 typedef MemberCaller<Increment, &Increment::apply> ApplyCaller;
136 };
137
138 void SurfaceInspector_GridChange();
139
140 class SurfaceInspector : public Dialog
141 {
142 ui::Window BuildDialog();
143
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;
155
156 IdleDraw m_idleDraw;
157
158 GtkCheckButton* m_surfaceFlags[32];
159 GtkCheckButton* m_contentFlags[32];
160
161 NonModalEntry m_valueEntry;
162 ui::Entry m_valueEntryWidget{ui::null};
163 public:
164 WindowPositionTracker m_positionTracker;
165 WindowPositionTrackerImportStringCaller m_importPosition;
166 WindowPositionTrackerExportStringCaller m_exportPosition;
167
168 // Dialog Data
169 float m_fitHorizontal;
170 float m_fitVertical;
171
172 Increment m_hshiftIncrement;
173 Increment m_vshiftIncrement;
174 Increment m_hscaleIncrement;
175 Increment m_vscaleIncrement;
176 Increment m_rotateIncrement;
177 ui::Entry m_texture{ui::null};
178
179 SurfaceInspector() :
180         m_textureEntry( ApplyShaderCaller( *this ), UpdateCaller( *this ) ),
181         m_hshiftSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
182         m_hshiftEntry( Increment::ApplyCaller( m_hshiftIncrement ), Increment::CancelCaller( m_hshiftIncrement ) ),
183         m_vshiftSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
184         m_vshiftEntry( Increment::ApplyCaller( m_vshiftIncrement ), Increment::CancelCaller( m_vshiftIncrement ) ),
185         m_hscaleSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
186         m_hscaleEntry( Increment::ApplyCaller( m_hscaleIncrement ), Increment::CancelCaller( m_hscaleIncrement ) ),
187         m_vscaleSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
188         m_vscaleEntry( Increment::ApplyCaller( m_vscaleIncrement ), Increment::CancelCaller( m_vscaleIncrement ) ),
189         m_rotateSpinner( ApplyTexdefCaller( *this ), UpdateCaller( *this ) ),
190         m_rotateEntry( Increment::ApplyCaller( m_rotateIncrement ), Increment::CancelCaller( m_rotateIncrement ) ),
191         m_idleDraw( UpdateCaller( *this ) ),
192         m_valueEntry( ApplyFlagsCaller( *this ), UpdateCaller( *this ) ),
193         m_importPosition( m_positionTracker ),
194         m_exportPosition( m_positionTracker ),
195         m_hshiftIncrement( g_si_globals.shift[0] ),
196         m_vshiftIncrement( g_si_globals.shift[1] ),
197         m_hscaleIncrement( g_si_globals.scale[0] ),
198         m_vscaleIncrement( g_si_globals.scale[1] ),
199         m_rotateIncrement( g_si_globals.rotate ){
200         m_fitVertical = 1;
201         m_fitHorizontal = 1;
202         m_positionTracker.setPosition( c_default_window_pos );
203 }
204
205 void constructWindow( ui::Window main_window ){
206         m_parent = main_window;
207         Create();
208         AddGridChangeCallback( FreeCaller<SurfaceInspector_GridChange>() );
209 }
210 void destroyWindow(){
211         Destroy();
212 }
213 bool visible() {
214         return GetWidget().visible();
215 }
216 void queueDraw(){
217         if ( visible() ) {
218                 m_idleDraw.queueDraw();
219         }
220 }
221
222 void Update();
223 typedef MemberCaller<SurfaceInspector, &SurfaceInspector::Update> UpdateCaller;
224 void ApplyShader();
225 typedef MemberCaller<SurfaceInspector, &SurfaceInspector::ApplyShader> ApplyShaderCaller;
226 void ApplyTexdef();
227 typedef MemberCaller<SurfaceInspector, &SurfaceInspector::ApplyTexdef> ApplyTexdefCaller;
228 void ApplyFlags();
229 typedef MemberCaller<SurfaceInspector, &SurfaceInspector::ApplyFlags> ApplyFlagsCaller;
230 };
231
232 namespace
233 {
234 SurfaceInspector* g_SurfaceInspector;
235
236 inline SurfaceInspector& getSurfaceInspector(){
237         ASSERT_NOTNULL( g_SurfaceInspector );
238         return *g_SurfaceInspector;
239 }
240 }
241
242 void SurfaceInspector_constructWindow( ui::Window main_window ){
243         getSurfaceInspector().constructWindow( main_window );
244 }
245 void SurfaceInspector_destroyWindow(){
246         getSurfaceInspector().destroyWindow();
247 }
248
249 void SurfaceInspector_queueDraw(){
250         getSurfaceInspector().queueDraw();
251 }
252
253 namespace
254 {
255 CopiedString g_selectedShader;
256 TextureProjection g_selectedTexdef;
257 ContentsFlagsValue g_selectedFlags;
258 size_t g_selectedShaderSize[2];
259 }
260
261 void SurfaceInspector_SetSelectedShader( const char* shader ){
262         g_selectedShader = shader;
263         SurfaceInspector_queueDraw();
264 }
265
266 void SurfaceInspector_SetSelectedTexdef( const TextureProjection& projection ){
267         g_selectedTexdef = projection;
268         SurfaceInspector_queueDraw();
269 }
270
271 void SurfaceInspector_SetSelectedFlags( const ContentsFlagsValue& flags ){
272         g_selectedFlags = flags;
273         SurfaceInspector_queueDraw();
274 }
275
276 static bool s_texture_selection_dirty = false;
277
278 void SurfaceInspector_updateSelection(){
279         s_texture_selection_dirty = true;
280         SurfaceInspector_queueDraw();
281
282 #if TEXTOOL_ENABLED
283         if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
284                 TexTool::queueDraw();
285                 //globalOutputStream() << "textool texture changed..\n";
286         }
287 #endif
288 }
289
290 void SurfaceInspector_SelectionChanged( const Selectable& selectable ){
291         SurfaceInspector_updateSelection();
292 }
293
294 void SurfaceInspector_SetCurrent_FromSelected(){
295         if ( s_texture_selection_dirty == true ) {
296                 s_texture_selection_dirty = false;
297                 if ( !g_SelectedFaceInstances.empty() ) {
298                         TextureProjection projection;
299 //This *may* be the point before it fucks up... Let's see!
300 //Yep, there was a call to removeScale in there...
301                         Scene_BrushGetTexdef_Component_Selected( GlobalSceneGraph(), projection );
302
303                         SurfaceInspector_SetSelectedTexdef( projection );
304
305                         Scene_BrushGetShaderSize_Component_Selected( GlobalSceneGraph(), g_selectedShaderSize[0], g_selectedShaderSize[1] );
306                         g_selectedTexdef.m_brushprimit_texdef.coords[0][2] = float_mod( g_selectedTexdef.m_brushprimit_texdef.coords[0][2], (float)g_selectedShaderSize[0] );
307                         g_selectedTexdef.m_brushprimit_texdef.coords[1][2] = float_mod( g_selectedTexdef.m_brushprimit_texdef.coords[1][2], (float)g_selectedShaderSize[1] );
308
309                         CopiedString name;
310                         Scene_BrushGetShader_Component_Selected( GlobalSceneGraph(), name );
311                         if ( string_not_empty( name.c_str() ) ) {
312                                 SurfaceInspector_SetSelectedShader( name.c_str() );
313                         }
314
315                         ContentsFlagsValue flags;
316                         Scene_BrushGetFlags_Component_Selected( GlobalSceneGraph(), flags );
317                         SurfaceInspector_SetSelectedFlags( flags );
318                 }
319                 else
320                 {
321                         TextureProjection projection;
322                         Scene_BrushGetTexdef_Selected( GlobalSceneGraph(), projection );
323                         SurfaceInspector_SetSelectedTexdef( projection );
324
325                         CopiedString name;
326                         Scene_BrushGetShader_Selected( GlobalSceneGraph(), name );
327                         if ( string_empty( name.c_str() ) ) {
328                                 Scene_PatchGetShader_Selected( GlobalSceneGraph(), name );
329                         }
330                         if ( string_not_empty( name.c_str() ) ) {
331                                 SurfaceInspector_SetSelectedShader( name.c_str() );
332                         }
333
334                         ContentsFlagsValue flags( 0, 0, 0, false );
335                         Scene_BrushGetFlags_Selected( GlobalSceneGraph(), flags );
336                         SurfaceInspector_SetSelectedFlags( flags );
337                 }
338         }
339 }
340
341 const char* SurfaceInspector_GetSelectedShader(){
342         SurfaceInspector_SetCurrent_FromSelected();
343         return g_selectedShader.c_str();
344 }
345
346 const TextureProjection& SurfaceInspector_GetSelectedTexdef(){
347         SurfaceInspector_SetCurrent_FromSelected();
348         return g_selectedTexdef;
349 }
350
351 const ContentsFlagsValue& SurfaceInspector_GetSelectedFlags(){
352         SurfaceInspector_SetCurrent_FromSelected();
353         return g_selectedFlags;
354 }
355
356
357
358 /*
359    ===================================================
360
361    SURFACE INSPECTOR
362
363    ===================================================
364  */
365
366 si_globals_t g_si_globals;
367
368
369 // make the shift increments match the grid settings
370 // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size
371 // this depends on a scale value if you have selected a particular texture on which you want it to work:
372 // we move the textures in pixels, not world units. (i.e. increment values are in pixel)
373 // depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize()
374 // increment * scale = gridsize
375 // hscale and vscale are optional parameters, if they are zero they will be set to the default scale
376 // NOTE: the default scale depends if you are using BP mode or regular.
377 // For regular it's 0.5f (128 pixels cover 64 world units), for BP it's simply 1.0f
378 // see fenris #2810
379 void DoSnapTToGrid( float hscale, float vscale ){
380         g_si_globals.shift[0] = static_cast<float>( float_to_integer( static_cast<float>( GetGridSize() ) / hscale ) );
381         g_si_globals.shift[1] = static_cast<float>( float_to_integer( static_cast<float>( GetGridSize() ) / vscale ) );
382         getSurfaceInspector().queueDraw();
383 }
384
385 void SurfaceInspector_GridChange(){
386         if ( g_si_globals.m_bSnapTToGrid ) {
387                 DoSnapTToGrid( Texdef_getDefaultTextureScale(), Texdef_getDefaultTextureScale() );
388         }
389 }
390
391 // make the shift increments match the grid settings
392 // the objective being that the shift+arrows shortcuts move the texture by the corresponding grid size
393 // this depends on the current texture scale used?
394 // we move the textures in pixels, not world units. (i.e. increment values are in pixel)
395 // depending on the texture scale it doesn't take the same amount of pixels to move of GetGridSize()
396 // increment * scale = gridsize
397 static void OnBtnMatchGrid( ui::Widget widget, gpointer data ){
398         float hscale, vscale;
399         hscale = static_cast<float>( gtk_spin_button_get_value( getSurfaceInspector().m_hscaleIncrement.m_spin ) );
400         vscale = static_cast<float>( gtk_spin_button_get_value( getSurfaceInspector().m_vscaleIncrement.m_spin ) );
401
402         if ( hscale == 0.0f || vscale == 0.0f ) {
403                 globalOutputStream() << "ERROR: unexpected scale == 0.0f\n";
404                 return;
405         }
406
407         DoSnapTToGrid( hscale, vscale );
408 }
409
410 // DoSurface will always try to show the surface inspector
411 // or update it because something new has been selected
412 // Shamus: It does get called when the SI is hidden, but not when you select something new. ;-)
413 void DoSurface( void ){
414         if ( !getSurfaceInspector().GetWidget() ) {
415                 getSurfaceInspector().Create();
416
417         }
418         getSurfaceInspector().Update();
419         getSurfaceInspector().importData();
420         getSurfaceInspector().ShowDlg();
421 }
422
423 void SurfaceInspector_toggleShown(){
424         if ( getSurfaceInspector().visible() ) {
425                 getSurfaceInspector().HideDlg();
426         }
427         else
428         {
429                 DoSurface();
430         }
431 }
432
433 void SurfaceInspector_FitTexture(){
434         UndoableCommand undo( "textureAutoFit" );
435         Select_FitTexture( getSurfaceInspector().m_fitHorizontal, getSurfaceInspector().m_fitVertical );
436 }
437
438 static void OnBtnPatchdetails( ui::Widget widget, gpointer data ){
439         Patch_CapTexture();
440 }
441
442 static void OnBtnPatchnatural( ui::Widget widget, gpointer data ){
443         Patch_NaturalTexture();
444 }
445
446 static void OnBtnPatchreset( ui::Widget widget, gpointer data ){
447         Patch_ResetTexture();
448 }
449
450 static void OnBtnPatchFit( ui::Widget widget, gpointer data ){
451         Patch_FitTexture();
452 }
453
454 static void OnBtnAxial( ui::Widget widget, gpointer data ){
455 //globalOutputStream() << "--> [OnBtnAxial]...\n";
456         UndoableCommand undo( "textureDefault" );
457         TextureProjection projection;
458 //globalOutputStream() << "    TexDef_Construct_Default()...\n";
459         TexDef_Construct_Default( projection );
460 //globalOutputStream() << "    Select_SetTexdef()...\n";
461
462 #if TEXTOOL_ENABLED
463
464         //Shamus:
465         if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
466                 // Scale up texture width/height if in BP mode...
467 //NOTE: This may not be correct any more! :-P
468                 if ( !g_SelectedFaceInstances.empty() ) {
469                         Face & face = g_SelectedFaceInstances.last().getFace();
470                         float x = face.getShader().m_state->getTexture().width;
471                         float y = face.getShader().m_state->getTexture().height;
472                         projection.m_brushprimit_texdef.coords[0][0] /= x;
473                         projection.m_brushprimit_texdef.coords[0][1] /= y;
474                         projection.m_brushprimit_texdef.coords[1][0] /= x;
475                         projection.m_brushprimit_texdef.coords[1][1] /= y;
476                 }
477         }
478 #endif
479
480         Select_SetTexdef( projection );
481 }
482
483 static void OnBtnFaceFit( ui::Widget widget, gpointer data ){
484         getSurfaceInspector().exportData();
485         SurfaceInspector_FitTexture();
486 }
487
488 typedef const char* FlagName;
489
490 const FlagName surfaceflagNamesDefault[32] = {
491         "surf1",
492         "surf2",
493         "surf3",
494         "surf4",
495         "surf5",
496         "surf6",
497         "surf7",
498         "surf8",
499         "surf9",
500         "surf10",
501         "surf11",
502         "surf12",
503         "surf13",
504         "surf14",
505         "surf15",
506         "surf16",
507         "surf17",
508         "surf18",
509         "surf19",
510         "surf20",
511         "surf21",
512         "surf22",
513         "surf23",
514         "surf24",
515         "surf25",
516         "surf26",
517         "surf27",
518         "surf28",
519         "surf29",
520         "surf30",
521         "surf31",
522         "surf32"
523 };
524
525 const FlagName contentflagNamesDefault[32] = {
526         "cont1",
527         "cont2",
528         "cont3",
529         "cont4",
530         "cont5",
531         "cont6",
532         "cont7",
533         "cont8",
534         "cont9",
535         "cont10",
536         "cont11",
537         "cont12",
538         "cont13",
539         "cont14",
540         "cont15",
541         "cont16",
542         "cont17",
543         "cont18",
544         "cont19",
545         "cont20",
546         "cont21",
547         "cont22",
548         "cont23",
549         "cont24",
550         "cont25",
551         "cont26",
552         "cont27",
553         "cont28",
554         "cont29",
555         "cont30",
556         "cont31",
557         "cont32"
558 };
559
560 const char* getSurfaceFlagName( std::size_t bit ){
561         const char* value = g_pGameDescription->getKeyValue( surfaceflagNamesDefault[bit] );
562         if ( string_empty( value ) ) {
563                 return surfaceflagNamesDefault[bit];
564         }
565         return value;
566 }
567
568 const char* getContentFlagName( std::size_t bit ){
569         const char* value = g_pGameDescription->getKeyValue( contentflagNamesDefault[bit] );
570         if ( string_empty( value ) ) {
571                 return contentflagNamesDefault[bit];
572         }
573         return value;
574 }
575
576
577 // =============================================================================
578 // SurfaceInspector class
579
580 guint togglebutton_connect_toggled( GtkToggleButton* button, const Callback& callback ){
581         return g_signal_connect_swapped( G_OBJECT( button ), "toggled", G_CALLBACK( callback.getThunk() ), callback.getEnvironment() );
582 }
583
584 ui::Window SurfaceInspector::BuildDialog(){
585         ui::Window window = ui::Window(create_floating_window( "Surface Inspector", m_parent ));
586
587         m_positionTracker.connect( window );
588
589         global_accel_connect_window( window );
590
591         window_connect_focus_in_clear_focus_widget( window );
592
593
594         {
595                 // replaced by only the vbox:
596                 auto vbox = ui::VBox( FALSE, 5 );
597                 vbox.show();
598                 window.add(vbox);
599                 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
600
601                 {
602                         auto hbox2 = ui::HBox( FALSE, 5 );
603                         hbox2.show();
604                         vbox.pack_start( hbox2, FALSE, FALSE, 0 );
605
606                         {
607                                 ui::Widget label = ui::Label( "Texture" );
608                                 label.show();
609                                 hbox2.pack_start( label, FALSE, TRUE, 0 );
610                         }
611                         {
612                                 auto entry = ui::Entry(ui::New);
613                                 entry.show();
614                                 hbox2.pack_start( entry, TRUE, TRUE, 0 );
615                                 m_texture = entry;
616                                 m_textureEntry.connect( entry );
617                                 GlobalTextureEntryCompletion::instance().connect( entry );
618                         }
619                 }
620
621
622                 {
623                         auto table = ui::Table(6, 4, FALSE);
624                         table.show();
625                         vbox.pack_start( table, FALSE, FALSE, 0 );
626                         gtk_table_set_row_spacings(table, 5);
627                         gtk_table_set_col_spacings(table, 5);
628                         {
629                                 ui::Widget label = ui::Label( "Horizontal shift" );
630                                 label.show();
631                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
632                                 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
633                         }
634                         {
635                                 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 2 );
636                                 m_hshiftIncrement.m_spin = spin;
637                                 m_hshiftSpinner.connect( spin );
638                                 spin.show();
639                                 table.attach(spin, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
640                 spin.dimensions(60, -1);
641                         }
642                         {
643                                 ui::Widget label = ui::Label( "Step" );
644                                 label.show();
645                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
646                                 table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 0});
647                         }
648                         {
649                                 auto entry = ui::Entry(ui::New);
650                                 entry.show();
651                                 table.attach(entry, {3, 4, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
652                 entry.dimensions(50, -1);
653                                 m_hshiftIncrement.m_entry = entry;
654                                 m_hshiftEntry.connect( entry );
655                         }
656                         {
657                                 ui::Widget label = ui::Label( "Vertical shift" );
658                                 label.show();
659                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
660                                 table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
661                         }
662                         {
663                                 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 2 );
664                                 m_vshiftIncrement.m_spin = spin;
665                                 m_vshiftSpinner.connect( spin );
666                                 spin.show();
667                                 table.attach(spin, {1, 2, 1, 2}, {GTK_FILL, 0});
668                 spin.dimensions(60, -1);
669                         }
670                         {
671                                 ui::Widget label = ui::Label( "Step" );
672                                 label.show();
673                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
674                                 table.attach(label, {2, 3, 1, 2}, {GTK_FILL, 0});
675                         }
676                         {
677                                 auto entry = ui::Entry(ui::New);
678                                 entry.show();
679                                 table.attach(entry, {3, 4, 1, 2}, {GTK_FILL, 0});
680                 entry.dimensions(50, -1);
681                                 m_vshiftIncrement.m_entry = entry;
682                                 m_vshiftEntry.connect( entry );
683                         }
684                         {
685                                 ui::Widget label = ui::Label( "Horizontal stretch" );
686                                 label.show();
687                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
688                                 table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0});
689                         }
690                         {
691                                 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 5 );
692                                 m_hscaleIncrement.m_spin = spin;
693                                 m_hscaleSpinner.connect( spin );
694                                 spin.show();
695                                 table.attach(spin, {1, 2, 2, 3}, {GTK_FILL, 0});
696                 spin.dimensions(60, -1);
697                         }
698                         {
699                                 ui::Widget label = ui::Label( "Step" );
700                                 label.show();
701                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
702                                 table.attach(label, {2, 3, 2, 3}, {GTK_FILL, 0});
703                         }
704                         {
705                                 auto entry = ui::Entry(ui::New);
706                                 entry.show();
707                                 table.attach(entry, {3, 4, 2, 3}, {GTK_FILL, 0});
708                 entry.dimensions(50, -1);
709                                 m_hscaleIncrement.m_entry = entry;
710                                 m_hscaleEntry.connect( entry );
711                         }
712                         {
713                                 ui::Widget label = ui::Label( "Vertical stretch" );
714                                 label.show();
715                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
716                                 table.attach(label, {0, 1, 3, 4}, {GTK_FILL, 0});
717                         }
718                         {
719                                 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 5 );
720                                 m_vscaleIncrement.m_spin = spin;
721                                 m_vscaleSpinner.connect( spin );
722                                 spin.show();
723                                 table.attach(spin, {1, 2, 3, 4}, {GTK_FILL, 0});
724                 spin.dimensions(60, -1);
725                         }
726                         {
727                                 ui::Widget label = ui::Label( "Step" );
728                                 label.show();
729                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
730                                 table.attach(label, {2, 3, 3, 4}, {GTK_FILL, 0});
731                         }
732                         {
733                                 auto entry = ui::Entry(ui::New);
734                                 entry.show();
735                                 table.attach(entry, {3, 4, 3, 4}, {GTK_FILL, 0});
736                 entry.dimensions(50, -1);
737                                 m_vscaleIncrement.m_entry = entry;
738                                 m_vscaleEntry.connect( entry );
739                         }
740                         {
741                                 ui::Widget label = ui::Label( "Rotate" );
742                                 label.show();
743                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
744                                 table.attach(label, {0, 1, 4, 5}, {GTK_FILL, 0});
745                         }
746                         {
747                                 auto spin = ui::SpinButton( ui::Adjustment( 0, -8192, 8192, 2, 8, 0 ), 0, 2 );
748                                 m_rotateIncrement.m_spin = spin;
749                                 m_rotateSpinner.connect( spin );
750                                 spin.show();
751                                 table.attach(spin, {1, 2, 4, 5}, {GTK_FILL, 0});
752                 spin.dimensions(60, -1);
753                                 gtk_spin_button_set_wrap( spin, TRUE );
754                         }
755                         {
756                                 ui::Widget label = ui::Label( "Step" );
757                                 label.show();
758                                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0 );
759                                 table.attach(label, {2, 3, 4, 5}, {GTK_FILL, 0});
760                         }
761                         {
762                                 auto entry = ui::Entry(ui::New);
763                                 entry.show();
764                                 table.attach(entry, {3, 4, 4, 5}, {GTK_FILL, 0});
765                 entry.dimensions(50, -1);
766                                 m_rotateIncrement.m_entry = entry;
767                                 m_rotateEntry.connect( entry );
768                         }
769                         {
770                                 // match grid button
771                                 ui::Widget button = ui::Button( "Match Grid" );
772                                 button.show();
773                                 table.attach(button, {2, 4, 5, 6}, {GTK_EXPAND | GTK_FILL, 0});
774                                 button.connect( "clicked", G_CALLBACK( OnBtnMatchGrid ), 0 );
775                         }
776                 }
777
778                 {
779                         auto frame = ui::Frame( "Texturing" );
780                         frame.show();
781                         vbox.pack_start( frame, FALSE, FALSE, 0 );
782                         {
783                                 auto table = ui::Table(4, 4, FALSE);
784                                 table.show();
785                                 frame.add(table);
786                                 gtk_table_set_row_spacings(table, 5);
787                                 gtk_table_set_col_spacings(table, 5);
788                                 gtk_container_set_border_width( GTK_CONTAINER( table ), 5 );
789                                 {
790                                         ui::Widget label = ui::Label( "Brush" );
791                                         label.show();
792                                         table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
793                                 }
794                                 {
795                                         ui::Widget label = ui::Label( "Patch" );
796                                         label.show();
797                                         table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0});
798                                 }
799                                 {
800                                         ui::Widget label = ui::Label( "Width" );
801                                         label.show();
802                                         table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 0});
803                                 }
804                                 {
805                                         ui::Widget label = ui::Label( "Height" );
806                                         label.show();
807                                         table.attach(label, {3, 4, 0, 1}, {GTK_FILL, 0});
808                                 }
809                                 {
810                                         ui::Widget button = ui::Button( "Axial" );
811                                         button.show();
812                                         table.attach(button, {0, 1, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
813                                         button.connect( "clicked",
814                                                                           G_CALLBACK( OnBtnAxial ), 0 );
815                     button.dimensions(60, -1);
816                                 }
817                                 {
818                                         ui::Widget button = ui::Button( "Fit" );
819                                         button.show();
820                                         table.attach(button, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
821                                         button.connect( "clicked",
822                                                                           G_CALLBACK( OnBtnFaceFit ), 0 );
823                     button.dimensions(60, -1);
824                                 }
825                                 {
826                                         ui::Widget button = ui::Button( "CAP" );
827                                         button.show();
828                                         table.attach(button, {0, 1, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
829                                         button.connect( "clicked",
830                                                                           G_CALLBACK( OnBtnPatchdetails ), 0 );
831                     button.dimensions(60, -1);
832                                 }
833                                 {
834                                         ui::Widget button = ui::Button( "Set..." );
835                                         button.show();
836                                         table.attach(button, {1, 2, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
837                                         button.connect( "clicked",
838                                                                           G_CALLBACK( OnBtnPatchreset ), 0 );
839                     button.dimensions(60, -1);
840                                 }
841                                 {
842                                         ui::Widget button = ui::Button( "Natural" );
843                                         button.show();
844                                         table.attach(button, {2, 3, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
845                                         button.connect( "clicked",
846                                                                           G_CALLBACK( OnBtnPatchnatural ), 0 );
847                     button.dimensions(60, -1);
848                                 }
849                                 {
850                                         ui::Widget button = ui::Button( "Fit" );
851                                         button.show();
852                                         table.attach(button, {3, 4, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
853                                         button.connect( "clicked",
854                                                                           G_CALLBACK( OnBtnPatchFit ), 0 );
855                     button.dimensions(60, -1);
856                                 }
857                                 {
858                                         auto spin = ui::SpinButton( ui::Adjustment( 1, 0, 1 << 16, 1, 10, 0 ), 0, 6 );
859                                         spin.show();
860                                         table.attach(spin, {2, 3, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
861                     spin.dimensions(60, -1);
862                                         AddDialogData( *GTK_SPIN_BUTTON( spin ), m_fitHorizontal );
863                                 }
864                                 {
865                                         auto spin = ui::SpinButton( ui::Adjustment( 1, 0, 1 << 16, 1, 10, 0 ), 0, 6 );
866                                         spin.show();
867                                         table.attach(spin, {3, 4, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
868                     spin.dimensions(60, -1);
869                                         AddDialogData( *GTK_SPIN_BUTTON( spin ), m_fitVertical );
870                                 }
871                         }
872                 }
873                 if ( !string_empty( g_pGameDescription->getKeyValue( "si_flags" ) ) ) {
874                         {
875                                 auto frame = ui::Frame( "Surface Flags" );
876                                 frame.show();
877                                 vbox.pack_start( frame, TRUE, TRUE, 0 );
878                                 {
879                                         auto vbox3 = ui::VBox( FALSE, 4 );
880                                         //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
881                                         vbox3.show();
882                                         frame.add(vbox3);
883                                         {
884                                                 auto table = ui::Table( 8, 4, FALSE );
885                                                 table.show();
886                                                 vbox3.pack_start( table, TRUE, TRUE, 0 );
887                                                 gtk_table_set_row_spacings( table, 0 );
888                                                 gtk_table_set_col_spacings( table, 0 );
889
890                                                 GtkCheckButton** p = m_surfaceFlags;
891
892                                                 for (unsigned int c = 0; c != 4; ++c)
893                                                 {
894                                                         for (unsigned int r = 0; r != 8; ++r)
895                                                         {
896                                                                 auto check = ui::CheckButton( getSurfaceFlagName( c * 8 + r ) );
897                                                                 check.show();
898                                                                 table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0});
899                                                                 *p++ = check;
900                                                                 guint handler_id = togglebutton_connect_toggled( GTK_TOGGLE_BUTTON( check ), ApplyFlagsCaller( *this ) );
901                                                                 g_object_set_data( G_OBJECT( check ), "handler", gint_to_pointer( handler_id ) );
902                                                         }
903                                                 }
904                                         }
905                                 }
906                         }
907                         {
908                                 auto frame = ui::Frame( "Content Flags" );
909                                 frame.show();
910                                 vbox.pack_start( frame, TRUE, TRUE, 0 );
911                                 {
912                                         auto vbox3 = ui::VBox( FALSE, 4 );
913                                         //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
914                                         vbox3.show();
915                                         frame.add(vbox3);
916                                         {
917
918                                                 auto table = ui::Table( 8, 4, FALSE );
919                                                 table.show();
920                                                 vbox3.pack_start( table, TRUE, TRUE, 0 );
921                                                 gtk_table_set_row_spacings( table, 0 );
922                                                 gtk_table_set_col_spacings( table, 0 );
923
924                                                 GtkCheckButton** p = m_contentFlags;
925
926                                                 for (unsigned int c = 0; c != 4; ++c)
927                                                 {
928                                                         for (unsigned int r = 0; r != 8; ++r)
929                                                         {
930                                                                 auto check = ui::CheckButton( getContentFlagName( c * 8 + r ) );
931                                                                 check.show();
932                                                                 table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0});
933                                                                 *p++ = check;
934                                                                 guint handler_id = togglebutton_connect_toggled( GTK_TOGGLE_BUTTON( check ), ApplyFlagsCaller( *this ) );
935                                                                 g_object_set_data( G_OBJECT( check ), "handler", gint_to_pointer( handler_id ) );
936                                                         }
937                                                 }
938
939                                                 // not allowed to modify detail flag using Surface Inspector
940                                                 gtk_widget_set_sensitive( ui::CheckButton(m_contentFlags[BRUSH_DETAIL_FLAG]), FALSE );
941                                         }
942                                 }
943                         }
944                         {
945                                 auto frame = ui::Frame( "Value" );
946                                 frame.show();
947                                 vbox.pack_start( frame, TRUE, TRUE, 0 );
948                                 {
949                                         auto vbox3 = ui::VBox( FALSE, 4 );
950                                         gtk_container_set_border_width( GTK_CONTAINER( vbox3 ), 4 );
951                                         vbox3.show();
952                                         frame.add(vbox3);
953
954                                         {
955                                                 auto entry = ui::Entry(ui::New);
956                                                 entry.show();
957                                                 vbox3.pack_start( entry, TRUE, TRUE, 0 );
958                                                 m_valueEntryWidget = entry;
959                                                 m_valueEntry.connect( entry );
960                                         }
961                                 }
962                         }
963                 }
964
965 #if TEXTOOL_ENABLED
966                 if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
967 // Shamus: Textool goodies...
968                         ui::Widget frame = ui::Frame( "Textool" );
969                         frame.show();
970                         vbox.pack_start( frame , FALSE, FALSE, 0 );
971                         {
972                                 //Prolly should make this a member or global var, so the SI can draw on it...
973                                 TexTool::g_textoolWin = glwidget_new( FALSE );
974                                 // --> Dunno, but this stuff may be necessary... (Looks like it!)
975                                 g_object_ref( TexTool::g_textoolWin );
976                                 gtk_widget_set_events( TexTool::g_textoolWin, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK );
977                                 gtk_widget_set_can_focus( TexTool::g_textoolWin, true );
978                                 // <-- end stuff...
979                                 TexTool::g_textoolWin.show();
980                                 TexTool::g_textoolWin.dimensions( -1, 240 ); //Yeah!
981                                 frame.add(TexTool::g_textoolWin);
982
983                                 TexTool::g_textoolWin.connect( "size_allocate", G_CALLBACK( TexTool::size_allocate ), NULL );
984                                 TexTool::g_textoolWin.connect( "expose_event", G_CALLBACK( TexTool::expose ), NULL );
985                                 TexTool::g_textoolWin.connect( "button_press_event", G_CALLBACK( TexTool::button_press ), NULL );
986                                 TexTool::g_textoolWin.connect( "button_release_event", G_CALLBACK( TexTool::button_release ), NULL );
987                                 TexTool::g_textoolWin.connect( "motion_notify_event", G_CALLBACK( TexTool::motion ), NULL );
988                         }
989                         {
990                                 ui::Widget hbox = ui::HBox( FALSE, 5 );
991                                 hbox.show();
992                                 vbox.pack_start( hbox , FALSE, FALSE, 0 );
993                                 // Checkboxes go here... (Flip X/Y)
994                                 ui::Widget flipX = ui::CheckButton( "Flip X axis" );
995                                 ui::Widget flipY = ui::CheckButton( "Flip Y axis" );
996                                 flipX.show();
997                                 flipY.show();
998                                 hbox.pack_start( flipX, FALSE, FALSE, 0 );
999                                 hbox.pack_start( flipY, FALSE, FALSE, 0 );
1000
1001 //Instead of this, we probably need to create a vbox to put into the frame, then the
1002 //window, then the hbox. !!! FIX !!!
1003 //        frame.add(hbox);
1004
1005 //Hmm. Do we really need g_object_set_data? Mebbe not... And we don't! :-)
1006 //        g_object_set_data(G_OBJECT(flipX), "handler", gint_to_pointer(flipX.connect("toggled", G_CALLBACK(TexTool::flipX), 0)));
1007 //        g_object_set_data(G_OBJECT(flipY), "handler", gint_to_pointer(flipY.connect("toggled", G_CALLBACK(TexTool::flipY), 0)));
1008 //Instead, just do:
1009                                 flipX.connect( "toggled", G_CALLBACK( TexTool::flipX ), NULL );
1010                                 flipY.connect( "toggled", G_CALLBACK( TexTool::flipY ), NULL );
1011                         }
1012                 }
1013 #endif
1014         }
1015
1016         return window;
1017 }
1018
1019 /*
1020    ==============
1021    Update
1022
1023    Set the fields to the current texdef (i.e. map/texdef -> dialog widgets)
1024    if faces selected (instead of brushes) -> will read this face texdef, else current texdef
1025    if only patches selected, will read the patch texdef
1026    ===============
1027  */
1028
1029 void spin_button_set_value_no_signal( GtkSpinButton* spin, gdouble value ){
1030         guint handler_id = gpointer_to_int( g_object_get_data( G_OBJECT( spin ), "handler" ) );
1031         g_signal_handler_block( G_OBJECT( gtk_spin_button_get_adjustment( spin ) ), handler_id );
1032         gtk_spin_button_set_value( spin, value );
1033         g_signal_handler_unblock( G_OBJECT( gtk_spin_button_get_adjustment( spin ) ), handler_id );
1034 }
1035
1036 void spin_button_set_step_increment( GtkSpinButton* spin, gdouble value ){
1037         GtkAdjustment* adjust = gtk_spin_button_get_adjustment( spin );
1038         gtk_adjustment_set_step_increment(adjust, value);
1039 }
1040
1041 void SurfaceInspector::Update(){
1042         const char * name = SurfaceInspector_GetSelectedShader();
1043
1044         if ( shader_is_texture( name ) ) {
1045                 m_texture.text(shader_get_textureName(name));
1046         }
1047         else
1048         {
1049                 m_texture.text("");
1050         }
1051
1052         texdef_t shiftScaleRotate;
1053 //Shamus: This is where we get into trouble--the BP code tries to convert to a "faked"
1054 //shift, rotate & scale values from the brush face, which seems to screw up for some reason.
1055 //!!! FIX !!!
1056 /*globalOutputStream() << "--> SI::Update. About to do ShiftScaleRotate_fromFace()...\n";
1057    SurfaceInspector_GetSelectedBPTexdef();
1058    globalOutputStream() << "BP: (" << g_selectedBrushPrimitTexdef.coords[0][0] << ", " << g_selectedBrushPrimitTexdef.coords[0][1] << ")("
1059     << g_selectedBrushPrimitTexdef.coords[1][0] << ", " << g_selectedBrushPrimitTexdef.coords[1][1] << ")("
1060     << g_selectedBrushPrimitTexdef.coords[0][2] << ", " << g_selectedBrushPrimitTexdef.coords[1][2] << ") SurfaceInspector::Update\n";//*/
1061 //Ok, it's screwed up *before* we get here...
1062         ShiftScaleRotate_fromFace( shiftScaleRotate, SurfaceInspector_GetSelectedTexdef() );
1063
1064         // normalize again to hide the ridiculously high scale values that get created when using texlock
1065         shiftScaleRotate.shift[0] = float_mod( shiftScaleRotate.shift[0], (float)g_selectedShaderSize[0] );
1066         shiftScaleRotate.shift[1] = float_mod( shiftScaleRotate.shift[1], (float)g_selectedShaderSize[1] );
1067
1068         {
1069                 spin_button_set_value_no_signal( m_hshiftIncrement.m_spin, shiftScaleRotate.shift[0] );
1070                 spin_button_set_step_increment( m_hshiftIncrement.m_spin, g_si_globals.shift[0] );
1071                 entry_set_float( m_hshiftIncrement.m_entry, g_si_globals.shift[0] );
1072         }
1073
1074         {
1075                 spin_button_set_value_no_signal( m_vshiftIncrement.m_spin, shiftScaleRotate.shift[1] );
1076                 spin_button_set_step_increment( m_vshiftIncrement.m_spin, g_si_globals.shift[1] );
1077                 entry_set_float( m_vshiftIncrement.m_entry, g_si_globals.shift[1] );
1078         }
1079
1080         {
1081                 spin_button_set_value_no_signal( m_hscaleIncrement.m_spin, shiftScaleRotate.scale[0] );
1082                 spin_button_set_step_increment( m_hscaleIncrement.m_spin, g_si_globals.scale[0] );
1083                 entry_set_float( m_hscaleIncrement.m_entry, g_si_globals.scale[0] );
1084         }
1085
1086         {
1087                 spin_button_set_value_no_signal( m_vscaleIncrement.m_spin, shiftScaleRotate.scale[1] );
1088                 spin_button_set_step_increment( m_vscaleIncrement.m_spin, g_si_globals.scale[1] );
1089                 entry_set_float( m_vscaleIncrement.m_entry, g_si_globals.scale[1] );
1090         }
1091
1092         {
1093                 spin_button_set_value_no_signal( m_rotateIncrement.m_spin, shiftScaleRotate.rotate );
1094                 spin_button_set_step_increment( m_rotateIncrement.m_spin, g_si_globals.rotate );
1095                 entry_set_float( m_rotateIncrement.m_entry, g_si_globals.rotate );
1096         }
1097
1098         if ( !string_empty( g_pGameDescription->getKeyValue( "si_flags" ) ) ) {
1099                 ContentsFlagsValue flags( SurfaceInspector_GetSelectedFlags() );
1100
1101                 entry_set_int( m_valueEntryWidget, flags.m_value );
1102
1103                 for ( GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p )
1104                 {
1105                         toggle_button_set_active_no_signal( ui::CheckButton( *p ), flags.m_surfaceFlags & ( 1 << ( p - m_surfaceFlags ) ) );
1106                 }
1107
1108                 for ( GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p )
1109                 {
1110                         toggle_button_set_active_no_signal( ui::CheckButton( *p ), flags.m_contentFlags & ( 1 << ( p - m_contentFlags ) ) );
1111                 }
1112         }
1113 }
1114
1115 /*
1116    ==============
1117    Apply
1118
1119    Reads the fields to get the current texdef (i.e. widgets -> MAP)
1120    in brush primitive mode, grab the fake shift scale rot and compute a new texture matrix
1121    ===============
1122  */
1123 void SurfaceInspector::ApplyShader(){
1124         StringOutputStream name( 256 );
1125         name << GlobalTexturePrefix_get() << gtk_entry_get_text( m_texture );
1126
1127         // TTimo: detect and refuse invalid texture names (at least the ones with spaces)
1128         if ( !texdef_name_valid( name.c_str() ) ) {
1129                 globalErrorStream() << "invalid texture name '" << name.c_str() << "'\n";
1130                 SurfaceInspector_queueDraw();
1131                 return;
1132         }
1133
1134         UndoableCommand undo( "textureNameSetSelected" );
1135         Select_SetShader( name.c_str() );
1136 }
1137
1138 void SurfaceInspector::ApplyTexdef(){
1139         texdef_t shiftScaleRotate;
1140
1141         shiftScaleRotate.shift[0] = static_cast<float>( gtk_spin_button_get_value( m_hshiftIncrement.m_spin ) );
1142         shiftScaleRotate.shift[1] = static_cast<float>( gtk_spin_button_get_value( m_vshiftIncrement.m_spin ) );
1143         shiftScaleRotate.scale[0] = static_cast<float>( gtk_spin_button_get_value( m_hscaleIncrement.m_spin ) );
1144         shiftScaleRotate.scale[1] = static_cast<float>( gtk_spin_button_get_value( m_vscaleIncrement.m_spin ) );
1145         shiftScaleRotate.rotate = static_cast<float>( gtk_spin_button_get_value( m_rotateIncrement.m_spin ) );
1146
1147         TextureProjection projection;
1148 //Shamus: This is the other place that screws up, it seems, since it doesn't seem to do the
1149 //conversion from the face (I think) and so bogus values end up in the thing... !!! FIX !!!
1150 //This is actually OK. :-P
1151         ShiftScaleRotate_toFace( shiftScaleRotate, projection );
1152
1153         UndoableCommand undo( "textureProjectionSetSelected" );
1154         Select_SetTexdef( projection );
1155 }
1156
1157 void SurfaceInspector::ApplyFlags(){
1158         unsigned int surfaceflags = 0;
1159         for ( GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p )
1160         {
1161                 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( *p ) ) ) {
1162                         surfaceflags |= ( 1 << ( p - m_surfaceFlags ) );
1163                 }
1164         }
1165
1166         unsigned int contentflags = 0;
1167         for ( GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p )
1168         {
1169                 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( *p ) ) ) {
1170                         contentflags |= ( 1 << ( p - m_contentFlags ) );
1171                 }
1172         }
1173
1174         int value = entry_get_int( m_valueEntryWidget );
1175
1176         UndoableCommand undo( "flagsSetSelected" );
1177         Select_SetFlags( ContentsFlagsValue( surfaceflags, contentflags, value, true ) );
1178 }
1179
1180
1181 void Face_getTexture( Face& face, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags ){
1182         shader = face.GetShader();
1183         face.GetTexdef( projection );
1184         flags = face.getShader().m_flags;
1185 }
1186 typedef Function4<Face&, CopiedString&, TextureProjection&, ContentsFlagsValue&, void, Face_getTexture> FaceGetTexture;
1187
1188 void Face_setTexture( Face& face, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags ){
1189         face.SetShader( shader );
1190         face.SetTexdef( projection );
1191         face.SetFlags( flags );
1192 }
1193 typedef Function4<Face&, const char*, const TextureProjection&, const ContentsFlagsValue&, void, Face_setTexture> FaceSetTexture;
1194
1195
1196 void Patch_getTexture( Patch& patch, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags ){
1197         shader = patch.GetShader();
1198         projection = TextureProjection( texdef_t(), brushprimit_texdef_t(), Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) );
1199         flags = ContentsFlagsValue( 0, 0, 0, false );
1200 }
1201 typedef Function4<Patch&, CopiedString&, TextureProjection&, ContentsFlagsValue&, void, Patch_getTexture> PatchGetTexture;
1202
1203 void Patch_setTexture( Patch& patch, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags ){
1204         patch.SetShader( shader );
1205 }
1206 typedef Function4<Patch&, const char*, const TextureProjection&, const ContentsFlagsValue&, void, Patch_setTexture> PatchSetTexture;
1207
1208
1209 typedef Callback3<CopiedString&, TextureProjection&, ContentsFlagsValue&> GetTextureCallback;
1210 typedef Callback3<const char*, const TextureProjection&, const ContentsFlagsValue&> SetTextureCallback;
1211
1212 struct Texturable
1213 {
1214         GetTextureCallback getTexture;
1215         SetTextureCallback setTexture;
1216 };
1217
1218
1219 void Face_getClosest( Face& face, SelectionTest& test, SelectionIntersection& bestIntersection, Texturable& texturable ){
1220         SelectionIntersection intersection;
1221         face.testSelect( test, intersection );
1222         if ( intersection.valid()
1223                  && SelectionIntersection_closer( intersection, bestIntersection ) ) {
1224                 bestIntersection = intersection;
1225                 texturable.setTexture = makeCallback3( FaceSetTexture(), face );
1226                 texturable.getTexture = makeCallback3( FaceGetTexture(), face );
1227         }
1228 }
1229
1230
1231 class OccludeSelector : public Selector
1232 {
1233 SelectionIntersection& m_bestIntersection;
1234 bool& m_occluded;
1235 public:
1236 OccludeSelector( SelectionIntersection& bestIntersection, bool& occluded ) : m_bestIntersection( bestIntersection ), m_occluded( occluded ){
1237         m_occluded = false;
1238 }
1239 void pushSelectable( Selectable& selectable ){
1240 }
1241 void popSelectable(){
1242 }
1243 void addIntersection( const SelectionIntersection& intersection ){
1244         if ( SelectionIntersection_closer( intersection, m_bestIntersection ) ) {
1245                 m_bestIntersection = intersection;
1246                 m_occluded = true;
1247         }
1248 }
1249 };
1250
1251 class BrushGetClosestFaceVisibleWalker : public scene::Graph::Walker
1252 {
1253 SelectionTest& m_test;
1254 Texturable& m_texturable;
1255 mutable SelectionIntersection m_bestIntersection;
1256 public:
1257 BrushGetClosestFaceVisibleWalker( SelectionTest& test, Texturable& texturable ) : m_test( test ), m_texturable( texturable ){
1258 }
1259 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1260         if ( path.top().get().visible() ) {
1261                 BrushInstance* brush = Instance_getBrush( instance );
1262                 if ( brush != 0 ) {
1263                         m_test.BeginMesh( brush->localToWorld() );
1264
1265                         for ( Brush::const_iterator i = brush->getBrush().begin(); i != brush->getBrush().end(); ++i )
1266                         {
1267                                 Face_getClosest( *( *i ), m_test, m_bestIntersection, m_texturable );
1268                         }
1269                 }
1270                 else
1271                 {
1272                         SelectionTestable* selectionTestable = Instance_getSelectionTestable( instance );
1273                         if ( selectionTestable ) {
1274                                 bool occluded;
1275                                 OccludeSelector selector( m_bestIntersection, occluded );
1276                                 selectionTestable->testSelect( selector, m_test );
1277                                 if ( occluded ) {
1278                                         Patch* patch = Node_getPatch( path.top() );
1279                                         if ( patch != 0 ) {
1280                                                 m_texturable.setTexture = makeCallback3( PatchSetTexture(), *patch );
1281                                                 m_texturable.getTexture = makeCallback3( PatchGetTexture(), *patch );
1282                                         }
1283                                         else
1284                                         {
1285                                                 m_texturable = Texturable();
1286                                         }
1287                                 }
1288                         }
1289                 }
1290         }
1291         return true;
1292 }
1293 };
1294
1295 Texturable Scene_getClosestTexturable( scene::Graph& graph, SelectionTest& test ){
1296         Texturable texturable;
1297         graph.traverse( BrushGetClosestFaceVisibleWalker( test, texturable ) );
1298         return texturable;
1299 }
1300
1301 bool Scene_getClosestTexture( scene::Graph& graph, SelectionTest& test, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags ){
1302         Texturable texturable = Scene_getClosestTexturable( graph, test );
1303         if ( texturable.getTexture != GetTextureCallback() ) {
1304                 texturable.getTexture( shader, projection, flags );
1305                 return true;
1306         }
1307         return false;
1308 }
1309
1310 void Scene_setClosestTexture( scene::Graph& graph, SelectionTest& test, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags ){
1311         Texturable texturable = Scene_getClosestTexturable( graph, test );
1312         if ( texturable.setTexture != SetTextureCallback() ) {
1313                 texturable.setTexture( shader, projection, flags );
1314         }
1315 }
1316
1317
1318 class FaceTexture
1319 {
1320 public:
1321 TextureProjection m_projection;
1322 ContentsFlagsValue m_flags;
1323 };
1324
1325 FaceTexture g_faceTextureClipboard;
1326
1327 void FaceTextureClipboard_setDefault(){
1328         g_faceTextureClipboard.m_flags = ContentsFlagsValue( 0, 0, 0, false );
1329         TexDef_Construct_Default( g_faceTextureClipboard.m_projection );
1330 }
1331
1332 void TextureClipboard_textureSelected( const char* shader ){
1333         FaceTextureClipboard_setDefault();
1334 }
1335
1336 class TextureBrowser;
1337 extern TextureBrowser g_TextureBrowser;
1338 void TextureBrowser_SetSelectedShader( TextureBrowser& textureBrowser, const char* shader );
1339 const char* TextureBrowser_GetSelectedShader( TextureBrowser& textureBrowser );
1340
1341 void Scene_copyClosestTexture( SelectionTest& test ){
1342         CopiedString shader;
1343         if ( Scene_getClosestTexture( GlobalSceneGraph(), test, shader, g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags ) ) {
1344                 TextureBrowser_SetSelectedShader( g_TextureBrowser, shader.c_str() );
1345         }
1346 }
1347
1348 void Scene_applyClosestTexture( SelectionTest& test ){
1349         UndoableCommand command( "facePaintTexture" );
1350
1351         Scene_setClosestTexture( GlobalSceneGraph(), test, TextureBrowser_GetSelectedShader( g_TextureBrowser ), g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags );
1352
1353         SceneChangeNotify();
1354 }
1355
1356
1357
1358
1359
1360 void SelectedFaces_copyTexture(){
1361         if ( !g_SelectedFaceInstances.empty() ) {
1362                 Face& face = g_SelectedFaceInstances.last().getFace();
1363                 face.GetTexdef( g_faceTextureClipboard.m_projection );
1364                 g_faceTextureClipboard.m_flags = face.getShader().m_flags;
1365
1366                 TextureBrowser_SetSelectedShader( g_TextureBrowser, face.getShader().getShader() );
1367         }
1368 }
1369
1370 void FaceInstance_pasteTexture( FaceInstance& faceInstance ){
1371         faceInstance.getFace().SetTexdef( g_faceTextureClipboard.m_projection );
1372         faceInstance.getFace().SetShader( TextureBrowser_GetSelectedShader( g_TextureBrowser ) );
1373         faceInstance.getFace().SetFlags( g_faceTextureClipboard.m_flags );
1374         SceneChangeNotify();
1375 }
1376
1377 bool SelectedFaces_empty(){
1378         return g_SelectedFaceInstances.empty();
1379 }
1380
1381 void SelectedFaces_pasteTexture(){
1382         UndoableCommand command( "facePasteTexture" );
1383         g_SelectedFaceInstances.foreach( FaceInstance_pasteTexture );
1384 }
1385
1386
1387
1388 void SurfaceInspector_constructPreferences( PreferencesPage& page ){
1389         page.appendCheckBox( "", "Surface Inspector Increments Match Grid", g_si_globals.m_bSnapTToGrid );
1390 }
1391 void SurfaceInspector_constructPage( PreferenceGroup& group ){
1392         PreferencesPage page( group.createPage( "Surface Inspector", "Surface Inspector Preferences" ) );
1393         SurfaceInspector_constructPreferences( page );
1394 }
1395 void SurfaceInspector_registerPreferencesPage(){
1396         PreferencesDialog_addSettingsPage( FreeCaller1<PreferenceGroup&, SurfaceInspector_constructPage>() );
1397 }
1398
1399 void SurfaceInspector_registerCommands(){
1400         GlobalCommands_insert( "FitTexture", FreeCaller<SurfaceInspector_FitTexture>(), Accelerator( 'B', (GdkModifierType)GDK_SHIFT_MASK ) );
1401         GlobalCommands_insert( "SurfaceInspector", FreeCaller<SurfaceInspector_toggleShown>(), Accelerator( 'S' ) );
1402
1403         GlobalCommands_insert( "FaceCopyTexture", FreeCaller<SelectedFaces_copyTexture>() );
1404         GlobalCommands_insert( "FacePasteTexture", FreeCaller<SelectedFaces_pasteTexture>() );
1405 }
1406
1407
1408 #include "preferencesystem.h"
1409
1410
1411 void SurfaceInspector_Construct(){
1412         g_SurfaceInspector = new SurfaceInspector;
1413
1414         SurfaceInspector_registerCommands();
1415
1416         FaceTextureClipboard_setDefault();
1417
1418         GlobalPreferenceSystem().registerPreference( "SurfaceWnd", getSurfaceInspector().m_importPosition, getSurfaceInspector().m_exportPosition );
1419         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Scale1", FloatImportStringCaller( g_si_globals.scale[0] ), FloatExportStringCaller( g_si_globals.scale[0] ) );
1420         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Scale2", FloatImportStringCaller( g_si_globals.scale[1] ), FloatExportStringCaller( g_si_globals.scale[1] ) );
1421         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Shift1", FloatImportStringCaller( g_si_globals.shift[0] ), FloatExportStringCaller( g_si_globals.shift[0] ) );
1422         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Shift2", FloatImportStringCaller( g_si_globals.shift[1] ), FloatExportStringCaller( g_si_globals.shift[1] ) );
1423         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Rotate", FloatImportStringCaller( g_si_globals.rotate ), FloatExportStringCaller( g_si_globals.rotate ) );
1424         GlobalPreferenceSystem().registerPreference( "SnapTToGrid", BoolImportStringCaller( g_si_globals.m_bSnapTToGrid ), BoolExportStringCaller( g_si_globals.m_bSnapTToGrid ) );
1425
1426         typedef FreeCaller1<const Selectable&, SurfaceInspector_SelectionChanged> SurfaceInspectorSelectionChangedCaller;
1427         GlobalSelectionSystem().addSelectionChangeCallback( SurfaceInspectorSelectionChangedCaller() );
1428         typedef FreeCaller<SurfaceInspector_updateSelection> SurfaceInspectorUpdateSelectionCaller;
1429         Brush_addTextureChangedCallback( SurfaceInspectorUpdateSelectionCaller() );
1430         Patch_addTextureChangedCallback( SurfaceInspectorUpdateSelectionCaller() );
1431
1432         SurfaceInspector_registerPreferencesPage();
1433 }
1434 void SurfaceInspector_Destroy(){
1435         delete g_SurfaceInspector;
1436 }
1437
1438
1439
1440 #if TEXTOOL_ENABLED
1441
1442 namespace TexTool { // namespace hides these symbols from other object-files
1443 //
1444 //Shamus: Textool functions, including GTK+ callbacks
1445 //
1446
1447 //NOTE: Black screen when TT first comes up is caused by an uninitialized Extent... !!! FIX !!!
1448 //      But... You can see down below that it *is* initialized! WTF?
1449 struct Extent
1450 {
1451         float minX, minY, maxX, maxY;
1452         float width( void ) { return fabs( maxX - minX ); }
1453         float height( void ) { return fabs( maxY - minY ); }
1454 };
1455
1456 //This seems to control the texture scale... (Yep! ;-)
1457 Extent extents = { -2.0f, -2.0f, +2.0f, +2.0f };
1458 brushprimit_texdef_t tm;                        // Texture transform matrix
1459 Vector2 pts[c_brush_maxFaces];
1460 Vector2 center;
1461 int numPts;
1462 int textureNum;
1463 Vector2 textureSize;
1464 Vector2 windowSize;
1465 #define VP_PADDING  1.2
1466 #define PI          3.14159265358979
1467 bool lButtonDown = false;
1468 bool rButtonDown = false;
1469 //int dragPoint;
1470 //int anchorPoint;
1471 bool haveAnchor = false;
1472 brushprimit_texdef_t currentBP;
1473 brushprimit_texdef_t origBP;                    // Original brush primitive (before we muck it up)
1474 float controlRadius = 5.0f;
1475 float rotationAngle = 0.0f;
1476 float rotationAngle2 = 0.0f;
1477 float oldRotationAngle;
1478 Vector2 rotationPoint;
1479 bool translatingX = false;                      // Widget state variables
1480 bool translatingY = false;
1481 bool scalingX = false;
1482 bool scalingY = false;
1483 bool rotating = false;
1484 bool resizingX = false;                         // Not sure what this means... :-/
1485 bool resizingY = false;
1486 float origAngle, origScaleX, origScaleY;
1487 Vector2 oldCenter;
1488
1489
1490 // Function prototypes (move up to top later...)
1491
1492 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius );
1493
1494
1495 void CopyPointsFromSelectedFace( void ){
1496         // Make sure that there's a face and winding to get!
1497
1498         if ( g_SelectedFaceInstances.empty() ) {
1499                 numPts = 0;
1500                 return;
1501         }
1502
1503         Face & face = g_SelectedFaceInstances.last().getFace();
1504         textureNum = face.getShader().m_state->getTexture().texture_number;
1505         textureSize.x() = face.getShader().m_state->getTexture().width;
1506         textureSize.y() = face.getShader().m_state->getTexture().height;
1507 //globalOutputStream() << "--> Texture #" << textureNum << ": " << textureSize.x() << " x " << textureSize.y() << "...\n";
1508
1509         currentBP = SurfaceInspector_GetSelectedTexdef().m_brushprimit_texdef;
1510
1511         face.EmitTextureCoordinates();
1512         Winding & w = face.getWinding();
1513         int count = 0;
1514
1515         for ( Winding::const_iterator i = w.begin(); i != w.end(); i++ )
1516         {
1517                 //globalOutputStream() << (*i).texcoord.x() << " " << (*i).texcoord.y() << ", ";
1518                 pts[count].x() = ( *i ).texcoord.x();
1519                 pts[count].y() = ( *i ).texcoord.y();
1520                 count++;
1521         }
1522
1523         numPts = count;
1524
1525         //globalOutputStream() << " ..copied points\n";
1526 }
1527
1528 brushprimit_texdef_t bp;
1529 //This approach is probably wrongheaded and just not right anyway. So, !!! FIX !!! [DONE]
1530 void CommitChanges( void ){
1531         texdef_t t;                                 // Throwaway, since this is BP only
1532
1533         bp.coords[0][0] = tm.coords[0][0] * origBP.coords[0][0] + tm.coords[0][1] * origBP.coords[1][0];
1534         bp.coords[0][1] = tm.coords[0][0] * origBP.coords[0][1] + tm.coords[0][1] * origBP.coords[1][1];
1535         bp.coords[0][2] = tm.coords[0][0] * origBP.coords[0][2] + tm.coords[0][1] * origBP.coords[1][2] + tm.coords[0][2];
1536 //Ok, this works for translation...
1537 //      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();
1538         bp.coords[1][0] = tm.coords[1][0] * origBP.coords[0][0] + tm.coords[1][1] * origBP.coords[1][0];
1539         bp.coords[1][1] = tm.coords[1][0] * origBP.coords[0][1] + tm.coords[1][1] * origBP.coords[1][1];
1540         bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2];
1541 //      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();
1542
1543 //This doesn't work:    g_brush_texture_changed();
1544 // Let's try this:
1545 //Note: We should only set an undo *after* the button has been released... !!! FIX !!!
1546 //Definitely *should* have an undo, though!
1547 //  UndoableCommand undo("textureProjectionSetSelected");
1548         Select_SetTexdef( TextureProjection( t, bp, Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) ) );
1549 //This is working, but for some reason the translate is causing the rest of the SI
1550 //widgets to yield bad readings... !!! FIX !!!
1551 //I.e., click on textool window, translate face wireframe, then controls go crazy. Dunno why.
1552 //It's because there were some uncommented out add/removeScale functions in brush.h and a
1553 //removeScale in brushmanip.cpp... :-/
1554 //Translate isn't working at all now... :-(
1555 //It's because we need to multiply in some scaling factor (prolly the texture width/height)
1556 //Yep. :-P
1557 }
1558
1559 void UpdateControlPoints( void ){
1560         CommitChanges();
1561
1562         // Init texture transform matrix
1563
1564         tm.coords[0][0] = 1.0f; tm.coords[0][1] = 0.0f; tm.coords[0][2] = 0.0f;
1565         tm.coords[1][0] = 0.0f; tm.coords[1][1] = 1.0f; tm.coords[1][2] = 0.0f;
1566 }
1567
1568
1569 /*
1570    For shifting we have:
1571  */
1572 /*
1573    The code that should provide reasonable defaults, but doesn't for some reason:
1574    It's scaling the BP by 128 for some reason, between the time it's created and the
1575    time we get back to the SI widgets:
1576
1577    static void OnBtnAxial(GtkWidget *widget, gpointer data)
1578    {
1579    UndoableCommand undo("textureDefault");
1580    TextureProjection projection;
1581    TexDef_Construct_Default(projection);
1582    Select_SetTexdef(projection);
1583    }
1584
1585    Select_SetTexdef() calls Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection)
1586    which is in brushmanip.h: This eventually calls
1587    Texdef_Assign(m_texdef, texdef, m_brushprimit_texdef, brushprimit_texdef) in class Face...
1588    which just copies from brushpr to m_brushpr...
1589  */
1590
1591 //Small problem with this thing: It's scaled to the texture which is all screwed up... !!! FIX !!! [DONE]
1592 //Prolly should separate out the grid drawing so that we can draw it behind the polygon.
1593 const float gridWidth = 1.3f; // Let's try an absolute height... WORKS!!!
1594 // NOTE that 2.0 is the height of the viewport. Dunno why... Should make collision
1595 //      detection easier...
1596 const float gridRadius = gridWidth * 0.5f;
1597
1598 typedef const float WidgetColor[3];
1599 const WidgetColor widgetColor[10] = {
1600         { 1.0000f, 0.2000f, 0.0000f },          // Red
1601         { 0.9137f, 0.9765f, 0.4980f },          // Yellow
1602         { 0.0000f, 0.6000f, 0.3216f },          // Green
1603         { 0.6157f, 0.7726f, 0.8196f },          // Cyan
1604         { 0.4980f, 0.5000f, 0.4716f },          // Grey
1605
1606         // Highlight colors
1607         { 1.0000f, 0.6000f, 0.4000f },          // Light Red
1608         { 1.0000f, 1.0000f, 0.8980f },          // Light Yellow
1609         { 0.4000f, 1.0000f, 0.7216f },          // Light Green
1610         { 1.0000f, 1.0000f, 1.0000f },          // Light Cyan
1611         { 0.8980f, 0.9000f, 0.8716f }           // Light Grey
1612 };
1613
1614 #define COLOR_RED           0
1615 #define COLOR_YELLOW        1
1616 #define COLOR_GREEN         2
1617 #define COLOR_CYAN          3
1618 #define COLOR_GREY          4
1619 #define COLOR_LT_RED        5
1620 #define COLOR_LT_YELLOW     6
1621 #define COLOR_LT_GREEN      7
1622 #define COLOR_LT_CYAN       8
1623 #define COLOR_LT_GREY       9
1624
1625 void DrawControlWidgets( void ){
1626 //Note that the grid should go *behind* the face outline... !!! FIX !!!
1627         // Grid
1628         float xStart = center.x() - ( gridWidth / 2.0f );
1629         float yStart = center.y() - ( gridWidth / 2.0f );
1630         float xScale = ( extents.height() / extents.width() ) * ( textureSize.y() / textureSize.x() );
1631
1632         glPushMatrix();
1633 //Small problem with this approach: Changing the center point in the TX code doesn't seem to
1634 //change anything here--prolly because we load a new identity matrix. A couple of ways to fix
1635 //this would be to get rid of that code, or change the center to a new point by taking into
1636 //account the transforms that we toss with the new identity matrix. Dunno which is better.
1637         glLoadIdentity();
1638         glScalef( xScale, 1.0, 1.0 );           // Will that square it up? Yup.
1639         glRotatef( static_cast<float>( radians_to_degrees( atan2( -currentBP.coords[0][1], currentBP.coords[0][0] ) ) ), 0.0, 0.0, -1.0 );
1640         glTranslatef( -center.x(), -center.y(), 0.0 );
1641
1642         // Circle
1643         glColor3fv( translatingX && translatingY ? widgetColor[COLOR_LT_YELLOW] : widgetColor[COLOR_YELLOW] );
1644         glBegin( GL_LINE_LOOP );
1645         DrawCircularArc( center, 0, 2.0f * PI, gridRadius * 0.16 );
1646
1647         glEnd();
1648
1649         // Axes
1650         glBegin( GL_LINES );
1651         glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1652         glVertex2f( center.x(), center.y() + ( gridRadius * 0.16 ) );
1653         glVertex2f( center.x(), center.y() + ( gridRadius * 1.00 ) );
1654         glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1655         glVertex2f( center.x() + ( gridRadius * 0.16 ), center.y() );
1656         glVertex2f( center.x() + ( gridRadius * 1.00 ), center.y() );
1657         glEnd();
1658
1659         // Arrowheads
1660         glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1661         glBegin( GL_TRIANGLES );
1662         glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1663         glVertex2f( center.x(), center.y() + ( gridRadius * 1.10 ) );
1664         glVertex2f( center.x() + ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1665         glVertex2f( center.x() - ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1666         glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1667         glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() );
1668         glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() + ( gridRadius * 0.06 ) );
1669         glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() - ( gridRadius * 0.06 ) );
1670         glEnd();
1671
1672         // Arc
1673         glBegin( GL_LINE_STRIP );
1674         glColor3fv( rotating ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1675         DrawCircularArc( center, 0.03f * PI, 0.47f * PI, gridRadius * 0.90 );
1676         glEnd();
1677
1678         // Boxes
1679         glColor3fv( scalingY && !scalingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1680         glBegin( GL_LINES );
1681         glVertex2f( center.x() + ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1682         glVertex2f( center.x() - ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1683         glEnd();
1684         glBegin( GL_LINE_LOOP );
1685         glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1686         glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1687         glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1688         glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1689         glEnd();
1690
1691         glColor3fv( scalingX && !scalingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1692         glBegin( GL_LINES );
1693         glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 0.20 ) );
1694         glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() - ( gridRadius * 0.20 ) );
1695         glEnd();
1696         glBegin( GL_LINE_LOOP );
1697         glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 0.10 ) );
1698         glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() - ( gridRadius * 0.10 ) );
1699         glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() - ( gridRadius * 0.10 ) );
1700         glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 0.10 ) );
1701         glEnd();
1702
1703         glColor3fv( scalingX && scalingY ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1704         glBegin( GL_LINE_STRIP );
1705         glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.10 ) );
1706         glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.50 ) );
1707         glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() + ( gridRadius * 1.50 ) );
1708         glEnd();
1709         glBegin( GL_LINE_LOOP );
1710         glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.40 ) );
1711         glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.20 ) );
1712         glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.20 ) );
1713         glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.40 ) );
1714         glEnd();
1715
1716         glPopMatrix();
1717 }
1718
1719 void DrawControlPoints( void ){
1720         glColor3f( 1, 1, 1 );
1721         glBegin( GL_LINE_LOOP );
1722
1723         for ( int i = 0; i < numPts; i++ )
1724                 glVertex2f( pts[i].x(), pts[i].y() );
1725
1726         glEnd();
1727 }
1728
1729 // Note: Setup and all that jazz must be done by the caller!
1730
1731 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius ){
1732         float stepSize = ( 2.0f * PI ) / 200.0f;
1733
1734         for ( float angle = startAngle; angle <= endAngle; angle += stepSize )
1735                 glVertex2f( ctr.x() + radius * cos( angle ), ctr.y() + radius * sin( angle ) );
1736 }
1737
1738
1739 void focus(){
1740         if ( numPts == 0 ) {
1741                 return;
1742         }
1743
1744         // Find selected texture's extents...
1745
1746         extents.minX = extents.maxX = pts[0].x(),
1747         extents.minY = extents.maxY = pts[0].y();
1748
1749         for ( int i = 1; i < numPts; i++ )
1750         {
1751                 if ( pts[i].x() < extents.minX ) {
1752                         extents.minX = pts[i].x();
1753                 }
1754                 if ( pts[i].x() > extents.maxX ) {
1755                         extents.maxX = pts[i].x();
1756                 }
1757                 if ( pts[i].y() < extents.minY ) {
1758                         extents.minY = pts[i].y();
1759                 }
1760                 if ( pts[i].y() > extents.maxY ) {
1761                         extents.maxY = pts[i].y();
1762                 }
1763         }
1764
1765         // Do some viewport fitting stuff...
1766
1767 //globalOutputStream() << "--> Center: " << center.x() << ", " << center.y() << "\n";
1768 //globalOutputStream() << "--> Extents (stage 1): " << extents.minX << ", "
1769 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1770         // TTimo: Apply a ratio to get the area we'll draw.
1771         center.x() = 0.5f * ( extents.minX + extents.maxX ),
1772                 center.y() = 0.5f * ( extents.minY + extents.maxY );
1773         extents.minX = center.x() + VP_PADDING * ( extents.minX - center.x() ),
1774         extents.minY = center.y() + VP_PADDING * ( extents.minY - center.y() ),
1775         extents.maxX = center.x() + VP_PADDING * ( extents.maxX - center.x() ),
1776         extents.maxY = center.y() + VP_PADDING * ( extents.maxY - center.y() );
1777 //globalOutputStream() << "--> Extents (stage 2): " << extents.minX << ", "
1778 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1779
1780         // TTimo: We want a texture with the same X / Y ratio.
1781         // TTimo: Compute XY space / window size ratio.
1782         float SSize = extents.width(), TSize = extents.height();
1783         float ratioX = textureSize.x() * extents.width() / windowSize.x(),
1784                   ratioY = textureSize.y() * extents.height() / windowSize.y();
1785 //globalOutputStream() << "--> Texture size: " << textureSize.x() << ", " << textureSize.y() << "\n";
1786 //globalOutputStream() << "--> Window size: " << windowSize.x() << ", " << windowSize.y() << "\n";
1787
1788         if ( ratioX > ratioY ) {
1789                 TSize = ( windowSize.y() * ratioX ) / textureSize.y();
1790 //              TSize = extents.width() * (windowSize.y() / windowSize.x()) * (textureSize.x() / textureSize.y());
1791         }
1792         else
1793         {
1794                 SSize = ( windowSize.x() * ratioY ) / textureSize.x();
1795 //              SSize = extents.height() * (windowSize.x() / windowSize.y()) * (textureSize.y() / textureSize.x());
1796         }
1797
1798         extents.minX = center.x() - 0.5f * SSize, extents.maxX = center.x() + 0.5f * SSize,
1799         extents.minY = center.y() - 0.5f * TSize, extents.maxY = center.y() + 0.5f * TSize;
1800 //globalOutputStream() << "--> Extents (stage 3): " << extents.minX << ", "
1801 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1802 }
1803
1804 gboolean size_allocate( ui::Widget win, GtkAllocation * a, gpointer ){
1805         windowSize.x() = a->width;
1806         windowSize.y() = a->height;
1807         queueDraw();
1808         return false;
1809 }
1810
1811 gboolean expose( ui::Widget win, GdkEventExpose * e, gpointer ){
1812 //      globalOutputStream() << "--> Textool Window was exposed!\n";
1813 //      globalOutputStream() << "    (window width/height: " << cc << "/" << e->area.height << ")\n";
1814
1815 //      windowSize.x() = e->area.width, windowSize.y() = e->area.height;
1816 //This needs to go elsewhere...
1817 //      InitTextool();
1818
1819         if ( glwidget_make_current( win ) == FALSE ) {
1820                 globalOutputStream() << "    FAILED to make current! Oh, the agony! :-(\n";
1821                 return true;
1822         }
1823
1824         CopyPointsFromSelectedFace();
1825
1826         if ( !lButtonDown ) {
1827                 focus();
1828         }
1829
1830         // Probably should init button/anchor states here as well...
1831 //      rotationAngle = 0.0f;
1832         glClearColor( 0, 0, 0, 0 );
1833         glViewport( 0, 0, e->area.width, e->area.height );
1834         glMatrixMode( GL_PROJECTION );
1835         glLoadIdentity();
1836
1837 //???
1838         glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1839         glDisable( GL_DEPTH_TEST );
1840         glDisable( GL_BLEND );
1841
1842         glOrtho( extents.minX, extents.maxX, extents.maxY, extents.minY, -1, 1 );
1843
1844         glColor3f( 1, 1, 1 );
1845         // draw the texture background
1846         glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1847         glBindTexture( GL_TEXTURE_2D, textureNum );
1848
1849         glEnable( GL_TEXTURE_2D );
1850         glBegin( GL_QUADS );
1851         glTexCoord2f( extents.minX, extents.minY );
1852         glVertex2f( extents.minX, extents.minY );
1853         glTexCoord2f( extents.maxX, extents.minY );
1854         glVertex2f( extents.maxX, extents.minY );
1855         glTexCoord2f( extents.maxX, extents.maxY );
1856         glVertex2f( extents.maxX, extents.maxY );
1857         glTexCoord2f( extents.minX, extents.maxY );
1858         glVertex2f( extents.minX, extents.maxY );
1859         glEnd();
1860         glDisable( GL_TEXTURE_2D );
1861
1862         // draw the texture-space grid
1863         glColor3fv( widgetColor[COLOR_GREY] );
1864         glBegin( GL_LINES );
1865
1866         const int gridSubdivisions = 8;
1867         const float gridExtents = 4.0f;
1868
1869         for ( int i = 0; i < gridSubdivisions + 1; ++i )
1870         {
1871                 float y = i * ( gridExtents / float(gridSubdivisions) );
1872                 float x = i * ( gridExtents / float(gridSubdivisions) );
1873                 glVertex2f( 0, y );
1874                 glVertex2f( gridExtents, y );
1875                 glVertex2f( x, 0 );
1876                 glVertex2f( x, gridExtents );
1877         }
1878
1879         glEnd();
1880
1881         DrawControlPoints();
1882         DrawControlWidgets();
1883 //???
1884         // reset the current texture
1885 //  glBindTexture(GL_TEXTURE_2D, 0);
1886 //  glFinish();
1887         glwidget_swap_buffers( win );
1888
1889         return false;
1890 }
1891
1892 /*int FindSelectedPoint(int x, int y)
1893    {
1894     for(int i=0; i<numPts; i++)
1895     {
1896         int nx = (int)(windowSize.x() * (pts[i].x() - extents.minX) / extents.width());
1897         int ny = (int)(windowSize.y() * (pts[i].y() - extents.minY) / extents.height());
1898
1899         if (abs(nx - x) <= 3 && abs(ny - y) <= 3)
1900             return i;
1901     }
1902
1903     return -1;
1904    }//*/
1905
1906 Vector2 trans;
1907 Vector2 trans2;
1908 Vector2 dragPoint;  // Defined in terms of window space (+x/-y)
1909 Vector2 oldTrans;
1910 gboolean button_press( ui::Widget win, GdkEventButton * e, gpointer ){
1911 //      globalOutputStream() << "--> Textool button press...\n";
1912
1913         if ( e->button == 1 ) {
1914                 lButtonDown = true;
1915                 GlobalUndoSystem().start();
1916
1917                 origBP = currentBP;
1918
1919                 //globalOutputStream() << "--> Original BP: [" << origBP.coords[0][0] << "][" << origBP.coords[0][1] << "][" << origBP.coords[0][2] << "]\n";
1920                 //globalOutputStream() << "                 [" << origBP.coords[1][0] << "][" << origBP.coords[1][1] << "][" << origBP.coords[1][2] << "]\n";
1921                 //float angle = atan2(origBP.coords[0][1], origBP.coords[0][0]) * 180.0f / 3.141592653589f;
1922                 origAngle = ( origBP.coords[0][1] > 0 ? PI : -PI ); // Could also be -PI... !!! FIX !!! [DONE]
1923
1924                 if ( origBP.coords[0][0] != 0.0f ) {
1925                         origAngle = atan( origBP.coords[0][1] / origBP.coords[0][0] );
1926                 }
1927
1928                 origScaleX = origBP.coords[0][0] / cos( origAngle );
1929                 origScaleY = origBP.coords[1][1] / cos( origAngle );
1930                 rotationAngle = origAngle;
1931                 oldCenter[0] = oldCenter[1] = 0;
1932
1933                 //globalOutputStream() << "--> BP stats: ang=" << origAngle * RAD_TO_DEG << ", scale=" << origScaleX << "/" << origScaleY << "\n";
1934                 //Should also set the Flip X/Y checkboxes here as well... !!! FIX !!!
1935                 //Also: should reverse texture left/right up/down instead of flipping the points...
1936
1937 //disnowok
1938 //float nx = windowSize.x() * (e->x - extents.minX) / (extents.maxX - extents.minX);
1939 //float ny = windowSize.y() * (e->y - extents.minY) / (extents.maxY - extents.minY);
1940 //disdoes...
1941 //But I want it to scroll the texture window, not the points... !!! FIX !!!
1942 //Actually, should scroll the texture window only when mouse is down on no widgets...
1943                 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
1944                 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
1945                 trans.x() = -tm.coords[0][0] * nx - tm.coords[0][1] * ny;
1946                 trans.y() = -tm.coords[1][0] * nx - tm.coords[1][1] * ny;
1947
1948                 dragPoint.x() = e->x, dragPoint.y() = e->y;
1949                 trans2.x() = nx, trans2.y() = ny;
1950                 oldRotationAngle = rotationAngle;
1951 //              oldTrans.x() = tm.coords[0][2] - nx * textureSize.x();
1952 //              oldTrans.y() = tm.coords[1][2] - ny * textureSize.y();
1953                 oldTrans.x() = tm.coords[0][2];
1954                 oldTrans.y() = tm.coords[1][2];
1955                 oldCenter.x() = center.x();
1956                 oldCenter.y() = center.y();
1957
1958                 queueDraw();
1959
1960                 return true;
1961         }
1962 /*      else if (e->button == 3)
1963     {
1964         rButtonDown = true;
1965     }//*/
1966
1967 //globalOutputStream() << "(" << (haveAnchor ? "anchor" : "released") << ")\n";
1968
1969         return false;
1970 }
1971
1972 gboolean button_release( ui::Widget win, GdkEventButton * e, gpointer ){
1973 //      globalOutputStream() << "--> Textool button release...\n";
1974
1975         if ( e->button == 1 ) {
1976 /*              float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
1977         float pty = e->y / windowSize.y() * extents.height() + extents.minY;
1978
1979    //This prolly should go into the mouse move code...
1980    //Doesn't work correctly anyway...
1981         if (translatingX || translatingY)
1982             center.x() = ptx, center.y() = pty;//*/
1983
1984                 lButtonDown = false;
1985
1986                 if ( translatingX || translatingY ) {
1987                         GlobalUndoSystem().finish( "translateTexture" );
1988                 }
1989                 else if ( rotating ) {
1990                         GlobalUndoSystem().finish( "rotateTexture" );
1991                 }
1992                 else if ( scalingX || scalingY ) {
1993                         GlobalUndoSystem().finish( "scaleTexture" );
1994                 }
1995                 else if ( resizingX || resizingY ) {
1996                         GlobalUndoSystem().finish( "resizeTexture" );
1997                 }
1998                 else
1999                 {
2000                         GlobalUndoSystem().finish( "textoolUnknown" );
2001                 }
2002
2003                 rotating = translatingX = translatingY = scalingX = scalingY
2004                                                                                                                                 = resizingX = resizingY = false;
2005
2006                 queueDraw();
2007         }
2008         else if ( e->button == 3 ) {
2009                 rButtonDown = false;
2010         }
2011
2012         return true;
2013 }
2014
2015 /*
2016    void C2DView::GridForWindow( float c[2], int x, int y)
2017    {
2018    SpaceForWindow( c, x, y );
2019    if ( !m_bDoGrid )
2020     return;
2021    c[0] /= m_GridStep[0];
2022    c[1] /= m_GridStep[1];
2023    c[0] = (float)floor( c[0] + 0.5f );
2024    c[1] = (float)floor( c[1] + 0.5f );
2025    c[0] *= m_GridStep[0];
2026    c[1] *= m_GridStep[1];
2027    }
2028    void C2DView::SpaceForWindow( float c[2], int x, int y)
2029    {
2030    c[0] = ((float)(x))/((float)(m_rect.right-m_rect.left))*(m_Maxs[0]-m_Mins[0])+m_Mins[0];
2031    c[1] = ((float)(y))/((float)(m_rect.bottom-m_rect.top))*(m_Maxs[1]-m_Mins[1])+m_Mins[1];
2032    }
2033  */
2034 gboolean motion( ui::Widget win, GdkEventMotion * e, gpointer ){
2035 //      globalOutputStream() << "--> Textool motion...\n";
2036
2037         if ( lButtonDown ) {
2038                 if ( translatingX || translatingY ) {
2039                         float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2040                         float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2041
2042 //Need to fix this to take the rotation angle into account, so that it moves along
2043 //the rotated X/Y axis...
2044                         if ( translatingX ) {
2045 //                              tm.coords[0][2] = (trans.x() + ptx) * textureSize.x();
2046 //This works, but only when the angle is zero. !!! FIX !!! [DONE]
2047 //                              tm.coords[0][2] = oldCenter.x() + (ptx * textureSize.x());
2048                                 tm.coords[0][2] = oldTrans.x() + ( ptx - trans2.x() ) * textureSize.x();
2049 //                              center.x() = oldCenter.x() + (ptx - trans2.x());
2050                         }
2051
2052                         if ( translatingY ) {
2053 //                              tm.coords[1][2] = (trans.y() + pty) * textureSize.y();
2054 //                              tm.coords[1][2] = oldCenter.y() + (pty * textureSize.y());
2055                                 tm.coords[1][2] = oldTrans.y() + ( pty - trans2.y() ) * textureSize.y();
2056 //                              center.y() = oldCenter.y() + (pty - trans2.y());
2057                         }
2058
2059 //Need to update center.x/y() so that the widget translates as well. Also, oldCenter
2060 //is badly named... Should be oldTrans or something like that... !!! FIX !!!
2061 //Changing center.x/y() here doesn't seem to change anything... :-/
2062                         UpdateControlPoints();
2063                 }
2064                 else if ( rotating ) {
2065                         // Shamus: New rotate code
2066                         int cx = (int)( windowSize.x() * ( center.x() - extents.minX ) / extents.width() );
2067                         int cy = (int)( windowSize.y() * ( center.y() - extents.minY ) / extents.height() );
2068                         Vector3 v1( dragPoint.x() - cx, dragPoint.y() - cy, 0 ), v2( e->x - cx, e->y - cy, 0 );
2069
2070                         vector3_normalise( v1 );
2071                         vector3_normalise( v2 );
2072                         float c = vector3_dot( v1, v2 );
2073                         Vector3 cross = vector3_cross( v1, v2 );
2074                         float s = vector3_length( cross );
2075
2076                         if ( cross[2] > 0 ) {
2077                                 s = -s;
2078                         }
2079
2080 // Problem with this: arcsin/cos seems to only return -90 to 90 and 0 to 180...
2081 // Can't derive angle from that!
2082
2083 //rotationAngle = asin(s);// * 180.0f / 3.141592653589f;
2084                         rotationAngle = acos( c );
2085 //rotationAngle2 = asin(s);
2086                         if ( cross[2] < 0 ) {
2087                                 rotationAngle = -rotationAngle;
2088                         }
2089
2090 //NO! DOESN'T WORK! rotationAngle -= 45.0f * DEG_TO_RAD;
2091 //Let's try this:
2092 //No wok.
2093 /*c = cos(rotationAngle - oldRotationAngle);
2094    s = sin(rotationAngle - oldRotationAngle);
2095    rotationAngle += oldRotationAngle;
2096    //c += cos(oldRotationAngle);
2097    //s += sin(oldRotationAngle);
2098    //rotationAngle += oldRotationAngle;
2099    //c %= 2.0 * PI;
2100    //s %= 2.0 * PI;
2101    //rotationAngle %= 2.0 * PI;//*/
2102
2103 //This is wrong... Hmm...
2104 //It seems to shear the texture instead of rotating it... !!! FIX !!!
2105 // Now it rotates correctly. Seems TTimo was overcomplicating things here... ;-)
2106
2107 // Seems like what needs to happen here is multiplying these rotations by tm... !!! FIX !!!
2108
2109 // See brush_primit.cpp line 244 (Texdef_EmitTextureCoordinates()) for where texcoords come from...
2110
2111                         tm.coords[0][0] =  c;
2112                         tm.coords[0][1] =  s;
2113                         tm.coords[1][0] = -s;
2114                         tm.coords[1][1] =  c;
2115 //It doesn't work anymore... Dunno why...
2116 //tm.coords[0][2] = -trans.x();                 // This works!!! Yeah!!!
2117 //tm.coords[1][2] = -trans.y();
2118 //nope.
2119 //tm.coords[0][2] = rotationPoint.x();  // This works, but strangely...
2120 //tm.coords[1][2] = rotationPoint.y();
2121 //tm.coords[0][2] = 0;// center.x() / 2.0f;
2122 //tm.coords[1][2] = 0;// center.y() / 2.0f;
2123 //No.
2124 //tm.coords[0][2] = -(center.x() * textureSize.x());
2125 //tm.coords[1][2] = -(center.y() * textureSize.y());
2126 //Eh? No, but seems to be getting closer...
2127 /*float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2128    float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2129    tm.coords[0][2] = -c * center.x() - s * center.y() + ptx;
2130    tm.coords[1][2] =  s * center.x() - c * center.x() + pty;//*/
2131 //Kinda works, but center drifts around on non-square textures...
2132 /*tm.coords[0][2] = (-c * center.x() - s * center.y()) * textureSize.x();
2133    tm.coords[1][2] = ( s * center.x() - c * center.y()) * textureSize.y();//*/
2134 //Rotates correctly, but not around the actual center of the face's points...
2135 /*tm.coords[0][2] = -c * center.x() * textureSize.x() - s * center.y() * textureSize.y();
2136    tm.coords[1][2] =  s * center.x() * textureSize.x() - c * center.y() * textureSize.y();//*/
2137 //Yes!!!
2138                         tm.coords[0][2] = ( -c * center.x() * textureSize.x() - s * center.y() * textureSize.y() ) + center.x() * textureSize.x();
2139                         tm.coords[1][2] = ( s * center.x() * textureSize.x() - c * center.y() * textureSize.y() ) + center.y() * textureSize.y(); //*/
2140 //This doesn't work...
2141 //And this is the wrong place for this anyway (I'm pretty sure).
2142 /*tm.coords[0][2] += oldCenter.x();
2143    tm.coords[1][2] += oldCenter.y();//*/
2144                         UpdateControlPoints(); // will cause a redraw
2145                 }
2146
2147                 return true;
2148         }
2149         else                                    // Check for widget mouseovers
2150         {
2151                 Vector2 tran;
2152                 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2153                 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2154                 // Translate nx/y to the "center" point...
2155                 nx -= center.x();
2156                 ny -= center.y();
2157                 ny = -ny;   // Flip Y-axis so that increasing numbers move up
2158
2159                 tran.x() = tm.coords[0][0] * nx + tm.coords[0][1] * ny;
2160                 tran.y() = tm.coords[1][0] * nx + tm.coords[1][1] * ny;
2161 //This doesn't seem to generate a valid distance from the center--for some reason it
2162 //calculates a fixed number every time
2163 //Look at nx/y above: they're getting fixed there! !!! FIX !!! [DONE]
2164                 float dist = sqrt( ( nx * nx ) + ( ny * ny ) );
2165                 // Normalize to the 2.0 = height standard (for now)
2166 //globalOutputStream() << "--> Distance before: " << dist;
2167                 dist = dist * 2.0f / extents.height();
2168 //globalOutputStream() << ". After: " << dist;
2169                 tran.x() = tran.x() * 2.0f / extents.height();
2170                 tran.y() = tran.y() * 2.0f / extents.height();
2171 //globalOutputStream() << ". Trans: " << tran.x() << ", " << tran.y() << "\n";
2172
2173 //Let's try this instead...
2174 //Interesting! It seems that e->x/y are rotated
2175 //(no, they're not--the TM above is what's doing it...)
2176                 nx = ( ( e->x / windowSize.y() ) * 2.0f ) - ( windowSize.x() / windowSize.y() );
2177                 ny = ( ( e->y / windowSize.y() ) * 2.0f ) - ( windowSize.y() / windowSize.y() );
2178                 ny = -ny;
2179 //Cool! It works! Now just need to do rotation...
2180
2181                 rotating = translatingX = translatingY = scalingX = scalingY
2182                                                                                                                                 = resizingX = resizingY = false;
2183
2184                 if ( dist < ( gridRadius * 0.16f ) ) {
2185                         translatingX = translatingY = true;
2186                 }
2187                 else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2188                                   && fabs( ny ) < ( gridRadius * 0.05f ) && nx > 0 ) {
2189                         translatingX = true;
2190                 }
2191                 else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2192                                   && fabs( nx ) < ( gridRadius * 0.05f ) && ny > 0 ) {
2193                         translatingY = true;
2194                 }
2195                 // Should tighten up the angle on this, or put this test after the axis tests...
2196                 else if ( tran.x() > 0 && tran.y() > 0
2197                                   && ( dist > ( gridRadius * 0.82f ) && dist < ( gridRadius * 0.98f ) ) ) {
2198                         rotating = true;
2199                 }
2200
2201                 queueDraw();
2202
2203                 return true;
2204         }
2205
2206         return false;
2207 }
2208
2209 //It seems the fake tex coords conversion is screwing this stuff up... !!! FIX !!!
2210 //This is still wrong... Prolly need to do something with the oldScaleX/Y stuff...
2211 void flipX( GtkToggleButton *, gpointer ){
2212 //      globalOutputStream() << "--> Flip X...\n";
2213         //Shamus:
2214 //      SurfaceInspector_GetSelectedBPTexdef();         // Refresh g_selectedBrushPrimitTexdef...
2215 //      tm.coords[0][0] = -tm.coords[0][0];
2216 //      tm.coords[1][0] = -tm.coords[1][0];
2217 //      tm.coords[0][0] = -tm.coords[0][0];                     // This should be correct now...Nope.
2218 //      tm.coords[1][1] = -tm.coords[1][1];
2219         tm.coords[0][0] = -tm.coords[0][0];         // This should be correct now...
2220         tm.coords[1][0] = -tm.coords[1][0];
2221 //      tm.coords[2][0] = -tm.coords[2][0];//wil wok? no.
2222         UpdateControlPoints();
2223 }
2224
2225 void flipY( GtkToggleButton *, gpointer ){
2226 //      globalOutputStream() << "--> Flip Y...\n";
2227 //      tm.coords[0][1] = -tm.coords[0][1];
2228 //      tm.coords[1][1] = -tm.coords[1][1];
2229 //      tm.coords[0][1] = -tm.coords[0][1];                     // This should be correct now...Nope.
2230 //      tm.coords[1][0] = -tm.coords[1][0];
2231         tm.coords[0][1] = -tm.coords[0][1];         // This should be correct now...
2232         tm.coords[1][1] = -tm.coords[1][1];
2233 //      tm.coords[2][1] = -tm.coords[2][1];//wil wok? no.
2234         UpdateControlPoints();
2235 }
2236
2237 } // end namespace TexTool
2238
2239 #endif