X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=radiant%2Fselect.cpp;h=70e815d138acc9526ac281fe1cc46c36f8bd79b8;hb=72ce262ae89739330d16052b9548aad5cbe5c89d;hp=36b2e3e86021186f62531d4c6a4d23756c32bf98;hpb=12b372f89ce109a4db9d510884fbe7d05af79870;p=xonotic%2Fnetradiant.git diff --git a/radiant/select.cpp b/radiant/select.cpp index 36b2e3e8..70e815d1 100644 --- a/radiant/select.cpp +++ b/radiant/select.cpp @@ -30,6 +30,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include "stream/stringstream.h" +#include "signal/isignal.h" #include "shaderlib.h" #include "scenelib.h" @@ -37,6 +38,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "gtkutil/dialog.h" #include "gtkutil/widget.h" #include "brushmanip.h" +#include "brush.h" #include "patchmanip.h" #include "patchdialog.h" #include "selection.h" @@ -51,6 +53,172 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA select_workzone_t g_select_workzone; +/** + Loops over all selected brushes and stores their + world AABBs in the specified array. +*/ +class CollectSelectedBrushesBounds : public SelectionSystem::Visitor +{ + AABB* m_bounds; // array of AABBs + Unsigned m_max; // max AABB-elements in array + Unsigned& m_count;// count of valid AABBs stored in array + +public: + CollectSelectedBrushesBounds(AABB* bounds, Unsigned max, Unsigned& count) + : m_bounds(bounds), + m_max(max), + m_count(count) + { + m_count = 0; + } + + void visit(scene::Instance& instance) const + { + ASSERT_MESSAGE(m_count <= m_max, "Invalid m_count in CollectSelectedBrushesBounds"); + + // stop if the array is already full + if(m_count == m_max) + return; + + Selectable* selectable = Instance_getSelectable(instance); + if((selectable != 0) + && instance.isSelected()) + { + // brushes only + if(Instance_getBrush(instance) != 0) + { + m_bounds[m_count] = instance.worldAABB(); + ++m_count; + } + } + } +}; + +/** + Selects all objects that intersect one of the bounding AABBs. + The exact intersection-method is specified through TSelectionPolicy +*/ +template +class SelectByBounds : public scene::Graph::Walker +{ + AABB* m_aabbs; // selection aabbs + Unsigned m_count; // number of aabbs in m_aabbs + TSelectionPolicy policy; // type that contains a custom intersection method aabb<->aabb + +public: + SelectByBounds(AABB* aabbs, Unsigned count) + : m_aabbs(aabbs), + m_count(count) + { + } + + bool pre(const scene::Path& path, scene::Instance& instance) const + { + Selectable* selectable = Instance_getSelectable(instance); + + // ignore worldspawn + Entity* entity = Node_getEntity(path.top()); + if(entity) + { + if(string_equal(entity->getKeyValue("classname"), "worldspawn")) + return true; + } + + if( (path.size() > 1) && + (!path.top().get().isRoot()) && + (selectable != 0) + ) + { + for(Unsigned i = 0; i < m_count; ++i) + { + if(policy.Evaluate(m_aabbs[i], instance)) + { + selectable->setSelected(true); + } + } + } + + return true; + } + + /** + Performs selection operation on the global scenegraph. + If delete_bounds_src is true, then the objects which were + used as source for the selection aabbs will be deleted. +*/ + static void DoSelection(bool delete_bounds_src = true) + { + if(GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive) + { + // we may not need all AABBs since not all selected objects have to be brushes + const Unsigned max = (Unsigned)GlobalSelectionSystem().countSelected(); + AABB* aabbs = new AABB[max]; + + Unsigned count; + CollectSelectedBrushesBounds collector(aabbs, max, count); + GlobalSelectionSystem().foreachSelected(collector); + + // nothing usable in selection + if(!count) + { + delete[] aabbs; + return; + } + + // delete selected objects + if(delete_bounds_src)// see deleteSelection + { + UndoableCommand undo("deleteSelected"); + Select_Delete(); + } + + // select objects with bounds + GlobalSceneGraph().traverse(SelectByBounds(aabbs, count)); + + SceneChangeNotify(); + delete[] aabbs; + } + } +}; + +/** + SelectionPolicy for SelectByBounds + Returns true if box and the AABB of instance intersect +*/ +class SelectionPolicy_Touching +{ +public: + bool Evaluate(const AABB& box, scene::Instance& instance) const + { + const AABB& other(instance.worldAABB()); + for(Unsigned i = 0; i < 3; ++i) + { + if(fabsf(box.origin[i] - other.origin[i]) > (box.extents[i] + other.extents[i])) + return false; + } + return true; + } +}; + +/** + SelectionPolicy for SelectByBounds + Returns true if the AABB of instance is inside box +*/ +class SelectionPolicy_Inside +{ +public: + bool Evaluate(const AABB& box, scene::Instance& instance) const + { + const AABB& other(instance.worldAABB()); + for(Unsigned i = 0; i < 3; ++i) + { + if(fabsf(box.origin[i] - other.origin[i]) > (box.extents[i] - other.extents[i])) + return false; + } + return true; + } +}; + class DeleteSelected : public scene::Graph::Walker { mutable bool m_remove; @@ -72,12 +240,13 @@ public: { m_remove = true; - return false; + return false;// dont traverse into child elements } return true; } void post(const scene::Path& path, scene::Instance& instance) const { + if(m_removedChild) { m_removedChild = false; @@ -92,6 +261,7 @@ public: } } + // node should be removed if(m_remove) { if(Node_isEntity(path.parent()) != 0) @@ -227,7 +397,7 @@ void UpdateWorkzone_ForSelection() } // update the workzone to the current selection -void UpdateWorkzone_ForSelection(const Selectable& selectable) +void UpdateWorkzone_ForSelectionChanged(const Selectable& selectable) { if(selectable.isSelected()) { @@ -574,7 +744,15 @@ void Select_AllOfType() } } +void Select_Inside(void) +{ + SelectByBounds::DoSelection(); +} +void Select_Touching(void) +{ + SelectByBounds::DoSelection(false); +} void Select_FitTexture(float horizontal, float vertical) { @@ -730,16 +908,21 @@ void SceneSelectionChange(const Selectable& selectable) SceneChangeNotify(); } +SignalHandlerId Selection_boundsChanged; + void Selection_construct() { - GlobalSelectionSystem().addSelectionChangeCallback(FreeCaller1()); - GlobalSelectionSystem().addSelectionChangeCallback(FreeCaller1()); - GlobalSceneGraph().addBoundsChangedCallback(FreeCaller()); + typedef FreeCaller1 SceneSelectionChangeCaller; + GlobalSelectionSystem().addSelectionChangeCallback(SceneSelectionChangeCaller()); + typedef FreeCaller1 UpdateWorkzoneForSelectionChangedCaller; + GlobalSelectionSystem().addSelectionChangeCallback(UpdateWorkzoneForSelectionChangedCaller()); + typedef FreeCaller UpdateWorkzoneForSelectionCaller; + Selection_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback(UpdateWorkzoneForSelectionCaller()); } void Selection_destroy() { - GlobalSceneGraph().removeBoundsChangedCallback(FreeCaller()); + GlobalSceneGraph().removeBoundsChangedCallback(Selection_boundsChanged); } @@ -750,8 +933,20 @@ void Selection_destroy() #include #include + inline Quaternion quaternion_for_euler_xyz_degrees(const Vector3& eulerXYZ) { +#if 0 + return quaternion_for_matrix4_rotation(matrix4_rotation_for_euler_xyz_degrees(eulerXYZ)); +#elif 0 + return quaternion_multiplied_by_quaternion( + quaternion_multiplied_by_quaternion( + quaternion_for_z(degrees_to_radians(eulerXYZ[2])), + quaternion_for_y(degrees_to_radians(eulerXYZ[1])) + ), + quaternion_for_x(degrees_to_radians(eulerXYZ[0])) + ); +#elif 1 double cx = cos(degrees_to_radians(eulerXYZ[0] * 0.5)); double sx = sin(degrees_to_radians(eulerXYZ[0] * 0.5)); double cy = cos(degrees_to_radians(eulerXYZ[1] * 0.5)); @@ -760,11 +955,12 @@ inline Quaternion quaternion_for_euler_xyz_degrees(const Vector3& eulerXYZ) double sz = sin(degrees_to_radians(eulerXYZ[2] * 0.5)); return Quaternion( - static_cast(cx * sy * cz - sx * cy * sz), - static_cast(cx * sy * sz + sx * cy * cz), - static_cast(cx * cy * sz - sx * sy * cz), - static_cast(cx * cy * cz + sx * sy * sz) + cz * cy * sx - sz * sy * cx, + cz * sy * cx + sz * cy * sx, + sz * cy * cx - cz * sy * sx, + cz * cy * cx + sz * sy * sx ); +#endif } struct RotateDialog