]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/surfacedialog.cpp
Merge commit '5e3961896b9e30ebbc736b542f177054188c05bf' into master-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(); //happens in .ShowDlg() anyway
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_FitTexture( getSurfaceInspector().m_fitHorizontal, 0 );
437 }
438
439 void SurfaceInspector_FitTextureH(){
440         UndoableCommand undo( "textureAutoFitH" );
441         Select_FitTexture( 0, 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                                 }
822                                 {
823                                         ui::Widget label = ui::Label( "Height" );
824                                         label.show();
825                                         table.attach(label, {3, 4, 0, 1}, {GTK_FILL, 0});
826                                 }
827                                 {
828                                         ui::Widget button = ui::Button( "Axial" );
829                                         button.show();
830                                         table.attach(button, {0, 1, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
831                                         button.connect( "clicked",
832                                                                           G_CALLBACK( OnBtnAxial ), 0 );
833                     button.dimensions(60, -1);
834                                 }
835                                 {
836                                         ui::Widget button = ui::Button( "Fit" );
837                                         button.show();
838                                         table.attach(button, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
839                                         button.connect( "clicked",
840                                                                           G_CALLBACK( OnBtnFaceFit ), 0 );
841                     button.dimensions(60, -1);
842                                 }
843                                 {
844                                         ui::Widget button = ui::Button( "CAP" );
845                                         button.show();
846                                         table.attach(button, {0, 1, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
847                                         button.connect( "clicked",
848                                                                           G_CALLBACK( OnBtnPatchdetails ), 0 );
849                     button.dimensions(60, -1);
850                                 }
851                                 {
852                                         ui::Widget button = ui::Button( "Set..." );
853                                         button.show();
854                                         table.attach(button, {1, 2, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
855                                         button.connect( "clicked",
856                                                                           G_CALLBACK( OnBtnPatchreset ), 0 );
857                     button.dimensions(60, -1);
858                                 }
859                                 {
860                                         ui::Widget button = ui::Button( "Natural" );
861                                         button.show();
862                                         table.attach(button, {2, 3, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
863                                         button.connect( "clicked",
864                                                                           G_CALLBACK( OnBtnPatchnatural ), 0 );
865                     button.dimensions(60, -1);
866                                 }
867                                 {
868                                         ui::Widget button = ui::Button( "Fit" );
869                                         button.show();
870                                         table.attach(button, {3, 4, 3, 4}, {GTK_EXPAND | GTK_FILL, 0});
871                                         button.connect( "clicked",
872                                                                           G_CALLBACK( OnBtnPatchFit ), 0 );
873                     button.dimensions(60, -1);
874                                 }
875                                 {
876                                         auto spin = ui::SpinButton( ui::Adjustment( 1, 0, 1 << 16, 1, 10, 0 ), 0, 6 );
877                                         spin.show();
878                                         table.attach(spin, {2, 3, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
879                     spin.dimensions(60, -1);
880                                         AddDialogData( spin, m_fitHorizontal );
881                                 }
882                                 {
883                                         auto spin = ui::SpinButton( ui::Adjustment( 1, 0, 1 << 16, 1, 10, 0 ), 0, 6 );
884                                         spin.show();
885                                         table.attach(spin, {3, 4, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
886                     spin.dimensions(60, -1);
887                                         AddDialogData( spin, m_fitVertical );
888                                 }
889                         }
890                 }
891                 if ( !string_empty( g_pGameDescription->getKeyValue( "si_flags" ) ) ) {
892                         {
893                                 auto frame = ui::Frame( "Surface Flags" );
894                                 frame.show();
895                                 vbox.pack_start( frame, TRUE, TRUE, 0 );
896                                 {
897                                         auto vbox3 = ui::VBox( FALSE, 4 );
898                                         //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
899                                         vbox3.show();
900                                         frame.add(vbox3);
901                                         {
902                                                 auto table = ui::Table( 8, 4, FALSE );
903                                                 table.show();
904                                                 vbox3.pack_start( table, TRUE, TRUE, 0 );
905                                                 gtk_table_set_row_spacings( table, 0 );
906                                                 gtk_table_set_col_spacings( table, 0 );
907
908                                                 GtkCheckButton** p = m_surfaceFlags;
909
910                                                 for (unsigned int c = 0; c != 4; ++c)
911                                                 {
912                                                         for (unsigned int r = 0; r != 8; ++r)
913                                                         {
914                                                                 auto check = ui::CheckButton( getSurfaceFlagName( c * 8 + r ) );
915                                                                 check.show();
916                                                                 table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0});
917                                                                 *p++ = check;
918                                                                 guint handler_id = togglebutton_connect_toggled( check, ApplyFlagsCaller( *this ) );
919                                                                 g_object_set_data( G_OBJECT( check ), "handler", gint_to_pointer( handler_id ) );
920                                                         }
921                                                 }
922                                         }
923                                 }
924                         }
925                         {
926                                 auto frame = ui::Frame( "Content Flags" );
927                                 frame.show();
928                                 vbox.pack_start( frame, TRUE, TRUE, 0 );
929                                 {
930                                         auto vbox3 = ui::VBox( FALSE, 4 );
931                                         //gtk_container_set_border_width(GTK_CONTAINER(vbox3), 4);
932                                         vbox3.show();
933                                         frame.add(vbox3);
934                                         {
935
936                                                 auto table = ui::Table( 8, 4, FALSE );
937                                                 table.show();
938                                                 vbox3.pack_start( table, TRUE, TRUE, 0 );
939                                                 gtk_table_set_row_spacings( table, 0 );
940                                                 gtk_table_set_col_spacings( table, 0 );
941
942                                                 GtkCheckButton** p = m_contentFlags;
943
944                                                 for (unsigned int c = 0; c != 4; ++c)
945                                                 {
946                                                         for (unsigned int r = 0; r != 8; ++r)
947                                                         {
948                                                                 auto check = ui::CheckButton( getContentFlagName( c * 8 + r ) );
949                                                                 check.show();
950                                                                 table.attach(check, {c, c + 1, r, r + 1}, {GTK_EXPAND | GTK_FILL, 0});
951                                                                 *p++ = check;
952                                                                 guint handler_id = togglebutton_connect_toggled( check, ApplyFlagsCaller( *this ) );
953                                                                 g_object_set_data( G_OBJECT( check ), "handler", gint_to_pointer( handler_id ) );
954                                                         }
955                                                 }
956
957                                                 // not allowed to modify detail flag using Surface Inspector
958                                                 gtk_widget_set_sensitive( ui::CheckButton::from(m_contentFlags[BRUSH_DETAIL_FLAG]), FALSE );
959                                         }
960                                 }
961                         }
962                         {
963                                 auto frame = ui::Frame( "Value" );
964                                 frame.show();
965                                 vbox.pack_start( frame, TRUE, TRUE, 0 );
966                                 {
967                                         auto vbox3 = ui::VBox( FALSE, 4 );
968                                         gtk_container_set_border_width( GTK_CONTAINER( vbox3 ), 4 );
969                                         vbox3.show();
970                                         frame.add(vbox3);
971
972                                         {
973                                                 auto entry = ui::Entry(ui::New);
974                                                 entry.show();
975                                                 vbox3.pack_start( entry, TRUE, TRUE, 0 );
976                                                 m_valueEntryWidget = entry;
977                                                 m_valueEntry.connect( entry );
978                                         }
979                                 }
980                         }
981                 }
982
983 #if TEXTOOL_ENABLED
984                 if ( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_BRUSHPRIMITIVES ) {
985 // Shamus: Textool goodies...
986                         ui::Widget frame = ui::Frame( "Textool" );
987                         frame.show();
988                         vbox.pack_start( frame , FALSE, FALSE, 0 );
989                         {
990                                 //Prolly should make this a member or global var, so the SI can draw on it...
991                                 TexTool::g_textoolWin = glwidget_new( FALSE );
992                                 // --> Dunno, but this stuff may be necessary... (Looks like it!)
993                                 g_object_ref( TexTool::g_textoolWin );
994                                 gtk_widget_set_events( TexTool::g_textoolWin, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK );
995                                 gtk_widget_set_can_focus( TexTool::g_textoolWin, true );
996                                 // <-- end stuff...
997                                 TexTool::g_textoolWin.show();
998                                 TexTool::g_textoolWin.dimensions( -1, 240 ); //Yeah!
999                                 frame.add(TexTool::g_textoolWin);
1000
1001                                 TexTool::g_textoolWin.connect( "size_allocate", G_CALLBACK( TexTool::size_allocate ), NULL );
1002                                 TexTool::g_textoolWin.connect( "expose_event", G_CALLBACK( TexTool::expose ), NULL );
1003                                 TexTool::g_textoolWin.connect( "button_press_event", G_CALLBACK( TexTool::button_press ), NULL );
1004                                 TexTool::g_textoolWin.connect( "button_release_event", G_CALLBACK( TexTool::button_release ), NULL );
1005                                 TexTool::g_textoolWin.connect( "motion_notify_event", G_CALLBACK( TexTool::motion ), NULL );
1006                         }
1007                         {
1008                                 ui::Widget hbox = ui::HBox( FALSE, 5 );
1009                                 hbox.show();
1010                                 vbox.pack_start( hbox , FALSE, FALSE, 0 );
1011                                 // Checkboxes go here... (Flip X/Y)
1012                                 ui::Widget flipX = ui::CheckButton( "Flip X axis" );
1013                                 ui::Widget flipY = ui::CheckButton( "Flip Y axis" );
1014                                 flipX.show();
1015                                 flipY.show();
1016                                 hbox.pack_start( flipX, FALSE, FALSE, 0 );
1017                                 hbox.pack_start( flipY, FALSE, FALSE, 0 );
1018
1019 //Instead of this, we probably need to create a vbox to put into the frame, then the
1020 //window, then the hbox. !!! FIX !!!
1021 //        frame.add(hbox);
1022
1023 //Hmm. Do we really need g_object_set_data? Mebbe not... And we don't! :-)
1024 //        g_object_set_data(G_OBJECT(flipX), "handler", gint_to_pointer(flipX.connect("toggled", G_CALLBACK(TexTool::flipX), 0)));
1025 //        g_object_set_data(G_OBJECT(flipY), "handler", gint_to_pointer(flipY.connect("toggled", G_CALLBACK(TexTool::flipY), 0)));
1026 //Instead, just do:
1027                                 flipX.connect( "toggled", G_CALLBACK( TexTool::flipX ), NULL );
1028                                 flipY.connect( "toggled", G_CALLBACK( TexTool::flipY ), NULL );
1029                         }
1030                 }
1031 #endif
1032         }
1033
1034         return window;
1035 }
1036
1037 /*
1038    ==============
1039    Update
1040
1041    Set the fields to the current texdef (i.e. map/texdef -> dialog widgets)
1042    if faces selected (instead of brushes) -> will read this face texdef, else current texdef
1043    if only patches selected, will read the patch texdef
1044    ===============
1045  */
1046
1047 void spin_button_set_value_no_signal( ui::SpinButton spin, gdouble value ){
1048         guint handler_id = gpointer_to_int( g_object_get_data( G_OBJECT( spin ), "handler" ) );
1049         g_signal_handler_block( G_OBJECT( gtk_spin_button_get_adjustment( spin ) ), handler_id );
1050         gtk_spin_button_set_value( spin, value );
1051         g_signal_handler_unblock( G_OBJECT( gtk_spin_button_get_adjustment( spin ) ), handler_id );
1052 }
1053
1054 void spin_button_set_step_increment( ui::SpinButton spin, gdouble value ){
1055         auto adjust = gtk_spin_button_get_adjustment( spin );
1056         gtk_adjustment_set_step_increment(adjust, value);
1057 }
1058
1059 void SurfaceInspector::Update(){
1060         const char * name = SurfaceInspector_GetSelectedShader();
1061
1062         if ( shader_is_texture( name ) ) {
1063                 m_texture.text(shader_get_textureName(name));
1064         }
1065         else
1066         {
1067                 m_texture.text("");
1068         }
1069
1070         texdef_t shiftScaleRotate;
1071 //Shamus: This is where we get into trouble--the BP code tries to convert to a "faked"
1072 //shift, rotate & scale values from the brush face, which seems to screw up for some reason.
1073 //!!! FIX !!!
1074 /*globalOutputStream() << "--> SI::Update. About to do ShiftScaleRotate_fromFace()...\n";
1075    SurfaceInspector_GetSelectedBPTexdef();
1076    globalOutputStream() << "BP: (" << g_selectedBrushPrimitTexdef.coords[0][0] << ", " << g_selectedBrushPrimitTexdef.coords[0][1] << ")("
1077     << g_selectedBrushPrimitTexdef.coords[1][0] << ", " << g_selectedBrushPrimitTexdef.coords[1][1] << ")("
1078     << g_selectedBrushPrimitTexdef.coords[0][2] << ", " << g_selectedBrushPrimitTexdef.coords[1][2] << ") SurfaceInspector::Update\n";//*/
1079 //Ok, it's screwed up *before* we get here...
1080         ShiftScaleRotate_fromFace( shiftScaleRotate, SurfaceInspector_GetSelectedTexdef() );
1081
1082         // normalize again to hide the ridiculously high scale values that get created when using texlock
1083         shiftScaleRotate.shift[0] = float_mod( shiftScaleRotate.shift[0], (float)g_selectedShaderSize[0] );
1084         shiftScaleRotate.shift[1] = float_mod( shiftScaleRotate.shift[1], (float)g_selectedShaderSize[1] );
1085
1086         {
1087                 spin_button_set_value_no_signal( m_hshiftIncrement.m_spin, shiftScaleRotate.shift[0] );
1088                 spin_button_set_step_increment( m_hshiftIncrement.m_spin, g_si_globals.shift[0] );
1089                 entry_set_float( m_hshiftIncrement.m_entry, g_si_globals.shift[0] );
1090         }
1091
1092         {
1093                 spin_button_set_value_no_signal( m_vshiftIncrement.m_spin, shiftScaleRotate.shift[1] );
1094                 spin_button_set_step_increment( m_vshiftIncrement.m_spin, g_si_globals.shift[1] );
1095                 entry_set_float( m_vshiftIncrement.m_entry, g_si_globals.shift[1] );
1096         }
1097
1098         {
1099                 spin_button_set_value_no_signal( m_hscaleIncrement.m_spin, shiftScaleRotate.scale[0] );
1100                 spin_button_set_step_increment( m_hscaleIncrement.m_spin, g_si_globals.scale[0] );
1101                 entry_set_float( m_hscaleIncrement.m_entry, g_si_globals.scale[0] );
1102         }
1103
1104         {
1105                 spin_button_set_value_no_signal( m_vscaleIncrement.m_spin, shiftScaleRotate.scale[1] );
1106                 spin_button_set_step_increment( m_vscaleIncrement.m_spin, g_si_globals.scale[1] );
1107                 entry_set_float( m_vscaleIncrement.m_entry, g_si_globals.scale[1] );
1108         }
1109
1110         {
1111                 spin_button_set_value_no_signal( m_rotateIncrement.m_spin, shiftScaleRotate.rotate );
1112                 spin_button_set_step_increment( m_rotateIncrement.m_spin, g_si_globals.rotate );
1113                 entry_set_float( m_rotateIncrement.m_entry, g_si_globals.rotate );
1114         }
1115
1116         if ( !string_empty( g_pGameDescription->getKeyValue( "si_flags" ) ) ) {
1117                 ContentsFlagsValue flags( SurfaceInspector_GetSelectedFlags() );
1118
1119                 entry_set_int( m_valueEntryWidget, flags.m_value );
1120
1121                 for ( GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p )
1122                 {
1123                         toggle_button_set_active_no_signal( ui::CheckButton::from( *p ), flags.m_surfaceFlags & ( 1 << ( p - m_surfaceFlags ) ) );
1124                 }
1125
1126                 for ( GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p )
1127                 {
1128                         toggle_button_set_active_no_signal( ui::CheckButton::from( *p ), flags.m_contentFlags & ( 1 << ( p - m_contentFlags ) ) );
1129                 }
1130         }
1131 }
1132
1133 /*
1134    ==============
1135    Apply
1136
1137    Reads the fields to get the current texdef (i.e. widgets -> MAP)
1138    in brush primitive mode, grab the fake shift scale rot and compute a new texture matrix
1139    ===============
1140  */
1141 void SurfaceInspector::ApplyShader(){
1142         StringOutputStream name( 256 );
1143         name << GlobalTexturePrefix_get() << gtk_entry_get_text( m_texture );
1144
1145         // TTimo: detect and refuse invalid texture names (at least the ones with spaces)
1146         if ( !texdef_name_valid( name.c_str() ) ) {
1147                 globalErrorStream() << "invalid texture name '" << name.c_str() << "'\n";
1148                 SurfaceInspector_queueDraw();
1149                 return;
1150         }
1151
1152         UndoableCommand undo( "textureNameSetSelected" );
1153         Select_SetShader( name.c_str() );
1154 }
1155
1156 void SurfaceInspector::ApplyTexdef(){
1157         texdef_t shiftScaleRotate;
1158
1159         shiftScaleRotate.shift[0] = static_cast<float>( gtk_spin_button_get_value( m_hshiftIncrement.m_spin ) );
1160         shiftScaleRotate.shift[1] = static_cast<float>( gtk_spin_button_get_value( m_vshiftIncrement.m_spin ) );
1161         shiftScaleRotate.scale[0] = static_cast<float>( gtk_spin_button_get_value( m_hscaleIncrement.m_spin ) );
1162         shiftScaleRotate.scale[1] = static_cast<float>( gtk_spin_button_get_value( m_vscaleIncrement.m_spin ) );
1163         shiftScaleRotate.rotate = static_cast<float>( gtk_spin_button_get_value( m_rotateIncrement.m_spin ) );
1164
1165         TextureProjection projection;
1166 //Shamus: This is the other place that screws up, it seems, since it doesn't seem to do the
1167 //conversion from the face (I think) and so bogus values end up in the thing... !!! FIX !!!
1168 //This is actually OK. :-P
1169         ShiftScaleRotate_toFace( shiftScaleRotate, projection );
1170
1171         UndoableCommand undo( "textureProjectionSetSelected" );
1172         Select_SetTexdef( projection );
1173 }
1174
1175 void SurfaceInspector::ApplyFlags(){
1176         unsigned int surfaceflags = 0;
1177         for ( GtkCheckButton** p = m_surfaceFlags; p != m_surfaceFlags + 32; ++p )
1178         {
1179                 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( *p ) ) ) {
1180                         surfaceflags |= ( 1 << ( p - m_surfaceFlags ) );
1181                 }
1182         }
1183
1184         unsigned int contentflags = 0;
1185         for ( GtkCheckButton** p = m_contentFlags; p != m_contentFlags + 32; ++p )
1186         {
1187                 if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( *p ) ) ) {
1188                         contentflags |= ( 1 << ( p - m_contentFlags ) );
1189                 }
1190         }
1191
1192         int value = entry_get_int( m_valueEntryWidget );
1193
1194         UndoableCommand undo( "flagsSetSelected" );
1195         Select_SetFlags( ContentsFlagsValue( surfaceflags, contentflags, value, true ) );
1196 }
1197
1198
1199 void Face_getTexture( Face& face, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags ){
1200         shader = face.GetShader();
1201         face.GetTexdef( projection );
1202         flags = face.getShader().m_flags;
1203 }
1204 typedef Function<void(Face&, CopiedString&, TextureProjection&, ContentsFlagsValue&), Face_getTexture> FaceGetTexture;
1205
1206 void Face_setTexture( Face& face, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags ){
1207         face.SetShader( shader );
1208         face.SetTexdef( projection );
1209         face.SetFlags( flags );
1210 }
1211 typedef Function<void(Face&, const char*, const TextureProjection&, const ContentsFlagsValue&), Face_setTexture> FaceSetTexture;
1212
1213
1214 void Patch_getTexture( Patch& patch, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags ){
1215         shader = patch.GetShader();
1216         projection = TextureProjection( texdef_t(), brushprimit_texdef_t(), Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) );
1217         flags = ContentsFlagsValue( 0, 0, 0, false );
1218 }
1219 typedef Function<void(Patch&, CopiedString&, TextureProjection&, ContentsFlagsValue&), Patch_getTexture> PatchGetTexture;
1220
1221 void Patch_setTexture( Patch& patch, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags ){
1222         patch.SetShader( shader );
1223 }
1224 typedef Function<void(Patch&, const char*, const TextureProjection&, const ContentsFlagsValue&), Patch_setTexture> PatchSetTexture;
1225
1226
1227 typedef Callback<void(CopiedString&, TextureProjection&, ContentsFlagsValue&)> GetTextureCallback;
1228 typedef Callback<void(const char*, const TextureProjection&, const ContentsFlagsValue&)> SetTextureCallback;
1229
1230 struct Texturable
1231 {
1232         GetTextureCallback getTexture;
1233         SetTextureCallback setTexture;
1234 };
1235
1236
1237 void Face_getClosest( Face& face, SelectionTest& test, SelectionIntersection& bestIntersection, Texturable& texturable ){
1238         SelectionIntersection intersection;
1239         face.testSelect( test, intersection );
1240         if ( intersection.valid()
1241                  && SelectionIntersection_closer( intersection, bestIntersection ) ) {
1242                 bestIntersection = intersection;
1243                 texturable.setTexture = makeCallback( FaceSetTexture(), face );
1244                 texturable.getTexture = makeCallback( FaceGetTexture(), face );
1245         }
1246 }
1247
1248
1249 class OccludeSelector : public Selector
1250 {
1251 SelectionIntersection& m_bestIntersection;
1252 bool& m_occluded;
1253 public:
1254 OccludeSelector( SelectionIntersection& bestIntersection, bool& occluded ) : m_bestIntersection( bestIntersection ), m_occluded( occluded ){
1255         m_occluded = false;
1256 }
1257 void pushSelectable( Selectable& selectable ){
1258 }
1259 void popSelectable(){
1260 }
1261 void addIntersection( const SelectionIntersection& intersection ){
1262         if ( SelectionIntersection_closer( intersection, m_bestIntersection ) ) {
1263                 m_bestIntersection = intersection;
1264                 m_occluded = true;
1265         }
1266 }
1267 };
1268
1269 class BrushGetClosestFaceVisibleWalker : public scene::Graph::Walker
1270 {
1271 SelectionTest& m_test;
1272 Texturable& m_texturable;
1273 mutable SelectionIntersection m_bestIntersection;
1274 public:
1275 BrushGetClosestFaceVisibleWalker( SelectionTest& test, Texturable& texturable ) : m_test( test ), m_texturable( texturable ){
1276 }
1277 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1278         if ( path.top().get().visible() ) {
1279                 BrushInstance* brush = Instance_getBrush( instance );
1280                 if ( brush != 0 ) {
1281                         m_test.BeginMesh( brush->localToWorld() );
1282
1283                         for ( Brush::const_iterator i = brush->getBrush().begin(); i != brush->getBrush().end(); ++i )
1284                         {
1285                                 Face_getClosest( *( *i ), m_test, m_bestIntersection, m_texturable );
1286                         }
1287                 }
1288                 else
1289                 {
1290                         SelectionTestable* selectionTestable = Instance_getSelectionTestable( instance );
1291                         if ( selectionTestable ) {
1292                                 bool occluded;
1293                                 OccludeSelector selector( m_bestIntersection, occluded );
1294                                 selectionTestable->testSelect( selector, m_test );
1295                                 if ( occluded ) {
1296                                         Patch* patch = Node_getPatch( path.top() );
1297                                         if ( patch != 0 ) {
1298                                                 m_texturable.setTexture = makeCallback( PatchSetTexture(), *patch );
1299                                                 m_texturable.getTexture = makeCallback( PatchGetTexture(), *patch );
1300                                         }
1301                                         else
1302                                         {
1303                                                 m_texturable = Texturable();
1304                                         }
1305                                 }
1306                         }
1307                 }
1308         }
1309         else{
1310                 return false;
1311         }
1312         return true;
1313 }
1314 };
1315
1316 Texturable Scene_getClosestTexturable( scene::Graph& graph, SelectionTest& test ){
1317         Texturable texturable;
1318         graph.traverse( BrushGetClosestFaceVisibleWalker( test, texturable ) );
1319         return texturable;
1320 }
1321
1322 bool Scene_getClosestTexture( scene::Graph& graph, SelectionTest& test, CopiedString& shader, TextureProjection& projection, ContentsFlagsValue& flags ){
1323         Texturable texturable = Scene_getClosestTexturable( graph, test );
1324         if ( texturable.getTexture != GetTextureCallback() ) {
1325                 texturable.getTexture( shader, projection, flags );
1326                 return true;
1327         }
1328         return false;
1329 }
1330
1331 void Scene_setClosestTexture( scene::Graph& graph, SelectionTest& test, const char* shader, const TextureProjection& projection, const ContentsFlagsValue& flags ){
1332         Texturable texturable = Scene_getClosestTexturable( graph, test );
1333         if ( texturable.setTexture != SetTextureCallback() ) {
1334                 texturable.setTexture( shader, projection, flags );
1335         }
1336 }
1337
1338
1339 class FaceTexture
1340 {
1341 public:
1342 TextureProjection m_projection;
1343 ContentsFlagsValue m_flags;
1344 };
1345
1346 FaceTexture g_faceTextureClipboard;
1347
1348 void FaceTextureClipboard_setDefault(){
1349         g_faceTextureClipboard.m_flags = ContentsFlagsValue( 0, 0, 0, false );
1350         TexDef_Construct_Default( g_faceTextureClipboard.m_projection );
1351 }
1352
1353 void TextureClipboard_textureSelected( const char* shader ){
1354         FaceTextureClipboard_setDefault();
1355 }
1356
1357 class TextureBrowser;
1358 void TextureBrowser_SetSelectedShader( TextureBrowser& textureBrowser, const char* shader );
1359 const char* TextureBrowser_GetSelectedShader( TextureBrowser& textureBrowser );
1360
1361 void Scene_copyClosestTexture( SelectionTest& test ){
1362         CopiedString shader;
1363         if ( Scene_getClosestTexture( GlobalSceneGraph(), test, shader, g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags ) ) {
1364                 TextureBrowser_SetSelectedShader( GlobalTextureBrowser(), shader.c_str() );
1365         }
1366 }
1367
1368 void Scene_applyClosestTexture( SelectionTest& test ){
1369         UndoableCommand command( "facePaintTexture" );
1370
1371         Scene_setClosestTexture( GlobalSceneGraph(), test, TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ), g_faceTextureClipboard.m_projection, g_faceTextureClipboard.m_flags );
1372
1373         SceneChangeNotify();
1374 }
1375
1376
1377
1378
1379
1380 void SelectedFaces_copyTexture(){
1381         if ( !g_SelectedFaceInstances.empty() ) {
1382                 Face& face = g_SelectedFaceInstances.last().getFace();
1383                 face.GetTexdef( g_faceTextureClipboard.m_projection );
1384                 g_faceTextureClipboard.m_flags = face.getShader().m_flags;
1385
1386                 TextureBrowser_SetSelectedShader( GlobalTextureBrowser(), face.getShader().getShader() );
1387         }
1388 }
1389
1390 void FaceInstance_pasteTexture( FaceInstance& faceInstance ){
1391         faceInstance.getFace().SetTexdef( g_faceTextureClipboard.m_projection );
1392         faceInstance.getFace().SetShader( TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
1393         faceInstance.getFace().SetFlags( g_faceTextureClipboard.m_flags );
1394         SceneChangeNotify();
1395 }
1396
1397 bool SelectedFaces_empty(){
1398         return g_SelectedFaceInstances.empty();
1399 }
1400
1401 void SelectedFaces_pasteTexture(){
1402         UndoableCommand command( "facePasteTexture" );
1403         g_SelectedFaceInstances.foreach( FaceInstance_pasteTexture );
1404 }
1405
1406
1407
1408 void SurfaceInspector_constructPreferences( PreferencesPage& page ){
1409         page.appendCheckBox( "", "Surface Inspector Increments Match Grid", g_si_globals.m_bSnapTToGrid );
1410 }
1411 void SurfaceInspector_constructPage( PreferenceGroup& group ){
1412         PreferencesPage page( group.createPage( "Surface Inspector", "Surface Inspector Preferences" ) );
1413         SurfaceInspector_constructPreferences( page );
1414 }
1415 void SurfaceInspector_registerPreferencesPage(){
1416         PreferencesDialog_addSettingsPage( makeCallbackF(SurfaceInspector_constructPage) );
1417 }
1418
1419 void SurfaceInspector_registerCommands(){
1420         GlobalCommands_insert( "FitTexture", makeCallbackF(SurfaceInspector_FitTexture), Accelerator( 'B', (GdkModifierType)GDK_SHIFT_MASK ) );
1421         GlobalCommands_insert( "SurfaceInspector", makeCallbackF(SurfaceInspector_toggleShown), Accelerator( 'S' ) );
1422
1423         GlobalCommands_insert( "FaceCopyTexture", makeCallbackF(SelectedFaces_copyTexture) );
1424         GlobalCommands_insert( "FacePasteTexture", makeCallbackF(SelectedFaces_pasteTexture) );
1425 }
1426
1427
1428 #include "preferencesystem.h"
1429
1430
1431 void SurfaceInspector_Construct(){
1432         g_SurfaceInspector = new SurfaceInspector;
1433
1434         SurfaceInspector_registerCommands();
1435
1436         FaceTextureClipboard_setDefault();
1437
1438         GlobalPreferenceSystem().registerPreference( "SurfaceWnd", make_property<WindowPositionTracker_String>( getSurfaceInspector().m_positionTracker) );
1439         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Scale1", make_property_string( g_si_globals.scale[0] ) );
1440         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Scale2", make_property_string( g_si_globals.scale[1] ) );
1441         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Shift1", make_property_string( g_si_globals.shift[0] ) );
1442         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Shift2", make_property_string( g_si_globals.shift[1] ) );
1443         GlobalPreferenceSystem().registerPreference( "SI_SurfaceTexdef_Rotate", make_property_string( g_si_globals.rotate ) );
1444         GlobalPreferenceSystem().registerPreference( "SnapTToGrid", make_property_string( g_si_globals.m_bSnapTToGrid ) );
1445
1446         typedef FreeCaller<void(const Selectable&), SurfaceInspector_SelectionChanged> SurfaceInspectorSelectionChangedCaller;
1447         GlobalSelectionSystem().addSelectionChangeCallback( SurfaceInspectorSelectionChangedCaller() );
1448         typedef FreeCaller<void(), SurfaceInspector_updateSelection> SurfaceInspectorUpdateSelectionCaller;
1449         Brush_addTextureChangedCallback( SurfaceInspectorUpdateSelectionCaller() );
1450         Patch_addTextureChangedCallback( SurfaceInspectorUpdateSelectionCaller() );
1451
1452         SurfaceInspector_registerPreferencesPage();
1453 }
1454 void SurfaceInspector_Destroy(){
1455         delete g_SurfaceInspector;
1456 }
1457
1458
1459
1460 #if TEXTOOL_ENABLED
1461
1462 namespace TexTool { // namespace hides these symbols from other object-files
1463 //
1464 //Shamus: Textool functions, including GTK+ callbacks
1465 //
1466
1467 //NOTE: Black screen when TT first comes up is caused by an uninitialized Extent... !!! FIX !!!
1468 //      But... You can see down below that it *is* initialized! WTF?
1469 struct Extent
1470 {
1471         float minX, minY, maxX, maxY;
1472         float width( void ) { return fabs( maxX - minX ); }
1473         float height( void ) { return fabs( maxY - minY ); }
1474 };
1475
1476 //This seems to control the texture scale... (Yep! ;-)
1477 Extent extents = { -2.0f, -2.0f, +2.0f, +2.0f };
1478 brushprimit_texdef_t tm;                        // Texture transform matrix
1479 Vector2 pts[c_brush_maxFaces];
1480 Vector2 center;
1481 int numPts;
1482 int textureNum;
1483 Vector2 textureSize;
1484 Vector2 windowSize;
1485 #define VP_PADDING  1.2
1486 #define PI          3.14159265358979
1487 bool lButtonDown = false;
1488 bool rButtonDown = false;
1489 //int dragPoint;
1490 //int anchorPoint;
1491 bool haveAnchor = false;
1492 brushprimit_texdef_t currentBP;
1493 brushprimit_texdef_t origBP;                    // Original brush primitive (before we muck it up)
1494 float controlRadius = 5.0f;
1495 float rotationAngle = 0.0f;
1496 float rotationAngle2 = 0.0f;
1497 float oldRotationAngle;
1498 Vector2 rotationPoint;
1499 bool translatingX = false;                      // Widget state variables
1500 bool translatingY = false;
1501 bool scalingX = false;
1502 bool scalingY = false;
1503 bool rotating = false;
1504 bool resizingX = false;                         // Not sure what this means... :-/
1505 bool resizingY = false;
1506 float origAngle, origScaleX, origScaleY;
1507 Vector2 oldCenter;
1508
1509
1510 // Function prototypes (move up to top later...)
1511
1512 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius );
1513
1514
1515 void CopyPointsFromSelectedFace( void ){
1516         // Make sure that there's a face and winding to get!
1517
1518         if ( g_SelectedFaceInstances.empty() ) {
1519                 numPts = 0;
1520                 return;
1521         }
1522
1523         Face & face = g_SelectedFaceInstances.last().getFace();
1524         textureNum = face.getShader().m_state->getTexture().texture_number;
1525         textureSize.x() = face.getShader().m_state->getTexture().width;
1526         textureSize.y() = face.getShader().m_state->getTexture().height;
1527 //globalOutputStream() << "--> Texture #" << textureNum << ": " << textureSize.x() << " x " << textureSize.y() << "...\n";
1528
1529         currentBP = SurfaceInspector_GetSelectedTexdef().m_brushprimit_texdef;
1530
1531         face.EmitTextureCoordinates();
1532         Winding & w = face.getWinding();
1533         int count = 0;
1534
1535         for ( Winding::const_iterator i = w.begin(); i != w.end(); i++ )
1536         {
1537                 //globalOutputStream() << (*i).texcoord.x() << " " << (*i).texcoord.y() << ", ";
1538                 pts[count].x() = ( *i ).texcoord.x();
1539                 pts[count].y() = ( *i ).texcoord.y();
1540                 count++;
1541         }
1542
1543         numPts = count;
1544
1545         //globalOutputStream() << " ..copied points\n";
1546 }
1547
1548 brushprimit_texdef_t bp;
1549 //This approach is probably wrongheaded and just not right anyway. So, !!! FIX !!! [DONE]
1550 void CommitChanges( void ){
1551         texdef_t t;                                 // Throwaway, since this is BP only
1552
1553         bp.coords[0][0] = tm.coords[0][0] * origBP.coords[0][0] + tm.coords[0][1] * origBP.coords[1][0];
1554         bp.coords[0][1] = tm.coords[0][0] * origBP.coords[0][1] + tm.coords[0][1] * origBP.coords[1][1];
1555         bp.coords[0][2] = tm.coords[0][0] * origBP.coords[0][2] + tm.coords[0][1] * origBP.coords[1][2] + tm.coords[0][2];
1556 //Ok, this works for translation...
1557 //      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();
1558         bp.coords[1][0] = tm.coords[1][0] * origBP.coords[0][0] + tm.coords[1][1] * origBP.coords[1][0];
1559         bp.coords[1][1] = tm.coords[1][0] * origBP.coords[0][1] + tm.coords[1][1] * origBP.coords[1][1];
1560         bp.coords[1][2] = tm.coords[1][0] * origBP.coords[0][2] + tm.coords[1][1] * origBP.coords[1][2] + tm.coords[1][2];
1561 //      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();
1562
1563 //This doesn't work:    g_brush_texture_changed();
1564 // Let's try this:
1565 //Note: We should only set an undo *after* the button has been released... !!! FIX !!!
1566 //Definitely *should* have an undo, though!
1567 //  UndoableCommand undo("textureProjectionSetSelected");
1568         Select_SetTexdef( TextureProjection( t, bp, Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) ) );
1569 //This is working, but for some reason the translate is causing the rest of the SI
1570 //widgets to yield bad readings... !!! FIX !!!
1571 //I.e., click on textool window, translate face wireframe, then controls go crazy. Dunno why.
1572 //It's because there were some uncommented out add/removeScale functions in brush.h and a
1573 //removeScale in brushmanip.cpp... :-/
1574 //Translate isn't working at all now... :-(
1575 //It's because we need to multiply in some scaling factor (prolly the texture width/height)
1576 //Yep. :-P
1577 }
1578
1579 void UpdateControlPoints( void ){
1580         CommitChanges();
1581
1582         // Init texture transform matrix
1583
1584         tm.coords[0][0] = 1.0f; tm.coords[0][1] = 0.0f; tm.coords[0][2] = 0.0f;
1585         tm.coords[1][0] = 0.0f; tm.coords[1][1] = 1.0f; tm.coords[1][2] = 0.0f;
1586 }
1587
1588
1589 /*
1590    For shifting we have:
1591  */
1592 /*
1593    The code that should provide reasonable defaults, but doesn't for some reason:
1594    It's scaling the BP by 128 for some reason, between the time it's created and the
1595    time we get back to the SI widgets:
1596
1597    static void OnBtnAxial(GtkWidget *widget, gpointer data)
1598    {
1599    UndoableCommand undo("textureDefault");
1600    TextureProjection projection;
1601    TexDef_Construct_Default(projection);
1602    Select_SetTexdef(projection);
1603    }
1604
1605    Select_SetTexdef() calls Scene_BrushSetTexdef_Component_Selected(GlobalSceneGraph(), projection)
1606    which is in brushmanip.h: This eventually calls
1607    Texdef_Assign(m_texdef, texdef, m_brushprimit_texdef, brushprimit_texdef) in class Face...
1608    which just copies from brushpr to m_brushpr...
1609  */
1610
1611 //Small problem with this thing: It's scaled to the texture which is all screwed up... !!! FIX !!! [DONE]
1612 //Prolly should separate out the grid drawing so that we can draw it behind the polygon.
1613 const float gridWidth = 1.3f; // Let's try an absolute height... WORKS!!!
1614 // NOTE that 2.0 is the height of the viewport. Dunno why... Should make collision
1615 //      detection easier...
1616 const float gridRadius = gridWidth * 0.5f;
1617
1618 typedef const float WidgetColor[3];
1619 const WidgetColor widgetColor[10] = {
1620         { 1.0000f, 0.2000f, 0.0000f },          // Red
1621         { 0.9137f, 0.9765f, 0.4980f },          // Yellow
1622         { 0.0000f, 0.6000f, 0.3216f },          // Green
1623         { 0.6157f, 0.7726f, 0.8196f },          // Cyan
1624         { 0.4980f, 0.5000f, 0.4716f },          // Grey
1625
1626         // Highlight colors
1627         { 1.0000f, 0.6000f, 0.4000f },          // Light Red
1628         { 1.0000f, 1.0000f, 0.8980f },          // Light Yellow
1629         { 0.4000f, 1.0000f, 0.7216f },          // Light Green
1630         { 1.0000f, 1.0000f, 1.0000f },          // Light Cyan
1631         { 0.8980f, 0.9000f, 0.8716f }           // Light Grey
1632 };
1633
1634 #define COLOR_RED           0
1635 #define COLOR_YELLOW        1
1636 #define COLOR_GREEN         2
1637 #define COLOR_CYAN          3
1638 #define COLOR_GREY          4
1639 #define COLOR_LT_RED        5
1640 #define COLOR_LT_YELLOW     6
1641 #define COLOR_LT_GREEN      7
1642 #define COLOR_LT_CYAN       8
1643 #define COLOR_LT_GREY       9
1644
1645 void DrawControlWidgets( void ){
1646 //Note that the grid should go *behind* the face outline... !!! FIX !!!
1647         // Grid
1648         float xStart = center.x() - ( gridWidth / 2.0f );
1649         float yStart = center.y() - ( gridWidth / 2.0f );
1650         float xScale = ( extents.height() / extents.width() ) * ( textureSize.y() / textureSize.x() );
1651
1652         glPushMatrix();
1653 //Small problem with this approach: Changing the center point in the TX code doesn't seem to
1654 //change anything here--prolly because we load a new identity matrix. A couple of ways to fix
1655 //this would be to get rid of that code, or change the center to a new point by taking into
1656 //account the transforms that we toss with the new identity matrix. Dunno which is better.
1657         glLoadIdentity();
1658         glScalef( xScale, 1.0, 1.0 );           // Will that square it up? Yup.
1659         glRotatef( static_cast<float>( radians_to_degrees( atan2( -currentBP.coords[0][1], currentBP.coords[0][0] ) ) ), 0.0, 0.0, -1.0 );
1660         glTranslatef( -center.x(), -center.y(), 0.0 );
1661
1662         // Circle
1663         glColor3fv( translatingX && translatingY ? widgetColor[COLOR_LT_YELLOW] : widgetColor[COLOR_YELLOW] );
1664         glBegin( GL_LINE_LOOP );
1665         DrawCircularArc( center, 0, 2.0f * PI, gridRadius * 0.16 );
1666
1667         glEnd();
1668
1669         // Axes
1670         glBegin( GL_LINES );
1671         glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1672         glVertex2f( center.x(), center.y() + ( gridRadius * 0.16 ) );
1673         glVertex2f( center.x(), center.y() + ( gridRadius * 1.00 ) );
1674         glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1675         glVertex2f( center.x() + ( gridRadius * 0.16 ), center.y() );
1676         glVertex2f( center.x() + ( gridRadius * 1.00 ), center.y() );
1677         glEnd();
1678
1679         // Arrowheads
1680         glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1681         glBegin( GL_TRIANGLES );
1682         glColor3fv( translatingY && !translatingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1683         glVertex2f( center.x(), center.y() + ( gridRadius * 1.10 ) );
1684         glVertex2f( center.x() + ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1685         glVertex2f( center.x() - ( gridRadius * 0.06 ), center.y() + ( gridRadius * 0.94 ) );
1686         glColor3fv( translatingX && !translatingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1687         glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() );
1688         glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() + ( gridRadius * 0.06 ) );
1689         glVertex2f( center.x() + ( gridRadius * 0.94 ), center.y() - ( gridRadius * 0.06 ) );
1690         glEnd();
1691
1692         // Arc
1693         glBegin( GL_LINE_STRIP );
1694         glColor3fv( rotating ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1695         DrawCircularArc( center, 0.03f * PI, 0.47f * PI, gridRadius * 0.90 );
1696         glEnd();
1697
1698         // Boxes
1699         glColor3fv( scalingY && !scalingX ? widgetColor[COLOR_LT_GREEN] : widgetColor[COLOR_GREEN] );
1700         glBegin( GL_LINES );
1701         glVertex2f( center.x() + ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1702         glVertex2f( center.x() - ( gridRadius * 0.20 ), center.y() + ( gridRadius * 1.50 ) );
1703         glEnd();
1704         glBegin( GL_LINE_LOOP );
1705         glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1706         glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.40 ) );
1707         glVertex2f( center.x() - ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1708         glVertex2f( center.x() + ( gridRadius * 0.10 ), center.y() + ( gridRadius * 1.20 ) );
1709         glEnd();
1710
1711         glColor3fv( scalingX && !scalingY ? widgetColor[COLOR_LT_RED] : widgetColor[COLOR_RED] );
1712         glBegin( GL_LINES );
1713         glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 0.20 ) );
1714         glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() - ( gridRadius * 0.20 ) );
1715         glEnd();
1716         glBegin( GL_LINE_LOOP );
1717         glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 0.10 ) );
1718         glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() - ( gridRadius * 0.10 ) );
1719         glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() - ( gridRadius * 0.10 ) );
1720         glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 0.10 ) );
1721         glEnd();
1722
1723         glColor3fv( scalingX && scalingY ? widgetColor[COLOR_LT_CYAN] : widgetColor[COLOR_CYAN] );
1724         glBegin( GL_LINE_STRIP );
1725         glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.10 ) );
1726         glVertex2f( center.x() + ( gridRadius * 1.50 ), center.y() + ( gridRadius * 1.50 ) );
1727         glVertex2f( center.x() + ( gridRadius * 1.10 ), center.y() + ( gridRadius * 1.50 ) );
1728         glEnd();
1729         glBegin( GL_LINE_LOOP );
1730         glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.40 ) );
1731         glVertex2f( center.x() + ( gridRadius * 1.40 ), center.y() + ( gridRadius * 1.20 ) );
1732         glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.20 ) );
1733         glVertex2f( center.x() + ( gridRadius * 1.20 ), center.y() + ( gridRadius * 1.40 ) );
1734         glEnd();
1735
1736         glPopMatrix();
1737 }
1738
1739 void DrawControlPoints( void ){
1740         glColor3f( 1, 1, 1 );
1741         glBegin( GL_LINE_LOOP );
1742
1743         for ( int i = 0; i < numPts; i++ )
1744                 glVertex2f( pts[i].x(), pts[i].y() );
1745
1746         glEnd();
1747 }
1748
1749 // Note: Setup and all that jazz must be done by the caller!
1750
1751 void DrawCircularArc( Vector2 ctr, float startAngle, float endAngle, float radius ){
1752         float stepSize = ( 2.0f * PI ) / 200.0f;
1753
1754         for ( float angle = startAngle; angle <= endAngle; angle += stepSize )
1755                 glVertex2f( ctr.x() + radius * cos( angle ), ctr.y() + radius * sin( angle ) );
1756 }
1757
1758
1759 void focus(){
1760         if ( numPts == 0 ) {
1761                 return;
1762         }
1763
1764         // Find selected texture's extents...
1765
1766         extents.minX = extents.maxX = pts[0].x(),
1767         extents.minY = extents.maxY = pts[0].y();
1768
1769         for ( int i = 1; i < numPts; i++ )
1770         {
1771                 if ( pts[i].x() < extents.minX ) {
1772                         extents.minX = pts[i].x();
1773                 }
1774                 if ( pts[i].x() > extents.maxX ) {
1775                         extents.maxX = pts[i].x();
1776                 }
1777                 if ( pts[i].y() < extents.minY ) {
1778                         extents.minY = pts[i].y();
1779                 }
1780                 if ( pts[i].y() > extents.maxY ) {
1781                         extents.maxY = pts[i].y();
1782                 }
1783         }
1784
1785         // Do some viewport fitting stuff...
1786
1787 //globalOutputStream() << "--> Center: " << center.x() << ", " << center.y() << "\n";
1788 //globalOutputStream() << "--> Extents (stage 1): " << extents.minX << ", "
1789 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1790         // TTimo: Apply a ratio to get the area we'll draw.
1791         center.x() = 0.5f * ( extents.minX + extents.maxX ),
1792                 center.y() = 0.5f * ( extents.minY + extents.maxY );
1793         extents.minX = center.x() + VP_PADDING * ( extents.minX - center.x() ),
1794         extents.minY = center.y() + VP_PADDING * ( extents.minY - center.y() ),
1795         extents.maxX = center.x() + VP_PADDING * ( extents.maxX - center.x() ),
1796         extents.maxY = center.y() + VP_PADDING * ( extents.maxY - center.y() );
1797 //globalOutputStream() << "--> Extents (stage 2): " << extents.minX << ", "
1798 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1799
1800         // TTimo: We want a texture with the same X / Y ratio.
1801         // TTimo: Compute XY space / window size ratio.
1802         float SSize = extents.width(), TSize = extents.height();
1803         float ratioX = textureSize.x() * extents.width() / windowSize.x(),
1804                   ratioY = textureSize.y() * extents.height() / windowSize.y();
1805 //globalOutputStream() << "--> Texture size: " << textureSize.x() << ", " << textureSize.y() << "\n";
1806 //globalOutputStream() << "--> Window size: " << windowSize.x() << ", " << windowSize.y() << "\n";
1807
1808         if ( ratioX > ratioY ) {
1809                 TSize = ( windowSize.y() * ratioX ) / textureSize.y();
1810 //              TSize = extents.width() * (windowSize.y() / windowSize.x()) * (textureSize.x() / textureSize.y());
1811         }
1812         else
1813         {
1814                 SSize = ( windowSize.x() * ratioY ) / textureSize.x();
1815 //              SSize = extents.height() * (windowSize.x() / windowSize.y()) * (textureSize.y() / textureSize.x());
1816         }
1817
1818         extents.minX = center.x() - 0.5f * SSize, extents.maxX = center.x() + 0.5f * SSize,
1819         extents.minY = center.y() - 0.5f * TSize, extents.maxY = center.y() + 0.5f * TSize;
1820 //globalOutputStream() << "--> Extents (stage 3): " << extents.minX << ", "
1821 //      << extents.maxX << ", " << extents.minY << ", " << extents.maxY << "\n";
1822 }
1823
1824 gboolean size_allocate( ui::Widget win, GtkAllocation * a, gpointer ){
1825         windowSize.x() = a->width;
1826         windowSize.y() = a->height;
1827         queueDraw();
1828         return false;
1829 }
1830
1831 gboolean expose( ui::Widget win, GdkEventExpose * e, gpointer ){
1832 //      globalOutputStream() << "--> Textool Window was exposed!\n";
1833 //      globalOutputStream() << "    (window width/height: " << cc << "/" << e->area.height << ")\n";
1834
1835 //      windowSize.x() = e->area.width, windowSize.y() = e->area.height;
1836 //This needs to go elsewhere...
1837 //      InitTextool();
1838
1839         if ( glwidget_make_current( win ) == FALSE ) {
1840                 globalOutputStream() << "    FAILED to make current! Oh, the agony! :-(\n";
1841                 return true;
1842         }
1843
1844         CopyPointsFromSelectedFace();
1845
1846         if ( !lButtonDown ) {
1847                 focus();
1848         }
1849
1850         // Probably should init button/anchor states here as well...
1851 //      rotationAngle = 0.0f;
1852         glClearColor( 0, 0, 0, 0 );
1853         glViewport( 0, 0, e->area.width, e->area.height );
1854         glMatrixMode( GL_PROJECTION );
1855         glLoadIdentity();
1856
1857 //???
1858         glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1859         glDisable( GL_DEPTH_TEST );
1860         glDisable( GL_BLEND );
1861
1862         glOrtho( extents.minX, extents.maxX, extents.maxY, extents.minY, -1, 1 );
1863
1864         glColor3f( 1, 1, 1 );
1865         // draw the texture background
1866         glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1867         glBindTexture( GL_TEXTURE_2D, textureNum );
1868
1869         glEnable( GL_TEXTURE_2D );
1870         glBegin( GL_QUADS );
1871         glTexCoord2f( extents.minX, extents.minY );
1872         glVertex2f( extents.minX, extents.minY );
1873         glTexCoord2f( extents.maxX, extents.minY );
1874         glVertex2f( extents.maxX, extents.minY );
1875         glTexCoord2f( extents.maxX, extents.maxY );
1876         glVertex2f( extents.maxX, extents.maxY );
1877         glTexCoord2f( extents.minX, extents.maxY );
1878         glVertex2f( extents.minX, extents.maxY );
1879         glEnd();
1880         glDisable( GL_TEXTURE_2D );
1881
1882         // draw the texture-space grid
1883         glColor3fv( widgetColor[COLOR_GREY] );
1884         glBegin( GL_LINES );
1885
1886         const int gridSubdivisions = 8;
1887         const float gridExtents = 4.0f;
1888
1889         for ( int i = 0; i < gridSubdivisions + 1; ++i )
1890         {
1891                 float y = i * ( gridExtents / float(gridSubdivisions) );
1892                 float x = i * ( gridExtents / float(gridSubdivisions) );
1893                 glVertex2f( 0, y );
1894                 glVertex2f( gridExtents, y );
1895                 glVertex2f( x, 0 );
1896                 glVertex2f( x, gridExtents );
1897         }
1898
1899         glEnd();
1900
1901         DrawControlPoints();
1902         DrawControlWidgets();
1903 //???
1904         // reset the current texture
1905 //  glBindTexture(GL_TEXTURE_2D, 0);
1906 //  glFinish();
1907         glwidget_swap_buffers( win );
1908
1909         return false;
1910 }
1911
1912 /*int FindSelectedPoint(int x, int y)
1913    {
1914     for(int i=0; i<numPts; i++)
1915     {
1916         int nx = (int)(windowSize.x() * (pts[i].x() - extents.minX) / extents.width());
1917         int ny = (int)(windowSize.y() * (pts[i].y() - extents.minY) / extents.height());
1918
1919         if (abs(nx - x) <= 3 && abs(ny - y) <= 3)
1920             return i;
1921     }
1922
1923     return -1;
1924    }//*/
1925
1926 Vector2 trans;
1927 Vector2 trans2;
1928 Vector2 dragPoint;  // Defined in terms of window space (+x/-y)
1929 Vector2 oldTrans;
1930 gboolean button_press( ui::Widget win, GdkEventButton * e, gpointer ){
1931 //      globalOutputStream() << "--> Textool button press...\n";
1932
1933         if ( e->button == 1 ) {
1934                 lButtonDown = true;
1935                 GlobalUndoSystem().start();
1936
1937                 origBP = currentBP;
1938
1939                 //globalOutputStream() << "--> Original BP: [" << origBP.coords[0][0] << "][" << origBP.coords[0][1] << "][" << origBP.coords[0][2] << "]\n";
1940                 //globalOutputStream() << "                 [" << origBP.coords[1][0] << "][" << origBP.coords[1][1] << "][" << origBP.coords[1][2] << "]\n";
1941                 //float angle = atan2(origBP.coords[0][1], origBP.coords[0][0]) * 180.0f / 3.141592653589f;
1942                 origAngle = ( origBP.coords[0][1] > 0 ? PI : -PI ); // Could also be -PI... !!! FIX !!! [DONE]
1943
1944                 if ( origBP.coords[0][0] != 0.0f ) {
1945                         origAngle = atan( origBP.coords[0][1] / origBP.coords[0][0] );
1946                 }
1947
1948                 origScaleX = origBP.coords[0][0] / cos( origAngle );
1949                 origScaleY = origBP.coords[1][1] / cos( origAngle );
1950                 rotationAngle = origAngle;
1951                 oldCenter[0] = oldCenter[1] = 0;
1952
1953                 //globalOutputStream() << "--> BP stats: ang=" << origAngle * RAD_TO_DEG << ", scale=" << origScaleX << "/" << origScaleY << "\n";
1954                 //Should also set the Flip X/Y checkboxes here as well... !!! FIX !!!
1955                 //Also: should reverse texture left/right up/down instead of flipping the points...
1956
1957 //disnowok
1958 //float nx = windowSize.x() * (e->x - extents.minX) / (extents.maxX - extents.minX);
1959 //float ny = windowSize.y() * (e->y - extents.minY) / (extents.maxY - extents.minY);
1960 //disdoes...
1961 //But I want it to scroll the texture window, not the points... !!! FIX !!!
1962 //Actually, should scroll the texture window only when mouse is down on no widgets...
1963                 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
1964                 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
1965                 trans.x() = -tm.coords[0][0] * nx - tm.coords[0][1] * ny;
1966                 trans.y() = -tm.coords[1][0] * nx - tm.coords[1][1] * ny;
1967
1968                 dragPoint.x() = e->x, dragPoint.y() = e->y;
1969                 trans2.x() = nx, trans2.y() = ny;
1970                 oldRotationAngle = rotationAngle;
1971 //              oldTrans.x() = tm.coords[0][2] - nx * textureSize.x();
1972 //              oldTrans.y() = tm.coords[1][2] - ny * textureSize.y();
1973                 oldTrans.x() = tm.coords[0][2];
1974                 oldTrans.y() = tm.coords[1][2];
1975                 oldCenter.x() = center.x();
1976                 oldCenter.y() = center.y();
1977
1978                 queueDraw();
1979
1980                 return true;
1981         }
1982 /*      else if (e->button == 3)
1983     {
1984         rButtonDown = true;
1985     }//*/
1986
1987 //globalOutputStream() << "(" << (haveAnchor ? "anchor" : "released") << ")\n";
1988
1989         return false;
1990 }
1991
1992 gboolean button_release( ui::Widget win, GdkEventButton * e, gpointer ){
1993 //      globalOutputStream() << "--> Textool button release...\n";
1994
1995         if ( e->button == 1 ) {
1996 /*              float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
1997         float pty = e->y / windowSize.y() * extents.height() + extents.minY;
1998
1999    //This prolly should go into the mouse move code...
2000    //Doesn't work correctly anyway...
2001         if (translatingX || translatingY)
2002             center.x() = ptx, center.y() = pty;//*/
2003
2004                 lButtonDown = false;
2005
2006                 if ( translatingX || translatingY ) {
2007                         GlobalUndoSystem().finish( "translateTexture" );
2008                 }
2009                 else if ( rotating ) {
2010                         GlobalUndoSystem().finish( "rotateTexture" );
2011                 }
2012                 else if ( scalingX || scalingY ) {
2013                         GlobalUndoSystem().finish( "scaleTexture" );
2014                 }
2015                 else if ( resizingX || resizingY ) {
2016                         GlobalUndoSystem().finish( "resizeTexture" );
2017                 }
2018                 else
2019                 {
2020                         GlobalUndoSystem().finish( "textoolUnknown" );
2021                 }
2022
2023                 rotating = translatingX = translatingY = scalingX = scalingY
2024                                                                                                                                 = resizingX = resizingY = false;
2025
2026                 queueDraw();
2027         }
2028         else if ( e->button == 3 ) {
2029                 rButtonDown = false;
2030         }
2031
2032         return true;
2033 }
2034
2035 /*
2036    void C2DView::GridForWindow( float c[2], int x, int y)
2037    {
2038    SpaceForWindow( c, x, y );
2039    if ( !m_bDoGrid )
2040     return;
2041    c[0] /= m_GridStep[0];
2042    c[1] /= m_GridStep[1];
2043    c[0] = (float)floor( c[0] + 0.5f );
2044    c[1] = (float)floor( c[1] + 0.5f );
2045    c[0] *= m_GridStep[0];
2046    c[1] *= m_GridStep[1];
2047    }
2048    void C2DView::SpaceForWindow( float c[2], int x, int y)
2049    {
2050    c[0] = ((float)(x))/((float)(m_rect.right-m_rect.left))*(m_Maxs[0]-m_Mins[0])+m_Mins[0];
2051    c[1] = ((float)(y))/((float)(m_rect.bottom-m_rect.top))*(m_Maxs[1]-m_Mins[1])+m_Mins[1];
2052    }
2053  */
2054 gboolean motion( ui::Widget win, GdkEventMotion * e, gpointer ){
2055 //      globalOutputStream() << "--> Textool motion...\n";
2056
2057         if ( lButtonDown ) {
2058                 if ( translatingX || translatingY ) {
2059                         float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2060                         float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2061
2062 //Need to fix this to take the rotation angle into account, so that it moves along
2063 //the rotated X/Y axis...
2064                         if ( translatingX ) {
2065 //                              tm.coords[0][2] = (trans.x() + ptx) * textureSize.x();
2066 //This works, but only when the angle is zero. !!! FIX !!! [DONE]
2067 //                              tm.coords[0][2] = oldCenter.x() + (ptx * textureSize.x());
2068                                 tm.coords[0][2] = oldTrans.x() + ( ptx - trans2.x() ) * textureSize.x();
2069 //                              center.x() = oldCenter.x() + (ptx - trans2.x());
2070                         }
2071
2072                         if ( translatingY ) {
2073 //                              tm.coords[1][2] = (trans.y() + pty) * textureSize.y();
2074 //                              tm.coords[1][2] = oldCenter.y() + (pty * textureSize.y());
2075                                 tm.coords[1][2] = oldTrans.y() + ( pty - trans2.y() ) * textureSize.y();
2076 //                              center.y() = oldCenter.y() + (pty - trans2.y());
2077                         }
2078
2079 //Need to update center.x/y() so that the widget translates as well. Also, oldCenter
2080 //is badly named... Should be oldTrans or something like that... !!! FIX !!!
2081 //Changing center.x/y() here doesn't seem to change anything... :-/
2082                         UpdateControlPoints();
2083                 }
2084                 else if ( rotating ) {
2085                         // Shamus: New rotate code
2086                         int cx = (int)( windowSize.x() * ( center.x() - extents.minX ) / extents.width() );
2087                         int cy = (int)( windowSize.y() * ( center.y() - extents.minY ) / extents.height() );
2088                         Vector3 v1( dragPoint.x() - cx, dragPoint.y() - cy, 0 ), v2( e->x - cx, e->y - cy, 0 );
2089
2090                         vector3_normalise( v1 );
2091                         vector3_normalise( v2 );
2092                         float c = vector3_dot( v1, v2 );
2093                         Vector3 cross = vector3_cross( v1, v2 );
2094                         float s = vector3_length( cross );
2095
2096                         if ( cross[2] > 0 ) {
2097                                 s = -s;
2098                         }
2099
2100 // Problem with this: arcsin/cos seems to only return -90 to 90 and 0 to 180...
2101 // Can't derive angle from that!
2102
2103 //rotationAngle = asin(s);// * 180.0f / 3.141592653589f;
2104                         rotationAngle = acos( c );
2105 //rotationAngle2 = asin(s);
2106                         if ( cross[2] < 0 ) {
2107                                 rotationAngle = -rotationAngle;
2108                         }
2109
2110 //NO! DOESN'T WORK! rotationAngle -= 45.0f * DEG_TO_RAD;
2111 //Let's try this:
2112 //No wok.
2113 /*c = cos(rotationAngle - oldRotationAngle);
2114    s = sin(rotationAngle - oldRotationAngle);
2115    rotationAngle += oldRotationAngle;
2116    //c += cos(oldRotationAngle);
2117    //s += sin(oldRotationAngle);
2118    //rotationAngle += oldRotationAngle;
2119    //c %= 2.0 * PI;
2120    //s %= 2.0 * PI;
2121    //rotationAngle %= 2.0 * PI;//*/
2122
2123 //This is wrong... Hmm...
2124 //It seems to shear the texture instead of rotating it... !!! FIX !!!
2125 // Now it rotates correctly. Seems TTimo was overcomplicating things here... ;-)
2126
2127 // Seems like what needs to happen here is multiplying these rotations by tm... !!! FIX !!!
2128
2129 // See brush_primit.cpp line 244 (Texdef_EmitTextureCoordinates()) for where texcoords come from...
2130
2131                         tm.coords[0][0] =  c;
2132                         tm.coords[0][1] =  s;
2133                         tm.coords[1][0] = -s;
2134                         tm.coords[1][1] =  c;
2135 //It doesn't work anymore... Dunno why...
2136 //tm.coords[0][2] = -trans.x();                 // This works!!! Yeah!!!
2137 //tm.coords[1][2] = -trans.y();
2138 //nope.
2139 //tm.coords[0][2] = rotationPoint.x();  // This works, but strangely...
2140 //tm.coords[1][2] = rotationPoint.y();
2141 //tm.coords[0][2] = 0;// center.x() / 2.0f;
2142 //tm.coords[1][2] = 0;// center.y() / 2.0f;
2143 //No.
2144 //tm.coords[0][2] = -(center.x() * textureSize.x());
2145 //tm.coords[1][2] = -(center.y() * textureSize.y());
2146 //Eh? No, but seems to be getting closer...
2147 /*float ptx = e->x / windowSize.x() * extents.width() + extents.minX;
2148    float pty = e->y / windowSize.y() * extents.height() + extents.minY;
2149    tm.coords[0][2] = -c * center.x() - s * center.y() + ptx;
2150    tm.coords[1][2] =  s * center.x() - c * center.x() + pty;//*/
2151 //Kinda works, but center drifts around on non-square textures...
2152 /*tm.coords[0][2] = (-c * center.x() - s * center.y()) * textureSize.x();
2153    tm.coords[1][2] = ( s * center.x() - c * center.y()) * textureSize.y();//*/
2154 //Rotates correctly, but not around the actual center of the face's points...
2155 /*tm.coords[0][2] = -c * center.x() * textureSize.x() - s * center.y() * textureSize.y();
2156    tm.coords[1][2] =  s * center.x() * textureSize.x() - c * center.y() * textureSize.y();//*/
2157 //Yes!!!
2158                         tm.coords[0][2] = ( -c * center.x() * textureSize.x() - s * center.y() * textureSize.y() ) + center.x() * textureSize.x();
2159                         tm.coords[1][2] = ( s * center.x() * textureSize.x() - c * center.y() * textureSize.y() ) + center.y() * textureSize.y(); //*/
2160 //This doesn't work...
2161 //And this is the wrong place for this anyway (I'm pretty sure).
2162 /*tm.coords[0][2] += oldCenter.x();
2163    tm.coords[1][2] += oldCenter.y();//*/
2164                         UpdateControlPoints(); // will cause a redraw
2165                 }
2166
2167                 return true;
2168         }
2169         else                                    // Check for widget mouseovers
2170         {
2171                 Vector2 tran;
2172                 float nx = e->x / windowSize.x() * extents.width() + extents.minX;
2173                 float ny = e->y / windowSize.y() * extents.height() + extents.minY;
2174                 // Translate nx/y to the "center" point...
2175                 nx -= center.x();
2176                 ny -= center.y();
2177                 ny = -ny;   // Flip Y-axis so that increasing numbers move up
2178
2179                 tran.x() = tm.coords[0][0] * nx + tm.coords[0][1] * ny;
2180                 tran.y() = tm.coords[1][0] * nx + tm.coords[1][1] * ny;
2181 //This doesn't seem to generate a valid distance from the center--for some reason it
2182 //calculates a fixed number every time
2183 //Look at nx/y above: they're getting fixed there! !!! FIX !!! [DONE]
2184                 float dist = sqrt( ( nx * nx ) + ( ny * ny ) );
2185                 // Normalize to the 2.0 = height standard (for now)
2186 //globalOutputStream() << "--> Distance before: " << dist;
2187                 dist = dist * 2.0f / extents.height();
2188 //globalOutputStream() << ". After: " << dist;
2189                 tran.x() = tran.x() * 2.0f / extents.height();
2190                 tran.y() = tran.y() * 2.0f / extents.height();
2191 //globalOutputStream() << ". Trans: " << tran.x() << ", " << tran.y() << "\n";
2192
2193 //Let's try this instead...
2194 //Interesting! It seems that e->x/y are rotated
2195 //(no, they're not--the TM above is what's doing it...)
2196                 nx = ( ( e->x / windowSize.y() ) * 2.0f ) - ( windowSize.x() / windowSize.y() );
2197                 ny = ( ( e->y / windowSize.y() ) * 2.0f ) - ( windowSize.y() / windowSize.y() );
2198                 ny = -ny;
2199 //Cool! It works! Now just need to do rotation...
2200
2201                 rotating = translatingX = translatingY = scalingX = scalingY
2202                                                                                                                                 = resizingX = resizingY = false;
2203
2204                 if ( dist < ( gridRadius * 0.16f ) ) {
2205                         translatingX = translatingY = true;
2206                 }
2207                 else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2208                                   && fabs( ny ) < ( gridRadius * 0.05f ) && nx > 0 ) {
2209                         translatingX = true;
2210                 }
2211                 else if ( dist > ( gridRadius * 0.16f ) && dist < ( gridRadius * 1.10f )
2212                                   && fabs( nx ) < ( gridRadius * 0.05f ) && ny > 0 ) {
2213                         translatingY = true;
2214                 }
2215                 // Should tighten up the angle on this, or put this test after the axis tests...
2216                 else if ( tran.x() > 0 && tran.y() > 0
2217                                   && ( dist > ( gridRadius * 0.82f ) && dist < ( gridRadius * 0.98f ) ) ) {
2218                         rotating = true;
2219                 }
2220
2221                 queueDraw();
2222
2223                 return true;
2224         }
2225
2226         return false;
2227 }
2228
2229 //It seems the fake tex coords conversion is screwing this stuff up... !!! FIX !!!
2230 //This is still wrong... Prolly need to do something with the oldScaleX/Y stuff...
2231 void flipX( ui::ToggleButton, gpointer ){
2232 //      globalOutputStream() << "--> Flip X...\n";
2233         //Shamus:
2234 //      SurfaceInspector_GetSelectedBPTexdef();         // Refresh g_selectedBrushPrimitTexdef...
2235 //      tm.coords[0][0] = -tm.coords[0][0];
2236 //      tm.coords[1][0] = -tm.coords[1][0];
2237 //      tm.coords[0][0] = -tm.coords[0][0];                     // This should be correct now...Nope.
2238 //      tm.coords[1][1] = -tm.coords[1][1];
2239         tm.coords[0][0] = -tm.coords[0][0];         // This should be correct now...
2240         tm.coords[1][0] = -tm.coords[1][0];
2241 //      tm.coords[2][0] = -tm.coords[2][0];//wil wok? no.
2242         UpdateControlPoints();
2243 }
2244
2245 void flipY( ui::ToggleButton, gpointer ){
2246 //      globalOutputStream() << "--> Flip Y...\n";
2247 //      tm.coords[0][1] = -tm.coords[0][1];
2248 //      tm.coords[1][1] = -tm.coords[1][1];
2249 //      tm.coords[0][1] = -tm.coords[0][1];                     // This should be correct now...Nope.
2250 //      tm.coords[1][0] = -tm.coords[1][0];
2251         tm.coords[0][1] = -tm.coords[0][1];         // This should be correct now...
2252         tm.coords[1][1] = -tm.coords[1][1];
2253 //      tm.coords[2][1] = -tm.coords[2][1];//wil wok? no.
2254         UpdateControlPoints();
2255 }
2256
2257 } // end namespace TexTool
2258
2259 #endif