]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/brushmanip.cpp
cf6e932c50d982efa84120cf88802bc4eeb30562
[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] = mins[axis];
144
145                 planepts[1][( axis + 1 ) % 3] = planepts[0][( axis + 1 ) % 3];
146                 planepts[1][( axis + 2 ) % 3] = planepts[0][( axis + 2 ) % 3];
147                 planepts[1][axis] = maxs[axis];
148
149                 planepts[2][( axis + 1 ) % 3] = static_cast<float>( floor( planepts[0][( axis + 1 ) % 3] - radius * sv + 0.5 ) );
150                 planepts[2][( axis + 2 ) % 3] = static_cast<float>( floor( planepts[0][( axis + 2 ) % 3] + radius * cv + 0.5 ) );
151                 planepts[2][axis] = maxs[axis];
152
153                 brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
154         }
155 }
156
157 const std::size_t c_brushCone_minSides = 3;
158 const std::size_t c_brushCone_maxSides = 32;
159 const char* const c_brushCone_name = "brushCone";
160
161 void Brush_ConstructCone( Brush& brush, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){
162         if ( sides < c_brushCone_minSides ) {
163                 globalErrorStream() << c_brushCone_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushCone_minSides ) << "\n";
164                 return;
165         }
166         if ( sides > c_brushCone_maxSides ) {
167                 globalErrorStream() << c_brushCone_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushCone_maxSides ) << "\n";
168                 return;
169         }
170
171         brush.clear();
172         brush.reserve( sides + 1 );
173
174         Vector3 mins( vector3_subtracted( bounds.origin, bounds.extents ) );
175         Vector3 maxs( vector3_added( bounds.origin, bounds.extents ) );
176
177         float radius = max_extent( bounds.extents );
178         const Vector3& mid = bounds.origin;
179         Vector3 planepts[3];
180
181         planepts[0][0] = mins[0]; planepts[0][1] = mins[1]; planepts[0][2] = mins[2];
182         planepts[1][0] = maxs[0]; planepts[1][1] = mins[1]; planepts[1][2] = mins[2];
183         planepts[2][0] = maxs[0]; planepts[2][1] = maxs[1]; planepts[2][2] = mins[2];
184
185         brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
186
187         for ( std::size_t i = 0 ; i < sides ; ++i )
188         {
189                 double sv = sin( i * 3.14159265 * 2 / sides );
190                 double cv = cos( i * 3.14159265 * 2 / sides );
191
192                 planepts[0][0] = static_cast<float>( floor( mid[0] + radius * cv + 0.5 ) );
193                 planepts[0][1] = static_cast<float>( floor( mid[1] + radius * sv + 0.5 ) );
194                 planepts[0][2] = mins[2];
195
196                 planepts[1][0] = mid[0];
197                 planepts[1][1] = mid[1];
198                 planepts[1][2] = maxs[2];
199
200                 planepts[2][0] = static_cast<float>( floor( planepts[0][0] - radius * sv + 0.5 ) );
201                 planepts[2][1] = static_cast<float>( floor( planepts[0][1] + radius * cv + 0.5 ) );
202                 planepts[2][2] = maxs[2];
203
204                 brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
205         }
206 }
207
208 const std::size_t c_brushSphere_minSides = 3;
209 const std::size_t c_brushSphere_maxSides = 31;
210 const char* const c_brushSphere_name = "brushSphere";
211
212 void Brush_ConstructSphere( Brush& brush, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){
213         if ( sides < c_brushSphere_minSides ) {
214                 globalErrorStream() << c_brushSphere_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushSphere_minSides ) << "\n";
215                 return;
216         }
217         if ( sides > c_brushSphere_maxSides ) {
218                 globalErrorStream() << c_brushSphere_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushSphere_maxSides ) << "\n";
219                 return;
220         }
221
222         brush.clear();
223         brush.reserve( sides * sides );
224
225         float radius = max_extent( bounds.extents );
226         const Vector3& mid = bounds.origin;
227         Vector3 planepts[3];
228
229         double dt = 2 * c_pi / sides;
230         double dp = c_pi / sides;
231         for ( std::size_t i = 0; i < sides; i++ )
232         {
233                 for ( std::size_t j = 0; j < sides - 1; j++ )
234                 {
235                         double t = i * dt;
236                         double p = float(j * dp - c_pi / 2);
237
238                         planepts[0] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t, p ), radius ) );
239                         planepts[1] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t, p + dp ), radius ) );
240                         planepts[2] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t + dt, p + dp ), radius ) );
241
242                         brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
243                 }
244         }
245
246         {
247                 double p = ( sides - 1 ) * dp - c_pi / 2;
248                 for ( std::size_t i = 0; i < sides; i++ )
249                 {
250                         double t = i * dt;
251
252                         planepts[0] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t, p ), radius ) );
253                         planepts[1] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t + dt, p + dp ), radius ) );
254                         planepts[2] = vector3_added( mid, vector3_scaled( vector3_for_spherical( t + dt, p ), radius ) );
255
256                         brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
257                 }
258         }
259 }
260
261 const std::size_t c_brushRock_minSides = 10;
262 const std::size_t c_brushRock_maxSides = 1000;
263 const char* const c_brushRock_name = "brushRock";
264
265 void Brush_ConstructRock( Brush& brush, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){
266         if ( sides < c_brushRock_minSides ) {
267                 globalErrorStream() << c_brushRock_name << ": sides " << Unsigned( sides ) << ": too few sides, minimum is " << Unsigned( c_brushRock_minSides ) << "\n";
268                 return;
269         }
270         if ( sides > c_brushRock_maxSides ) {
271                 globalErrorStream() << c_brushRock_name << ": sides " << Unsigned( sides ) << ": too many sides, maximum is " << Unsigned( c_brushRock_maxSides ) << "\n";
272                 return;
273         }
274
275         brush.clear();
276         brush.reserve( sides * sides );
277
278         float radius = max_extent( bounds.extents );
279         const Vector3& mid = bounds.origin;
280         Vector3 planepts[3];
281
282         for ( std::size_t j = 0; j < sides; j++ )
283         {
284                 planepts[0][0] = rand() - ( RAND_MAX / 2 );
285                 planepts[0][1] = rand() - ( RAND_MAX / 2 );
286                 planepts[0][2] = rand() - ( RAND_MAX / 2 );
287                 vector3_normalise( planepts[0] );
288
289                 // find two vectors that are perpendicular to planepts[0]
290                 ComputeAxisBase( planepts[0], planepts[1], planepts[2] );
291
292                 planepts[0] = vector3_added( mid, vector3_scaled( planepts[0], radius ) );
293                 planepts[1] = vector3_added( planepts[0], vector3_scaled( planepts[1], radius ) );
294                 planepts[2] = vector3_added( planepts[0], vector3_scaled( planepts[2], radius ) );
295
296                 brush.addPlane( planepts[0], planepts[1], planepts[2], shader, projection );
297         }
298 }
299
300 int GetViewAxis(){
301         switch ( GlobalXYWnd_getCurrentViewType() )
302         {
303         case XY:
304                 return 2;
305         case XZ:
306                 return 1;
307         case YZ:
308                 return 0;
309         }
310         return 2;
311 }
312
313 void Brush_ConstructPrefab( Brush& brush, EBrushPrefab type, const AABB& bounds, std::size_t sides, const char* shader, const TextureProjection& projection ){
314         switch ( type )
315         {
316         case eBrushCuboid:
317         {
318                 UndoableCommand undo( "brushCuboid" );
319
320                 Brush_ConstructCuboid( brush, bounds, shader, projection );
321         }
322         break;
323         case eBrushPrism:
324         {
325                 int axis = GetViewAxis();
326                 StringOutputStream command;
327                 command << c_brushPrism_name << " -sides " << Unsigned( sides ) << " -axis " << axis;
328                 UndoableCommand undo( command.c_str() );
329
330                 Brush_ConstructPrism( brush, bounds, sides, axis, shader, projection );
331         }
332         break;
333         case eBrushCone:
334         {
335                 StringOutputStream command;
336                 command << c_brushCone_name << " -sides " << Unsigned( sides );
337                 UndoableCommand undo( command.c_str() );
338
339                 Brush_ConstructCone( brush, bounds, sides, shader, projection );
340         }
341         break;
342         case eBrushSphere:
343         {
344                 StringOutputStream command;
345                 command << c_brushSphere_name << " -sides " << Unsigned( sides );
346                 UndoableCommand undo( command.c_str() );
347
348                 Brush_ConstructSphere( brush, bounds, sides, shader, projection );
349         }
350         break;
351         case eBrushRock:
352         {
353                 StringOutputStream command;
354                 command << c_brushRock_name << " -sides " << Unsigned( sides );
355                 UndoableCommand undo( command.c_str() );
356
357                 Brush_ConstructRock( brush, bounds, sides, shader, projection );
358         }
359         break;
360         }
361 }
362
363
364 void ConstructRegionBrushes( scene::Node* brushes[6], const Vector3& region_mins, const Vector3& region_maxs ){
365         {
366                 // set mins
367                 Vector3 mins( region_mins[0] - 32, region_mins[1] - 32, region_mins[2] - 32 );
368
369                 // vary maxs
370                 for ( std::size_t i = 0; i < 3; i++ )
371                 {
372                         Vector3 maxs( region_maxs[0] + 32, region_maxs[1] + 32, region_maxs[2] + 32 );
373                         maxs[i] = region_mins[i];
374                         Brush_ConstructCuboid( *Node_getBrush( *brushes[i] ), aabb_for_minmax( mins, maxs ), texdef_name_default(), TextureProjection() );
375                 }
376         }
377
378         {
379                 // set maxs
380                 Vector3 maxs( region_maxs[0] + 32, region_maxs[1] + 32, region_maxs[2] + 32 );
381
382                 // vary mins
383                 for ( std::size_t i = 0; i < 3; i++ )
384                 {
385                         Vector3 mins( region_mins[0] - 32, region_mins[1] - 32, region_mins[2] - 32 );
386                         mins[i] = region_maxs[i];
387                         Brush_ConstructCuboid( *Node_getBrush( *brushes[i + 3] ), aabb_for_minmax( mins, maxs ), texdef_name_default(), TextureProjection() );
388                 }
389         }
390 }
391
392
393 class FaceSetTexdef
394 {
395 const TextureProjection& m_projection;
396 public:
397 FaceSetTexdef( const TextureProjection& projection ) : m_projection( projection ){
398 }
399 void operator()( Face& face ) const {
400         face.SetTexdef( m_projection );
401 }
402 };
403
404 void Scene_BrushSetTexdef_Selected( scene::Graph& graph, const TextureProjection& projection ){
405         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceSetTexdef( projection ) );
406         SceneChangeNotify();
407 }
408
409 void Scene_BrushSetTexdef_Component_Selected( scene::Graph& graph, const TextureProjection& projection ){
410         Scene_ForEachSelectedBrushFace( graph, FaceSetTexdef( projection ) );
411         SceneChangeNotify();
412 }
413
414
415 class FaceSetFlags
416 {
417 const ContentsFlagsValue& m_projection;
418 public:
419 FaceSetFlags( const ContentsFlagsValue& flags ) : m_projection( flags ){
420 }
421 void operator()( Face& face ) const {
422         face.SetFlags( m_projection );
423 }
424 };
425
426 void Scene_BrushSetFlags_Selected( scene::Graph& graph, const ContentsFlagsValue& flags ){
427         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceSetFlags( flags ) );
428         SceneChangeNotify();
429 }
430
431 void Scene_BrushSetFlags_Component_Selected( scene::Graph& graph, const ContentsFlagsValue& flags ){
432         Scene_ForEachSelectedBrushFace( graph, FaceSetFlags( flags ) );
433         SceneChangeNotify();
434 }
435
436 class FaceShiftTexdef
437 {
438 float m_s, m_t;
439 public:
440 FaceShiftTexdef( float s, float t ) : m_s( s ), m_t( t ){
441 }
442 void operator()( Face& face ) const {
443         face.ShiftTexdef( m_s, m_t );
444 }
445 };
446
447 void Scene_BrushShiftTexdef_Selected( scene::Graph& graph, float s, float t ){
448         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceShiftTexdef( s, t ) );
449         SceneChangeNotify();
450 }
451
452 void Scene_BrushShiftTexdef_Component_Selected( scene::Graph& graph, float s, float t ){
453         Scene_ForEachSelectedBrushFace( graph, FaceShiftTexdef( s, t ) );
454         SceneChangeNotify();
455 }
456
457 class FaceScaleTexdef
458 {
459 float m_s, m_t;
460 public:
461 FaceScaleTexdef( float s, float t ) : m_s( s ), m_t( t ){
462 }
463 void operator()( Face& face ) const {
464         face.ScaleTexdef( m_s, m_t );
465 }
466 };
467
468 void Scene_BrushScaleTexdef_Selected( scene::Graph& graph, float s, float t ){
469         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceScaleTexdef( s, t ) );
470         SceneChangeNotify();
471 }
472
473 void Scene_BrushScaleTexdef_Component_Selected( scene::Graph& graph, float s, float t ){
474         Scene_ForEachSelectedBrushFace( graph, FaceScaleTexdef( s, t ) );
475         SceneChangeNotify();
476 }
477
478 class FaceRotateTexdef
479 {
480 float m_angle;
481 public:
482 FaceRotateTexdef( float angle ) : m_angle( angle ){
483 }
484 void operator()( Face& face ) const {
485         face.RotateTexdef( m_angle );
486 }
487 };
488
489 void Scene_BrushRotateTexdef_Selected( scene::Graph& graph, float angle ){
490         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceRotateTexdef( angle ) );
491         SceneChangeNotify();
492 }
493
494 void Scene_BrushRotateTexdef_Component_Selected( scene::Graph& graph, float angle ){
495         Scene_ForEachSelectedBrushFace( graph, FaceRotateTexdef( angle ) );
496         SceneChangeNotify();
497 }
498
499
500 class FaceSetShader
501 {
502 const char* m_name;
503 public:
504 FaceSetShader( const char* name ) : m_name( name ) {}
505 void operator()( Face& face ) const {
506         face.SetShader( m_name );
507 }
508 };
509
510 void Scene_BrushSetShader_Selected( scene::Graph& graph, const char* name ){
511         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceSetShader( name ) );
512         SceneChangeNotify();
513 }
514
515 void Scene_BrushSetShader_Component_Selected( scene::Graph& graph, const char* name ){
516         Scene_ForEachSelectedBrushFace( graph, FaceSetShader( name ) );
517         SceneChangeNotify();
518 }
519
520 class FaceSetDetail
521 {
522 bool m_detail;
523 public:
524 FaceSetDetail( bool detail ) : m_detail( detail ){
525 }
526 void operator()( Face& face ) const {
527         face.setDetail( m_detail );
528 }
529 };
530
531 void Scene_BrushSetDetail_Selected( scene::Graph& graph, bool detail ){
532         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceSetDetail( detail ) );
533         SceneChangeNotify();
534 }
535
536 bool Face_FindReplaceShader( Face& face, const char* find, const char* replace ){
537         if ( shader_equal( face.GetShader(), find ) ) {
538                 face.SetShader( replace );
539                 return true;
540         }
541         return false;
542 }
543
544 class FaceFindReplaceShader
545 {
546 const char* m_find;
547 const char* m_replace;
548 public:
549 FaceFindReplaceShader( const char* find, const char* replace ) : m_find( find ), m_replace( replace ){
550 }
551 void operator()( Face& face ) const {
552         Face_FindReplaceShader( face, m_find, m_replace );
553 }
554 };
555
556 class FaceFindShader
557 {
558 const char* m_find;
559 const char* m_replace;
560 public:
561 FaceFindShader( const char* find ) : m_find( find ){
562 }
563 void operator()( FaceInstance& faceinst ) const {
564         if ( shader_equal( faceinst.getFace().GetShader(), m_find ) ) {
565                 faceinst.setSelected( SelectionSystem::eFace, true );
566         }
567 }
568 };
569
570 bool DoingSearch( const char *repl ){
571         return ( repl == NULL || ( strcmp( "textures/", repl ) == 0 ) );
572 }
573
574 void Scene_BrushFindReplaceShader( scene::Graph& graph, const char* find, const char* replace ){
575         if ( DoingSearch( replace ) ) {
576                 Scene_ForEachBrush_ForEachFaceInstance( graph, FaceFindShader( find ) );
577         }
578         else
579         {
580                 Scene_ForEachBrush_ForEachFace( graph, FaceFindReplaceShader( find, replace ) );
581         }
582 }
583
584 void Scene_BrushFindReplaceShader_Selected( scene::Graph& graph, const char* find, const char* replace ){
585         if ( DoingSearch( replace ) ) {
586                 Scene_ForEachSelectedBrush_ForEachFaceInstance( graph,
587                                                                                                                 FaceFindShader( find ) );
588         }
589         else
590         {
591                 Scene_ForEachSelectedBrush_ForEachFace( graph,
592                                                                                                 FaceFindReplaceShader( find, replace ) );
593         }
594 }
595
596 // TODO: find for components
597 // d1223m: dont even know what they are...
598 void Scene_BrushFindReplaceShader_Component_Selected( scene::Graph& graph, const char* find, const char* replace ){
599         if ( DoingSearch( replace ) ) {
600
601         }
602         else
603         {
604                 Scene_ForEachSelectedBrushFace( graph, FaceFindReplaceShader( find, replace ) );
605         }
606 }
607
608
609 class FaceFitTexture
610 {
611 float m_s_repeat, m_t_repeat;
612 public:
613 FaceFitTexture( float s_repeat, float t_repeat ) : m_s_repeat( s_repeat ), m_t_repeat( t_repeat ){
614 }
615 void operator()( Face& face ) const {
616         face.FitTexture( m_s_repeat, m_t_repeat );
617 }
618 };
619
620 void Scene_BrushFitTexture_Selected( scene::Graph& graph, float s_repeat, float t_repeat ){
621         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceFitTexture( s_repeat, t_repeat ) );
622         SceneChangeNotify();
623 }
624
625 void Scene_BrushFitTexture_Component_Selected( scene::Graph& graph, float s_repeat, float t_repeat ){
626         Scene_ForEachSelectedBrushFace( graph, FaceFitTexture( s_repeat, t_repeat ) );
627         SceneChangeNotify();
628 }
629
630 TextureProjection g_defaultTextureProjection;
631 const TextureProjection& TextureTransform_getDefault(){
632         TexDef_Construct_Default( g_defaultTextureProjection );
633         return g_defaultTextureProjection;
634 }
635
636 void Scene_BrushConstructPrefab( scene::Graph& graph, EBrushPrefab type, std::size_t sides, const char* shader ){
637         if ( GlobalSelectionSystem().countSelected() != 0 ) {
638                 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
639
640                 Brush* brush = Node_getBrush( path.top() );
641                 if ( brush != 0 ) {
642                         AABB bounds = brush->localAABB(); // copy bounds because the brush will be modified
643                         Brush_ConstructPrefab( *brush, type, bounds, sides, shader, TextureTransform_getDefault() );
644                         SceneChangeNotify();
645                 }
646         }
647 }
648
649 void Scene_BrushResize_Selected( scene::Graph& graph, const AABB& bounds, const char* shader ){
650         if ( GlobalSelectionSystem().countSelected() != 0 ) {
651                 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
652
653                 Brush* brush = Node_getBrush( path.top() );
654                 if ( brush != 0 ) {
655                         Brush_ConstructCuboid( *brush, bounds, shader, TextureTransform_getDefault() );
656                         SceneChangeNotify();
657                 }
658         }
659 }
660
661 bool Brush_hasShader( const Brush& brush, const char* name ){
662         for ( Brush::const_iterator i = brush.begin(); i != brush.end(); ++i )
663         {
664                 if ( shader_equal( ( *i )->GetShader(), name ) ) {
665                         return true;
666                 }
667         }
668         return false;
669 }
670
671 class BrushSelectByShaderWalker : public scene::Graph::Walker
672 {
673 const char* m_name;
674 public:
675 BrushSelectByShaderWalker( const char* name )
676         : m_name( name ){
677 }
678 bool pre( const scene::Path& path, scene::Instance& instance ) const {
679         if ( path.top().get().visible() ) {
680                 Brush* brush = Node_getBrush( path.top() );
681                 if ( brush != 0 && Brush_hasShader( *brush, m_name ) ) {
682                         Instance_getSelectable( instance )->setSelected( true );
683                 }
684         }
685         return true;
686 }
687 };
688
689 void Scene_BrushSelectByShader( scene::Graph& graph, const char* name ){
690         graph.traverse( BrushSelectByShaderWalker( name ) );
691 }
692
693 class FaceSelectByShader
694 {
695 const char* m_name;
696 public:
697 FaceSelectByShader( const char* name )
698         : m_name( name ){
699 }
700 void operator()( FaceInstance& face ) const {
701         printf( "checking %s = %s\n", face.getFace().GetShader(), m_name );
702         if ( shader_equal( face.getFace().GetShader(), m_name ) ) {
703                 face.setSelected( SelectionSystem::eFace, true );
704         }
705 }
706 };
707
708 void Scene_BrushSelectByShader_Component( scene::Graph& graph, const char* name ){
709         Scene_ForEachSelectedBrush_ForEachFaceInstance( graph, FaceSelectByShader( name ) );
710 }
711
712 class FaceGetTexdef
713 {
714 TextureProjection& m_projection;
715 mutable bool m_done;
716 public:
717 FaceGetTexdef( TextureProjection& projection )
718         : m_projection( projection ), m_done( false ){
719 }
720 void operator()( Face& face ) const {
721         if ( !m_done ) {
722                 m_done = true;
723                 face.GetTexdef( m_projection );
724         }
725 }
726 };
727
728
729 void Scene_BrushGetTexdef_Selected( scene::Graph& graph, TextureProjection& projection ){
730         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetTexdef( projection ) );
731 }
732
733 void Scene_BrushGetTexdef_Component_Selected( scene::Graph& graph, TextureProjection& projection ){
734 #if 1
735         if ( !g_SelectedFaceInstances.empty() ) {
736                 FaceInstance& faceInstance = g_SelectedFaceInstances.last();
737                 faceInstance.getFace().GetTexdef( projection );
738         }
739 #else
740         FaceGetTexdef visitor( projection );
741         Scene_ForEachSelectedBrushFace( graph, visitor );
742 #endif
743 }
744
745 void Scene_BrushGetShaderSize_Component_Selected( scene::Graph& graph, size_t& width, size_t& height ){
746         if ( !g_SelectedFaceInstances.empty() ) {
747                 FaceInstance& faceInstance = g_SelectedFaceInstances.last();
748                 width = faceInstance.getFace().getShader().width();
749                 height = faceInstance.getFace().getShader().height();
750         }
751 }
752
753
754 class FaceGetFlags
755 {
756 ContentsFlagsValue& m_flags;
757 mutable bool m_done;
758 public:
759 FaceGetFlags( ContentsFlagsValue& flags )
760         : m_flags( flags ), m_done( false ){
761 }
762 void operator()( Face& face ) const {
763         if ( !m_done ) {
764                 m_done = true;
765                 face.GetFlags( m_flags );
766         }
767 }
768 };
769
770
771 void Scene_BrushGetFlags_Selected( scene::Graph& graph, ContentsFlagsValue& flags ){
772 #if 1
773         if ( GlobalSelectionSystem().countSelected() != 0 ) {
774                 BrushInstance* brush = Instance_getBrush( GlobalSelectionSystem().ultimateSelected() );
775                 if ( brush != 0 ) {
776                         Brush_forEachFace( *brush, FaceGetFlags( flags ) );
777                 }
778         }
779 #else
780         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetFlags( flags ) );
781 #endif
782 }
783
784 void Scene_BrushGetFlags_Component_Selected( scene::Graph& graph, ContentsFlagsValue& flags ){
785 #if 1
786         if ( !g_SelectedFaceInstances.empty() ) {
787                 FaceInstance& faceInstance = g_SelectedFaceInstances.last();
788                 faceInstance.getFace().GetFlags( flags );
789         }
790 #else
791         Scene_ForEachSelectedBrushFace( graph, FaceGetFlags( flags ) );
792 #endif
793 }
794
795
796 class FaceGetShader
797 {
798 CopiedString& m_shader;
799 mutable bool m_done;
800 public:
801 FaceGetShader( CopiedString& shader )
802         : m_shader( shader ), m_done( false ){
803 }
804 void operator()( Face& face ) const {
805         if ( !m_done ) {
806                 m_done = true;
807                 m_shader = face.GetShader();
808         }
809 }
810 };
811
812 void Scene_BrushGetShader_Selected( scene::Graph& graph, CopiedString& shader ){
813 #if 1
814         if ( GlobalSelectionSystem().countSelected() != 0 ) {
815                 BrushInstance* brush = Instance_getBrush( GlobalSelectionSystem().ultimateSelected() );
816                 if ( brush != 0 ) {
817                         Brush_forEachFace( *brush, FaceGetShader( shader ) );
818                 }
819         }
820 #else
821         Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetShader( shader ) );
822 #endif
823 }
824
825 void Scene_BrushGetShader_Component_Selected( scene::Graph& graph, CopiedString& shader ){
826 #if 1
827         if ( !g_SelectedFaceInstances.empty() ) {
828                 FaceInstance& faceInstance = g_SelectedFaceInstances.last();
829                 shader = faceInstance.getFace().GetShader();
830         }
831 #else
832         FaceGetShader visitor( shader );
833         Scene_ForEachSelectedBrushFace( graph, visitor );
834 #endif
835 }
836
837
838 class filter_face_shader : public FaceFilter
839 {
840 const char* m_shader;
841 public:
842 filter_face_shader( const char* shader ) : m_shader( shader ){
843 }
844 bool filter( const Face& face ) const {
845         return shader_equal( face.GetShader(), m_shader );
846 }
847 };
848
849 class filter_face_shader_prefix : public FaceFilter
850 {
851 const char* m_prefix;
852 public:
853 filter_face_shader_prefix( const char* prefix ) : m_prefix( prefix ){
854 }
855 bool filter( const Face& face ) const {
856         return shader_equal_n( face.GetShader(), m_prefix, strlen( m_prefix ) );
857 }
858 };
859
860 class filter_face_flags : public FaceFilter
861 {
862 int m_flags;
863 public:
864 filter_face_flags( int flags ) : m_flags( flags ){
865 }
866 bool filter( const Face& face ) const {
867         return ( face.getShader().shaderFlags() & m_flags ) != 0;
868 }
869 };
870
871 class filter_face_contents : public FaceFilter
872 {
873 int m_contents;
874 public:
875 filter_face_contents( int contents ) : m_contents( contents ){
876 }
877 bool filter( const Face& face ) const {
878         return ( face.getShader().m_flags.m_contentFlags & m_contents ) != 0;
879 }
880 };
881
882
883
884 class FaceFilterAny
885 {
886 FaceFilter* m_filter;
887 bool& m_filtered;
888 public:
889 FaceFilterAny( FaceFilter* filter, bool& filtered ) : m_filter( filter ), m_filtered( filtered ){
890         m_filtered = false;
891 }
892 void operator()( Face& face ) const {
893         if ( m_filter->filter( face ) ) {
894                 m_filtered = true;
895         }
896 }
897 };
898
899 class filter_brush_any_face : public BrushFilter
900 {
901 FaceFilter* m_filter;
902 public:
903 filter_brush_any_face( FaceFilter* filter ) : m_filter( filter ){
904 }
905 bool filter( const Brush& brush ) const {
906         bool filtered;
907         Brush_forEachFace( brush, FaceFilterAny( m_filter, filtered ) );
908         return filtered;
909 }
910 };
911
912 class FaceFilterAll
913 {
914 FaceFilter* m_filter;
915 bool& m_filtered;
916 public:
917 FaceFilterAll( FaceFilter* filter, bool& filtered ) : m_filter( filter ), m_filtered( filtered ){
918         m_filtered = true;
919 }
920 void operator()( Face& face ) const {
921         if ( !m_filter->filter( face ) ) {
922                 m_filtered = false;
923         }
924 }
925 };
926
927 class filter_brush_all_faces : public BrushFilter
928 {
929 FaceFilter* m_filter;
930 public:
931 filter_brush_all_faces( FaceFilter* filter ) : m_filter( filter ){
932 }
933 bool filter( const Brush& brush ) const {
934         bool filtered;
935         Brush_forEachFace( brush, FaceFilterAll( m_filter, filtered ) );
936         return filtered;
937 }
938 };
939
940
941 filter_face_flags g_filter_face_clip( QER_CLIP );
942 filter_brush_all_faces g_filter_brush_clip( &g_filter_face_clip );
943
944 filter_face_shader g_filter_face_clip_q2( "textures/clip" );
945 filter_brush_all_faces g_filter_brush_clip_q2( &g_filter_face_clip_q2 );
946
947 filter_face_shader g_filter_face_weapclip( "textures/common/weapclip" );
948 filter_brush_all_faces g_filter_brush_weapclip( &g_filter_face_weapclip );
949
950 filter_face_shader g_filter_face_commonclip( "textures/common/clip" );
951 filter_brush_all_faces g_filter_brush_commonclip( &g_filter_face_commonclip );
952
953 filter_face_shader g_filter_face_fullclip( "textures/common/fullclip" );
954 filter_brush_all_faces g_filter_brush_fullclip( &g_filter_face_fullclip );
955
956 filter_face_shader g_filter_face_botclip( "textures/common/botclip" );
957 filter_brush_all_faces g_filter_brush_botclip( &g_filter_face_botclip );
958
959 filter_face_shader_prefix g_filter_face_caulk( "textures/common/caulk" );
960 filter_brush_all_faces g_filter_brush_caulk( &g_filter_face_caulk );
961
962 filter_face_shader_prefix g_filter_face_caulk_ja( "textures/system/caulk" );
963 filter_brush_all_faces g_filter_brush_caulk_ja( &g_filter_face_caulk_ja );
964
965 filter_face_shader_prefix g_filter_face_liquids( "textures/liquids/" );
966 filter_brush_any_face g_filter_brush_liquids( &g_filter_face_liquids );
967
968 filter_face_shader g_filter_face_hint( "textures/common/hint" );
969 filter_brush_any_face g_filter_brush_hint( &g_filter_face_hint );
970
971 filter_face_shader g_filter_face_hint_q2( "textures/hint" );
972 filter_brush_any_face g_filter_brush_hint_q2( &g_filter_face_hint_q2 );
973
974 filter_face_shader g_filter_face_hint_ja( "textures/system/hint" );
975 filter_brush_any_face g_filter_brush_hint_ja( &g_filter_face_hint_ja );
976
977 filter_face_shader g_filter_face_areaportal( "textures/common/areaportal" );
978 filter_brush_all_faces g_filter_brush_areaportal( &g_filter_face_areaportal );
979
980 filter_face_shader g_filter_face_visportal( "textures/editor/visportal" );
981 filter_brush_any_face g_filter_brush_visportal( &g_filter_face_visportal );
982
983 filter_face_shader g_filter_face_clusterportal( "textures/common/clusterportal" );
984 filter_brush_all_faces g_filter_brush_clusterportal( &g_filter_face_clusterportal );
985
986 filter_face_shader g_filter_face_lightgrid( "textures/common/lightgrid" );
987 filter_brush_all_faces g_filter_brush_lightgrid( &g_filter_face_lightgrid );
988
989 filter_face_flags g_filter_face_translucent( QER_TRANS );
990 filter_brush_all_faces g_filter_brush_translucent( &g_filter_face_translucent );
991
992 filter_face_contents g_filter_face_detail( BRUSH_DETAIL_MASK );
993 filter_brush_all_faces g_filter_brush_detail( &g_filter_face_detail );
994
995 filter_face_shader_prefix g_filter_face_decals( "textures/decals/" );
996 filter_brush_any_face g_filter_brush_decals( &g_filter_face_decals );
997
998
999 void BrushFilters_construct(){
1000         add_brush_filter( g_filter_brush_clip, EXCLUDE_CLIP );
1001         add_brush_filter( g_filter_brush_clip_q2, EXCLUDE_CLIP );
1002         add_brush_filter( g_filter_brush_weapclip, EXCLUDE_CLIP );
1003         add_brush_filter( g_filter_brush_fullclip, EXCLUDE_CLIP );
1004         add_brush_filter( g_filter_brush_commonclip, EXCLUDE_CLIP );
1005         add_brush_filter( g_filter_brush_botclip, EXCLUDE_BOTCLIP );
1006         add_brush_filter( g_filter_brush_caulk, EXCLUDE_CAULK );
1007         add_brush_filter( g_filter_brush_caulk_ja, EXCLUDE_CAULK );
1008         add_face_filter( g_filter_face_caulk, EXCLUDE_CAULK );
1009         add_face_filter( g_filter_face_caulk_ja, EXCLUDE_CAULK );
1010         add_brush_filter( g_filter_brush_liquids, EXCLUDE_LIQUIDS );
1011         add_brush_filter( g_filter_brush_hint, EXCLUDE_HINTSSKIPS );
1012         add_brush_filter( g_filter_brush_hint_q2, EXCLUDE_HINTSSKIPS );
1013         add_brush_filter( g_filter_brush_hint_ja, EXCLUDE_HINTSSKIPS );
1014         add_brush_filter( g_filter_brush_clusterportal, EXCLUDE_CLUSTERPORTALS );
1015         add_brush_filter( g_filter_brush_visportal, EXCLUDE_VISPORTALS );
1016         add_brush_filter( g_filter_brush_areaportal, EXCLUDE_AREAPORTALS );
1017         add_brush_filter( g_filter_brush_translucent, EXCLUDE_TRANSLUCENT );
1018         add_brush_filter( g_filter_brush_detail, EXCLUDE_DETAILS );
1019         add_brush_filter( g_filter_brush_detail, EXCLUDE_STRUCTURAL, true );
1020         add_brush_filter( g_filter_brush_lightgrid, EXCLUDE_LIGHTGRID );
1021         add_brush_filter( g_filter_brush_decals, EXCLUDE_DECALS );
1022 }
1023
1024 void Select_MakeDetail(){
1025         UndoableCommand undo( "brushSetDetail" );
1026         Scene_BrushSetDetail_Selected( GlobalSceneGraph(), true );
1027 }
1028
1029 void Select_MakeStructural(){
1030         UndoableCommand undo( "brushClearDetail" );
1031         Scene_BrushSetDetail_Selected( GlobalSceneGraph(), false );
1032 }
1033
1034 class BrushMakeSided
1035 {
1036 std::size_t m_count;
1037 public:
1038 BrushMakeSided( std::size_t count )
1039         : m_count( count ){
1040 }
1041 void set(){
1042         Scene_BrushConstructPrefab( GlobalSceneGraph(), eBrushPrism, m_count, TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
1043 }
1044 typedef MemberCaller<BrushMakeSided, &BrushMakeSided::set> SetCaller;
1045 };
1046
1047
1048 BrushMakeSided g_brushmakesided3( 3 );
1049 BrushMakeSided g_brushmakesided4( 4 );
1050 BrushMakeSided g_brushmakesided5( 5 );
1051 BrushMakeSided g_brushmakesided6( 6 );
1052 BrushMakeSided g_brushmakesided7( 7 );
1053 BrushMakeSided g_brushmakesided8( 8 );
1054 BrushMakeSided g_brushmakesided9( 9 );
1055
1056 inline int axis_for_viewtype( int viewtype ){
1057         switch ( viewtype )
1058         {
1059         case XY:
1060                 return 2;
1061         case XZ:
1062                 return 1;
1063         case YZ:
1064                 return 0;
1065         }
1066         return 2;
1067 }
1068
1069 class BrushPrefab
1070 {
1071 EBrushPrefab m_type;
1072 public:
1073 BrushPrefab( EBrushPrefab type )
1074         : m_type( type ){
1075 }
1076 void set(){
1077         DoSides( m_type, axis_for_viewtype( GetViewAxis() ) );
1078 }
1079 typedef MemberCaller<BrushPrefab, &BrushPrefab::set> SetCaller;
1080 };
1081
1082 BrushPrefab g_brushprism( eBrushPrism );
1083 BrushPrefab g_brushcone( eBrushCone );
1084 BrushPrefab g_brushsphere( eBrushSphere );
1085 BrushPrefab g_brushrock( eBrushRock );
1086
1087
1088 void FlipClip();
1089 void SplitClip();
1090 void Clip();
1091 void OnClipMode( bool enable );
1092 bool ClipMode();
1093
1094
1095 void ClipSelected(){
1096         if ( ClipMode() ) {
1097                 UndoableCommand undo( "clipperClip" );
1098                 Clip();
1099         }
1100 }
1101
1102 void SplitSelected(){
1103         if ( ClipMode() ) {
1104                 UndoableCommand undo( "clipperSplit" );
1105                 SplitClip();
1106         }
1107 }
1108
1109 void FlipClipper(){
1110         FlipClip();
1111 }
1112
1113
1114 Callback g_texture_lock_status_changed;
1115 BoolExportCaller g_texdef_movelock_caller( g_brush_texturelock_enabled );
1116 ToggleItem g_texdef_movelock_item( g_texdef_movelock_caller );
1117
1118 void Texdef_ToggleMoveLock(){
1119         g_brush_texturelock_enabled = !g_brush_texturelock_enabled;
1120         g_texdef_movelock_item.update();
1121         g_texture_lock_status_changed();
1122 }
1123
1124
1125
1126
1127
1128 void Brush_registerCommands(){
1129         GlobalToggles_insert( "TogTexLock", FreeCaller<Texdef_ToggleMoveLock>(), ToggleItem::AddCallbackCaller( g_texdef_movelock_item ), Accelerator( 'T', (GdkModifierType)GDK_SHIFT_MASK ) );
1130
1131         GlobalCommands_insert( "BrushPrism", BrushPrefab::SetCaller( g_brushprism ) );
1132         GlobalCommands_insert( "BrushCone", BrushPrefab::SetCaller( g_brushcone ) );
1133         GlobalCommands_insert( "BrushSphere", BrushPrefab::SetCaller( g_brushsphere ) );
1134         GlobalCommands_insert( "BrushRock", BrushPrefab::SetCaller( g_brushrock ) );
1135
1136         GlobalCommands_insert( "Brush3Sided", BrushMakeSided::SetCaller( g_brushmakesided3 ), Accelerator( '3', (GdkModifierType)GDK_CONTROL_MASK ) );
1137         GlobalCommands_insert( "Brush4Sided", BrushMakeSided::SetCaller( g_brushmakesided4 ), Accelerator( '4', (GdkModifierType)GDK_CONTROL_MASK ) );
1138         GlobalCommands_insert( "Brush5Sided", BrushMakeSided::SetCaller( g_brushmakesided5 ), Accelerator( '5', (GdkModifierType)GDK_CONTROL_MASK ) );
1139         GlobalCommands_insert( "Brush6Sided", BrushMakeSided::SetCaller( g_brushmakesided6 ), Accelerator( '6', (GdkModifierType)GDK_CONTROL_MASK ) );
1140         GlobalCommands_insert( "Brush7Sided", BrushMakeSided::SetCaller( g_brushmakesided7 ), Accelerator( '7', (GdkModifierType)GDK_CONTROL_MASK ) );
1141         GlobalCommands_insert( "Brush8Sided", BrushMakeSided::SetCaller( g_brushmakesided8 ), Accelerator( '8', (GdkModifierType)GDK_CONTROL_MASK ) );
1142         GlobalCommands_insert( "Brush9Sided", BrushMakeSided::SetCaller( g_brushmakesided9 ), Accelerator( '9', (GdkModifierType)GDK_CONTROL_MASK ) );
1143
1144         GlobalCommands_insert( "ClipSelected", FreeCaller<ClipSelected>(), Accelerator( GDK_Return ) );
1145         GlobalCommands_insert( "SplitSelected", FreeCaller<SplitSelected>(), Accelerator( GDK_Return, (GdkModifierType)GDK_SHIFT_MASK ) );
1146         GlobalCommands_insert( "FlipClip", FreeCaller<FlipClipper>(), Accelerator( GDK_Return, (GdkModifierType)GDK_CONTROL_MASK ) );
1147
1148         GlobalCommands_insert( "MakeDetail", FreeCaller<Select_MakeDetail>(), Accelerator( 'M', (GdkModifierType)GDK_CONTROL_MASK ) );
1149         GlobalCommands_insert( "MakeStructural", FreeCaller<Select_MakeStructural>(), Accelerator( 'S', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
1150 }
1151
1152 void Brush_constructMenu( GtkMenu* menu ){
1153         create_menu_item_with_mnemonic( menu, "Prism...", "BrushPrism" );
1154         create_menu_item_with_mnemonic( menu, "Cone...", "BrushCone" );
1155         create_menu_item_with_mnemonic( menu, "Sphere...", "BrushSphere" );
1156         create_menu_item_with_mnemonic( menu, "Rock...", "BrushRock" );
1157         menu_separator( menu );
1158         {
1159                 GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic( menu, "CSG" );
1160                 if ( g_Layout_enableDetachableMenus.m_value ) {
1161                         menu_tearoff( menu_in_menu );
1162                 }
1163                 create_menu_item_with_mnemonic( menu_in_menu, "Make _Hollow", "CSGHollow" );
1164                 create_menu_item_with_mnemonic( menu_in_menu, "CSG _Subtract", "CSGSubtract" );
1165                 create_menu_item_with_mnemonic( menu_in_menu, "CSG _Merge", "CSGMerge" );
1166         }
1167         menu_separator( menu );
1168         {
1169                 GtkMenu* menu_in_menu = create_sub_menu_with_mnemonic( menu, "Clipper" );
1170                 if ( g_Layout_enableDetachableMenus.m_value ) {
1171                         menu_tearoff( menu_in_menu );
1172                 }
1173
1174                 create_menu_item_with_mnemonic( menu_in_menu, "Clip selection", "ClipSelected" );
1175                 create_menu_item_with_mnemonic( menu_in_menu, "Split selection", "SplitSelected" );
1176                 create_menu_item_with_mnemonic( menu_in_menu, "Flip Clip orientation", "FlipClip" );
1177         }
1178         menu_separator( menu );
1179         create_menu_item_with_mnemonic( menu, "Make detail", "MakeDetail" );
1180         create_menu_item_with_mnemonic( menu, "Make structural", "MakeStructural" );
1181         create_menu_item_with_mnemonic( menu, "Snap selection to _grid", "SnapToGrid" );
1182
1183         create_check_menu_item_with_mnemonic( menu, "Texture Lock", "TogTexLock" );
1184         menu_separator( menu );
1185         create_menu_item_with_mnemonic( menu, "Copy Face Texture", "FaceCopyTexture" );
1186         create_menu_item_with_mnemonic( menu, "Paste Face Texture", "FacePasteTexture" );
1187
1188         command_connect_accelerator( "Brush3Sided" );
1189         command_connect_accelerator( "Brush4Sided" );
1190         command_connect_accelerator( "Brush5Sided" );
1191         command_connect_accelerator( "Brush6Sided" );
1192         command_connect_accelerator( "Brush7Sided" );
1193         command_connect_accelerator( "Brush8Sided" );
1194         command_connect_accelerator( "Brush9Sided" );
1195 }