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