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"
39 #include "ifilesystem.h"
40 #include "namespace.h"
41 #include "moduleobserver.h"
46 #include <gdk/gdkkeysyms.h>
47 #include "uilib/uilib.h"
50 #include "transformlib.h"
51 #include "selectionlib.h"
52 #include "instancelib.h"
53 #include "traverselib.h"
55 #include "eclasslib.h"
57 #include "stream/textfilestream.h"
59 #include "uniquenames.h"
60 #include "modulesystem/singletonmodule.h"
61 #include "modulesystem/moduleregistry.h"
62 #include "stream/stringstream.h"
63 #include "signal/signal.h"
65 #include "gtkutil/filechooser.h"
69 #include "filetypes.h"
71 #include "entityinspector.h"
74 #include "camwindow.h"
76 #include "mainframe.h"
77 #include "preferences.h"
78 #include "preferencesystem.h"
79 #include "referencecache.h"
83 #include "brushmodule.h"
93 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
94 m_names.insert( name_read( c_str() ) );
99 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
100 m_names.erase( name_read( c_str() ) );
104 NameObserver& operator=( const NameObserver& other );
106 NameObserver( UniqueNames& names ) : m_names( names ){
109 NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
116 return string_empty( c_str() );
118 const char* c_str() const {
119 return m_name.c_str();
121 void nameChanged( const char* name ){
126 typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
129 class BasicNamespace : public Namespace
131 typedef std::map<NameCallback, NameObserver> Names;
133 UniqueNames m_uniqueNames;
136 ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
138 void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
139 std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
140 ASSERT_MESSAGE( result.second, "cannot attach name" );
141 attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
142 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
144 void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
145 Names::iterator i = m_names.find( setName );
146 ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
147 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
148 detachObserver( NameObserver::NameChangedCaller( ( *i ).second ) );
152 void makeUnique( const char* name, const NameCallback& setName ) const {
154 name_write( buffer, m_uniqueNames.make_unique( name_read( name ) ) );
158 void mergeNames( const BasicNamespace& other ) const {
159 typedef std::list<NameCallback> SetNameCallbacks;
160 typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
163 UniqueNames uniqueNames( other.m_uniqueNames );
165 for ( Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
167 groups[( *i ).second.c_str()].push_back( ( *i ).first );
170 for ( NameGroups::iterator i = groups.begin(); i != groups.end(); ++i )
172 name_t uniqueName( uniqueNames.make_unique( name_read( ( *i ).first.c_str() ) ) );
173 uniqueNames.insert( uniqueName );
176 name_write( buffer, uniqueName );
178 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
180 SetNameCallbacks& setNameCallbacks = ( *i ).second;
182 for ( SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j )
190 BasicNamespace g_defaultNamespace;
191 BasicNamespace g_cloneNamespace;
195 Namespace* m_namespace;
197 typedef Namespace Type;
198 STRING_CONSTANT( Name, "*" );
201 m_namespace = &g_defaultNamespace;
203 Namespace* getTable(){
208 typedef SingletonModule<NamespaceAPI> NamespaceModule;
209 typedef Static<NamespaceModule> StaticNamespaceModule;
210 StaticRegisterModule staticRegisterDefaultNamespace( StaticNamespaceModule::instance() );
213 std::list<Namespaced*> g_cloned;
215 inline Namespaced* Node_getNamespaced( scene::Node& node ){
216 return NodeTypeCast<Namespaced>::cast( node );
219 void Node_gatherNamespaced( scene::Node& node ){
220 Namespaced* namespaced = Node_getNamespaced( node );
221 if ( namespaced != 0 ) {
222 g_cloned.push_back( namespaced );
226 class GatherNamespaced : public scene::Traversable::Walker
229 bool pre( scene::Node& node ) const {
230 Node_gatherNamespaced( node );
235 void Map_gatherNamespaced( scene::Node& root ){
236 Node_traverseSubgraph( root, GatherNamespaced() );
239 void Map_mergeClonedNames(){
240 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
242 ( *i )->setNamespace( g_cloneNamespace );
244 g_cloneNamespace.mergeNames( g_defaultNamespace );
245 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
247 ( *i )->setNamespace( g_defaultNamespace );
260 void set( scene::Node* node ){
269 scene::Node* get() const {
275 void Map_SetValid( Map& map, bool valid );
276 void Map_UpdateTitle( const Map& map );
277 void Map_SetWorldspawn( Map& map, scene::Node* node );
280 class Map : public ModuleObserver
284 Resource* m_resource;
288 void ( *m_modified_changed )( const Map& );
290 Signal0 m_mapValidCallbacks;
292 WorldNode m_world_node; // "classname" "worldspawn" !
294 Map() : m_resource( 0 ), m_valid( false ), m_modified_changed( Map_UpdateTitle ){
298 if ( m_resource != 0 ) {
299 if ( Map_Unnamed( *this ) ) {
300 g_map.m_resource->setNode( NewMapRoot( "" ).get_pointer() );
301 MapFile* map = Node_getMapFile( *g_map.m_resource->getNode() );
311 GlobalSceneGraph().insert_root( *m_resource->getNode() );
315 Map_SetValid( g_map, true );
319 if ( m_resource != 0 ) {
320 Map_SetValid( g_map, false );
321 Map_SetWorldspawn( g_map, 0 );
324 GlobalUndoSystem().clear();
326 GlobalSceneGraph().erase_root();
332 Map* g_currentMap = 0;
334 void Map_addValidCallback( Map& map, const SignalHandler& handler ){
335 map.m_mapValidCallbacks.connectLast( handler );
338 bool Map_Valid( const Map& map ){
342 void Map_SetValid( Map& map, bool valid ){
344 map.m_mapValidCallbacks();
348 const char* Map_Name( const Map& map ){
349 return map.m_name.c_str();
352 bool Map_Unnamed( const Map& map ){
353 return string_equal( Map_Name( map ), "unnamed.map" );
356 inline const MapFormat& MapFormat_forFile( const char* filename ){
357 const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), path_get_extension( filename ) );
358 MapFormat* format = Radiant_getMapModules().findModule( moduleName );
359 ASSERT_MESSAGE( format != 0, "map format not found for file " << makeQuoted( filename ) );
363 const MapFormat& Map_getFormat( const Map& map ){
364 return MapFormat_forFile( Map_Name( map ) );
368 bool Map_Modified( const Map& map ){
369 return map.m_modified;
372 void Map_SetModified( Map& map, bool modified ){
373 if ( map.m_modified ^ modified ) {
374 map.m_modified = modified;
376 map.m_modified_changed( map );
380 void Map_UpdateTitle( const Map& map ){
381 Sys_SetTitle( map.m_name.c_str(), Map_Modified( map ) );
386 scene::Node* Map_GetWorldspawn( const Map& map ){
387 return map.m_world_node.get();
390 void Map_SetWorldspawn( Map& map, scene::Node* node ){
391 map.m_world_node.set( node );
396 // need that in a variable, will have to tweak depending on the game
397 float g_MaxWorldCoord = 64 * 1024;
398 float g_MinWorldCoord = -64 * 1024;
400 void AddRegionBrushes( void );
401 void RemoveRegionBrushes( void );
408 free all map elements, reinitialize the structures that depend on them
414 g_map.m_resource->detach( g_map );
415 GlobalReferenceCache().release( g_map.m_name.c_str() );
416 g_map.m_resource = 0;
421 Brush_unlatchPreferences();
424 class EntityFindByClassname : public scene::Graph::Walker
429 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
432 bool pre( const scene::Path& path, scene::Instance& instance ) const {
433 if ( m_entity == 0 ) {
434 Entity* entity = Node_getEntity( path.top() );
436 && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
444 Entity* Scene_FindEntityByClass( const char* name ){
446 GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
450 Entity *Scene_FindPlayerStart(){
451 typedef const char* StaticString;
452 StaticString strings[] = {
454 "info_player_deathmatch",
455 "team_CTF_redplayer",
456 "team_CTF_blueplayer",
458 "team_CTF_bluespawn",
460 typedef const StaticString* StaticStringIterator;
461 for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
463 Entity* entity = Scene_FindEntityByClass( *i );
472 // move the view to a start position
476 void FocusViews( const Vector3& point, float angle ){
477 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
478 Camera_setOrigin( camwnd, point );
479 Vector3 angles( Camera_getAngles( camwnd ) );
480 angles[CAMERA_PITCH] = 0;
481 angles[CAMERA_YAW] = angle;
482 Camera_setAngles( camwnd, angles );
484 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
485 xywnd->SetOrigin( point );
488 #include "stringio.h"
490 void Map_StartPosition(){
491 Entity* entity = Scene_FindPlayerStart();
495 string_parse_vector3( entity->getKeyValue( "origin" ), origin );
496 FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
500 FocusViews( g_vector3_identity, 0 );
505 inline bool node_is_worldspawn( scene::Node& node ){
506 Entity* entity = Node_getEntity( node );
507 return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
511 // use first worldspawn
512 class entity_updateworldspawn : public scene::Traversable::Walker
515 bool pre( scene::Node& node ) const {
516 if ( node_is_worldspawn( node ) ) {
517 if ( Map_GetWorldspawn( g_map ) == 0 ) {
518 Map_SetWorldspawn( g_map, &node );
525 scene::Node* Map_FindWorldspawn( Map& map ){
526 Map_SetWorldspawn( map, 0 );
528 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
530 return Map_GetWorldspawn( map );
534 class CollectAllWalker : public scene::Traversable::Walker
537 UnsortedNodeSet& m_nodes;
539 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
541 bool pre( scene::Node& node ) const {
542 m_nodes.insert( NodeSmartReference( node ) );
543 Node_getTraversable( m_root )->erase( node );
548 void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
549 UnsortedNodeSet nodes;
550 Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
551 Node_getTraversable( parent )->insert( child );
553 for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
555 Node_getTraversable( parent )->insert( ( *i ) );
559 scene::Node& createWorldspawn(){
560 NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
561 Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
565 void Map_UpdateWorldspawn( Map& map ){
566 if ( Map_FindWorldspawn( map ) == 0 ) {
567 Map_SetWorldspawn( map, &createWorldspawn() );
571 scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
572 Map_UpdateWorldspawn( map );
573 return *Map_GetWorldspawn( map );
577 class MapMergeAll : public scene::Traversable::Walker
579 mutable scene::Path m_path;
581 MapMergeAll( const scene::Path& root )
584 bool pre( scene::Node& node ) const {
585 Node_getTraversable( m_path.top() )->insert( node );
586 m_path.push( makeReference( node ) );
587 selectPath( m_path, true );
590 void post( scene::Node& node ) const {
595 class MapMergeEntities : public scene::Traversable::Walker
597 mutable scene::Path m_path;
599 MapMergeEntities( const scene::Path& root )
602 bool pre( scene::Node& node ) const {
603 if ( node_is_worldspawn( node ) ) {
604 scene::Node* world_node = Map_FindWorldspawn( g_map );
605 if ( world_node == 0 ) {
606 Map_SetWorldspawn( g_map, &node );
607 Node_getTraversable( m_path.top().get() )->insert( node );
608 m_path.push( makeReference( node ) );
609 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
613 m_path.push( makeReference( *world_node ) );
614 Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
619 Node_getTraversable( m_path.top() )->insert( node );
620 m_path.push( makeReference( node ) );
621 if ( node_is_group( node ) ) {
622 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
626 selectPath( m_path, true );
631 void post( scene::Node& node ) const {
636 class BasicContainer : public scene::Node::Symbiot
640 NodeTypeCastTable m_casts;
643 NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
645 NodeTypeCastTable& get(){
651 TraversableNodeSet m_traverse;
654 typedef LazyStatic<TypeCasts> StaticTypeCasts;
656 scene::Traversable& get( NullType<scene::Traversable>){
660 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
670 /// Merges the map graph rooted at \p node into the global scene-graph.
671 void MergeMap( scene::Node& node ){
672 Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
674 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
675 NodeSmartReference node( ( new BasicContainer )->node() );
676 format.readGraph( node, in, GlobalEntityCreator() );
677 Map_gatherNamespaced( node );
678 Map_mergeClonedNames();
682 inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
683 return NodeTypeCast<scene::Cloneable>::cast( node );
686 inline scene::Node& node_clone( scene::Node& node ){
687 scene::Cloneable* cloneable = Node_getCloneable( node );
688 if ( cloneable != 0 ) {
689 return cloneable->clone();
692 return ( new scene::NullNode )->node();
695 class CloneAll : public scene::Traversable::Walker
697 mutable scene::Path m_path;
699 CloneAll( scene::Node& root )
700 : m_path( makeReference( root ) ){
702 bool pre( scene::Node& node ) const {
703 if ( node.isRoot() ) {
707 m_path.push( makeReference( node_clone( node ) ) );
708 m_path.top().get().IncRef();
712 void post( scene::Node& node ) const {
713 if ( node.isRoot() ) {
717 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
719 m_path.top().get().DecRef();
724 scene::Node& Node_Clone( scene::Node& node ){
725 scene::Node& clone = node_clone( node );
726 scene::Traversable* traversable = Node_getTraversable( node );
727 if ( traversable != 0 ) {
728 traversable->traverse( CloneAll( clone ) );
734 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
736 class EntityBreakdownWalker : public scene::Graph::Walker
738 EntityBreakdown& m_entitymap;
740 EntityBreakdownWalker( EntityBreakdown& entitymap )
741 : m_entitymap( entitymap ){
743 bool pre( const scene::Path& path, scene::Instance& instance ) const {
744 Entity* entity = Node_getEntity( path.top() );
746 const EntityClass& eclass = entity->getEntityClass();
747 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
748 m_entitymap[eclass.name()] = 1;
750 else{ ++m_entitymap[eclass.name()]; }
756 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
757 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
761 WindowPosition g_posMapInfoWnd( c_default_window_pos );
765 GtkEntry* brushes_entry;
766 GtkEntry* entities_entry;
767 GtkListStore* EntityBreakdownWalker;
769 ui::Window window = MainFrame_getWindow().create_dialog_window("Map Info", G_CALLBACK(dialog_delete_callback ), &dialog );
771 window_set_position( window, g_posMapInfoWnd );
774 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
775 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
778 GtkHBox* hbox = create_dialog_hbox( 4 );
779 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
782 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
783 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
786 GtkEntry* entry = ui::Entry();
787 gtk_widget_show( GTK_WIDGET( entry ) );
788 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
789 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
790 (GtkAttachOptions) ( 0 ), 0, 0 );
791 gtk_editable_set_editable( GTK_EDITABLE(entry), FALSE );
793 brushes_entry = entry;
796 GtkEntry* entry = ui::Entry();
797 gtk_widget_show( GTK_WIDGET( entry ) );
798 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
799 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
800 (GtkAttachOptions) ( 0 ), 0, 0 );
801 gtk_editable_set_editable( GTK_EDITABLE(entry), FALSE );
803 entities_entry = entry;
806 ui::Widget label = ui::Label( "Total Brushes" );
807 gtk_widget_show( label );
808 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
809 (GtkAttachOptions) ( GTK_FILL ),
810 (GtkAttachOptions) ( 0 ), 0, 0 );
811 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
814 ui::Widget label = ui::Label( "Total Entities" );
815 gtk_widget_show( label );
816 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
817 (GtkAttachOptions) ( GTK_FILL ),
818 (GtkAttachOptions) ( 0 ), 0, 0 );
819 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
823 GtkVBox* vbox2 = create_dialog_vbox( 4 );
824 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
827 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
828 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
833 ui::Widget label = ui::Label( "Entity breakdown" );
834 gtk_widget_show( label );
835 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
836 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
839 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
840 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
843 GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
845 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
846 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
849 auto renderer = ui::CellRendererText();
850 GtkTreeViewColumn* column = ui::TreeViewColumn( "Entity", renderer, {{"text", 0}} );
851 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
852 gtk_tree_view_column_set_sort_column_id( column, 0 );
856 auto renderer = ui::CellRendererText();
857 GtkTreeViewColumn* column = ui::TreeViewColumn( "Count", renderer, {{"text", 1}} );
858 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
859 gtk_tree_view_column_set_sort_column_id( column, 1 );
862 gtk_widget_show( view );
864 gtk_container_add( GTK_CONTAINER( scr ), view );
866 EntityBreakdownWalker = store;
874 EntityBreakdown entitymap;
875 Scene_EntityBreakdown( entitymap );
877 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
880 sprintf( tmp, "%u", Unsigned( ( *i ).second ) );
882 gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
883 gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, tmp, -1 );
887 g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
890 sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) );
891 gtk_entry_set_text( GTK_ENTRY( brushes_entry ), tmp );
892 sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) );
893 gtk_entry_set_text( GTK_ENTRY( entities_entry ), tmp );
895 modal_dialog_show( window, dialog );
898 window_get_position( window, g_posMapInfoWnd );
900 gtk_widget_destroy( GTK_WIDGET( window ) );
908 const char* m_message;
910 ScopeTimer( const char* message )
911 : m_message( message ){
915 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
916 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
920 CopiedString g_strLastFolder = "";
928 void Map_LoadFile( const char *filename ){
929 globalOutputStream() << "Loading map from " << filename << "\n";
930 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
932 MRU_AddFile( filename );
933 g_strLastFolder = g_path_get_dirname( filename );
936 ScopeTimer timer( "map load" );
938 const MapFormat* format = NULL;
939 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
940 if ( string_not_empty( moduleName ) ) {
941 format = ReferenceAPI_getMapModules().findModule( moduleName );
944 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
949 Brush_toggleFormat( i );
950 g_map.m_name = filename;
951 Map_UpdateTitle( g_map );
952 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
954 format->wrongFormat = false;
956 g_map.m_resource->attach( g_map );
958 if ( !format->wrongFormat ) {
964 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
967 globalOutputStream() << "--- LoadMapFile ---\n";
968 globalOutputStream() << g_map.m_name.c_str() << "\n";
970 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
971 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
973 //GlobalEntityCreator().printStatistics();
976 // move the view to a start position
980 g_currentMap = &g_map;
982 // restart VFS to apply new pak filtering based on mapname
983 // needed for daemon DPK VFS
990 virtual bool excluded( scene::Node& node ) const = 0;
993 class ExcludeWalker : public scene::Traversable::Walker
995 const scene::Traversable::Walker& m_walker;
996 const Excluder* m_exclude;
999 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
1000 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
1002 bool pre( scene::Node& node ) const {
1003 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1009 m_walker.pre( node );
1013 void post( scene::Node& node ) const {
1019 m_walker.post( node );
1024 class AnyInstanceSelected : public scene::Instantiable::Visitor
1028 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1031 void visit( scene::Instance& instance ) const {
1032 Selectable* selectable = Instance_getSelectable( instance );
1033 if ( selectable != 0
1034 && selectable->isSelected() ) {
1040 bool Node_instanceSelected( scene::Node& node ){
1041 scene::Instantiable* instantiable = Node_getInstantiable( node );
1042 ASSERT_NOTNULL( instantiable );
1044 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1048 class SelectedDescendantWalker : public scene::Traversable::Walker
1052 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1056 bool pre( scene::Node& node ) const {
1057 if ( node.isRoot() ) {
1061 if ( Node_instanceSelected( node ) ) {
1069 bool Node_selectedDescendant( scene::Node& node ){
1071 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1075 class SelectionExcluder : public Excluder
1078 bool excluded( scene::Node& node ) const {
1079 return !Node_selectedDescendant( node );
1083 class IncludeSelectedWalker : public scene::Traversable::Walker
1085 const scene::Traversable::Walker& m_walker;
1086 mutable std::size_t m_selected;
1087 mutable bool m_skip;
1089 bool selectedParent() const {
1090 return m_selected != 0;
1093 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1094 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1096 bool pre( scene::Node& node ) const {
1098 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1099 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1100 if ( Node_instanceSelected( node ) ) {
1103 m_walker.pre( node );
1112 void post( scene::Node& node ) const {
1118 if ( Node_instanceSelected( node ) ) {
1121 m_walker.post( node );
1126 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1127 scene::Traversable* traversable = Node_getTraversable( root );
1128 if ( traversable != 0 ) {
1130 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1132 traversable->traverse( IncludeSelectedWalker( walker ) );
1137 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1138 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1141 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1142 scene::Traversable* traversable = Node_getTraversable( root );
1143 if ( traversable != 0 ) {
1144 traversable->traverse( walker );
1148 class RegionExcluder : public Excluder
1151 bool excluded( scene::Node& node ) const {
1152 return node.excluded();
1156 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1157 scene::Traversable* traversable = Node_getTraversable( root );
1158 if ( traversable != 0 ) {
1159 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1163 bool Map_SaveRegion( const char *filename ){
1166 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1168 RemoveRegionBrushes();
1174 void Map_RenameAbsolute( const char* absolute ){
1175 Resource* resource = GlobalReferenceCache().capture( absolute );
1176 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1177 resource->setNode( clone.get_pointer() );
1180 //ScopeTimer timer("clone subgraph");
1181 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1184 g_map.m_resource->detach( g_map );
1185 GlobalReferenceCache().release( g_map.m_name.c_str() );
1187 g_map.m_resource = resource;
1189 g_map.m_name = absolute;
1190 Map_UpdateTitle( g_map );
1192 g_map.m_resource->attach( g_map );
1193 // refresh VFS to apply new pak filtering based on mapname
1194 // needed for daemon DPK VFS
1198 void Map_Rename( const char* filename ){
1199 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1200 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1202 Map_RenameAbsolute( filename );
1204 SceneChangeNotify();
1215 ScopeTimer timer( "map save" );
1217 return true; // assume success..
1227 //globalOutputStream() << "Map_New\n";
1229 g_map.m_name = "unnamed.map";
1230 Map_UpdateTitle( g_map );
1233 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1234 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1235 g_map.m_resource->attach( g_map );
1237 SceneChangeNotify();
1240 FocusViews( g_vector3_identity, 0 );
1242 g_currentMap = &g_map;
1244 // restart VFS to apply new pak filtering based on mapname
1245 // needed for daemon DPK VFS
1249 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1251 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1253 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1254 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1255 for now, let's just print an error
1258 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1260 for ( int i = 0 ; i < 3 ; i++ )
1262 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1263 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1268 // write the info_playerstart
1270 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1271 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1272 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1273 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1277 ===========================================================
1281 ===========================================================
1284 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1285 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1287 scene::Node* region_sides[6];
1288 scene::Node* region_startpoint = 0;
1293 a regioned map will have temp walls put up at the region boundary
1294 \todo TODO TTimo old implementation of region brushes
1295 we still add them straight in the worldspawn and take them out after the map is saved
1296 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1299 void AddRegionBrushes( void ){
1302 for ( i = 0; i < 6; i++ )
1304 region_sides[i] = &GlobalBrushCreator().createBrush();
1305 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1308 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1310 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1311 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1313 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1316 void RemoveRegionBrushes( void ){
1317 for ( std::size_t i = 0; i < 6; i++ )
1319 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1321 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1324 inline void exclude_node( scene::Node& node, bool exclude ){
1326 ? node.enable( scene::Node::eExcluded )
1327 : node.disable( scene::Node::eExcluded );
1330 class ExcludeAllWalker : public scene::Graph::Walker
1334 ExcludeAllWalker( bool exclude )
1335 : m_exclude( exclude ){
1337 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1338 exclude_node( path.top(), m_exclude );
1344 void Scene_Exclude_All( bool exclude ){
1345 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1348 bool Instance_isSelected( const scene::Instance& instance ){
1349 const Selectable* selectable = Instance_getSelectable( instance );
1350 return selectable != 0 && selectable->isSelected();
1353 class ExcludeSelectedWalker : public scene::Graph::Walker
1357 ExcludeSelectedWalker( bool exclude )
1358 : m_exclude( exclude ){
1360 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1361 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1366 void Scene_Exclude_Selected( bool exclude ){
1367 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1370 class ExcludeRegionedWalker : public scene::Graph::Walker
1374 ExcludeRegionedWalker( bool exclude )
1375 : m_exclude( exclude ){
1377 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1382 aabb_intersects_aabb(
1383 instance.worldAABB(),
1384 aabb_for_minmax( region_mins, region_maxs )
1394 void Scene_Exclude_Region( bool exclude ){
1395 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1402 Other filtering options may still be on
1405 void Map_RegionOff(){
1406 region_active = false;
1408 region_maxs[0] = g_MaxWorldCoord - 64;
1409 region_mins[0] = g_MinWorldCoord + 64;
1410 region_maxs[1] = g_MaxWorldCoord - 64;
1411 region_mins[1] = g_MinWorldCoord + 64;
1412 region_maxs[2] = g_MaxWorldCoord - 64;
1413 region_mins[2] = g_MinWorldCoord + 64;
1415 Scene_Exclude_All( false );
1418 void Map_ApplyRegion( void ){
1419 region_active = true;
1421 Scene_Exclude_Region( false );
1426 ========================
1427 Map_RegionSelectedBrushes
1428 ========================
1430 void Map_RegionSelectedBrushes( void ){
1433 if ( GlobalSelectionSystem().countSelected() != 0
1434 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1435 region_active = true;
1436 Select_GetBounds( region_mins, region_maxs );
1438 Scene_Exclude_Selected( false );
1440 GlobalSelectionSystem().setSelectedAll( false );
1450 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1453 region_mins[0] = x_min;
1454 region_maxs[0] = x_max;
1455 region_mins[1] = y_min;
1456 region_maxs[1] = y_max;
1457 region_mins[2] = g_MinWorldCoord + 64;
1458 region_maxs[2] = g_MaxWorldCoord - 64;
1463 void Map_RegionBounds( const AABB& bounds ){
1466 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1467 region_maxs = vector3_added( bounds.origin, bounds.extents );
1479 void Map_RegionBrush( void ){
1480 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1481 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1482 Map_RegionBounds( instance.worldAABB() );
1491 bool Map_ImportFile( const char* filename ){
1492 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1494 g_strLastFolder = g_path_get_dirname( filename );
1496 bool success = false;
1498 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1503 const MapFormat* format = NULL;
1504 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1505 if ( string_not_empty( moduleName ) ) {
1506 format = ReferenceAPI_getMapModules().findModule( moduleName );
1510 format->wrongFormat = false;
1512 Resource* resource = GlobalReferenceCache().capture( filename );
1513 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1514 if ( !resource->load() ) {
1515 GlobalReferenceCache().release( filename );
1519 if ( format->wrongFormat ) {
1520 GlobalReferenceCache().release( filename );
1524 NodeSmartReference clone( NewMapRoot( "" ) );
1525 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1526 Map_gatherNamespaced( clone );
1527 Map_mergeClonedNames();
1530 GlobalReferenceCache().release( filename );
1533 SceneChangeNotify();
1539 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
1540 int n = string_length( path_get_extension( filename ) );
1541 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1542 StringBuffer output;
1543 output.push_string( AppPath_get() );
1544 output.push_string( "q3map2." );
1545 output.push_string( RADIANT_EXECUTABLE );
1546 output.push_string( " -v -game " );
1547 output.push_string( ( type && *type ) ? type : "quake3" );
1548 output.push_string( " -fs_basepath \"" );
1549 output.push_string( EnginePath_get() );
1550 output.push_string( "\" -fs_homepath \"" );
1551 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1552 output.push_string( "\" -fs_game " );
1553 output.push_string( gamename_get() );
1554 output.push_string( " -convert -format " );
1555 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1556 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1557 output.push_string( " -readmap " );
1559 output.push_string( " \"" );
1560 output.push_string( filename );
1561 output.push_string( "\"" );
1564 Q_Exec( NULL, output.c_str(), NULL, false, true );
1566 // rebuild filename as "filenamewithoutext_converted.map"
1568 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1569 output.push_string( "_converted.map" );
1570 filename = output.c_str();
1573 Resource* resource = GlobalReferenceCache().capture( filename );
1574 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1575 if ( !resource->load() ) {
1576 GlobalReferenceCache().release( filename );
1579 NodeSmartReference clone( NewMapRoot( "" ) );
1580 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1581 Map_gatherNamespaced( clone );
1582 Map_mergeClonedNames();
1585 GlobalReferenceCache().release( filename );
1588 SceneChangeNotify();
1597 bool Map_SaveFile( const char* filename ){
1598 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1599 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1601 // refresh VFS to apply new pak filtering based on mapname
1602 // needed for daemon DPK VFS
1613 // Saves selected world brushes and whole entities with partial/full selections
1615 bool Map_SaveSelected( const char* filename ){
1616 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1620 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1622 scene::Node& m_parent;
1624 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1626 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1627 if ( path.top().get_pointer() != &m_parent
1628 && Node_isPrimitive( path.top() ) ) {
1629 Selectable* selectable = Instance_getSelectable( instance );
1630 if ( selectable != 0
1631 && selectable->isSelected()
1632 && path.size() > 1 ) {
1638 void post( const scene::Path& path, scene::Instance& instance ) const {
1639 if ( path.top().get_pointer() != &m_parent
1640 && Node_isPrimitive( path.top() ) ) {
1641 Selectable* selectable = Instance_getSelectable( instance );
1642 if ( selectable != 0
1643 && selectable->isSelected()
1644 && path.size() > 1 ) {
1645 scene::Node& parent = path.parent();
1646 if ( &parent != &m_parent ) {
1647 NodeSmartReference node( path.top().get() );
1648 Node_getTraversable( parent )->erase( node );
1649 Node_getTraversable( m_parent )->insert( node );
1656 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1657 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1660 class CountSelectedBrushes : public scene::Graph::Walker
1662 std::size_t& m_count;
1663 mutable std::size_t m_depth;
1665 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1668 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1669 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1672 Selectable* selectable = Instance_getSelectable( instance );
1673 if ( selectable != 0
1674 && selectable->isSelected()
1675 && Node_isPrimitive( path.top() ) ) {
1680 void post( const scene::Path& path, scene::Instance& instance ) const {
1685 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1687 graph.traverse( CountSelectedBrushes( count ) );
1699 const char* nodetype_get_name( ENodeType type ){
1700 if ( type == eNodeMap ) {
1703 if ( type == eNodeEntity ) {
1706 if ( type == eNodePrimitive ) {
1712 ENodeType node_get_nodetype( scene::Node& node ){
1713 if ( Node_isEntity( node ) ) {
1716 if ( Node_isPrimitive( node ) ) {
1717 return eNodePrimitive;
1719 return eNodeUnknown;
1722 bool contains_entity( scene::Node& node ){
1723 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1726 bool contains_primitive( scene::Node& node ){
1727 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1730 ENodeType node_get_contains( scene::Node& node ){
1731 if ( contains_entity( node ) ) {
1734 if ( contains_primitive( node ) ) {
1735 return eNodePrimitive;
1737 return eNodeUnknown;
1740 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1741 ENodeType contains = node_get_contains( parent.top() );
1742 ENodeType type = node_get_nodetype( child.top() );
1744 if ( contains != eNodeUnknown && contains == type ) {
1745 NodeSmartReference node( child.top().get() );
1746 Path_deleteTop( child );
1747 Node_getTraversable( parent.top() )->insert( node );
1748 SceneChangeNotify();
1752 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1756 void Scene_parentSelected(){
1757 UndoableCommand undo( "parentSelected" );
1759 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1760 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1762 const scene::Path& m_parent;
1764 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1766 void visit( scene::Instance& instance ) const {
1767 if ( &m_parent != &instance.path() ) {
1768 Path_parent( m_parent, instance.path() );
1773 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1774 GlobalSelectionSystem().foreachSelected( visitor );
1778 globalOutputStream() << "failed - did not find two selected nodes.\n";
1785 if ( ConfirmModified( "New Map" ) ) {
1792 CopiedString g_mapsPath;
1794 const char* getMapsPath(){
1795 return g_mapsPath.c_str();
1798 const char* getLastFolderPath(){
1799 if (g_strLastFolder.empty()) {
1800 GlobalPreferenceSystem().registerPreference( "LastFolder", CopiedStringImportStringCaller( g_strLastFolder ), CopiedStringExportStringCaller( g_strLastFolder ) );
1801 if (g_strLastFolder.empty()) {
1802 g_strLastFolder = g_qeglobals.m_userGamePath;
1805 return g_strLastFolder.c_str();
1808 const char* map_open( const char* title ){
1809 return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), true, false, false );
1812 const char* map_import( const char* title ){
1813 return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), false, true, false );
1816 const char* map_save( const char* title ){
1817 return MainFrame_getWindow().file_dialog( FALSE, title, getLastFolderPath(), MapFormat::Name(), false, false, true );
1821 if ( !ConfirmModified( "Open Map" ) ) {
1825 const char* filename = map_open( "Open Map" );
1827 if ( filename != NULL ) {
1828 MRU_AddFile( filename );
1831 Map_LoadFile( filename );
1836 const char* filename = map_import( "Import Map" );
1838 if ( filename != NULL ) {
1839 UndoableCommand undo( "mapImport" );
1840 Map_ImportFile( filename );
1845 const char* filename = map_save( "Save Map" );
1847 if ( filename != NULL ) {
1848 g_strLastFolder = g_path_get_dirname( filename );
1849 MRU_AddFile( filename );
1850 Map_Rename( filename );
1861 if ( Map_Unnamed( g_map ) ) {
1864 else if ( Map_Modified( g_map ) ) {
1870 const char* filename = map_save( "Export Selection" );
1872 if ( filename != NULL ) {
1873 g_strLastFolder = g_path_get_dirname( filename );
1874 Map_SaveSelected( filename );
1879 const char* filename = map_save( "Export Region" );
1881 if ( filename != NULL ) {
1882 g_strLastFolder = g_path_get_dirname( filename );
1883 Map_SaveRegion( filename );
1890 SceneChangeNotify();
1895 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1896 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1897 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1898 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1900 SceneChangeNotify();
1905 SceneChangeNotify();
1908 void RegionSelected(){
1909 Map_RegionSelectedBrushes();
1910 SceneChangeNotify();
1917 class BrushFindByIndexWalker : public scene::Traversable::Walker
1919 mutable std::size_t m_index;
1920 scene::Path& m_path;
1922 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1923 : m_index( index ), m_path( path ){
1925 bool pre( scene::Node& node ) const {
1926 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1927 m_path.push( makeReference( node ) );
1933 class EntityFindByIndexWalker : public scene::Traversable::Walker
1935 mutable std::size_t m_index;
1936 scene::Path& m_path;
1938 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1939 : m_index( index ), m_path( path ){
1941 bool pre( scene::Node& node ) const {
1942 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1943 m_path.push( makeReference( node ) );
1949 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1950 path.push( makeReference( GlobalSceneGraph().root() ) );
1952 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1954 if ( path.size() == 2 ) {
1955 scene::Traversable* traversable = Node_getTraversable( path.top() );
1956 if ( traversable != 0 ) {
1957 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1962 inline bool Node_hasChildren( scene::Node& node ){
1963 scene::Traversable* traversable = Node_getTraversable( node );
1964 return traversable != 0 && !traversable->empty();
1967 void SelectBrush( int entitynum, int brushnum ){
1969 Scene_FindEntityBrush( entitynum, brushnum, path );
1970 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1971 scene::Instance* instance = GlobalSceneGraph().find( path );
1972 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1973 Selectable* selectable = Instance_getSelectable( *instance );
1974 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1975 selectable->setSelected( true );
1976 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1981 class BrushFindIndexWalker : public scene::Graph::Walker
1983 mutable const scene::Node* m_node;
1984 std::size_t& m_count;
1986 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1987 : m_node( &node ), m_count( count ){
1989 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1990 if ( Node_isPrimitive( path.top() ) ) {
1991 if ( m_node == path.top().get_pointer() ) {
2002 class EntityFindIndexWalker : public scene::Graph::Walker
2004 mutable const scene::Node* m_node;
2005 std::size_t& m_count;
2007 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
2008 : m_node( &node ), m_count( count ){
2010 bool pre( const scene::Path& path, scene::Instance& instance ) const {
2011 if ( Node_isEntity( path.top() ) ) {
2012 if ( m_node == path.top().get_pointer() ) {
2023 static void GetSelectionIndex( int *ent, int *brush ){
2024 std::size_t count_brush = 0;
2025 std::size_t count_entity = 0;
2026 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2027 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2029 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2030 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2032 *brush = int(count_brush);
2033 *ent = int(count_entity);
2041 ui::Window window = MainFrame_getWindow().create_dialog_window("Find Brush", G_CALLBACK(dialog_delete_callback ), &dialog );
2043 auto accel = ui::AccelGroup();
2044 window.add_accel_group( accel );
2047 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2048 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2050 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2051 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2053 ui::Widget label = ui::Label( "Entity number" );
2054 gtk_widget_show( label );
2055 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2056 (GtkAttachOptions) ( 0 ),
2057 (GtkAttachOptions) ( 0 ), 0, 0 );
2060 ui::Widget label = ui::Label( "Brush number" );
2061 gtk_widget_show( label );
2062 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2063 (GtkAttachOptions) ( 0 ),
2064 (GtkAttachOptions) ( 0 ), 0, 0 );
2067 GtkEntry* entry = ui::Entry();
2068 gtk_widget_show( GTK_WIDGET( entry ) );
2069 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2070 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2071 (GtkAttachOptions) ( 0 ), 0, 0 );
2072 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2076 GtkEntry* entry = ui::Entry();
2077 gtk_widget_show( GTK_WIDGET( entry ) );
2078 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2079 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2080 (GtkAttachOptions) ( 0 ), 0, 0 );
2086 GtkHBox* hbox = create_dialog_hbox( 4 );
2087 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2089 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2090 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2091 widget_make_default( GTK_WIDGET( button ) );
2092 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_KEY_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2095 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2096 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2097 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_KEY_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2102 // Initialize dialog
2106 GetSelectionIndex( &ent, &br );
2107 sprintf( buf, "%i", ent );
2108 gtk_entry_set_text( entity, buf );
2109 sprintf( buf, "%i", br );
2110 gtk_entry_set_text( brush, buf );
2112 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2113 const char *entstr = gtk_entry_get_text( entity );
2114 const char *brushstr = gtk_entry_get_text( brush );
2115 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2118 gtk_widget_destroy( GTK_WIDGET( window ) );
2121 void Map_constructPreferences( PreferencesPage& page ){
2122 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2126 class MapEntityClasses : public ModuleObserver
2128 std::size_t m_unrealised;
2130 MapEntityClasses() : m_unrealised( 1 ){
2133 if ( --m_unrealised == 0 ) {
2134 if ( g_map.m_resource != 0 ) {
2135 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2136 g_map.m_resource->realise();
2141 if ( ++m_unrealised == 1 ) {
2142 if ( g_map.m_resource != 0 ) {
2143 g_map.m_resource->flush();
2144 g_map.m_resource->unrealise();
2150 MapEntityClasses g_MapEntityClasses;
2153 class MapModuleObserver : public ModuleObserver
2155 std::size_t m_unrealised;
2157 MapModuleObserver() : m_unrealised( 1 ){
2160 if ( --m_unrealised == 0 ) {
2161 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2162 StringOutputStream buffer( 256 );
2163 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2164 Q_mkdir( buffer.c_str() );
2165 g_mapsPath = buffer.c_str();
2169 if ( ++m_unrealised == 1 ) {
2175 MapModuleObserver g_MapModuleObserver;
2177 CopiedString g_strLastMap;
2178 bool g_bLoadLastMap = false;
2180 void Map_Construct(){
2181 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2182 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2183 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2184 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2186 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2187 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2188 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2190 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2192 GlobalEntityClassManager().attach( g_MapEntityClasses );
2193 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2197 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2198 GlobalEntityClassManager().detach( g_MapEntityClasses );