2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
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.
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.
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
22 #include "brushmanip.h"
25 #include "gtkutil/widget.h"
26 #include "gtkutil/menu.h"
28 #include "brushnode.h"
30 #include "texwindow.h"
33 #include "mainframe.h"
36 #include "preferences.h"
39 #include <gdk/gdkkeysyms.h>
41 void Brush_ConstructCuboid(Brush &brush, const AABB &bounds, const char *shader, const TextureProjection &projection)
43 const unsigned char box[3][2] = {{0, 1},
46 Vector3 mins(vector3_subtracted(bounds.origin, bounds.extents));
47 Vector3 maxs(vector3_added(bounds.origin, bounds.extents));
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]];
59 brush.addPlane(maxs, planepts1, planepts2, shader, projection);
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]];
69 brush.addPlane(mins, planepts1, planepts2, shader, projection);
74 inline float max_extent(const Vector3 &extents)
76 return std::max(std::max(extents[0], extents[1]), extents[2]);
79 inline float max_extent_2d(const Vector3 &extents, int axis)
83 return std::max(extents[1], extents[2]);
85 return std::max(extents[0], extents[2]);
87 return std::max(extents[0], extents[1]);
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";
95 void Brush_ConstructPrism(Brush &brush, const AABB &bounds, std::size_t sides, int axis, const char *shader,
96 const TextureProjection &projection)
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";
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";
110 brush.reserve(sides + 2);
112 Vector3 mins(vector3_subtracted(bounds.origin, bounds.extents));
113 Vector3 maxs(vector3_added(bounds.origin, bounds.extents));
115 float radius = max_extent_2d(bounds.extents, axis);
116 const Vector3 &mid = bounds.origin;
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];
129 brush.addPlane(planepts[0], planepts[1], planepts[2], shader, projection);
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];
141 brush.addPlane(planepts[0], planepts[1], planepts[2], shader, projection);
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);
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];
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];
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];
159 brush.addPlane(planepts[0], planepts[1], planepts[2], shader, projection);
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";
167 void Brush_ConstructCone(Brush &brush, const AABB &bounds, std::size_t sides, const char *shader,
168 const TextureProjection &projection)
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";
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";
182 brush.reserve(sides + 1);
184 Vector3 mins(vector3_subtracted(bounds.origin, bounds.extents));
185 Vector3 maxs(vector3_added(bounds.origin, bounds.extents));
187 float radius = max_extent(bounds.extents);
188 const Vector3 &mid = bounds.origin;
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];
201 brush.addPlane(planepts[0], planepts[1], planepts[2], shader, projection);
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);
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];
211 planepts[1][0] = mid[0];
212 planepts[1][1] = mid[1];
213 planepts[1][2] = maxs[2];
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];
219 brush.addPlane(planepts[0], planepts[1], planepts[2], shader, projection);
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";
227 void Brush_ConstructSphere(Brush &brush, const AABB &bounds, std::size_t sides, const char *shader,
228 const TextureProjection &projection)
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";
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";
242 brush.reserve(sides * sides);
244 float radius = max_extent(bounds.extents);
245 const Vector3 &mid = bounds.origin;
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++) {
253 double p = float(j * dp - c_pi / 2);
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));
259 brush.addPlane(planepts[0], planepts[1], planepts[2], shader, projection);
264 double p = (sides - 1) * dp - c_pi / 2;
265 for (std::size_t i = 0; i < sides; i++) {
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));
272 brush.addPlane(planepts[0], planepts[1], planepts[2], shader, projection);
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";
281 void Brush_ConstructRock(Brush &brush, const AABB &bounds, std::size_t sides, const char *shader,
282 const TextureProjection &projection)
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";
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";
296 brush.reserve(sides * sides);
298 float radius = max_extent(bounds.extents);
299 const Vector3 &mid = bounds.origin;
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]);
308 // find two vectors that are perpendicular to planepts[0]
309 ComputeAxisBase(planepts[0], planepts[1], planepts[2]);
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));
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 ) {
320 planepts[1] = planepts[2];
322 globalOutputStream() << "flip\n";
325 globalOutputStream() << "no flip\n";
329 brush.addPlane(planepts[0], planepts[1], planepts[2], shader, projection);
335 switch (GlobalXYWnd_getCurrentViewType()) {
346 void Brush_ConstructPrefab(Brush &brush, EBrushPrefab type, const AABB &bounds, std::size_t sides, const char *shader,
347 const TextureProjection &projection)
351 UndoableCommand undo("brushCuboid");
353 Brush_ConstructCuboid(brush, bounds, shader, projection);
357 int axis = GetViewAxis();
358 StringOutputStream command;
359 command << c_brushPrism_name << " -sides " << Unsigned(sides) << " -axis " << axis;
360 UndoableCommand undo(command.c_str());
362 Brush_ConstructPrism(brush, bounds, sides, axis, shader, projection);
366 StringOutputStream command;
367 command << c_brushCone_name << " -sides " << Unsigned(sides);
368 UndoableCommand undo(command.c_str());
370 Brush_ConstructCone(brush, bounds, sides, shader, projection);
374 StringOutputStream command;
375 command << c_brushSphere_name << " -sides " << Unsigned(sides);
376 UndoableCommand undo(command.c_str());
378 Brush_ConstructSphere(brush, bounds, sides, shader, projection);
382 StringOutputStream command;
383 command << c_brushRock_name << " -sides " << Unsigned(sides);
384 UndoableCommand undo(command.c_str());
386 Brush_ConstructRock(brush, bounds, sides, shader, projection);
393 void ConstructRegionBrushes(scene::Node *brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs)
397 Vector3 mins(region_mins[0] - 32, region_mins[1] - 32, region_mins[2] - 32);
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());
410 Vector3 maxs(region_maxs[0] + 32, region_maxs[1] + 32, region_maxs[2] + 32);
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());
423 void Scene_BrushSetTexdef_Selected(scene::Graph &graph, const TextureProjection &projection)
425 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
426 face.SetTexdef(projection);
431 void Scene_BrushSetTexdef_Component_Selected(scene::Graph &graph, const TextureProjection &projection)
433 Scene_ForEachSelectedBrushFace(graph, [&](Face &face) {
434 face.SetTexdef(projection);
440 void Scene_BrushSetFlags_Selected(scene::Graph &graph, const ContentsFlagsValue &flags)
442 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
443 face.SetFlags(flags);
448 void Scene_BrushSetFlags_Component_Selected(scene::Graph &graph, const ContentsFlagsValue &flags)
450 Scene_ForEachSelectedBrushFace(graph, [&](Face &face) {
451 face.SetFlags(flags);
456 void Scene_BrushShiftTexdef_Selected(scene::Graph &graph, float s, float t)
458 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
459 face.ShiftTexdef(s, t);
464 void Scene_BrushShiftTexdef_Component_Selected(scene::Graph &graph, float s, float t)
466 Scene_ForEachSelectedBrushFace(graph, [&](Face &face) {
467 face.ShiftTexdef(s, t);
472 void Scene_BrushScaleTexdef_Selected(scene::Graph &graph, float s, float t)
474 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
475 face.ScaleTexdef(s, t);
480 void Scene_BrushScaleTexdef_Component_Selected(scene::Graph &graph, float s, float t)
482 Scene_ForEachSelectedBrushFace(graph, [&](Face &face) {
483 face.ScaleTexdef(s, t);
488 void Scene_BrushRotateTexdef_Selected(scene::Graph &graph, float angle)
490 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
491 face.RotateTexdef(angle);
496 void Scene_BrushRotateTexdef_Component_Selected(scene::Graph &graph, float angle)
498 Scene_ForEachSelectedBrushFace(graph, [&](Face &face) {
499 face.RotateTexdef(angle);
505 void Scene_BrushSetShader_Selected(scene::Graph &graph, const char *name)
507 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
508 face.SetShader(name);
513 void Scene_BrushSetShader_Component_Selected(scene::Graph &graph, const char *name)
515 Scene_ForEachSelectedBrushFace(graph, [&](Face &face) {
516 face.SetShader(name);
521 void Scene_BrushSetDetail_Selected(scene::Graph &graph, bool detail)
523 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
524 face.setDetail(detail);
529 bool Face_FindReplaceShader(Face &face, const char *find, const char *replace)
531 if (shader_equal(face.GetShader(), find)) {
532 face.SetShader(replace);
538 bool DoingSearch(const char *repl)
540 return (repl == NULL || (strcmp("textures/", repl) == 0));
543 void Scene_BrushFindReplaceShader(scene::Graph &graph, const char *find, const char *replace)
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);
552 Scene_ForEachBrush_ForEachFace(graph, [&](Face &face) { Face_FindReplaceShader(face, find, replace); });
556 void Scene_BrushFindReplaceShader_Selected(scene::Graph &graph, const char *find, const char *replace)
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);
565 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
566 Face_FindReplaceShader(face, find, replace);
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)
575 if (DoingSearch(replace)) {
578 Scene_ForEachSelectedBrushFace(graph, [&](Face &face) {
579 Face_FindReplaceShader(face, find, replace);
585 void Scene_BrushFitTexture_Selected(scene::Graph &graph, float s_repeat, float t_repeat)
587 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
588 face.FitTexture(s_repeat, t_repeat);
593 void Scene_BrushFitTexture_Component_Selected(scene::Graph &graph, float s_repeat, float t_repeat)
595 Scene_ForEachSelectedBrushFace(graph, [&](Face &face) {
596 face.FitTexture(s_repeat, t_repeat);
601 TextureProjection g_defaultTextureProjection;
603 const TextureProjection &TextureTransform_getDefault()
605 TexDef_Construct_Default(g_defaultTextureProjection);
606 return g_defaultTextureProjection;
609 void Scene_BrushConstructPrefab(scene::Graph &graph, EBrushPrefab type, std::size_t sides, const char *shader)
611 if (GlobalSelectionSystem().countSelected() != 0) {
612 const scene::Path &path = GlobalSelectionSystem().ultimateSelected().path();
614 Brush *brush = Node_getBrush(path.top());
616 AABB bounds = brush->localAABB(); // copy bounds because the brush will be modified
617 Brush_ConstructPrefab(*brush, type, bounds, sides, shader, TextureTransform_getDefault());
623 void Scene_BrushResize_Selected(scene::Graph &graph, const AABB &bounds, const char *shader)
625 if (GlobalSelectionSystem().countSelected() != 0) {
626 const scene::Path &path = GlobalSelectionSystem().ultimateSelected().path();
628 Brush *brush = Node_getBrush(path.top());
630 Brush_ConstructCuboid(*brush, bounds, shader, TextureTransform_getDefault());
636 bool Brush_hasShader(const Brush &brush, const char *name)
638 for (Brush::const_iterator i = brush.begin(); i != brush.end(); ++i) {
639 if (shader_equal((*i)->GetShader(), name)) {
646 class BrushSelectByShaderWalker : public scene::Graph::Walker {
649 BrushSelectByShaderWalker(const char *name)
654 bool pre(const scene::Path &path, scene::Instance &instance) const
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);
666 void Scene_BrushSelectByShader(scene::Graph &graph, const char *name)
668 graph.traverse(BrushSelectByShaderWalker(name));
671 void Scene_BrushSelectByShader_Component(scene::Graph &graph, const char *name)
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);
681 void Scene_BrushGetTexdef_Selected(scene::Graph &graph, TextureProjection &projection)
684 Scene_ForEachSelectedBrush_ForEachFace(graph, [&](Face &face) {
687 face.GetTexdef(projection);
692 void Scene_BrushGetTexdef_Component_Selected(scene::Graph &graph, TextureProjection &projection)
695 if (!g_SelectedFaceInstances.empty()) {
696 FaceInstance &faceInstance = g_SelectedFaceInstances.last();
697 faceInstance.getFace().GetTexdef(projection);
700 FaceGetTexdef visitor( projection );
701 Scene_ForEachSelectedBrushFace( graph, visitor );
705 void Scene_BrushGetShaderSize_Component_Selected(scene::Graph &graph, size_t &width, size_t &height)
707 if (!g_SelectedFaceInstances.empty()) {
708 FaceInstance &faceInstance = g_SelectedFaceInstances.last();
709 width = faceInstance.getFace().getShader().width();
710 height = faceInstance.getFace().getShader().height();
715 void Scene_BrushGetFlags_Selected(scene::Graph &graph, ContentsFlagsValue &flags)
718 if (GlobalSelectionSystem().countSelected() != 0) {
719 BrushInstance *brush = Instance_getBrush(GlobalSelectionSystem().ultimateSelected());
722 Brush_forEachFace(*brush, [&](Face &face) {
725 face.GetFlags(flags);
731 Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetFlags( flags ) );
735 void Scene_BrushGetFlags_Component_Selected(scene::Graph &graph, ContentsFlagsValue &flags)
738 if (!g_SelectedFaceInstances.empty()) {
739 FaceInstance &faceInstance = g_SelectedFaceInstances.last();
740 faceInstance.getFace().GetFlags(flags);
743 Scene_ForEachSelectedBrushFace( graph, FaceGetFlags( flags ) );
748 void Scene_BrushGetShader_Selected(scene::Graph &graph, CopiedString &shader)
751 if (GlobalSelectionSystem().countSelected() != 0) {
752 BrushInstance *brush = Instance_getBrush(GlobalSelectionSystem().ultimateSelected());
755 Brush_forEachFace(*brush, [&](Face &face) {
758 shader = face.GetShader();
764 Scene_ForEachSelectedBrush_ForEachFace( graph, FaceGetShader( shader ) );
768 void Scene_BrushGetShader_Component_Selected(scene::Graph &graph, CopiedString &shader)
771 if (!g_SelectedFaceInstances.empty()) {
772 FaceInstance &faceInstance = g_SelectedFaceInstances.last();
773 shader = faceInstance.getFace().GetShader();
776 FaceGetShader visitor( shader );
777 Scene_ForEachSelectedBrushFace( graph, visitor );
782 class filter_face_shader : public FaceFilter {
783 const char *m_shader;
785 filter_face_shader(const char *shader) : m_shader(shader)
789 bool filter(const Face &face) const
791 return shader_equal(face.GetShader(), m_shader);
795 class filter_face_shader_prefix : public FaceFilter {
796 const char *m_prefix;
798 filter_face_shader_prefix(const char *prefix) : m_prefix(prefix)
802 bool filter(const Face &face) const
804 return shader_equal_n(face.GetShader(), m_prefix, strlen(m_prefix));
808 class filter_face_flags : public FaceFilter {
811 filter_face_flags(int flags) : m_flags(flags)
815 bool filter(const Face &face) const
817 return (face.getShader().shaderFlags() & m_flags) != 0;
821 class filter_face_contents : public FaceFilter {
824 filter_face_contents(int contents) : m_contents(contents)
828 bool filter(const Face &face) const
830 return (face.getShader().m_flags.m_contentFlags & m_contents) != 0;
835 class filter_brush_any_face : public BrushFilter {
836 FaceFilter *m_filter;
838 filter_brush_any_face(FaceFilter *filter) : m_filter(filter)
842 bool filter(const Brush &brush) const
844 bool filtered = false;
845 Brush_forEachFace(brush, [&](Face &face) {
846 if (m_filter->filter(face)) {
854 class filter_brush_all_faces : public BrushFilter {
855 FaceFilter *m_filter;
857 filter_brush_all_faces(FaceFilter *filter) : m_filter(filter)
861 bool filter(const Brush &brush) const
863 bool filtered = true;
864 Brush_forEachFace(brush, [&](Face &face) {
865 if (!m_filter->filter(face)) {
874 filter_face_flags g_filter_face_clip(QER_CLIP);
875 filter_brush_all_faces g_filter_brush_clip(&g_filter_face_clip);
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);
880 filter_face_shader g_filter_face_weapclip("textures/common/weapclip");
881 filter_brush_all_faces g_filter_brush_weapclip(&g_filter_face_weapclip);
883 filter_face_shader g_filter_face_commonclip("textures/common/clip");
884 filter_brush_all_faces g_filter_brush_commonclip(&g_filter_face_commonclip);
886 filter_face_shader g_filter_face_fullclip("textures/common/fullclip");
887 filter_brush_all_faces g_filter_brush_fullclip(&g_filter_face_fullclip);
889 filter_face_shader g_filter_face_botclip("textures/common/botclip");
890 filter_brush_all_faces g_filter_brush_botclip(&g_filter_face_botclip);
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);
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);
898 filter_face_shader_prefix g_filter_face_liquids("textures/liquids/");
899 filter_brush_any_face g_filter_brush_liquids(&g_filter_face_liquids);
901 filter_face_shader g_filter_face_hint("textures/common/hint");
902 filter_brush_any_face g_filter_brush_hint(&g_filter_face_hint);
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);
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);
910 filter_face_shader g_filter_face_areaportal("textures/common/areaportal");
911 filter_brush_all_faces g_filter_brush_areaportal(&g_filter_face_areaportal);
913 filter_face_shader g_filter_face_visportal("textures/editor/visportal");
914 filter_brush_any_face g_filter_brush_visportal(&g_filter_face_visportal);
916 filter_face_shader g_filter_face_clusterportal("textures/common/clusterportal");
917 filter_brush_all_faces g_filter_brush_clusterportal(&g_filter_face_clusterportal);
919 filter_face_shader g_filter_face_lightgrid("textures/common/lightgrid");
920 filter_brush_all_faces g_filter_brush_lightgrid(&g_filter_face_lightgrid);
922 filter_face_flags g_filter_face_translucent(QER_TRANS);
923 filter_brush_all_faces g_filter_brush_translucent(&g_filter_face_translucent);
925 filter_face_contents g_filter_face_detail(BRUSH_DETAIL_MASK);
926 filter_brush_all_faces g_filter_brush_detail(&g_filter_face_detail);
928 filter_face_shader_prefix g_filter_face_decals("textures/decals/");
929 filter_brush_any_face g_filter_brush_decals(&g_filter_face_decals);
932 void BrushFilters_construct()
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);
960 void normalquantisation_draw(){
962 glBegin( GL_POINTS );
963 for ( std::size_t i = 0; i <= c_quantise_normal; ++i )
965 for ( std::size_t j = 0; j <= c_quantise_normal; ++j )
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 )
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 ) );
981 class RenderableNormalQuantisation : public OpenGLRenderable
984 void render( RenderStateFlags state ) const {
985 normalquantisation_draw();
989 const float g_test_quantise_normal = 1.f / static_cast<float>( 1 << 3 );
991 class TestNormalQuantisation
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 );
1001 ASSERT_MESSAGE( normal3f_dot( normal, other ) > 0.99, "bleh" );
1004 void test_normal( const Normal3f& normal ){
1005 Normal3f test = normal3f_from_spherical( spherical_from_normal3f( normal ) );
1006 check_normal( normal, test );
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 );
1013 double scale = static_cast<float>( c_quantise_normal ) / ( folded.x + folded.y + folded.z );
1015 double zbits = folded.z * scale;
1016 double ybits = folded.y * scale;
1018 std::size_t zbits_q = static_cast<std::size_t>( zbits );
1019 std::size_t ybits_q = static_cast<std::size_t>( ybits );
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" );
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" );
1030 Normal3f tmp( c_quantise_normal - zbits_q - ybits_q, ybits_q, zbits_q );
1031 tmp = normal3f_normalised( tmp );
1033 Normal3f unfolded = normal3f_unfold_octant( normal3f_unfold_sextant( tmp, sextant ), octant );
1035 check_normal( normal, unfolded );
1037 double dot = normal3f_dot( normal, unfolded );
1038 float length = VectorLength( normal3f_to_array( unfolded ) );
1039 float inv_length = 1 / length;
1041 Normal3f quantised = normal3f_quantised( normal );
1042 check_normal( normal, quantised );
1044 void test2( const Normal3f& normal, const Normal3f& other ){
1045 if ( normal3f_quantised( normal ) != normal3f_quantised( other ) ) {
1050 static Normal3f normalise( float x, float y, float z ){
1051 return normal3f_normalised( Normal3f( x, y, z ) );
1055 return static_cast<float>( rand() - ( RAND_MAX / 2 ) );
1058 Normal3f normal3f_rand(){
1059 return normalise( vec_rand(), vec_rand(), vec_rand() );
1063 TestNormalQuantisation(){
1064 for ( int i = 4096; i > 0; --i )
1065 test_normal( normal3f_rand() );
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 ) );
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 ) );
1090 test2( normalise( 10000, 10000, 10000 ), normalise( 10000, 10000, 10001 ) );
1091 test2( normalise( 10000, 10000, 10001 ), normalise( 10000, 10001, 10000 ) );
1095 TestNormalQuantisation g_testNormalQuantisation;
1101 class TestSelectableObserver : public observer_template<const Selectable&>
1104 void notify( const Selectable& arguments ){
1105 bool bleh = arguments.isSelected();
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 );
1125 const TestBleh testbleh;
1130 class TestRefcountedString
1133 TestRefcountedString(){
1136 SmartString string1( "string1" );
1137 SmartString string2( string1 );
1138 SmartString string3( string2 );
1141 // refcounted assignment
1142 SmartString string1( "string1" );
1143 SmartString string2( "string2" );
1148 SmartString string1( "string1" );
1149 SmartString string2( "string2" );
1150 string1 = string2.c_str();
1154 SmartString string1( "string1" );
1158 // self-assignment via another reference
1159 SmartString string1( "string1" );
1160 SmartString string2( string1 );
1166 const TestRefcountedString g_testRefcountedString;
1170 void Select_MakeDetail()
1172 UndoableCommand undo("brushSetDetail");
1173 Scene_BrushSetDetail_Selected(GlobalSceneGraph(), true);
1176 void Select_MakeStructural()
1178 UndoableCommand undo("brushClearDetail");
1179 Scene_BrushSetDetail_Selected(GlobalSceneGraph(), false);
1182 class BrushMakeSided {
1183 std::size_t m_count;
1185 BrushMakeSided(std::size_t count)
1192 Scene_BrushConstructPrefab(GlobalSceneGraph(), eBrushPrism, m_count,
1193 TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
1196 typedef MemberCaller<BrushMakeSided, void(), &BrushMakeSided::set> SetCaller;
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);
1208 inline int axis_for_viewtype(int viewtype)
1222 EBrushPrefab m_type;
1224 BrushPrefab(EBrushPrefab type)
1231 DoSides(m_type, axis_for_viewtype(GetViewAxis()));
1234 typedef MemberCaller<BrushPrefab, void(), &BrushPrefab::set> SetCaller;
1237 BrushPrefab g_brushprism(eBrushPrism);
1238 BrushPrefab g_brushcone(eBrushCone);
1239 BrushPrefab g_brushsphere(eBrushSphere);
1240 BrushPrefab g_brushrock(eBrushRock);
1249 void OnClipMode(bool enable);
1257 UndoableCommand undo("clipperClip");
1262 void SplitSelected()
1265 UndoableCommand undo("clipperSplit");
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);
1281 void Texdef_ToggleMoveLock()
1283 g_brush_texturelock_enabled = !g_brush_texturelock_enabled;
1284 g_texdef_movelock_item.update();
1285 g_texture_lock_status_changed();
1289 void Brush_registerCommands()
1291 GlobalToggles_insert("TogTexLock", makeCallbackF(Texdef_ToggleMoveLock),
1292 ToggleItem::AddCallbackCaller(g_texdef_movelock_item),
1293 Accelerator('T', (GdkModifierType) GDK_SHIFT_MASK));
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));
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));
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));
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)));
1327 void Brush_constructMenu(ui::Menu menu)
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);
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);
1339 create_menu_item_with_mnemonic(menu_in_menu, "Make _Hollow", "CSGMakeHollow");
1340 create_menu_item_with_mnemonic(menu_in_menu, "Make _Room", "CSGMakeRoom");
1341 create_menu_item_with_mnemonic(menu_in_menu, "CSG _Subtract", "CSGSubtract");
1342 create_menu_item_with_mnemonic(menu_in_menu, "CSG _Merge", "CSGMerge");
1344 menu_separator(menu);
1346 auto menu_in_menu = create_sub_menu_with_mnemonic(menu, "Clipper");
1347 if (g_Layout_enableDetachableMenus.m_value) {
1348 menu_tearoff(menu_in_menu);
1351 create_menu_item_with_mnemonic(menu_in_menu, "Clip selection", "ClipSelected");
1352 create_menu_item_with_mnemonic(menu_in_menu, "Split selection", "SplitSelected");
1353 create_menu_item_with_mnemonic(menu_in_menu, "Flip Clip orientation", "FlipClip");
1355 menu_separator(menu);
1356 create_menu_item_with_mnemonic(menu, "Make detail", "MakeDetail");
1357 create_menu_item_with_mnemonic(menu, "Make structural", "MakeStructural");
1358 create_menu_item_with_mnemonic(menu, "Snap selection to _grid", "SnapToGrid");
1360 create_check_menu_item_with_mnemonic(menu, "Texture Lock", "TogTexLock");
1361 menu_separator(menu);
1362 create_menu_item_with_mnemonic(menu, "Copy Face Texture", "FaceCopyTexture");
1363 create_menu_item_with_mnemonic(menu, "Paste Face Texture", "FacePasteTexture");
1365 command_connect_accelerator("Brush3Sided");
1366 command_connect_accelerator("Brush4Sided");
1367 command_connect_accelerator("Brush5Sided");
1368 command_connect_accelerator("Brush6Sided");
1369 command_connect_accelerator("Brush7Sided");
1370 command_connect_accelerator("Brush8Sided");
1371 command_connect_accelerator("Brush9Sided");