2 Copyright (C) 2001-2006, William Joseph.
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 "selection.h"
23 #include "globaldefs.h"
25 #include "debugging/debugging.h"
31 #include "windowobserver.h"
35 #include "renderable.h"
36 #include "selectable.h"
39 #include "math/frustum.h"
40 #include "signal/signal.h"
41 #include "generic/object.h"
42 #include "selectionlib.h"
46 #include "stream/stringstream.h"
47 #include "eclasslib.h"
48 #include "generic/bitfield.h"
49 #include "generic/static.h"
52 #include "container/container.h"
56 TextOutputStream &ostream_write(TextOutputStream &t, const Vector4 &v)
58 return t << "[ " << v.x() << " " << v.y() << " " << v.z() << " " << v.w() << " ]";
61 TextOutputStream &ostream_write(TextOutputStream &t, const Matrix4 &m)
63 return t << "[ " << m.x() << " " << m.y() << " " << m.z() << " " << m.t() << " ]";
68 Matrix4 m_viewpointSpace;
69 Matrix4 m_viewplaneSpace;
70 Vector3 m_axis_screen;
73 update(const Matrix4 &pivot2world, const Matrix4 &modelview, const Matrix4 &projection, const Matrix4 &viewport)
75 Pivot2World_worldSpace(m_worldSpace, pivot2world, modelview, projection, viewport);
76 Pivot2World_viewpointSpace(m_viewpointSpace, m_axis_screen, pivot2world, modelview, projection, viewport);
77 Pivot2World_viewplaneSpace(m_viewplaneSpace, pivot2world, modelview, projection, viewport);
82 void point_for_device_point(Vector3 &point, const Matrix4 &device2object, const float x, const float y, const float z)
84 // transform from normalised device coords to object coords
85 point = vector4_projected(matrix4_transformed_vector4(device2object, Vector4(x, y, z, 1)));
88 void ray_for_device_point(Ray &ray, const Matrix4 &device2object, const float x, const float y)
90 // point at x, y, zNear
91 point_for_device_point(ray.origin, device2object, x, y, -1);
93 // point at x, y, zFar
94 point_for_device_point(ray.direction, device2object, x, y, 1);
97 vector3_subtract(ray.direction, ray.origin);
98 vector3_normalise(ray.direction);
101 bool sphere_intersect_ray(const Vector3 &origin, float radius, const Ray &ray, Vector3 &intersection)
103 intersection = vector3_subtracted(origin, ray.origin);
104 const double a = vector3_dot(intersection, ray.direction);
105 const double d = radius * radius - (vector3_dot(intersection, intersection) - a * a);
108 intersection = vector3_added(ray.origin, vector3_scaled(ray.direction, a - sqrt(d)));
111 intersection = vector3_added(ray.origin, vector3_scaled(ray.direction, a));
116 void ray_intersect_ray(const Ray &ray, const Ray &other, Vector3 &intersection)
118 intersection = vector3_subtracted(ray.origin, other.origin);
119 //float a = 1;//vector3_dot(ray.direction, ray.direction); // always >= 0
120 double dot = vector3_dot(ray.direction, other.direction);
121 //float c = 1;//vector3_dot(other.direction, other.direction); // always >= 0
122 double d = vector3_dot(ray.direction, intersection);
123 double e = vector3_dot(other.direction, intersection);
124 double D = 1 - dot * dot; //a*c - dot*dot; // always >= 0
127 // the lines are almost parallel
128 intersection = vector3_added(other.origin, vector3_scaled(other.direction, e));
130 intersection = vector3_added(other.origin, vector3_scaled(other.direction, (e - dot * d) / D));
134 const Vector3 g_origin(0, 0, 0);
135 const float g_radius = 64;
137 void point_on_sphere(Vector3 &point, const Matrix4 &device2object, const float x, const float y)
140 ray_for_device_point(ray, device2object, x, y);
141 sphere_intersect_ray(g_origin, g_radius, ray, point);
144 void point_on_axis(Vector3 &point, const Vector3 &axis, const Matrix4 &device2object, const float x, const float y)
147 ray_for_device_point(ray, device2object, x, y);
148 ray_intersect_ray(ray, Ray(Vector3(0, 0, 0), axis), point);
151 void point_on_plane(Vector3 &point, const Matrix4 &device2object, const float x, const float y)
153 Matrix4 object2device(matrix4_full_inverse(device2object));
154 point = vector4_projected(
155 matrix4_transformed_vector4(device2object, Vector4(x, y, object2device[14] / object2device[15], 1)));
158 //! a and b are unit vectors .. returns angle in radians
159 inline float angle_between(const Vector3 &a, const Vector3 &b)
161 return static_cast<float>( 2.0 * atan2(
162 vector3_length(vector3_subtracted(a, b)),
163 vector3_length(vector3_added(a, b))
172 test_quat(const Vector3 &from, const Vector3 &to)
174 Vector4 quaternion(quaternion_for_unit_vectors(from, to));
175 Matrix4 matrix(matrix4_rotation_for_quaternion(
176 quaternion_multiplied_by_quaternion(quaternion, c_quaternion_identity)));
182 static test_quat bleh(g_vector3_axis_x, g_vector3_axis_y);
185 //! axis is a unit vector
186 inline void constrain_to_axis(Vector3 &vec, const Vector3 &axis)
188 vec = vector3_normalised(vector3_added(vec, vector3_scaled(axis, -vector3_dot(vec, axis))));
191 //! a and b are unit vectors .. a and b must be orthogonal to axis .. returns angle in radians
192 float angle_for_axis(const Vector3 &a, const Vector3 &b, const Vector3 &axis)
194 if (vector3_dot(axis, vector3_cross(a, b)) > 0.0) {
195 return angle_between(a, b);
197 return -angle_between(a, b);
201 float distance_for_axis(const Vector3 &a, const Vector3 &b, const Vector3 &axis)
203 return static_cast<float>( vector3_dot(b, axis) - vector3_dot(a, axis));
206 class Manipulatable {
208 virtual void Construct(const Matrix4 &device2manip, const float x, const float y) = 0;
210 virtual void Transform(const Matrix4 &manip2object, const Matrix4 &device2manip, const float x, const float y) = 0;
213 void transform_local2object(Matrix4 &object, const Matrix4 &local, const Matrix4 &local2object)
215 object = matrix4_multiplied_by_matrix4(
216 matrix4_multiplied_by_matrix4(local2object, local),
217 matrix4_full_inverse(local2object)
223 virtual ~Rotatable() = default;
225 virtual void rotate(const Quaternion &rotation) = 0;
228 class RotateFree : public Manipulatable {
230 Rotatable &m_rotatable;
232 RotateFree(Rotatable &rotatable)
233 : m_rotatable(rotatable)
237 void Construct(const Matrix4 &device2manip, const float x, const float y)
239 point_on_sphere(m_start, device2manip, x, y);
240 vector3_normalise(m_start);
243 void Transform(const Matrix4 &manip2object, const Matrix4 &device2manip, const float x, const float y)
247 point_on_sphere(current, device2manip, x, y);
248 vector3_normalise(current);
250 m_rotatable.rotate(quaternion_for_unit_vectors(m_start, current));
254 class RotateAxis : public Manipulatable {
257 Rotatable &m_rotatable;
259 RotateAxis(Rotatable &rotatable)
260 : m_rotatable(rotatable)
264 void Construct(const Matrix4 &device2manip, const float x, const float y)
266 point_on_sphere(m_start, device2manip, x, y);
267 constrain_to_axis(m_start, m_axis);
270 /// \brief Converts current position to a normalised vector orthogonal to axis.
271 void Transform(const Matrix4 &manip2object, const Matrix4 &device2manip, const float x, const float y)
274 point_on_sphere(current, device2manip, x, y);
275 constrain_to_axis(current, m_axis);
277 m_rotatable.rotate(quaternion_for_axisangle(m_axis, angle_for_axis(m_start, current, m_axis)));
280 void SetAxis(const Vector3 &axis)
286 void translation_local2object(Vector3 &object, const Vector3 &local, const Matrix4 &local2object)
288 object = matrix4_get_translation_vec3(
289 matrix4_multiplied_by_matrix4(
290 matrix4_translated_by_vec3(local2object, local),
291 matrix4_full_inverse(local2object)
298 virtual ~Translatable() = default;
300 virtual void translate(const Vector3 &translation) = 0;
303 class TranslateAxis : public Manipulatable {
306 Translatable &m_translatable;
308 TranslateAxis(Translatable &translatable)
309 : m_translatable(translatable)
313 void Construct(const Matrix4 &device2manip, const float x, const float y)
315 point_on_axis(m_start, m_axis, device2manip, x, y);
318 void Transform(const Matrix4 &manip2object, const Matrix4 &device2manip, const float x, const float y)
321 point_on_axis(current, m_axis, device2manip, x, y);
322 current = vector3_scaled(m_axis, distance_for_axis(m_start, current, m_axis));
324 translation_local2object(current, current, manip2object);
325 vector3_snap(current, GetSnapGridSize());
327 m_translatable.translate(current);
330 void SetAxis(const Vector3 &axis)
336 class TranslateFree : public Manipulatable {
339 Translatable &m_translatable;
341 TranslateFree(Translatable &translatable)
342 : m_translatable(translatable)
346 void Construct(const Matrix4 &device2manip, const float x, const float y)
348 point_on_plane(m_start, device2manip, x, y);
351 void Transform(const Matrix4 &manip2object, const Matrix4 &device2manip, const float x, const float y)
354 point_on_plane(current, device2manip, x, y);
355 current = vector3_subtracted(current, m_start);
357 translation_local2object(current, current, manip2object);
358 vector3_snap(current, GetSnapGridSize());
360 m_translatable.translate(current);
367 virtual ~Scalable() = default;
369 virtual void scale(const Vector3 &scaling) = 0;
373 class ScaleAxis : public Manipulatable {
377 Scalable &m_scalable;
379 ScaleAxis(Scalable &scalable)
380 : m_scalable(scalable)
384 void Construct(const Matrix4 &device2manip, const float x, const float y)
386 point_on_axis(m_start, m_axis, device2manip, x, y);
389 void Transform(const Matrix4 &manip2object, const Matrix4 &device2manip, const float x, const float y)
392 point_on_axis(current, m_axis, device2manip, x, y);
393 Vector3 delta = vector3_subtracted(current, m_start);
395 translation_local2object(delta, delta, manip2object);
396 vector3_snap(delta, GetSnapGridSize());
398 Vector3 start(vector3_snapped(m_start, GetSnapGridSize()));
400 start[0] == 0 ? 1 : 1 + delta[0] / start[0],
401 start[1] == 0 ? 1 : 1 + delta[1] / start[1],
402 start[2] == 0 ? 1 : 1 + delta[2] / start[2]
404 m_scalable.scale(scale);
407 void SetAxis(const Vector3 &axis)
413 class ScaleFree : public Manipulatable {
416 Scalable &m_scalable;
418 ScaleFree(Scalable &scalable)
419 : m_scalable(scalable)
423 void Construct(const Matrix4 &device2manip, const float x, const float y)
425 point_on_plane(m_start, device2manip, x, y);
428 void Transform(const Matrix4 &manip2object, const Matrix4 &device2manip, const float x, const float y)
431 point_on_plane(current, device2manip, x, y);
432 Vector3 delta = vector3_subtracted(current, m_start);
434 translation_local2object(delta, delta, manip2object);
435 vector3_snap(delta, GetSnapGridSize());
437 Vector3 start(vector3_snapped(m_start, GetSnapGridSize()));
439 start[0] == 0 ? 1 : 1 + delta[0] / start[0],
440 start[1] == 0 ? 1 : 1 + delta[1] / start[1],
441 start[2] == 0 ? 1 : 1 + delta[2] / start[2]
443 m_scalable.scale(scale);
448 class RenderableClippedPrimitive : public OpenGLRenderable {
450 PointVertex m_points[9];
454 std::vector<primitive_t> m_primitives;
458 void render(RenderStateFlags state) const
460 for (std::size_t i = 0; i < m_primitives.size(); ++i) {
461 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(PointVertex), &m_primitives[i].m_points[0].colour);
462 glVertexPointer(3, GL_FLOAT, sizeof(PointVertex), &m_primitives[i].m_points[0].vertex);
463 switch (m_primitives[i].m_count) {
467 glDrawArrays(GL_LINES, 0, GLsizei(m_primitives[i].m_count));
470 glDrawArrays(GL_POLYGON, 0, GLsizei(m_primitives[i].m_count));
476 void construct(const Matrix4 &world2device)
478 m_inverse = matrix4_full_inverse(world2device);
479 m_world = g_matrix4_identity;
482 void insert(const Vector4 clipped[9], std::size_t count)
486 m_primitives.back().m_count = count;
487 for (std::size_t i = 0; i < count; ++i) {
488 Vector3 world_point(vector4_projected(matrix4_transformed_vector4(m_inverse, clipped[i])));
489 m_primitives.back().m_points[i].vertex = vertex3f_for_vector3(world_point);
495 m_primitives.clear();
501 m_primitives.push_back(primitive_t());
503 const Colour4b colour_clipped(255, 127, 0, 255);
505 for (std::size_t i = 0; i < 9; ++i) {
506 m_primitives.back().m_points[i].colour = colour_clipped;
512 #define DEBUG_SELECTION
515 #if defined( DEBUG_SELECTION )
516 Shader *g_state_clipped;
517 RenderableClippedPrimitive g_render_clipped;
522 // dist_Point_to_Line(): get the distance of a point to a line.
523 // Input: a Point P and a Line L (in any dimension)
524 // Return: the shortest distance from P to L
526 dist_Point_to_Line( Point P, Line L ){
527 Vector v = L.P1 - L.P0;
530 double c1 = dot( w,v );
531 double c2 = dot( v,v );
534 Point Pb = L.P0 + b * v;
540 typedef Vector3 point_type;
542 Segment3D(const point_type &_p0, const point_type &_p1)
550 typedef Vector3 Point3D;
552 inline double vector3_distance_squared(const Point3D &a, const Point3D &b)
554 return vector3_length_squared(b - a);
557 // get the distance of a point to a segment.
558 Point3D segment_closest_point_to_point(const Segment3D &segment, const Point3D &point)
560 Vector3 v = segment.p1 - segment.p0;
561 Vector3 w = point - segment.p0;
563 double c1 = vector3_dot(w, v);
568 double c2 = vector3_dot(v, v);
573 return Point3D(segment.p0 + v * (c1 / c2));
576 double segment_dist_to_point_3d(const Segment3D &segment, const Point3D &point)
578 return vector3_distance_squared(point, segment_closest_point_to_point(segment, point));
581 typedef Vector3 point_t;
582 typedef const Vector3 *point_iterator_t;
584 // crossing number test for a point in a polygon
585 // This code is patterned after [Franklin, 2000]
586 bool point_test_polygon_2d(const point_t &P, point_iterator_t start, point_iterator_t finish)
588 std::size_t crossings = 0;
590 // loop through all edges of the polygon
591 for (point_iterator_t prev = finish - 1, cur = start;
592 cur != finish; prev = cur, ++cur) { // edge from (*prev) to (*cur)
593 if ((((*prev)[1] <= P[1]) && ((*cur)[1] > P[1])) // an upward crossing
594 || (((*prev)[1] > P[1]) && ((*cur)[1] <= P[1]))) { // a downward crossing
595 // compute the actual edge-ray intersect x-coordinate
596 float vt = (float) (P[1] - (*prev)[1]) / ((*cur)[1] - (*prev)[1]);
597 if (P[0] < (*prev)[0] + vt * ((*cur)[0] - (*prev)[0])) { // P[0] < intersect
598 ++crossings; // a valid crossing of y=P[1] right of P[0]
602 return (crossings & 0x1) != 0; // 0 if even (out), and 1 if odd (in)
605 inline double triangle_signed_area_XY(const Vector3 &p0, const Vector3 &p1, const Vector3 &p2)
607 return ((p1[0] - p0[0]) * (p2[1] - p0[1])) - ((p2[0] - p0[0]) * (p1[1] - p0[1]));
617 inline SelectionIntersection select_point_from_clipped(Vector4 &clipped)
619 return SelectionIntersection(clipped[2] / clipped[3], static_cast<float>( vector3_length_squared(
620 Vector3(clipped[0] / clipped[3], clipped[1] / clipped[3], 0))));
623 void BestPoint(std::size_t count, Vector4 clipped[9], SelectionIntersection &best, clipcull_t cull)
625 Vector3 normalised[9];
628 for (std::size_t i = 0; i < count; ++i) {
629 normalised[i][0] = clipped[i][0] / clipped[i][3];
630 normalised[i][1] = clipped[i][1] / clipped[i][3];
631 normalised[i][2] = clipped[i][2] / clipped[i][3];
635 if (cull != eClipCullNone && count > 2) {
636 double signed_area = triangle_signed_area_XY(normalised[0], normalised[1], normalised[2]);
638 if ((cull == eClipCullCW && signed_area > 0)
639 || (cull == eClipCullCCW && signed_area < 0)) {
645 Segment3D segment(normalised[0], normalised[1]);
646 Point3D point = segment_closest_point_to_point(segment, Vector3(0, 0, 0));
647 assign_if_closer(best, SelectionIntersection(point.z(), 0));
648 } else if (count > 2 && !point_test_polygon_2d(Vector3(0, 0, 0), normalised, normalised + count)) {
649 point_iterator_t end = normalised + count;
650 for (point_iterator_t previous = end - 1, current = normalised; current != end; previous = current, ++current) {
651 Segment3D segment(*previous, *current);
652 Point3D point = segment_closest_point_to_point(segment, Vector3(0, 0, 0));
653 float depth = point.z();
655 float distance = static_cast<float>( vector3_length_squared(point));
657 assign_if_closer(best, SelectionIntersection(depth, distance));
659 } else if (count > 2) {
662 SelectionIntersection(
663 static_cast<float>( ray_distance_to_plane(
664 Ray(Vector3(0, 0, 0), Vector3(0, 0, 1)),
665 plane3_for_points(normalised[0], normalised[1], normalised[2])
672 #if defined( DEBUG_SELECTION )
674 g_render_clipped.insert(clipped, count);
679 void LineStrip_BestPoint(const Matrix4 &local2view, const PointVertex *vertices, const std::size_t size,
680 SelectionIntersection &best)
683 for (std::size_t i = 0; (i + 1) < size; ++i) {
684 const std::size_t count = matrix4_clip_line(local2view, vertex3f_to_vector3(vertices[i].vertex),
685 vertex3f_to_vector3(vertices[i + 1].vertex), clipped);
686 BestPoint(count, clipped, best, eClipCullNone);
690 void LineLoop_BestPoint(const Matrix4 &local2view, const PointVertex *vertices, const std::size_t size,
691 SelectionIntersection &best)
694 for (std::size_t i = 0; i < size; ++i) {
695 const std::size_t count = matrix4_clip_line(local2view, vertex3f_to_vector3(vertices[i].vertex),
696 vertex3f_to_vector3(vertices[(i + 1) % size].vertex), clipped);
697 BestPoint(count, clipped, best, eClipCullNone);
701 void Line_BestPoint(const Matrix4 &local2view, const PointVertex vertices[2], SelectionIntersection &best)
704 const std::size_t count = matrix4_clip_line(local2view, vertex3f_to_vector3(vertices[0].vertex),
705 vertex3f_to_vector3(vertices[1].vertex), clipped);
706 BestPoint(count, clipped, best, eClipCullNone);
709 void Circle_BestPoint(const Matrix4 &local2view, clipcull_t cull, const PointVertex *vertices, const std::size_t size,
710 SelectionIntersection &best)
713 for (std::size_t i = 0; i < size; ++i) {
714 const std::size_t count = matrix4_clip_triangle(local2view, g_vector3_identity,
715 vertex3f_to_vector3(vertices[i].vertex),
716 vertex3f_to_vector3(vertices[(i + 1) % size].vertex), clipped);
717 BestPoint(count, clipped, best, cull);
722 Quad_BestPoint(const Matrix4 &local2view, clipcull_t cull, const PointVertex *vertices, SelectionIntersection &best)
726 const std::size_t count = matrix4_clip_triangle(local2view, vertex3f_to_vector3(vertices[0].vertex),
727 vertex3f_to_vector3(vertices[1].vertex),
728 vertex3f_to_vector3(vertices[3].vertex), clipped);
729 BestPoint(count, clipped, best, cull);
732 const std::size_t count = matrix4_clip_triangle(local2view, vertex3f_to_vector3(vertices[1].vertex),
733 vertex3f_to_vector3(vertices[2].vertex),
734 vertex3f_to_vector3(vertices[3].vertex), clipped);
735 BestPoint(count, clipped, best, cull);
739 struct FlatShadedVertex {
750 typedef FlatShadedVertex *FlatShadedVertexIterator;
752 void Triangles_BestPoint(const Matrix4 &local2view, clipcull_t cull, FlatShadedVertexIterator first,
753 FlatShadedVertexIterator last, SelectionIntersection &best)
755 for (FlatShadedVertexIterator x(first), y(first + 1), z(first + 2); x != last; x += 3, y += 3, z += 3) {
758 matrix4_clip_triangle(
760 reinterpret_cast<const Vector3 &>((*x).vertex ),
761 reinterpret_cast<const Vector3 &>((*y).vertex ),
762 reinterpret_cast<const Vector3 &>((*z).vertex ),
773 typedef std::multimap<SelectionIntersection, Selectable *> SelectableSortedSet;
775 class SelectionPool : public Selector {
776 SelectableSortedSet m_pool;
777 SelectionIntersection m_intersection;
778 Selectable *m_selectable;
781 void pushSelectable(Selectable &selectable)
783 m_intersection = SelectionIntersection();
784 m_selectable = &selectable;
789 addSelectable(m_intersection, m_selectable);
790 m_intersection = SelectionIntersection();
793 void addIntersection(const SelectionIntersection &intersection)
795 assign_if_closer(m_intersection, intersection);
798 void addSelectable(const SelectionIntersection &intersection, Selectable *selectable)
800 if (intersection.valid()) {
801 m_pool.insert(SelectableSortedSet::value_type(intersection, selectable));
805 typedef SelectableSortedSet::iterator iterator;
809 return m_pool.begin();
819 return m_pool.empty();
824 const Colour4b g_colour_sphere(0, 0, 0, 255);
825 const Colour4b g_colour_screen(0, 255, 255, 255);
826 const Colour4b g_colour_selected(255, 255, 0, 255);
828 inline const Colour4b &colourSelected(const Colour4b &colour, bool selected)
830 return (selected) ? g_colour_selected : colour;
833 template<typename remap_policy>
834 inline void draw_semicircle(const std::size_t segments, const float radius, PointVertex *vertices, remap_policy remap)
836 const double increment = c_pi / double(segments << 2);
838 std::size_t count = 0;
841 remap_policy::set(vertices[segments << 2].vertex, -radius, 0, 0);
842 while (count < segments) {
843 PointVertex *i = vertices + count;
844 PointVertex *j = vertices + ((segments << 1) - (count + 1));
846 PointVertex *k = i + (segments << 1);
847 PointVertex *l = j + (segments << 1);
850 PointVertex* m = i + ( segments << 2 );
851 PointVertex* n = j + ( segments << 2 );
852 PointVertex* o = k + ( segments << 2 );
853 PointVertex* p = l + ( segments << 2 );
856 remap_policy::set(i->vertex, x, -y, 0);
857 remap_policy::set(k->vertex, -y, -x, 0);
859 remap_policy::set( m->vertex,-x, y, 0 );
860 remap_policy::set( o->vertex, y, x, 0 );
866 const double theta = increment * count;
867 x = static_cast<float>( radius * cos(theta));
868 y = static_cast<float>( radius * sin(theta));
871 remap_policy::set(j->vertex, y, -x, 0);
872 remap_policy::set(l->vertex, -x, -y, 0);
874 remap_policy::set( n->vertex,-y, x, 0 );
875 remap_policy::set( p->vertex, x, y, 0 );
882 virtual Manipulatable *GetManipulatable() = 0;
884 virtual void testSelect(const View &view, const Matrix4 &pivot2world)
888 virtual void render(Renderer &renderer, const VolumeTest &volume, const Matrix4 &pivot2world)
892 virtual void setSelected(bool select) = 0;
894 virtual bool isSelected() const = 0;
898 inline Vector3 normalised_safe(const Vector3 &self)
900 if (vector3_equal(self, g_vector3_identity)) {
901 return g_vector3_identity;
903 return vector3_normalised(self);
907 class RotateManipulator : public Manipulator {
908 struct RenderableCircle : public OpenGLRenderable {
909 Array<PointVertex> m_vertices;
911 RenderableCircle(std::size_t size) : m_vertices(size)
915 void render(RenderStateFlags state) const
917 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(PointVertex), &m_vertices.data()->colour);
918 glVertexPointer(3, GL_FLOAT, sizeof(PointVertex), &m_vertices.data()->vertex);
919 glDrawArrays(GL_LINE_LOOP, 0, GLsizei(m_vertices.size()));
922 void setColour(const Colour4b &colour)
924 for (Array<PointVertex>::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i) {
925 (*i).colour = colour;
930 struct RenderableSemiCircle : public OpenGLRenderable {
931 Array<PointVertex> m_vertices;
933 RenderableSemiCircle(std::size_t size) : m_vertices(size)
937 void render(RenderStateFlags state) const
939 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(PointVertex), &m_vertices.data()->colour);
940 glVertexPointer(3, GL_FLOAT, sizeof(PointVertex), &m_vertices.data()->vertex);
941 glDrawArrays(GL_LINE_STRIP, 0, GLsizei(m_vertices.size()));
944 void setColour(const Colour4b &colour)
946 for (Array<PointVertex>::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i) {
947 (*i).colour = colour;
954 Vector3 m_axis_screen;
955 RenderableSemiCircle m_circle_x;
956 RenderableSemiCircle m_circle_y;
957 RenderableSemiCircle m_circle_z;
958 RenderableCircle m_circle_screen;
959 RenderableCircle m_circle_sphere;
960 SelectableBool m_selectable_x;
961 SelectableBool m_selectable_y;
962 SelectableBool m_selectable_z;
963 SelectableBool m_selectable_screen;
964 SelectableBool m_selectable_sphere;
966 Matrix4 m_local2world_x;
967 Matrix4 m_local2world_y;
968 Matrix4 m_local2world_z;
969 bool m_circle_x_visible;
970 bool m_circle_y_visible;
971 bool m_circle_z_visible;
973 static Shader *m_state_outer;
975 RotateManipulator(Rotatable &rotatable, std::size_t segments, float radius) :
978 m_circle_x((segments << 2) + 1),
979 m_circle_y((segments << 2) + 1),
980 m_circle_z((segments << 2) + 1),
981 m_circle_screen(segments << 3),
982 m_circle_sphere(segments << 3)
984 draw_semicircle(segments, radius, m_circle_x.m_vertices.data(), RemapYZX());
985 draw_semicircle(segments, radius, m_circle_y.m_vertices.data(), RemapZXY());
986 draw_semicircle(segments, radius, m_circle_z.m_vertices.data(), RemapXYZ());
988 draw_circle(segments, radius * 1.15f, m_circle_screen.m_vertices.data(), RemapXYZ());
989 draw_circle(segments, radius, m_circle_sphere.m_vertices.data(), RemapXYZ());
995 m_circle_x.setColour(colourSelected(g_colour_x, m_selectable_x.isSelected()));
996 m_circle_y.setColour(colourSelected(g_colour_y, m_selectable_y.isSelected()));
997 m_circle_z.setColour(colourSelected(g_colour_z, m_selectable_z.isSelected()));
998 m_circle_screen.setColour(colourSelected(g_colour_screen, m_selectable_screen.isSelected()));
999 m_circle_sphere.setColour(colourSelected(g_colour_sphere, false));
1002 void updateCircleTransforms()
1004 Vector3 localViewpoint(matrix4_transformed_direction(matrix4_transposed(m_pivot.m_worldSpace),
1005 vector4_to_vector3(m_pivot.m_viewpointSpace.z())));
1007 m_circle_x_visible = !vector3_equal_epsilon(g_vector3_axis_x, localViewpoint, 1e-6f);
1008 if (m_circle_x_visible) {
1009 m_local2world_x = g_matrix4_identity;
1010 vector4_to_vector3(m_local2world_x.y()) = normalised_safe(
1011 vector3_cross(g_vector3_axis_x, localViewpoint)
1013 vector4_to_vector3(m_local2world_x.z()) = normalised_safe(
1014 vector3_cross(vector4_to_vector3(m_local2world_x.x()), vector4_to_vector3(m_local2world_x.y()))
1016 matrix4_premultiply_by_matrix4(m_local2world_x, m_pivot.m_worldSpace);
1019 m_circle_y_visible = !vector3_equal_epsilon(g_vector3_axis_y, localViewpoint, 1e-6f);
1020 if (m_circle_y_visible) {
1021 m_local2world_y = g_matrix4_identity;
1022 vector4_to_vector3(m_local2world_y.z()) = normalised_safe(
1023 vector3_cross(g_vector3_axis_y, localViewpoint)
1025 vector4_to_vector3(m_local2world_y.x()) = normalised_safe(
1026 vector3_cross(vector4_to_vector3(m_local2world_y.y()), vector4_to_vector3(m_local2world_y.z()))
1028 matrix4_premultiply_by_matrix4(m_local2world_y, m_pivot.m_worldSpace);
1031 m_circle_z_visible = !vector3_equal_epsilon(g_vector3_axis_z, localViewpoint, 1e-6f);
1032 if (m_circle_z_visible) {
1033 m_local2world_z = g_matrix4_identity;
1034 vector4_to_vector3(m_local2world_z.x()) = normalised_safe(
1035 vector3_cross(g_vector3_axis_z, localViewpoint)
1037 vector4_to_vector3(m_local2world_z.y()) = normalised_safe(
1038 vector3_cross(vector4_to_vector3(m_local2world_z.z()), vector4_to_vector3(m_local2world_z.x()))
1040 matrix4_premultiply_by_matrix4(m_local2world_z, m_pivot.m_worldSpace);
1044 void render(Renderer &renderer, const VolumeTest &volume, const Matrix4 &pivot2world)
1046 m_pivot.update(pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport());
1047 updateCircleTransforms();
1052 renderer.SetState(m_state_outer, Renderer::eWireframeOnly);
1053 renderer.SetState(m_state_outer, Renderer::eFullMaterials);
1055 renderer.addRenderable(m_circle_screen, m_pivot.m_viewpointSpace);
1056 renderer.addRenderable(m_circle_sphere, m_pivot.m_viewpointSpace);
1058 if (m_circle_x_visible) {
1059 renderer.addRenderable(m_circle_x, m_local2world_x);
1061 if (m_circle_y_visible) {
1062 renderer.addRenderable(m_circle_y, m_local2world_y);
1064 if (m_circle_z_visible) {
1065 renderer.addRenderable(m_circle_z, m_local2world_z);
1069 void testSelect(const View &view, const Matrix4 &pivot2world)
1071 m_pivot.update(pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport());
1072 updateCircleTransforms();
1074 SelectionPool selector;
1078 Matrix4 local2view(matrix4_multiplied_by_matrix4(view.GetViewMatrix(), m_local2world_x));
1080 #if defined( DEBUG_SELECTION )
1081 g_render_clipped.construct(view.GetViewMatrix());
1084 SelectionIntersection best;
1085 LineStrip_BestPoint(local2view, m_circle_x.m_vertices.data(), m_circle_x.m_vertices.size(), best);
1086 selector.addSelectable(best, &m_selectable_x);
1090 Matrix4 local2view(matrix4_multiplied_by_matrix4(view.GetViewMatrix(), m_local2world_y));
1092 #if defined( DEBUG_SELECTION )
1093 g_render_clipped.construct(view.GetViewMatrix());
1096 SelectionIntersection best;
1097 LineStrip_BestPoint(local2view, m_circle_y.m_vertices.data(), m_circle_y.m_vertices.size(), best);
1098 selector.addSelectable(best, &m_selectable_y);
1102 Matrix4 local2view(matrix4_multiplied_by_matrix4(view.GetViewMatrix(), m_local2world_z));
1104 #if defined( DEBUG_SELECTION )
1105 g_render_clipped.construct(view.GetViewMatrix());
1108 SelectionIntersection best;
1109 LineStrip_BestPoint(local2view, m_circle_z.m_vertices.data(), m_circle_z.m_vertices.size(), best);
1110 selector.addSelectable(best, &m_selectable_z);
1115 Matrix4 local2view(matrix4_multiplied_by_matrix4(view.GetViewMatrix(), m_pivot.m_viewpointSpace));
1118 SelectionIntersection best;
1119 LineLoop_BestPoint(local2view, m_circle_screen.m_vertices.data(), m_circle_screen.m_vertices.size(),
1121 selector.addSelectable(best, &m_selectable_screen);
1125 SelectionIntersection best;
1126 Circle_BestPoint(local2view, eClipCullCW, m_circle_sphere.m_vertices.data(),
1127 m_circle_sphere.m_vertices.size(), best);
1128 selector.addSelectable(best, &m_selectable_sphere);
1132 m_axis_screen = m_pivot.m_axis_screen;
1134 if (!selector.failed()) {
1135 (*selector.begin()).second->setSelected(true);
1139 Manipulatable *GetManipulatable()
1141 if (m_selectable_x.isSelected()) {
1142 m_axis.SetAxis(g_vector3_axis_x);
1144 } else if (m_selectable_y.isSelected()) {
1145 m_axis.SetAxis(g_vector3_axis_y);
1147 } else if (m_selectable_z.isSelected()) {
1148 m_axis.SetAxis(g_vector3_axis_z);
1150 } else if (m_selectable_screen.isSelected()) {
1151 m_axis.SetAxis(m_axis_screen);
1158 void setSelected(bool select)
1160 m_selectable_x.setSelected(select);
1161 m_selectable_y.setSelected(select);
1162 m_selectable_z.setSelected(select);
1163 m_selectable_screen.setSelected(select);
1166 bool isSelected() const
1168 return m_selectable_x.isSelected()
1169 | m_selectable_y.isSelected()
1170 | m_selectable_z.isSelected()
1171 | m_selectable_screen.isSelected()
1172 | m_selectable_sphere.isSelected();
1176 Shader *RotateManipulator::m_state_outer;
1179 const float arrowhead_length = 16;
1180 const float arrowhead_radius = 4;
1182 inline void draw_arrowline(const float length, PointVertex *line, const std::size_t axis)
1184 (*line++).vertex = vertex3f_identity;
1185 (*line).vertex = vertex3f_identity;
1186 vertex3f_to_array((*line).vertex)[axis] = length - arrowhead_length;
1189 template<typename VertexRemap, typename NormalRemap>
1191 draw_arrowhead(const std::size_t segments, const float length, FlatShadedVertex *vertices, VertexRemap, NormalRemap)
1193 std::size_t head_tris = (segments << 3);
1194 const double head_segment = c_2pi / head_tris;
1195 for (std::size_t i = 0; i < head_tris; ++i) {
1197 FlatShadedVertex &point = vertices[i * 6 + 0];
1198 VertexRemap::x(point.vertex) = length - arrowhead_length;
1199 VertexRemap::y(point.vertex) = arrowhead_radius * static_cast<float>( cos(i * head_segment));
1200 VertexRemap::z(point.vertex) = arrowhead_radius * static_cast<float>( sin(i * head_segment));
1201 NormalRemap::x(point.normal) = arrowhead_radius / arrowhead_length;
1202 NormalRemap::y(point.normal) = static_cast<float>( cos(i * head_segment));
1203 NormalRemap::z(point.normal) = static_cast<float>( sin(i * head_segment));
1206 FlatShadedVertex &point = vertices[i * 6 + 1];
1207 VertexRemap::x(point.vertex) = length;
1208 VertexRemap::y(point.vertex) = 0;
1209 VertexRemap::z(point.vertex) = 0;
1210 NormalRemap::x(point.normal) = arrowhead_radius / arrowhead_length;
1211 NormalRemap::y(point.normal) = static_cast<float>( cos((i + 0.5) * head_segment));
1212 NormalRemap::z(point.normal) = static_cast<float>( sin((i + 0.5) * head_segment));
1215 FlatShadedVertex &point = vertices[i * 6 + 2];
1216 VertexRemap::x(point.vertex) = length - arrowhead_length;
1217 VertexRemap::y(point.vertex) = arrowhead_radius * static_cast<float>( cos((i + 1) * head_segment));
1218 VertexRemap::z(point.vertex) = arrowhead_radius * static_cast<float>( sin((i + 1) * head_segment));
1219 NormalRemap::x(point.normal) = arrowhead_radius / arrowhead_length;
1220 NormalRemap::y(point.normal) = static_cast<float>( cos((i + 1) * head_segment));
1221 NormalRemap::z(point.normal) = static_cast<float>( sin((i + 1) * head_segment));
1225 FlatShadedVertex &point = vertices[i * 6 + 3];
1226 VertexRemap::x(point.vertex) = length - arrowhead_length;
1227 VertexRemap::y(point.vertex) = 0;
1228 VertexRemap::z(point.vertex) = 0;
1229 NormalRemap::x(point.normal) = -1;
1230 NormalRemap::y(point.normal) = 0;
1231 NormalRemap::z(point.normal) = 0;
1234 FlatShadedVertex &point = vertices[i * 6 + 4];
1235 VertexRemap::x(point.vertex) = length - arrowhead_length;
1236 VertexRemap::y(point.vertex) = arrowhead_radius * static_cast<float>( cos(i * head_segment));
1237 VertexRemap::z(point.vertex) = arrowhead_radius * static_cast<float>( sin(i * head_segment));
1238 NormalRemap::x(point.normal) = -1;
1239 NormalRemap::y(point.normal) = 0;
1240 NormalRemap::z(point.normal) = 0;
1243 FlatShadedVertex &point = vertices[i * 6 + 5];
1244 VertexRemap::x(point.vertex) = length - arrowhead_length;
1245 VertexRemap::y(point.vertex) = arrowhead_radius * static_cast<float>( cos((i + 1) * head_segment));
1246 VertexRemap::z(point.vertex) = arrowhead_radius * static_cast<float>( sin((i + 1) * head_segment));
1247 NormalRemap::x(point.normal) = -1;
1248 NormalRemap::y(point.normal) = 0;
1249 NormalRemap::z(point.normal) = 0;
1254 template<typename Triple>
1255 class TripleRemapXYZ {
1257 static float &x(Triple &triple)
1262 static float &y(Triple &triple)
1267 static float &z(Triple &triple)
1273 template<typename Triple>
1274 class TripleRemapYZX {
1276 static float &x(Triple &triple)
1281 static float &y(Triple &triple)
1286 static float &z(Triple &triple)
1292 template<typename Triple>
1293 class TripleRemapZXY {
1295 static float &x(Triple &triple)
1300 static float &y(Triple &triple)
1305 static float &z(Triple &triple)
1311 void vector3_print(const Vector3 &v)
1313 globalOutputStream() << "( " << v.x() << " " << v.y() << " " << v.z() << " )";
1316 class TranslateManipulator : public Manipulator {
1317 struct RenderableArrowLine : public OpenGLRenderable {
1318 PointVertex m_line[2];
1320 RenderableArrowLine()
1324 void render(RenderStateFlags state) const
1326 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(PointVertex), &m_line[0].colour);
1327 glVertexPointer(3, GL_FLOAT, sizeof(PointVertex), &m_line[0].vertex);
1328 glDrawArrays(GL_LINES, 0, 2);
1331 void setColour(const Colour4b &colour)
1333 m_line[0].colour = colour;
1334 m_line[1].colour = colour;
1338 struct RenderableArrowHead : public OpenGLRenderable {
1339 Array<FlatShadedVertex> m_vertices;
1341 RenderableArrowHead(std::size_t size)
1346 void render(RenderStateFlags state) const
1348 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(FlatShadedVertex), &m_vertices.data()->colour);
1349 glVertexPointer(3, GL_FLOAT, sizeof(FlatShadedVertex), &m_vertices.data()->vertex);
1350 glNormalPointer(GL_FLOAT, sizeof(FlatShadedVertex), &m_vertices.data()->normal);
1351 glDrawArrays(GL_TRIANGLES, 0, GLsizei(m_vertices.size()));
1354 void setColour(const Colour4b &colour)
1356 for (Array<FlatShadedVertex>::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i) {
1357 (*i).colour = colour;
1362 struct RenderableQuad : public OpenGLRenderable {
1363 PointVertex m_quad[4];
1365 void render(RenderStateFlags state) const
1367 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(PointVertex), &m_quad[0].colour);
1368 glVertexPointer(3, GL_FLOAT, sizeof(PointVertex), &m_quad[0].vertex);
1369 glDrawArrays(GL_LINE_LOOP, 0, 4);
1372 void setColour(const Colour4b &colour)
1374 m_quad[0].colour = colour;
1375 m_quad[1].colour = colour;
1376 m_quad[2].colour = colour;
1377 m_quad[3].colour = colour;
1381 TranslateFree m_free;
1382 TranslateAxis m_axis;
1383 RenderableArrowLine m_arrow_x;
1384 RenderableArrowLine m_arrow_y;
1385 RenderableArrowLine m_arrow_z;
1386 RenderableArrowHead m_arrow_head_x;
1387 RenderableArrowHead m_arrow_head_y;
1388 RenderableArrowHead m_arrow_head_z;
1389 RenderableQuad m_quad_screen;
1390 SelectableBool m_selectable_x;
1391 SelectableBool m_selectable_y;
1392 SelectableBool m_selectable_z;
1393 SelectableBool m_selectable_screen;
1394 Pivot2World m_pivot;
1396 static Shader *m_state_wire;
1397 static Shader *m_state_fill;
1399 TranslateManipulator(Translatable &translatable, std::size_t segments, float length) :
1400 m_free(translatable),
1401 m_axis(translatable),
1402 m_arrow_head_x(3 * 2 * (segments << 3)),
1403 m_arrow_head_y(3 * 2 * (segments << 3)),
1404 m_arrow_head_z(3 * 2 * (segments << 3))
1406 draw_arrowline(length, m_arrow_x.m_line, 0);
1407 draw_arrowhead(segments, length, m_arrow_head_x.m_vertices.data(), TripleRemapXYZ<Vertex3f>(),
1408 TripleRemapXYZ<Normal3f>());
1409 draw_arrowline(length, m_arrow_y.m_line, 1);
1410 draw_arrowhead(segments, length, m_arrow_head_y.m_vertices.data(), TripleRemapYZX<Vertex3f>(),
1411 TripleRemapYZX<Normal3f>());
1412 draw_arrowline(length, m_arrow_z.m_line, 2);
1413 draw_arrowhead(segments, length, m_arrow_head_z.m_vertices.data(), TripleRemapZXY<Vertex3f>(),
1414 TripleRemapZXY<Normal3f>());
1416 draw_quad(16, m_quad_screen.m_quad);
1419 void UpdateColours()
1421 m_arrow_x.setColour(colourSelected(g_colour_x, m_selectable_x.isSelected()));
1422 m_arrow_head_x.setColour(colourSelected(g_colour_x, m_selectable_x.isSelected()));
1423 m_arrow_y.setColour(colourSelected(g_colour_y, m_selectable_y.isSelected()));
1424 m_arrow_head_y.setColour(colourSelected(g_colour_y, m_selectable_y.isSelected()));
1425 m_arrow_z.setColour(colourSelected(g_colour_z, m_selectable_z.isSelected()));
1426 m_arrow_head_z.setColour(colourSelected(g_colour_z, m_selectable_z.isSelected()));
1427 m_quad_screen.setColour(colourSelected(g_colour_screen, m_selectable_screen.isSelected()));
1430 bool manipulator_show_axis(const Pivot2World &pivot, const Vector3 &axis)
1432 return fabs(vector3_dot(pivot.m_axis_screen, axis)) < 0.95;
1435 void render(Renderer &renderer, const VolumeTest &volume, const Matrix4 &pivot2world)
1437 m_pivot.update(pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport());
1442 Vector3 x = vector3_normalised(vector4_to_vector3(m_pivot.m_worldSpace.x()));
1443 bool show_x = manipulator_show_axis(m_pivot, x);
1445 Vector3 y = vector3_normalised(vector4_to_vector3(m_pivot.m_worldSpace.y()));
1446 bool show_y = manipulator_show_axis(m_pivot, y);
1448 Vector3 z = vector3_normalised(vector4_to_vector3(m_pivot.m_worldSpace.z()));
1449 bool show_z = manipulator_show_axis(m_pivot, z);
1451 renderer.SetState(m_state_wire, Renderer::eWireframeOnly);
1452 renderer.SetState(m_state_wire, Renderer::eFullMaterials);
1455 renderer.addRenderable(m_arrow_x, m_pivot.m_worldSpace);
1458 renderer.addRenderable(m_arrow_y, m_pivot.m_worldSpace);
1461 renderer.addRenderable(m_arrow_z, m_pivot.m_worldSpace);
1464 renderer.addRenderable(m_quad_screen, m_pivot.m_viewplaneSpace);
1466 renderer.SetState(m_state_fill, Renderer::eWireframeOnly);
1467 renderer.SetState(m_state_fill, Renderer::eFullMaterials);
1470 renderer.addRenderable(m_arrow_head_x, m_pivot.m_worldSpace);
1473 renderer.addRenderable(m_arrow_head_y, m_pivot.m_worldSpace);
1476 renderer.addRenderable(m_arrow_head_z, m_pivot.m_worldSpace);
1480 void testSelect(const View &view, const Matrix4 &pivot2world)
1482 m_pivot.update(pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport());
1484 SelectionPool selector;
1486 Vector3 x = vector3_normalised(vector4_to_vector3(m_pivot.m_worldSpace.x()));
1487 bool show_x = manipulator_show_axis(m_pivot, x);
1489 Vector3 y = vector3_normalised(vector4_to_vector3(m_pivot.m_worldSpace.y()));
1490 bool show_y = manipulator_show_axis(m_pivot, y);
1492 Vector3 z = vector3_normalised(vector4_to_vector3(m_pivot.m_worldSpace.z()));
1493 bool show_z = manipulator_show_axis(m_pivot, z);
1496 Matrix4 local2view(matrix4_multiplied_by_matrix4(view.GetViewMatrix(), m_pivot.m_viewpointSpace));
1499 SelectionIntersection best;
1500 Quad_BestPoint(local2view, eClipCullCW, m_quad_screen.m_quad, best);
1502 best = SelectionIntersection(0, 0);
1503 selector.addSelectable(best, &m_selectable_screen);
1509 Matrix4 local2view(matrix4_multiplied_by_matrix4(view.GetViewMatrix(), m_pivot.m_worldSpace));
1511 #if defined( DEBUG_SELECTION )
1512 g_render_clipped.construct(view.GetViewMatrix());
1516 SelectionIntersection best;
1517 Line_BestPoint(local2view, m_arrow_x.m_line, best);
1518 Triangles_BestPoint(local2view, eClipCullCW, m_arrow_head_x.m_vertices.begin(),
1519 m_arrow_head_x.m_vertices.end(), best);
1520 selector.addSelectable(best, &m_selectable_x);
1524 SelectionIntersection best;
1525 Line_BestPoint(local2view, m_arrow_y.m_line, best);
1526 Triangles_BestPoint(local2view, eClipCullCW, m_arrow_head_y.m_vertices.begin(),
1527 m_arrow_head_y.m_vertices.end(), best);
1528 selector.addSelectable(best, &m_selectable_y);
1532 SelectionIntersection best;
1533 Line_BestPoint(local2view, m_arrow_z.m_line, best);
1534 Triangles_BestPoint(local2view, eClipCullCW, m_arrow_head_z.m_vertices.begin(),
1535 m_arrow_head_z.m_vertices.end(), best);
1536 selector.addSelectable(best, &m_selectable_z);
1540 if (!selector.failed()) {
1541 (*selector.begin()).second->setSelected(true);
1545 Manipulatable *GetManipulatable()
1547 if (m_selectable_x.isSelected()) {
1548 m_axis.SetAxis(g_vector3_axis_x);
1550 } else if (m_selectable_y.isSelected()) {
1551 m_axis.SetAxis(g_vector3_axis_y);
1553 } else if (m_selectable_z.isSelected()) {
1554 m_axis.SetAxis(g_vector3_axis_z);
1561 void setSelected(bool select)
1563 m_selectable_x.setSelected(select);
1564 m_selectable_y.setSelected(select);
1565 m_selectable_z.setSelected(select);
1566 m_selectable_screen.setSelected(select);
1569 bool isSelected() const
1571 return m_selectable_x.isSelected()
1572 | m_selectable_y.isSelected()
1573 | m_selectable_z.isSelected()
1574 | m_selectable_screen.isSelected();
1578 Shader *TranslateManipulator::m_state_wire;
1579 Shader *TranslateManipulator::m_state_fill;
1581 class ScaleManipulator : public Manipulator {
1582 struct RenderableArrow : public OpenGLRenderable {
1583 PointVertex m_line[2];
1585 void render(RenderStateFlags state) const
1587 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(PointVertex), &m_line[0].colour);
1588 glVertexPointer(3, GL_FLOAT, sizeof(PointVertex), &m_line[0].vertex);
1589 glDrawArrays(GL_LINES, 0, 2);
1592 void setColour(const Colour4b &colour)
1594 m_line[0].colour = colour;
1595 m_line[1].colour = colour;
1599 struct RenderableQuad : public OpenGLRenderable {
1600 PointVertex m_quad[4];
1602 void render(RenderStateFlags state) const
1604 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(PointVertex), &m_quad[0].colour);
1605 glVertexPointer(3, GL_FLOAT, sizeof(PointVertex), &m_quad[0].vertex);
1606 glDrawArrays(GL_QUADS, 0, 4);
1609 void setColour(const Colour4b &colour)
1611 m_quad[0].colour = colour;
1612 m_quad[1].colour = colour;
1613 m_quad[2].colour = colour;
1614 m_quad[3].colour = colour;
1620 RenderableArrow m_arrow_x;
1621 RenderableArrow m_arrow_y;
1622 RenderableArrow m_arrow_z;
1623 RenderableQuad m_quad_screen;
1624 SelectableBool m_selectable_x;
1625 SelectableBool m_selectable_y;
1626 SelectableBool m_selectable_z;
1627 SelectableBool m_selectable_screen;
1628 Pivot2World m_pivot;
1630 ScaleManipulator(Scalable &scalable, std::size_t segments, float length) :
1634 draw_arrowline(length, m_arrow_x.m_line, 0);
1635 draw_arrowline(length, m_arrow_y.m_line, 1);
1636 draw_arrowline(length, m_arrow_z.m_line, 2);
1638 draw_quad(16, m_quad_screen.m_quad);
1641 Pivot2World &getPivot()
1646 void UpdateColours()
1648 m_arrow_x.setColour(colourSelected(g_colour_x, m_selectable_x.isSelected()));
1649 m_arrow_y.setColour(colourSelected(g_colour_y, m_selectable_y.isSelected()));
1650 m_arrow_z.setColour(colourSelected(g_colour_z, m_selectable_z.isSelected()));
1651 m_quad_screen.setColour(colourSelected(g_colour_screen, m_selectable_screen.isSelected()));
1654 void render(Renderer &renderer, const VolumeTest &volume, const Matrix4 &pivot2world)
1656 m_pivot.update(pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport());
1661 renderer.addRenderable(m_arrow_x, m_pivot.m_worldSpace);
1662 renderer.addRenderable(m_arrow_y, m_pivot.m_worldSpace);
1663 renderer.addRenderable(m_arrow_z, m_pivot.m_worldSpace);
1665 renderer.addRenderable(m_quad_screen, m_pivot.m_viewpointSpace);
1668 void testSelect(const View &view, const Matrix4 &pivot2world)
1670 m_pivot.update(pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport());
1672 SelectionPool selector;
1675 Matrix4 local2view(matrix4_multiplied_by_matrix4(view.GetViewMatrix(), m_pivot.m_worldSpace));
1677 #if defined( DEBUG_SELECTION )
1678 g_render_clipped.construct(view.GetViewMatrix());
1682 SelectionIntersection best;
1683 Line_BestPoint(local2view, m_arrow_x.m_line, best);
1684 selector.addSelectable(best, &m_selectable_x);
1688 SelectionIntersection best;
1689 Line_BestPoint(local2view, m_arrow_y.m_line, best);
1690 selector.addSelectable(best, &m_selectable_y);
1694 SelectionIntersection best;
1695 Line_BestPoint(local2view, m_arrow_z.m_line, best);
1696 selector.addSelectable(best, &m_selectable_z);
1701 Matrix4 local2view(matrix4_multiplied_by_matrix4(view.GetViewMatrix(), m_pivot.m_viewpointSpace));
1704 SelectionIntersection best;
1705 Quad_BestPoint(local2view, eClipCullCW, m_quad_screen.m_quad, best);
1706 selector.addSelectable(best, &m_selectable_screen);
1710 if (!selector.failed()) {
1711 (*selector.begin()).second->setSelected(true);
1715 Manipulatable *GetManipulatable()
1717 if (m_selectable_x.isSelected()) {
1718 m_axis.SetAxis(g_vector3_axis_x);
1720 } else if (m_selectable_y.isSelected()) {
1721 m_axis.SetAxis(g_vector3_axis_y);
1723 } else if (m_selectable_z.isSelected()) {
1724 m_axis.SetAxis(g_vector3_axis_z);
1731 void setSelected(bool select)
1733 m_selectable_x.setSelected(select);
1734 m_selectable_y.setSelected(select);
1735 m_selectable_z.setSelected(select);
1736 m_selectable_screen.setSelected(select);
1739 bool isSelected() const
1741 return m_selectable_x.isSelected()
1742 | m_selectable_y.isSelected()
1743 | m_selectable_z.isSelected()
1744 | m_selectable_screen.isSelected();
1749 inline PlaneSelectable *Instance_getPlaneSelectable(scene::Instance &instance)
1751 return InstanceTypeCast<PlaneSelectable>::cast(instance);
1754 class PlaneSelectableSelectPlanes : public scene::Graph::Walker {
1755 Selector &m_selector;
1756 SelectionTest &m_test;
1757 PlaneCallback m_selectedPlaneCallback;
1759 PlaneSelectableSelectPlanes(Selector &selector, SelectionTest &test, const PlaneCallback &selectedPlaneCallback)
1760 : m_selector(selector), m_test(test), m_selectedPlaneCallback(selectedPlaneCallback)
1764 bool pre(const scene::Path &path, scene::Instance &instance) const
1766 if (path.top().get().visible()) {
1767 Selectable *selectable = Instance_getSelectable(instance);
1768 if (selectable != 0 && selectable->isSelected()) {
1769 PlaneSelectable *planeSelectable = Instance_getPlaneSelectable(instance);
1770 if (planeSelectable != 0) {
1771 planeSelectable->selectPlanes(m_selector, m_test, m_selectedPlaneCallback);
1779 class PlaneSelectableSelectReversedPlanes : public scene::Graph::Walker {
1780 Selector &m_selector;
1781 const SelectedPlanes &m_selectedPlanes;
1783 PlaneSelectableSelectReversedPlanes(Selector &selector, const SelectedPlanes &selectedPlanes)
1784 : m_selector(selector), m_selectedPlanes(selectedPlanes)
1788 bool pre(const scene::Path &path, scene::Instance &instance) const
1790 if (path.top().get().visible()) {
1791 Selectable *selectable = Instance_getSelectable(instance);
1792 if (selectable != 0 && selectable->isSelected()) {
1793 PlaneSelectable *planeSelectable = Instance_getPlaneSelectable(instance);
1794 if (planeSelectable != 0) {
1795 planeSelectable->selectReversedPlanes(m_selector, m_selectedPlanes);
1803 void Scene_forEachPlaneSelectable_selectPlanes(scene::Graph &graph, Selector &selector, SelectionTest &test,
1804 const PlaneCallback &selectedPlaneCallback)
1806 graph.traverse(PlaneSelectableSelectPlanes(selector, test, selectedPlaneCallback));
1809 void Scene_forEachPlaneSelectable_selectReversedPlanes(scene::Graph &graph, Selector &selector,
1810 const SelectedPlanes &selectedPlanes)
1812 graph.traverse(PlaneSelectableSelectReversedPlanes(selector, selectedPlanes));
1818 bool operator()(const Plane3 &plane, const Plane3 &other) const
1820 if (plane.a < other.a) {
1823 if (other.a < plane.a) {
1827 if (plane.b < other.b) {
1830 if (other.b < plane.b) {
1834 if (plane.c < other.c) {
1837 if (other.c < plane.c) {
1841 if (plane.d < other.d) {
1844 if (other.d < plane.d) {
1852 typedef std::set<Plane3, PlaneLess> PlaneSet;
1854 inline void PlaneSet_insert(PlaneSet &self, const Plane3 &plane)
1859 inline bool PlaneSet_contains(const PlaneSet &self, const Plane3 &plane)
1861 return self.find(plane) != self.end();
1865 class SelectedPlaneSet : public SelectedPlanes {
1866 PlaneSet m_selectedPlanes;
1870 return m_selectedPlanes.empty();
1873 void insert(const Plane3 &plane)
1875 PlaneSet_insert(m_selectedPlanes, plane);
1878 bool contains(const Plane3 &plane) const
1880 return PlaneSet_contains(m_selectedPlanes, plane);
1883 typedef MemberCaller<SelectedPlaneSet, void(const Plane3 &), &SelectedPlaneSet::insert> InsertCaller;
1887 bool Scene_forEachPlaneSelectable_selectPlanes(scene::Graph &graph, Selector &selector, SelectionTest &test)
1889 SelectedPlaneSet selectedPlanes;
1891 Scene_forEachPlaneSelectable_selectPlanes(graph, selector, test, SelectedPlaneSet::InsertCaller(selectedPlanes));
1892 Scene_forEachPlaneSelectable_selectReversedPlanes(graph, selector, selectedPlanes);
1894 return !selectedPlanes.empty();
1897 void Scene_Translate_Component_Selected(scene::Graph &graph, const Vector3 &translation);
1899 void Scene_Translate_Selected(scene::Graph &graph, const Vector3 &translation);
1901 void Scene_TestSelect_Primitive(Selector &selector, SelectionTest &test, const VolumeTest &volume);
1903 void Scene_TestSelect_Component(Selector &selector, SelectionTest &test, const VolumeTest &volume,
1904 SelectionSystem::EComponentMode componentMode);
1906 void Scene_TestSelect_Component_Selected(Selector &selector, SelectionTest &test, const VolumeTest &volume,
1907 SelectionSystem::EComponentMode componentMode);
1909 void Scene_SelectAll_Component(bool select, SelectionSystem::EComponentMode componentMode);
1911 class ResizeTranslatable : public Translatable {
1912 void translate(const Vector3 &translation)
1914 Scene_Translate_Component_Selected(GlobalSceneGraph(), translation);
1918 class DragTranslatable : public Translatable {
1919 void translate(const Vector3 &translation)
1921 if (GlobalSelectionSystem().Mode() == SelectionSystem::eComponent) {
1922 Scene_Translate_Component_Selected(GlobalSceneGraph(), translation);
1924 Scene_Translate_Selected(GlobalSceneGraph(), translation);
1929 class SelectionVolume : public SelectionTest {
1930 Matrix4 m_local2view;
1936 SelectionVolume(const View &view)
1941 const VolumeTest &getVolume() const
1946 const Vector3 &getNear() const
1951 const Vector3 &getFar() const
1956 void BeginMesh(const Matrix4 &localToWorld, bool twoSided)
1958 m_local2view = matrix4_multiplied_by_matrix4(m_view.GetViewMatrix(), localToWorld);
1960 // Cull back-facing polygons based on winding being clockwise or counter-clockwise.
1961 // Don't cull if the view is wireframe and the polygons are two-sided.
1962 m_cull = twoSided && !m_view.fill() ? eClipCullNone : (matrix4_handedness(localToWorld) == MATRIX4_RIGHTHANDED)
1963 ? eClipCullCW : eClipCullCCW;
1966 Matrix4 screen2world(matrix4_full_inverse(m_local2view));
1968 m_near = vector4_projected(
1969 matrix4_transformed_vector4(
1971 Vector4(0, 0, -1, 1)
1975 m_far = vector4_projected(
1976 matrix4_transformed_vector4(
1983 #if defined( DEBUG_SELECTION )
1984 g_render_clipped.construct(m_view.GetViewMatrix());
1988 void TestPoint(const Vector3 &point, SelectionIntersection &best)
1991 if (matrix4_clip_point(m_local2view, point, clipped) == c_CLIP_PASS) {
1992 best = select_point_from_clipped(clipped);
1996 void TestPolygon(const VertexPointer &vertices, std::size_t count, SelectionIntersection &best)
1999 for (std::size_t i = 0; i + 2 < count; ++i) {
2001 matrix4_clip_triangle(
2003 reinterpret_cast<const Vector3 &>( vertices[0] ),
2004 reinterpret_cast<const Vector3 &>( vertices[i + 1] ),
2005 reinterpret_cast<const Vector3 &>( vertices[i + 2] ),
2015 void TestLineLoop(const VertexPointer &vertices, std::size_t count, SelectionIntersection &best)
2021 for (VertexPointer::iterator i = vertices.begin(), end = i + count, prev = i + (count - 1);
2022 i != end; prev = i, ++i) {
2026 reinterpret_cast<const Vector3 &>((*prev)),
2027 reinterpret_cast<const Vector3 &>((*i)),
2037 void TestLineStrip(const VertexPointer &vertices, std::size_t count, SelectionIntersection &best)
2043 for (VertexPointer::iterator i = vertices.begin(), end = i + count, next = i + 1;
2044 next != end; i = next, ++next) {
2048 reinterpret_cast<const Vector3 &>((*i)),
2049 reinterpret_cast<const Vector3 &>((*next)),
2059 void TestLines(const VertexPointer &vertices, std::size_t count, SelectionIntersection &best)
2065 for (VertexPointer::iterator i = vertices.begin(), end = i + count; i != end; i += 2) {
2069 reinterpret_cast<const Vector3 &>((*i)),
2070 reinterpret_cast<const Vector3 &>((*(i + 1))),
2080 void TestTriangles(const VertexPointer &vertices, const IndexPointer &indices, SelectionIntersection &best)
2083 for (IndexPointer::iterator i(indices.begin()); i != indices.end(); i += 3) {
2085 matrix4_clip_triangle(
2087 reinterpret_cast<const Vector3 &>( vertices[*i] ),
2088 reinterpret_cast<const Vector3 &>( vertices[*(i + 1)] ),
2089 reinterpret_cast<const Vector3 &>( vertices[*(i + 2)] ),
2099 void TestQuads(const VertexPointer &vertices, const IndexPointer &indices, SelectionIntersection &best)
2102 for (IndexPointer::iterator i(indices.begin()); i != indices.end(); i += 4) {
2104 matrix4_clip_triangle(
2106 reinterpret_cast<const Vector3 &>( vertices[*i] ),
2107 reinterpret_cast<const Vector3 &>( vertices[*(i + 1)] ),
2108 reinterpret_cast<const Vector3 &>( vertices[*(i + 3)] ),
2116 matrix4_clip_triangle(
2118 reinterpret_cast<const Vector3 &>( vertices[*(i + 1)] ),
2119 reinterpret_cast<const Vector3 &>( vertices[*(i + 2)] ),
2120 reinterpret_cast<const Vector3 &>( vertices[*(i + 3)] ),
2130 void TestQuadStrip(const VertexPointer &vertices, const IndexPointer &indices, SelectionIntersection &best)
2133 for (IndexPointer::iterator i(indices.begin()); i + 2 != indices.end(); i += 2) {
2135 matrix4_clip_triangle(
2137 reinterpret_cast<const Vector3 &>( vertices[*i] ),
2138 reinterpret_cast<const Vector3 &>( vertices[*(i + 1)] ),
2139 reinterpret_cast<const Vector3 &>( vertices[*(i + 2)] ),
2147 matrix4_clip_triangle(
2149 reinterpret_cast<const Vector3 &>( vertices[*(i + 2)] ),
2150 reinterpret_cast<const Vector3 &>( vertices[*(i + 1)] ),
2151 reinterpret_cast<const Vector3 &>( vertices[*(i + 3)] ),
2162 class SelectionCounter {
2164 using func = void(const Selectable &);
2166 SelectionCounter(const SelectionChangeCallback &onchanged)
2167 : m_count(0), m_onchanged(onchanged)
2171 void operator()(const Selectable &selectable)
2173 if (selectable.isSelected()) {
2176 ASSERT_MESSAGE(m_count != 0, "selection counter underflow");
2180 m_onchanged(selectable);
2185 return m_count == 0;
2188 std::size_t size() const
2194 std::size_t m_count;
2195 SelectionChangeCallback m_onchanged;
2198 inline void ConstructSelectionTest(View &view, const rect_t selection_box)
2200 view.EnableScissor(selection_box.min[0], selection_box.max[0], selection_box.min[1], selection_box.max[1]);
2203 inline const rect_t SelectionBoxForPoint(const float device_point[2], const float device_epsilon[2])
2205 rect_t selection_box;
2206 selection_box.min[0] = device_point[0] - device_epsilon[0];
2207 selection_box.min[1] = device_point[1] - device_epsilon[1];
2208 selection_box.max[0] = device_point[0] + device_epsilon[0];
2209 selection_box.max[1] = device_point[1] + device_epsilon[1];
2210 return selection_box;
2213 inline const rect_t SelectionBoxForArea(const float device_point[2], const float device_delta[2])
2215 rect_t selection_box;
2216 selection_box.min[0] = (device_delta[0] < 0) ? (device_point[0] + device_delta[0]) : (device_point[0]);
2217 selection_box.min[1] = (device_delta[1] < 0) ? (device_point[1] + device_delta[1]) : (device_point[1]);
2218 selection_box.max[0] = (device_delta[0] > 0) ? (device_point[0] + device_delta[0]) : (device_point[0]);
2219 selection_box.max[1] = (device_delta[1] > 0) ? (device_point[1] + device_delta[1]) : (device_point[1]);
2220 return selection_box;
2223 Quaternion construct_local_rotation(const Quaternion &world, const Quaternion &localToWorld)
2225 return quaternion_normalised(quaternion_multiplied_by_quaternion(
2226 quaternion_normalised(quaternion_multiplied_by_quaternion(
2227 quaternion_inverse(localToWorld),
2234 inline void matrix4_assign_rotation(Matrix4 &matrix, const Matrix4 &other)
2236 matrix[0] = other[0];
2237 matrix[1] = other[1];
2238 matrix[2] = other[2];
2239 matrix[4] = other[4];
2240 matrix[5] = other[5];
2241 matrix[6] = other[6];
2242 matrix[8] = other[8];
2243 matrix[9] = other[9];
2244 matrix[10] = other[10];
2247 void matrix4_assign_rotation_for_pivot(Matrix4 &matrix, scene::Instance &instance)
2249 Editable *editable = Node_getEditable(instance.path().top());
2250 if (editable != 0) {
2251 matrix4_assign_rotation(matrix,
2252 matrix4_multiplied_by_matrix4(instance.localToWorld(), editable->getLocalPivot()));
2254 matrix4_assign_rotation(matrix, instance.localToWorld());
2258 inline bool Instance_isSelectedComponents(scene::Instance &instance)
2260 ComponentSelectionTestable *componentSelectionTestable = Instance_getComponentSelectionTestable(instance);
2261 return componentSelectionTestable != 0
2262 && componentSelectionTestable->isSelectedComponents();
2265 class TranslateSelected : public SelectionSystem::Visitor {
2266 const Vector3 &m_translate;
2268 TranslateSelected(const Vector3 &translate)
2269 : m_translate(translate)
2273 void visit(scene::Instance &instance) const
2275 Transformable *transform = Instance_getTransformable(instance);
2276 if (transform != 0) {
2277 transform->setType(TRANSFORM_PRIMITIVE);
2278 transform->setTranslation(m_translate);
2283 void Scene_Translate_Selected(scene::Graph &graph, const Vector3 &translation)
2285 if (GlobalSelectionSystem().countSelected() != 0) {
2286 GlobalSelectionSystem().foreachSelected(TranslateSelected(translation));
2290 Vector3 get_local_pivot(const Vector3 &world_pivot, const Matrix4 &localToWorld)
2293 matrix4_transformed_point(
2294 matrix4_full_inverse(localToWorld),
2300 void translation_for_pivoted_matrix_transform(Vector3 &parent_translation, const Matrix4 &local_transform,
2301 const Vector3 &world_pivot, const Matrix4 &localToWorld,
2302 const Matrix4 &localToParent)
2304 // we need a translation inside the parent system to move the origin of this object to the right place
2306 // mathematically, it must fulfill:
2308 // local_translation local_transform local_pivot = local_pivot
2309 // local_translation = local_pivot - local_transform local_pivot
2312 // local_transform local_translation local_pivot = local_pivot
2313 // local_translation local_pivot = local_transform^-1 local_pivot
2314 // local_translation + local_pivot = local_transform^-1 local_pivot
2315 // local_translation = local_transform^-1 local_pivot - local_pivot
2317 Vector3 local_pivot(get_local_pivot(world_pivot, localToWorld));
2319 Vector3 local_translation(
2322 matrix4_transformed_point(
2327 matrix4_transformed_point(
2328 matrix4_full_inverse(local_transform),
2336 translation_local2object(parent_translation, local_translation, localToParent);
2340 globalOutputStream() << "World pivot is at " << world_pivot << "\n";
2341 globalOutputStream() << "Local pivot is at " << local_pivot << "\n";
2342 globalOutputStream() << "Transformation " << local_transform << " moves it to: " << matrix4_transformed_point(local_transform, local_pivot) << "\n";
2343 globalOutputStream() << "Must move by " << local_translation << " in the local system" << "\n";
2344 globalOutputStream() << "Must move by " << parent_translation << " in the parent system" << "\n";
2348 void translation_for_pivoted_rotation(Vector3 &parent_translation, const Quaternion &local_rotation,
2349 const Vector3 &world_pivot, const Matrix4 &localToWorld,
2350 const Matrix4 &localToParent)
2352 translation_for_pivoted_matrix_transform(parent_translation,
2353 matrix4_rotation_for_quaternion_quantised(local_rotation), world_pivot,
2354 localToWorld, localToParent);
2357 void translation_for_pivoted_scale(Vector3 &parent_translation, const Vector3 &world_scale, const Vector3 &world_pivot,
2358 const Matrix4 &localToWorld, const Matrix4 &localToParent)
2360 Matrix4 local_transform(
2361 matrix4_multiplied_by_matrix4(
2362 matrix4_full_inverse(localToWorld),
2363 matrix4_multiplied_by_matrix4(
2364 matrix4_scale_for_vec3(world_scale),
2369 local_transform.tx() = local_transform.ty() = local_transform.tz() = 0; // cancel translation parts
2370 translation_for_pivoted_matrix_transform(parent_translation, local_transform, world_pivot, localToWorld,
2374 class rotate_selected : public SelectionSystem::Visitor {
2375 const Quaternion &m_rotate;
2376 const Vector3 &m_world_pivot;
2378 rotate_selected(const Quaternion &rotation, const Vector3 &world_pivot)
2379 : m_rotate(rotation), m_world_pivot(world_pivot)
2383 void visit(scene::Instance &instance) const
2385 TransformNode *transformNode = Node_getTransformNode(instance.path().top());
2386 if (transformNode != 0) {
2387 Transformable *transform = Instance_getTransformable(instance);
2388 if (transform != 0) {
2389 transform->setType(TRANSFORM_PRIMITIVE);
2390 transform->setScale(c_scale_identity);
2391 transform->setTranslation(c_translation_identity);
2393 transform->setType(TRANSFORM_PRIMITIVE);
2394 transform->setRotation(m_rotate);
2397 Editable *editable = Node_getEditable(instance.path().top());
2398 const Matrix4 &localPivot = editable != 0 ? editable->getLocalPivot() : g_matrix4_identity;
2400 Vector3 parent_translation;
2401 translation_for_pivoted_rotation(
2405 matrix4_multiplied_by_matrix4(instance.localToWorld(), localPivot),
2406 matrix4_multiplied_by_matrix4(transformNode->localToParent(), localPivot)
2409 transform->setTranslation(parent_translation);
2416 void Scene_Rotate_Selected(scene::Graph &graph, const Quaternion &rotation, const Vector3 &world_pivot)
2418 if (GlobalSelectionSystem().countSelected() != 0) {
2419 GlobalSelectionSystem().foreachSelected(rotate_selected(rotation, world_pivot));
2423 class scale_selected : public SelectionSystem::Visitor {
2424 const Vector3 &m_scale;
2425 const Vector3 &m_world_pivot;
2427 scale_selected(const Vector3 &scaling, const Vector3 &world_pivot)
2428 : m_scale(scaling), m_world_pivot(world_pivot)
2432 void visit(scene::Instance &instance) const
2434 TransformNode *transformNode = Node_getTransformNode(instance.path().top());
2435 if (transformNode != 0) {
2436 Transformable *transform = Instance_getTransformable(instance);
2437 if (transform != 0) {
2438 transform->setType(TRANSFORM_PRIMITIVE);
2439 transform->setScale(c_scale_identity);
2440 transform->setTranslation(c_translation_identity);
2442 transform->setType(TRANSFORM_PRIMITIVE);
2443 transform->setScale(m_scale);
2445 Editable *editable = Node_getEditable(instance.path().top());
2446 const Matrix4 &localPivot = editable != 0 ? editable->getLocalPivot() : g_matrix4_identity;
2448 Vector3 parent_translation;
2449 translation_for_pivoted_scale(
2453 matrix4_multiplied_by_matrix4(instance.localToWorld(), localPivot),
2454 matrix4_multiplied_by_matrix4(transformNode->localToParent(), localPivot)
2457 transform->setTranslation(parent_translation);
2464 void Scene_Scale_Selected(scene::Graph &graph, const Vector3 &scaling, const Vector3 &world_pivot)
2466 if (GlobalSelectionSystem().countSelected() != 0) {
2467 GlobalSelectionSystem().foreachSelected(scale_selected(scaling, world_pivot));
2472 class translate_component_selected : public SelectionSystem::Visitor {
2473 const Vector3 &m_translate;
2475 translate_component_selected(const Vector3 &translate)
2476 : m_translate(translate)
2480 void visit(scene::Instance &instance) const
2482 Transformable *transform = Instance_getTransformable(instance);
2483 if (transform != 0) {
2484 transform->setType(TRANSFORM_COMPONENT);
2485 transform->setTranslation(m_translate);
2490 void Scene_Translate_Component_Selected(scene::Graph &graph, const Vector3 &translation)
2492 if (GlobalSelectionSystem().countSelected() != 0) {
2493 GlobalSelectionSystem().foreachSelectedComponent(translate_component_selected(translation));
2497 class rotate_component_selected : public SelectionSystem::Visitor {
2498 const Quaternion &m_rotate;
2499 const Vector3 &m_world_pivot;
2501 rotate_component_selected(const Quaternion &rotation, const Vector3 &world_pivot)
2502 : m_rotate(rotation), m_world_pivot(world_pivot)
2506 void visit(scene::Instance &instance) const
2508 Transformable *transform = Instance_getTransformable(instance);
2509 if (transform != 0) {
2510 Vector3 parent_translation;
2511 translation_for_pivoted_rotation(parent_translation, m_rotate, m_world_pivot, instance.localToWorld(),
2512 Node_getTransformNode(instance.path().top())->localToParent());
2514 transform->setType(TRANSFORM_COMPONENT);
2515 transform->setRotation(m_rotate);
2516 transform->setTranslation(parent_translation);
2521 void Scene_Rotate_Component_Selected(scene::Graph &graph, const Quaternion &rotation, const Vector3 &world_pivot)
2523 if (GlobalSelectionSystem().countSelectedComponents() != 0) {
2524 GlobalSelectionSystem().foreachSelectedComponent(rotate_component_selected(rotation, world_pivot));
2528 class scale_component_selected : public SelectionSystem::Visitor {
2529 const Vector3 &m_scale;
2530 const Vector3 &m_world_pivot;
2532 scale_component_selected(const Vector3 &scaling, const Vector3 &world_pivot)
2533 : m_scale(scaling), m_world_pivot(world_pivot)
2537 void visit(scene::Instance &instance) const
2539 Transformable *transform = Instance_getTransformable(instance);
2540 if (transform != 0) {
2541 Vector3 parent_translation;
2542 translation_for_pivoted_scale(parent_translation, m_scale, m_world_pivot, instance.localToWorld(),
2543 Node_getTransformNode(instance.path().top())->localToParent());
2545 transform->setType(TRANSFORM_COMPONENT);
2546 transform->setScale(m_scale);
2547 transform->setTranslation(parent_translation);
2552 void Scene_Scale_Component_Selected(scene::Graph &graph, const Vector3 &scaling, const Vector3 &world_pivot)
2554 if (GlobalSelectionSystem().countSelectedComponents() != 0) {
2555 GlobalSelectionSystem().foreachSelectedComponent(scale_component_selected(scaling, world_pivot));
2560 class BooleanSelector : public Selector {
2562 SelectionIntersection m_intersection;
2563 Selectable *m_selectable;
2565 BooleanSelector() : m_selected(false)
2569 void pushSelectable(Selectable &selectable)
2571 m_intersection = SelectionIntersection();
2572 m_selectable = &selectable;
2575 void popSelectable()
2577 if (m_intersection.valid()) {
2580 m_intersection = SelectionIntersection();
2583 void addIntersection(const SelectionIntersection &intersection)
2585 if (m_selectable->isSelected()) {
2586 assign_if_closer(m_intersection, intersection);
2596 class BestSelector : public Selector {
2597 SelectionIntersection m_intersection;
2598 Selectable *m_selectable;
2599 SelectionIntersection m_bestIntersection;
2600 std::list<Selectable *> m_bestSelectable;
2602 BestSelector() : m_bestIntersection(SelectionIntersection()), m_bestSelectable(0)
2606 void pushSelectable(Selectable &selectable)
2608 m_intersection = SelectionIntersection();
2609 m_selectable = &selectable;
2612 void popSelectable()
2614 if (m_intersection.equalEpsilon(m_bestIntersection, 0.25f, 0.001f)) {
2615 m_bestSelectable.push_back(m_selectable);
2616 m_bestIntersection = m_intersection;
2617 } else if (m_intersection < m_bestIntersection) {
2618 m_bestSelectable.clear();
2619 m_bestSelectable.push_back(m_selectable);
2620 m_bestIntersection = m_intersection;
2622 m_intersection = SelectionIntersection();
2625 void addIntersection(const SelectionIntersection &intersection)
2627 assign_if_closer(m_intersection, intersection);
2630 std::list<Selectable *> &best()
2632 return m_bestSelectable;
2636 class DragManipulator : public Manipulator {
2637 TranslateFree m_freeResize;
2638 TranslateFree m_freeDrag;
2639 ResizeTranslatable m_resize;
2640 DragTranslatable m_drag;
2641 SelectableBool m_dragSelectable;
2646 DragManipulator() : m_freeResize(m_resize), m_freeDrag(m_drag), m_selected(false)
2650 Manipulatable *GetManipulatable()
2652 return m_dragSelectable.isSelected() ? &m_freeDrag : &m_freeResize;
2655 void testSelect(const View &view, const Matrix4 &pivot2world)
2657 SelectionPool selector;
2659 SelectionVolume test(view);
2661 if (GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive) {
2662 BooleanSelector booleanSelector;
2664 Scene_TestSelect_Primitive(booleanSelector, test, view);
2666 if (booleanSelector.isSelected()) {
2667 selector.addSelectable(SelectionIntersection(0, 0), &m_dragSelectable);
2670 m_selected = Scene_forEachPlaneSelectable_selectPlanes(GlobalSceneGraph(), selector, test);
2673 BestSelector bestSelector;
2674 Scene_TestSelect_Component_Selected(bestSelector, test, view, GlobalSelectionSystem().ComponentMode());
2675 for (std::list<Selectable *>::iterator i = bestSelector.best().begin();
2676 i != bestSelector.best().end(); ++i) {
2677 if (!(*i)->isSelected()) {
2678 GlobalSelectionSystem().setSelectedAllComponents(false);
2681 selector.addSelectable(SelectionIntersection(0, 0), (*i));
2682 m_dragSelectable.setSelected(true);
2686 for (SelectionPool::iterator i = selector.begin(); i != selector.end(); ++i) {
2687 (*i).second->setSelected(true);
2691 void setSelected(bool select)
2693 m_selected = select;
2694 m_dragSelectable.setSelected(select);
2697 bool isSelected() const
2699 return m_selected || m_dragSelectable.isSelected();
2703 class ClipManipulator : public Manipulator {
2706 Manipulatable *GetManipulatable()
2708 ERROR_MESSAGE("clipper is not manipulatable");
2712 void setSelected(bool select)
2716 bool isSelected() const
2722 class select_all : public scene::Graph::Walker {
2725 select_all(bool select)
2730 bool pre(const scene::Path &path, scene::Instance &instance) const
2732 Selectable *selectable = Instance_getSelectable(instance);
2733 if (selectable != 0) {
2734 selectable->setSelected(m_select);
2740 class select_all_component : public scene::Graph::Walker {
2742 SelectionSystem::EComponentMode m_mode;
2744 select_all_component(bool select, SelectionSystem::EComponentMode mode)
2745 : m_select(select), m_mode(mode)
2749 bool pre(const scene::Path &path, scene::Instance &instance) const
2751 ComponentSelectionTestable *componentSelectionTestable = Instance_getComponentSelectionTestable(instance);
2752 if (componentSelectionTestable) {
2753 componentSelectionTestable->setSelectedComponents(m_select, m_mode);
2759 void Scene_SelectAll_Component(bool select, SelectionSystem::EComponentMode componentMode)
2761 GlobalSceneGraph().traverse(select_all_component(select, componentMode));
2765 // RadiantSelectionSystem
2766 class RadiantSelectionSystem :
2767 public SelectionSystem,
2768 public Translatable,
2772 mutable Matrix4 m_pivot2world;
2773 Matrix4 m_pivot2world_start;
2774 Matrix4 m_manip2pivot_start;
2775 Translation m_translation;
2776 Rotation m_rotation;
2779 static Shader *m_state;
2781 EManipulatorMode m_manipulator_mode;
2782 Manipulator *m_manipulator;
2787 EComponentMode m_componentmode;
2789 SelectionCounter m_count_primitive;
2790 SelectionCounter m_count_component;
2792 TranslateManipulator m_translate_manipulator;
2793 RotateManipulator m_rotate_manipulator;
2794 ScaleManipulator m_scale_manipulator;
2795 DragManipulator m_drag_manipulator;
2796 ClipManipulator m_clip_manipulator;
2798 typedef SelectionList<scene::Instance> selection_t;
2799 selection_t m_selection;
2800 selection_t m_component_selection;
2802 Signal1<const Selectable &> m_selectionChanged_callbacks;
2804 void ConstructPivot() const;
2806 mutable bool m_pivotChanged;
2807 bool m_pivot_moving;
2809 void Scene_TestSelect(Selector &selector, SelectionTest &test, const View &view, SelectionSystem::EMode mode,
2810 SelectionSystem::EComponentMode componentMode);
2812 bool nothingSelected() const
2814 return (Mode() == eComponent && m_count_component.empty())
2815 || (Mode() == ePrimitive && m_count_primitive.empty());
2827 RadiantSelectionSystem() :
2828 m_undo_begun(false),
2830 m_componentmode(eDefault),
2831 m_count_primitive(SelectionChangedCaller(*this)),
2832 m_count_component(SelectionChangedCaller(*this)),
2833 m_translate_manipulator(*this, 2, 64),
2834 m_rotate_manipulator(*this, 8, 64),
2835 m_scale_manipulator(*this, 0, 64),
2836 m_pivotChanged(false),
2837 m_pivot_moving(false)
2839 SetManipulatorMode(eTranslate);
2841 addSelectionChangeCallback(PivotChangedSelectionCaller(*this));
2842 AddGridChangeCallback(PivotChangedCaller(*this));
2845 void pivotChanged() const
2847 m_pivotChanged = true;
2848 SceneChangeNotify();
2851 typedef ConstMemberCaller<RadiantSelectionSystem, void(), &RadiantSelectionSystem::pivotChanged> PivotChangedCaller;
2853 void pivotChangedSelection(const Selectable &selectable)
2858 typedef MemberCaller<RadiantSelectionSystem, void(
2859 const Selectable &), &RadiantSelectionSystem::pivotChangedSelection> PivotChangedSelectionCaller;
2861 void SetMode(EMode mode)
2863 if (m_mode != mode) {
2874 void SetComponentMode(EComponentMode mode)
2876 m_componentmode = mode;
2879 EComponentMode ComponentMode() const
2881 return m_componentmode;
2884 void SetManipulatorMode(EManipulatorMode mode)
2886 m_manipulator_mode = mode;
2887 switch (m_manipulator_mode) {
2889 m_manipulator = &m_translate_manipulator;
2892 m_manipulator = &m_rotate_manipulator;
2895 m_manipulator = &m_scale_manipulator;
2898 m_manipulator = &m_drag_manipulator;
2901 m_manipulator = &m_clip_manipulator;
2907 EManipulatorMode ManipulatorMode() const
2909 return m_manipulator_mode;
2912 SelectionChangeCallback getObserver(EMode mode)
2914 if (mode == ePrimitive) {
2915 return makeCallback(m_count_primitive);
2917 return makeCallback(m_count_component);
2921 std::size_t countSelected() const
2923 return m_count_primitive.size();
2926 std::size_t countSelectedComponents() const
2928 return m_count_component.size();
2931 void onSelectedChanged(scene::Instance &instance, const Selectable &selectable)
2933 if (selectable.isSelected()) {
2934 m_selection.append(instance);
2936 m_selection.erase(instance);
2939 ASSERT_MESSAGE(m_selection.size() == m_count_primitive.size(), "selection-tracking error");
2942 void onComponentSelection(scene::Instance &instance, const Selectable &selectable)
2944 if (selectable.isSelected()) {
2945 m_component_selection.append(instance);
2947 m_component_selection.erase(instance);
2950 ASSERT_MESSAGE(m_component_selection.size() == m_count_component.size(), "selection-tracking error");
2953 scene::Instance &ultimateSelected() const
2955 ASSERT_MESSAGE(m_selection.size() > 0, "no instance selected");
2956 return m_selection.back();
2959 scene::Instance &penultimateSelected() const
2961 ASSERT_MESSAGE(m_selection.size() > 1, "only one instance selected");
2962 return *(*(--(--m_selection.end())));
2965 void setSelectedAll(bool selected)
2967 GlobalSceneGraph().traverse(select_all(selected));
2969 m_manipulator->setSelected(selected);
2972 void setSelectedAllComponents(bool selected)
2974 Scene_SelectAll_Component(selected, SelectionSystem::eVertex);
2975 Scene_SelectAll_Component(selected, SelectionSystem::eEdge);
2976 Scene_SelectAll_Component(selected, SelectionSystem::eFace);
2978 m_manipulator->setSelected(selected);
2981 void foreachSelected(const Visitor &visitor) const
2983 selection_t::const_iterator i = m_selection.begin();
2984 while (i != m_selection.end()) {
2985 visitor.visit(*(*(i++)));
2989 void foreachSelectedComponent(const Visitor &visitor) const
2991 selection_t::const_iterator i = m_component_selection.begin();
2992 while (i != m_component_selection.end()) {
2993 visitor.visit(*(*(i++)));
2997 void addSelectionChangeCallback(const SelectionChangeHandler &handler)
2999 m_selectionChanged_callbacks.connectLast(handler);
3002 void selectionChanged(const Selectable &selectable)
3004 m_selectionChanged_callbacks(selectable);
3007 typedef MemberCaller<RadiantSelectionSystem, void(
3008 const Selectable &), &RadiantSelectionSystem::selectionChanged> SelectionChangedCaller;
3013 m_pivot2world_start = GetPivot2World();
3016 bool SelectManipulator(const View &view, const float device_point[2], const float device_epsilon[2])
3018 if (!nothingSelected() || (ManipulatorMode() == eDrag && Mode() == eComponent)) {
3019 #if defined ( DEBUG_SELECTION )
3020 g_render_clipped.destroy();
3023 m_manipulator->setSelected(false);
3025 if (!nothingSelected() || (ManipulatorMode() == eDrag && Mode() == eComponent)) {
3026 View scissored(view);
3027 ConstructSelectionTest(scissored, SelectionBoxForPoint(device_point, device_epsilon));
3028 m_manipulator->testSelect(scissored, GetPivot2World());
3033 m_pivot_moving = m_manipulator->isSelected();
3035 if (m_pivot_moving) {
3037 pivot.update(GetPivot2World(), view.GetModelview(), view.GetProjection(), view.GetViewport());
3039 m_manip2pivot_start = matrix4_multiplied_by_matrix4(matrix4_full_inverse(m_pivot2world_start),
3040 pivot.m_worldSpace);
3042 Matrix4 device2manip;
3043 ConstructDevice2Manip(device2manip, m_pivot2world_start, view.GetModelview(), view.GetProjection(),
3044 view.GetViewport());
3045 m_manipulator->GetManipulatable()->Construct(device2manip, device_point[0], device_point[1]);
3047 m_undo_begun = false;
3050 SceneChangeNotify();
3053 return m_pivot_moving;
3058 if (Mode() == eComponent) {
3059 setSelectedAllComponents(false);
3061 setSelectedAll(false);
3065 void SelectPoint(const View &view, const float device_point[2], const float device_epsilon[2],
3066 RadiantSelectionSystem::EModifier modifier, bool face)
3068 ASSERT_MESSAGE(fabs(device_point[0]) <= 1.0f && fabs(device_point[1]) <= 1.0f, "point-selection error");
3069 if (modifier == eReplace) {
3071 setSelectedAllComponents(false);
3077 #if defined ( DEBUG_SELECTION )
3078 g_render_clipped.destroy();
3082 View scissored(view);
3083 ConstructSelectionTest(scissored, SelectionBoxForPoint(device_point, device_epsilon));
3085 SelectionVolume volume(scissored);
3086 SelectionPool selector;
3088 Scene_TestSelect_Component(selector, volume, scissored, eFace);
3090 Scene_TestSelect(selector, volume, scissored, Mode(), ComponentMode());
3093 if (!selector.failed()) {
3095 case RadiantSelectionSystem::eToggle: {
3096 SelectableSortedSet::iterator best = selector.begin();
3097 // toggle selection of the object with least depth
3098 if ((*best).second->isSelected()) {
3099 (*best).second->setSelected(false);
3101 (*best).second->setSelected(true);
3105 // if cycle mode not enabled, enable it
3106 case RadiantSelectionSystem::eReplace: {
3108 (*selector.begin()).second->setSelected(true);
3111 // select the next object in the list from the one already selected
3112 case RadiantSelectionSystem::eCycle: {
3113 SelectionPool::iterator i = selector.begin();
3114 while (i != selector.end()) {
3115 if ((*i).second->isSelected()) {
3116 (*i).second->setSelected(false);
3118 if (i != selector.end()) {
3119 i->second->setSelected(true);
3121 selector.begin()->second->setSelected(true);
3136 void SelectArea(const View &view, const float device_point[2], const float device_delta[2],
3137 RadiantSelectionSystem::EModifier modifier, bool face)
3139 if (modifier == eReplace) {
3141 setSelectedAllComponents(false);
3147 #if defined ( DEBUG_SELECTION )
3148 g_render_clipped.destroy();
3152 View scissored(view);
3153 ConstructSelectionTest(scissored, SelectionBoxForArea(device_point, device_delta));
3155 SelectionVolume volume(scissored);
3158 Scene_TestSelect_Component(pool, volume, scissored, eFace);
3160 Scene_TestSelect(pool, volume, scissored, Mode(), ComponentMode());
3163 for (SelectionPool::iterator i = pool.begin(); i != pool.end(); ++i) {
3164 (*i).second->setSelected(!(modifier == RadiantSelectionSystem::eToggle && (*i).second->isSelected()));
3170 void translate(const Vector3 &translation)
3172 if (!nothingSelected()) {
3173 //ASSERT_MESSAGE(!m_pivotChanged, "pivot is invalid");
3175 m_translation = translation;
3177 m_pivot2world = m_pivot2world_start;
3178 matrix4_translate_by_vec3(m_pivot2world, translation);
3180 if (Mode() == eComponent) {
3181 Scene_Translate_Component_Selected(GlobalSceneGraph(), m_translation);
3183 Scene_Translate_Selected(GlobalSceneGraph(), m_translation);
3186 SceneChangeNotify();
3190 void outputTranslation(TextOutputStream &ostream)
3192 ostream << " -xyz " << m_translation.x() << " " << m_translation.y() << " " << m_translation.z();
3195 void rotate(const Quaternion &rotation)
3197 if (!nothingSelected()) {
3198 //ASSERT_MESSAGE(!m_pivotChanged, "pivot is invalid");
3200 m_rotation = rotation;
3202 if (Mode() == eComponent) {
3203 Scene_Rotate_Component_Selected(GlobalSceneGraph(), m_rotation, vector4_to_vector3(m_pivot2world.t()));
3205 matrix4_assign_rotation_for_pivot(m_pivot2world, m_component_selection.back());
3207 Scene_Rotate_Selected(GlobalSceneGraph(), m_rotation, vector4_to_vector3(m_pivot2world.t()));
3209 matrix4_assign_rotation_for_pivot(m_pivot2world, m_selection.back());
3212 SceneChangeNotify();
3216 void outputRotation(TextOutputStream &ostream)
3218 ostream << " -eulerXYZ " << m_rotation.x() << " " << m_rotation.y() << " " << m_rotation.z();
3221 void scale(const Vector3 &scaling)
3223 if (!nothingSelected()) {
3226 if (Mode() == eComponent) {
3227 Scene_Scale_Component_Selected(GlobalSceneGraph(), m_scale, vector4_to_vector3(m_pivot2world.t()));
3229 Scene_Scale_Selected(GlobalSceneGraph(), m_scale, vector4_to_vector3(m_pivot2world.t()));
3232 SceneChangeNotify();
3236 void outputScale(TextOutputStream &ostream)
3238 ostream << " -scale " << m_scale.x() << " " << m_scale.y() << " " << m_scale.z();
3241 void rotateSelected(const Quaternion &rotation)
3248 void translateSelected(const Vector3 &translation)
3251 translate(translation);
3255 void scaleSelected(const Vector3 &scaling)
3262 void MoveSelected(const View &view, const float device_point[2])
3264 if (m_manipulator->isSelected()) {
3265 if (!m_undo_begun) {
3266 m_undo_begun = true;
3267 GlobalUndoSystem().start();
3270 Matrix4 device2manip;
3271 ConstructDevice2Manip(device2manip, m_pivot2world_start, view.GetModelview(), view.GetProjection(),
3272 view.GetViewport());
3273 m_manipulator->GetManipulatable()->Transform(m_manip2pivot_start, device2manip, device_point[0],
3278 /// \todo Support view-dependent nudge.
3279 void NudgeManipulator(const Vector3 &nudge, const Vector3 &view)
3281 if (ManipulatorMode() == eTranslate || ManipulatorMode() == eDrag) {
3282 translateSelected(nudge);
3288 void freezeTransforms();
3290 void renderSolid(Renderer &renderer, const VolumeTest &volume) const;
3292 void renderWireframe(Renderer &renderer, const VolumeTest &volume) const
3294 renderSolid(renderer, volume);
3297 const Matrix4 &GetPivot2World() const
3300 return m_pivot2world;
3303 static void constructStatic()
3305 m_state = GlobalShaderCache().capture("$POINT");
3306 #if defined( DEBUG_SELECTION )
3307 g_state_clipped = GlobalShaderCache().capture("$DEBUG_CLIPPED");
3309 TranslateManipulator::m_state_wire = GlobalShaderCache().capture("$WIRE_OVERLAY");
3310 TranslateManipulator::m_state_fill = GlobalShaderCache().capture("$FLATSHADE_OVERLAY");
3311 RotateManipulator::m_state_outer = GlobalShaderCache().capture("$WIRE_OVERLAY");
3314 static void destroyStatic()
3316 #if defined( DEBUG_SELECTION )
3317 GlobalShaderCache().release("$DEBUG_CLIPPED");
3319 GlobalShaderCache().release("$WIRE_OVERLAY");
3320 GlobalShaderCache().release("$FLATSHADE_OVERLAY");
3321 GlobalShaderCache().release("$WIRE_OVERLAY");
3322 GlobalShaderCache().release("$POINT");
3326 Shader *RadiantSelectionSystem::m_state = 0;
3330 RadiantSelectionSystem *g_RadiantSelectionSystem;
3332 inline RadiantSelectionSystem &getSelectionSystem()
3334 return *g_RadiantSelectionSystem;
3339 class testselect_entity_visible : public scene::Graph::Walker {
3340 Selector &m_selector;
3341 SelectionTest &m_test;
3343 testselect_entity_visible(Selector &selector, SelectionTest &test)
3344 : m_selector(selector), m_test(test)
3348 bool pre(const scene::Path &path, scene::Instance &instance) const
3350 Selectable *selectable = Instance_getSelectable(instance);
3352 && Node_isEntity(path.top())) {
3353 m_selector.pushSelectable(*selectable);
3356 SelectionTestable *selectionTestable = Instance_getSelectionTestable(instance);
3357 if (selectionTestable) {
3358 selectionTestable->testSelect(m_selector, m_test);
3364 void post(const scene::Path &path, scene::Instance &instance) const
3366 Selectable *selectable = Instance_getSelectable(instance);
3368 && Node_isEntity(path.top())) {
3369 m_selector.popSelectable();
3374 class testselect_primitive_visible : public scene::Graph::Walker {
3375 Selector &m_selector;
3376 SelectionTest &m_test;
3378 testselect_primitive_visible(Selector &selector, SelectionTest &test)
3379 : m_selector(selector), m_test(test)
3383 bool pre(const scene::Path &path, scene::Instance &instance) const
3385 Selectable *selectable = Instance_getSelectable(instance);
3386 if (selectable != 0) {
3387 m_selector.pushSelectable(*selectable);
3390 SelectionTestable *selectionTestable = Instance_getSelectionTestable(instance);
3391 if (selectionTestable) {
3392 selectionTestable->testSelect(m_selector, m_test);
3398 void post(const scene::Path &path, scene::Instance &instance) const
3400 Selectable *selectable = Instance_getSelectable(instance);
3401 if (selectable != 0) {
3402 m_selector.popSelectable();
3407 class testselect_component_visible : public scene::Graph::Walker {
3408 Selector &m_selector;
3409 SelectionTest &m_test;
3410 SelectionSystem::EComponentMode m_mode;
3412 testselect_component_visible(Selector &selector, SelectionTest &test, SelectionSystem::EComponentMode mode)
3413 : m_selector(selector), m_test(test), m_mode(mode)
3417 bool pre(const scene::Path &path, scene::Instance &instance) const
3419 ComponentSelectionTestable *componentSelectionTestable = Instance_getComponentSelectionTestable(instance);
3420 if (componentSelectionTestable) {
3421 componentSelectionTestable->testSelectComponents(m_selector, m_test, m_mode);
3429 class testselect_component_visible_selected : public scene::Graph::Walker {
3430 Selector &m_selector;
3431 SelectionTest &m_test;
3432 SelectionSystem::EComponentMode m_mode;
3434 testselect_component_visible_selected(Selector &selector, SelectionTest &test, SelectionSystem::EComponentMode mode)
3435 : m_selector(selector), m_test(test), m_mode(mode)
3439 bool pre(const scene::Path &path, scene::Instance &instance) const
3441 Selectable *selectable = Instance_getSelectable(instance);
3442 if (selectable != 0 && selectable->isSelected()) {
3443 ComponentSelectionTestable *componentSelectionTestable = Instance_getComponentSelectionTestable(instance);
3444 if (componentSelectionTestable) {
3445 componentSelectionTestable->testSelectComponents(m_selector, m_test, m_mode);
3453 void Scene_TestSelect_Primitive(Selector &selector, SelectionTest &test, const VolumeTest &volume)
3455 Scene_forEachVisible(GlobalSceneGraph(), volume, testselect_primitive_visible(selector, test));
3458 void Scene_TestSelect_Component_Selected(Selector &selector, SelectionTest &test, const VolumeTest &volume,
3459 SelectionSystem::EComponentMode componentMode)
3461 Scene_forEachVisible(GlobalSceneGraph(), volume,
3462 testselect_component_visible_selected(selector, test, componentMode));
3465 void Scene_TestSelect_Component(Selector &selector, SelectionTest &test, const VolumeTest &volume,
3466 SelectionSystem::EComponentMode componentMode)
3468 Scene_forEachVisible(GlobalSceneGraph(), volume, testselect_component_visible(selector, test, componentMode));
3471 void RadiantSelectionSystem::Scene_TestSelect(Selector &selector, SelectionTest &test, const View &view,
3472 SelectionSystem::EMode mode,
3473 SelectionSystem::EComponentMode componentMode)
3477 Scene_forEachVisible(GlobalSceneGraph(), view, testselect_entity_visible(selector, test));
3481 Scene_TestSelect_Primitive(selector, test, view);
3484 Scene_TestSelect_Component_Selected(selector, test, view, componentMode);
3489 class FreezeTransforms : public scene::Graph::Walker {
3491 bool pre(const scene::Path &path, scene::Instance &instance) const
3493 TransformNode *transformNode = Node_getTransformNode(path.top());
3494 if (transformNode != 0) {
3495 Transformable *transform = Instance_getTransformable(instance);
3496 if (transform != 0) {
3497 transform->freezeTransform();
3504 void RadiantSelectionSystem::freezeTransforms()
3506 GlobalSceneGraph().traverse(FreezeTransforms());
3510 void RadiantSelectionSystem::endMove()
3514 if (Mode() == ePrimitive) {
3515 if (ManipulatorMode() == eDrag) {
3516 Scene_SelectAll_Component(false, SelectionSystem::eFace);
3520 m_pivot_moving = false;
3523 SceneChangeNotify();
3526 StringOutputStream command;
3528 if (ManipulatorMode() == eTranslate) {
3529 command << "translateTool";
3530 outputTranslation(command);
3531 } else if (ManipulatorMode() == eRotate) {
3532 command << "rotateTool";
3533 outputRotation(command);
3534 } else if (ManipulatorMode() == eScale) {
3535 command << "scaleTool";
3536 outputScale(command);
3537 } else if (ManipulatorMode() == eDrag) {
3538 command << "dragTool";
3541 GlobalUndoSystem().finish(command.c_str());
3546 inline AABB Instance_getPivotBounds(scene::Instance &instance)
3548 Entity *entity = Node_getEntity(instance.path().top());
3550 && (entity->getEntityClass().fixedsize
3551 || !node_is_group(instance.path().top()))) {
3552 Editable *editable = Node_getEditable(instance.path().top());
3553 if (editable != 0) {
3554 return AABB(vector4_to_vector3(
3555 matrix4_multiplied_by_matrix4(instance.localToWorld(), editable->getLocalPivot()).t()),
3558 return AABB(vector4_to_vector3(instance.localToWorld().t()), Vector3(0, 0, 0));
3562 return instance.worldAABB();
3565 class bounds_selected : public scene::Graph::Walker {
3568 bounds_selected(AABB &bounds)
3574 bool pre(const scene::Path &path, scene::Instance &instance) const
3576 Selectable *selectable = Instance_getSelectable(instance);
3578 && selectable->isSelected()) {
3579 aabb_extend_by_aabb_safe(m_bounds, Instance_getPivotBounds(instance));
3585 class bounds_selected_component : public scene::Graph::Walker {
3588 bounds_selected_component(AABB &bounds)
3594 bool pre(const scene::Path &path, scene::Instance &instance) const
3596 Selectable *selectable = Instance_getSelectable(instance);
3598 && selectable->isSelected()) {
3599 ComponentEditable *componentEditable = Instance_getComponentEditable(instance);
3600 if (componentEditable) {
3601 aabb_extend_by_aabb_safe(m_bounds,
3602 aabb_for_oriented_aabb_safe(componentEditable->getSelectedComponentsBounds(),
3603 instance.localToWorld()));
3610 void Scene_BoundsSelected(scene::Graph &graph, AABB &bounds)
3612 graph.traverse(bounds_selected(bounds));
3615 void Scene_BoundsSelectedComponent(scene::Graph &graph, AABB &bounds)
3617 graph.traverse(bounds_selected_component(bounds));
3621 inline void pivot_for_node( Matrix4& pivot, scene::Node& node, scene::Instance& instance ){
3622 ComponentEditable* componentEditable = Instance_getComponentEditable( instance );
3623 if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent
3624 && componentEditable != 0 ) {
3625 pivot = matrix4_translation_for_vec3( componentEditable->getSelectedComponentsBounds().origin );
3629 Bounded* bounded = Instance_getBounded( instance );
3630 if ( bounded != 0 ) {
3631 pivot = matrix4_translation_for_vec3( bounded->localAABB().origin );
3635 pivot = g_matrix4_identity;
3641 void RadiantSelectionSystem::ConstructPivot() const
3643 if (!m_pivotChanged || m_pivot_moving) {
3646 m_pivotChanged = false;
3648 Vector3 m_object_pivot;
3650 if (!nothingSelected()) {
3653 if (Mode() == eComponent) {
3654 Scene_BoundsSelectedComponent(GlobalSceneGraph(), bounds);
3656 Scene_BoundsSelected(GlobalSceneGraph(), bounds);
3658 m_object_pivot = bounds.origin;
3661 vector3_snap(m_object_pivot, GetSnapGridSize());
3662 m_pivot2world = matrix4_translation_for_vec3(m_object_pivot);
3664 switch (m_manipulator_mode) {
3668 if (Mode() == eComponent) {
3669 matrix4_assign_rotation_for_pivot(m_pivot2world, m_component_selection.back());
3671 matrix4_assign_rotation_for_pivot(m_pivot2world, m_selection.back());
3675 if (Mode() == eComponent) {
3676 matrix4_assign_rotation_for_pivot(m_pivot2world, m_component_selection.back());
3678 matrix4_assign_rotation_for_pivot(m_pivot2world, m_selection.back());
3687 void RadiantSelectionSystem::renderSolid(Renderer &renderer, const VolumeTest &volume) const
3689 //if(view->TestPoint(m_object_pivot))
3690 if (!nothingSelected()) {
3691 renderer.Highlight(Renderer::ePrimitive, false);
3692 renderer.Highlight(Renderer::eFace, false);
3694 renderer.SetState(m_state, Renderer::eWireframeOnly);
3695 renderer.SetState(m_state, Renderer::eFullMaterials);
3697 m_manipulator->render(renderer, volume, GetPivot2World());
3700 #if defined( DEBUG_SELECTION )
3701 renderer.SetState(g_state_clipped, Renderer::eWireframeOnly);
3702 renderer.SetState(g_state_clipped, Renderer::eFullMaterials);
3703 renderer.addRenderable(g_render_clipped, g_render_clipped.m_world);
3708 void SelectionSystem_OnBoundsChanged()
3710 getSelectionSystem().pivotChanged();
3714 SignalHandlerId SelectionSystem_boundsChanged;
3716 void SelectionSystem_Construct()
3718 RadiantSelectionSystem::constructStatic();
3720 g_RadiantSelectionSystem = new RadiantSelectionSystem;
3722 SelectionSystem_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback(
3723 FreeCaller<void(), SelectionSystem_OnBoundsChanged>());
3725 GlobalShaderCache().attachRenderable(getSelectionSystem());
3728 void SelectionSystem_Destroy()
3730 GlobalShaderCache().detachRenderable(getSelectionSystem());
3732 GlobalSceneGraph().removeBoundsChangedCallback(SelectionSystem_boundsChanged);
3734 delete g_RadiantSelectionSystem;
3736 RadiantSelectionSystem::destroyStatic();
3740 inline float screen_normalised(float pos, std::size_t size)
3742 return ((2.0f * pos) / size) - 1.0f;
3745 typedef Vector2 DeviceVector;
3747 inline DeviceVector window_to_normalised_device(WindowVector window, std::size_t width, std::size_t height)
3749 return DeviceVector(screen_normalised(window.x(), width), screen_normalised(height - 1 - window.y(), height));
3752 inline float device_constrained(float pos)
3754 return std::min(1.0f, std::max(-1.0f, pos));
3757 inline DeviceVector device_constrained(DeviceVector device)
3759 return DeviceVector(device_constrained(device.x()), device_constrained(device.y()));
3762 inline float window_constrained(float pos, std::size_t origin, std::size_t size)
3764 return std::min(static_cast<float>( origin + size ), std::max(static_cast<float>( origin ), pos));
3768 window_constrained(WindowVector window, std::size_t x, std::size_t y, std::size_t width, std::size_t height)
3770 return WindowVector(window_constrained(window.x(), x, width), window_constrained(window.y(), y, height));
3773 typedef Callback<void(DeviceVector)> MouseEventCallback;
3775 Single<MouseEventCallback> g_mouseMovedCallback;
3776 Single<MouseEventCallback> g_mouseUpCallback;
3779 const ButtonIdentifier c_button_select = c_buttonLeft;
3780 const ModifierFlags c_modifier_manipulator = c_modifierNone;
3781 const ModifierFlags c_modifier_toggle = c_modifierShift;
3782 const ModifierFlags c_modifier_replace = c_modifierShift | c_modifierAlt;
3783 const ModifierFlags c_modifier_face = c_modifierControl;
3785 const ButtonIdentifier c_button_select = c_buttonLeft;
3786 const ModifierFlags c_modifier_manipulator = c_modifierNone;
3787 const ModifierFlags c_modifier_toggle = c_modifierControl;
3788 const ModifierFlags c_modifier_replace = c_modifierNone;
3789 const ModifierFlags c_modifier_face = c_modifierShift;
3791 const ModifierFlags c_modifier_toggle_face = c_modifier_toggle | c_modifier_face;
3792 const ModifierFlags c_modifier_replace_face = c_modifier_replace | c_modifier_face;
3794 const ButtonIdentifier c_button_texture = c_buttonMiddle;
3795 const ModifierFlags c_modifier_apply_texture1 = c_modifierControl | c_modifierShift;
3796 const ModifierFlags c_modifier_apply_texture2 = c_modifierControl;
3797 const ModifierFlags c_modifier_apply_texture3 = c_modifierShift;
3798 const ModifierFlags c_modifier_copy_texture = c_modifierNone;
3801 RadiantSelectionSystem::EModifier modifier_for_state(ModifierFlags state)
3803 if (state == c_modifier_toggle || state == c_modifier_toggle_face) {
3804 return RadiantSelectionSystem::eToggle;
3806 if (state == c_modifier_replace || state == c_modifier_replace_face) {
3807 return RadiantSelectionSystem::eReplace;
3809 return RadiantSelectionSystem::eManipulator;
3812 rect_t getDeviceArea() const
3814 DeviceVector delta(m_current - m_start);
3815 if (selecting() && fabs(delta.x()) > m_epsilon.x() && fabs(delta.y()) > m_epsilon.y()) {
3816 return SelectionBoxForArea(&m_start[0], &delta[0]);
3818 rect_t default_area = {{0, 0,},
3820 return default_area;
3825 DeviceVector m_start;
3826 DeviceVector m_current;
3827 DeviceVector m_epsilon;
3828 std::size_t m_unmoved_replaces;
3829 ModifierFlags m_state;
3831 RectangleCallback m_window_update;
3833 Selector_() : m_start(0.0f, 0.0f), m_current(0.0f, 0.0f), m_unmoved_replaces(0), m_state(c_modifierNone)
3839 m_window_update(getDeviceArea());
3842 void testSelect(DeviceVector position)
3844 RadiantSelectionSystem::EModifier modifier = modifier_for_state(m_state);
3845 if (modifier != RadiantSelectionSystem::eManipulator) {
3846 DeviceVector delta(position - m_start);
3847 if (fabs(delta.x()) > m_epsilon.x() && fabs(delta.y()) > m_epsilon.y()) {
3848 DeviceVector delta(position - m_start);
3849 getSelectionSystem().SelectArea(*m_view, &m_start[0], &delta[0], modifier,
3850 (m_state & c_modifier_face) != c_modifierNone);
3852 if (modifier == RadiantSelectionSystem::eReplace && m_unmoved_replaces++ > 0) {
3853 modifier = RadiantSelectionSystem::eCycle;
3855 getSelectionSystem().SelectPoint(*m_view, &position[0], &m_epsilon[0], modifier,
3856 (m_state & c_modifier_face) != c_modifierNone);
3860 m_start = m_current = DeviceVector(0.0f, 0.0f);
3864 bool selecting() const
3866 return m_state != c_modifier_manipulator;
3869 void setState(ModifierFlags state)
3871 bool was_selecting = selecting();
3873 if (was_selecting ^ selecting()) {
3878 ModifierFlags getState() const
3883 void modifierEnable(ModifierFlags type)
3885 setState(bitfield_enable(getState(), type));
3888 void modifierDisable(ModifierFlags type)
3890 setState(bitfield_disable(getState(), type));
3893 void mouseDown(DeviceVector position)
3895 m_start = m_current = device_constrained(position);
3898 void mouseMoved(DeviceVector position)
3900 m_current = device_constrained(position);
3904 typedef MemberCaller<Selector_, void(DeviceVector), &Selector_::mouseMoved> MouseMovedCaller;
3906 void mouseUp(DeviceVector position)
3908 testSelect(device_constrained(position));
3910 g_mouseMovedCallback.clear();
3911 g_mouseUpCallback.clear();
3914 typedef MemberCaller<Selector_, void(DeviceVector), &Selector_::mouseUp> MouseUpCaller;
3918 class Manipulator_ {
3920 DeviceVector m_epsilon;
3923 bool mouseDown(DeviceVector position)
3925 return getSelectionSystem().SelectManipulator(*m_view, &position[0], &m_epsilon[0]);
3928 void mouseMoved(DeviceVector position)
3930 getSelectionSystem().MoveSelected(*m_view, &position[0]);
3933 typedef MemberCaller<Manipulator_, void(DeviceVector), &Manipulator_::mouseMoved> MouseMovedCaller;
3935 void mouseUp(DeviceVector position)
3937 getSelectionSystem().endMove();
3938 g_mouseMovedCallback.clear();
3939 g_mouseUpCallback.clear();
3942 typedef MemberCaller<Manipulator_, void(DeviceVector), &Manipulator_::mouseUp> MouseUpCaller;
3945 void Scene_copyClosestTexture(SelectionTest &test);
3947 void Scene_applyClosestTexture(SelectionTest &test);
3949 class RadiantWindowObserver : public SelectionSystemWindowObserver {
3960 Selector_ m_selector;
3961 Manipulator_ m_manipulator;
3963 RadiantWindowObserver() : m_mouse_down(false)
3972 void setView(const View &view)
3974 m_selector.m_view = &view;
3975 m_manipulator.m_view = &view;
3978 void setRectangleDrawCallback(const RectangleCallback &callback)
3980 m_selector.m_window_update = callback;
3983 void onSizeChanged(int width, int height)
3987 DeviceVector epsilon(SELECT_EPSILON / static_cast<float>( m_width ),
3988 SELECT_EPSILON / static_cast<float>( m_height ));
3989 m_selector.m_epsilon = m_manipulator.m_epsilon = epsilon;
3992 void onMouseDown(const WindowVector &position, ButtonIdentifier button, ModifierFlags modifiers)
3994 if (button == c_button_select) {
3995 m_mouse_down = true;
3997 DeviceVector devicePosition(window_to_normalised_device(position, m_width, m_height));
3998 if (modifiers == c_modifier_manipulator && m_manipulator.mouseDown(devicePosition)) {
3999 g_mouseMovedCallback.insert(MouseEventCallback(Manipulator_::MouseMovedCaller(m_manipulator)));
4000 g_mouseUpCallback.insert(MouseEventCallback(Manipulator_::MouseUpCaller(m_manipulator)));
4002 m_selector.mouseDown(devicePosition);
4003 g_mouseMovedCallback.insert(MouseEventCallback(Selector_::MouseMovedCaller(m_selector)));
4004 g_mouseUpCallback.insert(MouseEventCallback(Selector_::MouseUpCaller(m_selector)));
4006 } else if (button == c_button_texture) {
4007 DeviceVector devicePosition(device_constrained(window_to_normalised_device(position, m_width, m_height)));
4009 View scissored(*m_selector.m_view);
4010 ConstructSelectionTest(scissored, SelectionBoxForPoint(&devicePosition[0], &m_selector.m_epsilon[0]));
4011 SelectionVolume volume(scissored);
4013 if (modifiers == c_modifier_apply_texture1 || modifiers == c_modifier_apply_texture2 ||
4014 modifiers == c_modifier_apply_texture3) {
4015 Scene_applyClosestTexture(volume);
4016 } else if (modifiers == c_modifier_copy_texture) {
4017 Scene_copyClosestTexture(volume);
4022 void onMouseMotion(const WindowVector &position, ModifierFlags modifiers)
4024 m_selector.m_unmoved_replaces = 0;
4026 if (m_mouse_down && !g_mouseMovedCallback.empty()) {
4027 g_mouseMovedCallback.get()(window_to_normalised_device(position, m_width, m_height));
4031 void onMouseUp(const WindowVector &position, ButtonIdentifier button, ModifierFlags modifiers)
4033 if (button == c_button_select && !g_mouseUpCallback.empty()) {
4034 m_mouse_down = false;
4036 g_mouseUpCallback.get()(window_to_normalised_device(position, m_width, m_height));
4040 void onModifierDown(ModifierFlags type)
4042 m_selector.modifierEnable(type);
4045 void onModifierUp(ModifierFlags type)
4047 m_selector.modifierDisable(type);
4052 SelectionSystemWindowObserver *NewWindowObserver()
4054 return new RadiantWindowObserver;
4058 #include "modulesystem/singletonmodule.h"
4059 #include "modulesystem/moduleregistry.h"
4061 class SelectionDependencies :
4062 public GlobalSceneGraphModuleRef,
4063 public GlobalShaderCacheModuleRef,
4064 public GlobalOpenGLModuleRef {
4067 class SelectionAPI : public TypeSystemRef {
4068 SelectionSystem *m_selection;
4070 typedef SelectionSystem Type;
4072 STRING_CONSTANT(Name, "*");
4076 SelectionSystem_Construct();
4078 m_selection = &getSelectionSystem();
4083 SelectionSystem_Destroy();
4086 SelectionSystem *getTable()
4092 typedef SingletonModule<SelectionAPI, SelectionDependencies> SelectionModule;
4093 typedef Static<SelectionModule> StaticSelectionModule;
4094 StaticRegisterModule staticRegisterSelection(StaticSelectionModule::instance());