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