#include <vector>
#include "stream/stringstream.h"
+#include "signal/isignal.h"
#include "shaderlib.h"
#include "scenelib.h"
#include "gtkutil/dialog.h"
#include "gtkutil/widget.h"
#include "brushmanip.h"
+#include "brush.h"
#include "patchmanip.h"
#include "patchdialog.h"
#include "selection.h"
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 TSelectionPolicy>
+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<TSelectionPolicy>(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;
{
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;
}
}
+ // node should be removed
if(m_remove)
{
if(Node_isEntity(path.parent()) != 0)
}
// update the workzone to the current selection
-void UpdateWorkzone_ForSelection(const Selectable& selectable)
+void UpdateWorkzone_ForSelectionChanged(const Selectable& selectable)
{
if(selectable.isSelected())
{
}
}
+void Select_Inside(void)
+{
+ SelectByBounds<SelectionPolicy_Inside>::DoSelection();
+}
+void Select_Touching(void)
+{
+ SelectByBounds<SelectionPolicy_Touching>::DoSelection(false);
+}
void Select_FitTexture(float horizontal, float vertical)
{
SceneChangeNotify();
}
+SignalHandlerId Selection_boundsChanged;
+
void Selection_construct()
{
- GlobalSelectionSystem().addSelectionChangeCallback(FreeCaller1<const Selectable&, SceneSelectionChange>());
- GlobalSelectionSystem().addSelectionChangeCallback(FreeCaller1<const Selectable&, UpdateWorkzone_ForSelection>());
- GlobalSceneGraph().addBoundsChangedCallback(FreeCaller<UpdateWorkzone_ForSelection>());
+ typedef FreeCaller1<const Selectable&, SceneSelectionChange> SceneSelectionChangeCaller;
+ GlobalSelectionSystem().addSelectionChangeCallback(SceneSelectionChangeCaller());
+ typedef FreeCaller1<const Selectable&, UpdateWorkzone_ForSelectionChanged> UpdateWorkzoneForSelectionChangedCaller;
+ GlobalSelectionSystem().addSelectionChangeCallback(UpdateWorkzoneForSelectionChangedCaller());
+ typedef FreeCaller<UpdateWorkzone_ForSelection> UpdateWorkzoneForSelectionCaller;
+ Selection_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback(UpdateWorkzoneForSelectionCaller());
}
void Selection_destroy()
{
- GlobalSceneGraph().removeBoundsChangedCallback(FreeCaller<UpdateWorkzone_ForSelection>());
+ GlobalSceneGraph().removeBoundsChangedCallback(Selection_boundsChanged);
}
#include <gtk/gtklabel.h>
#include <gdk/gdkkeysyms.h>
+
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));
double sz = sin(degrees_to_radians(eulerXYZ[2] * 0.5));
return Quaternion(
- static_cast<float>(cx * sy * cz - sx * cy * sz),
- static_cast<float>(cx * sy * sz + sx * cy * cz),
- static_cast<float>(cx * cy * sz - sx * sy * cz),
- static_cast<float>(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