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 MapModules& ReferenceAPI_getMapModules();
28 #include "iselection.h"
32 #include "ireference.h"
33 #include "ifiletypes.h"
38 #include "ifilesystem.h"
39 #include "namespace.h"
40 #include "moduleobserver.h"
45 #include <gdk/gdkkeysyms.h>
46 #include "uilib/uilib.h"
49 #include "transformlib.h"
50 #include "selectionlib.h"
51 #include "instancelib.h"
52 #include "traverselib.h"
54 #include "eclasslib.h"
56 #include "stream/textfilestream.h"
58 #include "uniquenames.h"
59 #include "modulesystem/singletonmodule.h"
60 #include "modulesystem/moduleregistry.h"
61 #include "stream/stringstream.h"
62 #include "signal/signal.h"
64 #include "gtkutil/filechooser.h"
68 #include "filetypes.h"
70 #include "entityinspector.h"
73 #include "camwindow.h"
75 #include "mainframe.h"
76 #include "preferences.h"
77 #include "referencecache.h"
81 #include "brushmodule.h"
91 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
92 m_names.insert( name_read( c_str() ) );
97 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
98 m_names.erase( name_read( c_str() ) );
102 NameObserver& operator=( const NameObserver& other );
104 NameObserver( UniqueNames& names ) : m_names( names ){
107 NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
114 return string_empty( c_str() );
116 const char* c_str() const {
117 return m_name.c_str();
119 void nameChanged( const char* name ){
124 typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
127 class BasicNamespace : public Namespace
129 typedef std::map<NameCallback, NameObserver> Names;
131 UniqueNames m_uniqueNames;
134 ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
136 void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
137 std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
138 ASSERT_MESSAGE( result.second, "cannot attach name" );
139 attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
140 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
142 void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
143 Names::iterator i = m_names.find( setName );
144 ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
145 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
146 detachObserver( NameObserver::NameChangedCaller( ( *i ).second ) );
150 void makeUnique( const char* name, const NameCallback& setName ) const {
152 name_write( buffer, m_uniqueNames.make_unique( name_read( name ) ) );
156 void mergeNames( const BasicNamespace& other ) const {
157 typedef std::list<NameCallback> SetNameCallbacks;
158 typedef std::map<std::string, SetNameCallbacks> NameGroups;
161 UniqueNames uniqueNames( other.m_uniqueNames );
163 for ( Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
165 groups[( *i ).second.c_str()].push_back( ( *i ).first );
168 for ( NameGroups::iterator i = groups.begin(); i != groups.end(); ++i )
170 name_t uniqueName( uniqueNames.make_unique( name_read( ( *i ).first.c_str() ) ) );
171 uniqueNames.insert( uniqueName );
174 name_write( buffer, uniqueName );
176 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
178 SetNameCallbacks& setNameCallbacks = ( *i ).second;
180 for ( SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j )
188 BasicNamespace g_defaultNamespace;
189 BasicNamespace g_cloneNamespace;
193 Namespace* m_namespace;
195 typedef Namespace Type;
196 STRING_CONSTANT( Name, "*" );
199 m_namespace = &g_defaultNamespace;
201 Namespace* getTable(){
206 typedef SingletonModule<NamespaceAPI> NamespaceModule;
207 typedef Static<NamespaceModule> StaticNamespaceModule;
208 StaticRegisterModule staticRegisterDefaultNamespace( StaticNamespaceModule::instance() );
211 std::list<Namespaced*> g_cloned;
213 inline Namespaced* Node_getNamespaced( scene::Node& node ){
214 return NodeTypeCast<Namespaced>::cast( node );
217 void Node_gatherNamespaced( scene::Node& node ){
218 Namespaced* namespaced = Node_getNamespaced( node );
219 if ( namespaced != 0 ) {
220 g_cloned.push_back( namespaced );
224 class GatherNamespaced : public scene::Traversable::Walker
227 bool pre( scene::Node& node ) const {
228 Node_gatherNamespaced( node );
233 void Map_gatherNamespaced( scene::Node& root ){
234 Node_traverseSubgraph( root, GatherNamespaced() );
237 void Map_mergeClonedNames(){
238 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
240 ( *i )->setNamespace( g_cloneNamespace );
242 g_cloneNamespace.mergeNames( g_defaultNamespace );
243 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
245 ( *i )->setNamespace( g_defaultNamespace );
258 void set( scene::Node* node ){
267 scene::Node* get() const {
273 void Map_SetValid( Map& map, bool valid );
274 void Map_UpdateTitle( const Map& map );
275 void Map_SetWorldspawn( Map& map, scene::Node* node );
278 class Map : public ModuleObserver
282 Resource* m_resource;
286 void ( *m_modified_changed )( const Map& );
288 Signal0 m_mapValidCallbacks;
290 WorldNode m_world_node; // "classname" "worldspawn" !
292 Map() : m_resource( 0 ), m_valid( false ), m_modified_changed( Map_UpdateTitle ){
296 if ( m_resource != 0 ) {
297 if ( Map_Unnamed( *this ) ) {
298 g_map.m_resource->setNode( NewMapRoot( "" ).get_pointer() );
299 MapFile* map = Node_getMapFile( *g_map.m_resource->getNode() );
309 GlobalSceneGraph().insert_root( *m_resource->getNode() );
313 Map_SetValid( g_map, true );
317 if ( m_resource != 0 ) {
318 Map_SetValid( g_map, false );
319 Map_SetWorldspawn( g_map, 0 );
322 GlobalUndoSystem().clear();
324 GlobalSceneGraph().erase_root();
330 Map* g_currentMap = 0;
332 void Map_addValidCallback( Map& map, const SignalHandler& handler ){
333 map.m_mapValidCallbacks.connectLast( handler );
336 bool Map_Valid( const Map& map ){
340 void Map_SetValid( Map& map, bool valid ){
342 map.m_mapValidCallbacks();
346 const char* Map_Name( const Map& map ){
347 return map.m_name.c_str();
350 bool Map_Unnamed( const Map& map ){
351 return string_equal( Map_Name( map ), "unnamed.map" );
354 inline const MapFormat& MapFormat_forFile( const char* filename ){
355 const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), path_get_extension( filename ) );
356 MapFormat* format = Radiant_getMapModules().findModule( moduleName );
357 ASSERT_MESSAGE( format != 0, "map format not found for file " << makeQuoted( filename ) );
361 const MapFormat& Map_getFormat( const Map& map ){
362 return MapFormat_forFile( Map_Name( map ) );
366 bool Map_Modified( const Map& map ){
367 return map.m_modified;
370 void Map_SetModified( Map& map, bool modified ){
371 if ( map.m_modified ^ modified ) {
372 map.m_modified = modified;
374 map.m_modified_changed( map );
378 void Map_UpdateTitle( const Map& map ){
379 Sys_SetTitle( map.m_name.c_str(), Map_Modified( map ) );
384 scene::Node* Map_GetWorldspawn( const Map& map ){
385 return map.m_world_node.get();
388 void Map_SetWorldspawn( Map& map, scene::Node* node ){
389 map.m_world_node.set( node );
394 // need that in a variable, will have to tweak depending on the game
395 float g_MaxWorldCoord = 64 * 1024;
396 float g_MinWorldCoord = -64 * 1024;
398 void AddRegionBrushes( void );
399 void RemoveRegionBrushes( void );
405 free all map elements, reinitialize the structures that depend on them
411 g_map.m_resource->detach( g_map );
412 GlobalReferenceCache().release( g_map.m_name.c_str() );
413 g_map.m_resource = 0;
418 Brush_unlatchPreferences();
421 class EntityFindByClassname : public scene::Graph::Walker
426 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
429 bool pre( const scene::Path& path, scene::Instance& instance ) const {
430 if ( m_entity == 0 ) {
431 Entity* entity = Node_getEntity( path.top() );
433 && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
441 Entity* Scene_FindEntityByClass( const char* name ){
443 GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
447 Entity *Scene_FindPlayerStart(){
448 typedef const char* StaticString;
449 StaticString strings[] = {
451 "info_player_deathmatch",
452 "team_CTF_redplayer",
453 "team_CTF_blueplayer",
455 "team_CTF_bluespawn",
457 typedef const StaticString* StaticStringIterator;
458 for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
460 Entity* entity = Scene_FindEntityByClass( *i );
469 // move the view to a start position
473 void FocusViews( const Vector3& point, float angle ){
474 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
475 Camera_setOrigin( camwnd, point );
476 Vector3 angles( Camera_getAngles( camwnd ) );
477 angles[CAMERA_PITCH] = 0;
478 angles[CAMERA_YAW] = angle;
479 Camera_setAngles( camwnd, angles );
481 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
482 xywnd->SetOrigin( point );
485 #include "stringio.h"
487 void Map_StartPosition(){
488 Entity* entity = Scene_FindPlayerStart();
492 string_parse_vector3( entity->getKeyValue( "origin" ), origin );
493 FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
497 FocusViews( g_vector3_identity, 0 );
502 inline bool node_is_worldspawn( scene::Node& node ){
503 Entity* entity = Node_getEntity( node );
504 return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
508 // use first worldspawn
509 class entity_updateworldspawn : public scene::Traversable::Walker
512 bool pre( scene::Node& node ) const {
513 if ( node_is_worldspawn( node ) ) {
514 if ( Map_GetWorldspawn( g_map ) == 0 ) {
515 Map_SetWorldspawn( g_map, &node );
522 scene::Node* Map_FindWorldspawn( Map& map ){
523 Map_SetWorldspawn( map, 0 );
525 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
527 return Map_GetWorldspawn( map );
531 class CollectAllWalker : public scene::Traversable::Walker
534 UnsortedNodeSet& m_nodes;
536 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
538 bool pre( scene::Node& node ) const {
539 m_nodes.insert( NodeSmartReference( node ) );
540 Node_getTraversable( m_root )->erase( node );
545 void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
546 UnsortedNodeSet nodes;
547 Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
548 Node_getTraversable( parent )->insert( child );
550 for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
552 Node_getTraversable( parent )->insert( ( *i ) );
556 scene::Node& createWorldspawn(){
557 NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
558 Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
562 void Map_UpdateWorldspawn( Map& map ){
563 if ( Map_FindWorldspawn( map ) == 0 ) {
564 Map_SetWorldspawn( map, &createWorldspawn() );
568 scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
569 Map_UpdateWorldspawn( map );
570 return *Map_GetWorldspawn( map );
574 class MapMergeAll : public scene::Traversable::Walker
576 mutable scene::Path m_path;
578 MapMergeAll( const scene::Path& root )
581 bool pre( scene::Node& node ) const {
582 Node_getTraversable( m_path.top() )->insert( node );
583 m_path.push( makeReference( node ) );
584 selectPath( m_path, true );
587 void post( scene::Node& node ) const {
592 class MapMergeEntities : public scene::Traversable::Walker
594 mutable scene::Path m_path;
596 MapMergeEntities( const scene::Path& root )
599 bool pre( scene::Node& node ) const {
600 if ( node_is_worldspawn( node ) ) {
601 scene::Node* world_node = Map_FindWorldspawn( g_map );
602 if ( world_node == 0 ) {
603 Map_SetWorldspawn( g_map, &node );
604 Node_getTraversable( m_path.top().get() )->insert( node );
605 m_path.push( makeReference( node ) );
606 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
610 m_path.push( makeReference( *world_node ) );
611 Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
616 Node_getTraversable( m_path.top() )->insert( node );
617 m_path.push( makeReference( node ) );
618 if ( node_is_group( node ) ) {
619 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
623 selectPath( m_path, true );
628 void post( scene::Node& node ) const {
633 class BasicContainer : public scene::Node::Symbiot
637 NodeTypeCastTable m_casts;
640 NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
642 NodeTypeCastTable& get(){
648 TraversableNodeSet m_traverse;
651 typedef LazyStatic<TypeCasts> StaticTypeCasts;
653 scene::Traversable& get( NullType<scene::Traversable>){
657 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
667 /// Merges the map graph rooted at \p node into the global scene-graph.
668 void MergeMap( scene::Node& node ){
669 Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
671 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
672 NodeSmartReference node( ( new BasicContainer )->node() );
673 format.readGraph( node, in, GlobalEntityCreator() );
674 Map_gatherNamespaced( node );
675 Map_mergeClonedNames();
679 inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
680 return NodeTypeCast<scene::Cloneable>::cast( node );
683 inline scene::Node& node_clone( scene::Node& node ){
684 scene::Cloneable* cloneable = Node_getCloneable( node );
685 if ( cloneable != 0 ) {
686 return cloneable->clone();
689 return ( new scene::NullNode )->node();
692 class CloneAll : public scene::Traversable::Walker
694 mutable scene::Path m_path;
696 CloneAll( scene::Node& root )
697 : m_path( makeReference( root ) ){
699 bool pre( scene::Node& node ) const {
700 if ( node.isRoot() ) {
704 m_path.push( makeReference( node_clone( node ) ) );
705 m_path.top().get().IncRef();
709 void post( scene::Node& node ) const {
710 if ( node.isRoot() ) {
714 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
716 m_path.top().get().DecRef();
721 scene::Node& Node_Clone( scene::Node& node ){
722 scene::Node& clone = node_clone( node );
723 scene::Traversable* traversable = Node_getTraversable( node );
724 if ( traversable != 0 ) {
725 traversable->traverse( CloneAll( clone ) );
731 typedef std::map<std::string, std::size_t> EntityBreakdown;
733 class EntityBreakdownWalker : public scene::Graph::Walker
735 EntityBreakdown& m_entitymap;
737 EntityBreakdownWalker( EntityBreakdown& entitymap )
738 : m_entitymap( entitymap ){
740 bool pre( const scene::Path& path, scene::Instance& instance ) const {
741 Entity* entity = Node_getEntity( path.top() );
743 const EntityClass& eclass = entity->getEntityClass();
744 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
745 m_entitymap[eclass.name()] = 1;
747 else{ ++m_entitymap[eclass.name()]; }
753 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
754 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
758 WindowPosition g_posMapInfoWnd( c_default_window_pos );
762 GtkEntry* brushes_entry;
763 GtkEntry* entities_entry;
764 GtkListStore* EntityBreakdownWalker;
766 ui::Window window = MainFrame_getWindow().create_dialog_window("Map Info", G_CALLBACK(dialog_delete_callback ), &dialog );
768 window_set_position( window, g_posMapInfoWnd );
771 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
772 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
775 GtkHBox* hbox = create_dialog_hbox( 4 );
776 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
779 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
780 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
783 GtkEntry* entry = ui::Entry();
784 gtk_widget_show( GTK_WIDGET( entry ) );
785 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
786 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
787 (GtkAttachOptions) ( 0 ), 0, 0 );
788 gtk_entry_set_editable( entry, FALSE );
790 brushes_entry = entry;
793 GtkEntry* entry = ui::Entry();
794 gtk_widget_show( GTK_WIDGET( entry ) );
795 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
796 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
797 (GtkAttachOptions) ( 0 ), 0, 0 );
798 gtk_entry_set_editable( entry, FALSE );
800 entities_entry = entry;
803 ui::Widget label = ui::Label( "Total Brushes" );
804 gtk_widget_show( label );
805 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
806 (GtkAttachOptions) ( GTK_FILL ),
807 (GtkAttachOptions) ( 0 ), 0, 0 );
808 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
811 ui::Widget label = ui::Label( "Total Entities" );
812 gtk_widget_show( label );
813 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
814 (GtkAttachOptions) ( GTK_FILL ),
815 (GtkAttachOptions) ( 0 ), 0, 0 );
816 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
820 GtkVBox* vbox2 = create_dialog_vbox( 4 );
821 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
824 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
825 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
830 ui::Widget label = ui::Label( "Entity breakdown" );
831 gtk_widget_show( label );
832 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
833 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
836 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
837 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
840 GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
842 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
843 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
846 auto renderer = ui::CellRendererText();
847 GtkTreeViewColumn* column = ui::TreeViewColumn( "Entity", renderer, {{"text", 0}} );
848 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
849 gtk_tree_view_column_set_sort_column_id( column, 0 );
853 auto renderer = ui::CellRendererText();
854 GtkTreeViewColumn* column = ui::TreeViewColumn( "Count", renderer, {{"text", 1}} );
855 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
856 gtk_tree_view_column_set_sort_column_id( column, 1 );
859 gtk_widget_show( view );
861 gtk_container_add( GTK_CONTAINER( scr ), view );
863 EntityBreakdownWalker = store;
871 EntityBreakdown entitymap;
872 Scene_EntityBreakdown( entitymap );
874 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
877 sprintf( tmp, "%u", Unsigned( ( *i ).second ) );
879 gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
880 gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, tmp, -1 );
884 g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
887 sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) );
888 gtk_entry_set_text( GTK_ENTRY( brushes_entry ), tmp );
889 sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) );
890 gtk_entry_set_text( GTK_ENTRY( entities_entry ), tmp );
892 modal_dialog_show( window, dialog );
895 window_get_position( window, g_posMapInfoWnd );
897 gtk_widget_destroy( GTK_WIDGET( window ) );
905 const char* m_message;
907 ScopeTimer( const char* message )
908 : m_message( message ){
912 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
913 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
923 void Map_LoadFile( const char *filename ){
924 globalOutputStream() << "Loading map from " << filename << "\n";
925 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
928 ScopeTimer timer( "map load" );
930 const MapFormat* format = NULL;
931 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
932 if ( string_not_empty( moduleName ) ) {
933 format = ReferenceAPI_getMapModules().findModule( moduleName );
936 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
941 Brush_toggleFormat( i );
942 g_map.m_name = filename;
943 Map_UpdateTitle( g_map );
944 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
946 format->wrongFormat = false;
948 g_map.m_resource->attach( g_map );
950 if ( !format->wrongFormat ) {
956 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
959 globalOutputStream() << "--- LoadMapFile ---\n";
960 globalOutputStream() << g_map.m_name.c_str() << "\n";
962 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
963 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
965 //GlobalEntityCreator().printStatistics();
968 // move the view to a start position
972 g_currentMap = &g_map;
978 virtual bool excluded( scene::Node& node ) const = 0;
981 class ExcludeWalker : public scene::Traversable::Walker
983 const scene::Traversable::Walker& m_walker;
984 const Excluder* m_exclude;
987 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
988 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
990 bool pre( scene::Node& node ) const {
991 if ( m_exclude->excluded( node ) || node.isRoot() ) {
997 m_walker.pre( node );
1001 void post( scene::Node& node ) const {
1007 m_walker.post( node );
1012 class AnyInstanceSelected : public scene::Instantiable::Visitor
1016 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1019 void visit( scene::Instance& instance ) const {
1020 Selectable* selectable = Instance_getSelectable( instance );
1021 if ( selectable != 0
1022 && selectable->isSelected() ) {
1028 bool Node_instanceSelected( scene::Node& node ){
1029 scene::Instantiable* instantiable = Node_getInstantiable( node );
1030 ASSERT_NOTNULL( instantiable );
1032 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1036 class SelectedDescendantWalker : public scene::Traversable::Walker
1040 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1044 bool pre( scene::Node& node ) const {
1045 if ( node.isRoot() ) {
1049 if ( Node_instanceSelected( node ) ) {
1057 bool Node_selectedDescendant( scene::Node& node ){
1059 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1063 class SelectionExcluder : public Excluder
1066 bool excluded( scene::Node& node ) const {
1067 return !Node_selectedDescendant( node );
1071 class IncludeSelectedWalker : public scene::Traversable::Walker
1073 const scene::Traversable::Walker& m_walker;
1074 mutable std::size_t m_selected;
1075 mutable bool m_skip;
1077 bool selectedParent() const {
1078 return m_selected != 0;
1081 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1082 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1084 bool pre( scene::Node& node ) const {
1086 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1087 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1088 if ( Node_instanceSelected( node ) ) {
1091 m_walker.pre( node );
1100 void post( scene::Node& node ) const {
1106 if ( Node_instanceSelected( node ) ) {
1109 m_walker.post( node );
1114 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1115 scene::Traversable* traversable = Node_getTraversable( root );
1116 if ( traversable != 0 ) {
1118 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1120 traversable->traverse( IncludeSelectedWalker( walker ) );
1125 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1126 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1129 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1130 scene::Traversable* traversable = Node_getTraversable( root );
1131 if ( traversable != 0 ) {
1132 traversable->traverse( walker );
1136 class RegionExcluder : public Excluder
1139 bool excluded( scene::Node& node ) const {
1140 return node.excluded();
1144 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1145 scene::Traversable* traversable = Node_getTraversable( root );
1146 if ( traversable != 0 ) {
1147 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1151 bool Map_SaveRegion( const char *filename ){
1154 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1156 RemoveRegionBrushes();
1162 void Map_RenameAbsolute( const char* absolute ){
1163 Resource* resource = GlobalReferenceCache().capture( absolute );
1164 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1165 resource->setNode( clone.get_pointer() );
1168 //ScopeTimer timer("clone subgraph");
1169 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1172 g_map.m_resource->detach( g_map );
1173 GlobalReferenceCache().release( g_map.m_name.c_str() );
1175 g_map.m_resource = resource;
1177 g_map.m_name = absolute;
1178 Map_UpdateTitle( g_map );
1180 g_map.m_resource->attach( g_map );
1183 void Map_Rename( const char* filename ){
1184 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1185 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1187 Map_RenameAbsolute( filename );
1189 SceneChangeNotify();
1200 ScopeTimer timer( "map save" );
1202 return true; // assume success..
1212 //globalOutputStream() << "Map_New\n";
1214 g_map.m_name = "unnamed.map";
1215 Map_UpdateTitle( g_map );
1218 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1219 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1220 g_map.m_resource->attach( g_map );
1222 SceneChangeNotify();
1225 FocusViews( g_vector3_identity, 0 );
1227 g_currentMap = &g_map;
1230 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1232 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1234 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1235 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1236 for now, let's just print an error
1239 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1241 for ( int i = 0 ; i < 3 ; i++ )
1243 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1244 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1249 // write the info_playerstart
1251 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1252 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1253 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1254 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1258 ===========================================================
1262 ===========================================================
1265 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1266 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1268 scene::Node* region_sides[6];
1269 scene::Node* region_startpoint = 0;
1274 a regioned map will have temp walls put up at the region boundary
1275 \todo TODO TTimo old implementation of region brushes
1276 we still add them straight in the worldspawn and take them out after the map is saved
1277 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1280 void AddRegionBrushes( void ){
1283 for ( i = 0; i < 6; i++ )
1285 region_sides[i] = &GlobalBrushCreator().createBrush();
1286 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1289 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1291 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1292 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1294 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1297 void RemoveRegionBrushes( void ){
1298 for ( std::size_t i = 0; i < 6; i++ )
1300 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1302 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1305 inline void exclude_node( scene::Node& node, bool exclude ){
1307 ? node.enable( scene::Node::eExcluded )
1308 : node.disable( scene::Node::eExcluded );
1311 class ExcludeAllWalker : public scene::Graph::Walker
1315 ExcludeAllWalker( bool exclude )
1316 : m_exclude( exclude ){
1318 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1319 exclude_node( path.top(), m_exclude );
1325 void Scene_Exclude_All( bool exclude ){
1326 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1329 bool Instance_isSelected( const scene::Instance& instance ){
1330 const Selectable* selectable = Instance_getSelectable( instance );
1331 return selectable != 0 && selectable->isSelected();
1334 class ExcludeSelectedWalker : public scene::Graph::Walker
1338 ExcludeSelectedWalker( bool exclude )
1339 : m_exclude( exclude ){
1341 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1342 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1347 void Scene_Exclude_Selected( bool exclude ){
1348 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1351 class ExcludeRegionedWalker : public scene::Graph::Walker
1355 ExcludeRegionedWalker( bool exclude )
1356 : m_exclude( exclude ){
1358 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1363 aabb_intersects_aabb(
1364 instance.worldAABB(),
1365 aabb_for_minmax( region_mins, region_maxs )
1375 void Scene_Exclude_Region( bool exclude ){
1376 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1383 Other filtering options may still be on
1386 void Map_RegionOff(){
1387 region_active = false;
1389 region_maxs[0] = g_MaxWorldCoord - 64;
1390 region_mins[0] = g_MinWorldCoord + 64;
1391 region_maxs[1] = g_MaxWorldCoord - 64;
1392 region_mins[1] = g_MinWorldCoord + 64;
1393 region_maxs[2] = g_MaxWorldCoord - 64;
1394 region_mins[2] = g_MinWorldCoord + 64;
1396 Scene_Exclude_All( false );
1399 void Map_ApplyRegion( void ){
1400 region_active = true;
1402 Scene_Exclude_Region( false );
1407 ========================
1408 Map_RegionSelectedBrushes
1409 ========================
1411 void Map_RegionSelectedBrushes( void ){
1414 if ( GlobalSelectionSystem().countSelected() != 0
1415 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1416 region_active = true;
1417 Select_GetBounds( region_mins, region_maxs );
1419 Scene_Exclude_Selected( false );
1421 GlobalSelectionSystem().setSelectedAll( false );
1431 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1434 region_mins[0] = x_min;
1435 region_maxs[0] = x_max;
1436 region_mins[1] = y_min;
1437 region_maxs[1] = y_max;
1438 region_mins[2] = g_MinWorldCoord + 64;
1439 region_maxs[2] = g_MaxWorldCoord - 64;
1444 void Map_RegionBounds( const AABB& bounds ){
1447 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1448 region_maxs = vector3_added( bounds.origin, bounds.extents );
1460 void Map_RegionBrush( void ){
1461 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1462 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1463 Map_RegionBounds( instance.worldAABB() );
1472 bool Map_ImportFile( const char* filename ){
1473 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1475 bool success = false;
1477 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1482 const MapFormat* format = NULL;
1483 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1484 if ( string_not_empty( moduleName ) ) {
1485 format = ReferenceAPI_getMapModules().findModule( moduleName );
1489 format->wrongFormat = false;
1491 Resource* resource = GlobalReferenceCache().capture( filename );
1492 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1493 if ( !resource->load() ) {
1494 GlobalReferenceCache().release( filename );
1498 if ( format->wrongFormat ) {
1499 GlobalReferenceCache().release( filename );
1503 NodeSmartReference clone( NewMapRoot( "" ) );
1504 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1505 Map_gatherNamespaced( clone );
1506 Map_mergeClonedNames();
1509 GlobalReferenceCache().release( filename );
1512 SceneChangeNotify();
1518 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
1519 int n = string_length( path_get_extension( filename ) );
1520 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1521 StringBuffer output;
1522 output.push_string( AppPath_get() );
1523 output.push_string( "q3map2." );
1524 output.push_string( RADIANT_EXECUTABLE );
1525 output.push_string( " -v -game " );
1526 output.push_string( ( type && *type ) ? type : "quake3" );
1527 output.push_string( " -fs_basepath \"" );
1528 output.push_string( EnginePath_get() );
1529 output.push_string( "\" -fs_homepath \"" );
1530 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1531 output.push_string( "\" -fs_game " );
1532 output.push_string( gamename_get() );
1533 output.push_string( " -convert -format " );
1534 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1535 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1536 output.push_string( " -readmap " );
1538 output.push_string( " \"" );
1539 output.push_string( filename );
1540 output.push_string( "\"" );
1543 Q_Exec( NULL, output.c_str(), NULL, false, true );
1545 // rebuild filename as "filenamewithoutext_converted.map"
1547 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1548 output.push_string( "_converted.map" );
1549 filename = output.c_str();
1552 Resource* resource = GlobalReferenceCache().capture( filename );
1553 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1554 if ( !resource->load() ) {
1555 GlobalReferenceCache().release( filename );
1558 NodeSmartReference clone( NewMapRoot( "" ) );
1559 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1560 Map_gatherNamespaced( clone );
1561 Map_mergeClonedNames();
1564 GlobalReferenceCache().release( filename );
1567 SceneChangeNotify();
1576 bool Map_SaveFile( const char* filename ){
1577 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1578 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1586 // Saves selected world brushes and whole entities with partial/full selections
1588 bool Map_SaveSelected( const char* filename ){
1589 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1593 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1595 scene::Node& m_parent;
1597 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1599 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1600 if ( path.top().get_pointer() != &m_parent
1601 && Node_isPrimitive( path.top() ) ) {
1602 Selectable* selectable = Instance_getSelectable( instance );
1603 if ( selectable != 0
1604 && selectable->isSelected()
1605 && path.size() > 1 ) {
1611 void post( const scene::Path& path, scene::Instance& instance ) const {
1612 if ( path.top().get_pointer() != &m_parent
1613 && Node_isPrimitive( path.top() ) ) {
1614 Selectable* selectable = Instance_getSelectable( instance );
1615 if ( selectable != 0
1616 && selectable->isSelected()
1617 && path.size() > 1 ) {
1618 scene::Node& parent = path.parent();
1619 if ( &parent != &m_parent ) {
1620 NodeSmartReference node( path.top().get() );
1621 Node_getTraversable( parent )->erase( node );
1622 Node_getTraversable( m_parent )->insert( node );
1629 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1630 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1633 class CountSelectedBrushes : public scene::Graph::Walker
1635 std::size_t& m_count;
1636 mutable std::size_t m_depth;
1638 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1641 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1642 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1645 Selectable* selectable = Instance_getSelectable( instance );
1646 if ( selectable != 0
1647 && selectable->isSelected()
1648 && Node_isPrimitive( path.top() ) ) {
1653 void post( const scene::Path& path, scene::Instance& instance ) const {
1658 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1660 graph.traverse( CountSelectedBrushes( count ) );
1672 const char* nodetype_get_name( ENodeType type ){
1673 if ( type == eNodeMap ) {
1676 if ( type == eNodeEntity ) {
1679 if ( type == eNodePrimitive ) {
1685 ENodeType node_get_nodetype( scene::Node& node ){
1686 if ( Node_isEntity( node ) ) {
1689 if ( Node_isPrimitive( node ) ) {
1690 return eNodePrimitive;
1692 return eNodeUnknown;
1695 bool contains_entity( scene::Node& node ){
1696 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1699 bool contains_primitive( scene::Node& node ){
1700 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1703 ENodeType node_get_contains( scene::Node& node ){
1704 if ( contains_entity( node ) ) {
1707 if ( contains_primitive( node ) ) {
1708 return eNodePrimitive;
1710 return eNodeUnknown;
1713 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1714 ENodeType contains = node_get_contains( parent.top() );
1715 ENodeType type = node_get_nodetype( child.top() );
1717 if ( contains != eNodeUnknown && contains == type ) {
1718 NodeSmartReference node( child.top().get() );
1719 Path_deleteTop( child );
1720 Node_getTraversable( parent.top() )->insert( node );
1721 SceneChangeNotify();
1725 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1729 void Scene_parentSelected(){
1730 UndoableCommand undo( "parentSelected" );
1732 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1733 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1735 const scene::Path& m_parent;
1737 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1739 void visit( scene::Instance& instance ) const {
1740 if ( &m_parent != &instance.path() ) {
1741 Path_parent( m_parent, instance.path() );
1746 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1747 GlobalSelectionSystem().foreachSelected( visitor );
1751 globalOutputStream() << "failed - did not find two selected nodes.\n";
1758 if ( ConfirmModified( "New Map" ) ) {
1765 std::string g_mapsPath;
1767 const char* getMapsPath(){
1768 return g_mapsPath.c_str();
1771 const char* map_open( const char* title ){
1772 return MainFrame_getWindow().file_dialog( TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1775 const char* map_import( const char* title ){
1776 return MainFrame_getWindow().file_dialog( TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1779 const char* map_save( const char* title ){
1780 return MainFrame_getWindow().file_dialog( FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1784 if ( !ConfirmModified( "Open Map" ) ) {
1788 const char* filename = map_open( "Open Map" );
1790 if ( filename != 0 ) {
1791 MRU_AddFile( filename );
1794 Map_LoadFile( filename );
1799 const char* filename = map_import( "Import Map" );
1801 if ( filename != 0 ) {
1802 UndoableCommand undo( "mapImport" );
1803 Map_ImportFile( filename );
1808 const char* filename = map_save( "Save Map" );
1810 if ( filename != 0 ) {
1811 MRU_AddFile( filename );
1812 Map_Rename( filename );
1823 if ( Map_Unnamed( g_map ) ) {
1826 else if ( Map_Modified( g_map ) ) {
1832 const char* filename = map_save( "Export Selection" );
1834 if ( filename != 0 ) {
1835 Map_SaveSelected( filename );
1840 const char* filename = map_save( "Export Region" );
1842 if ( filename != 0 ) {
1843 Map_SaveRegion( filename );
1850 SceneChangeNotify();
1855 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1856 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1857 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1858 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1860 SceneChangeNotify();
1865 SceneChangeNotify();
1868 void RegionSelected(){
1869 Map_RegionSelectedBrushes();
1870 SceneChangeNotify();
1877 class BrushFindByIndexWalker : public scene::Traversable::Walker
1879 mutable std::size_t m_index;
1880 scene::Path& m_path;
1882 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1883 : m_index( index ), m_path( path ){
1885 bool pre( scene::Node& node ) const {
1886 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1887 m_path.push( makeReference( node ) );
1893 class EntityFindByIndexWalker : public scene::Traversable::Walker
1895 mutable std::size_t m_index;
1896 scene::Path& m_path;
1898 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1899 : m_index( index ), m_path( path ){
1901 bool pre( scene::Node& node ) const {
1902 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1903 m_path.push( makeReference( node ) );
1909 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1910 path.push( makeReference( GlobalSceneGraph().root() ) );
1912 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1914 if ( path.size() == 2 ) {
1915 scene::Traversable* traversable = Node_getTraversable( path.top() );
1916 if ( traversable != 0 ) {
1917 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1922 inline bool Node_hasChildren( scene::Node& node ){
1923 scene::Traversable* traversable = Node_getTraversable( node );
1924 return traversable != 0 && !traversable->empty();
1927 void SelectBrush( int entitynum, int brushnum ){
1929 Scene_FindEntityBrush( entitynum, brushnum, path );
1930 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1931 scene::Instance* instance = GlobalSceneGraph().find( path );
1932 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1933 Selectable* selectable = Instance_getSelectable( *instance );
1934 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1935 selectable->setSelected( true );
1936 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1941 class BrushFindIndexWalker : public scene::Graph::Walker
1943 mutable const scene::Node* m_node;
1944 std::size_t& m_count;
1946 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1947 : m_node( &node ), m_count( count ){
1949 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1950 if ( Node_isPrimitive( path.top() ) ) {
1951 if ( m_node == path.top().get_pointer() ) {
1962 class EntityFindIndexWalker : public scene::Graph::Walker
1964 mutable const scene::Node* m_node;
1965 std::size_t& m_count;
1967 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
1968 : m_node( &node ), m_count( count ){
1970 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1971 if ( Node_isEntity( path.top() ) ) {
1972 if ( m_node == path.top().get_pointer() ) {
1983 static void GetSelectionIndex( int *ent, int *brush ){
1984 std::size_t count_brush = 0;
1985 std::size_t count_entity = 0;
1986 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1987 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
1989 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
1990 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
1992 *brush = int(count_brush);
1993 *ent = int(count_entity);
2001 ui::Window window = MainFrame_getWindow().create_dialog_window("Find Brush", G_CALLBACK(dialog_delete_callback ), &dialog );
2003 GtkAccelGroup* accel = ui::AccelGroup();
2004 gtk_window_add_accel_group( window, accel );
2007 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2008 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2010 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2011 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2013 ui::Widget label = ui::Label( "Entity number" );
2014 gtk_widget_show( label );
2015 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2016 (GtkAttachOptions) ( 0 ),
2017 (GtkAttachOptions) ( 0 ), 0, 0 );
2020 ui::Widget label = ui::Label( "Brush number" );
2021 gtk_widget_show( label );
2022 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2023 (GtkAttachOptions) ( 0 ),
2024 (GtkAttachOptions) ( 0 ), 0, 0 );
2027 GtkEntry* entry = ui::Entry();
2028 gtk_widget_show( GTK_WIDGET( entry ) );
2029 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2030 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2031 (GtkAttachOptions) ( 0 ), 0, 0 );
2032 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2036 GtkEntry* entry = ui::Entry();
2037 gtk_widget_show( GTK_WIDGET( entry ) );
2038 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2039 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2040 (GtkAttachOptions) ( 0 ), 0, 0 );
2046 GtkHBox* hbox = create_dialog_hbox( 4 );
2047 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2049 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2050 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2051 widget_make_default( GTK_WIDGET( button ) );
2052 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_KEY_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2055 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2056 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2057 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_KEY_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2062 // Initialize dialog
2066 GetSelectionIndex( &ent, &br );
2067 sprintf( buf, "%i", ent );
2068 gtk_entry_set_text( entity, buf );
2069 sprintf( buf, "%i", br );
2070 gtk_entry_set_text( brush, buf );
2072 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2073 const char *entstr = gtk_entry_get_text( entity );
2074 const char *brushstr = gtk_entry_get_text( brush );
2075 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2078 gtk_widget_destroy( GTK_WIDGET( window ) );
2081 void Map_constructPreferences( PreferencesPage& page ){
2082 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2086 class MapEntityClasses : public ModuleObserver
2088 std::size_t m_unrealised;
2090 MapEntityClasses() : m_unrealised( 1 ){
2093 if ( --m_unrealised == 0 ) {
2094 if ( g_map.m_resource != 0 ) {
2095 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2096 g_map.m_resource->realise();
2101 if ( ++m_unrealised == 1 ) {
2102 if ( g_map.m_resource != 0 ) {
2103 g_map.m_resource->flush();
2104 g_map.m_resource->unrealise();
2110 MapEntityClasses g_MapEntityClasses;
2113 class MapModuleObserver : public ModuleObserver
2115 std::size_t m_unrealised;
2117 MapModuleObserver() : m_unrealised( 1 ){
2120 if ( --m_unrealised == 0 ) {
2121 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2122 StringOutputStream buffer( 256 );
2123 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2124 Q_mkdir( buffer.c_str() );
2125 g_mapsPath = buffer.c_str();
2129 if ( ++m_unrealised == 1 ) {
2135 MapModuleObserver g_MapModuleObserver;
2137 #include "preferencesystem.h"
2139 std::string g_strLastMap;
2140 bool g_bLoadLastMap = false;
2142 void Map_Construct(){
2143 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2144 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2145 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2146 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2148 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2149 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2150 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2152 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2154 GlobalEntityClassManager().attach( g_MapEntityClasses );
2155 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2159 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2160 GlobalEntityClassManager().detach( g_MapEntityClasses );