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"
24 #include "debugging/debugging.h"
30 #include "windowobserver.h"
34 #include "renderable.h"
35 #include "selectable.h"
38 #include "math/frustum.h"
39 #include "signal/signal.h"
40 #include "generic/object.h"
41 #include "selectionlib.h"
45 #include "stream/stringstream.h"
46 #include "eclasslib.h"
47 #include "generic/bitfield.h"
48 #include "generic/static.h"
51 #include "container/container.h"
55 TextOutputStream& ostream_write( TextOutputStream& t, const Vector4& v ){
56 return t << "[ " << v.x() << " " << v.y() << " " << v.z() << " " << v.w() << " ]";
59 TextOutputStream& ostream_write( TextOutputStream& t, const Matrix4& m ){
60 return t << "[ " << m.x() << " " << m.y() << " " << m.z() << " " << m.t() << " ]";
66 Matrix4 m_viewpointSpace;
67 Matrix4 m_viewplaneSpace;
68 Vector3 m_axis_screen;
70 void update( const Matrix4& pivot2world, const Matrix4& modelview, const Matrix4& projection, const Matrix4& viewport ){
71 Pivot2World_worldSpace( m_worldSpace, pivot2world, modelview, projection, viewport );
72 Pivot2World_viewpointSpace( m_viewpointSpace, m_axis_screen, pivot2world, modelview, projection, viewport );
73 Pivot2World_viewplaneSpace( m_viewplaneSpace, pivot2world, modelview, projection, viewport );
78 void point_for_device_point( Vector3& point, const Matrix4& device2object, const float x, const float y, const float z ){
79 // transform from normalised device coords to object coords
80 point = vector4_projected( matrix4_transformed_vector4( device2object, Vector4( x, y, z, 1 ) ) );
83 void ray_for_device_point( Ray& ray, const Matrix4& device2object, const float x, const float y ){
84 // point at x, y, zNear
85 point_for_device_point( ray.origin, device2object, x, y, -1 );
87 // point at x, y, zFar
88 point_for_device_point( ray.direction, device2object, x, y, 1 );
91 vector3_subtract( ray.direction, ray.origin );
92 vector3_normalise( ray.direction );
95 bool sphere_intersect_ray( const Vector3& origin, float radius, const Ray& ray, Vector3& intersection ){
96 intersection = vector3_subtracted( origin, ray.origin );
97 const double a = vector3_dot( intersection, ray.direction );
98 const double d = radius * radius - ( vector3_dot( intersection, intersection ) - a * a );
101 intersection = vector3_added( ray.origin, vector3_scaled( ray.direction, a - sqrt( d ) ) );
106 intersection = vector3_added( ray.origin, vector3_scaled( ray.direction, a ) );
111 void ray_intersect_ray( const Ray& ray, const Ray& other, Vector3& intersection ){
112 intersection = vector3_subtracted( ray.origin, other.origin );
113 //float a = 1;//vector3_dot(ray.direction, ray.direction); // always >= 0
114 double dot = vector3_dot( ray.direction, other.direction );
115 //float c = 1;//vector3_dot(other.direction, other.direction); // always >= 0
116 double d = vector3_dot( ray.direction, intersection );
117 double e = vector3_dot( other.direction, intersection );
118 double D = 1 - dot * dot; //a*c - dot*dot; // always >= 0
120 if ( D < 0.000001 ) {
121 // the lines are almost parallel
122 intersection = vector3_added( other.origin, vector3_scaled( other.direction, e ) );
126 intersection = vector3_added( other.origin, vector3_scaled( other.direction, ( e - dot * d ) / D ) );
130 const Vector3 g_origin( 0, 0, 0 );
131 const float g_radius = 64;
133 void point_on_sphere( Vector3& point, const Matrix4& device2object, const float x, const float y ){
135 ray_for_device_point( ray, device2object, x, y );
136 sphere_intersect_ray( g_origin, g_radius, ray, point );
139 void point_on_axis( Vector3& point, const Vector3& axis, const Matrix4& device2object, const float x, const float y ){
141 ray_for_device_point( ray, device2object, x, y );
142 ray_intersect_ray( ray, Ray( Vector3( 0, 0, 0 ), axis ), point );
145 void point_on_plane( Vector3& point, const Matrix4& device2object, const float x, const float y ){
146 Matrix4 object2device( matrix4_full_inverse( device2object ) );
147 point = vector4_projected( matrix4_transformed_vector4( device2object, Vector4( x, y, object2device[14] / object2device[15], 1 ) ) );
150 //! a and b are unit vectors .. returns angle in radians
151 inline float angle_between( const Vector3& a, const Vector3& b ){
152 return static_cast<float>( 2.0 * atan2(
153 vector3_length( vector3_subtracted( a, b ) ),
154 vector3_length( vector3_added( a, b ) )
159 #if defined( _DEBUG )
163 test_quat( const Vector3& from, const Vector3& to ){
164 Vector4 quaternion( quaternion_for_unit_vectors( from, to ) );
165 Matrix4 matrix( matrix4_rotation_for_quaternion( quaternion_multiplied_by_quaternion( quaternion, c_quaternion_identity ) ) );
170 static test_quat bleh( g_vector3_axis_x, g_vector3_axis_y );
173 //! axis is a unit vector
174 inline void constrain_to_axis( Vector3& vec, const Vector3& axis ){
175 vec = vector3_normalised( vector3_added( vec, vector3_scaled( axis, -vector3_dot( vec, axis ) ) ) );
178 //! a and b are unit vectors .. a and b must be orthogonal to axis .. returns angle in radians
179 float angle_for_axis( const Vector3& a, const Vector3& b, const Vector3& axis ){
180 if ( vector3_dot( axis, vector3_cross( a, b ) ) > 0.0 ) {
181 return angle_between( a, b );
184 return -angle_between( a, b );
188 float distance_for_axis( const Vector3& a, const Vector3& b, const Vector3& axis ){
189 return static_cast<float>( vector3_dot( b, axis ) - vector3_dot( a, axis ) );
195 virtual void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ) = 0;
196 virtual void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapbbox ) = 0;
199 void transform_local2object( Matrix4& object, const Matrix4& local, const Matrix4& local2object ){
200 object = matrix4_multiplied_by_matrix4(
201 matrix4_multiplied_by_matrix4( local2object, local ),
202 matrix4_full_inverse( local2object )
209 virtual void rotate( const Quaternion& rotation ) = 0;
212 class RotateFree : public Manipulatable
215 Rotatable& m_rotatable;
217 RotateFree( Rotatable& rotatable )
218 : m_rotatable( rotatable ){
220 void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){
221 point_on_sphere( m_start, device2manip, x, y );
222 vector3_normalise( m_start );
224 void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapbbox ){
226 point_on_sphere( current, device2manip, x, y );
229 Vector3 axis( 0, 0, 0 );
230 for( std::size_t i = 0; i < 3; ++i ){
231 if( current[i] == 0.0f ){
236 if( vector3_length_squared( axis ) != 0 ){
237 constrain_to_axis( current, axis );
238 m_rotatable.rotate( quaternion_for_axisangle( axis, float_snapped( angle_for_axis( m_start, current, axis ), static_cast<float>( c_pi / 12.0 ) ) ) );
243 vector3_normalise( current );
244 m_rotatable.rotate( quaternion_for_unit_vectors( m_start, current ) );
248 class RotateAxis : public Manipulatable
252 Rotatable& m_rotatable;
254 RotateAxis( Rotatable& rotatable )
255 : m_rotatable( rotatable ){
257 void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){
258 point_on_sphere( m_start, device2manip, x, y );
259 constrain_to_axis( m_start, m_axis );
261 /// \brief Converts current position to a normalised vector orthogonal to axis.
262 void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapbbox ){
264 point_on_sphere( current, device2manip, x, y );
265 constrain_to_axis( current, m_axis );
268 m_rotatable.rotate( quaternion_for_axisangle( m_axis, float_snapped( angle_for_axis( m_start, current, m_axis ), static_cast<float>( c_pi / 12.0 ) ) ) );
271 m_rotatable.rotate( quaternion_for_axisangle( m_axis, angle_for_axis( m_start, current, m_axis ) ) );
275 void SetAxis( const Vector3& axis ){
280 void translation_local2object( Vector3& object, const Vector3& local, const Matrix4& local2object ){
281 object = matrix4_get_translation_vec3(
282 matrix4_multiplied_by_matrix4(
283 matrix4_translated_by_vec3( local2object, local ),
284 matrix4_full_inverse( local2object )
292 virtual void translate( const Vector3& translation ) = 0;
295 class TranslateAxis : public Manipulatable
299 Translatable& m_translatable;
302 TranslateAxis( Translatable& translatable )
303 : m_translatable( translatable ){
305 void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){
306 point_on_axis( m_start, m_axis, device2manip, x, y );
309 void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapbbox ){
311 point_on_axis( current, m_axis, device2manip, x, y );
312 current = vector3_scaled( m_axis, distance_for_axis( m_start, current, m_axis ) );
314 translation_local2object( current, current, manip2object );
316 float grid = GetSnapGridSize();
317 Vector3 maxs( m_bounds.origin + m_bounds.extents );
318 Vector3 mins( m_bounds.origin - m_bounds.extents );
319 // globalOutputStream() << "current: " << current << "\n";
320 for( std::size_t i = 0; i < 3; ++i ){
321 if( m_axis[i] != 0.f ){
322 float snapto1 = float_snapped( maxs[i] + current[i] , grid );
323 float snapto2 = float_snapped( mins[i] + current[i] , grid );
325 float dist1 = fabs( fabs( maxs[i] + current[i] ) - fabs( snapto1 ) );
326 float dist2 = fabs( fabs( mins[i] + current[i] ) - fabs( snapto2 ) );
328 // globalOutputStream() << "maxs[i] + current[i]: " << maxs[i] + current[i] << " snapto1: " << snapto1 << " dist1: " << dist1 << "\n";
329 // globalOutputStream() << "mins[i] + current[i]: " << mins[i] + current[i] << " snapto2: " << snapto2 << " dist2: " << dist2 << "\n";
330 current[i] = dist2 > dist1 ? snapto1 - maxs[i] : snapto2 - mins[i];
335 vector3_snap( current, GetSnapGridSize() );
338 m_translatable.translate( current );
341 void SetAxis( const Vector3& axis ){
346 class TranslateFree : public Manipulatable
350 Translatable& m_translatable;
353 TranslateFree( Translatable& translatable )
354 : m_translatable( translatable ){
356 void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){
357 point_on_plane( m_start, device2manip, x, y );
360 void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapbbox ){
362 point_on_plane( current, device2manip, x, y );
363 current = vector3_subtracted( current, m_start );
366 for ( std::size_t i = 0; i < 3 ; ++i ){
367 if( fabs( current[i] ) >= fabs( current[(i + 1) % 3] ) ){
368 current[(i + 1) % 3] = 0.0f;
376 translation_local2object( current, current, manip2object );
378 float grid = GetSnapGridSize();
379 Vector3 maxs( m_bounds.origin + m_bounds.extents );
380 Vector3 mins( m_bounds.origin - m_bounds.extents );
381 //globalOutputStream() << "current: " << current << "\n";
382 for( std::size_t i = 0; i < 3; ++i ){
383 if( fabs( current[i] ) > 0.000001f ){
384 float snapto1 = float_snapped( maxs[i] + current[i] , grid );
385 float snapto2 = float_snapped( mins[i] + current[i] , grid );
387 float dist1 = fabs( fabs( maxs[i] + current[i] ) - fabs( snapto1 ) );
388 float dist2 = fabs( fabs( mins[i] + current[i] ) - fabs( snapto2 ) );
390 current[i] = dist2 > dist1 ? snapto1 - maxs[i] : snapto2 - mins[i];
395 vector3_snap( current, GetSnapGridSize() );
398 m_translatable.translate( current );
405 virtual void scale( const Vector3& scaling ) = 0;
409 class ScaleAxis : public Manipulatable
414 Scalable& m_scalable;
416 Vector3 m_choosen_extent;
420 ScaleAxis( Scalable& scalable )
421 : m_scalable( scalable ){
423 void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){
424 point_on_axis( m_start, m_axis, device2manip, x, y );
426 m_choosen_extent = Vector3(
427 std::max( bounds.origin[0] + bounds.extents[0] - transform_origin[0], - bounds.origin[0] + bounds.extents[0] + transform_origin[0] ),
428 std::max( bounds.origin[1] + bounds.extents[1] - transform_origin[1], - bounds.origin[1] + bounds.extents[1] + transform_origin[1] ),
429 std::max( bounds.origin[2] + bounds.extents[2] - transform_origin[2], - bounds.origin[2] + bounds.extents[2] + transform_origin[2] )
433 void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapbbox ){
434 //globalOutputStream() << "manip2object: " << manip2object << " device2manip: " << device2manip << " x: " << x << " y:" << y <<"\n";
436 point_on_axis( current, m_axis, device2manip, x, y );
437 Vector3 delta = vector3_subtracted( current, m_start );
439 translation_local2object( delta, delta, manip2object );
440 vector3_snap( delta, GetSnapGridSize() );
442 Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0.0f ? GetSnapGridSize() : 0.001f ) );
443 for ( std::size_t i = 0; i < 3 ; ++i ){ //prevent snapping to 0 with big gridsize
444 if( float_snapped( m_start[i], 0.001f ) != 0.0f && start[i] == 0.0f ){
445 start[i] = GetSnapGridSize();
448 //globalOutputStream() << "m_start: " << m_start << " start: " << start << " delta: " << delta <<"\n";
450 start[0] == 0 ? 1 : 1 + delta[0] / start[0],
451 start[1] == 0 ? 1 : 1 + delta[1] / start[1],
452 start[2] == 0 ? 1 : 1 + delta[2] / start[2]
455 for( std::size_t i = 0; i < 3; i++ ){
456 if( m_choosen_extent[i] > 0.0625f && m_axis[i] != 0.f ){ //epsilon to prevent super high scale for set of models, having really small extent, formed by origins
457 scale[i] = ( m_choosen_extent[i] + delta[i] ) / m_choosen_extent[i];
459 float snappdwidth = float_snapped( scale[i] * m_bounds.extents[i] * 2.f, GetSnapGridSize() );
460 scale[i] = snappdwidth / ( m_bounds.extents[i] * 2.f );
465 for( std::size_t i = 0; i < 3; i++ ){
466 if( scale[i] == 1.0f ){
467 scale[i] = vector3_dot( scale, m_axis );
471 //globalOutputStream() << "scale: " << scale <<"\n";
472 m_scalable.scale( scale );
475 void SetAxis( const Vector3& axis ){
480 class ScaleFree : public Manipulatable
484 Scalable& m_scalable;
486 Vector3 m_choosen_extent;
490 ScaleFree( Scalable& scalable )
491 : m_scalable( scalable ){
493 void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){
494 point_on_plane( m_start, device2manip, x, y );
496 m_choosen_extent = Vector3(
497 std::max( bounds.origin[0] + bounds.extents[0] - transform_origin[0], - bounds.origin[0] + bounds.extents[0] + transform_origin[0] ),
498 std::max( bounds.origin[1] + bounds.extents[1] - transform_origin[1], - bounds.origin[1] + bounds.extents[1] + transform_origin[1] ),
499 std::max( bounds.origin[2] + bounds.extents[2] - transform_origin[2], - bounds.origin[2] + bounds.extents[2] + transform_origin[2] )
503 void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapbbox ){
505 point_on_plane( current, device2manip, x, y );
506 Vector3 delta = vector3_subtracted( current, m_start );
508 translation_local2object( delta, delta, manip2object );
509 vector3_snap( delta, GetSnapGridSize() );
511 Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0.0f ? GetSnapGridSize() : 0.001f ) );
512 for ( std::size_t i = 0; i < 3 ; ++i ){ //prevent snapping to 0 with big gridsize
513 if( float_snapped( m_start[i], 0.001f ) != 0.0f && start[i] == 0.0f ){
514 start[i] = GetSnapGridSize();
518 start[0] == 0 ? 1 : 1 + delta[0] / start[0],
519 start[1] == 0 ? 1 : 1 + delta[1] / start[1],
520 start[2] == 0 ? 1 : 1 + delta[2] / start[2]
523 //globalOutputStream() << "m_start: " << m_start << " start: " << start << " delta: " << delta <<"\n";
524 for( std::size_t i = 0; i < 3; i++ ){
525 if( m_choosen_extent[i] > 0.0625f ){
526 scale[i] = ( m_choosen_extent[i] + delta[i] ) / m_choosen_extent[i];
527 if( snapbbox && start[i] != 0.f ){
528 float snappdwidth = float_snapped( scale[i] * m_bounds.extents[i] * 2.f, GetSnapGridSize() );
529 scale[i] = snappdwidth / ( m_bounds.extents[i] * 2.f );
533 //globalOutputStream() << "pre snap scale: " << scale <<"\n";
535 float bestscale = scale[0];
536 for( std::size_t i = 1; i < 3; i++ ){
537 //if( fabs( 1.0f - fabs( scale[i] ) ) > fabs( 1.0f - fabs( bestscale ) ) ){
538 if( fabs( scale[i] ) > fabs( bestscale ) && scale[i] != 1.0f ){ //harder to scale down with this, but glitchier with upper one
539 bestscale = scale[i];
541 //globalOutputStream() << "bestscale: " << bestscale <<"\n";
543 for( std::size_t i = 0; i < 3; i++ ){
544 if( start[i] != 0.0f ){ // !!!!check grid == 0 case
545 scale[i] = ( scale[i] < 0.0f ) ? -fabs( bestscale ) : fabs( bestscale );
549 //globalOutputStream() << "scale: " << scale <<"\n";
550 m_scalable.scale( scale );
563 class RenderableClippedPrimitive : public OpenGLRenderable
567 PointVertex m_points[9];
571 std::vector<primitive_t> m_primitives;
575 void render( RenderStateFlags state ) const {
576 for ( std::size_t i = 0; i < m_primitives.size(); ++i )
578 glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_primitives[i].m_points[0].colour );
579 glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_primitives[i].m_points[0].vertex );
580 switch ( m_primitives[i].m_count )
583 case 2: glDrawArrays( GL_LINES, 0, GLsizei( m_primitives[i].m_count ) ); break;
584 default: glDrawArrays( GL_POLYGON, 0, GLsizei( m_primitives[i].m_count ) ); break;
589 void construct( const Matrix4& world2device ){
590 m_inverse = matrix4_full_inverse( world2device );
591 m_world = g_matrix4_identity;
594 void insert( const Vector4 clipped[9], std::size_t count ){
597 m_primitives.back().m_count = count;
598 for ( std::size_t i = 0; i < count; ++i )
600 Vector3 world_point( vector4_projected( matrix4_transformed_vector4( m_inverse, clipped[i] ) ) );
601 m_primitives.back().m_points[i].vertex = vertex3f_for_vector3( world_point );
606 m_primitives.clear();
610 m_primitives.push_back( primitive_t() );
612 const Colour4b colour_clipped( 255, 127, 0, 255 );
614 for ( std::size_t i = 0; i < 9; ++i )
615 m_primitives.back().m_points[i].colour = colour_clipped;
619 #if defined( _DEBUG )
620 #define DEBUG_SELECTION
623 #if defined( DEBUG_SELECTION )
624 Shader* g_state_clipped;
625 RenderableClippedPrimitive g_render_clipped;
630 // dist_Point_to_Line(): get the distance of a point to a line.
631 // Input: a Point P and a Line L (in any dimension)
632 // Return: the shortest distance from P to L
634 dist_Point_to_Line( Point P, Line L ){
635 Vector v = L.P1 - L.P0;
638 double c1 = dot( w,v );
639 double c2 = dot( v,v );
642 Point Pb = L.P0 + b * v;
649 typedef Vector3 point_type;
651 Segment3D( const point_type& _p0, const point_type& _p1 )
652 : p0( _p0 ), p1( _p1 ){
658 typedef Vector3 Point3D;
660 inline double vector3_distance_squared( const Point3D& a, const Point3D& b ){
661 return vector3_length_squared( b - a );
664 // get the distance of a point to a segment.
665 Point3D segment_closest_point_to_point( const Segment3D& segment, const Point3D& point ){
666 Vector3 v = segment.p1 - segment.p0;
667 Vector3 w = point - segment.p0;
669 double c1 = vector3_dot( w,v );
674 double c2 = vector3_dot( v,v );
679 return Point3D( segment.p0 + v * ( c1 / c2 ) );
682 double segment_dist_to_point_3d( const Segment3D& segment, const Point3D& point ){
683 return vector3_distance_squared( point, segment_closest_point_to_point( segment, point ) );
686 typedef Vector3 point_t;
687 typedef const Vector3* point_iterator_t;
689 // crossing number test for a point in a polygon
690 // This code is patterned after [Franklin, 2000]
691 bool point_test_polygon_2d( const point_t& P, point_iterator_t start, point_iterator_t finish ){
692 std::size_t crossings = 0;
694 // loop through all edges of the polygon
695 for ( point_iterator_t prev = finish - 1, cur = start; cur != finish; prev = cur, ++cur )
696 { // edge from (*prev) to (*cur)
697 if ( ( ( ( *prev )[1] <= P[1] ) && ( ( *cur )[1] > P[1] ) ) // an upward crossing
698 || ( ( ( *prev )[1] > P[1] ) && ( ( *cur )[1] <= P[1] ) ) ) { // a downward crossing
699 // compute the actual edge-ray intersect x-coordinate
700 float vt = (float)( P[1] - ( *prev )[1] ) / ( ( *cur )[1] - ( *prev )[1] );
701 if ( P[0] < ( *prev )[0] + vt * ( ( *cur )[0] - ( *prev )[0] ) ) { // P[0] < intersect
702 ++crossings; // a valid crossing of y=P[1] right of P[0]
706 return ( crossings & 0x1 ) != 0; // 0 if even (out), and 1 if odd (in)
709 inline double triangle_signed_area_XY( const Vector3& p0, const Vector3& p1, const Vector3& p2 ){
710 return ( ( p1[0] - p0[0] ) * ( p2[1] - p0[1] ) ) - ( ( p2[0] - p0[0] ) * ( p1[1] - p0[1] ) );
721 inline SelectionIntersection select_point_from_clipped( Vector4& clipped ){
722 return SelectionIntersection( clipped[2] / clipped[3], static_cast<float>( vector3_length_squared( Vector3( clipped[0] / clipped[3], clipped[1] / clipped[3], 0 ) ) ) );
725 void BestPoint( std::size_t count, Vector4 clipped[9], SelectionIntersection& best, clipcull_t cull ){
726 Vector3 normalised[9];
729 for ( std::size_t i = 0; i < count; ++i )
731 normalised[i][0] = clipped[i][0] / clipped[i][3];
732 normalised[i][1] = clipped[i][1] / clipped[i][3];
733 normalised[i][2] = clipped[i][2] / clipped[i][3];
737 if ( cull != eClipCullNone && count > 2 ) {
738 double signed_area = triangle_signed_area_XY( normalised[0], normalised[1], normalised[2] );
740 if ( ( cull == eClipCullCW && signed_area > 0 )
741 || ( cull == eClipCullCCW && signed_area < 0 ) ) {
747 Segment3D segment( normalised[0], normalised[1] );
748 Point3D point = segment_closest_point_to_point( segment, Vector3( 0, 0, 0 ) );
749 assign_if_closer( best, SelectionIntersection( point.z(), 0 ) );
751 else if ( count > 2 && !point_test_polygon_2d( Vector3( 0, 0, 0 ), normalised, normalised + count ) ) {
752 point_iterator_t end = normalised + count;
753 for ( point_iterator_t previous = end - 1, current = normalised; current != end; previous = current, ++current )
755 Segment3D segment( *previous, *current );
756 Point3D point = segment_closest_point_to_point( segment, Vector3( 0, 0, 0 ) );
757 float depth = point.z();
759 float distance = static_cast<float>( vector3_length_squared( point ) );
761 assign_if_closer( best, SelectionIntersection( depth, distance ) );
764 else if ( count > 2 ) {
767 SelectionIntersection(
768 static_cast<float>( ray_distance_to_plane(
769 Ray( Vector3( 0, 0, 0 ), Vector3( 0, 0, 1 ) ),
770 plane3_for_points( normalised[0], normalised[1], normalised[2] )
777 #if defined( DEBUG_SELECTION )
779 g_render_clipped.insert( clipped, count );
784 void LineStrip_BestPoint( const Matrix4& local2view, const PointVertex* vertices, const std::size_t size, SelectionIntersection& best ){
786 for ( std::size_t i = 0; ( i + 1 ) < size; ++i )
788 const std::size_t count = matrix4_clip_line( local2view, vertex3f_to_vector3( vertices[i].vertex ), vertex3f_to_vector3( vertices[i + 1].vertex ), clipped );
789 BestPoint( count, clipped, best, eClipCullNone );
793 void LineLoop_BestPoint( const Matrix4& local2view, const PointVertex* vertices, const std::size_t size, SelectionIntersection& best ){
795 for ( std::size_t i = 0; i < size; ++i )
797 const std::size_t count = matrix4_clip_line( local2view, vertex3f_to_vector3( vertices[i].vertex ), vertex3f_to_vector3( vertices[( i + 1 ) % size].vertex ), clipped );
798 BestPoint( count, clipped, best, eClipCullNone );
802 void Line_BestPoint( const Matrix4& local2view, const PointVertex vertices[2], SelectionIntersection& best ){
804 const std::size_t count = matrix4_clip_line( local2view, vertex3f_to_vector3( vertices[0].vertex ), vertex3f_to_vector3( vertices[1].vertex ), clipped );
805 BestPoint( count, clipped, best, eClipCullNone );
808 void Circle_BestPoint( const Matrix4& local2view, clipcull_t cull, const PointVertex* vertices, const std::size_t size, SelectionIntersection& best ){
810 for ( std::size_t i = 0; i < size; ++i )
812 const std::size_t count = matrix4_clip_triangle( local2view, g_vector3_identity, vertex3f_to_vector3( vertices[i].vertex ), vertex3f_to_vector3( vertices[( i + 1 ) % size].vertex ), clipped );
813 BestPoint( count, clipped, best, cull );
817 void Quad_BestPoint( const Matrix4& local2view, clipcull_t cull, const PointVertex* vertices, SelectionIntersection& best ){
820 const std::size_t count = matrix4_clip_triangle( local2view, vertex3f_to_vector3( vertices[0].vertex ), vertex3f_to_vector3( vertices[1].vertex ), vertex3f_to_vector3( vertices[3].vertex ), clipped );
821 BestPoint( count, clipped, best, cull );
824 const std::size_t count = matrix4_clip_triangle( local2view, vertex3f_to_vector3( vertices[1].vertex ), vertex3f_to_vector3( vertices[2].vertex ), vertex3f_to_vector3( vertices[3].vertex ), clipped );
825 BestPoint( count, clipped, best, cull );
829 struct FlatShadedVertex
840 typedef FlatShadedVertex* FlatShadedVertexIterator;
841 void Triangles_BestPoint( const Matrix4& local2view, clipcull_t cull, FlatShadedVertexIterator first, FlatShadedVertexIterator last, SelectionIntersection& best ){
842 for ( FlatShadedVertexIterator x( first ), y( first + 1 ), z( first + 2 ); x != last; x += 3, y += 3, z += 3 )
846 matrix4_clip_triangle(
848 reinterpret_cast<const Vector3&>( ( *x ).vertex ),
849 reinterpret_cast<const Vector3&>( ( *y ).vertex ),
850 reinterpret_cast<const Vector3&>( ( *z ).vertex ),
861 typedef std::multimap<SelectionIntersection, Selectable*> SelectableSortedSet;
863 class SelectionPool : public Selector
865 SelectableSortedSet m_pool;
866 SelectionIntersection m_intersection;
867 Selectable* m_selectable;
870 void pushSelectable( Selectable& selectable ){
871 m_intersection = SelectionIntersection();
872 m_selectable = &selectable;
874 void popSelectable(){
875 addSelectable( m_intersection, m_selectable );
876 m_intersection = SelectionIntersection();
878 void addIntersection( const SelectionIntersection& intersection ){
879 assign_if_closer( m_intersection, intersection );
881 void addSelectable( const SelectionIntersection& intersection, Selectable* selectable ){
882 if ( intersection.valid() ) {
883 m_pool.insert( SelectableSortedSet::value_type( intersection, selectable ) );
887 typedef SelectableSortedSet::iterator iterator;
890 return m_pool.begin();
897 return m_pool.empty();
902 const Colour4b g_colour_sphere( 0, 0, 0, 255 );
903 const Colour4b g_colour_screen( 0, 255, 255, 255 );
904 const Colour4b g_colour_selected( 255, 255, 0, 255 );
906 inline const Colour4b& colourSelected( const Colour4b& colour, bool selected ){
907 return ( selected ) ? g_colour_selected : colour;
910 template<typename remap_policy>
911 inline void draw_semicircle( const std::size_t segments, const float radius, PointVertex* vertices, remap_policy remap ){
912 const double increment = c_pi / double(segments << 2);
914 std::size_t count = 0;
917 remap_policy::set( vertices[segments << 2].vertex, -radius, 0, 0 );
918 while ( count < segments )
920 PointVertex* i = vertices + count;
921 PointVertex* j = vertices + ( ( segments << 1 ) - ( count + 1 ) );
923 PointVertex* k = i + ( segments << 1 );
924 PointVertex* l = j + ( segments << 1 );
927 PointVertex* m = i + ( segments << 2 );
928 PointVertex* n = j + ( segments << 2 );
929 PointVertex* o = k + ( segments << 2 );
930 PointVertex* p = l + ( segments << 2 );
933 remap_policy::set( i->vertex, x,-y, 0 );
934 remap_policy::set( k->vertex,-y,-x, 0 );
936 remap_policy::set( m->vertex,-x, y, 0 );
937 remap_policy::set( o->vertex, y, x, 0 );
943 const double theta = increment * count;
944 x = static_cast<float>( radius * cos( theta ) );
945 y = static_cast<float>( radius * sin( theta ) );
948 remap_policy::set( j->vertex, y,-x, 0 );
949 remap_policy::set( l->vertex,-x,-y, 0 );
951 remap_policy::set( n->vertex,-y, x, 0 );
952 remap_policy::set( p->vertex, x, y, 0 );
960 virtual Manipulatable* GetManipulatable() = 0;
961 virtual void testSelect( const View& view, const Matrix4& pivot2world ){
963 virtual void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ){
965 virtual void setSelected( bool select ) = 0;
966 virtual bool isSelected() const = 0;
970 inline Vector3 normalised_safe( const Vector3& self ){
971 if ( vector3_equal( self, g_vector3_identity ) ) {
972 return g_vector3_identity;
974 return vector3_normalised( self );
978 class RotateManipulator : public Manipulator
980 struct RenderableCircle : public OpenGLRenderable
982 Array<PointVertex> m_vertices;
984 RenderableCircle( std::size_t size ) : m_vertices( size ){
986 void render( RenderStateFlags state ) const {
987 glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_vertices.data()->colour );
988 glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_vertices.data()->vertex );
989 glDrawArrays( GL_LINE_LOOP, 0, GLsizei( m_vertices.size() ) );
991 void setColour( const Colour4b& colour ){
992 for ( Array<PointVertex>::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i )
994 ( *i ).colour = colour;
999 struct RenderableSemiCircle : public OpenGLRenderable
1001 Array<PointVertex> m_vertices;
1003 RenderableSemiCircle( std::size_t size ) : m_vertices( size ){
1005 void render( RenderStateFlags state ) const {
1006 glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_vertices.data()->colour );
1007 glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_vertices.data()->vertex );
1008 glDrawArrays( GL_LINE_STRIP, 0, GLsizei( m_vertices.size() ) );
1010 void setColour( const Colour4b& colour ){
1011 for ( Array<PointVertex>::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i )
1013 ( *i ).colour = colour;
1020 Vector3 m_axis_screen;
1021 RenderableSemiCircle m_circle_x;
1022 RenderableSemiCircle m_circle_y;
1023 RenderableSemiCircle m_circle_z;
1024 RenderableCircle m_circle_screen;
1025 RenderableCircle m_circle_sphere;
1026 SelectableBool m_selectable_x;
1027 SelectableBool m_selectable_y;
1028 SelectableBool m_selectable_z;
1029 SelectableBool m_selectable_screen;
1030 SelectableBool m_selectable_sphere;
1031 Pivot2World m_pivot;
1032 Matrix4 m_local2world_x;
1033 Matrix4 m_local2world_y;
1034 Matrix4 m_local2world_z;
1035 bool m_circle_x_visible;
1036 bool m_circle_y_visible;
1037 bool m_circle_z_visible;
1039 static Shader* m_state_outer;
1041 RotateManipulator( Rotatable& rotatable, std::size_t segments, float radius ) :
1042 m_free( rotatable ),
1043 m_axis( rotatable ),
1044 m_circle_x( ( segments << 2 ) + 1 ),
1045 m_circle_y( ( segments << 2 ) + 1 ),
1046 m_circle_z( ( segments << 2 ) + 1 ),
1047 m_circle_screen( segments << 3 ),
1048 m_circle_sphere( segments << 3 ){
1049 draw_semicircle( segments, radius, m_circle_x.m_vertices.data(), RemapYZX() );
1050 draw_semicircle( segments, radius, m_circle_y.m_vertices.data(), RemapZXY() );
1051 draw_semicircle( segments, radius, m_circle_z.m_vertices.data(), RemapXYZ() );
1053 draw_circle( segments, radius * 1.15f, m_circle_screen.m_vertices.data(), RemapXYZ() );
1054 draw_circle( segments, radius, m_circle_sphere.m_vertices.data(), RemapXYZ() );
1056 m_selectable_sphere.setSelected( true );
1060 void UpdateColours(){
1061 m_circle_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
1062 m_circle_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
1063 m_circle_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
1064 m_circle_screen.setColour( colourSelected( g_colour_screen, m_selectable_screen.isSelected() ) );
1065 m_circle_sphere.setColour( colourSelected( g_colour_sphere, false ) );
1068 void updateCircleTransforms(){
1069 Vector3 localViewpoint( matrix4_transformed_direction( matrix4_transposed( m_pivot.m_worldSpace ), vector4_to_vector3( m_pivot.m_viewpointSpace.z() ) ) );
1071 m_circle_x_visible = !vector3_equal_epsilon( g_vector3_axis_x, localViewpoint, 1e-6f );
1072 if ( m_circle_x_visible ) {
1073 m_local2world_x = g_matrix4_identity;
1074 vector4_to_vector3( m_local2world_x.y() ) = normalised_safe(
1075 vector3_cross( g_vector3_axis_x, localViewpoint )
1077 vector4_to_vector3( m_local2world_x.z() ) = normalised_safe(
1078 vector3_cross( vector4_to_vector3( m_local2world_x.x() ), vector4_to_vector3( m_local2world_x.y() ) )
1080 matrix4_premultiply_by_matrix4( m_local2world_x, m_pivot.m_worldSpace );
1083 m_circle_y_visible = !vector3_equal_epsilon( g_vector3_axis_y, localViewpoint, 1e-6f );
1084 if ( m_circle_y_visible ) {
1085 m_local2world_y = g_matrix4_identity;
1086 vector4_to_vector3( m_local2world_y.z() ) = normalised_safe(
1087 vector3_cross( g_vector3_axis_y, localViewpoint )
1089 vector4_to_vector3( m_local2world_y.x() ) = normalised_safe(
1090 vector3_cross( vector4_to_vector3( m_local2world_y.y() ), vector4_to_vector3( m_local2world_y.z() ) )
1092 matrix4_premultiply_by_matrix4( m_local2world_y, m_pivot.m_worldSpace );
1095 m_circle_z_visible = !vector3_equal_epsilon( g_vector3_axis_z, localViewpoint, 1e-6f );
1096 if ( m_circle_z_visible ) {
1097 m_local2world_z = g_matrix4_identity;
1098 vector4_to_vector3( m_local2world_z.x() ) = normalised_safe(
1099 vector3_cross( g_vector3_axis_z, localViewpoint )
1101 vector4_to_vector3( m_local2world_z.y() ) = normalised_safe(
1102 vector3_cross( vector4_to_vector3( m_local2world_z.z() ), vector4_to_vector3( m_local2world_z.x() ) )
1104 matrix4_premultiply_by_matrix4( m_local2world_z, m_pivot.m_worldSpace );
1108 void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ){
1109 m_pivot.update( pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
1110 updateCircleTransforms();
1115 renderer.SetState( m_state_outer, Renderer::eWireframeOnly );
1116 renderer.SetState( m_state_outer, Renderer::eFullMaterials );
1118 renderer.addRenderable( m_circle_screen, m_pivot.m_viewpointSpace );
1119 renderer.addRenderable( m_circle_sphere, m_pivot.m_viewpointSpace );
1121 if ( m_circle_x_visible ) {
1122 renderer.addRenderable( m_circle_x, m_local2world_x );
1124 if ( m_circle_y_visible ) {
1125 renderer.addRenderable( m_circle_y, m_local2world_y );
1127 if ( m_circle_z_visible ) {
1128 renderer.addRenderable( m_circle_z, m_local2world_z );
1131 void testSelect( const View& view, const Matrix4& pivot2world ){
1132 m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() );
1133 updateCircleTransforms();
1135 SelectionPool selector;
1139 Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_local2world_x ) );
1141 #if defined( DEBUG_SELECTION )
1142 g_render_clipped.construct( view.GetViewMatrix() );
1145 SelectionIntersection best;
1146 LineStrip_BestPoint( local2view, m_circle_x.m_vertices.data(), m_circle_x.m_vertices.size(), best );
1147 selector.addSelectable( best, &m_selectable_x );
1151 Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_local2world_y ) );
1153 #if defined( DEBUG_SELECTION )
1154 g_render_clipped.construct( view.GetViewMatrix() );
1157 SelectionIntersection best;
1158 LineStrip_BestPoint( local2view, m_circle_y.m_vertices.data(), m_circle_y.m_vertices.size(), best );
1159 selector.addSelectable( best, &m_selectable_y );
1163 Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_local2world_z ) );
1165 #if defined( DEBUG_SELECTION )
1166 g_render_clipped.construct( view.GetViewMatrix() );
1169 SelectionIntersection best;
1170 LineStrip_BestPoint( local2view, m_circle_z.m_vertices.data(), m_circle_z.m_vertices.size(), best );
1171 selector.addSelectable( best, &m_selectable_z );
1176 Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_viewpointSpace ) );
1179 SelectionIntersection best;
1180 LineLoop_BestPoint( local2view, m_circle_screen.m_vertices.data(), m_circle_screen.m_vertices.size(), best );
1181 selector.addSelectable( best, &m_selectable_screen );
1185 SelectionIntersection best;
1186 Circle_BestPoint( local2view, eClipCullCW, m_circle_sphere.m_vertices.data(), m_circle_sphere.m_vertices.size(), best );
1187 selector.addSelectable( best, &m_selectable_sphere );
1191 m_axis_screen = m_pivot.m_axis_screen;
1193 if ( !selector.failed() ) {
1194 ( *selector.begin() ).second->setSelected( true );
1198 Manipulatable* GetManipulatable(){
1199 if ( m_selectable_x.isSelected() ) {
1200 m_axis.SetAxis( g_vector3_axis_x );
1203 else if ( m_selectable_y.isSelected() ) {
1204 m_axis.SetAxis( g_vector3_axis_y );
1207 else if ( m_selectable_z.isSelected() ) {
1208 m_axis.SetAxis( g_vector3_axis_z );
1211 else if ( m_selectable_screen.isSelected() ) {
1212 m_axis.SetAxis( m_axis_screen );
1220 void setSelected( bool select ){
1221 m_selectable_x.setSelected( select );
1222 m_selectable_y.setSelected( select );
1223 m_selectable_z.setSelected( select );
1224 m_selectable_screen.setSelected( select );
1226 bool isSelected() const {
1227 return m_selectable_x.isSelected()
1228 | m_selectable_y.isSelected()
1229 | m_selectable_z.isSelected()
1230 | m_selectable_screen.isSelected()
1231 | m_selectable_sphere.isSelected();
1235 Shader* RotateManipulator::m_state_outer;
1238 const float arrowhead_length = 16;
1239 const float arrowhead_radius = 4;
1241 inline void draw_arrowline( const float length, PointVertex* line, const std::size_t axis ){
1242 ( *line++ ).vertex = vertex3f_identity;
1243 ( *line ).vertex = vertex3f_identity;
1244 vertex3f_to_array( ( *line ).vertex )[axis] = length - arrowhead_length;
1247 template<typename VertexRemap, typename NormalRemap>
1248 inline void draw_arrowhead( const std::size_t segments, const float length, FlatShadedVertex* vertices, VertexRemap, NormalRemap ){
1249 std::size_t head_tris = ( segments << 3 );
1250 const double head_segment = c_2pi / head_tris;
1251 for ( std::size_t i = 0; i < head_tris; ++i )
1254 FlatShadedVertex& point = vertices[i * 6 + 0];
1255 VertexRemap::x( point.vertex ) = length - arrowhead_length;
1256 VertexRemap::y( point.vertex ) = arrowhead_radius * static_cast<float>( cos( i * head_segment ) );
1257 VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast<float>( sin( i * head_segment ) );
1258 NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length;
1259 NormalRemap::y( point.normal ) = static_cast<float>( cos( i * head_segment ) );
1260 NormalRemap::z( point.normal ) = static_cast<float>( sin( i * head_segment ) );
1263 FlatShadedVertex& point = vertices[i * 6 + 1];
1264 VertexRemap::x( point.vertex ) = length;
1265 VertexRemap::y( point.vertex ) = 0;
1266 VertexRemap::z( point.vertex ) = 0;
1267 NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length;
1268 NormalRemap::y( point.normal ) = static_cast<float>( cos( ( i + 0.5 ) * head_segment ) );
1269 NormalRemap::z( point.normal ) = static_cast<float>( sin( ( i + 0.5 ) * head_segment ) );
1272 FlatShadedVertex& point = vertices[i * 6 + 2];
1273 VertexRemap::x( point.vertex ) = length - arrowhead_length;
1274 VertexRemap::y( point.vertex ) = arrowhead_radius * static_cast<float>( cos( ( i + 1 ) * head_segment ) );
1275 VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast<float>( sin( ( i + 1 ) * head_segment ) );
1276 NormalRemap::x( point.normal ) = arrowhead_radius / arrowhead_length;
1277 NormalRemap::y( point.normal ) = static_cast<float>( cos( ( i + 1 ) * head_segment ) );
1278 NormalRemap::z( point.normal ) = static_cast<float>( sin( ( i + 1 ) * head_segment ) );
1282 FlatShadedVertex& point = vertices[i * 6 + 3];
1283 VertexRemap::x( point.vertex ) = length - arrowhead_length;
1284 VertexRemap::y( point.vertex ) = 0;
1285 VertexRemap::z( point.vertex ) = 0;
1286 NormalRemap::x( point.normal ) = -1;
1287 NormalRemap::y( point.normal ) = 0;
1288 NormalRemap::z( point.normal ) = 0;
1291 FlatShadedVertex& point = vertices[i * 6 + 4];
1292 VertexRemap::x( point.vertex ) = length - arrowhead_length;
1293 VertexRemap::y( point.vertex ) = arrowhead_radius * static_cast<float>( cos( i * head_segment ) );
1294 VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast<float>( sin( i * head_segment ) );
1295 NormalRemap::x( point.normal ) = -1;
1296 NormalRemap::y( point.normal ) = 0;
1297 NormalRemap::z( point.normal ) = 0;
1300 FlatShadedVertex& point = vertices[i * 6 + 5];
1301 VertexRemap::x( point.vertex ) = length - arrowhead_length;
1302 VertexRemap::y( point.vertex ) = arrowhead_radius * static_cast<float>( cos( ( i + 1 ) * head_segment ) );
1303 VertexRemap::z( point.vertex ) = arrowhead_radius * static_cast<float>( sin( ( i + 1 ) * head_segment ) );
1304 NormalRemap::x( point.normal ) = -1;
1305 NormalRemap::y( point.normal ) = 0;
1306 NormalRemap::z( point.normal ) = 0;
1311 template<typename Triple>
1312 class TripleRemapXYZ
1315 static float& x( Triple& triple ){
1318 static float& y( Triple& triple ){
1321 static float& z( Triple& triple ){
1326 template<typename Triple>
1327 class TripleRemapYZX
1330 static float& x( Triple& triple ){
1333 static float& y( Triple& triple ){
1336 static float& z( Triple& triple ){
1341 template<typename Triple>
1342 class TripleRemapZXY
1345 static float& x( Triple& triple ){
1348 static float& y( Triple& triple ){
1351 static float& z( Triple& triple ){
1356 void vector3_print( const Vector3& v ){
1357 globalOutputStream() << "( " << v.x() << " " << v.y() << " " << v.z() << " )";
1360 class TranslateManipulator : public Manipulator
1362 struct RenderableArrowLine : public OpenGLRenderable
1364 PointVertex m_line[2];
1366 RenderableArrowLine(){
1368 void render( RenderStateFlags state ) const {
1369 glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_line[0].colour );
1370 glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_line[0].vertex );
1371 glDrawArrays( GL_LINES, 0, 2 );
1373 void setColour( const Colour4b& colour ){
1374 m_line[0].colour = colour;
1375 m_line[1].colour = colour;
1378 struct RenderableArrowHead : public OpenGLRenderable
1380 Array<FlatShadedVertex> m_vertices;
1382 RenderableArrowHead( std::size_t size )
1383 : m_vertices( size ){
1385 void render( RenderStateFlags state ) const {
1386 glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( FlatShadedVertex ), &m_vertices.data()->colour );
1387 glVertexPointer( 3, GL_FLOAT, sizeof( FlatShadedVertex ), &m_vertices.data()->vertex );
1388 glNormalPointer( GL_FLOAT, sizeof( FlatShadedVertex ), &m_vertices.data()->normal );
1389 glDrawArrays( GL_TRIANGLES, 0, GLsizei( m_vertices.size() ) );
1391 void setColour( const Colour4b& colour ){
1392 for ( Array<FlatShadedVertex>::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i )
1394 ( *i ).colour = colour;
1398 struct RenderableQuad : public OpenGLRenderable
1400 PointVertex m_quad[4];
1401 void render( RenderStateFlags state ) const {
1402 glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_quad[0].colour );
1403 glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_quad[0].vertex );
1404 glDrawArrays( GL_LINE_LOOP, 0, 4 );
1406 void setColour( const Colour4b& colour ){
1407 m_quad[0].colour = colour;
1408 m_quad[1].colour = colour;
1409 m_quad[2].colour = colour;
1410 m_quad[3].colour = colour;
1414 TranslateFree m_free;
1415 TranslateAxis m_axis;
1416 RenderableArrowLine m_arrow_x;
1417 RenderableArrowLine m_arrow_y;
1418 RenderableArrowLine m_arrow_z;
1419 RenderableArrowHead m_arrow_head_x;
1420 RenderableArrowHead m_arrow_head_y;
1421 RenderableArrowHead m_arrow_head_z;
1422 RenderableQuad m_quad_screen;
1423 SelectableBool m_selectable_x;
1424 SelectableBool m_selectable_y;
1425 SelectableBool m_selectable_z;
1426 SelectableBool m_selectable_screen;
1427 Pivot2World m_pivot;
1429 static Shader* m_state_wire;
1430 static Shader* m_state_fill;
1432 TranslateManipulator( Translatable& translatable, std::size_t segments, float length ) :
1433 m_free( translatable ),
1434 m_axis( translatable ),
1435 m_arrow_head_x( 3 * 2 * ( segments << 3 ) ),
1436 m_arrow_head_y( 3 * 2 * ( segments << 3 ) ),
1437 m_arrow_head_z( 3 * 2 * ( segments << 3 ) ){
1438 draw_arrowline( length, m_arrow_x.m_line, 0 );
1439 draw_arrowhead( segments, length, m_arrow_head_x.m_vertices.data(), TripleRemapXYZ<Vertex3f>(), TripleRemapXYZ<Normal3f>() );
1440 draw_arrowline( length, m_arrow_y.m_line, 1 );
1441 draw_arrowhead( segments, length, m_arrow_head_y.m_vertices.data(), TripleRemapYZX<Vertex3f>(), TripleRemapYZX<Normal3f>() );
1442 draw_arrowline( length, m_arrow_z.m_line, 2 );
1443 draw_arrowhead( segments, length, m_arrow_head_z.m_vertices.data(), TripleRemapZXY<Vertex3f>(), TripleRemapZXY<Normal3f>() );
1445 draw_quad( 16, m_quad_screen.m_quad );
1448 void UpdateColours(){
1449 m_arrow_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
1450 m_arrow_head_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
1451 m_arrow_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
1452 m_arrow_head_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
1453 m_arrow_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
1454 m_arrow_head_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
1455 m_quad_screen.setColour( colourSelected( g_colour_screen, m_selectable_screen.isSelected() ) );
1458 bool manipulator_show_axis( const Pivot2World& pivot, const Vector3& axis ){
1459 return fabs( vector3_dot( pivot.m_axis_screen, axis ) ) < 0.95;
1462 void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ){
1463 m_pivot.update( pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
1468 Vector3 x = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.x() ) );
1469 bool show_x = manipulator_show_axis( m_pivot, x );
1471 Vector3 y = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.y() ) );
1472 bool show_y = manipulator_show_axis( m_pivot, y );
1474 Vector3 z = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.z() ) );
1475 bool show_z = manipulator_show_axis( m_pivot, z );
1477 renderer.SetState( m_state_wire, Renderer::eWireframeOnly );
1478 renderer.SetState( m_state_wire, Renderer::eFullMaterials );
1481 renderer.addRenderable( m_arrow_x, m_pivot.m_worldSpace );
1484 renderer.addRenderable( m_arrow_y, m_pivot.m_worldSpace );
1487 renderer.addRenderable( m_arrow_z, m_pivot.m_worldSpace );
1490 renderer.addRenderable( m_quad_screen, m_pivot.m_viewplaneSpace );
1492 renderer.SetState( m_state_fill, Renderer::eWireframeOnly );
1493 renderer.SetState( m_state_fill, Renderer::eFullMaterials );
1496 renderer.addRenderable( m_arrow_head_x, m_pivot.m_worldSpace );
1499 renderer.addRenderable( m_arrow_head_y, m_pivot.m_worldSpace );
1502 renderer.addRenderable( m_arrow_head_z, m_pivot.m_worldSpace );
1505 void testSelect( const View& view, const Matrix4& pivot2world ){
1506 m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() );
1508 SelectionPool selector;
1510 Vector3 x = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.x() ) );
1511 bool show_x = manipulator_show_axis( m_pivot, x );
1513 Vector3 y = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.y() ) );
1514 bool show_y = manipulator_show_axis( m_pivot, y );
1516 Vector3 z = vector3_normalised( vector4_to_vector3( m_pivot.m_worldSpace.z() ) );
1517 bool show_z = manipulator_show_axis( m_pivot, z );
1520 Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_viewpointSpace ) );
1523 SelectionIntersection best;
1524 Quad_BestPoint( local2view, eClipCullCW, m_quad_screen.m_quad, best );
1525 if ( best.valid() ) {
1526 best = SelectionIntersection( 0, 0 );
1527 selector.addSelectable( best, &m_selectable_screen );
1533 Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_worldSpace ) );
1535 #if defined( DEBUG_SELECTION )
1536 g_render_clipped.construct( view.GetViewMatrix() );
1540 SelectionIntersection best;
1541 Line_BestPoint( local2view, m_arrow_x.m_line, best );
1542 Triangles_BestPoint( local2view, eClipCullCW, m_arrow_head_x.m_vertices.begin(), m_arrow_head_x.m_vertices.end(), best );
1543 selector.addSelectable( best, &m_selectable_x );
1547 SelectionIntersection best;
1548 Line_BestPoint( local2view, m_arrow_y.m_line, best );
1549 Triangles_BestPoint( local2view, eClipCullCW, m_arrow_head_y.m_vertices.begin(), m_arrow_head_y.m_vertices.end(), best );
1550 selector.addSelectable( best, &m_selectable_y );
1554 SelectionIntersection best;
1555 Line_BestPoint( local2view, m_arrow_z.m_line, best );
1556 Triangles_BestPoint( local2view, eClipCullCW, m_arrow_head_z.m_vertices.begin(), m_arrow_head_z.m_vertices.end(), best );
1557 selector.addSelectable( best, &m_selectable_z );
1561 if ( !selector.failed() ) {
1562 ( *selector.begin() ).second->setSelected( true );
1566 Manipulatable* GetManipulatable(){
1567 if ( m_selectable_x.isSelected() ) {
1568 m_axis.SetAxis( g_vector3_axis_x );
1571 else if ( m_selectable_y.isSelected() ) {
1572 m_axis.SetAxis( g_vector3_axis_y );
1575 else if ( m_selectable_z.isSelected() ) {
1576 m_axis.SetAxis( g_vector3_axis_z );
1585 void setSelected( bool select ){
1586 m_selectable_x.setSelected( select );
1587 m_selectable_y.setSelected( select );
1588 m_selectable_z.setSelected( select );
1589 m_selectable_screen.setSelected( select );
1591 bool isSelected() const {
1592 return m_selectable_x.isSelected()
1593 | m_selectable_y.isSelected()
1594 | m_selectable_z.isSelected()
1595 | m_selectable_screen.isSelected();
1599 Shader* TranslateManipulator::m_state_wire;
1600 Shader* TranslateManipulator::m_state_fill;
1602 class ScaleManipulator : public Manipulator
1604 struct RenderableArrow : public OpenGLRenderable
1606 PointVertex m_line[2];
1608 void render( RenderStateFlags state ) const {
1609 glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_line[0].colour );
1610 glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_line[0].vertex );
1611 glDrawArrays( GL_LINES, 0, 2 );
1613 void setColour( const Colour4b& colour ){
1614 m_line[0].colour = colour;
1615 m_line[1].colour = colour;
1618 struct RenderableQuad : public OpenGLRenderable
1620 PointVertex m_quad[4];
1621 void render( RenderStateFlags state ) const {
1622 glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_quad[0].colour );
1623 glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_quad[0].vertex );
1624 glDrawArrays( GL_QUADS, 0, 4 );
1626 void setColour( const Colour4b& colour ){
1627 m_quad[0].colour = colour;
1628 m_quad[1].colour = colour;
1629 m_quad[2].colour = colour;
1630 m_quad[3].colour = colour;
1636 RenderableArrow m_arrow_x;
1637 RenderableArrow m_arrow_y;
1638 RenderableArrow m_arrow_z;
1639 RenderableQuad m_quad_screen;
1640 SelectableBool m_selectable_x;
1641 SelectableBool m_selectable_y;
1642 SelectableBool m_selectable_z;
1643 SelectableBool m_selectable_screen;
1644 Pivot2World m_pivot;
1646 ScaleManipulator( Scalable& scalable, std::size_t segments, float length ) :
1649 draw_arrowline( length, m_arrow_x.m_line, 0 );
1650 draw_arrowline( length, m_arrow_y.m_line, 1 );
1651 draw_arrowline( length, m_arrow_z.m_line, 2 );
1653 draw_quad( 16, m_quad_screen.m_quad );
1656 Pivot2World& getPivot(){
1660 void UpdateColours(){
1661 m_arrow_x.setColour( colourSelected( g_colour_x, m_selectable_x.isSelected() ) );
1662 m_arrow_y.setColour( colourSelected( g_colour_y, m_selectable_y.isSelected() ) );
1663 m_arrow_z.setColour( colourSelected( g_colour_z, m_selectable_z.isSelected() ) );
1664 m_quad_screen.setColour( colourSelected( g_colour_screen, m_selectable_screen.isSelected() ) );
1667 void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ){
1668 m_pivot.update( pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() );
1673 renderer.addRenderable( m_arrow_x, m_pivot.m_worldSpace );
1674 renderer.addRenderable( m_arrow_y, m_pivot.m_worldSpace );
1675 renderer.addRenderable( m_arrow_z, m_pivot.m_worldSpace );
1677 renderer.addRenderable( m_quad_screen, m_pivot.m_viewpointSpace );
1679 void testSelect( const View& view, const Matrix4& pivot2world ){
1680 m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() );
1682 SelectionPool selector;
1685 Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_worldSpace ) );
1687 #if defined( DEBUG_SELECTION )
1688 g_render_clipped.construct( view.GetViewMatrix() );
1692 SelectionIntersection best;
1693 Line_BestPoint( local2view, m_arrow_x.m_line, best );
1694 selector.addSelectable( best, &m_selectable_x );
1698 SelectionIntersection best;
1699 Line_BestPoint( local2view, m_arrow_y.m_line, best );
1700 selector.addSelectable( best, &m_selectable_y );
1704 SelectionIntersection best;
1705 Line_BestPoint( local2view, m_arrow_z.m_line, best );
1706 selector.addSelectable( best, &m_selectable_z );
1711 Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_viewpointSpace ) );
1714 SelectionIntersection best;
1715 Quad_BestPoint( local2view, eClipCullCW, m_quad_screen.m_quad, best );
1716 selector.addSelectable( best, &m_selectable_screen );
1720 if ( !selector.failed() ) {
1721 ( *selector.begin() ).second->setSelected( true );
1725 Manipulatable* GetManipulatable(){
1726 if ( m_selectable_x.isSelected() ) {
1727 m_axis.SetAxis( g_vector3_axis_x );
1730 else if ( m_selectable_y.isSelected() ) {
1731 m_axis.SetAxis( g_vector3_axis_y );
1734 else if ( m_selectable_z.isSelected() ) {
1735 m_axis.SetAxis( g_vector3_axis_z );
1743 void setSelected( bool select ){
1744 m_selectable_x.setSelected( select );
1745 m_selectable_y.setSelected( select );
1746 m_selectable_z.setSelected( select );
1747 m_selectable_screen.setSelected( select );
1749 bool isSelected() const {
1750 return m_selectable_x.isSelected()
1751 | m_selectable_y.isSelected()
1752 | m_selectable_z.isSelected()
1753 | m_selectable_screen.isSelected();
1758 inline PlaneSelectable* Instance_getPlaneSelectable( scene::Instance& instance ){
1759 return InstanceTypeCast<PlaneSelectable>::cast( instance );
1762 class PlaneSelectableSelectPlanes : public scene::Graph::Walker
1764 Selector& m_selector;
1765 SelectionTest& m_test;
1766 PlaneCallback m_selectedPlaneCallback;
1768 PlaneSelectableSelectPlanes( Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback )
1769 : m_selector( selector ), m_test( test ), m_selectedPlaneCallback( selectedPlaneCallback ){
1771 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1772 if ( path.top().get().visible() ) {
1773 Selectable* selectable = Instance_getSelectable( instance );
1774 if ( selectable != 0 && selectable->isSelected() ) {
1775 PlaneSelectable* planeSelectable = Instance_getPlaneSelectable( instance );
1776 if ( planeSelectable != 0 ) {
1777 planeSelectable->selectPlanes( m_selector, m_test, m_selectedPlaneCallback );
1785 class PlaneSelectableSelectReversedPlanes : public scene::Graph::Walker
1787 Selector& m_selector;
1788 const SelectedPlanes& m_selectedPlanes;
1790 PlaneSelectableSelectReversedPlanes( Selector& selector, const SelectedPlanes& selectedPlanes )
1791 : m_selector( selector ), m_selectedPlanes( selectedPlanes ){
1793 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1794 if ( path.top().get().visible() ) {
1795 Selectable* selectable = Instance_getSelectable( instance );
1796 if ( selectable != 0 && selectable->isSelected() ) {
1797 PlaneSelectable* planeSelectable = Instance_getPlaneSelectable( instance );
1798 if ( planeSelectable != 0 ) {
1799 planeSelectable->selectReversedPlanes( m_selector, m_selectedPlanes );
1807 void Scene_forEachPlaneSelectable_selectPlanes( scene::Graph& graph, Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback ){
1808 graph.traverse( PlaneSelectableSelectPlanes( selector, test, selectedPlaneCallback ) );
1811 void Scene_forEachPlaneSelectable_selectReversedPlanes( scene::Graph& graph, Selector& selector, const SelectedPlanes& selectedPlanes ){
1812 graph.traverse( PlaneSelectableSelectReversedPlanes( selector, selectedPlanes ) );
1819 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 ){
1855 self.insert( plane );
1858 inline bool PlaneSet_contains( const PlaneSet& self, const Plane3& plane ){
1859 return self.find( plane ) != self.end();
1863 class SelectedPlaneSet : public SelectedPlanes
1865 PlaneSet m_selectedPlanes;
1867 bool empty() const {
1868 return m_selectedPlanes.empty();
1871 void insert( const Plane3& plane ){
1872 PlaneSet_insert( m_selectedPlanes, plane );
1874 bool contains( const Plane3& plane ) const {
1875 return PlaneSet_contains( m_selectedPlanes, plane );
1877 typedef MemberCaller1<SelectedPlaneSet, const Plane3&, &SelectedPlaneSet::insert> InsertCaller;
1881 bool Scene_forEachPlaneSelectable_selectPlanes( scene::Graph& graph, Selector& selector, SelectionTest& test ){
1882 SelectedPlaneSet selectedPlanes;
1884 Scene_forEachPlaneSelectable_selectPlanes( graph, selector, test, SelectedPlaneSet::InsertCaller( selectedPlanes ) );
1885 Scene_forEachPlaneSelectable_selectReversedPlanes( graph, selector, selectedPlanes );
1887 return !selectedPlanes.empty();
1893 class TestedBrushPlanesSelectVeritces : public scene::Graph::Walker
1895 SelectionTest& m_test;
1897 TestedBrushPlanesSelectVeritces( SelectionTest& test )
1900 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1901 if ( path.top().get().visible() ) {
1902 Selectable* selectable = Instance_getSelectable( instance );
1903 if ( selectable != 0 && selectable->isSelected() ) {
1904 BrushInstance* brushInstance = Instance_getBrush( instance );
1905 if ( brushInstance != 0 ) {
1906 brushInstance->selectVerticesOnPlanes( m_test );
1914 void Scene_forEachTestedBrushPlane_selectVertices( scene::Graph& graph, SelectionTest& test ){
1915 graph.traverse( TestedBrushPlanesSelectVeritces( test ) );
1918 class BrushPlanesSelectVeritces : public scene::Graph::Walker
1920 SelectionTest& m_test;
1922 BrushPlanesSelectVeritces( SelectionTest& test )
1925 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1926 if ( path.top().get().visible() ) {
1927 Selectable* selectable = Instance_getSelectable( instance );
1928 if ( selectable != 0 && selectable->isSelected() ) {
1929 BrushInstance* brushInstance = Instance_getBrush( instance );
1930 if ( brushInstance != 0 ) {
1931 brushInstance->selectVerticesOnPlanes( m_test );
1939 void Scene_forEachBrushPlane_selectVertices( scene::Graph& graph, SelectionTest& test ){
1940 graph.traverse( BrushPlanesSelectVeritces( test ) );
1943 void Scene_Translate_Component_Selected( scene::Graph& graph, const Vector3& translation );
1944 void Scene_Translate_Selected( scene::Graph& graph, const Vector3& translation );
1945 void Scene_TestSelect_Primitive( Selector& selector, SelectionTest& test, const VolumeTest& volume );
1946 void Scene_TestSelect_Component( Selector& selector, SelectionTest& test, const VolumeTest& volume, SelectionSystem::EComponentMode componentMode );
1947 void Scene_TestSelect_Component_Selected( Selector& selector, SelectionTest& test, const VolumeTest& volume, SelectionSystem::EComponentMode componentMode );
1948 void Scene_SelectAll_Component( bool select, SelectionSystem::EComponentMode componentMode );
1950 class ResizeTranslatable : public Translatable
1952 void translate( const Vector3& translation ){
1953 Scene_Translate_Component_Selected( GlobalSceneGraph(), translation );
1957 class DragTranslatable : public Translatable
1959 void translate( const Vector3& translation ){
1960 if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ) {
1961 Scene_Translate_Component_Selected( GlobalSceneGraph(), translation );
1965 Scene_Translate_Selected( GlobalSceneGraph(), translation );
1970 class SelectionVolume : public SelectionTest
1972 Matrix4 m_local2view;
1978 SelectionVolume( const View& view )
1982 const VolumeTest& getVolume() const {
1986 const Vector3& getNear() const {
1989 const Vector3& getFar() const {
1993 void BeginMesh( const Matrix4& localToWorld, bool twoSided ){
1994 m_local2view = matrix4_multiplied_by_matrix4( m_view.GetViewMatrix(), localToWorld );
1996 // Cull back-facing polygons based on winding being clockwise or counter-clockwise.
1997 // Don't cull if the view is wireframe and the polygons are two-sided.
1998 m_cull = twoSided && !m_view.fill() ? eClipCullNone : ( matrix4_handedness( localToWorld ) == MATRIX4_RIGHTHANDED ) ? eClipCullCW : eClipCullCCW;
2001 Matrix4 screen2world( matrix4_full_inverse( m_local2view ) );
2003 m_near = vector4_projected(
2004 matrix4_transformed_vector4(
2006 Vector4( 0, 0, -1, 1 )
2010 m_far = vector4_projected(
2011 matrix4_transformed_vector4(
2013 Vector4( 0, 0, 1, 1 )
2018 #if defined( DEBUG_SELECTION )
2019 g_render_clipped.construct( m_view.GetViewMatrix() );
2022 void TestPoint( const Vector3& point, SelectionIntersection& best ){
2024 if ( matrix4_clip_point( m_local2view, point, clipped ) == c_CLIP_PASS ) {
2025 best = select_point_from_clipped( clipped );
2028 void TestPolygon( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ){
2030 for ( std::size_t i = 0; i + 2 < count; ++i )
2033 matrix4_clip_triangle(
2035 reinterpret_cast<const Vector3&>( vertices[0] ),
2036 reinterpret_cast<const Vector3&>( vertices[i + 1] ),
2037 reinterpret_cast<const Vector3&>( vertices[i + 2] ),
2046 void TestLineLoop( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ){
2051 for ( VertexPointer::iterator i = vertices.begin(), end = i + count, prev = i + ( count - 1 ); i != end; prev = i, ++i )
2056 reinterpret_cast<const Vector3&>( ( *prev ) ),
2057 reinterpret_cast<const Vector3&>( ( *i ) ),
2066 void TestLineStrip( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ){
2071 for ( VertexPointer::iterator i = vertices.begin(), end = i + count, next = i + 1; next != end; i = next, ++next )
2076 reinterpret_cast<const Vector3&>( ( *i ) ),
2077 reinterpret_cast<const Vector3&>( ( *next ) ),
2086 void TestLines( const VertexPointer& vertices, std::size_t count, SelectionIntersection& best ){
2091 for ( VertexPointer::iterator i = vertices.begin(), end = i + count; i != end; i += 2 )
2096 reinterpret_cast<const Vector3&>( ( *i ) ),
2097 reinterpret_cast<const Vector3&>( ( *( i + 1 ) ) ),
2106 void TestTriangles( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ){
2108 for ( IndexPointer::iterator i( indices.begin() ); i != indices.end(); i += 3 )
2111 matrix4_clip_triangle(
2113 reinterpret_cast<const Vector3&>( vertices[*i] ),
2114 reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
2115 reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
2124 void TestQuads( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ){
2126 for ( IndexPointer::iterator i( indices.begin() ); i != indices.end(); i += 4 )
2129 matrix4_clip_triangle(
2131 reinterpret_cast<const Vector3&>( vertices[*i] ),
2132 reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
2133 reinterpret_cast<const Vector3&>( vertices[*( i + 3 )] ),
2141 matrix4_clip_triangle(
2143 reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
2144 reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
2145 reinterpret_cast<const Vector3&>( vertices[*( i + 3 )] ),
2154 void TestQuadStrip( const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best ){
2156 for ( IndexPointer::iterator i( indices.begin() ); i + 2 != indices.end(); i += 2 )
2159 matrix4_clip_triangle(
2161 reinterpret_cast<const Vector3&>( vertices[*i] ),
2162 reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
2163 reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
2171 matrix4_clip_triangle(
2173 reinterpret_cast<const Vector3&>( vertices[*( i + 2 )] ),
2174 reinterpret_cast<const Vector3&>( vertices[*( i + 1 )] ),
2175 reinterpret_cast<const Vector3&>( vertices[*( i + 3 )] ),
2186 class SelectionCounter
2189 typedef const Selectable& first_argument_type;
2191 SelectionCounter( const SelectionChangeCallback& onchanged )
2192 : m_count( 0 ), m_onchanged( onchanged ){
2194 void operator()( const Selectable& selectable ){
2195 if ( selectable.isSelected() ) {
2200 ASSERT_MESSAGE( m_count != 0, "selection counter underflow" );
2204 m_onchanged( selectable );
2206 bool empty() const {
2207 return m_count == 0;
2209 std::size_t size() const {
2213 std::size_t m_count;
2214 SelectionChangeCallback m_onchanged;
2217 inline void ConstructSelectionTest( View& view, const rect_t selection_box ){
2218 view.EnableScissor( selection_box.min[0], selection_box.max[0], selection_box.min[1], selection_box.max[1] );
2221 inline const rect_t SelectionBoxForPoint( const float device_point[2], const float device_epsilon[2] ){
2222 rect_t selection_box;
2223 selection_box.min[0] = device_point[0] - device_epsilon[0];
2224 selection_box.min[1] = device_point[1] - device_epsilon[1];
2225 selection_box.max[0] = device_point[0] + device_epsilon[0];
2226 selection_box.max[1] = device_point[1] + device_epsilon[1];
2227 return selection_box;
2230 inline const rect_t SelectionBoxForArea( const float device_point[2], const float device_delta[2] ){
2231 rect_t selection_box;
2232 selection_box.min[0] = ( device_delta[0] < 0 ) ? ( device_point[0] + device_delta[0] ) : ( device_point[0] );
2233 selection_box.min[1] = ( device_delta[1] < 0 ) ? ( device_point[1] + device_delta[1] ) : ( device_point[1] );
2234 selection_box.max[0] = ( device_delta[0] > 0 ) ? ( device_point[0] + device_delta[0] ) : ( device_point[0] );
2235 selection_box.max[1] = ( device_delta[1] > 0 ) ? ( device_point[1] + device_delta[1] ) : ( device_point[1] );
2236 return selection_box;
2239 Quaternion construct_local_rotation( const Quaternion& world, const Quaternion& localToWorld ){
2240 return quaternion_normalised( quaternion_multiplied_by_quaternion(
2241 quaternion_normalised( quaternion_multiplied_by_quaternion(
2242 quaternion_inverse( localToWorld ),
2249 inline void matrix4_assign_rotation( Matrix4& matrix, const Matrix4& other ){
2250 matrix[0] = other[0];
2251 matrix[1] = other[1];
2252 matrix[2] = other[2];
2253 matrix[4] = other[4];
2254 matrix[5] = other[5];
2255 matrix[6] = other[6];
2256 matrix[8] = other[8];
2257 matrix[9] = other[9];
2258 matrix[10] = other[10];
2261 void matrix4_assign_rotation_for_pivot( Matrix4& matrix, scene::Instance& instance ){
2262 Editable* editable = Node_getEditable( instance.path().top() );
2263 if ( editable != 0 ) {
2264 matrix4_assign_rotation( matrix, matrix4_multiplied_by_matrix4( instance.localToWorld(), editable->getLocalPivot() ) );
2268 matrix4_assign_rotation( matrix, instance.localToWorld() );
2272 inline bool Instance_isSelectedComponents( scene::Instance& instance ){
2273 ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance );
2274 return componentSelectionTestable != 0
2275 && componentSelectionTestable->isSelectedComponents();
2278 class TranslateSelected : public SelectionSystem::Visitor
2280 const Vector3& m_translate;
2282 TranslateSelected( const Vector3& translate )
2283 : m_translate( translate ){
2285 void visit( scene::Instance& instance ) const {
2286 Transformable* transform = Instance_getTransformable( instance );
2287 if ( transform != 0 ) {
2288 transform->setType( TRANSFORM_PRIMITIVE );
2289 transform->setTranslation( m_translate );
2294 void Scene_Translate_Selected( scene::Graph& graph, const Vector3& translation ){
2295 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2296 GlobalSelectionSystem().foreachSelected( TranslateSelected( translation ) );
2300 Vector3 get_local_pivot( const Vector3& world_pivot, const Matrix4& localToWorld ){
2302 matrix4_transformed_point(
2303 matrix4_full_inverse( localToWorld ),
2309 void translation_for_pivoted_matrix_transform( Vector3& parent_translation, const Matrix4& local_transform, const Vector3& world_pivot, const Matrix4& localToWorld, const Matrix4& localToParent ){
2310 // we need a translation inside the parent system to move the origin of this object to the right place
2312 // mathematically, it must fulfill:
2314 // local_translation local_transform local_pivot = local_pivot
2315 // local_translation = local_pivot - local_transform local_pivot
2318 // local_transform local_translation local_pivot = local_pivot
2319 // local_translation local_pivot = local_transform^-1 local_pivot
2320 // local_translation + local_pivot = local_transform^-1 local_pivot
2321 // local_translation = local_transform^-1 local_pivot - local_pivot
2323 Vector3 local_pivot( get_local_pivot( world_pivot, localToWorld ) );
2325 Vector3 local_translation(
2328 matrix4_transformed_point(
2333 matrix4_transformed_point(
2334 matrix4_full_inverse(local_transform),
2342 translation_local2object( parent_translation, local_translation, localToParent );
2346 globalOutputStream() << "World pivot is at " << world_pivot << "\n";
2347 globalOutputStream() << "Local pivot is at " << local_pivot << "\n";
2348 globalOutputStream() << "Transformation " << local_transform << " moves it to: " << matrix4_transformed_point(local_transform, local_pivot) << "\n";
2349 globalOutputStream() << "Must move by " << local_translation << " in the local system" << "\n";
2350 globalOutputStream() << "Must move by " << parent_translation << " in the parent system" << "\n";
2354 void translation_for_pivoted_rotation( Vector3& parent_translation, const Quaternion& local_rotation, const Vector3& world_pivot, const Matrix4& localToWorld, const Matrix4& localToParent ){
2355 translation_for_pivoted_matrix_transform( parent_translation, matrix4_rotation_for_quaternion_quantised( local_rotation ), world_pivot, localToWorld, localToParent );
2358 void translation_for_pivoted_scale( Vector3& parent_translation, const Vector3& world_scale, const Vector3& world_pivot, const Matrix4& localToWorld, const Matrix4& localToParent ){
2359 Matrix4 local_transform(
2360 matrix4_multiplied_by_matrix4(
2361 matrix4_full_inverse( localToWorld ),
2362 matrix4_multiplied_by_matrix4(
2363 matrix4_scale_for_vec3( world_scale ),
2368 local_transform.tx() = local_transform.ty() = local_transform.tz() = 0; // cancel translation parts
2369 translation_for_pivoted_matrix_transform( parent_translation, local_transform, world_pivot, localToWorld, localToParent );
2372 class rotate_selected : public SelectionSystem::Visitor
2374 const Quaternion& m_rotate;
2375 const Vector3& m_world_pivot;
2377 rotate_selected( const Quaternion& rotation, const Vector3& world_pivot )
2378 : m_rotate( rotation ), m_world_pivot( world_pivot ){
2380 void visit( scene::Instance& instance ) const {
2381 TransformNode* transformNode = Node_getTransformNode( instance.path().top() );
2382 if ( transformNode != 0 ) {
2383 Transformable* transform = Instance_getTransformable( instance );
2384 if ( transform != 0 ) {
2385 transform->setType( TRANSFORM_PRIMITIVE );
2386 transform->setScale( c_scale_identity );
2387 transform->setTranslation( c_translation_identity );
2389 transform->setType( TRANSFORM_PRIMITIVE );
2390 transform->setRotation( m_rotate );
2393 Editable* editable = Node_getEditable( instance.path().top() );
2394 const Matrix4& localPivot = editable != 0 ? editable->getLocalPivot() : g_matrix4_identity;
2396 Vector3 parent_translation;
2397 translation_for_pivoted_rotation(
2401 matrix4_multiplied_by_matrix4( instance.localToWorld(), localPivot ),
2402 matrix4_multiplied_by_matrix4( transformNode->localToParent(), localPivot )
2405 transform->setTranslation( parent_translation );
2412 void Scene_Rotate_Selected( scene::Graph& graph, const Quaternion& rotation, const Vector3& world_pivot ){
2413 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2414 GlobalSelectionSystem().foreachSelected( rotate_selected( rotation, world_pivot ) );
2418 class scale_selected : public SelectionSystem::Visitor
2420 const Vector3& m_scale;
2421 const Vector3& m_world_pivot;
2423 scale_selected( const Vector3& scaling, const Vector3& world_pivot )
2424 : m_scale( scaling ), m_world_pivot( world_pivot ){
2426 void visit( scene::Instance& instance ) const {
2427 TransformNode* transformNode = Node_getTransformNode( instance.path().top() );
2428 if ( transformNode != 0 ) {
2429 Transformable* transform = Instance_getTransformable( instance );
2430 if ( transform != 0 ) {
2431 transform->setType( TRANSFORM_PRIMITIVE );
2432 transform->setScale( c_scale_identity );
2433 transform->setTranslation( c_translation_identity );
2435 transform->setType( TRANSFORM_PRIMITIVE );
2436 transform->setScale( m_scale );
2438 Editable* editable = Node_getEditable( instance.path().top() );
2439 const Matrix4& localPivot = editable != 0 ? editable->getLocalPivot() : g_matrix4_identity;
2441 Vector3 parent_translation;
2442 translation_for_pivoted_scale(
2446 matrix4_multiplied_by_matrix4( instance.localToWorld(), localPivot ),
2447 matrix4_multiplied_by_matrix4( transformNode->localToParent(), localPivot )
2450 transform->setTranslation( parent_translation );
2457 void Scene_Scale_Selected( scene::Graph& graph, const Vector3& scaling, const Vector3& world_pivot ){
2458 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2459 GlobalSelectionSystem().foreachSelected( scale_selected( scaling, world_pivot ) );
2464 class translate_component_selected : public SelectionSystem::Visitor
2466 const Vector3& m_translate;
2468 translate_component_selected( const Vector3& translate )
2469 : m_translate( translate ){
2471 void visit( scene::Instance& instance ) const {
2472 Transformable* transform = Instance_getTransformable( instance );
2473 if ( transform != 0 ) {
2474 transform->setType( TRANSFORM_COMPONENT );
2475 transform->setTranslation( m_translate );
2480 void Scene_Translate_Component_Selected( scene::Graph& graph, const Vector3& translation ){
2481 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2482 GlobalSelectionSystem().foreachSelectedComponent( translate_component_selected( translation ) );
2486 class rotate_component_selected : public SelectionSystem::Visitor
2488 const Quaternion& m_rotate;
2489 const Vector3& m_world_pivot;
2491 rotate_component_selected( const Quaternion& rotation, const Vector3& world_pivot )
2492 : m_rotate( rotation ), m_world_pivot( world_pivot ){
2494 void visit( scene::Instance& instance ) const {
2495 Transformable* transform = Instance_getTransformable( instance );
2496 if ( transform != 0 ) {
2497 Vector3 parent_translation;
2498 translation_for_pivoted_rotation( parent_translation, m_rotate, m_world_pivot, instance.localToWorld(), Node_getTransformNode( instance.path().top() )->localToParent() );
2500 transform->setType( TRANSFORM_COMPONENT );
2501 transform->setRotation( m_rotate );
2502 transform->setTranslation( parent_translation );
2507 void Scene_Rotate_Component_Selected( scene::Graph& graph, const Quaternion& rotation, const Vector3& world_pivot ){
2508 if ( GlobalSelectionSystem().countSelectedComponents() != 0 ) {
2509 GlobalSelectionSystem().foreachSelectedComponent( rotate_component_selected( rotation, world_pivot ) );
2513 class scale_component_selected : public SelectionSystem::Visitor
2515 const Vector3& m_scale;
2516 const Vector3& m_world_pivot;
2518 scale_component_selected( const Vector3& scaling, const Vector3& world_pivot )
2519 : m_scale( scaling ), m_world_pivot( world_pivot ){
2521 void visit( scene::Instance& instance ) const {
2522 Transformable* transform = Instance_getTransformable( instance );
2523 if ( transform != 0 ) {
2524 Vector3 parent_translation;
2525 translation_for_pivoted_scale( parent_translation, m_scale, m_world_pivot, instance.localToWorld(), Node_getTransformNode( instance.path().top() )->localToParent() );
2527 transform->setType( TRANSFORM_COMPONENT );
2528 transform->setScale( m_scale );
2529 transform->setTranslation( parent_translation );
2534 void Scene_Scale_Component_Selected( scene::Graph& graph, const Vector3& scaling, const Vector3& world_pivot ){
2535 if ( GlobalSelectionSystem().countSelectedComponents() != 0 ) {
2536 GlobalSelectionSystem().foreachSelectedComponent( scale_component_selected( scaling, world_pivot ) );
2541 class BooleanSelector : public Selector
2544 SelectionIntersection m_intersection;
2545 Selectable* m_selectable;
2547 BooleanSelector() : m_selected( false ){
2550 void pushSelectable( Selectable& selectable ){
2551 m_intersection = SelectionIntersection();
2552 m_selectable = &selectable;
2554 void popSelectable(){
2555 if ( m_intersection.valid() ) {
2558 m_intersection = SelectionIntersection();
2560 void addIntersection( const SelectionIntersection& intersection ){
2561 if ( m_selectable->isSelected() ) {
2562 assign_if_closer( m_intersection, intersection );
2571 class BestSelector : public Selector
2573 SelectionIntersection m_intersection;
2574 Selectable* m_selectable;
2575 SelectionIntersection m_bestIntersection;
2576 std::list<Selectable*> m_bestSelectable;
2578 BestSelector() : m_bestIntersection( SelectionIntersection() ), m_bestSelectable( 0 ){
2581 void pushSelectable( Selectable& selectable ){
2582 m_intersection = SelectionIntersection();
2583 m_selectable = &selectable;
2585 void popSelectable(){
2586 if ( m_intersection.equalEpsilon( m_bestIntersection, 0.25f, 0.001f ) ) {
2587 m_bestSelectable.push_back( m_selectable );
2588 m_bestIntersection = m_intersection;
2590 else if ( m_intersection < m_bestIntersection ) {
2591 m_bestSelectable.clear();
2592 m_bestSelectable.push_back( m_selectable );
2593 m_bestIntersection = m_intersection;
2595 m_intersection = SelectionIntersection();
2597 void addIntersection( const SelectionIntersection& intersection ){
2598 assign_if_closer( m_intersection, intersection );
2601 std::list<Selectable*>& best(){
2602 return m_bestSelectable;
2606 class DeepBestSelector : public Selector
2608 SelectionIntersection m_intersection;
2609 Selectable* m_selectable;
2610 SelectionIntersection m_bestIntersection;
2611 std::list<Selectable*> m_bestSelectable;
2613 DeepBestSelector() : m_bestIntersection( SelectionIntersection() ), m_bestSelectable( 0 ){
2616 void pushSelectable( Selectable& selectable ){
2617 m_intersection = SelectionIntersection();
2618 m_selectable = &selectable;
2620 void popSelectable(){
2621 if ( m_intersection.equalEpsilon( m_bestIntersection, 0.25f, 2.f ) ) {
2622 m_bestSelectable.push_back( m_selectable );
2623 m_bestIntersection = m_intersection;
2625 else if ( m_intersection < m_bestIntersection ) {
2626 m_bestSelectable.clear();
2627 m_bestSelectable.push_back( m_selectable );
2628 m_bestIntersection = m_intersection;
2630 m_intersection = SelectionIntersection();
2632 void addIntersection( const SelectionIntersection& intersection ){
2633 assign_if_closer( m_intersection, intersection );
2636 std::list<Selectable*>& best(){
2637 return m_bestSelectable;
2641 bool g_bAltDragManipulatorResize = false; //+select primitives in component modes
2642 bool g_bTmpComponentMode = false;
2644 class DragManipulator : public Manipulator
2646 TranslateFree m_freeResize;
2647 TranslateFree m_freeDrag;
2648 ResizeTranslatable m_resize;
2649 DragTranslatable m_drag;
2650 SelectableBool m_dragSelectable; //drag already selected stuff
2653 bool m_selected; //selected temporally for drag
2655 DragManipulator() : m_freeResize( m_resize ), m_freeDrag( m_drag ), m_selected( false ){
2658 Manipulatable* GetManipulatable(){
2659 return m_dragSelectable.isSelected() ? &m_freeDrag : &m_freeResize;
2662 void testSelect( const View& view, const Matrix4& pivot2world ){
2663 SelectionPool selector;
2665 SelectionVolume test( view );
2667 if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
2668 BooleanSelector booleanSelector;
2670 Scene_TestSelect_Primitive( booleanSelector, test, view );
2672 if ( booleanSelector.isSelected() ) {
2673 if( g_bAltDragManipulatorResize ){
2674 DeepBestSelector deepSelector;
2675 Scene_TestSelect_Component_Selected( deepSelector, test, view, SelectionSystem::eVertex );
2676 for ( std::list<Selectable*>::iterator i = deepSelector.best().begin(); i != deepSelector.best().end(); ++i )
2678 if ( !( *i )->isSelected() ) {
2679 GlobalSelectionSystem().setSelectedAllComponents( false );
2681 selector.addSelectable( SelectionIntersection( 0, 0 ), ( *i ) );
2683 m_dragSelectable.setSelected( false );
2685 if( deepSelector.best().empty() ){
2686 //Scene_forEachTestedBrushPlane_selectVertices( GlobalSceneGraph(), test ); //todo? drag clicked face
2687 Scene_forEachBrushPlane_selectVertices( GlobalSceneGraph(), test );
2692 selector.addSelectable( SelectionIntersection( 0, 0 ), &m_dragSelectable );
2698 if( g_bAltDragManipulatorResize ){
2699 Scene_forEachBrushPlane_selectVertices( GlobalSceneGraph(), test );
2703 m_selected = Scene_forEachPlaneSelectable_selectPlanes( GlobalSceneGraph(), selector, test );
2709 BestSelector bestSelector;
2710 Scene_TestSelect_Component_Selected( bestSelector, test, view, GlobalSelectionSystem().ComponentMode() );
2711 for ( std::list<Selectable*>::iterator i = bestSelector.best().begin(); i != bestSelector.best().end(); ++i )
2713 if ( !( *i )->isSelected() ) {
2714 GlobalSelectionSystem().setSelectedAllComponents( false );
2717 selector.addSelectable( SelectionIntersection( 0, 0 ), ( *i ) );
2718 m_dragSelectable.setSelected( true );
2720 if( GlobalSelectionSystem().countSelectedComponents() != 0 ){
2721 m_dragSelectable.setSelected( true );
2725 for ( SelectionPool::iterator i = selector.begin(); i != selector.end(); ++i )
2727 ( *i ).second->setSelected( true );
2729 g_bTmpComponentMode = m_selected;
2732 void setSelected( bool select ){
2733 m_selected = select;
2734 m_dragSelectable.setSelected( select );
2736 bool isSelected() const {
2737 return m_selected || m_dragSelectable.isSelected();
2741 class ClipManipulator : public Manipulator
2745 Manipulatable* GetManipulatable(){
2746 ERROR_MESSAGE( "clipper is not manipulatable" );
2750 void setSelected( bool select ){
2752 bool isSelected() const {
2757 class select_all : public scene::Graph::Walker
2761 select_all( bool select )
2762 : m_select( select ){
2764 bool pre( const scene::Path& path, scene::Instance& instance ) const {
2765 Selectable* selectable = Instance_getSelectable( instance );
2766 if ( selectable != 0 ) {
2767 selectable->setSelected( m_select );
2773 class select_all_component : public scene::Graph::Walker
2776 SelectionSystem::EComponentMode m_mode;
2778 select_all_component( bool select, SelectionSystem::EComponentMode mode )
2779 : m_select( select ), m_mode( mode ){
2781 bool pre( const scene::Path& path, scene::Instance& instance ) const {
2782 ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance );
2783 if ( componentSelectionTestable ) {
2784 componentSelectionTestable->setSelectedComponents( m_select, m_mode );
2790 void Scene_SelectAll_Component( bool select, SelectionSystem::EComponentMode componentMode ){
2791 GlobalSceneGraph().traverse( select_all_component( select, componentMode ) );
2795 // RadiantSelectionSystem
2796 class RadiantSelectionSystem :
2797 public SelectionSystem,
2798 public Translatable,
2803 mutable Matrix4 m_pivot2world;
2804 Matrix4 m_pivot2world_start;
2805 Matrix4 m_manip2pivot_start;
2806 Translation m_translation;
2807 Rotation m_rotation;
2810 static Shader* m_state;
2811 bool m_bPreferPointEntsIn2D;
2813 EManipulatorMode m_manipulator_mode;
2814 Manipulator* m_manipulator;
2819 EComponentMode m_componentmode;
2821 SelectionCounter m_count_primitive;
2822 SelectionCounter m_count_component;
2824 TranslateManipulator m_translate_manipulator;
2825 RotateManipulator m_rotate_manipulator;
2826 ScaleManipulator m_scale_manipulator;
2827 DragManipulator m_drag_manipulator;
2828 ClipManipulator m_clip_manipulator;
2830 typedef SelectionList<scene::Instance> selection_t;
2831 selection_t m_selection;
2832 selection_t m_component_selection;
2834 Signal1<const Selectable&> m_selectionChanged_callbacks;
2836 void ConstructPivot() const;
2837 void setCustomPivotOrigin( Vector3& point ) const;
2839 AABB getSelectionAABB() const;
2841 mutable bool m_pivotChanged;
2842 bool m_pivot_moving;
2843 mutable bool m_pivotIsCustom;
2845 void Scene_TestSelect( Selector& selector, SelectionTest& test, const View& view, SelectionSystem::EMode mode, SelectionSystem::EComponentMode componentMode );
2847 bool nothingSelected() const {
2848 return ( Mode() == eComponent && m_count_component.empty() )
2849 || ( Mode() == ePrimitive && m_count_primitive.empty() );
2864 RadiantSelectionSystem() :
2865 m_bPreferPointEntsIn2D( true ),
2866 m_undo_begun( false ),
2867 m_mode( ePrimitive ),
2868 m_componentmode( eDefault ),
2869 m_count_primitive( SelectionChangedCaller( *this ) ),
2870 m_count_component( SelectionChangedCaller( *this ) ),
2871 m_translate_manipulator( *this, 2, 64 ),
2872 m_rotate_manipulator( *this, 8, 64 ),
2873 m_scale_manipulator( *this, 0, 64 ),
2874 m_pivotChanged( false ),
2875 m_pivot_moving( false ),
2876 m_pivotIsCustom( false ){
2877 SetManipulatorMode( eTranslate );
2879 addSelectionChangeCallback( PivotChangedSelectionCaller( *this ) );
2880 AddGridChangeCallback( PivotChangedCaller( *this ) );
2882 void pivotChanged() const {
2883 m_pivotChanged = true;
2884 SceneChangeNotify();
2886 typedef ConstMemberCaller<RadiantSelectionSystem, &RadiantSelectionSystem::pivotChanged> PivotChangedCaller;
2887 void pivotChangedSelection( const Selectable& selectable ){
2890 typedef MemberCaller1<RadiantSelectionSystem, const Selectable&, &RadiantSelectionSystem::pivotChangedSelection> PivotChangedSelectionCaller;
2892 void SetMode( EMode mode ){
2893 if ( m_mode != mode ) {
2898 EMode Mode() const {
2901 void SetComponentMode( EComponentMode mode ){
2902 m_componentmode = mode;
2904 EComponentMode ComponentMode() const {
2905 return m_componentmode;
2907 void SetManipulatorMode( EManipulatorMode mode ){
2908 m_pivotIsCustom = false;
2909 m_manipulator_mode = mode;
2910 switch ( m_manipulator_mode )
2912 case eTranslate: m_manipulator = &m_translate_manipulator; break;
2913 case eRotate: m_manipulator = &m_rotate_manipulator; break;
2914 case eScale: m_manipulator = &m_scale_manipulator; break;
2915 case eDrag: m_manipulator = &m_drag_manipulator; break;
2916 case eClip: m_manipulator = &m_clip_manipulator; break;
2920 EManipulatorMode ManipulatorMode() const {
2921 return m_manipulator_mode;
2924 SelectionChangeCallback getObserver( EMode mode ){
2925 if ( mode == ePrimitive ) {
2926 return makeCallback1( m_count_primitive );
2930 return makeCallback1( m_count_component );
2933 std::size_t countSelected() const {
2934 return m_count_primitive.size();
2936 std::size_t countSelectedComponents() const {
2937 return m_count_component.size();
2939 void onSelectedChanged( scene::Instance& instance, const Selectable& selectable ){
2940 if ( selectable.isSelected() ) {
2941 m_selection.append( instance );
2945 m_selection.erase( instance );
2948 ASSERT_MESSAGE( m_selection.size() == m_count_primitive.size(), "selection-tracking error" );
2950 void onComponentSelection( scene::Instance& instance, const Selectable& selectable ){
2951 if ( selectable.isSelected() ) {
2952 m_component_selection.append( instance );
2956 m_component_selection.erase( instance );
2959 ASSERT_MESSAGE( m_component_selection.size() == m_count_component.size(), "selection-tracking error" );
2961 scene::Instance& ultimateSelected() const {
2962 ASSERT_MESSAGE( m_selection.size() > 0, "no instance selected" );
2963 return m_selection.back();
2965 scene::Instance& penultimateSelected() const {
2966 ASSERT_MESSAGE( m_selection.size() > 1, "only one instance selected" );
2967 return *( *( --( --m_selection.end() ) ) );
2969 void setSelectedAll( bool selected ){
2970 GlobalSceneGraph().traverse( select_all( selected ) );
2972 m_manipulator->setSelected( selected );
2974 void setSelectedAllComponents( bool selected ){
2975 Scene_SelectAll_Component( selected, SelectionSystem::eVertex );
2976 Scene_SelectAll_Component( selected, SelectionSystem::eEdge );
2977 Scene_SelectAll_Component( selected, SelectionSystem::eFace );
2979 m_manipulator->setSelected( selected );
2982 void foreachSelected( const Visitor& visitor ) const {
2983 selection_t::const_iterator i = m_selection.begin();
2984 while ( i != m_selection.end() )
2986 visitor.visit( *( *( i++ ) ) );
2989 void foreachSelectedComponent( const Visitor& visitor ) const {
2990 selection_t::const_iterator i = m_component_selection.begin();
2991 while ( i != m_component_selection.end() )
2993 visitor.visit( *( *( i++ ) ) );
2997 void addSelectionChangeCallback( const SelectionChangeHandler& handler ){
2998 m_selectionChanged_callbacks.connectLast( handler );
3000 void selectionChanged( const Selectable& selectable ){
3001 m_selectionChanged_callbacks( selectable );
3003 typedef MemberCaller1<RadiantSelectionSystem, const Selectable&, &RadiantSelectionSystem::selectionChanged> SelectionChangedCaller;
3007 m_pivot2world_start = GetPivot2World();
3010 bool SelectManipulator( const View& view, const float device_point[2], const float device_epsilon[2] ){
3011 if ( !nothingSelected() || ( ManipulatorMode() == eDrag && Mode() == eComponent ) ) {
3012 #if defined ( DEBUG_SELECTION )
3013 g_render_clipped.destroy();
3016 m_manipulator->setSelected( false );
3018 if ( !nothingSelected() || ( ManipulatorMode() == eDrag && Mode() == eComponent ) ) {
3019 View scissored( view );
3020 ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) );
3021 m_manipulator->testSelect( scissored, GetPivot2World() );
3026 m_pivot_moving = m_manipulator->isSelected();
3028 if ( m_pivot_moving ) {
3030 pivot.update( GetPivot2World(), view.GetModelview(), view.GetProjection(), view.GetViewport() );
3032 m_manip2pivot_start = matrix4_multiplied_by_matrix4( matrix4_full_inverse( m_pivot2world_start ), pivot.m_worldSpace );
3034 Matrix4 device2manip;
3035 ConstructDevice2Manip( device2manip, m_pivot2world_start, view.GetModelview(), view.GetProjection(), view.GetViewport() );
3036 m_manipulator->GetManipulatable()->Construct( device2manip, device_point[0], device_point[1], getSelectionAABB(), vector4_to_vector3( GetPivot2World().t() ) );
3038 m_undo_begun = false;
3041 SceneChangeNotify();
3044 return m_pivot_moving;
3048 if ( Mode() == eComponent ) {
3049 setSelectedAllComponents( false );
3053 setSelectedAll( false );
3057 void deselectComponentsOrAll( bool components ){
3059 setSelectedAllComponents( false );
3067 void SelectPoint( const View& view, const float device_point[2], const float device_epsilon[2], RadiantSelectionSystem::EModifier modifier, bool face ){
3068 //globalOutputStream() << device_point[0] << " " << device_point[1] << "\n";
3069 ASSERT_MESSAGE( fabs( device_point[0] ) <= 1.0f && fabs( device_point[1] ) <= 1.0f, "point-selection error" );
3071 if ( modifier == eReplace ) {
3072 deselectComponentsOrAll( face );
3075 //nothingSelected() doesn't consider faces, selected in non-component mode, m
3076 if ( modifier == eCycle && nothingSelected() ){
3077 modifier = eReplace;
3080 #if defined ( DEBUG_SELECTION )
3081 g_render_clipped.destroy();
3085 View scissored( view );
3086 ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) );
3088 SelectionVolume volume( scissored );
3089 SelectionPool selector;
3090 SelectionPool selector_point_ents;
3091 const bool prefer_point_ents = m_bPreferPointEntsIn2D && Mode() == ePrimitive && !view.fill() && !face
3092 && ( modifier == RadiantSelectionSystem::eReplace || modifier == RadiantSelectionSystem::eSelect || modifier == RadiantSelectionSystem::eDeselect );
3094 if( prefer_point_ents ){
3095 Scene_TestSelect( selector_point_ents, volume, scissored, eEntity, ComponentMode() );
3097 if( prefer_point_ents && !selector_point_ents.failed() ){
3100 // if cycle mode not enabled, enable it
3101 case RadiantSelectionSystem::eReplace:
3104 ( *selector_point_ents.begin() ).second->setSelected( true );
3107 case RadiantSelectionSystem::eSelect:
3109 SelectionPool::iterator best = selector_point_ents.begin();
3110 if( !( *best ).second->isSelected() ){
3111 ( *best ).second->setSelected( true );
3113 SelectionPool::iterator i = best;
3115 while ( i != selector_point_ents.end() )
3117 if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
3118 if( !( *i ).second->isSelected() ){
3119 ( *i ).second->setSelected( true );
3129 case RadiantSelectionSystem::eDeselect:
3131 SelectionPool::iterator best = selector_point_ents.begin();
3132 if( ( *best ).second->isSelected() ){
3133 ( *best ).second->setSelected( false );
3135 SelectionPool::iterator i = best;
3137 while ( i != selector_point_ents.end() )
3139 if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
3140 if( ( *i ).second->isSelected() ){
3141 ( *i ).second->setSelected( false );
3157 Scene_TestSelect_Component( selector, volume, scissored, eFace );
3160 Scene_TestSelect( selector, volume, scissored, g_bAltDragManipulatorResize ? ePrimitive : Mode(), ComponentMode() );
3163 if ( !selector.failed() ) {
3166 case RadiantSelectionSystem::eToggle:
3168 SelectableSortedSet::iterator best = selector.begin();
3169 // toggle selection of the object with least depth
3170 if ( ( *best ).second->isSelected() ) {
3171 ( *best ).second->setSelected( false );
3174 ( *best ).second->setSelected( true );
3178 // if cycle mode not enabled, enable it
3179 case RadiantSelectionSystem::eReplace:
3182 ( *selector.begin() ).second->setSelected( true );
3185 // select the next object in the list from the one already selected
3186 case RadiantSelectionSystem::eCycle:
3188 bool CycleSelectionOccured = false;
3189 SelectionPool::iterator i = selector.begin();
3190 while ( i != selector.end() )
3192 if ( ( *i ).second->isSelected() ) {
3193 deselectComponentsOrAll( face );
3195 if ( i != selector.end() ) {
3196 i->second->setSelected( true );
3200 selector.begin()->second->setSelected( true );
3202 CycleSelectionOccured = true;
3207 if( !CycleSelectionOccured ){
3208 deselectComponentsOrAll( face );
3209 ( *selector.begin() ).second->setSelected( true );
3213 case RadiantSelectionSystem::eSelect:
3215 SelectionPool::iterator best = selector.begin();
3216 if( !( *best ).second->isSelected() ){
3217 ( *best ).second->setSelected( true );
3219 SelectionPool::iterator i = best;
3221 while ( i != selector.end() )
3223 if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
3224 if( !( *i ).second->isSelected() ){
3225 ( *i ).second->setSelected( true );
3235 case RadiantSelectionSystem::eDeselect:
3237 SelectionPool::iterator best = selector.begin();
3238 if( ( *best ).second->isSelected() ){
3239 ( *best ).second->setSelected( false );
3241 SelectionPool::iterator i = best;
3243 while ( i != selector.end() )
3245 if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
3246 if( ( *i ).second->isSelected() ){
3247 ( *i ).second->setSelected( false );
3261 else if( modifier == eCycle ){
3262 deselectComponentsOrAll( face );
3268 bool SelectPoint_InitPaint( const View& view, const float device_point[2], const float device_epsilon[2], bool face ){
3269 ASSERT_MESSAGE( fabs( device_point[0] ) <= 1.0f && fabs( device_point[1] ) <= 1.0f, "point-selection error" );
3270 #if defined ( DEBUG_SELECTION )
3271 g_render_clipped.destroy();
3275 View scissored( view );
3276 ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) );
3278 SelectionVolume volume( scissored );
3279 SelectionPool selector;
3280 SelectionPool selector_point_ents;
3281 const bool prefer_point_ents = m_bPreferPointEntsIn2D && Mode() == ePrimitive && !view.fill() && !face;
3283 if( prefer_point_ents ){
3284 Scene_TestSelect( selector_point_ents, volume, scissored, eEntity, ComponentMode() );
3286 if( prefer_point_ents && !selector_point_ents.failed() ){
3287 SelectableSortedSet::iterator best = selector_point_ents.begin();
3288 const bool wasSelected = ( *best ).second->isSelected();
3289 ( *best ).second->setSelected( !wasSelected );
3290 SelectableSortedSet::iterator i = best;
3292 while ( i != selector_point_ents.end() )
3294 if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
3295 ( *i ).second->setSelected( !wasSelected );
3302 return !wasSelected;
3304 else{//do primitives, if ents failed
3306 Scene_TestSelect_Component( selector, volume, scissored, eFace );
3309 Scene_TestSelect( selector, volume, scissored, g_bAltDragManipulatorResize ? ePrimitive : Mode(), ComponentMode() );
3311 if ( !selector.failed() ){
3312 SelectableSortedSet::iterator best = selector.begin();
3313 const bool wasSelected = ( *best ).second->isSelected();
3314 ( *best ).second->setSelected( !wasSelected );
3315 SelectableSortedSet::iterator i = best;
3317 while ( i != selector.end() )
3319 if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
3320 ( *i ).second->setSelected( !wasSelected );
3327 return !wasSelected;
3336 void SelectArea( const View& view, const float device_point[2], const float device_delta[2], RadiantSelectionSystem::EModifier modifier, bool face ){
3337 if ( modifier == eReplace ) {
3338 deselectComponentsOrAll( face );
3341 #if defined ( DEBUG_SELECTION )
3342 g_render_clipped.destroy();
3346 View scissored( view );
3347 ConstructSelectionTest( scissored, SelectionBoxForArea( device_point, device_delta ) );
3349 SelectionVolume volume( scissored );
3352 Scene_TestSelect_Component( pool, volume, scissored, eFace );
3356 Scene_TestSelect( pool, volume, scissored, Mode(), ComponentMode() );
3359 for ( SelectionPool::iterator i = pool.begin(); i != pool.end(); ++i )
3361 ( *i ).second->setSelected( !( modifier == RadiantSelectionSystem::eToggle && ( *i ).second->isSelected() ) );
3367 void translate( const Vector3& translation ){
3368 if ( !nothingSelected() ) {
3369 //ASSERT_MESSAGE(!m_pivotChanged, "pivot is invalid");
3371 m_translation = translation;
3373 m_pivot2world = m_pivot2world_start;
3374 matrix4_translate_by_vec3( m_pivot2world, translation );
3376 if ( Mode() == eComponent ) {
3377 Scene_Translate_Component_Selected( GlobalSceneGraph(), m_translation );
3381 Scene_Translate_Selected( GlobalSceneGraph(), m_translation );
3384 SceneChangeNotify();
3387 void outputTranslation( TextOutputStream& ostream ){
3388 ostream << " -xyz " << m_translation.x() << " " << m_translation.y() << " " << m_translation.z();
3390 void rotate( const Quaternion& rotation ){
3391 if ( !nothingSelected() ) {
3392 //ASSERT_MESSAGE(!m_pivotChanged, "pivot is invalid");
3394 m_rotation = rotation;
3396 if ( Mode() == eComponent ) {
3397 Scene_Rotate_Component_Selected( GlobalSceneGraph(), m_rotation, vector4_to_vector3( m_pivot2world.t() ) );
3399 matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() );
3403 Scene_Rotate_Selected( GlobalSceneGraph(), m_rotation, vector4_to_vector3( m_pivot2world.t() ) );
3405 matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() );
3408 SceneChangeNotify();
3411 void outputRotation( TextOutputStream& ostream ){
3412 ostream << " -eulerXYZ " << m_rotation.x() << " " << m_rotation.y() << " " << m_rotation.z();
3414 void scale( const Vector3& scaling ){
3415 if ( !nothingSelected() ) {
3418 if ( Mode() == eComponent ) {
3419 Scene_Scale_Component_Selected( GlobalSceneGraph(), m_scale, vector4_to_vector3( m_pivot2world.t() ) );
3423 Scene_Scale_Selected( GlobalSceneGraph(), m_scale, vector4_to_vector3( m_pivot2world.t() ) );
3426 SceneChangeNotify();
3429 void outputScale( TextOutputStream& ostream ){
3430 ostream << " -scale " << m_scale.x() << " " << m_scale.y() << " " << m_scale.z();
3433 void rotateSelected( const Quaternion& rotation, bool snapOrigin ){
3434 if( snapOrigin && !m_pivotIsCustom ){
3435 m_pivot2world.tx() = float_snapped( m_pivot2world.tx(), GetSnapGridSize() );
3436 m_pivot2world.ty() = float_snapped( m_pivot2world.ty(), GetSnapGridSize() );
3437 m_pivot2world.tz() = float_snapped( m_pivot2world.tz(), GetSnapGridSize() );
3443 void translateSelected( const Vector3& translation ){
3445 translate( translation );
3448 void scaleSelected( const Vector3& scaling ){
3454 void MoveSelected( const View& view, const float device_point[2], bool snap, bool snapbbox ){
3455 if ( m_manipulator->isSelected() ) {
3456 if ( !m_undo_begun ) {
3457 m_undo_begun = true;
3458 GlobalUndoSystem().start();
3461 Matrix4 device2manip;
3462 ConstructDevice2Manip( device2manip, m_pivot2world_start, view.GetModelview(), view.GetProjection(), view.GetViewport() );
3463 m_manipulator->GetManipulatable()->Transform( m_manip2pivot_start, device2manip, device_point[0], device_point[1], snap, snapbbox );
3467 /// \todo Support view-dependent nudge.
3468 void NudgeManipulator( const Vector3& nudge, const Vector3& view ){
3469 // if ( ManipulatorMode() == eTranslate || ManipulatorMode() == eDrag ) {
3470 translateSelected( nudge );
3475 void freezeTransforms();
3477 void renderSolid( Renderer& renderer, const VolumeTest& volume ) const;
3478 void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const {
3479 renderSolid( renderer, volume );
3482 const Matrix4& GetPivot2World() const {
3484 return m_pivot2world;
3487 static void constructStatic(){
3488 m_state = GlobalShaderCache().capture( "$POINT" );
3489 #if defined( DEBUG_SELECTION )
3490 g_state_clipped = GlobalShaderCache().capture( "$DEBUG_CLIPPED" );
3492 TranslateManipulator::m_state_wire = GlobalShaderCache().capture( "$WIRE_OVERLAY" );
3493 TranslateManipulator::m_state_fill = GlobalShaderCache().capture( "$FLATSHADE_OVERLAY" );
3494 RotateManipulator::m_state_outer = GlobalShaderCache().capture( "$WIRE_OVERLAY" );
3497 static void destroyStatic(){
3498 #if defined( DEBUG_SELECTION )
3499 GlobalShaderCache().release( "$DEBUG_CLIPPED" );
3501 GlobalShaderCache().release( "$WIRE_OVERLAY" );
3502 GlobalShaderCache().release( "$FLATSHADE_OVERLAY" );
3503 GlobalShaderCache().release( "$WIRE_OVERLAY" );
3504 GlobalShaderCache().release( "$POINT" );
3508 Shader* RadiantSelectionSystem::m_state = 0;
3513 RadiantSelectionSystem* g_RadiantSelectionSystem;
3515 inline RadiantSelectionSystem& getSelectionSystem(){
3516 return *g_RadiantSelectionSystem;
3522 class testselect_entity_visible : public scene::Graph::Walker
3524 Selector& m_selector;
3525 SelectionTest& m_test;
3527 testselect_entity_visible( Selector& selector, SelectionTest& test )
3528 : m_selector( selector ), m_test( test ){
3530 bool pre( const scene::Path& path, scene::Instance& instance ) const {
3531 if( path.top().get_pointer() == Map_GetWorldspawn( g_map ) ||
3532 node_is_group( path.top().get() ) ){
3535 Selectable* selectable = Instance_getSelectable( instance );
3536 if ( selectable != 0
3537 && Node_isEntity( path.top() ) ) {
3538 m_selector.pushSelectable( *selectable );
3541 SelectionTestable* selectionTestable = Instance_getSelectionTestable( instance );
3542 if ( selectionTestable ) {
3543 selectionTestable->testSelect( m_selector, m_test );
3548 void post( const scene::Path& path, scene::Instance& instance ) const {
3549 Selectable* selectable = Instance_getSelectable( instance );
3550 if ( selectable != 0
3551 && Node_isEntity( path.top() ) ) {
3552 m_selector.popSelectable();
3557 class testselect_primitive_visible : public scene::Graph::Walker
3559 Selector& m_selector;
3560 SelectionTest& m_test;
3562 testselect_primitive_visible( Selector& selector, SelectionTest& test )
3563 : m_selector( selector ), m_test( test ){
3565 bool pre( const scene::Path& path, scene::Instance& instance ) const {
3566 Selectable* selectable = Instance_getSelectable( instance );
3567 if ( selectable != 0 ) {
3568 m_selector.pushSelectable( *selectable );
3571 SelectionTestable* selectionTestable = Instance_getSelectionTestable( instance );
3572 if ( selectionTestable ) {
3573 selectionTestable->testSelect( m_selector, m_test );
3578 void post( const scene::Path& path, scene::Instance& instance ) const {
3579 Selectable* selectable = Instance_getSelectable( instance );
3580 if ( selectable != 0 ) {
3581 m_selector.popSelectable();
3586 class testselect_component_visible : public scene::Graph::Walker
3588 Selector& m_selector;
3589 SelectionTest& m_test;
3590 SelectionSystem::EComponentMode m_mode;
3592 testselect_component_visible( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode )
3593 : m_selector( selector ), m_test( test ), m_mode( mode ){
3595 bool pre( const scene::Path& path, scene::Instance& instance ) const {
3596 ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance );
3597 if ( componentSelectionTestable ) {
3598 componentSelectionTestable->testSelectComponents( m_selector, m_test, m_mode );
3606 class testselect_component_visible_selected : public scene::Graph::Walker
3608 Selector& m_selector;
3609 SelectionTest& m_test;
3610 SelectionSystem::EComponentMode m_mode;
3612 testselect_component_visible_selected( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode )
3613 : m_selector( selector ), m_test( test ), m_mode( mode ){
3615 bool pre( const scene::Path& path, scene::Instance& instance ) const {
3616 Selectable* selectable = Instance_getSelectable( instance );
3617 if ( selectable != 0 && selectable->isSelected() ) {
3618 ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance );
3619 if ( componentSelectionTestable ) {
3620 componentSelectionTestable->testSelectComponents( m_selector, m_test, m_mode );
3628 void Scene_TestSelect_Primitive( Selector& selector, SelectionTest& test, const VolumeTest& volume ){
3629 Scene_forEachVisible( GlobalSceneGraph(), volume, testselect_primitive_visible( selector, test ) );
3632 void Scene_TestSelect_Component_Selected( Selector& selector, SelectionTest& test, const VolumeTest& volume, SelectionSystem::EComponentMode componentMode ){
3633 Scene_forEachVisible( GlobalSceneGraph(), volume, testselect_component_visible_selected( selector, test, componentMode ) );
3636 void Scene_TestSelect_Component( Selector& selector, SelectionTest& test, const VolumeTest& volume, SelectionSystem::EComponentMode componentMode ){
3637 Scene_forEachVisible( GlobalSceneGraph(), volume, testselect_component_visible( selector, test, componentMode ) );
3640 void RadiantSelectionSystem::Scene_TestSelect( Selector& selector, SelectionTest& test, const View& view, SelectionSystem::EMode mode, SelectionSystem::EComponentMode componentMode ){
3645 Scene_forEachVisible( GlobalSceneGraph(), view, testselect_entity_visible( selector, test ) );
3649 Scene_TestSelect_Primitive( selector, test, view );
3652 Scene_TestSelect_Component_Selected( selector, test, view, componentMode );
3657 class FreezeTransforms : public scene::Graph::Walker
3660 bool pre( const scene::Path& path, scene::Instance& instance ) const {
3661 TransformNode* transformNode = Node_getTransformNode( path.top() );
3662 if ( transformNode != 0 ) {
3663 Transformable* transform = Instance_getTransformable( instance );
3664 if ( transform != 0 ) {
3665 transform->freezeTransform();
3672 void RadiantSelectionSystem::freezeTransforms(){
3673 GlobalSceneGraph().traverse( FreezeTransforms() );
3677 void RadiantSelectionSystem::endMove(){
3680 if ( Mode() == ePrimitive ) {
3681 if ( ManipulatorMode() == eDrag ) {
3682 g_bTmpComponentMode = false;
3683 if( g_bAltDragManipulatorResize ){
3684 Scene_SelectAll_Component( false, SelectionSystem::eVertex );
3687 Scene_SelectAll_Component( false, SelectionSystem::eFace );
3692 m_pivot_moving = false;
3695 SceneChangeNotify();
3697 if ( m_undo_begun ) {
3698 StringOutputStream command;
3700 if ( ManipulatorMode() == eTranslate ) {
3701 command << "translateTool";
3702 outputTranslation( command );
3704 else if ( ManipulatorMode() == eRotate ) {
3705 command << "rotateTool";
3706 outputRotation( command );
3708 else if ( ManipulatorMode() == eScale ) {
3709 command << "scaleTool";
3710 outputScale( command );
3712 else if ( ManipulatorMode() == eDrag ) {
3713 command << "dragTool";
3716 GlobalUndoSystem().finish( command.c_str() );
3721 inline AABB Instance_getPivotBounds( scene::Instance& instance ){
3722 Entity* entity = Node_getEntity( instance.path().top() );
3724 && ( entity->getEntityClass().fixedsize
3725 || !node_is_group( instance.path().top() ) ) ) {
3726 Editable* editable = Node_getEditable( instance.path().top() );
3727 if ( editable != 0 ) {
3728 return AABB( vector4_to_vector3( matrix4_multiplied_by_matrix4( instance.localToWorld(), editable->getLocalPivot() ).t() ), Vector3( 0, 0, 0 ) );
3732 return AABB( vector4_to_vector3( instance.localToWorld().t() ), Vector3( 0, 0, 0 ) );
3736 return instance.worldAABB();
3739 class bounds_selected : public scene::Graph::Walker
3743 bounds_selected( AABB& bounds )
3744 : m_bounds( bounds ){
3747 bool pre( const scene::Path& path, scene::Instance& instance ) const {
3748 Selectable* selectable = Instance_getSelectable( instance );
3749 if ( selectable != 0
3750 && selectable->isSelected() ) {
3751 aabb_extend_by_aabb_safe( m_bounds, Instance_getPivotBounds( instance ) );
3757 class bounds_selected_component : public scene::Graph::Walker
3761 bounds_selected_component( AABB& bounds )
3762 : m_bounds( bounds ){
3765 bool pre( const scene::Path& path, scene::Instance& instance ) const {
3766 Selectable* selectable = Instance_getSelectable( instance );
3767 if ( selectable != 0
3768 && selectable->isSelected() ) {
3769 ComponentEditable* componentEditable = Instance_getComponentEditable( instance );
3770 if ( componentEditable ) {
3771 aabb_extend_by_aabb_safe( m_bounds, aabb_for_oriented_aabb_safe( componentEditable->getSelectedComponentsBounds(), instance.localToWorld() ) );
3778 void Scene_BoundsSelected( scene::Graph& graph, AABB& bounds ){
3779 graph.traverse( bounds_selected( bounds ) );
3782 void Scene_BoundsSelectedComponent( scene::Graph& graph, AABB& bounds ){
3783 graph.traverse( bounds_selected_component( bounds ) );
3787 inline void pivot_for_node( Matrix4& pivot, scene::Node& node, scene::Instance& instance ){
3788 ComponentEditable* componentEditable = Instance_getComponentEditable( instance );
3789 if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent
3790 && componentEditable != 0 ) {
3791 pivot = matrix4_translation_for_vec3( componentEditable->getSelectedComponentsBounds().origin );
3795 Bounded* bounded = Instance_getBounded( instance );
3796 if ( bounded != 0 ) {
3797 pivot = matrix4_translation_for_vec3( bounded->localAABB().origin );
3801 pivot = g_matrix4_identity;
3807 void RadiantSelectionSystem::ConstructPivot() const {
3808 if ( !m_pivotChanged || m_pivot_moving || m_pivotIsCustom ) {
3811 m_pivotChanged = false;
3813 Vector3 m_object_pivot;
3815 if ( !nothingSelected() ) {
3818 if ( Mode() == eComponent ) {
3819 Scene_BoundsSelectedComponent( GlobalSceneGraph(), bounds );
3823 Scene_BoundsSelected( GlobalSceneGraph(), bounds );
3825 m_object_pivot = bounds.origin;
3828 //vector3_snap( m_object_pivot, GetSnapGridSize() );
3829 //globalOutputStream() << m_object_pivot << "\n";
3830 m_pivot2world = matrix4_translation_for_vec3( m_object_pivot );
3832 switch ( m_manipulator_mode )
3837 if ( Mode() == eComponent ) {
3838 matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() );
3842 matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() );
3846 if ( Mode() == eComponent ) {
3847 matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() );
3851 matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() );
3860 void RadiantSelectionSystem::setCustomPivotOrigin( Vector3& point ) const {
3861 if ( !nothingSelected() && ( m_manipulator_mode == eTranslate || m_manipulator_mode == eRotate || m_manipulator_mode == eScale ) ) {
3863 if ( Mode() == eComponent ) {
3864 Scene_BoundsSelectedComponent( GlobalSceneGraph(), bounds );
3868 Scene_BoundsSelected( GlobalSceneGraph(), bounds );
3870 //globalOutputStream() << point << "\n";
3871 for( std::size_t i = 0; i < 3; i++ ){
3872 if( point[i] < 900000.0f ){
3873 float bestsnapDist = fabs( bounds.origin[i] - point[i] );
3874 float bestsnapTo = bounds.origin[i];
3875 float othersnapDist = fabs( bounds.origin[i] + bounds.extents[i] - point[i] );
3876 if( othersnapDist < bestsnapDist ){
3877 bestsnapDist = othersnapDist;
3878 bestsnapTo = bounds.origin[i] + bounds.extents[i];
3880 othersnapDist = fabs( bounds.origin[i] - bounds.extents[i] - point[i] );
3881 if( othersnapDist < bestsnapDist ){
3882 bestsnapDist = othersnapDist;
3883 bestsnapTo = bounds.origin[i] - bounds.extents[i];
3885 othersnapDist = fabs( float_snapped( point[i], GetSnapGridSize() ) - point[i] );
3886 if( othersnapDist < bestsnapDist ){
3887 bestsnapDist = othersnapDist;
3888 bestsnapTo = float_snapped( point[i], GetSnapGridSize() );
3890 point[i] = bestsnapTo;
3892 m_pivot2world[i + 12] = point[i]; //m_pivot2world.tx() .ty() .tz()
3896 switch ( m_manipulator_mode )
3901 if ( Mode() == eComponent ) {
3902 matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() );
3906 matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() );
3910 if ( Mode() == eComponent ) {
3911 matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() );
3915 matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() );
3922 m_pivotIsCustom = true;
3926 AABB RadiantSelectionSystem::getSelectionAABB() const {
3928 if ( !nothingSelected() ) {
3929 if ( Mode() == eComponent || g_bTmpComponentMode ) {
3930 Scene_BoundsSelectedComponent( GlobalSceneGraph(), bounds );
3934 Scene_BoundsSelected( GlobalSceneGraph(), bounds );
3940 void RadiantSelectionSystem::renderSolid( Renderer& renderer, const VolumeTest& volume ) const {
3941 //if(view->TestPoint(m_object_pivot))
3942 if ( !nothingSelected() ) {
3943 renderer.Highlight( Renderer::ePrimitive, false );
3944 renderer.Highlight( Renderer::eFace, false );
3946 renderer.SetState( m_state, Renderer::eWireframeOnly );
3947 renderer.SetState( m_state, Renderer::eFullMaterials );
3949 m_manipulator->render( renderer, volume, GetPivot2World() );
3952 #if defined( DEBUG_SELECTION )
3953 renderer.SetState( g_state_clipped, Renderer::eWireframeOnly );
3954 renderer.SetState( g_state_clipped, Renderer::eFullMaterials );
3955 renderer.addRenderable( g_render_clipped, g_render_clipped.m_world );
3959 #include "preferencesystem.h"
3960 #include "preferences.h"
3962 bool g_bLeftMouseClickSelector = true;
3964 void SelectionSystem_constructPreferences( PreferencesPage& page ){
3965 page.appendCheckBox( "", "Prefer point entities in 2D", getSelectionSystem().m_bPreferPointEntsIn2D );
3966 page.appendCheckBox( "", "Left mouse click tunnel selector", g_bLeftMouseClickSelector );
3968 void SelectionSystem_constructPage( PreferenceGroup& group ){
3969 PreferencesPage page( group.createPage( "Selection", "Selection System Settings" ) );
3970 SelectionSystem_constructPreferences( page );
3972 void SelectionSystem_registerPreferencesPage(){
3973 PreferencesDialog_addSettingsPage( FreeCaller1<PreferenceGroup&, SelectionSystem_constructPage>() );
3978 void SelectionSystem_OnBoundsChanged(){
3979 getSelectionSystem().pivotChanged();
3982 SignalHandlerId SelectionSystem_boundsChanged;
3984 void SelectionSystem_Construct(){
3985 RadiantSelectionSystem::constructStatic();
3987 g_RadiantSelectionSystem = new RadiantSelectionSystem;
3989 SelectionSystem_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback( FreeCaller<SelectionSystem_OnBoundsChanged>() );
3991 GlobalShaderCache().attachRenderable( getSelectionSystem() );
3993 GlobalPreferenceSystem().registerPreference( "PreferPointEntsIn2D", BoolImportStringCaller( getSelectionSystem().m_bPreferPointEntsIn2D ), BoolExportStringCaller( getSelectionSystem().m_bPreferPointEntsIn2D ) );
3994 GlobalPreferenceSystem().registerPreference( "LeftMouseClickSelector", BoolImportStringCaller( g_bLeftMouseClickSelector ), BoolExportStringCaller( g_bLeftMouseClickSelector ) );
3995 SelectionSystem_registerPreferencesPage();
3998 void SelectionSystem_Destroy(){
3999 GlobalShaderCache().detachRenderable( getSelectionSystem() );
4001 GlobalSceneGraph().removeBoundsChangedCallback( SelectionSystem_boundsChanged );
4003 delete g_RadiantSelectionSystem;
4005 RadiantSelectionSystem::destroyStatic();
4011 inline float screen_normalised( float pos, std::size_t size ){
4012 return ( ( 2.0f * pos ) / size ) - 1.0f;
4015 typedef Vector2 DeviceVector;
4017 inline DeviceVector window_to_normalised_device( WindowVector window, std::size_t width, std::size_t height ){
4018 return DeviceVector( screen_normalised( window.x(), width ), screen_normalised( height - 1 - window.y(), height ) );
4021 inline float device_constrained( float pos ){
4022 return std::min( 1.0f, std::max( -1.0f, pos ) );
4025 inline DeviceVector device_constrained( DeviceVector device ){
4026 return DeviceVector( device_constrained( device.x() ), device_constrained( device.y() ) );
4029 inline float window_constrained( float pos, std::size_t origin, std::size_t size ){
4030 return std::min( static_cast<float>( origin + size ), std::max( static_cast<float>( origin ), pos ) );
4033 inline WindowVector window_constrained( WindowVector window, std::size_t x, std::size_t y, std::size_t width, std::size_t height ){
4034 return WindowVector( window_constrained( window.x(), x, width ), window_constrained( window.y(), y, height ) );
4037 typedef Callback1<DeviceVector> MouseEventCallback;
4039 Single<MouseEventCallback> g_mouseMovedCallback;
4040 Single<MouseEventCallback> g_mouseUpCallback;
4043 const ButtonIdentifier c_button_select = c_buttonLeft;
4044 const ButtonIdentifier c_button_select2 = c_buttonRight;
4045 const ModifierFlags c_modifier_manipulator = c_modifierNone;
4046 const ModifierFlags c_modifier_toggle = c_modifierShift;
4047 const ModifierFlags c_modifier_replace = c_modifierShift | c_modifierAlt;
4048 const ModifierFlags c_modifier_face = c_modifierControl;
4050 const ButtonIdentifier c_button_select = c_buttonLeft;
4051 const ModifierFlags c_modifier_manipulator = c_modifierNone;
4052 const ModifierFlags c_modifier_toggle = c_modifierControl;
4053 const ModifierFlags c_modifier_replace = c_modifierNone;
4054 const ModifierFlags c_modifier_face = c_modifierShift;
4056 const ModifierFlags c_modifier_toggle_face = c_modifier_toggle | c_modifier_face;
4057 const ModifierFlags c_modifier_replace_face = c_modifier_replace | c_modifier_face;
4059 const ButtonIdentifier c_button_texture = c_buttonMiddle;
4060 const ModifierFlags c_modifier_apply_texture1 = c_modifierControl | c_modifierShift;
4061 const ModifierFlags c_modifier_apply_texture2 = c_modifierControl;
4062 const ModifierFlags c_modifier_apply_texture3 = c_modifierShift;
4063 const ModifierFlags c_modifier_copy_texture = c_modifierNone;
4067 RadiantSelectionSystem::EModifier modifier_for_state( ModifierFlags state ){
4068 if ( ( state == c_modifier_toggle || state == c_modifier_toggle_face || state == c_modifier_face || state == c_modifierAlt ) ) {
4070 return RadiantSelectionSystem::eReplace;
4073 return RadiantSelectionSystem::eToggle;
4076 return RadiantSelectionSystem::eManipulator;
4079 rect_t getDeviceArea() const {
4080 DeviceVector delta( m_current - m_start );
4081 if ( selecting() && fabs( delta.x() ) > m_epsilon.x() && fabs( delta.y() ) > m_epsilon.y() ) {
4082 return SelectionBoxForArea( &m_start[0], &delta[0] );
4086 rect_t default_area = { { 0, 0, }, { 0, 0, }, };
4087 return default_area;
4092 DeviceVector m_start;
4093 DeviceVector m_current;
4094 DeviceVector m_epsilon;
4095 ModifierFlags m_state;
4098 bool m_mouseMovedWhilePressed;
4101 RectangleCallback m_window_update;
4103 Selector_() : m_start( 0.0f, 0.0f ), m_current( 0.0f, 0.0f ), m_state( c_modifierNone ), m_mouse2( false ), m_mouseMoved( false ), m_mouseMovedWhilePressed( false ){
4107 m_window_update( getDeviceArea() );
4110 void testSelect( DeviceVector position ){
4111 RadiantSelectionSystem::EModifier modifier = modifier_for_state( m_state );
4112 if ( modifier != RadiantSelectionSystem::eManipulator ) {
4113 DeviceVector delta( position - m_start );
4114 if ( fabs( delta.x() ) > m_epsilon.x() && fabs( delta.y() ) > m_epsilon.y() ) {
4115 DeviceVector delta( position - m_start );
4116 //getSelectionSystem().SelectArea( *m_view, &m_start[0], &delta[0], modifier, ( m_state & c_modifier_face ) != c_modifierNone );
4117 getSelectionSystem().SelectArea( *m_view, &m_start[0], &delta[0], RadiantSelectionSystem::eToggle, ( m_state & c_modifier_face ) != c_modifierNone );
4119 else if( !m_mouseMovedWhilePressed ){
4120 if ( modifier == RadiantSelectionSystem::eReplace && !m_mouseMoved ) {
4121 modifier = RadiantSelectionSystem::eCycle;
4123 getSelectionSystem().SelectPoint( *m_view, &position[0], &m_epsilon[0], modifier, ( m_state & c_modifier_face ) != c_modifierNone );
4127 m_start = m_current = DeviceVector( 0.0f, 0.0f );
4131 void testSelect_simpleM1( DeviceVector position ){
4132 /*RadiantSelectionSystem::EModifier modifier = RadiantSelectionSystem::eReplace;
4133 DeviceVector delta( position - m_start );
4134 if ( fabs( delta.x() ) < m_epsilon.x() && fabs( delta.y() ) < m_epsilon.y() ) {
4135 modifier = RadiantSelectionSystem::eCycle;
4137 getSelectionSystem().SelectPoint( *m_view, &position[0], &m_epsilon[0], modifier, false );*/
4138 if( g_bLeftMouseClickSelector ){
4139 getSelectionSystem().SelectPoint( *m_view, &position[0], &m_epsilon[0], m_mouseMoved ? RadiantSelectionSystem::eReplace : RadiantSelectionSystem::eCycle, false );
4141 m_start = m_current = device_constrained( position );
4145 bool selecting() const {
4146 return m_state != c_modifier_manipulator && m_mouse2;
4149 void setState( ModifierFlags state ){
4150 bool was_selecting = selecting();
4152 if ( was_selecting ^ selecting() ) {
4157 ModifierFlags getState() const {
4161 void modifierEnable( ModifierFlags type ){
4162 setState( bitfield_enable( getState(), type ) );
4164 void modifierDisable( ModifierFlags type ){
4165 setState( bitfield_disable( getState(), type ) );
4168 void mouseDown( DeviceVector position ){
4169 m_start = m_current = device_constrained( position );
4170 if( !m_mouse2 && m_state != c_modifierNone ){
4171 m_paintSelect = getSelectionSystem().SelectPoint_InitPaint( *m_view, &position[0], &m_epsilon[0], ( m_state & c_modifier_face ) != c_modifierNone );
4175 void mouseMoved( DeviceVector position ){
4176 m_current = device_constrained( position );
4177 m_mouseMovedWhilePressed = true;
4181 else if( m_state != c_modifier_manipulator ){
4182 getSelectionSystem().SelectPoint( *m_view, &m_current[0], &m_epsilon[0],
4183 m_paintSelect ? RadiantSelectionSystem::eSelect : RadiantSelectionSystem::eDeselect,
4184 ( m_state & c_modifier_face ) != c_modifierNone );
4187 typedef MemberCaller1<Selector_, DeviceVector, &Selector_::mouseMoved> MouseMovedCaller;
4189 void mouseUp( DeviceVector position ){
4191 testSelect( device_constrained( position ) );
4194 m_start = m_current = DeviceVector( 0.0f, 0.0f );
4197 g_mouseMovedCallback.clear();
4198 g_mouseUpCallback.clear();
4200 typedef MemberCaller1<Selector_, DeviceVector, &Selector_::mouseUp> MouseUpCaller;
4207 DeviceVector m_epsilon;
4209 ModifierFlags m_state;
4211 Manipulator_() : m_state( c_modifierNone ){
4214 bool mouseDown( DeviceVector position ){
4215 return getSelectionSystem().SelectManipulator( *m_view, &position[0], &m_epsilon[0] );
4218 void mouseMoved( DeviceVector position ){
4219 getSelectionSystem().MoveSelected( *m_view, &position[0], ( m_state & c_modifierShift ) == c_modifierShift, ( m_state & c_modifierControl ) == c_modifierControl );
4221 typedef MemberCaller1<Manipulator_, DeviceVector, &Manipulator_::mouseMoved> MouseMovedCaller;
4223 void mouseUp( DeviceVector position ){
4224 getSelectionSystem().endMove();
4225 g_mouseMovedCallback.clear();
4226 g_mouseUpCallback.clear();
4228 typedef MemberCaller1<Manipulator_, DeviceVector, &Manipulator_::mouseUp> MouseUpCaller;
4230 void setState( ModifierFlags state ){
4234 ModifierFlags getState() const {
4238 void modifierEnable( ModifierFlags type ){
4239 setState( bitfield_enable( getState(), type ) );
4241 void modifierDisable( ModifierFlags type ){
4242 setState( bitfield_disable( getState(), type ) );
4246 void Scene_copyClosestTexture( SelectionTest& test );
4247 void Scene_applyClosestTexture( SelectionTest& test );
4249 class RadiantWindowObserver : public SelectionSystemWindowObserver
4262 Selector_ m_selector;
4263 Manipulator_ m_manipulator;
4265 RadiantWindowObserver() : m_mouse_down( false ){
4270 void setView( const View& view ){
4271 m_selector.m_view = &view;
4272 m_manipulator.m_view = &view;
4274 void setRectangleDrawCallback( const RectangleCallback& callback ){
4275 m_selector.m_window_update = callback;
4277 void onSizeChanged( int width, int height ){
4280 DeviceVector epsilon( SELECT_EPSILON / static_cast<float>( m_width ), SELECT_EPSILON / static_cast<float>( m_height ) );
4281 m_selector.m_epsilon = m_manipulator.m_epsilon = epsilon;
4283 void onMouseDown( const WindowVector& position, ButtonIdentifier button, ModifierFlags modifiers ){
4284 if ( button == c_button_select || ( button == c_button_select2 && modifiers != c_modifierNone ) ) {
4285 m_mouse_down = true;
4286 //m_selector.m_mouseMoved = false;
4288 DeviceVector devicePosition( window_to_normalised_device( position, m_width, m_height ) );
4289 g_bAltDragManipulatorResize = ( modifiers == c_modifierAlt ) ? true : false;
4290 if ( ( modifiers == c_modifier_manipulator || ( modifiers == c_modifierAlt && getSelectionSystem().Mode() != SelectionSystem::eComponent ) ) && m_manipulator.mouseDown( devicePosition ) ) {
4291 g_mouseMovedCallback.insert( MouseEventCallback( Manipulator_::MouseMovedCaller( m_manipulator ) ) );
4292 g_mouseUpCallback.insert( MouseEventCallback( Manipulator_::MouseUpCaller( m_manipulator ) ) );
4296 m_selector.m_mouse2 = ( button == c_button_select ) ? false : true;
4297 m_selector.mouseDown( devicePosition );
4298 g_mouseMovedCallback.insert( MouseEventCallback( Selector_::MouseMovedCaller( m_selector ) ) );
4299 g_mouseUpCallback.insert( MouseEventCallback( Selector_::MouseUpCaller( m_selector ) ) );
4302 else if ( button == c_button_texture ) {
4303 DeviceVector devicePosition( device_constrained( window_to_normalised_device( position, m_width, m_height ) ) );
4305 View scissored( *m_selector.m_view );
4306 ConstructSelectionTest( scissored, SelectionBoxForPoint( &devicePosition[0], &m_selector.m_epsilon[0] ) );
4307 SelectionVolume volume( scissored );
4309 if ( modifiers == c_modifier_apply_texture1 || modifiers == c_modifier_apply_texture2 || modifiers == c_modifier_apply_texture3 ) {
4310 Scene_applyClosestTexture( volume );
4312 else if ( modifiers == c_modifier_copy_texture ) {
4313 Scene_copyClosestTexture( volume );
4317 void onMouseMotion( const WindowVector& position, ModifierFlags modifiers ){
4318 m_selector.m_mouseMoved = true;
4319 if ( m_mouse_down && !g_mouseMovedCallback.empty() ) {
4320 m_selector.m_mouseMovedWhilePressed = true;
4321 g_mouseMovedCallback.get() ( window_to_normalised_device( position, m_width, m_height ) );
4324 void onMouseUp( const WindowVector& position, ButtonIdentifier button, ModifierFlags modifiers ){
4325 if ( ( button == c_button_select || button == c_button_select2 ) && !g_mouseUpCallback.empty() ) {
4326 m_mouse_down = false;
4328 g_mouseUpCallback.get() ( window_to_normalised_device( position, m_width, m_height ) );
4330 //L button w/o scene changed = tunnel selection
4331 if( // !getSelectionSystem().m_undo_begun &&
4332 modifiers == c_modifierNone && button == c_button_select &&
4333 //( !m_selector.m_mouseMoved || !m_mouse_down ) &&
4334 !m_selector.m_mouseMovedWhilePressed &&
4335 ( getSelectionSystem().Mode() != SelectionSystem::eComponent || getSelectionSystem().ManipulatorMode() != SelectionSystem::eDrag ) ){
4336 m_selector.testSelect_simpleM1( device_constrained( window_to_normalised_device( position, m_width, m_height ) ) );
4338 //getSelectionSystem().m_undo_begun = false;
4339 m_selector.m_mouseMoved = false;
4340 m_selector.m_mouseMovedWhilePressed = false;
4342 void onModifierDown( ModifierFlags type ){
4343 m_selector.modifierEnable( type );
4344 m_manipulator.modifierEnable( type );
4346 void onModifierUp( ModifierFlags type ){
4347 m_selector.modifierDisable( type );
4348 m_manipulator.modifierDisable( type );
4354 SelectionSystemWindowObserver* NewWindowObserver(){
4355 return new RadiantWindowObserver;
4360 #include "modulesystem/singletonmodule.h"
4361 #include "modulesystem/moduleregistry.h"
4363 class SelectionDependencies :
4364 public GlobalSceneGraphModuleRef,
4365 public GlobalShaderCacheModuleRef,
4366 public GlobalOpenGLModuleRef
4370 class SelectionAPI : public TypeSystemRef
4372 SelectionSystem* m_selection;
4374 typedef SelectionSystem Type;
4375 STRING_CONSTANT( Name, "*" );
4378 SelectionSystem_Construct();
4380 m_selection = &getSelectionSystem();
4383 SelectionSystem_Destroy();
4385 SelectionSystem* getTable(){
4390 typedef SingletonModule<SelectionAPI, SelectionDependencies> SelectionModule;
4391 typedef Static<SelectionModule> StaticSelectionModule;
4392 StaticRegisterModule staticRegisterSelection( StaticSelectionModule::instance() );