]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/brushmanip.cpp
Radiant:
[xonotic/netradiant.git] / radiant / brushmanip.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 #include "brushmanip.h"
23
24
25 #include "gtkutil/widget.h"
26 #include "gtkutil/menu.h"
27 #include "gtkmisc.h"
28 #include "brushnode.h"
29 #include "map.h"
30 #include "texwindow.h"
31 #include "gtkdlgs.h"
32 #include "commands.h"
33 #include "mainframe.h"
34 #include "dialog.h"
35 #include "xywindow.h"
36 #include "preferences.h"
37
38 #include <list>
39
40 void Brush_ConstructCuboid( Brush& brush, const AABB& bounds, const char* shader, const TextureProjection& projection ){
41         const unsigned char box[3][2] = { { 0, 1 }, { 2, 0 }, { 1, 2 } };
42         Vector3 mins( vector3_subtracted( bounds.origin, bounds.extents ) );
43         Vector3 maxs( vector3_added( bounds.origin, bounds.extents ) );
44
45         brush.clear();
46         brush.reserve( 6 );
47
48         {
49                 for ( int i = 0; i < 3; ++i )
50                 {
51                         Vector3 planepts1( maxs );
52                         Vector3 planepts2( maxs );
53                         planepts2[box[i][0]] = mins[box[i][0]];
54                         planepts1[box[i][1]] = mins[box[i][1]];
55
56                         brush.addPlane( maxs, planepts1, planepts2, shader, projection );
57                 }
58         }
59         {
60                 for ( int i = 0; i < 3; ++i )
61                 {
62                         Vector3 planepts1( mins );
63                         Vector3 planepts2( mins );
64                         planepts1[box[i][0]] = maxs[box[i][0]];
65                         planepts2[box[i][1]] = maxs[box[i][1]];
66
67                         brush.addPlane( mins, planepts1, planepts2, shader, projection );
68                 }
69         }
70 }
71
72 inline float max_extent( const Vector3& extents ){
73         return std::max( std::max( extents[0], extents[1] ), extents[2] );
74 }
75
76 inline float max_extent_2d( const Vector3& extents, int axis ){
77         switch ( axis )
78         {
79         case 0:
80                 return std::max( extents[1], extents[2] );
81         case 1:
82                 return std::max( extents[0], extents[2] );
83         default:
84                 return std::max( extents[0], extents[1] );
85         }
86 }
87
88 const std::size_t c_brushPrism_minSides = 3;
89 const std::size_t c_brushPrism_maxSides = c_brush_maxFaces - 2;
90 const char* const c_brushPrism_name = "brushPrism";
91
92 void Brush_ConstructPrism( Brush& brush, const AABB& bounds, std::size_t sides, int axis, const char* shader, const TextureProjection& projection ){
93         if ( sides < c_brushPrism_minSides ) {
94                 globalErrorStream() << c_brushPrism_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushPrism_minSides ) << "\n";
95                 return;
96         }
97         if ( sides > c_brushPrism_maxSides ) {
98                 globalErrorStream() << c_brushPrism_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushPrism_maxSides ) << "\n";
99                 return;
100         }
101
102         brush.clear();
103         brush.reserve( sides + 2 );
104
105         Vector3 mins( vector3_subtracted( bounds.origin, bounds.extents ) );
106         Vector3 maxs( vector3_added( bounds.origin, bounds.extents ) );
107
108         float radius = max_extent_2d( bounds.extents, axis );
109         const Vector3& mid = bounds.origin;
110         Vector3 planepts[3];
111
112         planepts[2][( axis + 1 ) % 3] = mins[( axis + 1 ) % 3];
113         planepts[2][( axis + 2 ) % 3] = mins[( axis + 2 ) % 3];
114         planepts[2][axis] = maxs[axis];
115         planepts[1][( axis + 1 ) % 3] = maxs[( axis + 1 ) % 3];
116         planepts[1][( axis + 2 ) % 3] = mins[( axis + 2 ) % 3];
117         planepts[1][axis] = maxs[axis];
118         planepts[0][( axis + 1 ) % 3] = maxs[( axis + 1 ) % 3];
119         planepts[0][( axis + 2 ) % 3] = maxs[( axis + 2 ) % 3];
120         planepts[0][axis] = maxs[axis];
121
122         brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
123
124         planepts[0][( axis + 1 ) % 3] = mins[( axis + 1 ) % 3];
125         planepts[0][( axis + 2 ) % 3] = mins[( axis + 2 ) % 3];
126         planepts[0][axis] = mins[axis];
127         planepts[1][( axis + 1 ) % 3] = maxs[( axis + 1 ) % 3];
128         planepts[1][( axis + 2 ) % 3] = mins[( axis + 2 ) % 3];
129         planepts[1][axis] = mins[axis];
130         planepts[2][( axis + 1 ) % 3] = maxs[( axis + 1 ) % 3];
131         planepts[2][( axis + 2 ) % 3] = maxs[( axis + 2 ) % 3];
132         planepts[2][axis] = mins[axis];
133
134         brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
135
136         for ( std::size_t i = 0 ; i < sides ; ++i )
137         {
138                 double sv = sin( i * 3.14159265 * 2 / sides );
139                 double cv = cos( i * 3.14159265 * 2 / sides );
140
141 //              planepts[0][( axis + 1 ) % 3] = static_cast<float>( floor( mid[( axis + 1 ) % 3] + radius * cv + 0.5 ) );
142 //              planepts[0][( axis + 2 ) % 3] = static_cast<float>( floor( mid[( axis + 2 ) % 3] + radius * sv + 0.5 ) );
143                 planepts[0][( axis + 1 ) % 3] = static_cast<float>( mid[( axis + 1 ) % 3] + radius * cv );
144                 planepts[0][( axis + 2 ) % 3] = static_cast<float>( mid[( axis + 2 ) % 3] + radius * sv );
145                 planepts[0][axis] = mins[axis];
146
147                 planepts[1][( axis + 1 ) % 3] = planepts[0][( axis + 1 ) % 3];
148                 planepts[1][( axis + 2 ) % 3] = planepts[0][( axis + 2 ) % 3];
149                 planepts[1][axis] = maxs[axis];
150
151 //              planepts[2][( axis + 1 ) % 3] = static_cast<float>( floor( planepts[0][( axis + 1 ) % 3] - radius * sv + 0.5 ) );
152 //              planepts[2][( axis + 2 ) % 3] = static_cast<float>( floor( planepts[0][( axis + 2 ) % 3] + radius * cv + 0.5 ) );
153                 planepts[2][( axis + 1 ) % 3] = static_cast<float>( planepts[0][( axis + 1 ) % 3] - radius * sv );
154                 planepts[2][( axis + 2 ) % 3] = static_cast<float>( planepts[0][( axis + 2 ) % 3] + radius * cv );
155                 planepts[2][axis] = maxs[axis];
156                 //globalOutputStream() << planepts[0] << "   " << planepts[2] << "  #" << i << "   sin " << sv << "  cos " << cv << "\n";
157
158                 brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
159         }
160 }
161
162 const std::size_t c_brushCone_minSides = 3;
163 const std::size_t c_brushCone_maxSides = c_brush_maxFaces - 1;
164 const char* const c_brushCone_name = "brushCone";
165
166 void Brush_ConstructCone( Brush& brush, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){
167         if ( sides < c_brushCone_minSides ) {
168                 globalErrorStream() << c_brushCone_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushCone_minSides ) << "\n";
169                 return;
170         }
171         if ( sides > c_brushCone_maxSides ) {
172                 globalErrorStream() << c_brushCone_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushCone_maxSides ) << "\n";
173                 return;
174         }
175
176         brush.clear();
177         brush.reserve( sides + 1 );
178
179         Vector3 mins( vector3_subtracted( bounds.origin, bounds.extents ) );
180         Vector3 maxs( vector3_added( bounds.origin, bounds.extents ) );
181
182         float radius = max_extent( bounds.extents );
183         const Vector3& mid = bounds.origin;
184         Vector3 planepts[3];
185
186         planepts[0][0] = mins[0]; planepts[0][1] = mins[1]; planepts[0][2] = mins[2];
187         planepts[1][0] = maxs[0]; planepts[1][1] = mins[1]; planepts[1][2] = mins[2];
188         planepts[2][0] = maxs[0]; planepts[2][1] = maxs[1]; planepts[2][2] = mins[2];
189
190         brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
191
192         for ( std::size_t i = 0 ; i < sides ; ++i )
193         {
194                 double sv = sin( i * 3.14159265 * 2 / sides );
195                 double cv = cos( i * 3.14159265 * 2 / sides );
196
197                 planepts[0][0] = static_cast<float>( mid[0] + radius * cv );
198                 planepts[0][1] = static_cast<float>( mid[1] + radius * sv );
199 //              planepts[0][0] = static_cast<float>( floor( mid[0] + radius * cv + 0.5 ) );
200 //              planepts[0][1] = static_cast<float>( floor( mid[1] + radius * sv + 0.5 ) );
201                 planepts[0][2] = mins[2];
202
203                 planepts[1][0] = mid[0];
204                 planepts[1][1] = mid[1];
205                 planepts[1][2] = maxs[2];
206
207                 planepts[2][0] = static_cast<float>( planepts[0][0] - radius * sv );
208                 planepts[2][1] = static_cast<float>( planepts[0][1] + radius * cv );
209 //              planepts[2][0] = static_cast<float>( floor( planepts[0][0] - radius * sv + 0.5 ) );
210 //              planepts[2][1] = static_cast<float>( floor( planepts[0][1] + radius * cv + 0.5 ) );
211                 planepts[2][2] = maxs[2];
212
213                 brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
214         }
215 }
216
217 const std::size_t c_brushSphere_minSides = 3;
218 const std::size_t c_brushSphere_maxSides = 31;
219 const char* const c_brushSphere_name = "brushSphere";
220
221 void Brush_ConstructSphere( Brush& brush, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){
222         if ( sides < c_brushSphere_minSides ) {
223                 globalErrorStream() << c_brushSphere_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushSphere_minSides ) << "\n";
224                 return;
225         }
226         if ( sides > c_brushSphere_maxSides ) {
227                 globalErrorStream() << c_brushSphere_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushSphere_maxSides ) << "\n";
228                 return;
229         }
230
231         brush.clear();
232         brush.reserve( sides * sides );
233
234         float radius = max_extent( bounds.extents );
235         const Vector3& mid = bounds.origin;
236         Vector3 planepts[3];
237
238         double dt = 2 * c_pi / sides;
239         double dp = c_pi / sides;
240         for ( std::size_t i = 0; i < sides; i++ )
241         {
242                 for ( std::size_t j = 0; j < sides - 1; j++ )
243                 {
244                         double t = i * dt;
245                         double p = float(j * dp - c_pi / 2);
246
247                         planepts[0] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t, p ), radius ) );
248                         planepts[1] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t, p + dp ), radius ) );
249                         planepts[2] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t + dt, p + dp ), radius ) );
250
251                         brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
252                 }
253         }
254
255         {
256                 double p = ( sides - 1 ) * dp - c_pi / 2;
257                 for ( std::size_t i = 0; i < sides; i++ )
258                 {
259                         double t = i * dt;
260
261                         planepts[0] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t, p ), radius ) );
262                         planepts[1] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t + dt, p + dp ), radius ) );
263                         planepts[2] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t + dt, p ), radius ) );
264
265                         brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
266                 }
267         }
268 }
269
270 const std::size_t c_brushRock_minSides = 10;
271 const std::size_t c_brushRock_maxSides = 1000;
272 const char* const c_brushRock_name = "brushRock";
273
274 void Brush_ConstructRock( Brush& brush, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){
275         if ( sides < c_brushRock_minSides ) {
276                 globalErrorStream() << c_brushRock_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushRock_minSides ) << "\n";
277                 return;
278         }
279         if ( sides > c_brushRock_maxSides ) {
280                 globalErrorStream() << c_brushRock_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushRock_maxSides ) << "\n";
281                 return;
282         }
283
284         brush.clear();
285         brush.reserve( sides * sides );
286
287         float radius = max_extent( bounds.extents );
288         const Vector3& mid = bounds.origin;
289         Vector3 planepts[3];
290
291         for ( std::size_t j = 0; j < sides; j++ )
292         {
293                 planepts[0][0] = rand() - ( RAND_MAX / 2 );
294                 planepts[0][1] = rand() - ( RAND_MAX / 2 );
295                 planepts[0][2] = rand() - ( RAND_MAX / 2 );
296                 vector3_normalise( planepts[0] );
297
298                 // find two vectors that are perpendicular to planepts[0]
299                 ComputeAxisBase( planepts[0], planepts[1], planepts[2] );
300
301                 planepts[0] = vector3_added( mid, vector3_scaled( planepts[0], radius ) );
302                 planepts[1] = vector3_added( planepts[0], vector3_scaled( planepts[1], radius ) );
303                 planepts[2] = vector3_added( planepts[0], vector3_scaled( planepts[2], radius ) );
304
305 #if 0
306                 // make sure the orientation is right
307                 if ( vector3_dot( vector3_subtracted( planepts[0], mid ), vector3_cross( vector3_subtracted( planepts[1], mid ), vector3_subtracted( planepts[2], mid ) ) ) > 0 ) {
308                         Vector3 h;
309                         h = planepts[1];
310                         planepts[1] = planepts[2];
311                         planepts[2] = h;
312                         globalOutputStream() << "flip\n";
313                 }
314                 else{
315                         globalOutputStream() << "no flip\n";
316                 }
317 #endif
318
319                 brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
320         }
321 }
322
323 int GetViewAxis(){
324         switch ( GlobalXYWnd_getCurrentViewType() )
325         {
326         case XY:
327                 return 2;
328         case XZ:
329                 return 1;
330         case YZ:
331                 return 0;
332         }
333         return 2;
334 }
335
336 void Brush_ConstructPrefab( Brush& brush, EBrushPrefab type, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){
337         switch ( type )
338         {
339         case eBrushCuboid:
340         {
341                 UndoableCommand undo( "brushCuboid" );
342
343                 Brush_ConstructCuboid( brush, bounds, shader, projection );
344         }
345         break;
346         case eBrushPrism:
347         {
348                 int axis = GetViewAxis();
349                 StringOutputStream command;
350                 command << c_brushPrism_name << " -sides " << Unsigned( sides ) << " -axis " << axis;
351                 UndoableCommand undo( command.c_str() );
352
353                 Brush_ConstructPrism( brush, bounds, sides, axis, shader, projection );
354         }
355         break;
356         case eBrushCone:
357         {
358                 StringOutputStream command;
359                 command << c_brushCone_name << " -sides " << Unsigned( sides );
360                 UndoableCommand undo( command.c_str() );
361
362                 Brush_ConstructCone( brush, bounds, sides, shader, projection );
363         }
364         break;
365         case eBrushSphere:
366         {
367                 StringOutputStream command;
368                 command << c_brushSphere_name << " -sides " << Unsigned( sides );
369                 UndoableCommand undo( command.c_str() );
370
371                 Brush_ConstructSphere( brush, bounds, sides, shader, projection );
372         }
373         break;
374         case eBrushRock:
375         {
376                 StringOutputStream command;
377                 command << c_brushRock_name << " -sides " << Unsigned( sides );
378                 UndoableCommand undo( command.c_str() );
379
380                 Brush_ConstructRock( brush, bounds, sides, shader, projection );
381         }
382         break;
383         }
384 }
385
386
387 void ConstructRegionBrushes( scene::Node* brushes[6], const Vector3& region_mins, const Vector3& region_maxs ){
388         {
389                 // set mins
390                 Vector3 mins( region_mins[0] - 32, region_mins[1] - 32, region_mins[2] - 32 );
391
392                 // vary maxs
393                 for ( std::size_t i = 0; i < 3; i++ )
394                 {
395                         Vector3 maxs( region_maxs[0] + 32, region_maxs[1] + 32, region_maxs[2] + 32 );
396                         maxs[i] = region_mins[i];
397                         Brush_ConstructCuboid( *Node_getBrush( *brushes[i] ), aabb_for_minmax( mins, maxs ), texdef_name_default(), TextureProjection() );
398                 }
399         }
400
401         {
402                 // set maxs
403                 Vector3 maxs( region_maxs[0] + 32, region_maxs[1] + 32, region_maxs[2] + 32 );
404
405                 // vary mins
406                 for ( std::size_t i = 0; i < 3; i++ )
407                 {
408                         Vector3 mins( region_mins[0] - 32, region_mins[1] - 32, region_mins[2] - 32 );
409                         mins[i] = region_maxs[i];
410                         Brush_ConstructCuboid( *Node_getBrush( *brushes[i + 3] ), aabb_for_minmax( mins, maxs ), texdef_name_default(), TextureProjection() );
411                 }
412         }
413 }
414
415
416 class FaceSetTexdef
417 {
418 const TextureProjection& m_projection;
419 public:
420 FaceSetTexdef( const TextureProjection& projection ) : m_projection( projection ){
421 }
422 void operator()( Face& face ) const {
423         face.SetTexdef( m_projection );
424 }
425 };
426
427 void Scene_BrushSetTexdef_Selected( scene::Graph& graph, const TextureProjection& projection ){
428         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceSetTexdef( projection ) );
429         SceneChangeNotify();
430 }
431
432 void Scene_BrushSetTexdef_Component_Selected( scene::Graph& graph, const TextureProjection& projection ){
433         Scene_ForEachSelectedBrushFace( graph, FaceSetTexdef( projection ) );
434         SceneChangeNotify();
435 }
436
437
438 class FaceSetFlags
439 {
440 const ContentsFlagsValue& m_flags;
441 public:
442 FaceSetFlags( const ContentsFlagsValue& flags ) : m_flags( flags ){
443 }
444 void operator()( Face& face ) const {
445         face.SetFlags( m_flags );
446 }
447 };
448
449 void Scene_BrushSetFlags_Selected( scene::Graph& graph, const ContentsFlagsValue& flags ){
450         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceSetFlags( flags ) );
451         SceneChangeNotify();
452 }
453
454 void Scene_BrushSetFlags_Component_Selected( scene::Graph& graph, const ContentsFlagsValue& flags ){
455         Scene_ForEachSelectedBrushFace( graph, FaceSetFlags( flags ) );
456         SceneChangeNotify();
457 }
458
459 class FaceShiftTexdef
460 {
461 float m_s, m_t;
462 public:
463 FaceShiftTexdef( float s, float t ) : m_s( s ), m_t( t ){
464 }
465 void operator()( Face& face ) const {
466         face.ShiftTexdef( m_s, m_t );
467 }
468 };
469
470 void Scene_BrushShiftTexdef_Selected( scene::Graph& graph, float s, float t ){
471         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceShiftTexdef( s, t ) );
472         SceneChangeNotify();
473 }
474
475 void Scene_BrushShiftTexdef_Component_Selected( scene::Graph& graph, float s, float t ){
476         Scene_ForEachSelectedBrushFace( graph, FaceShiftTexdef( s, t ) );
477         SceneChangeNotify();
478 }
479
480 class FaceScaleTexdef
481 {
482 float m_s, m_t;
483 public:
484 FaceScaleTexdef( float s, float t ) : m_s( s ), m_t( t ){
485 }
486 void operator()( Face& face ) const {
487         face.ScaleTexdef( m_s, m_t );
488 }
489 };
490
491 void Scene_BrushScaleTexdef_Selected( scene::Graph& graph, float s, float t ){
492         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceScaleTexdef( s, t ) );
493         SceneChangeNotify();
494 }
495
496 void Scene_BrushScaleTexdef_Component_Selected( scene::Graph& graph, float s, float t ){
497         Scene_ForEachSelectedBrushFace( graph, FaceScaleTexdef( s, t ) );
498         SceneChangeNotify();
499 }
500
501 class FaceRotateTexdef
502 {
503 float m_angle;
504 public:
505 FaceRotateTexdef( float angle ) : m_angle( angle ){
506 }
507 void operator()( Face& face ) const {
508         face.RotateTexdef( m_angle );
509 }
510 };
511
512 void Scene_BrushRotateTexdef_Selected( scene::Graph& graph, float angle ){
513         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceRotateTexdef( angle ) );
514         SceneChangeNotify();
515 }
516
517 void Scene_BrushRotateTexdef_Component_Selected( scene::Graph& graph, float angle ){
518         Scene_ForEachSelectedBrushFace( graph, FaceRotateTexdef( angle ) );
519         SceneChangeNotify();
520 }
521
522
523 class FaceSetShader
524 {
525 const char* m_name;
526 public:
527 FaceSetShader( const char* name ) : m_name( name ) {}
528 void operator()( Face& face ) const {
529         face.SetShader( m_name );
530 }
531 };
532
533 void Scene_BrushSetShader_Selected( scene::Graph& graph, const char* name ){
534         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceSetShader( name ) );
535         SceneChangeNotify();
536 }
537
538 void Scene_BrushSetShader_Component_Selected( scene::Graph& graph, const char* name ){
539         Scene_ForEachSelectedBrushFace( graph, FaceSetShader( name ) );
540         SceneChangeNotify();
541 }
542
543 class FaceSetDetail
544 {
545 bool m_detail;
546 public:
547 FaceSetDetail( bool detail ) : m_detail( detail ){
548 }
549 void operator()( Face& face ) const {
550         face.setDetail( m_detail );
551 }
552 };
553
554 void Scene_BrushSetDetail_Selected( scene::Graph& graph, bool detail ){
555         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceSetDetail( detail ) );
556         SceneChangeNotify();
557 }
558
559 bool Face_FindReplaceShader( Face& face, const char* find, const char* replace ){
560         if ( shader_equal( face.GetShader(), find ) ) {
561                 face.SetShader( replace );
562                 return true;
563         }
564         return false;
565 }
566
567 class FaceFindReplaceShader
568 {
569 const char* m_find;
570 const char* m_replace;
571 public:
572 FaceFindReplaceShader( const char* find, const char* replace ) : m_find( find ), m_replace( replace ){
573 }
574 void operator()( Face& face ) const {
575         Face_FindReplaceShader( face, m_find, m_replace );
576 }
577 };
578
579 class FaceFindShader
580 {
581 const char* m_find;
582 const char* m_replace;
583 public:
584 FaceFindShader( const char* find ) : m_find( find ){
585 }
586 void operator()( FaceInstance& faceinst ) const {
587         if ( shader_equal( faceinst.getFace().GetShader(), m_find ) ) {
588                 faceinst.setSelected( SelectionSystem::eFace, true );
589         }
590 }
591 };
592
593 bool DoingSearch( const char *repl ){
594         return ( repl == NULL || ( strcmp( "textures/", repl ) == 0 ) );
595 }
596
597 void Scene_BrushFindReplaceShader( scene::Graph& graph, const char* find, const char* replace ){
598         if ( DoingSearch( replace ) ) {
599                 Scene_ForEachBrush_ForEachFaceInstance( graph, FaceFindShader( find ) );
600         }
601         else
602         {
603                 Scene_ForEachBrush_ForEachFace( graph, FaceFindReplaceShader( find, replace ) );
604         }
605 }
606
607 void Scene_BrushFindReplaceShader_Selected( scene::Graph& graph, const char* find, const char* replace ){
608         if ( DoingSearch( replace ) ) {
609                 Scene_ForEachSelectedBrush_ForEachFaceInstance( graph,
610                                                                                                                 FaceFindShader( find ) );
611         }
612         else
613         {
614                 Scene_ForEachSelectedBrush_ForEachFace( graph,
615                                                                                                 FaceFindReplaceShader( find, replace ) );
616         }
617 }
618
619 // TODO: find for components
620 // d1223m: dont even know what they are...
621 void Scene_BrushFindReplaceShader_Component_Selected( scene::Graph& graph, const char* find, const char* replace ){
622         if ( DoingSearch( replace ) ) {
623
624         }
625         else
626         {
627                 Scene_ForEachSelectedBrushFace( graph, FaceFindReplaceShader( find, replace ) );
628         }
629 }
630
631
632 class FaceFitTexture
633 {
634 float m_s_repeat, m_t_repeat;
635 public:
636 FaceFitTexture( float s_repeat, float t_repeat ) : m_s_repeat( s_repeat ), m_t_repeat( t_repeat ){
637 }
638 void operator()( Face& face ) const {
639         face.FitTexture( m_s_repeat, m_t_repeat );
640 }
641 };
642
643 void Scene_BrushFitTexture_Selected( scene::Graph& graph, float s_repeat, float t_repeat ){
644         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceFitTexture( s_repeat, t_repeat ) );
645         SceneChangeNotify();
646 }
647
648 void Scene_BrushFitTexture_Component_Selected( scene::Graph& graph, float s_repeat, float t_repeat ){
649         Scene_ForEachSelectedBrushFace( graph, FaceFitTexture( s_repeat, t_repeat ) );
650         SceneChangeNotify();
651 }
652
653 TextureProjection g_defaultTextureProjection;
654 const TextureProjection& TextureTransform_getDefault(){
655         TexDef_Construct_Default( g_defaultTextureProjection );
656         return g_defaultTextureProjection;
657 }
658
659 void Scene_BrushConstructPrefab( scene::Graph& graph, EBrushPrefab type, std::size_t sides, const char* shader ){
660         if ( GlobalSelectionSystem().countSelected() != 0 ) {
661                 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
662
663                 Brush* brush = Node_getBrush( path.top() );
664                 if ( brush != 0 ) {
665                         AABB bounds = brush->localAABB(); // copy bounds because the brush will be modified
666                         Brush_ConstructPrefab( *brush, type, bounds, sides, shader, TextureTransform_getDefault() );
667                         SceneChangeNotify();
668                 }
669         }
670 }
671
672 void Scene_BrushResize_Selected( scene::Graph& graph, const AABB& bounds, const char* shader ){
673         if ( GlobalSelectionSystem().countSelected() != 0 ) {
674                 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
675
676                 Brush* brush = Node_getBrush( path.top() );
677                 if ( brush != 0 ) {
678                         Brush_ConstructCuboid( *brush, bounds, shader, TextureTransform_getDefault() );
679                         SceneChangeNotify();
680                 }
681         }
682 }
683
684 bool Brush_hasShader( const Brush& brush, const char* name ){
685         for ( Brush::const_iterator i = brush.begin(); i != brush.end(); ++i )
686         {
687                 if ( shader_equal( ( *i )->GetShader(), name ) ) {
688                         return true;
689                 }
690         }
691         return false;
692 }
693
694 class BrushSelectByShaderWalker : public scene::Graph::Walker
695 {
696 const char* m_name;
697 public:
698 BrushSelectByShaderWalker( const char* name )
699         : m_name( name ){
700 }
701 bool pre( const scene::Path& path, scene::Instance& instance ) const {
702         if ( path.top().get().visible() ) {
703                 Brush* brush = Node_getBrush( path.top() );
704                 if ( brush != 0 && Brush_hasShader( *brush, m_name ) ) {
705                         Instance_getSelectable( instance )->setSelected( true );
706                 }
707         }
708         else{
709                 return false;
710         }
711         return true;
712 }
713 };
714
715 void Scene_BrushSelectByShader( scene::Graph& graph, const char* name ){
716         graph.traverse( BrushSelectByShaderWalker( name ) );
717 }
718
719 class FaceSelectByShader
720 {
721 const char* m_name;
722 public:
723 FaceSelectByShader( const char* name )
724         : m_name( name ){
725 }
726 void operator()( FaceInstance& face ) const {
727         printf( "checking %s = %s\n", face.getFace().GetShader(), m_name );
728         if ( shader_equal( face.getFace().GetShader(), m_name ) ) {
729                 face.setSelected( SelectionSystem::eFace, true );
730         }
731 }
732 };
733
734 void Scene_BrushSelectByShader_Component( scene::Graph& graph, const char* name ){
735         Scene_ForEachSelectedBrush_ForEachFaceInstance( graph, FaceSelectByShader( name ) );
736 }
737
738 class FaceGetTexdef
739 {
740 TextureProjection& m_projection;
741 mutable bool m_done;
742 public:
743 FaceGetTexdef( TextureProjection& projection )
744         : m_projection( projection ), m_done( false ){
745 }
746 void operator()( Face& face ) const {
747         if ( !m_done ) {
748                 m_done = true;
749                 face.GetTexdef( m_projection );
750         }
751 }
752 };
753
754
755 void Scene_BrushGetTexdef_Selected( scene::Graph& graph, TextureProjection& projection ){
756         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetTexdef( projection ) );
757 }
758
759 void Scene_BrushGetTexdef_Component_Selected( scene::Graph& graph, TextureProjection& projection ){
760 #if 1
761         if ( !g_SelectedFaceInstances.empty() ) {
762                 FaceInstance& faceInstance = g_SelectedFaceInstances.last();
763                 faceInstance.getFace().GetTexdef( projection );
764         }
765 #else
766         FaceGetTexdef visitor( projection );
767         Scene_ForEachSelectedBrushFace( graph, visitor );
768 #endif
769 }
770
771 void Scene_BrushGetShaderSize_Component_Selected( scene::Graph& graph, size_t& width, size_t& height ){
772         if ( !g_SelectedFaceInstances.empty() ) {
773                 FaceInstance& faceInstance = g_SelectedFaceInstances.last();
774                 width = faceInstance.getFace().getShader().width();
775                 height = faceInstance.getFace().getShader().height();
776         }
777 }
778
779
780 class FaceGetFlags
781 {
782 ContentsFlagsValue& m_flags;
783 mutable bool m_done;
784 public:
785 FaceGetFlags( ContentsFlagsValue& flags )
786         : m_flags( flags ), m_done( false ){
787 }
788 void operator()( Face& face ) const {
789         if ( !m_done ) {
790                 m_done = true;
791                 face.GetFlags( m_flags );
792         }
793 }
794 };
795
796
797 void Scene_BrushGetFlags_Selected( scene::Graph& graph, ContentsFlagsValue& flags ){
798 #if 1
799         if ( GlobalSelectionSystem().countSelected() != 0 ) {
800                 BrushInstance* brush = Instance_getBrush( GlobalSelectionSystem().ultimateSelected() );
801                 if ( brush != 0 ) {
802                         Brush_forEachFace( *brush, FaceGetFlags( flags ) );
803                 }
804         }
805 #else
806         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetFlags( flags ) );
807 #endif
808 }
809
810 void Scene_BrushGetFlags_Component_Selected( scene::Graph& graph, ContentsFlagsValue& flags ){
811 #if 1
812         if ( !g_SelectedFaceInstances.empty() ) {
813                 FaceInstance& faceInstance = g_SelectedFaceInstances.last();
814                 faceInstance.getFace().GetFlags( flags );
815         }
816 #else
817         Scene_ForEachSelectedBrushFace( graph, FaceGetFlags( flags ) );
818 #endif
819 }
820
821
822 class FaceGetShader
823 {
824 CopiedString& m_shader;
825 mutable bool m_done;
826 public:
827 FaceGetShader( CopiedString& shader )
828         : m_shader( shader ), m_done( false ){
829 }
830 void operator()( Face& face ) const {
831         if ( !m_done ) {
832                 m_done = true;
833                 m_shader = face.GetShader();
834         }
835 }
836 };
837
838 void Scene_BrushGetShader_Selected( scene::Graph& graph, CopiedString& shader ){
839 #if 1
840         if ( GlobalSelectionSystem().countSelected() != 0 ) {
841                 BrushInstance* brush = Instance_getBrush( GlobalSelectionSystem().ultimateSelected() );
842                 if ( brush != 0 ) {
843                         Brush_forEachFace( *brush, FaceGetShader( shader ) );
844                 }
845         }
846 #else
847         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetShader( shader ) );
848 #endif
849 }
850
851 void Scene_BrushGetShader_Component_Selected( scene::Graph& graph, CopiedString& shader ){
852 #if 1
853         if ( !g_SelectedFaceInstances.empty() ) {
854                 FaceInstance& faceInstance = g_SelectedFaceInstances.last();
855                 shader = faceInstance.getFace().GetShader();
856         }
857 #else
858         FaceGetShader visitor( shader );
859         Scene_ForEachSelectedBrushFace( graph, visitor );
860 #endif
861 }
862
863
864 class filter_face_shader : public FaceFilter
865 {
866 const char* m_shader;
867 public:
868 filter_face_shader( const char* shader ) : m_shader( shader ){
869 }
870 bool filter( const Face& face ) const {
871         return shader_equal( face.GetShader(), m_shader );
872 }
873 };
874
875 class filter_face_shader_prefix : public FaceFilter
876 {
877 const char* m_prefix;
878 public:
879 filter_face_shader_prefix( const char* prefix ) : m_prefix( prefix ){
880 }
881 bool filter( const Face& face ) const {
882         return shader_equal_n( face.GetShader(), m_prefix, strlen( m_prefix ) );
883 }
884 };
885
886 class filter_face_flags : public FaceFilter
887 {
888 int m_flags;
889 public:
890 filter_face_flags( int flags ) : m_flags( flags ){
891 }
892 bool filter( const Face& face ) const {
893         return ( face.getShader().shaderFlags() & m_flags ) != 0;
894 }
895 };
896
897 class filter_face_contents : public FaceFilter
898 {
899 int m_contents;
900 public:
901 filter_face_contents( int contents ) : m_contents( contents ){
902 }
903 bool filter( const Face& face ) const {
904         return ( face.getShader().m_flags.m_contentFlags & m_contents ) != 0;
905 }
906 };
907
908
909
910 class FaceFilterAny
911 {
912 FaceFilter* m_filter;
913 bool& m_filtered;
914 public:
915 FaceFilterAny( FaceFilter* filter, bool& filtered ) : m_filter( filter ), m_filtered( filtered ){
916         m_filtered = false;
917 }
918 void operator()( Face& face ) const {
919         if ( m_filter->filter( face ) ) {
920                 m_filtered = true;
921         }
922 }
923 };
924
925 class filter_brush_any_face : public BrushFilter
926 {
927 FaceFilter* m_filter;
928 public:
929 filter_brush_any_face( FaceFilter* filter ) : m_filter( filter ){
930 }
931 bool filter( const Brush& brush ) const {
932         bool filtered;
933         Brush_forEachFace( brush, FaceFilterAny( m_filter, filtered ) );
934         return filtered;
935 }
936 };
937
938 class FaceFilterAll
939 {
940 FaceFilter* m_filter;
941 bool& m_filtered;
942 public:
943 FaceFilterAll( FaceFilter* filter, bool& filtered ) : m_filter( filter ), m_filtered( filtered ){
944         m_filtered = true;
945 }
946 void operator()( Face& face ) const {
947         if ( !m_filter->filter( face ) ) {
948                 m_filtered = false;
949         }
950 }
951 };
952
953 class filter_brush_all_faces : public BrushFilter
954 {
955 FaceFilter* m_filter;
956 public:
957 filter_brush_all_faces( FaceFilter* filter ) : m_filter( filter ){
958 }
959 bool filter( const Brush& brush ) const {
960         bool filtered;
961         Brush_forEachFace( brush, FaceFilterAll( m_filter, filtered ) );
962         return filtered;
963 }
964 };
965
966
967 filter_face_flags g_filter_face_clip( QER_CLIP );
968 filter_brush_all_faces g_filter_brush_clip( &g_filter_face_clip );
969
970 filter_face_shader g_filter_face_clip_q2( "textures/clip" );
971 filter_brush_all_faces g_filter_brush_clip_q2( &g_filter_face_clip_q2 );
972
973 filter_face_shader g_filter_face_weapclip( "textures/common/weapclip" );
974 filter_brush_all_faces g_filter_brush_weapclip( &g_filter_face_weapclip );
975
976 filter_face_shader g_filter_face_commonclip( "textures/common/clip" );
977 filter_brush_all_faces g_filter_brush_commonclip( &g_filter_face_commonclip );
978
979 filter_face_shader g_filter_face_fullclip( "textures/common/fullclip" );
980 filter_brush_all_faces g_filter_brush_fullclip( &g_filter_face_fullclip );
981
982 filter_face_shader g_filter_face_botclip( "textures/common/botclip" );
983 filter_brush_all_faces g_filter_brush_botclip( &g_filter_face_botclip );
984
985 filter_face_shader_prefix g_filter_face_caulk( "textures/common/caulk" );
986 filter_brush_all_faces g_filter_brush_caulk( &g_filter_face_caulk );
987
988 filter_face_shader_prefix g_filter_face_caulk_ja( "textures/system/caulk" );
989 filter_brush_all_faces g_filter_brush_caulk_ja( &g_filter_face_caulk_ja );
990
991 filter_face_flags g_filter_face_liquids( QER_LIQUID );
992 filter_brush_any_face g_filter_brush_liquids( &g_filter_face_liquids );
993
994 filter_face_shader_prefix g_filter_face_liquidsdir( "textures/liquids/" );
995 filter_brush_any_face g_filter_brush_liquidsdir( &g_filter_face_liquidsdir );
996
997 filter_face_shader g_filter_face_hint( "textures/common/hint" );
998 filter_brush_any_face g_filter_brush_hint( &g_filter_face_hint );
999
1000 filter_face_shader g_filter_face_hintlocal( "textures/common/hintlocal" );
1001 filter_brush_any_face g_filter_brush_hintlocal( &g_filter_face_hintlocal );
1002
1003 filter_face_shader g_filter_face_hint_q2( "textures/hint" );
1004 filter_brush_any_face g_filter_brush_hint_q2( &g_filter_face_hint_q2 );
1005
1006 filter_face_shader g_filter_face_hint_ja( "textures/system/hint" );
1007 filter_brush_any_face g_filter_brush_hint_ja( &g_filter_face_hint_ja );
1008
1009 filter_face_shader g_filter_face_areaportal( "textures/common/areaportal" );
1010 filter_brush_any_face g_filter_brush_areaportal( &g_filter_face_areaportal );
1011
1012 filter_face_shader g_filter_face_visportal( "textures/editor/visportal" );
1013 filter_brush_any_face g_filter_brush_visportal( &g_filter_face_visportal );
1014
1015 filter_face_shader g_filter_face_clusterportal( "textures/common/clusterportal" );
1016 filter_brush_all_faces g_filter_brush_clusterportal( &g_filter_face_clusterportal );
1017
1018 filter_face_shader g_filter_face_lightgrid( "textures/common/lightgrid" );
1019 filter_brush_all_faces g_filter_brush_lightgrid( &g_filter_face_lightgrid );
1020
1021 filter_face_flags g_filter_face_translucent( QER_TRANS | QER_ALPHATEST );
1022 filter_brush_any_face g_filter_brush_translucent( &g_filter_face_translucent );
1023
1024 filter_face_contents g_filter_face_detail( BRUSH_DETAIL_MASK );
1025 filter_brush_all_faces g_filter_brush_detail( &g_filter_face_detail );
1026
1027 filter_face_shader_prefix g_filter_face_decals( "textures/decals/" );
1028 filter_brush_any_face g_filter_brush_decals( &g_filter_face_decals );
1029
1030
1031 void BrushFilters_construct(){
1032         add_brush_filter( g_filter_brush_clip, EXCLUDE_CLIP );
1033         add_brush_filter( g_filter_brush_clip_q2, EXCLUDE_CLIP );
1034         add_brush_filter( g_filter_brush_weapclip, EXCLUDE_CLIP );
1035         add_brush_filter( g_filter_brush_fullclip, EXCLUDE_CLIP );
1036         add_brush_filter( g_filter_brush_commonclip, EXCLUDE_CLIP );
1037         add_brush_filter( g_filter_brush_botclip, EXCLUDE_BOTCLIP );
1038         add_brush_filter( g_filter_brush_caulk, EXCLUDE_CAULK );
1039         add_brush_filter( g_filter_brush_caulk_ja, EXCLUDE_CAULK );
1040         add_face_filter( g_filter_face_caulk, EXCLUDE_CAULK );
1041         add_face_filter( g_filter_face_caulk_ja, EXCLUDE_CAULK );
1042         add_brush_filter( g_filter_brush_liquids, EXCLUDE_LIQUIDS );
1043         add_brush_filter( g_filter_brush_liquidsdir, EXCLUDE_LIQUIDS );
1044         add_brush_filter( g_filter_brush_hint, EXCLUDE_HINTSSKIPS );
1045         add_brush_filter( g_filter_brush_hintlocal, EXCLUDE_HINTSSKIPS );
1046         add_brush_filter( g_filter_brush_hint_q2, EXCLUDE_HINTSSKIPS );
1047         add_brush_filter( g_filter_brush_hint_ja, EXCLUDE_HINTSSKIPS );
1048         add_brush_filter( g_filter_brush_clusterportal, EXCLUDE_CLUSTERPORTALS );
1049         add_brush_filter( g_filter_brush_visportal, EXCLUDE_VISPORTALS );
1050         add_brush_filter( g_filter_brush_areaportal, EXCLUDE_AREAPORTALS );
1051         add_brush_filter( g_filter_brush_translucent, EXCLUDE_TRANSLUCENT );
1052         add_brush_filter( g_filter_brush_detail, EXCLUDE_DETAILS );
1053         add_brush_filter( g_filter_brush_detail, EXCLUDE_STRUCTURAL, true );
1054         add_brush_filter( g_filter_brush_lightgrid, EXCLUDE_LIGHTGRID );
1055         add_brush_filter( g_filter_brush_decals, EXCLUDE_DECALS );
1056 }
1057
1058 #if 0
1059
1060 void normalquantisation_draw(){
1061         glPointSize( 1 );
1062         glBegin( GL_POINTS );
1063         for ( std::size_t i = 0; i <= c_quantise_normal; ++i )
1064         {
1065                 for ( std::size_t j = 0; j <= c_quantise_normal; ++j )
1066                 {
1067                         Normal3f vertex( normal3f_normalised( Normal3f(
1068                                                                                                           static_cast<float>( c_quantise_normal - j - i ),
1069                                                                                                           static_cast<float>( i ),
1070                                                                                                           static_cast<float>( j )
1071                                                                                                           ) ) );
1072                         VectorScale( normal3f_to_array( vertex ), 64.f, normal3f_to_array( vertex ) );
1073                         glVertex3fv( normal3f_to_array( vertex ) );
1074                         vertex.x = -vertex.x;
1075                         glVertex3fv( normal3f_to_array( vertex ) );
1076                 }
1077         }
1078         glEnd();
1079 }
1080
1081 class RenderableNormalQuantisation : public OpenGLRenderable
1082 {
1083 public:
1084 void render( RenderStateFlags state ) const {
1085         normalquantisation_draw();
1086 }
1087 };
1088
1089 const float g_test_quantise_normal = 1.f / static_cast<float>( 1 << 3 );
1090
1091 class TestNormalQuantisation
1092 {
1093 void check_normal( const Normal3f& normal, const Normal3f& other ){
1094         spherical_t spherical = spherical_from_normal3f( normal );
1095         double longditude = RAD2DEG( spherical.longditude );
1096         double latitude = RAD2DEG( spherical.latitude );
1097         double x = cos( spherical.longditude ) * sin( spherical.latitude );
1098         double y = sin( spherical.longditude ) * sin( spherical.latitude );
1099         double z = cos( spherical.latitude );
1100
1101         ASSERT_MESSAGE( normal3f_dot( normal, other ) > 0.99, "bleh" );
1102 }
1103
1104 void test_normal( const Normal3f& normal ){
1105         Normal3f test = normal3f_from_spherical( spherical_from_normal3f( normal ) );
1106         check_normal( normal, test );
1107
1108         EOctant octant = normal3f_classify_octant( normal );
1109         Normal3f folded = normal3f_fold_octant( normal, octant );
1110         ESextant sextant = normal3f_classify_sextant( folded );
1111         folded = normal3f_fold_sextant( folded, sextant );
1112
1113         double scale = static_cast<float>( c_quantise_normal ) / ( folded.x + folded.y + folded.z );
1114
1115         double zbits = folded.z * scale;
1116         double ybits = folded.y * scale;
1117
1118         std::size_t zbits_q = static_cast<std::size_t>( zbits );
1119         std::size_t ybits_q = static_cast<std::size_t>( ybits );
1120
1121         ASSERT_MESSAGE( zbits_q <= ( c_quantise_normal / 8 ) * 3, "bleh" );
1122         ASSERT_MESSAGE( ybits_q <= ( c_quantise_normal / 2 ), "bleh" );
1123         ASSERT_MESSAGE( zbits_q + ( ( c_quantise_normal / 2 ) - ybits_q ) <= ( c_quantise_normal / 2 ), "bleh" );
1124
1125         std::size_t y_t = ( zbits_q < ( c_quantise_normal / 4 ) ) ? ybits_q : ( c_quantise_normal / 2 ) - ybits_q;
1126         std::size_t z_t = ( zbits_q < ( c_quantise_normal / 4 ) ) ? zbits_q : ( c_quantise_normal / 2 ) - zbits_q;
1127         std::size_t index = ( c_quantise_normal / 4 ) * y_t + z_t;
1128         ASSERT_MESSAGE( index <= ( c_quantise_normal / 4 ) * ( c_quantise_normal / 2 ), "bleh" );
1129
1130         Normal3f tmp( c_quantise_normal - zbits_q - ybits_q, ybits_q, zbits_q );
1131         tmp = normal3f_normalised( tmp );
1132
1133         Normal3f unfolded = normal3f_unfold_octant( normal3f_unfold_sextant( tmp, sextant ), octant );
1134
1135         check_normal( normal, unfolded );
1136
1137         double dot = normal3f_dot( normal, unfolded );
1138         float length = VectorLength( normal3f_to_array( unfolded ) );
1139         float inv_length = 1 / length;
1140
1141         Normal3f quantised = normal3f_quantised( normal );
1142         check_normal( normal, quantised );
1143 }
1144 void test2( const Normal3f& normal, const Normal3f& other ){
1145         if ( normal3f_quantised( normal ) != normal3f_quantised( other ) ) {
1146                 int bleh = 0;
1147         }
1148 }
1149
1150 static Normal3f normalise( float x, float y, float z ){
1151         return normal3f_normalised( Normal3f( x, y, z ) );
1152 }
1153
1154 float vec_rand(){
1155         return static_cast<float>( rand() - ( RAND_MAX / 2 ) );
1156 }
1157
1158 Normal3f normal3f_rand(){
1159         return normalise( vec_rand(), vec_rand(), vec_rand() );
1160 }
1161
1162 public:
1163 TestNormalQuantisation(){
1164         for ( int i = 4096; i > 0; --i )
1165                 test_normal( normal3f_rand() );
1166
1167         test_normal( normalise( 1, 0, 0 ) );
1168         test_normal( normalise( 0, 1, 0 ) );
1169         test_normal( normalise( 0, 0, 1 ) );
1170         test_normal( normalise( 1, 1, 0 ) );
1171         test_normal( normalise( 1, 0, 1 ) );
1172         test_normal( normalise( 0, 1, 1 ) );
1173
1174         test_normal( normalise( 10000, 10000, 10000 ) );
1175         test_normal( normalise( 10000, 10000, 10001 ) );
1176         test_normal( normalise( 10000, 10000, 10002 ) );
1177         test_normal( normalise( 10000, 10000, 10010 ) );
1178         test_normal( normalise( 10000, 10000, 10020 ) );
1179         test_normal( normalise( 10000, 10000, 10030 ) );
1180         test_normal( normalise( 10000, 10000, 10100 ) );
1181         test_normal( normalise( 10000, 10000, 10101 ) );
1182         test_normal( normalise( 10000, 10000, 10102 ) );
1183         test_normal( normalise( 10000, 10000, 10200 ) );
1184         test_normal( normalise( 10000, 10000, 10201 ) );
1185         test_normal( normalise( 10000, 10000, 10202 ) );
1186         test_normal( normalise( 10000, 10000, 10203 ) );
1187         test_normal( normalise( 10000, 10000, 10300 ) );
1188
1189
1190         test2( normalise( 10000, 10000, 10000 ), normalise( 10000, 10000, 10001 ) );
1191         test2( normalise( 10000, 10000, 10001 ), normalise( 10000, 10001, 10000 ) );
1192 }
1193 };
1194
1195 TestNormalQuantisation g_testNormalQuantisation;
1196
1197
1198 #endif
1199
1200 #if 0
1201 class TestSelectableObserver : public observer_template<const Selectable&>
1202 {
1203 public:
1204 void notify( const Selectable& arguments ){
1205         bool bleh = arguments.isSelected();
1206 }
1207 };
1208
1209 inline void test_bleh(){
1210         TestSelectableObserver test;
1211         ObservableSelectableInstance< SingleObservable< SelectionChangeCallback > > bleh;
1212         bleh.attach( test );
1213         bleh.setSelected( true );
1214         bleh.detach( test );
1215 }
1216
1217 class TestBleh
1218 {
1219 public:
1220 TestBleh(){
1221         test_bleh();
1222 }
1223 };
1224
1225 const TestBleh testbleh;
1226 #endif
1227
1228
1229 #if 0
1230 class TestRefcountedString
1231 {
1232 public:
1233 TestRefcountedString(){
1234         {
1235                 // copy construct
1236                 SmartString string1( "string1" );
1237                 SmartString string2( string1 );
1238                 SmartString string3( string2 );
1239         }
1240         {
1241                 // refcounted assignment
1242                 SmartString string1( "string1" );
1243                 SmartString string2( "string2" );
1244                 string1 = string2;
1245         }
1246         {
1247                 // copy assignment
1248                 SmartString string1( "string1" );
1249                 SmartString string2( "string2" );
1250                 string1 = string2.c_str();
1251         }
1252         {
1253                 // self-assignment
1254                 SmartString string1( "string1" );
1255                 string1 = string1;
1256         }
1257         {
1258                 // self-assignment via another reference
1259                 SmartString string1( "string1" );
1260                 SmartString string2( string1 );
1261                 string1 = string2;
1262         }
1263 }
1264 };
1265
1266 const TestRefcountedString g_testRefcountedString;
1267
1268 #endif
1269
1270 void Select_MakeDetail(){
1271         UndoableCommand undo( "brushSetDetail" );
1272         Scene_BrushSetDetail_Selected( GlobalSceneGraph(), true );
1273 }
1274
1275 void Select_MakeStructural(){
1276         UndoableCommand undo( "brushClearDetail" );
1277         Scene_BrushSetDetail_Selected( GlobalSceneGraph(), false );
1278 }
1279
1280 class BrushMakeSided
1281 {
1282 std::size_t m_count;
1283 public:
1284 BrushMakeSided( std::size_t count )
1285         : m_count( count ){
1286 }
1287 void set(){
1288         Scene_BrushConstructPrefab( GlobalSceneGraph(), eBrushPrism, m_count, TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
1289 }
1290 typedef MemberCaller<BrushMakeSided, &BrushMakeSided::set> SetCaller;
1291 };
1292
1293
1294 BrushMakeSided g_brushmakesided3( 3 );
1295 BrushMakeSided g_brushmakesided4( 4 );
1296 BrushMakeSided g_brushmakesided5( 5 );
1297 BrushMakeSided g_brushmakesided6( 6 );
1298 BrushMakeSided g_brushmakesided7( 7 );
1299 BrushMakeSided g_brushmakesided8( 8 );
1300 BrushMakeSided g_brushmakesided9( 9 );
1301
1302 inline int axis_for_viewtype( int viewtype ){
1303         switch ( viewtype )
1304         {
1305         case XY:
1306                 return 2;
1307         case XZ:
1308                 return 1;
1309         case YZ:
1310                 return 0;
1311         }
1312         return 2;
1313 }
1314
1315 class BrushPrefab
1316 {
1317 EBrushPrefab m_type;
1318 public:
1319 BrushPrefab( EBrushPrefab type )
1320         : m_type( type ){
1321 }
1322 void set(){
1323         DoSides( m_type, axis_for_viewtype( GetViewAxis() ) );
1324 }
1325 typedef MemberCaller<BrushPrefab, &BrushPrefab::set> SetCaller;
1326 };
1327
1328 BrushPrefab g_brushprism( eBrushPrism );
1329 BrushPrefab g_brushcone( eBrushCone );
1330 BrushPrefab g_brushsphere( eBrushSphere );
1331 BrushPrefab g_brushrock( eBrushRock );
1332
1333
1334 void FlipClip();
1335 void SplitClip();
1336 void Clip();
1337 void OnClipMode( bool enable );
1338 bool ClipMode();
1339
1340
1341 void ClipSelected(){
1342         if ( ClipMode() ) {
1343                 UndoableCommand undo( "clipperClip" );
1344                 Clip();
1345         }
1346 }
1347
1348 void SplitSelected(){
1349         if ( ClipMode() ) {
1350                 UndoableCommand undo( "clipperSplit" );
1351                 SplitClip();
1352         }
1353 }
1354
1355 void FlipClipper(){
1356         FlipClip();
1357 }
1358
1359
1360 Callback g_texture_lock_status_changed;
1361 BoolExportCaller g_texdef_movelock_caller( g_brush_texturelock_enabled );
1362 ToggleItem g_texdef_movelock_item( g_texdef_movelock_caller );
1363
1364 void Texdef_ToggleMoveLock(){
1365         g_brush_texturelock_enabled = !g_brush_texturelock_enabled;
1366         g_texdef_movelock_item.update();
1367         g_texture_lock_status_changed();
1368 }
1369
1370
1371
1372
1373
1374 void Brush_registerCommands(){
1375         GlobalToggles_insert( "TogTexLock", FreeCaller<Texdef_ToggleMoveLock>(), ToggleItem::AddCallbackCaller( g_texdef_movelock_item ), Accelerator( 'T', (GdkModifierType)GDK_SHIFT_MASK ) );
1376
1377         GlobalCommands_insert( "BrushPrism", BrushPrefab::SetCaller( g_brushprism ) );
1378         GlobalCommands_insert( "BrushCone", BrushPrefab::SetCaller( g_brushcone ) );
1379         GlobalCommands_insert( "BrushSphere", BrushPrefab::SetCaller( g_brushsphere ) );
1380         GlobalCommands_insert( "BrushRock", BrushPrefab::SetCaller( g_brushrock ) );
1381
1382         GlobalCommands_insert( "Brush3Sided", BrushMakeSided::SetCaller( g_brushmakesided3 ), Accelerator( '3', (GdkModifierType)GDK_CONTROL_MASK ) );
1383         GlobalCommands_insert( "Brush4Sided", BrushMakeSided::SetCaller( g_brushmakesided4 ), Accelerator( '4', (GdkModifierType)GDK_CONTROL_MASK ) );
1384         GlobalCommands_insert( "Brush5Sided", BrushMakeSided::SetCaller( g_brushmakesided5 ), Accelerator( '5', (GdkModifierType)GDK_CONTROL_MASK ) );
1385         GlobalCommands_insert( "Brush6Sided", BrushMakeSided::SetCaller( g_brushmakesided6 ), Accelerator( '6', (GdkModifierType)GDK_CONTROL_MASK ) );
1386         GlobalCommands_insert( "Brush7Sided", BrushMakeSided::SetCaller( g_brushmakesided7 ), Accelerator( '7', (GdkModifierType)GDK_CONTROL_MASK ) );
1387         GlobalCommands_insert( "Brush8Sided", BrushMakeSided::SetCaller( g_brushmakesided8 ), Accelerator( '8', (GdkModifierType)GDK_CONTROL_MASK ) );
1388         GlobalCommands_insert( "Brush9Sided", BrushMakeSided::SetCaller( g_brushmakesided9 ), Accelerator( '9', (GdkModifierType)GDK_CONTROL_MASK ) );
1389
1390         GlobalCommands_insert( "ClipSelected", FreeCaller<ClipSelected>(), Accelerator( GDK_Return ) );
1391         GlobalCommands_insert( "SplitSelected", FreeCaller<SplitSelected>(), Accelerator( GDK_Return, (GdkModifierType)GDK_SHIFT_MASK ) );
1392         GlobalCommands_insert( "FlipClip", FreeCaller<FlipClipper>(), Accelerator( GDK_Return, (GdkModifierType)GDK_CONTROL_MASK ) );
1393
1394         GlobalCommands_insert( "MakeDetail", FreeCaller<Select_MakeDetail>(), Accelerator( 'D', (GdkModifierType)GDK_MOD1_MASK ) );
1395         GlobalCommands_insert( "MakeStructural", FreeCaller<Select_MakeStructural>(), Accelerator( 'S', (GdkModifierType)GDK_MOD1_MASK ) );
1396 }
1397
1398 void Brush_constructMenu( GtkMenu* menu ){
1399         create_menu_item_with_mnemonic( menu, "Prism...", "BrushPrism" );
1400         create_menu_item_with_mnemonic( menu, "Cone...", "BrushCone" );
1401         create_menu_item_with_mnemonic( menu, "Sphere...", "BrushSphere" );
1402         create_menu_item_with_mnemonic( menu, "Rock...", "BrushRock" );
1403         menu_separator( menu );
1404         {
1405                 GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic( menu, "CSG" );
1406                 if ( g_Layout_enableDetachableMenus.m_value ) {
1407                         menu_tearoff( menu_in_menu );
1408                 }
1409                 create_menu_item_with_mnemonic( menu_in_menu, "CSG _Subtract", "CSGSubtract" );
1410                 create_menu_item_with_mnemonic( menu_in_menu, "CSG _Merge", "CSGMerge" );
1411                 create_menu_item_with_mnemonic( menu_in_menu, "Make _Room", "CSGroom" );
1412                 create_menu_item_with_mnemonic( menu_in_menu, "CSG _Tool", "CSGTool" );
1413         }
1414         menu_separator( menu );
1415         {
1416                 GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic( menu, "Clipper" );
1417                 if ( g_Layout_enableDetachableMenus.m_value ) {
1418                         menu_tearoff( menu_in_menu );
1419                 }
1420
1421                 create_menu_item_with_mnemonic( menu_in_menu, "Clip selection", "ClipSelected" );
1422                 create_menu_item_with_mnemonic( menu_in_menu, "Split selection", "SplitSelected" );
1423                 create_menu_item_with_mnemonic( menu_in_menu, "Flip Clip orientation", "FlipClip" );
1424         }
1425         menu_separator( menu );
1426         create_menu_item_with_mnemonic( menu, "Make detail", "MakeDetail" );
1427         create_menu_item_with_mnemonic( menu, "Make structural", "MakeStructural" );
1428 //      create_menu_item_with_mnemonic( menu, "Snap selection to _grid", "SnapToGrid" );
1429
1430         create_check_menu_item_with_mnemonic( menu, "Texture Lock", "TogTexLock" );
1431         menu_separator( menu );
1432         create_menu_item_with_mnemonic( menu, "Copy Face Texture", "FaceCopyTexture" );
1433         create_menu_item_with_mnemonic( menu, "Paste Face Texture", "FacePasteTexture" );
1434
1435         command_connect_accelerator( "Brush3Sided" );
1436         command_connect_accelerator( "Brush4Sided" );
1437         command_connect_accelerator( "Brush5Sided" );
1438         command_connect_accelerator( "Brush6Sided" );
1439         command_connect_accelerator( "Brush7Sided" );
1440         command_connect_accelerator( "Brush8Sided" );
1441         command_connect_accelerator( "Brush9Sided" );
1442 }