2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "debugging/debugging.h"
27 MapModules& ReferenceAPI_getMapModules();
28 #include "iselection.h"
32 #include "ireference.h"
33 #include "ifiletypes.h"
38 #include "ifilesystem.h"
39 #include "namespace.h"
40 #include "moduleobserver.h"
44 #include <gtk/gtkmain.h>
45 #include <gtk/gtkbox.h>
46 #include <gtk/gtkentry.h>
47 #include <gtk/gtklabel.h>
48 #include <gtk/gtktable.h>
49 #include <gtk/gtktreemodel.h>
50 #include <gtk/gtktreeview.h>
51 #include <gtk/gtkliststore.h>
52 #include <gtk/gtkcellrenderertext.h>
55 #include "transformlib.h"
56 #include "selectionlib.h"
57 #include "instancelib.h"
58 #include "traverselib.h"
60 #include "eclasslib.h"
62 #include "stream/textfilestream.h"
64 #include "uniquenames.h"
65 #include "modulesystem/singletonmodule.h"
66 #include "modulesystem/moduleregistry.h"
67 #include "stream/stringstream.h"
68 #include "signal/signal.h"
70 #include "gtkutil/filechooser.h"
74 #include "filetypes.h"
76 #include "entityinspector.h"
79 #include "camwindow.h"
81 #include "mainframe.h"
82 #include "preferences.h"
83 #include "referencecache.h"
87 #include "brushmodule.h"
98 //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
99 m_names.insert( name_read( c_str() ) );
104 //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
105 m_names.erase( name_read( c_str() ) );
109 NameObserver& operator=( const NameObserver& other );
111 NameObserver( UniqueNames& names ) : m_names( names ){
114 NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
121 return string_empty( c_str() );
123 const char* c_str() const {
124 return m_name.c_str();
126 void nameChanged( const char* name ){
131 typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
134 class BasicNamespace : public Namespace
136 typedef std::map<NameCallback, NameObserver> Names;
138 UniqueNames m_uniqueNames;
141 ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
143 void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
144 std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
145 ASSERT_MESSAGE( result.second, "cannot attach name" );
146 attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
147 //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
149 void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
150 Names::iterator i = m_names.find( setName );
151 ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
152 //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
153 detachObserver( NameObserver::NameChangedCaller( ( *i ).second ) );
157 void makeUnique( const char* name, const NameCallback& setName ) const {
159 name_write( buffer, m_uniqueNames.make_unique( name_read( name ) ) );
163 void mergeNames( const BasicNamespace& other ) const {
164 typedef std::list<NameCallback> SetNameCallbacks;
165 typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
168 UniqueNames uniqueNames( other.m_uniqueNames );
170 for ( Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
172 groups[( *i ).second.c_str()].push_back( ( *i ).first );
175 for ( NameGroups::iterator i = groups.begin(); i != groups.end(); ++i )
177 name_t uniqueName( uniqueNames.make_unique( name_read( ( *i ).first.c_str() ) ) );
178 uniqueNames.insert( uniqueName );
181 name_write( buffer, uniqueName );
183 //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
185 SetNameCallbacks& setNameCallbacks = ( *i ).second;
187 for ( SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j )
195 BasicNamespace g_defaultNamespace;
196 BasicNamespace g_cloneNamespace;
200 Namespace* m_namespace;
202 typedef Namespace Type;
203 STRING_CONSTANT( Name, "*" );
206 m_namespace = &g_defaultNamespace;
208 Namespace* getTable(){
213 typedef SingletonModule<NamespaceAPI> NamespaceModule;
214 typedef Static<NamespaceModule> StaticNamespaceModule;
215 StaticRegisterModule staticRegisterDefaultNamespace( StaticNamespaceModule::instance() );
218 std::list<Namespaced*> g_cloned;
220 inline Namespaced* Node_getNamespaced( scene::Node& node ){
221 return NodeTypeCast<Namespaced>::cast( node );
224 void Node_gatherNamespaced( scene::Node& node ){
225 Namespaced* namespaced = Node_getNamespaced( node );
226 if ( namespaced != 0 ) {
227 g_cloned.push_back( namespaced );
231 class GatherNamespaced : public scene::Traversable::Walker
234 bool pre( scene::Node& node ) const {
235 Node_gatherNamespaced( node );
240 void Map_gatherNamespaced( scene::Node& root ){
241 Node_traverseSubgraph( root, GatherNamespaced() );
244 void Map_mergeClonedNames(){
245 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
247 ( *i )->setNamespace( g_cloneNamespace );
249 g_cloneNamespace.mergeNames( g_defaultNamespace );
250 for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
252 ( *i )->setNamespace( g_defaultNamespace );
265 void set( scene::Node* node ){
274 scene::Node* get() const {
280 void Map_SetValid( Map& map, bool valid );
281 void Map_UpdateTitle( const Map& map );
282 void Map_SetWorldspawn( Map& map, scene::Node* node );
285 class Map : public ModuleObserver
289 Resource* m_resource;
293 void ( *m_modified_changed )( const Map& );
295 Signal0 m_mapValidCallbacks;
297 WorldNode m_world_node; // "classname" "worldspawn" !
299 Map() : m_resource( 0 ), m_valid( false ), m_modified_changed( Map_UpdateTitle ){
303 if ( m_resource != 0 ) {
304 if ( Map_Unnamed( *this ) ) {
305 g_map.m_resource->setNode( NewMapRoot( "" ).get_pointer() );
306 MapFile* map = Node_getMapFile( *g_map.m_resource->getNode() );
316 GlobalSceneGraph().insert_root( *m_resource->getNode() );
320 Map_SetValid( g_map, true );
324 if ( m_resource != 0 ) {
325 Map_SetValid( g_map, false );
326 Map_SetWorldspawn( g_map, 0 );
329 GlobalUndoSystem().clear();
331 GlobalSceneGraph().erase_root();
337 Map* g_currentMap = 0;
339 void Map_addValidCallback( Map& map, const SignalHandler& handler ){
340 map.m_mapValidCallbacks.connectLast( handler );
343 bool Map_Valid( const Map& map ){
347 void Map_SetValid( Map& map, bool valid ){
349 map.m_mapValidCallbacks();
353 const char* Map_Name( const Map& map ){
354 return map.m_name.c_str();
357 bool Map_Unnamed( const Map& map ){
358 return string_equal( Map_Name( map ), "unnamed.map" );
361 inline const MapFormat& MapFormat_forFile( const char* filename ){
362 const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), path_get_extension( filename ) );
363 MapFormat* format = Radiant_getMapModules().findModule( moduleName );
364 ASSERT_MESSAGE( format != 0, "map format not found for file " << makeQuoted( filename ) );
368 const MapFormat& Map_getFormat( const Map& map ){
369 return MapFormat_forFile( Map_Name( map ) );
373 bool Map_Modified( const Map& map ){
374 return map.m_modified;
377 void Map_SetModified( Map& map, bool modified ){
378 if ( map.m_modified ^ modified ) {
379 map.m_modified = modified;
381 map.m_modified_changed( map );
385 void Map_UpdateTitle( const Map& map ){
386 Sys_SetTitle( map.m_name.c_str(), Map_Modified( map ) );
391 scene::Node* Map_GetWorldspawn( const Map& map ){
392 return map.m_world_node.get();
395 void Map_SetWorldspawn( Map& map, scene::Node* node ){
396 map.m_world_node.set( node );
401 // need that in a variable, will have to tweak depending on the game
402 float g_MaxWorldCoord = 64 * 1024;
403 float g_MinWorldCoord = -64 * 1024;
405 void AddRegionBrushes( void );
406 void RemoveRegionBrushes( void );
412 free all map elements, reinitialize the structures that depend on them
418 g_map.m_resource->detach( g_map );
419 GlobalReferenceCache().release( g_map.m_name.c_str() );
420 g_map.m_resource = 0;
425 Brush_unlatchPreferences();
428 class EntityFindByClassname : public scene::Graph::Walker
433 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
436 bool pre( const scene::Path& path, scene::Instance& instance ) const {
437 if ( m_entity == 0 ) {
438 Entity* entity = Node_getEntity( path.top() );
440 && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
448 Entity* Scene_FindEntityByClass( const char* name ){
450 GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
454 Entity *Scene_FindPlayerStart(){
455 typedef const char* StaticString;
456 StaticString strings[] = {
458 "info_player_deathmatch",
459 "team_CTF_redplayer",
460 "team_CTF_blueplayer",
462 "team_CTF_bluespawn",
464 typedef const StaticString* StaticStringIterator;
465 for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
467 Entity* entity = Scene_FindEntityByClass( *i );
476 // move the view to a start position
480 void FocusViews( const Vector3& point, float angle ){
481 CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
482 Camera_setOrigin( camwnd, point );
483 Vector3 angles( Camera_getAngles( camwnd ) );
484 angles[CAMERA_PITCH] = 0;
485 angles[CAMERA_YAW] = angle;
486 Camera_setAngles( camwnd, angles );
488 XYWnd* xywnd = g_pParentWnd->GetXYWnd();
489 xywnd->SetOrigin( point );
492 #include "stringio.h"
494 void Map_StartPosition(){
495 Entity* entity = Scene_FindPlayerStart();
499 string_parse_vector3( entity->getKeyValue( "origin" ), origin );
500 FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
504 FocusViews( g_vector3_identity, 0 );
509 inline bool node_is_worldspawn( scene::Node& node ){
510 Entity* entity = Node_getEntity( node );
511 return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
515 // use first worldspawn
516 class entity_updateworldspawn : public scene::Traversable::Walker
519 bool pre( scene::Node& node ) const {
520 if ( node_is_worldspawn( node ) ) {
521 if ( Map_GetWorldspawn( g_map ) == 0 ) {
522 Map_SetWorldspawn( g_map, &node );
529 scene::Node* Map_FindWorldspawn( Map& map ){
530 Map_SetWorldspawn( map, 0 );
532 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
534 return Map_GetWorldspawn( map );
538 class CollectAllWalker : public scene::Traversable::Walker
541 UnsortedNodeSet& m_nodes;
543 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
545 bool pre( scene::Node& node ) const {
546 m_nodes.insert( NodeSmartReference( node ) );
547 Node_getTraversable( m_root )->erase( node );
552 void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
553 UnsortedNodeSet nodes;
554 Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
555 Node_getTraversable( parent )->insert( child );
557 for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
559 Node_getTraversable( parent )->insert( ( *i ) );
563 scene::Node& createWorldspawn(){
564 NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
565 Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
569 void Map_UpdateWorldspawn( Map& map ){
570 if ( Map_FindWorldspawn( map ) == 0 ) {
571 Map_SetWorldspawn( map, &createWorldspawn() );
575 scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
576 Map_UpdateWorldspawn( map );
577 return *Map_GetWorldspawn( map );
581 class MapMergeAll : public scene::Traversable::Walker
583 mutable scene::Path m_path;
585 MapMergeAll( const scene::Path& root )
588 bool pre( scene::Node& node ) const {
589 Node_getTraversable( m_path.top() )->insert( node );
590 m_path.push( makeReference( node ) );
591 selectPath( m_path, true );
594 void post( scene::Node& node ) const {
599 class MapMergeEntities : public scene::Traversable::Walker
601 mutable scene::Path m_path;
603 MapMergeEntities( const scene::Path& root )
606 bool pre( scene::Node& node ) const {
607 if ( node_is_worldspawn( node ) ) {
608 scene::Node* world_node = Map_FindWorldspawn( g_map );
609 if ( world_node == 0 ) {
610 Map_SetWorldspawn( g_map, &node );
611 Node_getTraversable( m_path.top().get() )->insert( node );
612 m_path.push( makeReference( node ) );
613 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
617 m_path.push( makeReference( *world_node ) );
618 Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
623 Node_getTraversable( m_path.top() )->insert( node );
624 m_path.push( makeReference( node ) );
625 if ( node_is_group( node ) ) {
626 Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
630 selectPath( m_path, true );
635 void post( scene::Node& node ) const {
640 class BasicContainer : public scene::Node::Symbiot
644 NodeTypeCastTable m_casts;
647 NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
649 NodeTypeCastTable& get(){
655 TraversableNodeSet m_traverse;
658 typedef LazyStatic<TypeCasts> StaticTypeCasts;
660 scene::Traversable& get( NullType<scene::Traversable>){
664 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
674 /// Merges the map graph rooted at \p node into the global scene-graph.
675 void MergeMap( scene::Node& node ){
676 Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
678 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
679 NodeSmartReference node( ( new BasicContainer )->node() );
680 format.readGraph( node, in, GlobalEntityCreator() );
681 Map_gatherNamespaced( node );
682 Map_mergeClonedNames();
686 inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
687 return NodeTypeCast<scene::Cloneable>::cast( node );
690 inline scene::Node& node_clone( scene::Node& node ){
691 scene::Cloneable* cloneable = Node_getCloneable( node );
692 if ( cloneable != 0 ) {
693 return cloneable->clone();
696 return ( new scene::NullNode )->node();
699 class CloneAll : public scene::Traversable::Walker
701 mutable scene::Path m_path;
703 CloneAll( scene::Node& root )
704 : m_path( makeReference( root ) ){
706 bool pre( scene::Node& node ) const {
707 if ( node.isRoot() ) {
711 m_path.push( makeReference( node_clone( node ) ) );
712 m_path.top().get().IncRef();
716 void post( scene::Node& node ) const {
717 if ( node.isRoot() ) {
721 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
723 m_path.top().get().DecRef();
728 scene::Node& Node_Clone( scene::Node& node ){
729 scene::Node& clone = node_clone( node );
730 scene::Traversable* traversable = Node_getTraversable( node );
731 if ( traversable != 0 ) {
732 traversable->traverse( CloneAll( clone ) );
737 bool Node_instanceSelected( scene::Node& node );
739 class CloneAllSelected : public scene::Traversable::Walker
741 mutable scene::Path m_path;
743 CloneAllSelected( scene::Node& root )
744 : m_path( makeReference( root ) ){
746 bool pre( scene::Node& node ) const {
747 if ( node.isRoot() ) {
751 if( Node_instanceSelected( node ) ){
752 m_path.push( makeReference( node_clone( node ) ) );
753 m_path.top().get().IncRef();
758 void post( scene::Node& node ) const {
759 if ( node.isRoot() ) {
763 if( Node_instanceSelected( node ) ){
764 Node_getTraversable( m_path.parent() )->insert( m_path.top() );
766 m_path.top().get().DecRef();
772 scene::Node& Node_Clone_Selected( scene::Node& node ){
773 scene::Node& clone = node_clone( node );
774 scene::Traversable* traversable = Node_getTraversable( node );
775 if ( traversable != 0 ) {
776 traversable->traverse( CloneAllSelected( clone ) );
782 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
784 class EntityBreakdownWalker : public scene::Graph::Walker
786 EntityBreakdown& m_entitymap;
788 EntityBreakdownWalker( EntityBreakdown& entitymap )
789 : m_entitymap( entitymap ){
791 bool pre( const scene::Path& path, scene::Instance& instance ) const {
792 Entity* entity = Node_getEntity( path.top() );
794 const EntityClass& eclass = entity->getEntityClass();
795 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
796 m_entitymap[eclass.name()] = 1;
798 else{ ++m_entitymap[eclass.name()]; }
804 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
805 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
808 class CountStuffWalker : public scene::Graph::Walker
813 int& m_groupents_ingame;
815 CountStuffWalker( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame )
816 : m_patches( patches ), m_ents_ingame( ents_ingame ), m_groupents( groupents ), m_groupents_ingame( groupents_ingame ){
818 bool pre( const scene::Path& path, scene::Instance& instance ) const {
819 Patch* patch = Node_getPatch( path.top() );
823 Entity* entity = Node_getEntity( path.top() );
825 if( entity->isContainer() ){
827 if( !string_equal_nocase( "func_group", entity->getKeyValue( "classname" ) ) &&
828 !string_equal_nocase( "_decal", entity->getKeyValue( "classname" ) ) ){
829 ++m_groupents_ingame;
834 if( !string_equal_nocase_n( "light", entity->getKeyValue( "classname" ), 5 ) &&
835 !string_equal_nocase( "misc_model", entity->getKeyValue( "classname" ) ) ){
843 void Scene_CountStuff( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame ){
844 GlobalSceneGraph().traverse( CountStuffWalker( patches, ents_ingame, groupents, groupents_ingame ) );
847 WindowPosition g_posMapInfoWnd( c_default_window_pos );
852 GtkWidget* w_brushes;
853 GtkWidget* w_patches;
855 GtkWidget* w_ents_ingame;
856 GtkWidget* w_groupents;
857 GtkWidget* w_groupents_ingame;
859 GtkListStore* EntityBreakdownWalker;
861 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Map Info", G_CALLBACK( dialog_delete_callback ), &dialog );
863 window_set_position( window, g_posMapInfoWnd );
866 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
867 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
870 GtkHBox* hbox = create_dialog_hbox( 4 );
871 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, FALSE, 0 );
874 GtkTable* table = create_dialog_table( 3, 4, 4, 4 );
875 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
877 GtkWidget* label = gtk_label_new( "Total Brushes:" );
878 gtk_widget_show( label );
879 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
880 (GtkAttachOptions) ( GTK_FILL ),
881 (GtkAttachOptions) ( 0 ), 0, 0 );
882 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
885 GtkWidget* label = gtk_label_new( "" );
886 gtk_widget_show( label );
887 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 0, 1,
888 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
889 (GtkAttachOptions) ( 0 ), 3, 0 );
890 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
894 GtkWidget* label = gtk_label_new( "Total Patches:" );
895 gtk_widget_show( label );
896 gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 0, 1,
897 (GtkAttachOptions) ( GTK_FILL ),
898 (GtkAttachOptions) ( 0 ), 0, 0 );
899 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
902 GtkWidget* label = gtk_label_new( "" );
903 gtk_widget_show( label );
904 gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 0, 1,
905 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
906 (GtkAttachOptions) ( 0 ), 3, 0 );
907 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
911 GtkWidget* label = gtk_label_new( "Total Entities:" );
912 gtk_widget_show( label );
913 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
914 (GtkAttachOptions) ( GTK_FILL ),
915 (GtkAttachOptions) ( 0 ), 0, 0 );
916 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
919 GtkWidget* label = gtk_label_new( "" );
920 gtk_widget_show( label );
921 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 1, 2,
922 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
923 (GtkAttachOptions) ( 0 ), 3, 0 );
924 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
928 GtkWidget* label = gtk_label_new( "Ingame Entities:" );
929 gtk_widget_show( label );
930 gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 1, 2,
931 (GtkAttachOptions) ( GTK_FILL ),
932 (GtkAttachOptions) ( 0 ), 0, 0 );
933 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
936 GtkWidget* label = gtk_label_new( "" );
937 gtk_widget_show( label );
938 gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 1, 2,
939 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
940 (GtkAttachOptions) ( 0 ), 3, 0 );
941 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
942 w_ents_ingame = label;
945 GtkWidget* label = gtk_label_new( "Group Entities:" );
946 gtk_widget_show( label );
947 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 2, 3,
948 (GtkAttachOptions) ( GTK_FILL ),
949 (GtkAttachOptions) ( 0 ), 0, 0 );
950 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
953 GtkWidget* label = gtk_label_new( "" );
954 gtk_widget_show( label );
955 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 2, 3,
956 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
957 (GtkAttachOptions) ( 0 ), 3, 0 );
958 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
962 GtkWidget* label = gtk_label_new( "Ingame Group Entities:" );
963 gtk_widget_show( label );
964 gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 2, 3,
965 (GtkAttachOptions) ( GTK_FILL ),
966 (GtkAttachOptions) ( 0 ), 0, 0 );
967 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
970 GtkWidget* label = gtk_label_new( "" );
971 gtk_widget_show( label );
972 gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 2, 3,
973 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
974 (GtkAttachOptions) ( 0 ), 3, 0 );
975 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
976 w_groupents_ingame = label;
981 GtkVBox* vbox2 = create_dialog_vbox( 4 );
982 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
985 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
986 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
991 GtkWidget* label = gtk_label_new( "*** Entity breakdown ***" );
992 gtk_widget_show( label );
993 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
994 gtk_misc_set_alignment( GTK_MISC( label ), 0.5, 0.5 );
997 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
998 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
1001 GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_UINT );
1003 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
1004 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
1007 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1008 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Entity", renderer, "text", 0, 0 );
1009 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
1010 gtk_tree_view_column_set_sort_column_id( column, 0 );
1014 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
1015 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Count", renderer, "text", 1, 0 );
1016 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
1017 gtk_tree_view_column_set_sort_column_id( column, 1 );
1020 gtk_widget_show( view );
1022 gtk_container_add( GTK_CONTAINER( scr ), view );
1024 EntityBreakdownWalker = store;
1029 // Initialize fields
1032 EntityBreakdown entitymap;
1033 Scene_EntityBreakdown( entitymap );
1035 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
1038 gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
1039 gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, Unsigned( ( *i ).second ), -1 );
1043 g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
1046 int n_ents_ingame = 0;
1047 int n_groupents = 0;
1048 int n_groupents_ingame = 0;
1049 Scene_CountStuff( n_patches, n_ents_ingame, n_groupents, n_groupents_ingame );
1050 //globalOutputStream() << n_patches << n_ents_ingame << n_groupents << n_groupents_ingame << "\n";
1054 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%u</b></span> ", Unsigned( g_brushCount.get() ) );
1055 gtk_label_set_markup( GTK_LABEL( w_brushes ), markup );
1058 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span> ", n_patches );
1059 gtk_label_set_markup( GTK_LABEL( w_patches ), markup );
1062 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%u</b></span> ", Unsigned( g_entityCount.get() ) );
1063 gtk_label_set_markup( GTK_LABEL( w_ents ), markup );
1066 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span> ", n_ents_ingame );
1067 gtk_label_set_markup( GTK_LABEL( w_ents_ingame ), markup );
1070 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span> ", n_groupents );
1071 gtk_label_set_markup( GTK_LABEL( w_groupents ), markup );
1074 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span> ", n_groupents_ingame );
1075 gtk_label_set_markup( GTK_LABEL( w_groupents_ingame ), markup );
1078 modal_dialog_show( window, dialog );
1081 window_get_position( window, g_posMapInfoWnd );
1083 gtk_widget_destroy( GTK_WIDGET( window ) );
1091 const char* m_message;
1093 ScopeTimer( const char* message )
1094 : m_message( message ){
1098 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
1099 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
1109 void Map_LoadFile( const char *filename ){
1110 globalOutputStream() << "Loading map from " << filename << "\n";
1111 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1114 ScopeTimer timer( "map load" );
1116 const MapFormat* format = NULL;
1117 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1118 if ( string_not_empty( moduleName ) ) {
1119 format = ReferenceAPI_getMapModules().findModule( moduleName );
1122 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
1127 Brush_toggleFormat( i );
1128 g_map.m_name = filename;
1129 Map_UpdateTitle( g_map );
1130 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1132 format->wrongFormat = false;
1134 g_map.m_resource->attach( g_map );
1136 if ( !format->wrongFormat ) {
1142 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
1145 globalOutputStream() << "--- LoadMapFile ---\n";
1146 globalOutputStream() << g_map.m_name.c_str() << "\n";
1148 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
1149 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
1151 //GlobalEntityCreator().printStatistics();
1154 // move the view to a start position
1156 Map_StartPosition();
1158 g_currentMap = &g_map;
1164 virtual bool excluded( scene::Node& node ) const = 0;
1167 class ExcludeWalker : public scene::Traversable::Walker
1169 const scene::Traversable::Walker& m_walker;
1170 const Excluder* m_exclude;
1171 mutable bool m_skip;
1173 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
1174 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
1176 bool pre( scene::Node& node ) const {
1177 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1183 m_walker.pre( node );
1187 void post( scene::Node& node ) const {
1193 m_walker.post( node );
1198 class AnyInstanceSelected : public scene::Instantiable::Visitor
1202 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1205 void visit( scene::Instance& instance ) const {
1206 Selectable* selectable = Instance_getSelectable( instance );
1207 if ( selectable != 0
1208 && selectable->isSelected() ) {
1214 bool Node_instanceSelected( scene::Node& node ){
1215 scene::Instantiable* instantiable = Node_getInstantiable( node );
1216 ASSERT_NOTNULL( instantiable );
1218 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1222 class SelectedDescendantWalker : public scene::Traversable::Walker
1226 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1230 bool pre( scene::Node& node ) const {
1231 if ( node.isRoot() ) {
1235 if ( Node_instanceSelected( node ) ) {
1243 bool Node_selectedDescendant( scene::Node& node ){
1245 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1249 class SelectionExcluder : public Excluder
1252 bool excluded( scene::Node& node ) const {
1253 return !Node_selectedDescendant( node );
1257 class IncludeSelectedWalker : public scene::Traversable::Walker
1259 const scene::Traversable::Walker& m_walker;
1260 mutable std::size_t m_selected;
1261 mutable bool m_skip;
1263 bool selectedParent() const {
1264 return m_selected != 0;
1267 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1268 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1270 bool pre( scene::Node& node ) const {
1272 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1273 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1274 if ( Node_instanceSelected( node ) ) {
1277 m_walker.pre( node );
1286 void post( scene::Node& node ) const {
1292 if ( Node_instanceSelected( node ) ) {
1295 m_walker.post( node );
1300 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1301 scene::Traversable* traversable = Node_getTraversable( root );
1302 if ( traversable != 0 ) {
1304 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1306 traversable->traverse( IncludeSelectedWalker( walker ) );
1311 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1312 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1315 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1316 scene::Traversable* traversable = Node_getTraversable( root );
1317 if ( traversable != 0 ) {
1318 traversable->traverse( walker );
1322 class RegionExcluder : public Excluder
1325 bool excluded( scene::Node& node ) const {
1326 return node.excluded();
1330 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1331 scene::Traversable* traversable = Node_getTraversable( root );
1332 if ( traversable != 0 ) {
1333 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1337 bool Map_SaveRegion( const char *filename ){
1340 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1342 RemoveRegionBrushes();
1348 void Map_RenameAbsolute( const char* absolute ){
1349 Resource* resource = GlobalReferenceCache().capture( absolute );
1350 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1351 resource->setNode( clone.get_pointer() );
1354 //ScopeTimer timer("clone subgraph");
1355 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1358 g_map.m_resource->detach( g_map );
1359 GlobalReferenceCache().release( g_map.m_name.c_str() );
1361 g_map.m_resource = resource;
1363 g_map.m_name = absolute;
1364 Map_UpdateTitle( g_map );
1366 g_map.m_resource->attach( g_map );
1369 void Map_Rename( const char* filename ){
1370 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1371 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1373 Map_RenameAbsolute( filename );
1375 SceneChangeNotify();
1386 ScopeTimer timer( "map save" );
1388 return true; // assume success..
1398 //globalOutputStream() << "Map_New\n";
1400 g_map.m_name = "unnamed.map";
1401 Map_UpdateTitle( g_map );
1404 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1405 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1406 g_map.m_resource->attach( g_map );
1408 SceneChangeNotify();
1411 FocusViews( g_vector3_identity, 0 );
1413 g_currentMap = &g_map;
1416 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1418 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1420 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1421 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1422 for now, let's just print an error
1425 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1427 for ( int i = 0 ; i < 3 ; i++ )
1429 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1430 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1435 // write the info_playerstart
1437 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1438 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1439 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1440 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1444 ===========================================================
1448 ===========================================================
1450 bool region_active = false;
1452 BoolExportCaller g_region_caller( region_active );
1453 ToggleItem g_region_item( g_region_caller );
1455 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1456 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1458 scene::Node* region_sides[6];
1459 scene::Node* region_startpoint = 0;
1464 a regioned map will have temp walls put up at the region boundary
1465 \todo TODO TTimo old implementation of region brushes
1466 we still add them straight in the worldspawn and take them out after the map is saved
1467 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1470 void AddRegionBrushes( void ){
1473 for ( i = 0; i < 6; i++ )
1475 region_sides[i] = &GlobalBrushCreator().createBrush();
1476 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1479 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1481 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1482 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1484 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1487 void RemoveRegionBrushes( void ){
1488 for ( std::size_t i = 0; i < 6; i++ )
1490 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1492 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1495 inline void exclude_node( scene::Node& node, bool exclude ){
1497 ? node.enable( scene::Node::eExcluded )
1498 : node.disable( scene::Node::eExcluded );
1501 class ExcludeAllWalker : public scene::Graph::Walker
1505 ExcludeAllWalker( bool exclude )
1506 : m_exclude( exclude ){
1508 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1509 exclude_node( path.top(), m_exclude );
1515 void Scene_Exclude_All( bool exclude ){
1516 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1519 bool Instance_isSelected( const scene::Instance& instance ){
1520 const Selectable* selectable = Instance_getSelectable( instance );
1521 return selectable != 0 && selectable->isSelected();
1524 class ExcludeSelectedWalker : public scene::Graph::Walker
1528 ExcludeSelectedWalker( bool exclude )
1529 : m_exclude( exclude ){
1531 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1532 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1537 void Scene_Exclude_Selected( bool exclude ){
1538 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1541 class ExcludeRegionedWalker : public scene::Graph::Walker
1545 ExcludeRegionedWalker( bool exclude )
1546 : m_exclude( exclude ){
1548 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1553 aabb_intersects_aabb(
1554 instance.worldAABB(),
1555 aabb_for_minmax( region_mins, region_maxs )
1565 void Scene_Exclude_Region( bool exclude ){
1566 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1573 Other filtering options may still be on
1576 void Map_RegionOff(){
1577 region_active = false;
1578 g_region_item.update();
1580 region_maxs[0] = g_MaxWorldCoord - 64;
1581 region_mins[0] = g_MinWorldCoord + 64;
1582 region_maxs[1] = g_MaxWorldCoord - 64;
1583 region_mins[1] = g_MinWorldCoord + 64;
1584 region_maxs[2] = g_MaxWorldCoord - 64;
1585 region_mins[2] = g_MinWorldCoord + 64;
1587 Scene_Exclude_All( false );
1590 void Map_ApplyRegion( void ){
1591 region_active = true;
1592 g_region_item.update();
1594 Scene_Exclude_Region( false );
1599 ========================
1600 Map_RegionSelectedBrushes
1601 ========================
1603 void Map_RegionSelectedBrushes( void ){
1606 if ( GlobalSelectionSystem().countSelected() != 0
1607 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1608 region_active = true;
1609 g_region_item.update();
1610 Select_GetBounds( region_mins, region_maxs );
1612 Scene_Exclude_Selected( false );
1614 GlobalSelectionSystem().setSelectedAll( false );
1624 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1627 region_mins[0] = x_min;
1628 region_maxs[0] = x_max;
1629 region_mins[1] = y_min;
1630 region_maxs[1] = y_max;
1631 region_mins[2] = g_MinWorldCoord + 64;
1632 region_maxs[2] = g_MaxWorldCoord - 64;
1637 void Map_RegionBounds( const AABB& bounds ){
1640 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1641 region_maxs = vector3_added( bounds.origin, bounds.extents );
1653 void Map_RegionBrush( void ){
1654 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1655 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1656 Map_RegionBounds( instance.worldAABB() );
1665 bool Map_ImportFile( const char* filename ){
1666 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1668 bool success = false;
1670 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1675 const MapFormat* format = NULL;
1676 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1677 if ( string_not_empty( moduleName ) ) {
1678 format = ReferenceAPI_getMapModules().findModule( moduleName );
1682 format->wrongFormat = false;
1684 Resource* resource = GlobalReferenceCache().capture( filename );
1685 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1686 if ( !resource->load() ) {
1687 GlobalReferenceCache().release( filename );
1691 if ( format->wrongFormat ) {
1692 GlobalReferenceCache().release( filename );
1696 NodeSmartReference clone( NewMapRoot( "" ) );
1697 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1698 Map_gatherNamespaced( clone );
1699 Map_mergeClonedNames();
1702 GlobalReferenceCache().release( filename );
1705 SceneChangeNotify();
1711 const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" );
1712 int n = string_length( path_get_extension( filename ) );
1713 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1714 StringBuffer output;
1715 output.push_string( AppPath_get() );
1716 output.push_string( "q3map2." );
1717 output.push_string( RADIANT_EXECUTABLE );
1718 output.push_string( " -v -game " );
1719 output.push_string( ( type && *type ) ? type : "quake3" );
1720 output.push_string( " -fs_basepath \"" );
1721 output.push_string( EnginePath_get() );
1722 output.push_string( "\" -fs_homepath \"" );
1723 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1724 output.push_string( "\" -fs_game " );
1725 output.push_string( gamename_get() );
1726 output.push_string( " -convert -format " );
1727 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1728 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1729 output.push_string( " -readmap " );
1731 output.push_string( " \"" );
1732 output.push_string( filename );
1733 output.push_string( "\"" );
1736 Q_Exec( NULL, output.c_str(), NULL, false, true );
1738 // rebuild filename as "filenamewithoutext_converted.map"
1740 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1741 output.push_string( "_converted.map" );
1742 filename = output.c_str();
1745 Resource* resource = GlobalReferenceCache().capture( filename );
1746 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1747 if ( !resource->load() ) {
1748 GlobalReferenceCache().release( filename );
1751 NodeSmartReference clone( NewMapRoot( "" ) );
1752 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1753 Map_gatherNamespaced( clone );
1754 Map_mergeClonedNames();
1757 GlobalReferenceCache().release( filename );
1760 SceneChangeNotify();
1769 bool Map_SaveFile( const char* filename ){
1770 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1771 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1779 // Saves selected world brushes and whole entities with partial/full selections
1781 bool Map_SaveSelected( const char* filename ){
1782 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1785 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1787 scene::Node& m_parent;
1788 mutable bool m_emptyOldParent;
1791 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ), m_emptyOldParent( false ){
1793 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1794 if ( path.top().get_pointer() != &m_parent && ( Node_isPrimitive( path.top() ) || m_emptyOldParent ) ) {
1795 Selectable* selectable = Instance_getSelectable( instance );
1796 if ( selectable && selectable->isSelected() && path.size() > 1 ) {
1802 void post( const scene::Path& path, scene::Instance& instance ) const {
1803 if ( path.top().get_pointer() == &m_parent )
1806 if ( Node_isPrimitive( path.top() ) ){
1807 m_emptyOldParent = false;
1808 Selectable* selectable = Instance_getSelectable( instance );
1810 if ( selectable && selectable->isSelected() && path.size() > 1 ){
1811 scene::Node& parent = path.parent();
1812 if ( &parent != &m_parent ){
1813 NodeSmartReference node( path.top().get() );
1814 scene::Traversable* traversable_parent = Node_getTraversable( parent );
1815 traversable_parent->erase( node );
1816 Node_getTraversable( m_parent )->insert( node );
1817 if ( traversable_parent->empty() )
1818 m_emptyOldParent = true;
1822 else if ( m_emptyOldParent ){
1823 m_emptyOldParent = false;
1824 // delete empty entities
1825 Entity* entity = Node_getEntity( path.top() );
1826 if ( entity != 0 && path.top().get_pointer() != Map_FindWorldspawn( g_map ) && Node_getTraversable( path.top() )->empty() ) {
1827 Path_deleteTop( path );
1833 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1834 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1837 class CountSelectedBrushes : public scene::Graph::Walker
1839 std::size_t& m_count;
1840 mutable std::size_t m_depth;
1842 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1845 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1846 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1849 Selectable* selectable = Instance_getSelectable( instance );
1850 if ( selectable != 0
1851 && selectable->isSelected()
1852 && Node_isPrimitive( path.top() ) ) {
1857 void post( const scene::Path& path, scene::Instance& instance ) const {
1862 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1864 graph.traverse( CountSelectedBrushes( count ) );
1876 const char* nodetype_get_name( ENodeType type ){
1877 if ( type == eNodeMap ) {
1880 if ( type == eNodeEntity ) {
1883 if ( type == eNodePrimitive ) {
1889 ENodeType node_get_nodetype( scene::Node& node ){
1890 if ( Node_isEntity( node ) ) {
1893 if ( Node_isPrimitive( node ) ) {
1894 return eNodePrimitive;
1896 return eNodeUnknown;
1899 bool contains_entity( scene::Node& node ){
1900 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1903 bool contains_primitive( scene::Node& node ){
1904 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1907 ENodeType node_get_contains( scene::Node& node ){
1908 if ( contains_entity( node ) ) {
1911 if ( contains_primitive( node ) ) {
1912 return eNodePrimitive;
1914 return eNodeUnknown;
1917 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1918 ENodeType contains = node_get_contains( parent.top() );
1919 ENodeType type = node_get_nodetype( child.top() );
1921 if ( contains != eNodeUnknown && contains == type ) {
1922 NodeSmartReference node( child.top().get() );
1923 Path_deleteTop( child );
1924 Node_getTraversable( parent.top() )->insert( node );
1925 SceneChangeNotify();
1929 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1933 void Scene_parentSelected(){
1934 UndoableCommand undo( "parentSelected" );
1936 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1937 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1939 const scene::Path& m_parent;
1941 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1943 void visit( scene::Instance& instance ) const {
1944 if ( &m_parent != &instance.path() ) {
1945 Path_parent( m_parent, instance.path() );
1950 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1951 GlobalSelectionSystem().foreachSelected( visitor );
1955 globalOutputStream() << "failed - did not find two selected nodes.\n";
1962 if ( ConfirmModified( "New Map" ) ) {
1969 CopiedString g_mapsPath;
1971 const char* getMapsPath(){
1972 return g_mapsPath.c_str();
1975 const char* map_open( const char* title ){
1976 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1979 const char* map_import( const char* title ){
1980 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1983 const char* map_save( const char* title ){
1984 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1988 if ( !ConfirmModified( "Open Map" ) ) {
1992 const char* filename = map_open( "Open Map" );
1994 if ( filename != 0 ) {
1995 MRU_AddFile( filename );
1998 Map_LoadFile( filename );
2003 const char* filename = map_import( "Import Map" );
2005 if ( filename != 0 ) {
2006 UndoableCommand undo( "mapImport" );
2007 Map_ImportFile( filename );
2012 const char* filename = map_save( "Save Map" );
2014 if ( filename != 0 ) {
2015 MRU_AddFile( filename );
2016 Map_Rename( filename );
2027 if ( Map_Unnamed( g_map ) ) {
2030 else if ( Map_Modified( g_map ) ) {
2032 MRU_AddFile( g_map.m_name.c_str() ); //add on saving, but not opening via cmd line: spoils the list
2037 const char* filename = map_save( "Export Selection" );
2039 if ( filename != 0 ) {
2040 Map_SaveSelected( filename );
2045 const char* filename = map_save( "Export Region" );
2047 if ( filename != 0 ) {
2048 Map_SaveRegion( filename );
2055 SceneChangeNotify();
2060 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
2061 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
2062 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
2063 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
2065 SceneChangeNotify();
2070 SceneChangeNotify();
2073 void RegionSelected(){
2074 Map_RegionSelectedBrushes();
2075 SceneChangeNotify();
2082 class BrushFindByIndexWalker : public scene::Traversable::Walker
2084 mutable std::size_t m_index;
2085 scene::Path& m_path;
2087 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
2088 : m_index( index ), m_path( path ){
2090 bool pre( scene::Node& node ) const {
2091 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
2092 m_path.push( makeReference( node ) );
2098 class EntityFindByIndexWalker : public scene::Traversable::Walker
2100 mutable std::size_t m_index;
2101 scene::Path& m_path;
2103 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
2104 : m_index( index ), m_path( path ){
2106 bool pre( scene::Node& node ) const {
2107 if ( Node_isEntity( node ) && m_index-- == 0 ) {
2108 m_path.push( makeReference( node ) );
2114 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
2115 path.push( makeReference( GlobalSceneGraph().root() ) );
2117 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
2119 if ( path.size() == 2 ) {
2120 scene::Traversable* traversable = Node_getTraversable( path.top() );
2121 if ( traversable != 0 ) {
2122 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
2127 inline bool Node_hasChildren( scene::Node& node ){
2128 scene::Traversable* traversable = Node_getTraversable( node );
2129 return traversable != 0 && !traversable->empty();
2132 void SelectBrush( int entitynum, int brushnum ){
2134 Scene_FindEntityBrush( entitynum, brushnum, path );
2135 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
2136 scene::Instance* instance = GlobalSceneGraph().find( path );
2137 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
2138 Selectable* selectable = Instance_getSelectable( *instance );
2139 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
2140 selectable->setSelected( true );
2141 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
2146 class BrushFindIndexWalker : public scene::Traversable::Walker
2148 mutable const scene::Node* m_node;
2149 std::size_t& m_count;
2151 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
2152 : m_node( &node ), m_count( count ){
2154 bool pre( scene::Node& node ) const {
2155 if ( Node_isPrimitive( node ) ) {
2156 if ( m_node == &node ) {
2167 class EntityFindIndexWalker : public scene::Traversable::Walker
2169 mutable const scene::Node* m_node;
2170 std::size_t& m_count;
2172 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
2173 : m_node( &node ), m_count( count ){
2175 bool pre( scene::Node& node ) const {
2176 if ( Node_isEntity( node ) ) {
2177 if ( m_node == &node ) {
2188 static void GetSelectionIndex( int *ent, int *brush ){
2189 std::size_t count_brush = 0;
2190 std::size_t count_entity = 0;
2191 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2192 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2195 scene::Traversable* traversable = Node_getTraversable( path.parent() );
2196 if ( traversable != 0 && path.size() == 3 ) {
2197 traversable->traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2202 scene::Traversable* traversable = Node_getTraversable( GlobalSceneGraph().root() );
2203 if ( traversable != 0 ) {
2204 if( path.size() == 3 ){
2205 traversable->traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2207 else if ( path.size() == 2 ){
2208 traversable->traverse( EntityFindIndexWalker( path.top(), count_entity ) );
2213 *brush = int(count_brush);
2214 *ent = int(count_entity);
2222 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2224 GtkAccelGroup* accel = gtk_accel_group_new();
2225 gtk_window_add_accel_group( window, accel );
2228 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2229 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2231 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2232 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2234 GtkWidget* label = gtk_label_new( "Entity number" );
2235 gtk_widget_show( label );
2236 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2237 (GtkAttachOptions) ( 0 ),
2238 (GtkAttachOptions) ( 0 ), 0, 0 );
2241 GtkWidget* label = gtk_label_new( "Brush number" );
2242 gtk_widget_show( label );
2243 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2244 (GtkAttachOptions) ( 0 ),
2245 (GtkAttachOptions) ( 0 ), 0, 0 );
2248 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2249 gtk_widget_show( GTK_WIDGET( entry ) );
2250 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2251 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2252 (GtkAttachOptions) ( 0 ), 0, 0 );
2253 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2257 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2258 gtk_widget_show( GTK_WIDGET( entry ) );
2259 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2260 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2261 (GtkAttachOptions) ( 0 ), 0, 0 );
2267 GtkHBox* hbox = create_dialog_hbox( 4 );
2268 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2270 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2271 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2272 widget_make_default( GTK_WIDGET( button ) );
2273 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2276 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2277 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2278 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2283 // Initialize dialog
2287 GetSelectionIndex( &ent, &br );
2288 sprintf( buf, "%i", ent );
2289 gtk_entry_set_text( entity, buf );
2290 sprintf( buf, "%i", br );
2291 gtk_entry_set_text( brush, buf );
2293 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2294 const char *entstr = gtk_entry_get_text( entity );
2295 const char *brushstr = gtk_entry_get_text( brush );
2296 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2299 gtk_widget_destroy( GTK_WIDGET( window ) );
2302 void Map_constructPreferences( PreferencesPage& page ){
2303 page.appendCheckBox( "", "Load last map at startup", g_bLoadLastMap );
2307 class MapEntityClasses : public ModuleObserver
2309 std::size_t m_unrealised;
2311 MapEntityClasses() : m_unrealised( 1 ){
2314 if ( --m_unrealised == 0 ) {
2315 if ( g_map.m_resource != 0 ) {
2316 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2317 g_map.m_resource->realise();
2322 if ( ++m_unrealised == 1 ) {
2323 if ( g_map.m_resource != 0 ) {
2324 g_map.m_resource->flush();
2325 g_map.m_resource->unrealise();
2331 MapEntityClasses g_MapEntityClasses;
2334 class MapModuleObserver : public ModuleObserver
2336 std::size_t m_unrealised;
2338 MapModuleObserver() : m_unrealised( 1 ){
2341 if ( --m_unrealised == 0 ) {
2342 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2343 StringOutputStream buffer( 256 );
2344 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2345 Q_mkdir( buffer.c_str() );
2346 g_mapsPath = buffer.c_str();
2350 if ( ++m_unrealised == 1 ) {
2356 MapModuleObserver g_MapModuleObserver;
2358 #include "preferencesystem.h"
2360 CopiedString g_strLastMap;
2361 bool g_bLoadLastMap = false;
2363 void Map_Construct(){
2364 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2365 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2366 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2367 //GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2368 GlobalToggles_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), ToggleItem::AddCallbackCaller( g_region_item ), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2370 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2371 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2372 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2374 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2376 GlobalEntityClassManager().attach( g_MapEntityClasses );
2377 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2381 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2382 GlobalEntityClassManager().detach( g_MapEntityClasses );