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
26 #include "debugging/debugging.h"
29 MapModules& ReferenceAPI_getMapModules();
30 #include "iselection.h"
34 #include "ireference.h"
35 #include "ifiletypes.h"
41 #include "ifilesystem.h"
42 #include "namespace.h"
43 #include "moduleobserver.h"
47 #include <gdk/gdkkeysyms.h>
48 #include "uilib/uilib.h"
51 #include "transformlib.h"
52 #include "selectionlib.h"
53 #include "instancelib.h"
54 #include "traverselib.h"
56 #include "eclasslib.h"
58 #include "stream/textfilestream.h"
60 #include "uniquenames.h"
61 #include "modulesystem/singletonmodule.h"
62 #include "modulesystem/moduleregistry.h"
63 #include "stream/stringstream.h"
64 #include "signal/signal.h"
66 #include "gtkutil/filechooser.h"
70 #include "filetypes.h"
72 #include "entityinspector.h"
75 #include "camwindow.h"
77 #include "mainframe.h"
78 #include "preferences.h"
79 #include "preferencesystem.h"
80 #include "referencecache.h"
84 #include "brushmodule.h"
94 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
95 m_names.insert( name_read( c_str() ) );
100 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
101 m_names.erase( name_read( c_str() ) );
105 NameObserver& operator=( const NameObserver& other );
107 NameObserver( UniqueNames& names ) : m_names( names ){
110 NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
117 return string_empty( c_str() );
119 const char* c_str() const {
120 return m_name.c_str();
122 void nameChanged( const char* name ){
127 typedef MemberCaller<NameObserver, void(const char*), &NameObserver::nameChanged> NameChangedCaller;
130 class BasicNamespace : public Namespace
132 typedef std::map<NameCallback, NameObserver> Names;
134 UniqueNames m_uniqueNames;
137 ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
139 void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
140 std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
141 ASSERT_MESSAGE( result.second, "cannot attach name" );
142 attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
143 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
145 void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
146 Names::iterator i = m_names.find( setName );
147 ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
148 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
149 detachObserver( NameObserver::NameChangedCaller( ( *i ).second ) );
153 void makeUnique( const char* name, const NameCallback& setName ) const {
155 name_write( buffer, m_uniqueNames.make_unique( name_read( name ) ) );
159 void mergeNames( const BasicNamespace& other ) const {
160 typedef std::list<NameCallback> SetNameCallbacks;
161 typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
164 UniqueNames uniqueNames( other.m_uniqueNames );
166 for ( Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
168 groups[( *i ).second.c_str()].push_back( ( *i ).first );
171 for ( NameGroups::iterator i = groups.begin(); i != groups.end(); ++i )
173 name_t uniqueName( uniqueNames.make_unique( name_read( ( *i ).first.c_str() ) ) );
174 uniqueNames.insert( uniqueName );
177 name_write( buffer, uniqueName );
179 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
181 SetNameCallbacks& setNameCallbacks = ( *i ).second;
183 for ( SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j )
191 BasicNamespace g_defaultNamespace;
192 BasicNamespace g_cloneNamespace;
196 Namespace* m_namespace;
198 typedef Namespace Type;
199 STRING_CONSTANT( Name, "*" );
202 m_namespace = &g_defaultNamespace;
204 Namespace* getTable(){
209 typedef SingletonModule<NamespaceAPI> NamespaceModule;
210 typedef Static<NamespaceModule> StaticNamespaceModule;
211 StaticRegisterModule staticRegisterDefaultNamespace( StaticNamespaceModule::instance() );
214 std::list<Namespaced*> g_cloned;
216 inline Namespaced* Node_getNamespaced( scene::Node& node ){
217 return NodeTypeCast<Namespaced>::cast( node );
220 void Node_gatherNamespaced( scene::Node& node ){
221 Namespaced* namespaced = Node_getNamespaced( node );
222 if ( namespaced != 0 ) {
223 g_cloned.push_back( namespaced );
227 class GatherNamespaced : public scene::Traversable::Walker
230 bool pre( scene::Node& node ) const {
231 Node_gatherNamespaced( node );
236 void Map_gatherNamespaced( scene::Node& root ){
237 Node_traverseSubgraph( root, GatherNamespaced() );
240 void Map_mergeClonedNames(){
241 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
243 ( *i )->setNamespace( g_cloneNamespace );
245 g_cloneNamespace.mergeNames( g_defaultNamespace );
246 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
248 ( *i )->setNamespace( g_defaultNamespace );
261 void set( scene::Node* node ){
270 scene::Node* get() const {
276 void Map_SetValid( Map& map, bool valid );
277 void Map_UpdateTitle( const Map& map );
278 void Map_SetWorldspawn( Map& map, scene::Node* node );
281 class Map : public ModuleObserver
285 Resource* m_resource;
289 void ( *m_modified_changed )( const Map& );
291 Signal0 m_mapValidCallbacks;
293 WorldNode m_world_node; // "classname" "worldspawn" !
295 Map() : m_resource( 0 ), m_valid( false ), m_modified_changed( Map_UpdateTitle ){
299 if ( m_resource != 0 ) {
300 if ( Map_Unnamed( *this ) ) {
301 g_map.m_resource->setNode( NewMapRoot( "" ).get_pointer() );
302 MapFile* map = Node_getMapFile( *g_map.m_resource->getNode() );
312 GlobalSceneGraph().insert_root( *m_resource->getNode() );
316 Map_SetValid( g_map, true );
320 if ( m_resource != 0 ) {
321 Map_SetValid( g_map, false );
322 Map_SetWorldspawn( g_map, 0 );
325 GlobalUndoSystem().clear();
327 GlobalSceneGraph().erase_root();
333 Map* g_currentMap = 0;
335 void Map_addValidCallback( Map& map, const SignalHandler& handler ){
336 map.m_mapValidCallbacks.connectLast( handler );
339 bool Map_Valid( const Map& map ){
343 void Map_SetValid( Map& map, bool valid ){
345 map.m_mapValidCallbacks();
349 const char* Map_Name( const Map& map ){
350 return map.m_name.c_str();
353 bool Map_Unnamed( const Map& map ){
354 return string_equal( Map_Name( map ), "unnamed.map" );
357 inline const MapFormat& MapFormat_forFile( const char* filename ){
358 const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), path_get_extension( filename ) );
359 MapFormat* format = Radiant_getMapModules().findModule( moduleName );
360 ASSERT_MESSAGE( format != 0, "map format not found for file " << makeQuoted( filename ) );
364 const MapFormat& Map_getFormat( const Map& map ){
365 return MapFormat_forFile( Map_Name( map ) );
369 bool Map_Modified( const Map& map ){
370 return map.m_modified;
373 void Map_SetModified( Map& map, bool modified ){
374 if ( map.m_modified ^ modified ) {
375 map.m_modified = modified;
377 map.m_modified_changed( map );
381 void Map_UpdateTitle( const Map& map ){
382 Sys_SetTitle( map.m_name.c_str(), Map_Modified( map ) );
387 scene::Node* Map_GetWorldspawn( const Map& map ){
388 return map.m_world_node.get();
391 void Map_SetWorldspawn( Map& map, scene::Node* node ){
392 map.m_world_node.set( node );
397 // need that in a variable, will have to tweak depending on the game
398 float g_MaxWorldCoord = 64 * 1024;
399 float g_MinWorldCoord = -64 * 1024;
401 void AddRegionBrushes( void );
402 void RemoveRegionBrushes( void );
409 free all map elements, reinitialize the structures that depend on them
415 g_map.m_resource->detach( g_map );
416 GlobalReferenceCache().release( g_map.m_name.c_str() );
417 g_map.m_resource = 0;
422 Brush_unlatchPreferences();
425 class EntityFindByClassname : public scene::Graph::Walker
430 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
433 bool pre( const scene::Path& path, scene::Instance& instance ) const {
434 if ( m_entity == 0 ) {
435 Entity* entity = Node_getEntity( path.top() );
437 && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
445 Entity* Scene_FindEntityByClass( const char* name ){
447 GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
451 Entity *Scene_FindPlayerStart(){
452 typedef const char* StaticString;
453 StaticString strings[] = {
455 "info_player_deathmatch",
456 "team_CTF_redplayer",
457 "team_CTF_blueplayer",
459 "team_CTF_bluespawn",
461 typedef const StaticString* StaticStringIterator;
462 for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
464 Entity* entity = Scene_FindEntityByClass( *i );
473 // move the view to a start position
477 void FocusViews( const Vector3& point, float angle ){
478 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
479 Camera_setOrigin( camwnd, point );
480 Vector3 angles( Camera_getAngles( camwnd ) );
481 angles[CAMERA_PITCH] = 0;
482 angles[CAMERA_YAW] = angle;
483 Camera_setAngles( camwnd, angles );
485 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
486 xywnd->SetOrigin( point );
489 #include "stringio.h"
491 void Map_StartPosition(){
492 Entity* entity = Scene_FindPlayerStart();
496 string_parse_vector3( entity->getKeyValue( "origin" ), origin );
497 FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
501 FocusViews( g_vector3_identity, 0 );
506 inline bool node_is_worldspawn( scene::Node& node ){
507 Entity* entity = Node_getEntity( node );
508 return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
512 // use first worldspawn
513 class entity_updateworldspawn : public scene::Traversable::Walker
516 bool pre( scene::Node& node ) const {
517 if ( node_is_worldspawn( node ) ) {
518 if ( Map_GetWorldspawn( g_map ) == 0 ) {
519 Map_SetWorldspawn( g_map, &node );
526 scene::Node* Map_FindWorldspawn( Map& map ){
527 Map_SetWorldspawn( map, 0 );
529 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
531 return Map_GetWorldspawn( map );
535 class CollectAllWalker : public scene::Traversable::Walker
538 UnsortedNodeSet& m_nodes;
540 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
542 bool pre( scene::Node& node ) const {
543 m_nodes.insert( NodeSmartReference( node ) );
544 Node_getTraversable( m_root )->erase( node );
549 void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
550 UnsortedNodeSet nodes;
551 Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
552 Node_getTraversable( parent )->insert( child );
554 for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
556 Node_getTraversable( parent )->insert( ( *i ) );
560 scene::Node& createWorldspawn(){
561 NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
562 Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
566 void Map_UpdateWorldspawn( Map& map ){
567 if ( Map_FindWorldspawn( map ) == 0 ) {
568 Map_SetWorldspawn( map, &createWorldspawn() );
572 scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
573 Map_UpdateWorldspawn( map );
574 return *Map_GetWorldspawn( map );
578 class MapMergeAll : public scene::Traversable::Walker
580 mutable scene::Path m_path;
582 MapMergeAll( const scene::Path& root )
585 bool pre( scene::Node& node ) const {
586 Node_getTraversable( m_path.top() )->insert( node );
587 m_path.push( makeReference( node ) );
588 selectPath( m_path, true );
591 void post( scene::Node& node ) const {
596 class MapMergeEntities : public scene::Traversable::Walker
598 mutable scene::Path m_path;
600 MapMergeEntities( const scene::Path& root )
603 bool pre( scene::Node& node ) const {
604 if ( node_is_worldspawn( node ) ) {
605 scene::Node* world_node = Map_FindWorldspawn( g_map );
606 if ( world_node == 0 ) {
607 Map_SetWorldspawn( g_map, &node );
608 Node_getTraversable( m_path.top().get() )->insert( node );
609 m_path.push( makeReference( node ) );
610 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
614 m_path.push( makeReference( *world_node ) );
615 Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
620 Node_getTraversable( m_path.top() )->insert( node );
621 m_path.push( makeReference( node ) );
622 if ( node_is_group( node ) ) {
623 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
627 selectPath( m_path, true );
632 void post( scene::Node& node ) const {
637 class BasicContainer : public scene::Node::Symbiot
641 NodeTypeCastTable m_casts;
644 NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
646 NodeTypeCastTable& get(){
652 TraversableNodeSet m_traverse;
655 typedef LazyStatic<TypeCasts> StaticTypeCasts;
657 scene::Traversable& get( NullType<scene::Traversable>){
661 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
671 /// Merges the map graph rooted at \p node into the global scene-graph.
672 void MergeMap( scene::Node& node ){
673 Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
675 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
676 NodeSmartReference node( ( new BasicContainer )->node() );
677 format.readGraph( node, in, GlobalEntityCreator() );
678 Map_gatherNamespaced( node );
679 Map_mergeClonedNames();
683 inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
684 return NodeTypeCast<scene::Cloneable>::cast( node );
687 inline scene::Node& node_clone( scene::Node& node ){
688 scene::Cloneable* cloneable = Node_getCloneable( node );
689 if ( cloneable != 0 ) {
690 return cloneable->clone();
693 return ( new scene::NullNode )->node();
696 class CloneAll : public scene::Traversable::Walker
698 mutable scene::Path m_path;
700 CloneAll( scene::Node& root )
701 : m_path( makeReference( root ) ){
703 bool pre( scene::Node& node ) const {
704 if ( node.isRoot() ) {
708 m_path.push( makeReference( node_clone( node ) ) );
709 m_path.top().get().IncRef();
713 void post( scene::Node& node ) const {
714 if ( node.isRoot() ) {
718 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
720 m_path.top().get().DecRef();
725 scene::Node& Node_Clone( scene::Node& node ){
726 scene::Node& clone = node_clone( node );
727 scene::Traversable* traversable = Node_getTraversable( node );
728 if ( traversable != 0 ) {
729 traversable->traverse( CloneAll( clone ) );
735 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
737 class EntityBreakdownWalker : public scene::Graph::Walker
739 EntityBreakdown& m_entitymap;
741 EntityBreakdownWalker( EntityBreakdown& entitymap )
742 : m_entitymap( entitymap ){
744 bool pre( const scene::Path& path, scene::Instance& instance ) const {
745 Entity* entity = Node_getEntity( path.top() );
747 const EntityClass& eclass = entity->getEntityClass();
748 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
749 m_entitymap[eclass.name()] = 1;
751 else{ ++m_entitymap[eclass.name()]; }
757 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
758 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
762 WindowPosition g_posMapInfoWnd( c_default_window_pos );
766 ui::Entry brushes_entry{ui::null};
767 ui::Entry entities_entry{ui::null};
768 ui::ListStore EntityBreakdownWalker{ui::null};
770 ui::Window window = MainFrame_getWindow().create_dialog_window("Map Info", G_CALLBACK(dialog_delete_callback ), &dialog );
772 window_set_position( window, g_posMapInfoWnd );
775 auto vbox = create_dialog_vbox( 4, 4 );
779 auto hbox = create_dialog_hbox( 4 );
780 vbox.pack_start( hbox, FALSE, TRUE, 0 );
783 auto table = create_dialog_table( 2, 2, 4, 4 );
784 hbox.pack_start( table, TRUE, TRUE, 0 );
787 auto entry = ui::Entry(ui::New);
789 table.attach(entry, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
790 gtk_editable_set_editable( GTK_EDITABLE(entry), FALSE );
792 brushes_entry = entry;
795 auto entry = ui::Entry(ui::New);
797 table.attach(entry, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
798 gtk_editable_set_editable( GTK_EDITABLE(entry), FALSE );
800 entities_entry = entry;
803 ui::Widget label = ui::Label( "Total Brushes" );
805 table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
806 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
809 ui::Widget label = ui::Label( "Total Entities" );
811 table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
812 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
816 auto vbox2 = create_dialog_vbox( 4 );
817 hbox.pack_start( vbox2, FALSE, FALSE, 0 );
820 auto button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
821 vbox2.pack_start( button, FALSE, FALSE, 0 );
826 ui::Widget label = ui::Label( "Entity breakdown" );
828 vbox.pack_start( label, FALSE, TRUE, 0 );
829 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
832 auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
833 vbox.pack_start( scr, TRUE, TRUE, 0 );
836 ui::ListStore store = ui::ListStore(gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING ));
838 auto view = ui::TreeView(ui::TreeModel(store ));
839 gtk_tree_view_set_headers_clickable(view, TRUE );
842 auto renderer = ui::CellRendererText(ui::New);
843 auto column = ui::TreeViewColumn( "Entity", renderer, {{"text", 0}} );
844 gtk_tree_view_append_column(view, column );
845 gtk_tree_view_column_set_sort_column_id( column, 0 );
849 auto renderer = ui::CellRendererText(ui::New);
850 auto column = ui::TreeViewColumn( "Count", renderer, {{"text", 1}} );
851 gtk_tree_view_append_column(view, column );
852 gtk_tree_view_column_set_sort_column_id( column, 1 );
859 EntityBreakdownWalker = store;
867 EntityBreakdown entitymap;
868 Scene_EntityBreakdown( entitymap );
870 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
873 sprintf( tmp, "%u", Unsigned( ( *i ).second ) );
874 EntityBreakdownWalker.append(0, (*i).first.c_str(), 1, tmp);
878 EntityBreakdownWalker.unref();
881 sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) );
882 brushes_entry.text(tmp);
883 sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) );
884 entities_entry.text(tmp);
886 modal_dialog_show( window, dialog );
889 window_get_position( window, g_posMapInfoWnd );
899 const char* m_message;
901 ScopeTimer( const char* message )
902 : m_message( message ){
906 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
907 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
911 CopiedString g_strLastFolder = "";
919 void Map_LoadFile( const char *filename ){
920 globalOutputStream() << "Loading map from " << filename << "\n";
921 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
923 MRU_AddFile( filename );
924 g_strLastFolder = g_path_get_dirname( filename );
927 ScopeTimer timer( "map load" );
929 const MapFormat* format = NULL;
930 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
931 if ( string_not_empty( moduleName ) ) {
932 format = ReferenceAPI_getMapModules().findModule( moduleName );
935 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
940 Brush_toggleFormat( i );
941 g_map.m_name = filename;
942 Map_UpdateTitle( g_map );
943 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
945 format->wrongFormat = false;
947 g_map.m_resource->attach( g_map );
949 if ( !format->wrongFormat ) {
955 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
958 globalOutputStream() << "--- LoadMapFile ---\n";
959 globalOutputStream() << g_map.m_name.c_str() << "\n";
961 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
962 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
964 //GlobalEntityCreator().printStatistics();
967 // move the view to a start position
971 g_currentMap = &g_map;
973 // restart VFS to apply new pak filtering based on mapname
974 // needed for daemon DPK VFS
981 virtual bool excluded( scene::Node& node ) const = 0;
984 class ExcludeWalker : public scene::Traversable::Walker
986 const scene::Traversable::Walker& m_walker;
987 const Excluder* m_exclude;
990 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
991 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
993 bool pre( scene::Node& node ) const {
994 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1000 m_walker.pre( node );
1004 void post( scene::Node& node ) const {
1010 m_walker.post( node );
1015 class AnyInstanceSelected : public scene::Instantiable::Visitor
1019 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1022 void visit( scene::Instance& instance ) const {
1023 Selectable* selectable = Instance_getSelectable( instance );
1024 if ( selectable != 0
1025 && selectable->isSelected() ) {
1031 bool Node_instanceSelected( scene::Node& node ){
1032 scene::Instantiable* instantiable = Node_getInstantiable( node );
1033 ASSERT_NOTNULL( instantiable );
1035 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1039 class SelectedDescendantWalker : public scene::Traversable::Walker
1043 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1047 bool pre( scene::Node& node ) const {
1048 if ( node.isRoot() ) {
1052 if ( Node_instanceSelected( node ) ) {
1060 bool Node_selectedDescendant( scene::Node& node ){
1062 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1066 class SelectionExcluder : public Excluder
1069 bool excluded( scene::Node& node ) const {
1070 return !Node_selectedDescendant( node );
1074 class IncludeSelectedWalker : public scene::Traversable::Walker
1076 const scene::Traversable::Walker& m_walker;
1077 mutable std::size_t m_selected;
1078 mutable bool m_skip;
1080 bool selectedParent() const {
1081 return m_selected != 0;
1084 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1085 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1087 bool pre( scene::Node& node ) const {
1089 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1090 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1091 if ( Node_instanceSelected( node ) ) {
1094 m_walker.pre( node );
1103 void post( scene::Node& node ) const {
1109 if ( Node_instanceSelected( node ) ) {
1112 m_walker.post( node );
1117 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1118 scene::Traversable* traversable = Node_getTraversable( root );
1119 if ( traversable != 0 ) {
1121 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1123 traversable->traverse( IncludeSelectedWalker( walker ) );
1128 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1129 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1132 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1133 scene::Traversable* traversable = Node_getTraversable( root );
1134 if ( traversable != 0 ) {
1135 traversable->traverse( walker );
1139 class RegionExcluder : public Excluder
1142 bool excluded( scene::Node& node ) const {
1143 return node.excluded();
1147 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1148 scene::Traversable* traversable = Node_getTraversable( root );
1149 if ( traversable != 0 ) {
1150 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1154 bool Map_SaveRegion( const char *filename ){
1157 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1159 RemoveRegionBrushes();
1165 void Map_RenameAbsolute( const char* absolute ){
1166 Resource* resource = GlobalReferenceCache().capture( absolute );
1167 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1168 resource->setNode( clone.get_pointer() );
1171 //ScopeTimer timer("clone subgraph");
1172 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1175 g_map.m_resource->detach( g_map );
1176 GlobalReferenceCache().release( g_map.m_name.c_str() );
1178 g_map.m_resource = resource;
1180 g_map.m_name = absolute;
1181 Map_UpdateTitle( g_map );
1183 g_map.m_resource->attach( g_map );
1184 // refresh VFS to apply new pak filtering based on mapname
1185 // needed for daemon DPK VFS
1189 void Map_Rename( const char* filename ){
1190 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1191 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1193 Map_RenameAbsolute( filename );
1195 SceneChangeNotify();
1206 ScopeTimer timer( "map save" );
1208 return true; // assume success..
1218 //globalOutputStream() << "Map_New\n";
1220 g_map.m_name = "unnamed.map";
1221 Map_UpdateTitle( g_map );
1224 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1225 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1226 g_map.m_resource->attach( g_map );
1228 SceneChangeNotify();
1231 FocusViews( g_vector3_identity, 0 );
1233 g_currentMap = &g_map;
1235 // restart VFS to apply new pak filtering based on mapname
1236 // needed for daemon DPK VFS
1240 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1242 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1244 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1245 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1246 for now, let's just print an error
1249 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1251 for ( int i = 0 ; i < 3 ; i++ )
1253 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1254 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1259 // write the info_playerstart
1261 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1262 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1263 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1264 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1268 ===========================================================
1272 ===========================================================
1275 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1276 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1278 scene::Node* region_sides[6];
1279 scene::Node* region_startpoint = 0;
1284 a regioned map will have temp walls put up at the region boundary
1285 \todo TODO TTimo old implementation of region brushes
1286 we still add them straight in the worldspawn and take them out after the map is saved
1287 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1290 void AddRegionBrushes( void ){
1293 for ( i = 0; i < 6; i++ )
1295 region_sides[i] = &GlobalBrushCreator().createBrush();
1296 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1299 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1301 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1302 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1304 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1307 void RemoveRegionBrushes( void ){
1308 for ( std::size_t i = 0; i < 6; i++ )
1310 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1312 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1315 inline void exclude_node( scene::Node& node, bool exclude ){
1317 ? node.enable( scene::Node::eExcluded )
1318 : node.disable( scene::Node::eExcluded );
1321 class ExcludeAllWalker : public scene::Graph::Walker
1325 ExcludeAllWalker( bool exclude )
1326 : m_exclude( exclude ){
1328 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1329 exclude_node( path.top(), m_exclude );
1335 void Scene_Exclude_All( bool exclude ){
1336 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1339 bool Instance_isSelected( const scene::Instance& instance ){
1340 const Selectable* selectable = Instance_getSelectable( instance );
1341 return selectable != 0 && selectable->isSelected();
1344 class ExcludeSelectedWalker : public scene::Graph::Walker
1348 ExcludeSelectedWalker( bool exclude )
1349 : m_exclude( exclude ){
1351 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1352 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1357 void Scene_Exclude_Selected( bool exclude ){
1358 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1361 class ExcludeRegionedWalker : public scene::Graph::Walker
1365 ExcludeRegionedWalker( bool exclude )
1366 : m_exclude( exclude ){
1368 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1373 aabb_intersects_aabb(
1374 instance.worldAABB(),
1375 aabb_for_minmax( region_mins, region_maxs )
1385 void Scene_Exclude_Region( bool exclude ){
1386 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1393 Other filtering options may still be on
1396 void Map_RegionOff(){
1397 region_active = false;
1399 region_maxs[0] = g_MaxWorldCoord - 64;
1400 region_mins[0] = g_MinWorldCoord + 64;
1401 region_maxs[1] = g_MaxWorldCoord - 64;
1402 region_mins[1] = g_MinWorldCoord + 64;
1403 region_maxs[2] = g_MaxWorldCoord - 64;
1404 region_mins[2] = g_MinWorldCoord + 64;
1406 Scene_Exclude_All( false );
1409 void Map_ApplyRegion( void ){
1410 region_active = true;
1412 Scene_Exclude_Region( false );
1417 ========================
1418 Map_RegionSelectedBrushes
1419 ========================
1421 void Map_RegionSelectedBrushes( void ){
1424 if ( GlobalSelectionSystem().countSelected() != 0
1425 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1426 region_active = true;
1427 Select_GetBounds( region_mins, region_maxs );
1429 Scene_Exclude_Selected( false );
1431 GlobalSelectionSystem().setSelectedAll( false );
1441 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1444 region_mins[0] = x_min;
1445 region_maxs[0] = x_max;
1446 region_mins[1] = y_min;
1447 region_maxs[1] = y_max;
1448 region_mins[2] = g_MinWorldCoord + 64;
1449 region_maxs[2] = g_MaxWorldCoord - 64;
1454 void Map_RegionBounds( const AABB& bounds ){
1457 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1458 region_maxs = vector3_added( bounds.origin, bounds.extents );
1470 void Map_RegionBrush( void ){
1471 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1472 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1473 Map_RegionBounds( instance.worldAABB() );
1482 bool Map_ImportFile( const char* filename ){
1483 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1485 g_strLastFolder = g_path_get_dirname( filename );
1487 bool success = false;
1489 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1494 const MapFormat* format = NULL;
1495 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1496 if ( string_not_empty( moduleName ) ) {
1497 format = ReferenceAPI_getMapModules().findModule( moduleName );
1501 format->wrongFormat = false;
1503 Resource* resource = GlobalReferenceCache().capture( filename );
1504 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1505 if ( !resource->load() ) {
1506 GlobalReferenceCache().release( filename );
1510 if ( format->wrongFormat ) {
1511 GlobalReferenceCache().release( filename );
1515 NodeSmartReference clone( NewMapRoot( "" ) );
1516 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1517 Map_gatherNamespaced( clone );
1518 Map_mergeClonedNames();
1521 GlobalReferenceCache().release( filename );
1524 SceneChangeNotify();
1530 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
1531 int n = string_length( path_get_extension( filename ) );
1532 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1533 StringBuffer output;
1534 output.push_string( AppPath_get() );
1535 output.push_string( "q3map2." );
1536 output.push_string( RADIANT_EXECUTABLE );
1537 output.push_string( " -v -game " );
1538 output.push_string( ( type && *type ) ? type : "quake3" );
1539 output.push_string( " -fs_basepath \"" );
1540 output.push_string( EnginePath_get() );
1541 output.push_string( "\" -fs_homepath \"" );
1542 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1543 output.push_string( "\" -fs_game " );
1544 output.push_string( gamename_get() );
1545 output.push_string( " -convert -format " );
1546 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1547 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1548 output.push_string( " -readmap " );
1550 output.push_string( " \"" );
1551 output.push_string( filename );
1552 output.push_string( "\"" );
1555 Q_Exec( NULL, output.c_str(), NULL, false, true );
1557 // rebuild filename as "filenamewithoutext_converted.map"
1559 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1560 output.push_string( "_converted.map" );
1561 filename = output.c_str();
1564 Resource* resource = GlobalReferenceCache().capture( filename );
1565 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1566 if ( !resource->load() ) {
1567 GlobalReferenceCache().release( filename );
1570 NodeSmartReference clone( NewMapRoot( "" ) );
1571 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1572 Map_gatherNamespaced( clone );
1573 Map_mergeClonedNames();
1576 GlobalReferenceCache().release( filename );
1579 SceneChangeNotify();
1588 bool Map_SaveFile( const char* filename ){
1589 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1590 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1592 // refresh VFS to apply new pak filtering based on mapname
1593 // needed for daemon DPK VFS
1604 // Saves selected world brushes and whole entities with partial/full selections
1606 bool Map_SaveSelected( const char* filename ){
1607 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1611 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1613 scene::Node& m_parent;
1615 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1617 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1618 if ( path.top().get_pointer() != &m_parent
1619 && Node_isPrimitive( path.top() ) ) {
1620 Selectable* selectable = Instance_getSelectable( instance );
1621 if ( selectable != 0
1622 && selectable->isSelected()
1623 && path.size() > 1 ) {
1629 void post( const scene::Path& path, scene::Instance& instance ) const {
1630 if ( path.top().get_pointer() != &m_parent
1631 && Node_isPrimitive( path.top() ) ) {
1632 Selectable* selectable = Instance_getSelectable( instance );
1633 if ( selectable != 0
1634 && selectable->isSelected()
1635 && path.size() > 1 ) {
1636 scene::Node& parent = path.parent();
1637 if ( &parent != &m_parent ) {
1638 NodeSmartReference node( path.top().get() );
1639 Node_getTraversable( parent )->erase( node );
1640 Node_getTraversable( m_parent )->insert( node );
1647 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1648 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1651 class CountSelectedBrushes : public scene::Graph::Walker
1653 std::size_t& m_count;
1654 mutable std::size_t m_depth;
1656 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1659 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1660 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1663 Selectable* selectable = Instance_getSelectable( instance );
1664 if ( selectable != 0
1665 && selectable->isSelected()
1666 && Node_isPrimitive( path.top() ) ) {
1671 void post( const scene::Path& path, scene::Instance& instance ) const {
1676 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1678 graph.traverse( CountSelectedBrushes( count ) );
1690 const char* nodetype_get_name( ENodeType type ){
1691 if ( type == eNodeMap ) {
1694 if ( type == eNodeEntity ) {
1697 if ( type == eNodePrimitive ) {
1703 ENodeType node_get_nodetype( scene::Node& node ){
1704 if ( Node_isEntity( node ) ) {
1707 if ( Node_isPrimitive( node ) ) {
1708 return eNodePrimitive;
1710 return eNodeUnknown;
1713 bool contains_entity( scene::Node& node ){
1714 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1717 bool contains_primitive( scene::Node& node ){
1718 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1721 ENodeType node_get_contains( scene::Node& node ){
1722 if ( contains_entity( node ) ) {
1725 if ( contains_primitive( node ) ) {
1726 return eNodePrimitive;
1728 return eNodeUnknown;
1731 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1732 ENodeType contains = node_get_contains( parent.top() );
1733 ENodeType type = node_get_nodetype( child.top() );
1735 if ( contains != eNodeUnknown && contains == type ) {
1736 NodeSmartReference node( child.top().get() );
1737 Path_deleteTop( child );
1738 Node_getTraversable( parent.top() )->insert( node );
1739 SceneChangeNotify();
1743 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1747 void Scene_parentSelected(){
1748 UndoableCommand undo( "parentSelected" );
1750 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1751 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1753 const scene::Path& m_parent;
1755 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1757 void visit( scene::Instance& instance ) const {
1758 if ( &m_parent != &instance.path() ) {
1759 Path_parent( m_parent, instance.path() );
1764 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1765 GlobalSelectionSystem().foreachSelected( visitor );
1769 globalOutputStream() << "failed - did not find two selected nodes.\n";
1776 if ( ConfirmModified( "New Map" ) ) {
1783 CopiedString g_mapsPath;
1785 const char* getMapsPath(){
1786 return g_mapsPath.c_str();
1789 const char* getLastFolderPath(){
1790 if (g_strLastFolder.empty()) {
1791 GlobalPreferenceSystem().registerPreference( "LastFolder", CopiedStringImportStringCaller( g_strLastFolder ), CopiedStringExportStringCaller( g_strLastFolder ) );
1792 if (g_strLastFolder.empty()) {
1793 g_strLastFolder = g_qeglobals.m_userGamePath;
1796 return g_strLastFolder.c_str();
1799 const char* map_open( const char* title ){
1800 return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), true, false, false );
1803 const char* map_import( const char* title ){
1804 return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), false, true, false );
1807 const char* map_save( const char* title ){
1808 return MainFrame_getWindow().file_dialog( FALSE, title, getLastFolderPath(), MapFormat::Name(), false, false, true );
1812 if ( !ConfirmModified( "Open Map" ) ) {
1816 const char* filename = map_open( "Open Map" );
1818 if ( filename != NULL ) {
1819 MRU_AddFile( filename );
1822 Map_LoadFile( filename );
1827 const char* filename = map_import( "Import Map" );
1829 if ( filename != NULL ) {
1830 UndoableCommand undo( "mapImport" );
1831 Map_ImportFile( filename );
1836 const char* filename = map_save( "Save Map" );
1838 if ( filename != NULL ) {
1839 g_strLastFolder = g_path_get_dirname( filename );
1840 MRU_AddFile( filename );
1841 Map_Rename( filename );
1852 if ( Map_Unnamed( g_map ) ) {
1855 else if ( Map_Modified( g_map ) ) {
1861 const char* filename = map_save( "Export Selection" );
1863 if ( filename != NULL ) {
1864 g_strLastFolder = g_path_get_dirname( filename );
1865 Map_SaveSelected( filename );
1870 const char* filename = map_save( "Export Region" );
1872 if ( filename != NULL ) {
1873 g_strLastFolder = g_path_get_dirname( filename );
1874 Map_SaveRegion( filename );
1881 SceneChangeNotify();
1886 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1887 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1888 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1889 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1891 SceneChangeNotify();
1896 SceneChangeNotify();
1899 void RegionSelected(){
1900 Map_RegionSelectedBrushes();
1901 SceneChangeNotify();
1908 class BrushFindByIndexWalker : public scene::Traversable::Walker
1910 mutable std::size_t m_index;
1911 scene::Path& m_path;
1913 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1914 : m_index( index ), m_path( path ){
1916 bool pre( scene::Node& node ) const {
1917 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1918 m_path.push( makeReference( node ) );
1924 class EntityFindByIndexWalker : public scene::Traversable::Walker
1926 mutable std::size_t m_index;
1927 scene::Path& m_path;
1929 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1930 : m_index( index ), m_path( path ){
1932 bool pre( scene::Node& node ) const {
1933 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1934 m_path.push( makeReference( node ) );
1940 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1941 path.push( makeReference( GlobalSceneGraph().root() ) );
1943 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1945 if ( path.size() == 2 ) {
1946 scene::Traversable* traversable = Node_getTraversable( path.top() );
1947 if ( traversable != 0 ) {
1948 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1953 inline bool Node_hasChildren( scene::Node& node ){
1954 scene::Traversable* traversable = Node_getTraversable( node );
1955 return traversable != 0 && !traversable->empty();
1958 void SelectBrush( int entitynum, int brushnum ){
1960 Scene_FindEntityBrush( entitynum, brushnum, path );
1961 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1962 scene::Instance* instance = GlobalSceneGraph().find( path );
1963 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1964 Selectable* selectable = Instance_getSelectable( *instance );
1965 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1966 selectable->setSelected( true );
1967 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1972 class BrushFindIndexWalker : public scene::Graph::Walker
1974 mutable const scene::Node* m_node;
1975 std::size_t& m_count;
1977 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1978 : m_node( &node ), m_count( count ){
1980 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1981 if ( Node_isPrimitive( path.top() ) ) {
1982 if ( m_node == path.top().get_pointer() ) {
1993 class EntityFindIndexWalker : public scene::Graph::Walker
1995 mutable const scene::Node* m_node;
1996 std::size_t& m_count;
1998 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
1999 : m_node( &node ), m_count( count ){
2001 bool pre( const scene::Path& path, scene::Instance& instance ) const {
2002 if ( Node_isEntity( path.top() ) ) {
2003 if ( m_node == path.top().get_pointer() ) {
2014 static void GetSelectionIndex( int *ent, int *brush ){
2015 std::size_t count_brush = 0;
2016 std::size_t count_entity = 0;
2017 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2018 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2020 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2021 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2023 *brush = int(count_brush);
2024 *ent = int(count_entity);
2029 ui::Entry entity{ui::null};
2030 ui::Entry brush{ui::null};
2032 ui::Window window = MainFrame_getWindow().create_dialog_window("Find Brush", G_CALLBACK(dialog_delete_callback ), &dialog );
2034 auto accel = ui::AccelGroup(ui::New);
2035 window.add_accel_group( accel );
2038 auto vbox = create_dialog_vbox( 4, 4 );
2041 auto table = create_dialog_table( 2, 2, 4, 4 );
2042 vbox.pack_start( table, TRUE, TRUE, 0 );
2044 ui::Widget label = ui::Label( "Entity number" );
2046 (table).attach(label, {0, 1, 0, 1}, {0, 0});
2049 ui::Widget label = ui::Label( "Brush number" );
2051 (table).attach(label, {0, 1, 1, 2}, {0, 0});
2054 auto entry = ui::Entry(ui::New);
2056 table.attach(entry, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
2057 gtk_widget_grab_focus( entry );
2061 auto entry = ui::Entry(ui::New);
2063 table.attach(entry, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
2069 auto hbox = create_dialog_hbox( 4 );
2070 vbox.pack_start( hbox, TRUE, TRUE, 0 );
2072 auto button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2073 hbox.pack_start( button, FALSE, FALSE, 0 );
2074 widget_make_default( button );
2075 gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2078 auto button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2079 hbox.pack_start( button, FALSE, FALSE, 0 );
2080 gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2085 // Initialize dialog
2089 GetSelectionIndex( &ent, &br );
2090 sprintf( buf, "%i", ent );
2092 sprintf( buf, "%i", br );
2095 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2096 const char *entstr = gtk_entry_get_text( entity );
2097 const char *brushstr = gtk_entry_get_text( brush );
2098 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2104 void Map_constructPreferences( PreferencesPage& page ){
2105 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2109 class MapEntityClasses : public ModuleObserver
2111 std::size_t m_unrealised;
2113 MapEntityClasses() : m_unrealised( 1 ){
2116 if ( --m_unrealised == 0 ) {
2117 if ( g_map.m_resource != 0 ) {
2118 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2119 g_map.m_resource->realise();
2124 if ( ++m_unrealised == 1 ) {
2125 if ( g_map.m_resource != 0 ) {
2126 g_map.m_resource->flush();
2127 g_map.m_resource->unrealise();
2133 MapEntityClasses g_MapEntityClasses;
2136 class MapModuleObserver : public ModuleObserver
2138 std::size_t m_unrealised;
2140 MapModuleObserver() : m_unrealised( 1 ){
2143 if ( --m_unrealised == 0 ) {
2144 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2145 StringOutputStream buffer( 256 );
2146 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2147 Q_mkdir( buffer.c_str() );
2148 g_mapsPath = buffer.c_str();
2152 if ( ++m_unrealised == 1 ) {
2158 MapModuleObserver g_MapModuleObserver;
2160 CopiedString g_strLastMap;
2161 bool g_bLoadLastMap = false;
2163 void Map_Construct(){
2164 GlobalCommands_insert( "RegionOff", FreeCaller<void(), RegionOff>() );
2165 GlobalCommands_insert( "RegionSetXY", FreeCaller<void(), RegionXY>() );
2166 GlobalCommands_insert( "RegionSetBrush", FreeCaller<void(), RegionBrush>() );
2167 GlobalCommands_insert( "RegionSetSelection", FreeCaller<void(), RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2169 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2170 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2171 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2173 PreferencesDialog_addSettingsPreferences( FreeCaller<void(PreferencesPage&), Map_constructPreferences>() );
2175 GlobalEntityClassManager().attach( g_MapEntityClasses );
2176 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2180 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2181 GlobalEntityClassManager().detach( g_MapEntityClasses );