2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "debugging/debugging.h"
29 MapModules& ReferenceAPI_getMapModules();
30 #include "iselection.h"
34 #include "ireference.h"
35 #include "ifiletypes.h"
41 #include "ifilesystem.h"
42 #include "namespace.h"
43 #include "moduleobserver.h"
47 #include <gdk/gdkkeysyms.h>
48 #include "uilib/uilib.h"
51 #include "transformlib.h"
52 #include "selectionlib.h"
53 #include "instancelib.h"
54 #include "traverselib.h"
56 #include "eclasslib.h"
58 #include "stream/textfilestream.h"
60 #include "uniquenames.h"
61 #include "modulesystem/singletonmodule.h"
62 #include "modulesystem/moduleregistry.h"
63 #include "stream/stringstream.h"
64 #include "signal/signal.h"
66 #include "gtkutil/filechooser.h"
70 #include "filetypes.h"
72 #include "entityinspector.h"
75 #include "camwindow.h"
77 #include "mainframe.h"
78 #include "preferences.h"
79 #include "preferencesystem.h"
80 #include "referencecache.h"
84 #include "brushmodule.h"
94 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
95 m_names.insert( name_read( c_str() ) );
100 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
101 m_names.erase( name_read( c_str() ) );
105 NameObserver& operator=( const NameObserver& other );
107 NameObserver( UniqueNames& names ) : m_names( names ){
110 NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
117 return string_empty( c_str() );
119 const char* c_str() const {
120 return m_name.c_str();
122 void nameChanged( const char* name ){
127 typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
130 class BasicNamespace : public Namespace
132 typedef std::map<NameCallback, NameObserver> Names;
134 UniqueNames m_uniqueNames;
137 ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
139 void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
140 std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
141 ASSERT_MESSAGE( result.second, "cannot attach name" );
142 attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
143 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
145 void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
146 Names::iterator i = m_names.find( setName );
147 ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
148 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
149 detachObserver( NameObserver::NameChangedCaller( ( *i ).second ) );
153 void makeUnique( const char* name, const NameCallback& setName ) const {
155 name_write( buffer, m_uniqueNames.make_unique( name_read( name ) ) );
159 void mergeNames( const BasicNamespace& other ) const {
160 typedef std::list<NameCallback> SetNameCallbacks;
161 typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
164 UniqueNames uniqueNames( other.m_uniqueNames );
166 for ( Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
168 groups[( *i ).second.c_str()].push_back( ( *i ).first );
171 for ( NameGroups::iterator i = groups.begin(); i != groups.end(); ++i )
173 name_t uniqueName( uniqueNames.make_unique( name_read( ( *i ).first.c_str() ) ) );
174 uniqueNames.insert( uniqueName );
177 name_write( buffer, uniqueName );
179 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
181 SetNameCallbacks& setNameCallbacks = ( *i ).second;
183 for ( SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j )
191 BasicNamespace g_defaultNamespace;
192 BasicNamespace g_cloneNamespace;
196 Namespace* m_namespace;
198 typedef Namespace Type;
199 STRING_CONSTANT( Name, "*" );
202 m_namespace = &g_defaultNamespace;
204 Namespace* getTable(){
209 typedef SingletonModule<NamespaceAPI> NamespaceModule;
210 typedef Static<NamespaceModule> StaticNamespaceModule;
211 StaticRegisterModule staticRegisterDefaultNamespace( StaticNamespaceModule::instance() );
214 std::list<Namespaced*> g_cloned;
216 inline Namespaced* Node_getNamespaced( scene::Node& node ){
217 return NodeTypeCast<Namespaced>::cast( node );
220 void Node_gatherNamespaced( scene::Node& node ){
221 Namespaced* namespaced = Node_getNamespaced( node );
222 if ( namespaced != 0 ) {
223 g_cloned.push_back( namespaced );
227 class GatherNamespaced : public scene::Traversable::Walker
230 bool pre( scene::Node& node ) const {
231 Node_gatherNamespaced( node );
236 void Map_gatherNamespaced( scene::Node& root ){
237 Node_traverseSubgraph( root, GatherNamespaced() );
240 void Map_mergeClonedNames(){
241 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
243 ( *i )->setNamespace( g_cloneNamespace );
245 g_cloneNamespace.mergeNames( g_defaultNamespace );
246 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
248 ( *i )->setNamespace( g_defaultNamespace );
261 void set( scene::Node* node ){
270 scene::Node* get() const {
276 void Map_SetValid( Map& map, bool valid );
277 void Map_UpdateTitle( const Map& map );
278 void Map_SetWorldspawn( Map& map, scene::Node* node );
281 class Map : public ModuleObserver
285 Resource* m_resource;
289 void ( *m_modified_changed )( const Map& );
291 Signal0 m_mapValidCallbacks;
293 WorldNode m_world_node; // "classname" "worldspawn" !
295 Map() : m_resource( 0 ), m_valid( false ), m_modified_changed( Map_UpdateTitle ){
299 if ( m_resource != 0 ) {
300 if ( Map_Unnamed( *this ) ) {
301 g_map.m_resource->setNode( NewMapRoot( "" ).get_pointer() );
302 MapFile* map = Node_getMapFile( *g_map.m_resource->getNode() );
312 GlobalSceneGraph().insert_root( *m_resource->getNode() );
316 Map_SetValid( g_map, true );
320 if ( m_resource != 0 ) {
321 Map_SetValid( g_map, false );
322 Map_SetWorldspawn( g_map, 0 );
325 GlobalUndoSystem().clear();
327 GlobalSceneGraph().erase_root();
333 Map* g_currentMap = 0;
335 void Map_addValidCallback( Map& map, const SignalHandler& handler ){
336 map.m_mapValidCallbacks.connectLast( handler );
339 bool Map_Valid( const Map& map ){
343 void Map_SetValid( Map& map, bool valid ){
345 map.m_mapValidCallbacks();
349 const char* Map_Name( const Map& map ){
350 return map.m_name.c_str();
353 bool Map_Unnamed( const Map& map ){
354 return string_equal( Map_Name( map ), "unnamed.map" );
357 inline const MapFormat& MapFormat_forFile( const char* filename ){
358 const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), path_get_extension( filename ) );
359 MapFormat* format = Radiant_getMapModules().findModule( moduleName );
360 ASSERT_MESSAGE( format != 0, "map format not found for file " << makeQuoted( filename ) );
364 const MapFormat& Map_getFormat( const Map& map ){
365 return MapFormat_forFile( Map_Name( map ) );
369 bool Map_Modified( const Map& map ){
370 return map.m_modified;
373 void Map_SetModified( Map& map, bool modified ){
374 if ( map.m_modified ^ modified ) {
375 map.m_modified = modified;
377 map.m_modified_changed( map );
381 void Map_UpdateTitle( const Map& map ){
382 Sys_SetTitle( map.m_name.c_str(), Map_Modified( map ) );
387 scene::Node* Map_GetWorldspawn( const Map& map ){
388 return map.m_world_node.get();
391 void Map_SetWorldspawn( Map& map, scene::Node* node ){
392 map.m_world_node.set( node );
397 // need that in a variable, will have to tweak depending on the game
398 float g_MaxWorldCoord = 64 * 1024;
399 float g_MinWorldCoord = -64 * 1024;
401 void AddRegionBrushes( void );
402 void RemoveRegionBrushes( void );
409 free all map elements, reinitialize the structures that depend on them
415 g_map.m_resource->detach( g_map );
416 GlobalReferenceCache().release( g_map.m_name.c_str() );
417 g_map.m_resource = 0;
422 Brush_unlatchPreferences();
425 class EntityFindByClassname : public scene::Graph::Walker
430 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
433 bool pre( const scene::Path& path, scene::Instance& instance ) const {
434 if ( m_entity == 0 ) {
435 Entity* entity = Node_getEntity( path.top() );
437 && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
445 Entity* Scene_FindEntityByClass( const char* name ){
447 GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
451 Entity *Scene_FindPlayerStart(){
452 typedef const char* StaticString;
453 StaticString strings[] = {
455 "info_player_deathmatch",
456 "team_CTF_redplayer",
457 "team_CTF_blueplayer",
459 "team_CTF_bluespawn",
461 typedef const StaticString* StaticStringIterator;
462 for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
464 Entity* entity = Scene_FindEntityByClass( *i );
473 // move the view to a start position
477 void FocusViews( const Vector3& point, float angle ){
478 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
479 Camera_setOrigin( camwnd, point );
480 Vector3 angles( Camera_getAngles( camwnd ) );
481 angles[CAMERA_PITCH] = 0;
482 angles[CAMERA_YAW] = angle;
483 Camera_setAngles( camwnd, angles );
485 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
486 xywnd->SetOrigin( point );
489 #include "stringio.h"
491 void Map_StartPosition(){
492 Entity* entity = Scene_FindPlayerStart();
496 string_parse_vector3( entity->getKeyValue( "origin" ), origin );
497 FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
501 FocusViews( g_vector3_identity, 0 );
506 inline bool node_is_worldspawn( scene::Node& node ){
507 Entity* entity = Node_getEntity( node );
508 return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
512 // use first worldspawn
513 class entity_updateworldspawn : public scene::Traversable::Walker
516 bool pre( scene::Node& node ) const {
517 if ( node_is_worldspawn( node ) ) {
518 if ( Map_GetWorldspawn( g_map ) == 0 ) {
519 Map_SetWorldspawn( g_map, &node );
526 scene::Node* Map_FindWorldspawn( Map& map ){
527 Map_SetWorldspawn( map, 0 );
529 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
531 return Map_GetWorldspawn( map );
535 class CollectAllWalker : public scene::Traversable::Walker
538 UnsortedNodeSet& m_nodes;
540 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
542 bool pre( scene::Node& node ) const {
543 m_nodes.insert( NodeSmartReference( node ) );
544 Node_getTraversable( m_root )->erase( node );
549 void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
550 UnsortedNodeSet nodes;
551 Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
552 Node_getTraversable( parent )->insert( child );
554 for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
556 Node_getTraversable( parent )->insert( ( *i ) );
560 scene::Node& createWorldspawn(){
561 NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
562 Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
566 void Map_UpdateWorldspawn( Map& map ){
567 if ( Map_FindWorldspawn( map ) == 0 ) {
568 Map_SetWorldspawn( map, &createWorldspawn() );
572 scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
573 Map_UpdateWorldspawn( map );
574 return *Map_GetWorldspawn( map );
578 class MapMergeAll : public scene::Traversable::Walker
580 mutable scene::Path m_path;
582 MapMergeAll( const scene::Path& root )
585 bool pre( scene::Node& node ) const {
586 Node_getTraversable( m_path.top() )->insert( node );
587 m_path.push( makeReference( node ) );
588 selectPath( m_path, true );
591 void post( scene::Node& node ) const {
596 class MapMergeEntities : public scene::Traversable::Walker
598 mutable scene::Path m_path;
600 MapMergeEntities( const scene::Path& root )
603 bool pre( scene::Node& node ) const {
604 if ( node_is_worldspawn( node ) ) {
605 scene::Node* world_node = Map_FindWorldspawn( g_map );
606 if ( world_node == 0 ) {
607 Map_SetWorldspawn( g_map, &node );
608 Node_getTraversable( m_path.top().get() )->insert( node );
609 m_path.push( makeReference( node ) );
610 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
614 m_path.push( makeReference( *world_node ) );
615 Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
620 Node_getTraversable( m_path.top() )->insert( node );
621 m_path.push( makeReference( node ) );
622 if ( node_is_group( node ) ) {
623 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
627 selectPath( m_path, true );
632 void post( scene::Node& node ) const {
637 class BasicContainer : public scene::Node::Symbiot
641 NodeTypeCastTable m_casts;
644 NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
646 NodeTypeCastTable& get(){
652 TraversableNodeSet m_traverse;
655 typedef LazyStatic<TypeCasts> StaticTypeCasts;
657 scene::Traversable& get( NullType<scene::Traversable>){
661 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
671 /// Merges the map graph rooted at \p node into the global scene-graph.
672 void MergeMap( scene::Node& node ){
673 Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
675 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
676 NodeSmartReference node( ( new BasicContainer )->node() );
677 format.readGraph( node, in, GlobalEntityCreator() );
678 Map_gatherNamespaced( node );
679 Map_mergeClonedNames();
683 inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
684 return NodeTypeCast<scene::Cloneable>::cast( node );
687 inline scene::Node& node_clone( scene::Node& node ){
688 scene::Cloneable* cloneable = Node_getCloneable( node );
689 if ( cloneable != 0 ) {
690 return cloneable->clone();
693 return ( new scene::NullNode )->node();
696 class CloneAll : public scene::Traversable::Walker
698 mutable scene::Path m_path;
700 CloneAll( scene::Node& root )
701 : m_path( makeReference( root ) ){
703 bool pre( scene::Node& node ) const {
704 if ( node.isRoot() ) {
708 m_path.push( makeReference( node_clone( node ) ) );
709 m_path.top().get().IncRef();
713 void post( scene::Node& node ) const {
714 if ( node.isRoot() ) {
718 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
720 m_path.top().get().DecRef();
725 scene::Node& Node_Clone( scene::Node& node ){
726 scene::Node& clone = node_clone( node );
727 scene::Traversable* traversable = Node_getTraversable( node );
728 if ( traversable != 0 ) {
729 traversable->traverse( CloneAll( clone ) );
735 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
737 class EntityBreakdownWalker : public scene::Graph::Walker
739 EntityBreakdown& m_entitymap;
741 EntityBreakdownWalker( EntityBreakdown& entitymap )
742 : m_entitymap( entitymap ){
744 bool pre( const scene::Path& path, scene::Instance& instance ) const {
745 Entity* entity = Node_getEntity( path.top() );
747 const EntityClass& eclass = entity->getEntityClass();
748 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
749 m_entitymap[eclass.name()] = 1;
751 else{ ++m_entitymap[eclass.name()]; }
757 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
758 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
762 WindowPosition g_posMapInfoWnd( c_default_window_pos );
766 ui::Entry brushes_entry{ui::null};
767 ui::Entry entities_entry{ui::null};
768 ui::ListStore EntityBreakdownWalker{ui::null};
770 ui::Window window = MainFrame_getWindow().create_dialog_window("Map Info", G_CALLBACK(dialog_delete_callback ), &dialog );
772 window_set_position( window, g_posMapInfoWnd );
775 auto vbox = create_dialog_vbox( 4, 4 );
779 auto hbox = create_dialog_hbox( 4 );
780 vbox.pack_start( hbox, FALSE, TRUE, 0 );
783 auto table = create_dialog_table( 2, 2, 4, 4 );
784 hbox.pack_start( table, TRUE, TRUE, 0 );
787 auto entry = ui::Entry(ui::New);
789 gtk_table_attach( table, entry , 1, 2, 0, 1,
790 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
791 (GtkAttachOptions) ( 0 ), 0, 0 );
792 gtk_editable_set_editable( GTK_EDITABLE(entry), FALSE );
794 brushes_entry = entry;
797 auto entry = ui::Entry(ui::New);
799 gtk_table_attach( table, entry , 1, 2, 1, 2,
800 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
801 (GtkAttachOptions) ( 0 ), 0, 0 );
802 gtk_editable_set_editable( GTK_EDITABLE(entry), FALSE );
804 entities_entry = entry;
807 ui::Widget label = ui::Label( "Total Brushes" );
809 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
810 (GtkAttachOptions) ( GTK_FILL ),
811 (GtkAttachOptions) ( 0 ), 0, 0 );
812 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
815 ui::Widget label = ui::Label( "Total Entities" );
817 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
818 (GtkAttachOptions) ( GTK_FILL ),
819 (GtkAttachOptions) ( 0 ), 0, 0 );
820 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
824 auto vbox2 = create_dialog_vbox( 4 );
825 hbox.pack_start( vbox2, FALSE, FALSE, 0 );
828 auto button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
829 vbox2.pack_start( button, FALSE, FALSE, 0 );
834 ui::Widget label = ui::Label( "Entity breakdown" );
836 vbox.pack_start( label, FALSE, TRUE, 0 );
837 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
840 auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
841 vbox.pack_start( scr, TRUE, TRUE, 0 );
844 ui::ListStore store = ui::ListStore(gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING ));
846 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
847 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
850 auto renderer = ui::CellRendererText(ui::New);
851 GtkTreeViewColumn* column = ui::TreeViewColumn( "Entity", renderer, {{"text", 0}} );
852 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
853 gtk_tree_view_column_set_sort_column_id( column, 0 );
857 auto renderer = ui::CellRendererText(ui::New);
858 GtkTreeViewColumn* column = ui::TreeViewColumn( "Count", renderer, {{"text", 1}} );
859 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
860 gtk_tree_view_column_set_sort_column_id( column, 1 );
867 EntityBreakdownWalker = store;
875 EntityBreakdown entitymap;
876 Scene_EntityBreakdown( entitymap );
878 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
881 sprintf( tmp, "%u", Unsigned( ( *i ).second ) );
882 EntityBreakdownWalker.append(0, (*i).first.c_str(), 1, tmp);
886 EntityBreakdownWalker.unref();
889 sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) );
890 brushes_entry.text(tmp);
891 sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) );
892 entities_entry.text(tmp);
894 modal_dialog_show( window, dialog );
897 window_get_position( window, g_posMapInfoWnd );
907 const char* m_message;
909 ScopeTimer( const char* message )
910 : m_message( message ){
914 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
915 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
919 CopiedString g_strLastFolder = "";
927 void Map_LoadFile( const char *filename ){
928 globalOutputStream() << "Loading map from " << filename << "\n";
929 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
931 MRU_AddFile( filename );
932 g_strLastFolder = g_path_get_dirname( filename );
935 ScopeTimer timer( "map load" );
937 const MapFormat* format = NULL;
938 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
939 if ( string_not_empty( moduleName ) ) {
940 format = ReferenceAPI_getMapModules().findModule( moduleName );
943 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
948 Brush_toggleFormat( i );
949 g_map.m_name = filename;
950 Map_UpdateTitle( g_map );
951 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
953 format->wrongFormat = false;
955 g_map.m_resource->attach( g_map );
957 if ( !format->wrongFormat ) {
963 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
966 globalOutputStream() << "--- LoadMapFile ---\n";
967 globalOutputStream() << g_map.m_name.c_str() << "\n";
969 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
970 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
972 //GlobalEntityCreator().printStatistics();
975 // move the view to a start position
979 g_currentMap = &g_map;
981 // restart VFS to apply new pak filtering based on mapname
982 // needed for daemon DPK VFS
989 virtual bool excluded( scene::Node& node ) const = 0;
992 class ExcludeWalker : public scene::Traversable::Walker
994 const scene::Traversable::Walker& m_walker;
995 const Excluder* m_exclude;
998 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
999 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
1001 bool pre( scene::Node& node ) const {
1002 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1008 m_walker.pre( node );
1012 void post( scene::Node& node ) const {
1018 m_walker.post( node );
1023 class AnyInstanceSelected : public scene::Instantiable::Visitor
1027 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1030 void visit( scene::Instance& instance ) const {
1031 Selectable* selectable = Instance_getSelectable( instance );
1032 if ( selectable != 0
1033 && selectable->isSelected() ) {
1039 bool Node_instanceSelected( scene::Node& node ){
1040 scene::Instantiable* instantiable = Node_getInstantiable( node );
1041 ASSERT_NOTNULL( instantiable );
1043 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1047 class SelectedDescendantWalker : public scene::Traversable::Walker
1051 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1055 bool pre( scene::Node& node ) const {
1056 if ( node.isRoot() ) {
1060 if ( Node_instanceSelected( node ) ) {
1068 bool Node_selectedDescendant( scene::Node& node ){
1070 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1074 class SelectionExcluder : public Excluder
1077 bool excluded( scene::Node& node ) const {
1078 return !Node_selectedDescendant( node );
1082 class IncludeSelectedWalker : public scene::Traversable::Walker
1084 const scene::Traversable::Walker& m_walker;
1085 mutable std::size_t m_selected;
1086 mutable bool m_skip;
1088 bool selectedParent() const {
1089 return m_selected != 0;
1092 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1093 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1095 bool pre( scene::Node& node ) const {
1097 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1098 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1099 if ( Node_instanceSelected( node ) ) {
1102 m_walker.pre( node );
1111 void post( scene::Node& node ) const {
1117 if ( Node_instanceSelected( node ) ) {
1120 m_walker.post( node );
1125 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1126 scene::Traversable* traversable = Node_getTraversable( root );
1127 if ( traversable != 0 ) {
1129 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1131 traversable->traverse( IncludeSelectedWalker( walker ) );
1136 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1137 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1140 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1141 scene::Traversable* traversable = Node_getTraversable( root );
1142 if ( traversable != 0 ) {
1143 traversable->traverse( walker );
1147 class RegionExcluder : public Excluder
1150 bool excluded( scene::Node& node ) const {
1151 return node.excluded();
1155 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1156 scene::Traversable* traversable = Node_getTraversable( root );
1157 if ( traversable != 0 ) {
1158 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1162 bool Map_SaveRegion( const char *filename ){
1165 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1167 RemoveRegionBrushes();
1173 void Map_RenameAbsolute( const char* absolute ){
1174 Resource* resource = GlobalReferenceCache().capture( absolute );
1175 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1176 resource->setNode( clone.get_pointer() );
1179 //ScopeTimer timer("clone subgraph");
1180 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1183 g_map.m_resource->detach( g_map );
1184 GlobalReferenceCache().release( g_map.m_name.c_str() );
1186 g_map.m_resource = resource;
1188 g_map.m_name = absolute;
1189 Map_UpdateTitle( g_map );
1191 g_map.m_resource->attach( g_map );
1192 // refresh VFS to apply new pak filtering based on mapname
1193 // needed for daemon DPK VFS
1197 void Map_Rename( const char* filename ){
1198 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1199 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1201 Map_RenameAbsolute( filename );
1203 SceneChangeNotify();
1214 ScopeTimer timer( "map save" );
1216 return true; // assume success..
1226 //globalOutputStream() << "Map_New\n";
1228 g_map.m_name = "unnamed.map";
1229 Map_UpdateTitle( g_map );
1232 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1233 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1234 g_map.m_resource->attach( g_map );
1236 SceneChangeNotify();
1239 FocusViews( g_vector3_identity, 0 );
1241 g_currentMap = &g_map;
1243 // restart VFS to apply new pak filtering based on mapname
1244 // needed for daemon DPK VFS
1248 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1250 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1252 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1253 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1254 for now, let's just print an error
1257 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1259 for ( int i = 0 ; i < 3 ; i++ )
1261 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1262 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1267 // write the info_playerstart
1269 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1270 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1271 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1272 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1276 ===========================================================
1280 ===========================================================
1283 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1284 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1286 scene::Node* region_sides[6];
1287 scene::Node* region_startpoint = 0;
1292 a regioned map will have temp walls put up at the region boundary
1293 \todo TODO TTimo old implementation of region brushes
1294 we still add them straight in the worldspawn and take them out after the map is saved
1295 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1298 void AddRegionBrushes( void ){
1301 for ( i = 0; i < 6; i++ )
1303 region_sides[i] = &GlobalBrushCreator().createBrush();
1304 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1307 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1309 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1310 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1312 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1315 void RemoveRegionBrushes( void ){
1316 for ( std::size_t i = 0; i < 6; i++ )
1318 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1320 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1323 inline void exclude_node( scene::Node& node, bool exclude ){
1325 ? node.enable( scene::Node::eExcluded )
1326 : node.disable( scene::Node::eExcluded );
1329 class ExcludeAllWalker : public scene::Graph::Walker
1333 ExcludeAllWalker( bool exclude )
1334 : m_exclude( exclude ){
1336 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1337 exclude_node( path.top(), m_exclude );
1343 void Scene_Exclude_All( bool exclude ){
1344 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1347 bool Instance_isSelected( const scene::Instance& instance ){
1348 const Selectable* selectable = Instance_getSelectable( instance );
1349 return selectable != 0 && selectable->isSelected();
1352 class ExcludeSelectedWalker : public scene::Graph::Walker
1356 ExcludeSelectedWalker( bool exclude )
1357 : m_exclude( exclude ){
1359 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1360 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1365 void Scene_Exclude_Selected( bool exclude ){
1366 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1369 class ExcludeRegionedWalker : public scene::Graph::Walker
1373 ExcludeRegionedWalker( bool exclude )
1374 : m_exclude( exclude ){
1376 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1381 aabb_intersects_aabb(
1382 instance.worldAABB(),
1383 aabb_for_minmax( region_mins, region_maxs )
1393 void Scene_Exclude_Region( bool exclude ){
1394 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1401 Other filtering options may still be on
1404 void Map_RegionOff(){
1405 region_active = false;
1407 region_maxs[0] = g_MaxWorldCoord - 64;
1408 region_mins[0] = g_MinWorldCoord + 64;
1409 region_maxs[1] = g_MaxWorldCoord - 64;
1410 region_mins[1] = g_MinWorldCoord + 64;
1411 region_maxs[2] = g_MaxWorldCoord - 64;
1412 region_mins[2] = g_MinWorldCoord + 64;
1414 Scene_Exclude_All( false );
1417 void Map_ApplyRegion( void ){
1418 region_active = true;
1420 Scene_Exclude_Region( false );
1425 ========================
1426 Map_RegionSelectedBrushes
1427 ========================
1429 void Map_RegionSelectedBrushes( void ){
1432 if ( GlobalSelectionSystem().countSelected() != 0
1433 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1434 region_active = true;
1435 Select_GetBounds( region_mins, region_maxs );
1437 Scene_Exclude_Selected( false );
1439 GlobalSelectionSystem().setSelectedAll( false );
1449 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1452 region_mins[0] = x_min;
1453 region_maxs[0] = x_max;
1454 region_mins[1] = y_min;
1455 region_maxs[1] = y_max;
1456 region_mins[2] = g_MinWorldCoord + 64;
1457 region_maxs[2] = g_MaxWorldCoord - 64;
1462 void Map_RegionBounds( const AABB& bounds ){
1465 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1466 region_maxs = vector3_added( bounds.origin, bounds.extents );
1478 void Map_RegionBrush( void ){
1479 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1480 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1481 Map_RegionBounds( instance.worldAABB() );
1490 bool Map_ImportFile( const char* filename ){
1491 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1493 g_strLastFolder = g_path_get_dirname( filename );
1495 bool success = false;
1497 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1502 const MapFormat* format = NULL;
1503 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1504 if ( string_not_empty( moduleName ) ) {
1505 format = ReferenceAPI_getMapModules().findModule( moduleName );
1509 format->wrongFormat = false;
1511 Resource* resource = GlobalReferenceCache().capture( filename );
1512 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1513 if ( !resource->load() ) {
1514 GlobalReferenceCache().release( filename );
1518 if ( format->wrongFormat ) {
1519 GlobalReferenceCache().release( filename );
1523 NodeSmartReference clone( NewMapRoot( "" ) );
1524 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1525 Map_gatherNamespaced( clone );
1526 Map_mergeClonedNames();
1529 GlobalReferenceCache().release( filename );
1532 SceneChangeNotify();
1538 const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
1539 int n = string_length( path_get_extension( filename ) );
1540 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1541 StringBuffer output;
1542 output.push_string( AppPath_get() );
1543 output.push_string( "q3map2." );
1544 output.push_string( RADIANT_EXECUTABLE );
1545 output.push_string( " -v -game " );
1546 output.push_string( ( type && *type ) ? type : "quake3" );
1547 output.push_string( " -fs_basepath \"" );
1548 output.push_string( EnginePath_get() );
1549 output.push_string( "\" -fs_homepath \"" );
1550 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1551 output.push_string( "\" -fs_game " );
1552 output.push_string( gamename_get() );
1553 output.push_string( " -convert -format " );
1554 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1555 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1556 output.push_string( " -readmap " );
1558 output.push_string( " \"" );
1559 output.push_string( filename );
1560 output.push_string( "\"" );
1563 Q_Exec( NULL, output.c_str(), NULL, false, true );
1565 // rebuild filename as "filenamewithoutext_converted.map"
1567 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1568 output.push_string( "_converted.map" );
1569 filename = output.c_str();
1572 Resource* resource = GlobalReferenceCache().capture( filename );
1573 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1574 if ( !resource->load() ) {
1575 GlobalReferenceCache().release( filename );
1578 NodeSmartReference clone( NewMapRoot( "" ) );
1579 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1580 Map_gatherNamespaced( clone );
1581 Map_mergeClonedNames();
1584 GlobalReferenceCache().release( filename );
1587 SceneChangeNotify();
1596 bool Map_SaveFile( const char* filename ){
1597 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1598 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1600 // refresh VFS to apply new pak filtering based on mapname
1601 // needed for daemon DPK VFS
1612 // Saves selected world brushes and whole entities with partial/full selections
1614 bool Map_SaveSelected( const char* filename ){
1615 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1619 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1621 scene::Node& m_parent;
1623 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
1625 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1626 if ( path.top().get_pointer() != &m_parent
1627 && Node_isPrimitive( path.top() ) ) {
1628 Selectable* selectable = Instance_getSelectable( instance );
1629 if ( selectable != 0
1630 && selectable->isSelected()
1631 && path.size() > 1 ) {
1637 void post( const scene::Path& path, scene::Instance& instance ) const {
1638 if ( path.top().get_pointer() != &m_parent
1639 && Node_isPrimitive( path.top() ) ) {
1640 Selectable* selectable = Instance_getSelectable( instance );
1641 if ( selectable != 0
1642 && selectable->isSelected()
1643 && path.size() > 1 ) {
1644 scene::Node& parent = path.parent();
1645 if ( &parent != &m_parent ) {
1646 NodeSmartReference node( path.top().get() );
1647 Node_getTraversable( parent )->erase( node );
1648 Node_getTraversable( m_parent )->insert( node );
1655 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1656 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1659 class CountSelectedBrushes : public scene::Graph::Walker
1661 std::size_t& m_count;
1662 mutable std::size_t m_depth;
1664 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1667 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1668 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1671 Selectable* selectable = Instance_getSelectable( instance );
1672 if ( selectable != 0
1673 && selectable->isSelected()
1674 && Node_isPrimitive( path.top() ) ) {
1679 void post( const scene::Path& path, scene::Instance& instance ) const {
1684 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1686 graph.traverse( CountSelectedBrushes( count ) );
1698 const char* nodetype_get_name( ENodeType type ){
1699 if ( type == eNodeMap ) {
1702 if ( type == eNodeEntity ) {
1705 if ( type == eNodePrimitive ) {
1711 ENodeType node_get_nodetype( scene::Node& node ){
1712 if ( Node_isEntity( node ) ) {
1715 if ( Node_isPrimitive( node ) ) {
1716 return eNodePrimitive;
1718 return eNodeUnknown;
1721 bool contains_entity( scene::Node& node ){
1722 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1725 bool contains_primitive( scene::Node& node ){
1726 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1729 ENodeType node_get_contains( scene::Node& node ){
1730 if ( contains_entity( node ) ) {
1733 if ( contains_primitive( node ) ) {
1734 return eNodePrimitive;
1736 return eNodeUnknown;
1739 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1740 ENodeType contains = node_get_contains( parent.top() );
1741 ENodeType type = node_get_nodetype( child.top() );
1743 if ( contains != eNodeUnknown && contains == type ) {
1744 NodeSmartReference node( child.top().get() );
1745 Path_deleteTop( child );
1746 Node_getTraversable( parent.top() )->insert( node );
1747 SceneChangeNotify();
1751 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1755 void Scene_parentSelected(){
1756 UndoableCommand undo( "parentSelected" );
1758 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1759 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1761 const scene::Path& m_parent;
1763 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1765 void visit( scene::Instance& instance ) const {
1766 if ( &m_parent != &instance.path() ) {
1767 Path_parent( m_parent, instance.path() );
1772 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1773 GlobalSelectionSystem().foreachSelected( visitor );
1777 globalOutputStream() << "failed - did not find two selected nodes.\n";
1784 if ( ConfirmModified( "New Map" ) ) {
1791 CopiedString g_mapsPath;
1793 const char* getMapsPath(){
1794 return g_mapsPath.c_str();
1797 const char* getLastFolderPath(){
1798 if (g_strLastFolder.empty()) {
1799 GlobalPreferenceSystem().registerPreference( "LastFolder", CopiedStringImportStringCaller( g_strLastFolder ), CopiedStringExportStringCaller( g_strLastFolder ) );
1800 if (g_strLastFolder.empty()) {
1801 g_strLastFolder = g_qeglobals.m_userGamePath;
1804 return g_strLastFolder.c_str();
1807 const char* map_open( const char* title ){
1808 return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), true, false, false );
1811 const char* map_import( const char* title ){
1812 return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), false, true, false );
1815 const char* map_save( const char* title ){
1816 return MainFrame_getWindow().file_dialog( FALSE, title, getLastFolderPath(), MapFormat::Name(), false, false, true );
1820 if ( !ConfirmModified( "Open Map" ) ) {
1824 const char* filename = map_open( "Open Map" );
1826 if ( filename != NULL ) {
1827 MRU_AddFile( filename );
1830 Map_LoadFile( filename );
1835 const char* filename = map_import( "Import Map" );
1837 if ( filename != NULL ) {
1838 UndoableCommand undo( "mapImport" );
1839 Map_ImportFile( filename );
1844 const char* filename = map_save( "Save Map" );
1846 if ( filename != NULL ) {
1847 g_strLastFolder = g_path_get_dirname( filename );
1848 MRU_AddFile( filename );
1849 Map_Rename( filename );
1860 if ( Map_Unnamed( g_map ) ) {
1863 else if ( Map_Modified( g_map ) ) {
1869 const char* filename = map_save( "Export Selection" );
1871 if ( filename != NULL ) {
1872 g_strLastFolder = g_path_get_dirname( filename );
1873 Map_SaveSelected( filename );
1878 const char* filename = map_save( "Export Region" );
1880 if ( filename != NULL ) {
1881 g_strLastFolder = g_path_get_dirname( filename );
1882 Map_SaveRegion( filename );
1889 SceneChangeNotify();
1894 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1895 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
1896 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
1897 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
1899 SceneChangeNotify();
1904 SceneChangeNotify();
1907 void RegionSelected(){
1908 Map_RegionSelectedBrushes();
1909 SceneChangeNotify();
1916 class BrushFindByIndexWalker : public scene::Traversable::Walker
1918 mutable std::size_t m_index;
1919 scene::Path& m_path;
1921 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
1922 : m_index( index ), m_path( path ){
1924 bool pre( scene::Node& node ) const {
1925 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
1926 m_path.push( makeReference( node ) );
1932 class EntityFindByIndexWalker : public scene::Traversable::Walker
1934 mutable std::size_t m_index;
1935 scene::Path& m_path;
1937 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
1938 : m_index( index ), m_path( path ){
1940 bool pre( scene::Node& node ) const {
1941 if ( Node_isEntity( node ) && m_index-- == 0 ) {
1942 m_path.push( makeReference( node ) );
1948 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
1949 path.push( makeReference( GlobalSceneGraph().root() ) );
1951 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
1953 if ( path.size() == 2 ) {
1954 scene::Traversable* traversable = Node_getTraversable( path.top() );
1955 if ( traversable != 0 ) {
1956 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
1961 inline bool Node_hasChildren( scene::Node& node ){
1962 scene::Traversable* traversable = Node_getTraversable( node );
1963 return traversable != 0 && !traversable->empty();
1966 void SelectBrush( int entitynum, int brushnum ){
1968 Scene_FindEntityBrush( entitynum, brushnum, path );
1969 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
1970 scene::Instance* instance = GlobalSceneGraph().find( path );
1971 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
1972 Selectable* selectable = Instance_getSelectable( *instance );
1973 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
1974 selectable->setSelected( true );
1975 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
1980 class BrushFindIndexWalker : public scene::Graph::Walker
1982 mutable const scene::Node* m_node;
1983 std::size_t& m_count;
1985 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
1986 : m_node( &node ), m_count( count ){
1988 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1989 if ( Node_isPrimitive( path.top() ) ) {
1990 if ( m_node == path.top().get_pointer() ) {
2001 class EntityFindIndexWalker : public scene::Graph::Walker
2003 mutable const scene::Node* m_node;
2004 std::size_t& m_count;
2006 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
2007 : m_node( &node ), m_count( count ){
2009 bool pre( const scene::Path& path, scene::Instance& instance ) const {
2010 if ( Node_isEntity( path.top() ) ) {
2011 if ( m_node == path.top().get_pointer() ) {
2022 static void GetSelectionIndex( int *ent, int *brush ){
2023 std::size_t count_brush = 0;
2024 std::size_t count_entity = 0;
2025 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2026 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2028 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2029 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2031 *brush = int(count_brush);
2032 *ent = int(count_entity);
2037 ui::Entry entity{ui::null};
2038 ui::Entry brush{ui::null};
2040 ui::Window window = MainFrame_getWindow().create_dialog_window("Find Brush", G_CALLBACK(dialog_delete_callback ), &dialog );
2042 auto accel = ui::AccelGroup(ui::New);
2043 window.add_accel_group( accel );
2046 auto vbox = create_dialog_vbox( 4, 4 );
2049 auto table = create_dialog_table( 2, 2, 4, 4 );
2050 vbox.pack_start( table, TRUE, TRUE, 0 );
2052 ui::Widget label = ui::Label( "Entity number" );
2054 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2055 (GtkAttachOptions) ( 0 ),
2056 (GtkAttachOptions) ( 0 ), 0, 0 );
2059 ui::Widget label = ui::Label( "Brush number" );
2061 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2062 (GtkAttachOptions) ( 0 ),
2063 (GtkAttachOptions) ( 0 ), 0, 0 );
2066 auto entry = ui::Entry(ui::New);
2068 gtk_table_attach( table, entry , 1, 2, 0, 1,
2069 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2070 (GtkAttachOptions) ( 0 ), 0, 0 );
2071 gtk_widget_grab_focus( entry );
2075 auto entry = ui::Entry(ui::New);
2077 gtk_table_attach( table, entry , 1, 2, 1, 2,
2078 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2079 (GtkAttachOptions) ( 0 ), 0, 0 );
2085 auto hbox = create_dialog_hbox( 4 );
2086 vbox.pack_start( hbox, TRUE, TRUE, 0 );
2088 auto button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2089 hbox.pack_start( button, FALSE, FALSE, 0 );
2090 widget_make_default( button );
2091 gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2094 auto button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2095 hbox.pack_start( button, FALSE, FALSE, 0 );
2096 gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2101 // Initialize dialog
2105 GetSelectionIndex( &ent, &br );
2106 sprintf( buf, "%i", ent );
2108 sprintf( buf, "%i", br );
2111 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2112 const char *entstr = gtk_entry_get_text( entity );
2113 const char *brushstr = gtk_entry_get_text( brush );
2114 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2120 void Map_constructPreferences( PreferencesPage& page ){
2121 page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
2125 class MapEntityClasses : public ModuleObserver
2127 std::size_t m_unrealised;
2129 MapEntityClasses() : m_unrealised( 1 ){
2132 if ( --m_unrealised == 0 ) {
2133 if ( g_map.m_resource != 0 ) {
2134 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2135 g_map.m_resource->realise();
2140 if ( ++m_unrealised == 1 ) {
2141 if ( g_map.m_resource != 0 ) {
2142 g_map.m_resource->flush();
2143 g_map.m_resource->unrealise();
2149 MapEntityClasses g_MapEntityClasses;
2152 class MapModuleObserver : public ModuleObserver
2154 std::size_t m_unrealised;
2156 MapModuleObserver() : m_unrealised( 1 ){
2159 if ( --m_unrealised == 0 ) {
2160 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2161 StringOutputStream buffer( 256 );
2162 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2163 Q_mkdir( buffer.c_str() );
2164 g_mapsPath = buffer.c_str();
2168 if ( ++m_unrealised == 1 ) {
2174 MapModuleObserver g_MapModuleObserver;
2176 CopiedString g_strLastMap;
2177 bool g_bLoadLastMap = false;
2179 void Map_Construct(){
2180 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2181 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2182 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2183 GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2185 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2186 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2187 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2189 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2191 GlobalEntityClassManager().attach( g_MapEntityClasses );
2192 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2196 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2197 GlobalEntityClassManager().detach( g_MapEntityClasses );