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 ) );
738 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
740 class EntityBreakdownWalker : public scene::Graph::Walker
742 EntityBreakdown& m_entitymap;
744 EntityBreakdownWalker( EntityBreakdown& entitymap )
745 : m_entitymap( entitymap ){
747 bool pre( const scene::Path& path, scene::Instance& instance ) const {
748 Entity* entity = Node_getEntity( path.top() );
750 const EntityClass& eclass = entity->getEntityClass();
751 if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
752 m_entitymap[eclass.name()] = 1;
754 else{ ++m_entitymap[eclass.name()]; }
760 void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
761 GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
764 class CountStuffWalker : public scene::Graph::Walker
769 int& m_groupents_ingame;
771 CountStuffWalker( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame )
772 : m_patches( patches ), m_ents_ingame( ents_ingame ), m_groupents( groupents ), m_groupents_ingame( groupents_ingame ){
774 bool pre( const scene::Path& path, scene::Instance& instance ) const {
775 Patch* patch = Node_getPatch( path.top() );
779 Entity* entity = Node_getEntity( path.top() );
781 if( entity->isContainer() ){
783 if( !string_equal_nocase( "func_group", entity->getKeyValue( "classname" ) ) &&
784 !string_equal_nocase( "_decal", entity->getKeyValue( "classname" ) ) ){
785 ++m_groupents_ingame;
790 if( !string_equal_nocase_n( "light", entity->getKeyValue( "classname" ), 5 ) &&
791 !string_equal_nocase( "misc_model", entity->getKeyValue( "classname" ) ) ){
799 void Scene_CountStuff( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame ){
800 GlobalSceneGraph().traverse( CountStuffWalker( patches, ents_ingame, groupents, groupents_ingame ) );
803 WindowPosition g_posMapInfoWnd( c_default_window_pos );
808 GtkWidget* w_brushes;
809 GtkWidget* w_patches;
811 GtkWidget* w_ents_ingame;
812 GtkWidget* w_groupents;
813 GtkWidget* w_groupents_ingame;
815 GtkListStore* EntityBreakdownWalker;
817 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Map Info", G_CALLBACK( dialog_delete_callback ), &dialog );
819 window_set_position( window, g_posMapInfoWnd );
822 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
823 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
826 GtkHBox* hbox = create_dialog_hbox( 4 );
827 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, FALSE, 0 );
830 GtkTable* table = create_dialog_table( 3, 4, 4, 4 );
831 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
833 GtkWidget* label = gtk_label_new( "Total Brushes:" );
834 gtk_widget_show( label );
835 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
836 (GtkAttachOptions) ( GTK_FILL ),
837 (GtkAttachOptions) ( 0 ), 0, 0 );
838 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
841 GtkWidget* label = gtk_label_new( "" );
842 gtk_widget_show( label );
843 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 0, 1,
844 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
845 (GtkAttachOptions) ( 0 ), 3, 0 );
846 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
850 GtkWidget* label = gtk_label_new( "Total Patches:" );
851 gtk_widget_show( label );
852 gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 0, 1,
853 (GtkAttachOptions) ( GTK_FILL ),
854 (GtkAttachOptions) ( 0 ), 0, 0 );
855 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
858 GtkWidget* label = gtk_label_new( "" );
859 gtk_widget_show( label );
860 gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 0, 1,
861 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
862 (GtkAttachOptions) ( 0 ), 3, 0 );
863 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
867 GtkWidget* label = gtk_label_new( "Total Entities:" );
868 gtk_widget_show( label );
869 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
870 (GtkAttachOptions) ( GTK_FILL ),
871 (GtkAttachOptions) ( 0 ), 0, 0 );
872 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
875 GtkWidget* label = gtk_label_new( "" );
876 gtk_widget_show( label );
877 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 1, 2,
878 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
879 (GtkAttachOptions) ( 0 ), 3, 0 );
880 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
884 GtkWidget* label = gtk_label_new( "Ingame Entities:" );
885 gtk_widget_show( label );
886 gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 1, 2,
887 (GtkAttachOptions) ( GTK_FILL ),
888 (GtkAttachOptions) ( 0 ), 0, 0 );
889 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
892 GtkWidget* label = gtk_label_new( "" );
893 gtk_widget_show( label );
894 gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 1, 2,
895 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
896 (GtkAttachOptions) ( 0 ), 3, 0 );
897 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
898 w_ents_ingame = label;
901 GtkWidget* label = gtk_label_new( "Group Entities:" );
902 gtk_widget_show( label );
903 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 2, 3,
904 (GtkAttachOptions) ( GTK_FILL ),
905 (GtkAttachOptions) ( 0 ), 0, 0 );
906 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
909 GtkWidget* label = gtk_label_new( "" );
910 gtk_widget_show( label );
911 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 2, 3,
912 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
913 (GtkAttachOptions) ( 0 ), 3, 0 );
914 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
918 GtkWidget* label = gtk_label_new( "Ingame Group Entities:" );
919 gtk_widget_show( label );
920 gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 2, 3,
921 (GtkAttachOptions) ( GTK_FILL ),
922 (GtkAttachOptions) ( 0 ), 0, 0 );
923 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
926 GtkWidget* label = gtk_label_new( "" );
927 gtk_widget_show( label );
928 gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 2, 3,
929 (GtkAttachOptions) ( GTK_FILL | GTK_EXPAND ),
930 (GtkAttachOptions) ( 0 ), 3, 0 );
931 gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
932 w_groupents_ingame = label;
937 GtkVBox* vbox2 = create_dialog_vbox( 4 );
938 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
941 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
942 gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
947 GtkWidget* label = gtk_label_new( "*** Entity breakdown ***" );
948 gtk_widget_show( label );
949 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
950 gtk_misc_set_alignment( GTK_MISC( label ), 0.5, 0.5 );
953 GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
954 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
957 GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_UINT );
959 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
960 gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
963 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
964 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Entity", renderer, "text", 0, 0 );
965 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
966 gtk_tree_view_column_set_sort_column_id( column, 0 );
970 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
971 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Count", renderer, "text", 1, 0 );
972 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
973 gtk_tree_view_column_set_sort_column_id( column, 1 );
976 gtk_widget_show( view );
978 gtk_container_add( GTK_CONTAINER( scr ), view );
980 EntityBreakdownWalker = store;
988 EntityBreakdown entitymap;
989 Scene_EntityBreakdown( entitymap );
991 for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
994 gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
995 gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, Unsigned( ( *i ).second ), -1 );
999 g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
1002 int n_ents_ingame = 0;
1003 int n_groupents = 0;
1004 int n_groupents_ingame = 0;
1005 Scene_CountStuff( n_patches, n_ents_ingame, n_groupents, n_groupents_ingame );
1006 //globalOutputStream() << n_patches << n_ents_ingame << n_groupents << n_groupents_ingame << "\n";
1010 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%u</b></span> ", Unsigned( g_brushCount.get() ) );
1011 gtk_label_set_markup( GTK_LABEL( w_brushes ), markup );
1014 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span> ", n_patches );
1015 gtk_label_set_markup( GTK_LABEL( w_patches ), markup );
1018 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%u</b></span> ", Unsigned( g_entityCount.get() ) );
1019 gtk_label_set_markup( GTK_LABEL( w_ents ), markup );
1022 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span> ", n_ents_ingame );
1023 gtk_label_set_markup( GTK_LABEL( w_ents_ingame ), markup );
1026 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span> ", n_groupents );
1027 gtk_label_set_markup( GTK_LABEL( w_groupents ), markup );
1030 markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span> ", n_groupents_ingame );
1031 gtk_label_set_markup( GTK_LABEL( w_groupents_ingame ), markup );
1034 modal_dialog_show( window, dialog );
1037 window_get_position( window, g_posMapInfoWnd );
1039 gtk_widget_destroy( GTK_WIDGET( window ) );
1047 const char* m_message;
1049 ScopeTimer( const char* message )
1050 : m_message( message ){
1054 double elapsed_time = m_timer.elapsed_msec() / 1000.f;
1055 globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
1065 void Map_LoadFile( const char *filename ){
1066 globalOutputStream() << "Loading map from " << filename << "\n";
1067 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1070 ScopeTimer timer( "map load" );
1072 const MapFormat* format = NULL;
1073 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1074 if ( string_not_empty( moduleName ) ) {
1075 format = ReferenceAPI_getMapModules().findModule( moduleName );
1078 for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
1083 Brush_toggleFormat( i );
1084 g_map.m_name = filename;
1085 Map_UpdateTitle( g_map );
1086 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1088 format->wrongFormat = false;
1090 g_map.m_resource->attach( g_map );
1092 if ( !format->wrongFormat ) {
1098 Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
1101 globalOutputStream() << "--- LoadMapFile ---\n";
1102 globalOutputStream() << g_map.m_name.c_str() << "\n";
1104 globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
1105 globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
1107 //GlobalEntityCreator().printStatistics();
1110 // move the view to a start position
1112 Map_StartPosition();
1114 g_currentMap = &g_map;
1120 virtual bool excluded( scene::Node& node ) const = 0;
1123 class ExcludeWalker : public scene::Traversable::Walker
1125 const scene::Traversable::Walker& m_walker;
1126 const Excluder* m_exclude;
1127 mutable bool m_skip;
1129 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
1130 : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
1132 bool pre( scene::Node& node ) const {
1133 if ( m_exclude->excluded( node ) || node.isRoot() ) {
1139 m_walker.pre( node );
1143 void post( scene::Node& node ) const {
1149 m_walker.post( node );
1154 class AnyInstanceSelected : public scene::Instantiable::Visitor
1158 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
1161 void visit( scene::Instance& instance ) const {
1162 Selectable* selectable = Instance_getSelectable( instance );
1163 if ( selectable != 0
1164 && selectable->isSelected() ) {
1170 bool Node_instanceSelected( scene::Node& node ){
1171 scene::Instantiable* instantiable = Node_getInstantiable( node );
1172 ASSERT_NOTNULL( instantiable );
1174 instantiable->forEachInstance( AnyInstanceSelected( selected ) );
1178 class SelectedDescendantWalker : public scene::Traversable::Walker
1182 SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
1186 bool pre( scene::Node& node ) const {
1187 if ( node.isRoot() ) {
1191 if ( Node_instanceSelected( node ) ) {
1199 bool Node_selectedDescendant( scene::Node& node ){
1201 Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
1205 class SelectionExcluder : public Excluder
1208 bool excluded( scene::Node& node ) const {
1209 return !Node_selectedDescendant( node );
1213 class IncludeSelectedWalker : public scene::Traversable::Walker
1215 const scene::Traversable::Walker& m_walker;
1216 mutable std::size_t m_selected;
1217 mutable bool m_skip;
1219 bool selectedParent() const {
1220 return m_selected != 0;
1223 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
1224 : m_walker( walker ), m_selected( 0 ), m_skip( false ){
1226 bool pre( scene::Node& node ) const {
1228 // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
1229 if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
1230 if ( Node_instanceSelected( node ) ) {
1233 m_walker.pre( node );
1242 void post( scene::Node& node ) const {
1248 if ( Node_instanceSelected( node ) ) {
1251 m_walker.post( node );
1256 void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
1257 scene::Traversable* traversable = Node_getTraversable( root );
1258 if ( traversable != 0 ) {
1260 traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
1262 traversable->traverse( IncludeSelectedWalker( walker ) );
1267 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
1268 format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
1271 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
1272 scene::Traversable* traversable = Node_getTraversable( root );
1273 if ( traversable != 0 ) {
1274 traversable->traverse( walker );
1278 class RegionExcluder : public Excluder
1281 bool excluded( scene::Node& node ) const {
1282 return node.excluded();
1286 void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
1287 scene::Traversable* traversable = Node_getTraversable( root );
1288 if ( traversable != 0 ) {
1289 traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
1293 bool Map_SaveRegion( const char *filename ){
1296 bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
1298 RemoveRegionBrushes();
1304 void Map_RenameAbsolute( const char* absolute ){
1305 Resource* resource = GlobalReferenceCache().capture( absolute );
1306 NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
1307 resource->setNode( clone.get_pointer() );
1310 //ScopeTimer timer("clone subgraph");
1311 Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
1314 g_map.m_resource->detach( g_map );
1315 GlobalReferenceCache().release( g_map.m_name.c_str() );
1317 g_map.m_resource = resource;
1319 g_map.m_name = absolute;
1320 Map_UpdateTitle( g_map );
1322 g_map.m_resource->attach( g_map );
1325 void Map_Rename( const char* filename ){
1326 if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
1327 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1329 Map_RenameAbsolute( filename );
1331 SceneChangeNotify();
1342 ScopeTimer timer( "map save" );
1344 return true; // assume success..
1354 //globalOutputStream() << "Map_New\n";
1356 g_map.m_name = "unnamed.map";
1357 Map_UpdateTitle( g_map );
1360 g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
1361 // ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
1362 g_map.m_resource->attach( g_map );
1364 SceneChangeNotify();
1367 FocusViews( g_vector3_identity, 0 );
1369 g_currentMap = &g_map;
1372 extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
1374 void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
1376 \todo we need to make sure that the player start IS inside the region and bail out if it's not
1377 the compiler will refuse to compile a map with a player_start somewhere in empty space..
1378 for now, let's just print an error
1381 Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
1383 for ( int i = 0 ; i < 3 ; i++ )
1385 if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
1386 globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
1391 // write the info_playerstart
1393 sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
1394 Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
1395 sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
1396 Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
1400 ===========================================================
1404 ===========================================================
1406 bool region_active = false;
1408 BoolExportCaller g_region_caller( region_active );
1409 ToggleItem g_region_item( g_region_caller );
1411 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
1412 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
1414 scene::Node* region_sides[6];
1415 scene::Node* region_startpoint = 0;
1420 a regioned map will have temp walls put up at the region boundary
1421 \todo TODO TTimo old implementation of region brushes
1422 we still add them straight in the worldspawn and take them out after the map is saved
1423 with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
1426 void AddRegionBrushes( void ){
1429 for ( i = 0; i < 6; i++ )
1431 region_sides[i] = &GlobalBrushCreator().createBrush();
1432 Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
1435 region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
1437 ConstructRegionBrushes( region_sides, region_mins, region_maxs );
1438 ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
1440 Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
1443 void RemoveRegionBrushes( void ){
1444 for ( std::size_t i = 0; i < 6; i++ )
1446 Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
1448 Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
1451 inline void exclude_node( scene::Node& node, bool exclude ){
1453 ? node.enable( scene::Node::eExcluded )
1454 : node.disable( scene::Node::eExcluded );
1457 class ExcludeAllWalker : public scene::Graph::Walker
1461 ExcludeAllWalker( bool exclude )
1462 : m_exclude( exclude ){
1464 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1465 exclude_node( path.top(), m_exclude );
1471 void Scene_Exclude_All( bool exclude ){
1472 GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
1475 bool Instance_isSelected( const scene::Instance& instance ){
1476 const Selectable* selectable = Instance_getSelectable( instance );
1477 return selectable != 0 && selectable->isSelected();
1480 class ExcludeSelectedWalker : public scene::Graph::Walker
1484 ExcludeSelectedWalker( bool exclude )
1485 : m_exclude( exclude ){
1487 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1488 exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
1493 void Scene_Exclude_Selected( bool exclude ){
1494 GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
1497 class ExcludeRegionedWalker : public scene::Graph::Walker
1501 ExcludeRegionedWalker( bool exclude )
1502 : m_exclude( exclude ){
1504 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1509 aabb_intersects_aabb(
1510 instance.worldAABB(),
1511 aabb_for_minmax( region_mins, region_maxs )
1521 void Scene_Exclude_Region( bool exclude ){
1522 GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
1529 Other filtering options may still be on
1532 void Map_RegionOff(){
1533 region_active = false;
1534 g_region_item.update();
1536 region_maxs[0] = g_MaxWorldCoord - 64;
1537 region_mins[0] = g_MinWorldCoord + 64;
1538 region_maxs[1] = g_MaxWorldCoord - 64;
1539 region_mins[1] = g_MinWorldCoord + 64;
1540 region_maxs[2] = g_MaxWorldCoord - 64;
1541 region_mins[2] = g_MinWorldCoord + 64;
1543 Scene_Exclude_All( false );
1546 void Map_ApplyRegion( void ){
1547 region_active = true;
1548 g_region_item.update();
1550 Scene_Exclude_Region( false );
1555 ========================
1556 Map_RegionSelectedBrushes
1557 ========================
1559 void Map_RegionSelectedBrushes( void ){
1562 if ( GlobalSelectionSystem().countSelected() != 0
1563 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1564 region_active = true;
1565 g_region_item.update();
1566 Select_GetBounds( region_mins, region_maxs );
1568 Scene_Exclude_Selected( false );
1570 GlobalSelectionSystem().setSelectedAll( false );
1580 void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
1583 region_mins[0] = x_min;
1584 region_maxs[0] = x_max;
1585 region_mins[1] = y_min;
1586 region_maxs[1] = y_max;
1587 region_mins[2] = g_MinWorldCoord + 64;
1588 region_maxs[2] = g_MaxWorldCoord - 64;
1593 void Map_RegionBounds( const AABB& bounds ){
1596 region_mins = vector3_subtracted( bounds.origin, bounds.extents );
1597 region_maxs = vector3_added( bounds.origin, bounds.extents );
1609 void Map_RegionBrush( void ){
1610 if ( GlobalSelectionSystem().countSelected() != 0 ) {
1611 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1612 Map_RegionBounds( instance.worldAABB() );
1621 bool Map_ImportFile( const char* filename ){
1622 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
1624 bool success = false;
1626 if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
1631 const MapFormat* format = NULL;
1632 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
1633 if ( string_not_empty( moduleName ) ) {
1634 format = ReferenceAPI_getMapModules().findModule( moduleName );
1638 format->wrongFormat = false;
1640 Resource* resource = GlobalReferenceCache().capture( filename );
1641 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1642 if ( !resource->load() ) {
1643 GlobalReferenceCache().release( filename );
1647 if ( format->wrongFormat ) {
1648 GlobalReferenceCache().release( filename );
1652 NodeSmartReference clone( NewMapRoot( "" ) );
1653 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1654 Map_gatherNamespaced( clone );
1655 Map_mergeClonedNames();
1658 GlobalReferenceCache().release( filename );
1661 SceneChangeNotify();
1667 const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" );
1668 int n = string_length( path_get_extension( filename ) );
1669 if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
1670 StringBuffer output;
1671 output.push_string( AppPath_get() );
1672 output.push_string( "q3map2." );
1673 output.push_string( RADIANT_EXECUTABLE );
1674 output.push_string( " -v -game " );
1675 output.push_string( ( type && *type ) ? type : "quake3" );
1676 output.push_string( " -fs_basepath \"" );
1677 output.push_string( EnginePath_get() );
1678 output.push_string( "\" -fs_homepath \"" );
1679 output.push_string( g_qeglobals.m_userEnginePath.c_str() );
1680 output.push_string( "\" -fs_game " );
1681 output.push_string( gamename_get() );
1682 output.push_string( " -convert -format " );
1683 output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
1684 if ( extension_equal( path_get_extension( filename ), "map" ) ) {
1685 output.push_string( " -readmap " );
1687 output.push_string( " \"" );
1688 output.push_string( filename );
1689 output.push_string( "\"" );
1692 Q_Exec( NULL, output.c_str(), NULL, false, true );
1694 // rebuild filename as "filenamewithoutext_converted.map"
1696 output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
1697 output.push_string( "_converted.map" );
1698 filename = output.c_str();
1701 Resource* resource = GlobalReferenceCache().capture( filename );
1702 resource->refresh(); // avoid loading old version if map has changed on disk since last import
1703 if ( !resource->load() ) {
1704 GlobalReferenceCache().release( filename );
1707 NodeSmartReference clone( NewMapRoot( "" ) );
1708 Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
1709 Map_gatherNamespaced( clone );
1710 Map_mergeClonedNames();
1713 GlobalReferenceCache().release( filename );
1716 SceneChangeNotify();
1725 bool Map_SaveFile( const char* filename ){
1726 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
1727 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
1735 // Saves selected world brushes and whole entities with partial/full selections
1737 bool Map_SaveSelected( const char* filename ){
1738 return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
1741 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
1743 scene::Node& m_parent;
1744 mutable bool m_emptyOldParent;
1747 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ), m_emptyOldParent( false ){
1749 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1750 if ( path.top().get_pointer() != &m_parent && ( Node_isPrimitive( path.top() ) || m_emptyOldParent ) ) {
1751 Selectable* selectable = Instance_getSelectable( instance );
1752 if ( selectable && selectable->isSelected() && path.size() > 1 ) {
1758 void post( const scene::Path& path, scene::Instance& instance ) const {
1759 if ( path.top().get_pointer() == &m_parent )
1762 if ( Node_isPrimitive( path.top() ) ){
1763 m_emptyOldParent = false;
1764 Selectable* selectable = Instance_getSelectable( instance );
1766 if ( selectable && selectable->isSelected() && path.size() > 1 ){
1767 scene::Node& parent = path.parent();
1768 if ( &parent != &m_parent ){
1769 NodeSmartReference node( path.top().get() );
1770 scene::Traversable* traversable_parent = Node_getTraversable( parent );
1771 traversable_parent->erase( node );
1772 Node_getTraversable( m_parent )->insert( node );
1773 if ( traversable_parent->empty() )
1774 m_emptyOldParent = true;
1778 else if ( m_emptyOldParent ){
1779 m_emptyOldParent = false;
1780 // delete empty entities
1781 Entity* entity = Node_getEntity( path.top() );
1782 if ( entity != 0 && path.top().get_pointer() != Map_FindWorldspawn( g_map ) && Node_getTraversable( path.top() )->empty() ) {
1783 Path_deleteTop( path );
1789 void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
1790 graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
1793 class CountSelectedBrushes : public scene::Graph::Walker
1795 std::size_t& m_count;
1796 mutable std::size_t m_depth;
1798 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
1801 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1802 if ( ++m_depth != 1 && path.top().get().isRoot() ) {
1805 Selectable* selectable = Instance_getSelectable( instance );
1806 if ( selectable != 0
1807 && selectable->isSelected()
1808 && Node_isPrimitive( path.top() ) ) {
1813 void post( const scene::Path& path, scene::Instance& instance ) const {
1818 std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
1820 graph.traverse( CountSelectedBrushes( count ) );
1832 const char* nodetype_get_name( ENodeType type ){
1833 if ( type == eNodeMap ) {
1836 if ( type == eNodeEntity ) {
1839 if ( type == eNodePrimitive ) {
1845 ENodeType node_get_nodetype( scene::Node& node ){
1846 if ( Node_isEntity( node ) ) {
1849 if ( Node_isPrimitive( node ) ) {
1850 return eNodePrimitive;
1852 return eNodeUnknown;
1855 bool contains_entity( scene::Node& node ){
1856 return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
1859 bool contains_primitive( scene::Node& node ){
1860 return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
1863 ENodeType node_get_contains( scene::Node& node ){
1864 if ( contains_entity( node ) ) {
1867 if ( contains_primitive( node ) ) {
1868 return eNodePrimitive;
1870 return eNodeUnknown;
1873 void Path_parent( const scene::Path& parent, const scene::Path& child ){
1874 ENodeType contains = node_get_contains( parent.top() );
1875 ENodeType type = node_get_nodetype( child.top() );
1877 if ( contains != eNodeUnknown && contains == type ) {
1878 NodeSmartReference node( child.top().get() );
1879 Path_deleteTop( child );
1880 Node_getTraversable( parent.top() )->insert( node );
1881 SceneChangeNotify();
1885 globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
1889 void Scene_parentSelected(){
1890 UndoableCommand undo( "parentSelected" );
1892 if ( GlobalSelectionSystem().countSelected() > 1 ) {
1893 class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
1895 const scene::Path& m_parent;
1897 ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
1899 void visit( scene::Instance& instance ) const {
1900 if ( &m_parent != &instance.path() ) {
1901 Path_parent( m_parent, instance.path() );
1906 ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
1907 GlobalSelectionSystem().foreachSelected( visitor );
1911 globalOutputStream() << "failed - did not find two selected nodes.\n";
1918 if ( ConfirmModified( "New Map" ) ) {
1925 CopiedString g_mapsPath;
1927 const char* getMapsPath(){
1928 return g_mapsPath.c_str();
1931 const char* map_open( const char* title ){
1932 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
1935 const char* map_import( const char* title ){
1936 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
1939 const char* map_save( const char* title ){
1940 return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
1944 if ( !ConfirmModified( "Open Map" ) ) {
1948 const char* filename = map_open( "Open Map" );
1950 if ( filename != 0 ) {
1951 MRU_AddFile( filename );
1954 Map_LoadFile( filename );
1959 const char* filename = map_import( "Import Map" );
1961 if ( filename != 0 ) {
1962 UndoableCommand undo( "mapImport" );
1963 Map_ImportFile( filename );
1968 const char* filename = map_save( "Save Map" );
1970 if ( filename != 0 ) {
1971 MRU_AddFile( filename );
1972 Map_Rename( filename );
1983 if ( Map_Unnamed( g_map ) ) {
1986 else if ( Map_Modified( g_map ) ) {
1988 MRU_AddFile( g_map.m_name.c_str() ); //add on saving, but not opening via cmd line: spoils the list
1993 const char* filename = map_save( "Export Selection" );
1995 if ( filename != 0 ) {
1996 Map_SaveSelected( filename );
2001 const char* filename = map_save( "Export Region" );
2003 if ( filename != 0 ) {
2004 Map_SaveRegion( filename );
2011 SceneChangeNotify();
2016 g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
2017 g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
2018 g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
2019 g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
2021 SceneChangeNotify();
2026 SceneChangeNotify();
2029 void RegionSelected(){
2030 Map_RegionSelectedBrushes();
2031 SceneChangeNotify();
2038 class BrushFindByIndexWalker : public scene::Traversable::Walker
2040 mutable std::size_t m_index;
2041 scene::Path& m_path;
2043 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
2044 : m_index( index ), m_path( path ){
2046 bool pre( scene::Node& node ) const {
2047 if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
2048 m_path.push( makeReference( node ) );
2054 class EntityFindByIndexWalker : public scene::Traversable::Walker
2056 mutable std::size_t m_index;
2057 scene::Path& m_path;
2059 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
2060 : m_index( index ), m_path( path ){
2062 bool pre( scene::Node& node ) const {
2063 if ( Node_isEntity( node ) && m_index-- == 0 ) {
2064 m_path.push( makeReference( node ) );
2070 void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
2071 path.push( makeReference( GlobalSceneGraph().root() ) );
2073 Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
2075 if ( path.size() == 2 ) {
2076 scene::Traversable* traversable = Node_getTraversable( path.top() );
2077 if ( traversable != 0 ) {
2078 traversable->traverse( BrushFindByIndexWalker( brush, path ) );
2083 inline bool Node_hasChildren( scene::Node& node ){
2084 scene::Traversable* traversable = Node_getTraversable( node );
2085 return traversable != 0 && !traversable->empty();
2088 void SelectBrush( int entitynum, int brushnum ){
2090 Scene_FindEntityBrush( entitynum, brushnum, path );
2091 if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
2092 scene::Instance* instance = GlobalSceneGraph().find( path );
2093 ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
2094 Selectable* selectable = Instance_getSelectable( *instance );
2095 ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
2096 selectable->setSelected( true );
2097 g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
2102 class BrushFindIndexWalker : public scene::Graph::Walker
2104 mutable const scene::Node* m_node;
2105 std::size_t& m_count;
2107 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
2108 : m_node( &node ), m_count( count ){
2110 bool pre( const scene::Path& path, scene::Instance& instance ) const {
2111 if ( Node_isPrimitive( path.top() ) ) {
2112 if ( m_node == path.top().get_pointer() ) {
2123 class EntityFindIndexWalker : public scene::Graph::Walker
2125 mutable const scene::Node* m_node;
2126 std::size_t& m_count;
2128 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
2129 : m_node( &node ), m_count( count ){
2131 bool pre( const scene::Path& path, scene::Instance& instance ) const {
2132 if ( Node_isEntity( path.top() ) ) {
2133 if ( m_node == path.top().get_pointer() ) {
2144 static void GetSelectionIndex( int *ent, int *brush ){
2145 std::size_t count_brush = 0;
2146 std::size_t count_entity = 0;
2147 if ( GlobalSelectionSystem().countSelected() != 0 ) {
2148 const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
2150 GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
2151 GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
2153 *brush = int(count_brush);
2154 *ent = int(count_entity);
2162 GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
2164 GtkAccelGroup* accel = gtk_accel_group_new();
2165 gtk_window_add_accel_group( window, accel );
2168 GtkVBox* vbox = create_dialog_vbox( 4, 4 );
2169 gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
2171 GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
2172 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
2174 GtkWidget* label = gtk_label_new( "Entity number" );
2175 gtk_widget_show( label );
2176 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2177 (GtkAttachOptions) ( 0 ),
2178 (GtkAttachOptions) ( 0 ), 0, 0 );
2181 GtkWidget* label = gtk_label_new( "Brush number" );
2182 gtk_widget_show( label );
2183 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2184 (GtkAttachOptions) ( 0 ),
2185 (GtkAttachOptions) ( 0 ), 0, 0 );
2188 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2189 gtk_widget_show( GTK_WIDGET( entry ) );
2190 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
2191 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2192 (GtkAttachOptions) ( 0 ), 0, 0 );
2193 gtk_widget_grab_focus( GTK_WIDGET( entry ) );
2197 GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
2198 gtk_widget_show( GTK_WIDGET( entry ) );
2199 gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
2200 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2201 (GtkAttachOptions) ( 0 ), 0, 0 );
2207 GtkHBox* hbox = create_dialog_hbox( 4 );
2208 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
2210 GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
2211 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2212 widget_make_default( GTK_WIDGET( button ) );
2213 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
2216 GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
2217 gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
2218 gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
2223 // Initialize dialog
2227 GetSelectionIndex( &ent, &br );
2228 sprintf( buf, "%i", ent );
2229 gtk_entry_set_text( entity, buf );
2230 sprintf( buf, "%i", br );
2231 gtk_entry_set_text( brush, buf );
2233 if ( modal_dialog_show( window, dialog ) == eIDOK ) {
2234 const char *entstr = gtk_entry_get_text( entity );
2235 const char *brushstr = gtk_entry_get_text( brush );
2236 SelectBrush( atoi( entstr ), atoi( brushstr ) );
2239 gtk_widget_destroy( GTK_WIDGET( window ) );
2242 void Map_constructPreferences( PreferencesPage& page ){
2243 page.appendCheckBox( "", "Load last map at startup", g_bLoadLastMap );
2247 class MapEntityClasses : public ModuleObserver
2249 std::size_t m_unrealised;
2251 MapEntityClasses() : m_unrealised( 1 ){
2254 if ( --m_unrealised == 0 ) {
2255 if ( g_map.m_resource != 0 ) {
2256 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
2257 g_map.m_resource->realise();
2262 if ( ++m_unrealised == 1 ) {
2263 if ( g_map.m_resource != 0 ) {
2264 g_map.m_resource->flush();
2265 g_map.m_resource->unrealise();
2271 MapEntityClasses g_MapEntityClasses;
2274 class MapModuleObserver : public ModuleObserver
2276 std::size_t m_unrealised;
2278 MapModuleObserver() : m_unrealised( 1 ){
2281 if ( --m_unrealised == 0 ) {
2282 ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
2283 StringOutputStream buffer( 256 );
2284 buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
2285 Q_mkdir( buffer.c_str() );
2286 g_mapsPath = buffer.c_str();
2290 if ( ++m_unrealised == 1 ) {
2296 MapModuleObserver g_MapModuleObserver;
2298 #include "preferencesystem.h"
2300 CopiedString g_strLastMap;
2301 bool g_bLoadLastMap = false;
2303 void Map_Construct(){
2304 GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
2305 GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
2306 GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
2307 //GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2308 GlobalToggles_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), ToggleItem::AddCallbackCaller( g_region_item ), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
2310 GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
2311 GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
2312 GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
2314 PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
2316 GlobalEntityClassManager().attach( g_MapEntityClasses );
2317 Radiant_attachHomePathsObserver( g_MapModuleObserver );
2321 Radiant_detachHomePathsObserver( g_MapModuleObserver );
2322 GlobalEntityClassManager().detach( g_MapEntityClasses );