2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "debugging/debugging.h"
27 #include "iselection.h"
32 #include "stream/stringstream.h"
33 #include "signal/isignal.h"
34 #include "shaderlib.h"
37 #include "gtkutil/idledraw.h"
38 #include "gtkutil/dialog.h"
39 #include "gtkutil/widget.h"
40 #include "brushmanip.h"
43 #include "patchmanip.h"
44 #include "patchdialog.h"
45 #include "selection.h"
46 #include "texwindow.h"
48 #include "mainframe.h"
51 #include "entityinspector.h"
55 select_workzone_t g_select_workzone;
59 Loops over all selected brushes and stores their
60 world AABBs in the specified array.
62 class CollectSelectedBrushesBounds : public SelectionSystem::Visitor
64 AABB* m_bounds; // array of AABBs
65 Unsigned m_max; // max AABB-elements in array
66 Unsigned& m_count; // count of valid AABBs stored in array
69 CollectSelectedBrushesBounds( AABB* bounds, Unsigned max, Unsigned& count )
76 void visit( scene::Instance& instance ) const {
77 ASSERT_MESSAGE( m_count <= m_max, "Invalid m_count in CollectSelectedBrushesBounds" );
79 // stop if the array is already full
80 if ( m_count == m_max ) {
84 Selectable* selectable = Instance_getSelectable( instance );
85 if ( ( selectable != 0 )
86 && instance.isSelected() ) {
88 if ( Instance_getBrush( instance ) != 0 ) {
89 m_bounds[m_count] = instance.worldAABB();
97 Selects all objects that intersect one of the bounding AABBs.
98 The exact intersection-method is specified through TSelectionPolicy
100 template<class TSelectionPolicy>
101 class SelectByBounds : public scene::Graph::Walker
103 AABB* m_aabbs; // selection aabbs
104 Unsigned m_count; // number of aabbs in m_aabbs
105 TSelectionPolicy policy; // type that contains a custom intersection method aabb<->aabb
108 SelectByBounds( AABB* aabbs, Unsigned count )
113 bool pre( const scene::Path& path, scene::Instance& instance ) const {
114 if( path.top().get().visible() ){
115 Selectable* selectable = Instance_getSelectable( instance );
118 Entity* entity = Node_getEntity( path.top() );
120 if ( string_equal( entity->getKeyValue( "classname" ), "worldspawn" ) ) {
125 if ( ( path.size() > 1 ) &&
126 ( !path.top().get().isRoot() ) &&
127 ( selectable != 0 ) &&
128 ( !node_is_group( path.top() ) )
130 for ( Unsigned i = 0; i < m_count; ++i )
132 if ( policy.Evaluate( m_aabbs[i], instance ) ) {
133 selectable->setSelected( true );
146 Performs selection operation on the global scenegraph.
147 If delete_bounds_src is true, then the objects which were
148 used as source for the selection aabbs will be deleted.
150 static void DoSelection( bool delete_bounds_src = true ){
151 if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
152 // we may not need all AABBs since not all selected objects have to be brushes
153 const Unsigned max = (Unsigned)GlobalSelectionSystem().countSelected();
154 AABB* aabbs = new AABB[max];
157 CollectSelectedBrushesBounds collector( aabbs, max, count );
158 GlobalSelectionSystem().foreachSelected( collector );
160 // nothing usable in selection
166 // delete selected objects
167 if ( delete_bounds_src ) { // see deleteSelection
168 UndoableCommand undo( "deleteSelected" );
172 // select objects with bounds
173 GlobalSceneGraph().traverse( SelectByBounds<TSelectionPolicy>( aabbs, count ) );
182 SelectionPolicy for SelectByBounds
183 Returns true if box and the AABB of instance intersect
185 class SelectionPolicy_Touching
188 bool Evaluate( const AABB& box, scene::Instance& instance ) const {
189 const AABB& other( instance.worldAABB() );
190 for ( Unsigned i = 0; i < 3; ++i )
192 if ( fabsf( box.origin[i] - other.origin[i] ) > ( box.extents[i] + other.extents[i] ) ) {
201 SelectionPolicy for SelectByBounds
202 Returns true if the AABB of instance is inside box
204 class SelectionPolicy_Inside
207 bool Evaluate( const AABB& box, scene::Instance& instance ) const {
208 const AABB& other( instance.worldAABB() );
209 for ( Unsigned i = 0; i < 3; ++i )
211 if ( fabsf( box.origin[i] - other.origin[i] ) > ( box.extents[i] - other.extents[i] ) ) {
219 class DeleteSelected : public scene::Graph::Walker
221 mutable bool m_remove;
222 mutable bool m_removedChild;
225 : m_remove( false ), m_removedChild( false ){
227 bool pre( const scene::Path& path, scene::Instance& instance ) const {
228 m_removedChild = false;
230 Selectable* selectable = Instance_getSelectable( instance );
232 && selectable->isSelected()
234 && !path.top().get().isRoot() ) {
237 return false; // dont traverse into child elements
241 void post( const scene::Path& path, scene::Instance& instance ) const {
243 if ( m_removedChild ) {
244 m_removedChild = false;
246 // delete empty entities
247 Entity* entity = Node_getEntity( path.top() );
249 && path.top().get_pointer() != Map_FindWorldspawn( g_map )
250 && Node_getTraversable( path.top() )->empty() ) {
251 Path_deleteTop( path );
255 // node should be removed
257 if ( Node_isEntity( path.parent() ) != 0 ) {
258 m_removedChild = true;
262 Path_deleteTop( path );
267 void Scene_DeleteSelected( scene::Graph& graph ){
268 graph.traverse( DeleteSelected() );
272 void Select_Delete( void ){
273 Scene_DeleteSelected( GlobalSceneGraph() );
276 class InvertSelectionWalker : public scene::Graph::Walker
278 SelectionSystem::EMode m_mode;
279 SelectionSystem::EComponentMode m_compmode;
280 mutable Selectable* m_selectable;
282 InvertSelectionWalker( SelectionSystem::EMode mode, SelectionSystem::EComponentMode compmode )
283 : m_mode( mode ), m_compmode( compmode ), m_selectable( 0 ){
285 bool pre( const scene::Path& path, scene::Instance& instance ) const {
286 if( !path.top().get().visible() ){
290 Selectable* selectable = Instance_getSelectable( instance );
294 case SelectionSystem::eEntity:
295 if ( Node_isEntity( path.top() ) != 0 ) {
296 m_selectable = path.top().get().visible() ? selectable : 0;
299 case SelectionSystem::ePrimitive:
300 m_selectable = path.top().get().visible() ? selectable : 0;
302 case SelectionSystem::eComponent:
303 BrushInstance* brushinstance = Instance_getBrush( instance );
304 if( brushinstance != 0 ){
305 if( brushinstance->isSelected() )
306 brushinstance->invertComponentSelection( m_compmode );
309 PatchInstance* patchinstance = Instance_getPatch( instance );
310 if( patchinstance != 0 && m_compmode == SelectionSystem::eVertex ){
311 if( patchinstance->isSelected() )
312 patchinstance->invertComponentSelection();
320 void post( const scene::Path& path, scene::Instance& instance ) const {
321 if ( m_selectable != 0 ) {
322 m_selectable->setSelected( !m_selectable->isSelected() );
328 void Scene_Invert_Selection( scene::Graph& graph ){
329 graph.traverse( InvertSelectionWalker( GlobalSelectionSystem().Mode(), GlobalSelectionSystem().ComponentMode() ) );
332 void Select_Invert(){
333 Scene_Invert_Selection( GlobalSceneGraph() );
336 //interesting printings
337 class ExpandSelectionToEntitiesWalker_dbg : public scene::Graph::Walker
339 mutable std::size_t m_depth;
340 NodeSmartReference worldspawn;
342 ExpandSelectionToEntitiesWalker_dbg() : m_depth( 0 ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){
344 bool pre( const scene::Path& path, scene::Instance& instance ) const {
346 globalOutputStream() << "pre depth_" << m_depth;
347 globalOutputStream() << " path.size()_" << path.size();
348 if ( path.top().get() == worldspawn )
349 globalOutputStream() << " worldspawn";
350 if( path.top().get().isRoot() )
351 globalOutputStream() << " path.top().get().isRoot()";
352 Entity* entity = Node_getEntity( path.top() );
354 globalOutputStream() << " entity!=0";
355 if( entity->isContainer() ){
356 globalOutputStream() << " entity->isContainer()";
358 globalOutputStream() << " classname_" << entity->getKeyValue( "classname" );
360 globalOutputStream() << "\n";
361 // globalOutputStream() << "" << ;
362 // globalOutputStream() << "" << ;
363 // globalOutputStream() << "" << ;
364 // globalOutputStream() << "" << ;
367 void post( const scene::Path& path, scene::Instance& instance ) const {
368 globalOutputStream() << "post depth_" << m_depth;
369 globalOutputStream() << " path.size()_" << path.size();
370 if ( path.top().get() == worldspawn )
371 globalOutputStream() << " worldspawn";
372 if( path.top().get().isRoot() )
373 globalOutputStream() << " path.top().get().isRoot()";
374 Entity* entity = Node_getEntity( path.top() );
376 globalOutputStream() << " entity!=0";
377 if( entity->isContainer() ){
378 globalOutputStream() << " entity->isContainer()";
380 globalOutputStream() << " classname_" << entity->getKeyValue( "classname" );
382 globalOutputStream() << "\n";
387 class ExpandSelectionToEntitiesWalker : public scene::Graph::Walker
389 mutable std::size_t m_depth;
390 NodeSmartReference worldspawn;
392 ExpandSelectionToEntitiesWalker() : m_depth( 0 ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){
394 bool pre( const scene::Path& path, scene::Instance& instance ) const {
398 // NodeSmartReference me( path.top().get() );
399 // if ( me == worldspawn ) {
403 if ( m_depth == 2 ) { // entity depth
404 // traverse and select children if any one is selected
405 bool beselected = false;
406 if ( instance.childSelected() || instance.isSelected() ) {
408 if( path.top().get() != worldspawn ){
409 Instance_setSelected( instance, true );
412 return Node_getEntity( path.top() )->isContainer() && beselected;
414 else if ( m_depth == 3 ) { // primitive depth
415 Instance_setSelected( instance, true );
420 void post( const scene::Path& path, scene::Instance& instance ) const {
425 void Scene_ExpandSelectionToEntities(){
426 GlobalSceneGraph().traverse( ExpandSelectionToEntitiesWalker() );
432 void Selection_UpdateWorkzone(){
433 if ( GlobalSelectionSystem().countSelected() != 0 ) {
434 Select_GetBounds( g_select_workzone.d_work_min, g_select_workzone.d_work_max );
437 typedef FreeCaller<Selection_UpdateWorkzone> SelectionUpdateWorkzoneCaller;
439 IdleDraw g_idleWorkzone = IdleDraw( SelectionUpdateWorkzoneCaller() );
442 const select_workzone_t& Select_getWorkZone(){
443 g_idleWorkzone.flush();
444 return g_select_workzone;
447 void UpdateWorkzone_ForSelection(){
448 g_idleWorkzone.queueDraw();
451 // update the workzone to the current selection
452 void UpdateWorkzone_ForSelectionChanged( const Selectable& selectable ){
453 if ( selectable.isSelected() ) {
454 UpdateWorkzone_ForSelection();
458 void Select_SetShader( const char* shader ){
459 if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) {
460 Scene_BrushSetShader_Selected( GlobalSceneGraph(), shader );
461 Scene_PatchSetShader_Selected( GlobalSceneGraph(), shader );
463 Scene_BrushSetShader_Component_Selected( GlobalSceneGraph(), shader );
466 void Select_SetTexdef( const TextureProjection& projection ){
467 if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) {
468 Scene_BrushSetTexdef_Selected( GlobalSceneGraph(), projection );
470 Scene_BrushSetTexdef_Component_Selected( GlobalSceneGraph(), projection );
473 void Select_SetFlags( const ContentsFlagsValue& flags ){
474 if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) {
475 Scene_BrushSetFlags_Selected( GlobalSceneGraph(), flags );
477 Scene_BrushSetFlags_Component_Selected( GlobalSceneGraph(), flags );
480 void Select_GetBounds( Vector3& mins, Vector3& maxs ){
482 Scene_BoundsSelected( GlobalSceneGraph(), bounds );
483 maxs = vector3_added( bounds.origin, bounds.extents );
484 mins = vector3_subtracted( bounds.origin, bounds.extents );
487 void Select_GetMid( Vector3& mid ){
489 Scene_BoundsSelected( GlobalSceneGraph(), bounds );
490 mid = vector3_snapped( bounds.origin );
494 void Select_FlipAxis( int axis ){
495 Vector3 flip( 1, 1, 1 );
497 GlobalSelectionSystem().scaleSelected( flip );
501 void Select_Scale( float x, float y, float z ){
502 GlobalSelectionSystem().scaleSelected( Vector3( x, y, z ) );
518 inline Matrix4 matrix4_rotation_for_axis90( axis_t axis, sign_t sign ){
522 if ( sign == eSignPositive ) {
523 return matrix4_rotation_for_sincos_x( 1, 0 );
527 return matrix4_rotation_for_sincos_x( -1, 0 );
530 if ( sign == eSignPositive ) {
531 return matrix4_rotation_for_sincos_y( 1, 0 );
535 return matrix4_rotation_for_sincos_y( -1, 0 );
537 default: //case eAxisZ:
538 if ( sign == eSignPositive ) {
539 return matrix4_rotation_for_sincos_z( 1, 0 );
543 return matrix4_rotation_for_sincos_z( -1, 0 );
548 inline void matrix4_rotate_by_axis90( Matrix4& matrix, axis_t axis, sign_t sign ){
549 matrix4_multiply_by_matrix4( matrix, matrix4_rotation_for_axis90( axis, sign ) );
552 inline void matrix4_pivoted_rotate_by_axis90( Matrix4& matrix, axis_t axis, sign_t sign, const Vector3& pivotpoint ){
553 matrix4_translate_by_vec3( matrix, pivotpoint );
554 matrix4_rotate_by_axis90( matrix, axis, sign );
555 matrix4_translate_by_vec3( matrix, vector3_negated( pivotpoint ) );
558 inline Quaternion quaternion_for_axis90( axis_t axis, sign_t sign ){
563 if ( sign == eSignPositive ) {
564 return Quaternion( c_half_sqrt2f, 0, 0, c_half_sqrt2f );
568 return Quaternion( -c_half_sqrt2f, 0, 0, -c_half_sqrt2f );
571 if ( sign == eSignPositive ) {
572 return Quaternion( 0, c_half_sqrt2f, 0, c_half_sqrt2f );
576 return Quaternion( 0, -c_half_sqrt2f, 0, -c_half_sqrt2f );
578 default: //case eAxisZ:
579 if ( sign == eSignPositive ) {
580 return Quaternion( 0, 0, c_half_sqrt2f, c_half_sqrt2f );
584 return Quaternion( 0, 0, -c_half_sqrt2f, -c_half_sqrt2f );
588 quaternion_for_matrix4_rotation( matrix4_rotation_for_axis90( (axis_t)axis, ( deg > 0 ) ? eSignPositive : eSignNegative ) );
592 void Select_RotateAxis( int axis, float deg ){
593 if ( fabs( deg ) == 90.f ) {
594 GlobalSelectionSystem().rotateSelected( quaternion_for_axis90( (axis_t)axis, ( deg > 0 ) ? eSignPositive : eSignNegative ), true );
601 GlobalSelectionSystem().rotateSelected( quaternion_for_matrix4_rotation( matrix4_rotation_for_x_degrees( deg ) ), false );
604 GlobalSelectionSystem().rotateSelected( quaternion_for_matrix4_rotation( matrix4_rotation_for_y_degrees( deg ) ), false );
607 GlobalSelectionSystem().rotateSelected( quaternion_for_matrix4_rotation( matrix4_rotation_for_z_degrees( deg ) ), false );
614 void Select_ShiftTexture( float x, float y ){
615 if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) {
616 Scene_BrushShiftTexdef_Selected( GlobalSceneGraph(), x, y );
617 Scene_PatchTranslateTexture_Selected( GlobalSceneGraph(), x, y );
619 //globalOutputStream() << "shift selected face textures: s=" << x << " t=" << y << '\n';
620 Scene_BrushShiftTexdef_Component_Selected( GlobalSceneGraph(), x, y );
623 void Select_ScaleTexture( float x, float y ){
624 if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) {
625 Scene_BrushScaleTexdef_Selected( GlobalSceneGraph(), x, y );
626 Scene_PatchScaleTexture_Selected( GlobalSceneGraph(), x, y );
628 Scene_BrushScaleTexdef_Component_Selected( GlobalSceneGraph(), x, y );
631 void Select_RotateTexture( float amt ){
632 if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) {
633 Scene_BrushRotateTexdef_Selected( GlobalSceneGraph(), amt );
634 Scene_PatchRotateTexture_Selected( GlobalSceneGraph(), amt );
636 Scene_BrushRotateTexdef_Component_Selected( GlobalSceneGraph(), amt );
639 // TTimo modified to handle shader architecture:
640 // expects shader names at input, comparison relies on shader names .. texture names no longer relevant
641 void FindReplaceTextures( const char* pFind, const char* pReplace, bool bSelected ){
642 if ( !texdef_name_valid( pFind ) ) {
643 globalErrorStream() << "FindReplaceTextures: invalid texture name: '" << pFind << "', aborted\n";
646 if ( !texdef_name_valid( pReplace ) ) {
647 globalErrorStream() << "FindReplaceTextures: invalid texture name: '" << pReplace << "', aborted\n";
651 StringOutputStream command;
652 command << "textureFindReplace -find " << pFind << " -replace " << pReplace;
653 UndoableCommand undo( command.c_str() );
656 if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) {
657 Scene_BrushFindReplaceShader_Selected( GlobalSceneGraph(), pFind, pReplace );
658 Scene_PatchFindReplaceShader_Selected( GlobalSceneGraph(), pFind, pReplace );
660 Scene_BrushFindReplaceShader_Component_Selected( GlobalSceneGraph(), pFind, pReplace );
664 Scene_BrushFindReplaceShader( GlobalSceneGraph(), pFind, pReplace );
665 Scene_PatchFindReplaceShader( GlobalSceneGraph(), pFind, pReplace );
669 typedef std::vector<const char*> PropertyValues;
671 bool propertyvalues_contain( const PropertyValues& propertyvalues, const char *str ){
672 for ( PropertyValues::const_iterator i = propertyvalues.begin(); i != propertyvalues.end(); ++i )
674 if ( string_equal( str, *i ) ) {
681 class EntityFindByPropertyValueWalker : public scene::Graph::Walker
683 const PropertyValues& m_propertyvalues;
685 const NodeSmartReference worldspawn;
687 EntityFindByPropertyValueWalker( const char *prop, const PropertyValues& propertyvalues )
688 : m_propertyvalues( propertyvalues ), m_prop( prop ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){
690 bool pre( const scene::Path& path, scene::Instance& instance ) const {
691 if( !path.top().get().visible() ){
695 if ( path.top().get() == worldspawn ) {
699 Entity* entity = Node_getEntity( path.top() );
701 if( propertyvalues_contain( m_propertyvalues, entity->getKeyValue( m_prop ) ) ) {
702 Instance_getSelectable( instance )->setSelected( true );
707 else if( path.size() > 2 && !path.top().get().isRoot() ){
708 Selectable* selectable = Instance_getSelectable( instance );
709 if( selectable != 0 )
710 selectable->setSelected( true );
716 void Scene_EntitySelectByPropertyValues( scene::Graph& graph, const char *prop, const PropertyValues& propertyvalues ){
717 graph.traverse( EntityFindByPropertyValueWalker( prop, propertyvalues ) );
720 class EntityGetSelectedPropertyValuesWalker : public scene::Graph::Walker
722 PropertyValues& m_propertyvalues;
724 const NodeSmartReference worldspawn;
726 EntityGetSelectedPropertyValuesWalker( const char *prop, PropertyValues& propertyvalues )
727 : m_propertyvalues( propertyvalues ), m_prop( prop ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){
729 bool pre( const scene::Path& path, scene::Instance& instance ) const {
730 Entity* entity = Node_getEntity( path.top() );
732 if( path.top().get() != worldspawn ){
733 Selectable* selectable = Instance_getSelectable( instance );
734 if ( ( selectable != 0 && selectable->isSelected() ) || instance.childSelected() ) {
735 if ( !propertyvalues_contain( m_propertyvalues, entity->getKeyValue( m_prop ) ) ) {
736 m_propertyvalues.push_back( entity->getKeyValue( m_prop ) );
746 class EntityGetSelectedPropertyValuesWalker : public scene::Graph::Walker
748 PropertyValues& m_propertyvalues;
750 mutable bool m_selected_children;
751 const NodeSmartReference worldspawn;
753 EntityGetSelectedPropertyValuesWalker( const char *prop, PropertyValues& propertyvalues )
754 : m_propertyvalues( propertyvalues ), m_prop( prop ), m_selected_children( false ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){
756 bool pre( const scene::Path& path, scene::Instance& instance ) const {
757 Selectable* selectable = Instance_getSelectable( instance );
759 && selectable->isSelected() ) {
760 Entity* entity = Node_getEntity( path.top() );
762 if ( !propertyvalues_contain( m_propertyvalues, entity->getKeyValue( m_prop ) ) ) {
763 m_propertyvalues.push_back( entity->getKeyValue( m_prop ) );
768 m_selected_children = true;
773 void post( const scene::Path& path, scene::Instance& instance ) const {
774 Entity* entity = Node_getEntity( path.top() );
775 if( entity != 0 && m_selected_children ){
776 m_selected_children = false;
777 if( path.top().get() == worldspawn )
779 if ( !propertyvalues_contain( m_propertyvalues, entity->getKeyValue( m_prop ) ) ) {
780 m_propertyvalues.push_back( entity->getKeyValue( m_prop ) );
786 void Scene_EntityGetPropertyValues( scene::Graph& graph, const char *prop, PropertyValues& propertyvalues ){
787 graph.traverse( EntityGetSelectedPropertyValuesWalker( prop, propertyvalues ) );
790 void Select_AllOfType(){
791 if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ) {
792 if ( GlobalSelectionSystem().ComponentMode() == SelectionSystem::eFace ) {
793 GlobalSelectionSystem().setSelectedAllComponents( false );
794 Scene_BrushSelectByShader_Component( GlobalSceneGraph(), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
799 PropertyValues propertyvalues;
800 const char *prop = EntityInspector_getCurrentKey();
801 if ( !prop || !*prop ) {
804 Scene_EntityGetPropertyValues( GlobalSceneGraph(), prop, propertyvalues );
805 GlobalSelectionSystem().setSelectedAll( false );
806 if ( !propertyvalues.empty() ) {
807 Scene_EntitySelectByPropertyValues( GlobalSceneGraph(), prop, propertyvalues );
811 Scene_BrushSelectByShader( GlobalSceneGraph(), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
812 Scene_PatchSelectByShader( GlobalSceneGraph(), TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ) );
817 void Select_Inside( void ){
818 SelectByBounds<SelectionPolicy_Inside>::DoSelection();
821 void Select_Touching( void ){
822 SelectByBounds<SelectionPolicy_Touching>::DoSelection( false );
825 void Select_FitTexture( float horizontal, float vertical ){
826 if ( GlobalSelectionSystem().Mode() != SelectionSystem::eComponent ) {
827 Scene_BrushFitTexture_Selected( GlobalSceneGraph(), horizontal, vertical );
829 Scene_BrushFitTexture_Component_Selected( GlobalSceneGraph(), horizontal, vertical );
835 #include "commands.h"
838 inline void hide_node( scene::Node& node, bool hide ){
840 ? node.enable( scene::Node::eHidden )
841 : node.disable( scene::Node::eHidden );
844 bool g_nodes_be_hidden = false;
846 BoolExportCaller g_hidden_caller( g_nodes_be_hidden );
847 ToggleItem g_hidden_item( g_hidden_caller );
849 class HideSelectedWalker : public scene::Graph::Walker
853 HideSelectedWalker( bool hide )
856 bool pre( const scene::Path& path, scene::Instance& instance ) const {
857 Selectable* selectable = Instance_getSelectable( instance );
859 && selectable->isSelected() ) {
860 g_nodes_be_hidden = m_hide;
861 hide_node( path.top(), m_hide );
867 void Scene_Hide_Selected( bool hide ){
868 GlobalSceneGraph().traverse( HideSelectedWalker( hide ) );
872 Scene_Hide_Selected( true );
878 GlobalSelectionSystem().setSelectedAll( false );
879 g_hidden_item.update();
883 class HideAllWalker : public scene::Graph::Walker
887 HideAllWalker( bool hide )
890 bool pre( const scene::Path& path, scene::Instance& instance ) const {
891 hide_node( path.top(), m_hide );
896 void Scene_Hide_All( bool hide ){
897 GlobalSceneGraph().traverse( HideAllWalker( hide ) );
900 void Select_ShowAllHidden(){
901 Scene_Hide_All( false );
903 g_nodes_be_hidden = false;
904 g_hidden_item.update();
908 void Selection_Flipx(){
909 UndoableCommand undo( "mirrorSelected -axis x" );
910 Select_FlipAxis( 0 );
913 void Selection_Flipy(){
914 UndoableCommand undo( "mirrorSelected -axis y" );
915 Select_FlipAxis( 1 );
918 void Selection_Flipz(){
919 UndoableCommand undo( "mirrorSelected -axis z" );
920 Select_FlipAxis( 2 );
923 void Selection_Rotatex(){
924 UndoableCommand undo( "rotateSelected -axis x -angle -90" );
925 Select_RotateAxis( 0,-90 );
928 void Selection_Rotatey(){
929 UndoableCommand undo( "rotateSelected -axis y -angle 90" );
930 Select_RotateAxis( 1, 90 );
933 void Selection_Rotatez(){
934 UndoableCommand undo( "rotateSelected -axis z -angle -90" );
935 Select_RotateAxis( 2,-90 );
937 #include "xywindow.h"
938 void Selection_FlipHorizontally(){
939 VIEWTYPE viewtype = GlobalXYWnd_getCurrentViewType();
952 void Selection_FlipVertically(){
953 VIEWTYPE viewtype = GlobalXYWnd_getCurrentViewType();
966 void Selection_RotateClockwise(){
967 UndoableCommand undo( "rotateSelected Clockwise 90" );
968 VIEWTYPE viewtype = GlobalXYWnd_getCurrentViewType();
972 Select_RotateAxis( 2, -90 );
975 Select_RotateAxis( 1, 90 );
978 Select_RotateAxis( 0, -90 );
983 void Selection_RotateAnticlockwise(){
984 UndoableCommand undo( "rotateSelected Anticlockwise 90" );
985 VIEWTYPE viewtype = GlobalXYWnd_getCurrentViewType();
989 Select_RotateAxis( 2, 90 );
992 Select_RotateAxis( 1, -90 );
995 Select_RotateAxis( 0, 90 );
1003 void Select_registerCommands(){
1004 GlobalCommands_insert( "ShowHidden", FreeCaller<Select_ShowAllHidden>(), Accelerator( 'H', (GdkModifierType)GDK_SHIFT_MASK ) );
1005 GlobalToggles_insert( "HideSelected", FreeCaller<HideSelected>(), ToggleItem::AddCallbackCaller( g_hidden_item ), Accelerator( 'H' ) );
1007 GlobalCommands_insert( "MirrorSelectionX", FreeCaller<Selection_Flipx>() );
1008 GlobalCommands_insert( "RotateSelectionX", FreeCaller<Selection_Rotatex>() );
1009 GlobalCommands_insert( "MirrorSelectionY", FreeCaller<Selection_Flipy>() );
1010 GlobalCommands_insert( "RotateSelectionY", FreeCaller<Selection_Rotatey>() );
1011 GlobalCommands_insert( "MirrorSelectionZ", FreeCaller<Selection_Flipz>() );
1012 GlobalCommands_insert( "RotateSelectionZ", FreeCaller<Selection_Rotatez>() );
1014 GlobalCommands_insert( "MirrorSelectionHorizontally", FreeCaller<Selection_FlipHorizontally>() );
1015 GlobalCommands_insert( "MirrorSelectionVertically", FreeCaller<Selection_FlipVertically>() );
1017 GlobalCommands_insert( "RotateSelectionClockwise", FreeCaller<Selection_RotateClockwise>() );
1018 GlobalCommands_insert( "RotateSelectionAnticlockwise", FreeCaller<Selection_RotateAnticlockwise>() );
1022 void Nudge( int nDim, float fNudge ){
1023 Vector3 translate( 0, 0, 0 );
1024 translate[nDim] = fNudge;
1026 GlobalSelectionSystem().translateSelected( translate );
1029 void Selection_NudgeZ( float amount ){
1030 StringOutputStream command;
1031 command << "nudgeSelected -axis z -amount " << amount;
1032 UndoableCommand undo( command.c_str() );
1037 void Selection_MoveDown(){
1038 Selection_NudgeZ( -GetGridSize() );
1041 void Selection_MoveUp(){
1042 Selection_NudgeZ( GetGridSize() );
1045 void SceneSelectionChange( const Selectable& selectable ){
1046 SceneChangeNotify();
1049 SignalHandlerId Selection_boundsChanged;
1051 void Selection_construct(){
1052 typedef FreeCaller1<const Selectable&, SceneSelectionChange> SceneSelectionChangeCaller;
1053 GlobalSelectionSystem().addSelectionChangeCallback( SceneSelectionChangeCaller() );
1054 typedef FreeCaller1<const Selectable&, UpdateWorkzone_ForSelectionChanged> UpdateWorkzoneForSelectionChangedCaller;
1055 GlobalSelectionSystem().addSelectionChangeCallback( UpdateWorkzoneForSelectionChangedCaller() );
1056 typedef FreeCaller<UpdateWorkzone_ForSelection> UpdateWorkzoneForSelectionCaller;
1057 Selection_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback( UpdateWorkzoneForSelectionCaller() );
1060 void Selection_destroy(){
1061 GlobalSceneGraph().removeBoundsChangedCallback( Selection_boundsChanged );
1065 #include "gtkdlgs.h"
1066 #include <gtk/gtkbox.h>
1067 #include <gtk/gtkspinbutton.h>
1068 #include <gtk/gtktable.h>
1069 #include <gtk/gtklabel.h>
1070 #include <gdk/gdkkeysyms.h>
1073 inline Quaternion quaternion_for_euler_xyz_degrees( const Vector3& eulerXYZ ){
1075 return quaternion_for_matrix4_rotation( matrix4_rotation_for_euler_xyz_degrees( eulerXYZ ) );
1077 return quaternion_multiplied_by_quaternion(
1078 quaternion_multiplied_by_quaternion(
1079 quaternion_for_z( degrees_to_radians( eulerXYZ[2] ) ),
1080 quaternion_for_y( degrees_to_radians( eulerXYZ[1] ) )
1082 quaternion_for_x( degrees_to_radians( eulerXYZ[0] ) )
1085 double cx = cos( degrees_to_radians( eulerXYZ[0] * 0.5 ) );
1086 double sx = sin( degrees_to_radians( eulerXYZ[0] * 0.5 ) );
1087 double cy = cos( degrees_to_radians( eulerXYZ[1] * 0.5 ) );
1088 double sy = sin( degrees_to_radians( eulerXYZ[1] * 0.5 ) );
1089 double cz = cos( degrees_to_radians( eulerXYZ[2] * 0.5 ) );
1090 double sz = sin( degrees_to_radians( eulerXYZ[2] * 0.5 ) );
1093 cz * cy * sx - sz * sy * cx,
1094 cz * sy * cx + sz * cy * sx,
1095 sz * cy * cx - cz * sy * sx,
1096 cz * cy * cx + sz * sy * sx
1109 static gboolean rotatedlg_apply( GtkWidget *widget, RotateDialog* rotateDialog ){
1112 gtk_spin_button_update ( rotateDialog->x );
1113 gtk_spin_button_update ( rotateDialog->y );
1114 gtk_spin_button_update ( rotateDialog->z );
1115 eulerXYZ[0] = static_cast<float>( gtk_spin_button_get_value( rotateDialog->x ) );
1116 eulerXYZ[1] = static_cast<float>( gtk_spin_button_get_value( rotateDialog->y ) );
1117 eulerXYZ[2] = static_cast<float>( gtk_spin_button_get_value( rotateDialog->z ) );
1119 StringOutputStream command;
1120 command << "rotateSelectedEulerXYZ -x " << eulerXYZ[0] << " -y " << eulerXYZ[1] << " -z " << eulerXYZ[2];
1121 UndoableCommand undo( command.c_str() );
1123 GlobalSelectionSystem().rotateSelected( quaternion_for_euler_xyz_degrees( eulerXYZ ), false );
1127 static gboolean rotatedlg_cancel( GtkWidget *widget, RotateDialog* rotateDialog ){
1128 gtk_widget_hide( GTK_WIDGET( rotateDialog->window ) );
1130 gtk_spin_button_set_value( rotateDialog->x, 0.0f ); // reset to 0 on close
1131 gtk_spin_button_set_value( rotateDialog->y, 0.0f );
1132 gtk_spin_button_set_value( rotateDialog->z, 0.0f );
1137 static gboolean rotatedlg_ok( GtkWidget *widget, RotateDialog* rotateDialog ){
1138 rotatedlg_apply( widget, rotateDialog );
1139 // rotatedlg_cancel( widget, rotateDialog );
1140 gtk_widget_hide( GTK_WIDGET( rotateDialog->window ) );
1144 static gboolean rotatedlg_delete( GtkWidget *widget, GdkEventAny *event, RotateDialog* rotateDialog ){
1145 rotatedlg_cancel( widget, rotateDialog );
1149 RotateDialog g_rotate_dialog;
1151 if ( g_rotate_dialog.window == NULL ) {
1152 g_rotate_dialog.window = create_dialog_window( MainFrame_getWindow(), "Arbitrary rotation", G_CALLBACK( rotatedlg_delete ), &g_rotate_dialog );
1154 GtkAccelGroup* accel = gtk_accel_group_new();
1155 gtk_window_add_accel_group( g_rotate_dialog.window, accel );
1158 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
1159 gtk_container_add( GTK_CONTAINER( g_rotate_dialog.window ), GTK_WIDGET( hbox ) );
1161 GtkTable* table = create_dialog_table( 3, 2, 4, 4 );
1162 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
1164 GtkWidget* label = gtk_label_new( " X " );
1165 gtk_widget_show( label );
1166 gtk_table_attach( table, label, 0, 1, 0, 1,
1167 (GtkAttachOptions) ( 0 ),
1168 (GtkAttachOptions) ( 0 ), 0, 0 );
1171 GtkWidget* label = gtk_label_new( " Y " );
1172 gtk_widget_show( label );
1173 gtk_table_attach( table, label, 0, 1, 1, 2,
1174 (GtkAttachOptions) ( 0 ),
1175 (GtkAttachOptions) ( 0 ), 0, 0 );
1178 GtkWidget* label = gtk_label_new( " Z " );
1179 gtk_widget_show( label );
1180 gtk_table_attach( table, label, 0, 1, 2, 3,
1181 (GtkAttachOptions) ( 0 ),
1182 (GtkAttachOptions) ( 0 ), 0, 0 );
1185 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, -359, 359, 1, 10, 0 ) );
1186 GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 1 ) );
1187 gtk_widget_show( GTK_WIDGET( spin ) );
1188 gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 0, 1,
1189 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1190 (GtkAttachOptions) ( 0 ), 0, 0 );
1191 gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 );
1192 gtk_spin_button_set_wrap( spin, TRUE );
1194 gtk_widget_grab_focus( GTK_WIDGET( spin ) );
1196 g_rotate_dialog.x = spin;
1199 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, -359, 359, 1, 10, 0 ) );
1200 GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 1 ) );
1201 gtk_widget_show( GTK_WIDGET( spin ) );
1202 gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 1, 2,
1203 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1204 (GtkAttachOptions) ( 0 ), 0, 0 );
1205 gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 );
1206 gtk_spin_button_set_wrap( spin, TRUE );
1208 g_rotate_dialog.y = spin;
1211 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, -359, 359, 1, 10, 0 ) );
1212 GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 1 ) );
1213 gtk_widget_show( GTK_WIDGET( spin ) );
1214 gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 2, 3,
1215 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1216 (GtkAttachOptions) ( 0 ), 0, 0 );
1217 gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 );
1218 gtk_spin_button_set_wrap( spin, TRUE );
1220 g_rotate_dialog.z = spin;
1224 GtkVBox* vbox = create_dialog_vbox( 4 );
1225 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
1227 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( rotatedlg_ok ), &g_rotate_dialog );
1228 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1229 widget_make_default( GTK_WIDGET( button ) );
1230 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
1233 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( rotatedlg_cancel ), &g_rotate_dialog );
1234 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1235 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
1238 GtkButton* button = create_dialog_button( "Apply", G_CALLBACK( rotatedlg_apply ), &g_rotate_dialog );
1239 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1245 gtk_widget_show( GTK_WIDGET( g_rotate_dialog.window ) );
1264 static gboolean scaledlg_apply( GtkWidget *widget, ScaleDialog* scaleDialog ){
1267 sx = static_cast<float>( atof( gtk_entry_get_text( GTK_ENTRY( scaleDialog->x ) ) ) );
1268 sy = static_cast<float>( atof( gtk_entry_get_text( GTK_ENTRY( scaleDialog->y ) ) ) );
1269 sz = static_cast<float>( atof( gtk_entry_get_text( GTK_ENTRY( scaleDialog->z ) ) ) );
1271 StringOutputStream command;
1272 command << "scaleSelected -x " << sx << " -y " << sy << " -z " << sz;
1273 UndoableCommand undo( command.c_str() );
1275 Select_Scale( sx, sy, sz );
1280 static gboolean scaledlg_cancel( GtkWidget *widget, ScaleDialog* scaleDialog ){
1281 gtk_widget_hide( GTK_WIDGET( scaleDialog->window ) );
1283 gtk_entry_set_text( GTK_ENTRY( scaleDialog->x ), "1.0" );
1284 gtk_entry_set_text( GTK_ENTRY( scaleDialog->y ), "1.0" );
1285 gtk_entry_set_text( GTK_ENTRY( scaleDialog->z ), "1.0" );
1290 static gboolean scaledlg_ok( GtkWidget *widget, ScaleDialog* scaleDialog ){
1291 scaledlg_apply( widget, scaleDialog );
1292 //scaledlg_cancel( widget, scaleDialog );
1293 gtk_widget_hide( GTK_WIDGET( scaleDialog->window ) );
1297 static gboolean scaledlg_delete( GtkWidget *widget, GdkEventAny *event, ScaleDialog* scaleDialog ){
1298 scaledlg_cancel( widget, scaleDialog );
1302 ScaleDialog g_scale_dialog;
1305 if ( g_scale_dialog.window == NULL ) {
1306 g_scale_dialog.window = create_dialog_window( MainFrame_getWindow(), "Arbitrary scale", G_CALLBACK( scaledlg_delete ), &g_scale_dialog );
1308 GtkAccelGroup* accel = gtk_accel_group_new();
1309 gtk_window_add_accel_group( g_scale_dialog.window, accel );
1312 GtkHBox* hbox = create_dialog_hbox( 4, 4 );
1313 gtk_container_add( GTK_CONTAINER( g_scale_dialog.window ), GTK_WIDGET( hbox ) );
1315 GtkTable* table = create_dialog_table( 3, 2, 4, 4 );
1316 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
1318 GtkWidget* label = gtk_label_new( " X " );
1319 gtk_widget_show( label );
1320 gtk_table_attach( table, label, 0, 1, 0, 1,
1321 (GtkAttachOptions) ( 0 ),
1322 (GtkAttachOptions) ( 0 ), 0, 0 );
1325 GtkWidget* label = gtk_label_new( " Y " );
1326 gtk_widget_show( label );
1327 gtk_table_attach( table, label, 0, 1, 1, 2,
1328 (GtkAttachOptions) ( 0 ),
1329 (GtkAttachOptions) ( 0 ), 0, 0 );
1332 GtkWidget* label = gtk_label_new( " Z " );
1333 gtk_widget_show( label );
1334 gtk_table_attach( table, label, 0, 1, 2, 3,
1335 (GtkAttachOptions) ( 0 ),
1336 (GtkAttachOptions) ( 0 ), 0, 0 );
1339 GtkWidget* entry = gtk_entry_new();
1340 gtk_entry_set_text( GTK_ENTRY( entry ), "1.0" );
1341 gtk_widget_show( entry );
1342 gtk_table_attach( table, entry, 1, 2, 0, 1,
1343 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1344 (GtkAttachOptions) ( 0 ), 0, 0 );
1346 g_scale_dialog.x = entry;
1349 GtkWidget* entry = gtk_entry_new();
1350 gtk_entry_set_text( GTK_ENTRY( entry ), "1.0" );
1351 gtk_widget_show( entry );
1352 gtk_table_attach( table, entry, 1, 2, 1, 2,
1353 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1354 (GtkAttachOptions) ( 0 ), 0, 0 );
1356 g_scale_dialog.y = entry;
1359 GtkWidget* entry = gtk_entry_new();
1360 gtk_entry_set_text( GTK_ENTRY( entry ), "1.0" );
1361 gtk_widget_show( entry );
1362 gtk_table_attach( table, entry, 1, 2, 2, 3,
1363 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1364 (GtkAttachOptions) ( 0 ), 0, 0 );
1366 g_scale_dialog.z = entry;
1370 GtkVBox* vbox = create_dialog_vbox( 4 );
1371 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox ), TRUE, TRUE, 0 );
1373 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( scaledlg_ok ), &g_scale_dialog );
1374 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1375 widget_make_default( GTK_WIDGET( button ) );
1376 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
1379 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( scaledlg_cancel ), &g_scale_dialog );
1380 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1381 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
1384 GtkButton* button = create_dialog_button( "Apply", G_CALLBACK( scaledlg_apply ), &g_scale_dialog );
1385 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
1391 gtk_widget_show( GTK_WIDGET( g_scale_dialog.window ) );