+ Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
+ }
+
+ globalOutputStream() << "--- LoadMapFile ---\n";
+ globalOutputStream() << g_map.m_name.c_str() << "\n";
+
+ globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
+ globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
+
+ //GlobalEntityCreator().printStatistics();
+
+ //
+ // move the view to a start position
+ //
+ Map_StartPosition();
+
+ g_currentMap = &g_map;
+}
+
+class Excluder
+{
+public:
+virtual bool excluded( scene::Node& node ) const = 0;
+};
+
+class ExcludeWalker : public scene::Traversable::Walker
+{
+const scene::Traversable::Walker& m_walker;
+const Excluder* m_exclude;
+mutable bool m_skip;
+public:
+ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
+ : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
+}
+bool pre( scene::Node& node ) const {
+ if ( m_exclude->excluded( node ) || node.isRoot() ) {
+ m_skip = true;
+ return false;
+ }
+ else
+ {
+ m_walker.pre( node );
+ }
+ return true;
+}
+void post( scene::Node& node ) const {
+ if ( m_skip ) {
+ m_skip = false;
+ }
+ else
+ {
+ m_walker.post( node );
+ }
+}
+};
+
+class AnyInstanceSelected : public scene::Instantiable::Visitor
+{
+bool& m_selected;
+public:
+AnyInstanceSelected( bool& selected ) : m_selected( selected ){
+ m_selected = false;
+}
+void visit( scene::Instance& instance ) const {
+ Selectable* selectable = Instance_getSelectable( instance );
+ if ( selectable != 0
+ && selectable->isSelected() ) {
+ m_selected = true;
+ }
+}
+};
+
+bool Node_instanceSelected( scene::Node& node ){
+ scene::Instantiable* instantiable = Node_getInstantiable( node );
+ ASSERT_NOTNULL( instantiable );
+ bool selected;
+ instantiable->forEachInstance( AnyInstanceSelected( selected ) );
+ return selected;
+}
+
+class SelectedDescendantWalker : public scene::Traversable::Walker
+{
+bool& m_selected;
+public:
+SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
+ m_selected = false;
+}
+
+bool pre( scene::Node& node ) const {
+ if ( node.isRoot() ) {
+ return false;
+ }
+
+ if ( Node_instanceSelected( node ) ) {
+ m_selected = true;
+ }
+
+ return true;
+}
+};
+
+bool Node_selectedDescendant( scene::Node& node ){
+ bool selected;
+ Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
+ return selected;
+}
+
+class SelectionExcluder : public Excluder
+{
+public:
+bool excluded( scene::Node& node ) const {
+ return !Node_selectedDescendant( node );
+}
+};
+
+class IncludeSelectedWalker : public scene::Traversable::Walker
+{
+const scene::Traversable::Walker& m_walker;
+mutable std::size_t m_selected;
+mutable bool m_skip;
+
+bool selectedParent() const {
+ return m_selected != 0;
+}
+public:
+IncludeSelectedWalker( const scene::Traversable::Walker& walker )
+ : m_walker( walker ), m_selected( 0 ), m_skip( false ){
+}
+bool pre( scene::Node& node ) const {
+ // include node if:
+ // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
+ if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
+ if ( Node_instanceSelected( node ) ) {
+ ++m_selected;
+ }
+ m_walker.pre( node );
+ return true;
+ }
+ else
+ {
+ m_skip = true;
+ return false;
+ }
+}
+void post( scene::Node& node ) const {
+ if ( m_skip ) {
+ m_skip = false;
+ }
+ else
+ {
+ if ( Node_instanceSelected( node ) ) {
+ --m_selected;
+ }
+ m_walker.post( node );
+ }
+}
+};
+
+void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
+ scene::Traversable* traversable = Node_getTraversable( root );
+ if ( traversable != 0 ) {
+#if 0
+ traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
+#else
+ traversable->traverse( IncludeSelectedWalker( walker ) );
+#endif
+ }
+}
+
+void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
+ format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
+}
+
+void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
+ scene::Traversable* traversable = Node_getTraversable( root );
+ if ( traversable != 0 ) {
+ traversable->traverse( walker );
+ }
+}
+
+class RegionExcluder : public Excluder
+{
+public:
+bool excluded( scene::Node& node ) const {
+ return node.excluded();
+}
+};
+
+void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
+ scene::Traversable* traversable = Node_getTraversable( root );
+ if ( traversable != 0 ) {
+ traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
+ }
+}
+
+bool Map_SaveRegion( const char *filename ){
+ AddRegionBrushes();
+
+ bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
+
+ RemoveRegionBrushes();
+
+ return success;
+}
+
+
+void Map_RenameAbsolute( const char* absolute ){
+ Resource* resource = GlobalReferenceCache().capture( absolute );
+ NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
+ resource->setNode( clone.get_pointer() );
+
+ {
+ //ScopeTimer timer("clone subgraph");
+ Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
+ }
+
+ g_map.m_resource->detach( g_map );
+ GlobalReferenceCache().release( g_map.m_name.c_str() );
+
+ g_map.m_resource = resource;
+
+ g_map.m_name = absolute;
+ Map_UpdateTitle( g_map );
+
+ g_map.m_resource->attach( g_map );
+}
+
+void Map_Rename( const char* filename ){
+ if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
+ ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
+
+ Map_RenameAbsolute( filename );
+
+ SceneChangeNotify();
+ }
+ else
+ {
+ SaveReferences();
+ }
+}
+
+bool Map_Save(){
+ Pointfile_Clear();
+
+ ScopeTimer timer( "map save" );
+ SaveReferences();
+ return true; // assume success..
+}
+
+/*
+ ===========
+ Map_New
+
+ ===========
+ */
+void Map_New(){
+ //globalOutputStream() << "Map_New\n";
+
+ g_map.m_name = "unnamed.map";
+ Map_UpdateTitle( g_map );
+
+ {
+ g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
+// ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
+ g_map.m_resource->attach( g_map );
+
+ SceneChangeNotify();
+ }
+
+ FocusViews( g_vector3_identity, 0 );
+
+ g_currentMap = &g_map;
+}
+
+extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs );
+
+void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
+ /*!
+ \todo we need to make sure that the player start IS inside the region and bail out if it's not
+ the compiler will refuse to compile a map with a player_start somewhere in empty space..
+ for now, let's just print an error
+ */
+
+ Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
+
+ for ( int i = 0 ; i < 3 ; i++ )
+ {
+ if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
+ globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
+ break;
+ }
+ }
+
+ // write the info_playerstart
+ char sTmp[1024];
+ sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
+ Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
+ sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
+ Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
+}
+
+/*
+ ===========================================================
+
+ REGION
+
+ ===========================================================
+ */
+bool region_active;
+Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
+Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
+
+scene::Node* region_sides[6];
+scene::Node* region_startpoint = 0;
+
+/*
+ ===========
+ AddRegionBrushes
+ a regioned map will have temp walls put up at the region boundary
+ \todo TODO TTimo old implementation of region brushes
+ we still add them straight in the worldspawn and take them out after the map is saved
+ with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
+ ===========
+ */
+void AddRegionBrushes( void ){
+ int i;
+
+ for ( i = 0; i < 6; i++ )
+ {
+ region_sides[i] = &GlobalBrushCreator().createBrush();
+ Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
+ }
+
+ region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
+
+ ConstructRegionBrushes( region_sides, region_mins, region_maxs );
+ ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
+
+ Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
+}
+
+void RemoveRegionBrushes( void ){
+ for ( std::size_t i = 0; i < 6; i++ )
+ {
+ Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
+ }
+ Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
+}
+
+inline void exclude_node( scene::Node& node, bool exclude ){
+ exclude
+ ? node.enable( scene::Node::eExcluded )
+ : node.disable( scene::Node::eExcluded );
+}
+
+class ExcludeAllWalker : public scene::Graph::Walker
+{
+bool m_exclude;
+public:
+ExcludeAllWalker( bool exclude )
+ : m_exclude( exclude ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+ exclude_node( path.top(), m_exclude );
+
+ return true;
+}
+};
+
+void Scene_Exclude_All( bool exclude ){
+ GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
+}
+
+bool Instance_isSelected( const scene::Instance& instance ){
+ const Selectable* selectable = Instance_getSelectable( instance );
+ return selectable != 0 && selectable->isSelected();
+}
+
+class ExcludeSelectedWalker : public scene::Graph::Walker
+{
+bool m_exclude;
+public:
+ExcludeSelectedWalker( bool exclude )
+ : m_exclude( exclude ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+ exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
+ return true;
+}
+};
+
+void Scene_Exclude_Selected( bool exclude ){
+ GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
+}
+
+class ExcludeRegionedWalker : public scene::Graph::Walker
+{
+bool m_exclude;
+public:
+ExcludeRegionedWalker( bool exclude )
+ : m_exclude( exclude ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+ exclude_node(
+ path.top(),
+ !(
+ (
+ aabb_intersects_aabb(
+ instance.worldAABB(),
+ aabb_for_minmax( region_mins, region_maxs )
+ ) != 0
+ ) ^ m_exclude
+ )
+ );
+
+ return true;
+}
+};
+
+void Scene_Exclude_Region( bool exclude ){
+ GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
+}
+
+/*
+ ===========
+ Map_RegionOff
+
+ Other filtering options may still be on
+ ===========
+ */
+void Map_RegionOff(){
+ region_active = false;
+
+ region_maxs[0] = g_MaxWorldCoord - 64;
+ region_mins[0] = g_MinWorldCoord + 64;
+ region_maxs[1] = g_MaxWorldCoord - 64;
+ region_mins[1] = g_MinWorldCoord + 64;
+ region_maxs[2] = g_MaxWorldCoord - 64;
+ region_mins[2] = g_MinWorldCoord + 64;
+
+ Scene_Exclude_All( false );
+}
+
+void Map_ApplyRegion( void ){
+ region_active = true;
+
+ Scene_Exclude_Region( false );
+}
+
+
+/*
+ ========================
+ Map_RegionSelectedBrushes
+ ========================
+ */
+void Map_RegionSelectedBrushes( void ){
+ Map_RegionOff();
+
+ if ( GlobalSelectionSystem().countSelected() != 0
+ && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
+ region_active = true;
+ Select_GetBounds( region_mins, region_maxs );
+
+ Scene_Exclude_Selected( false );
+
+ GlobalSelectionSystem().setSelectedAll( false );
+ }
+}
+
+
+/*
+ ===========
+ Map_RegionXY
+ ===========
+ */
+void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
+ Map_RegionOff();
+
+ region_mins[0] = x_min;
+ region_maxs[0] = x_max;
+ region_mins[1] = y_min;
+ region_maxs[1] = y_max;
+ region_mins[2] = g_MinWorldCoord + 64;
+ region_maxs[2] = g_MaxWorldCoord - 64;
+
+ Map_ApplyRegion();
+}
+
+void Map_RegionBounds( const AABB& bounds ){
+ Map_RegionOff();
+
+ region_mins = vector3_subtracted( bounds.origin, bounds.extents );
+ region_maxs = vector3_added( bounds.origin, bounds.extents );
+
+ deleteSelection();
+
+ Map_ApplyRegion();
+}
+
+/*
+ ===========
+ Map_RegionBrush
+ ===========
+ */
+void Map_RegionBrush( void ){
+ if ( GlobalSelectionSystem().countSelected() != 0 ) {
+ scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
+ Map_RegionBounds( instance.worldAABB() );
+ }
+}
+
+//
+//================
+//Map_ImportFile
+//================
+//
+bool Map_ImportFile( const char* filename ){
+ ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
+
+ bool success = false;
+
+ if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
+ goto tryDecompile;
+ }
+
+ {
+ const MapFormat* format = NULL;
+ const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
+ if ( string_not_empty( moduleName ) ) {
+ format = ReferenceAPI_getMapModules().findModule( moduleName );
+ }
+
+ if ( format ) {
+ format->wrongFormat = false;
+ }
+ Resource* resource = GlobalReferenceCache().capture( filename );
+ resource->refresh(); // avoid loading old version if map has changed on disk since last import
+ if ( !resource->load() ) {
+ GlobalReferenceCache().release( filename );
+ goto tryDecompile;
+ }
+ if ( format ) {
+ if ( format->wrongFormat ) {
+ GlobalReferenceCache().release( filename );
+ goto tryDecompile;
+ }
+ }
+ NodeSmartReference clone( NewMapRoot( "" ) );
+ Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
+ Map_gatherNamespaced( clone );
+ Map_mergeClonedNames();
+ MergeMap( clone );
+ success = true;
+ GlobalReferenceCache().release( filename );
+ }
+
+ SceneChangeNotify();
+
+ return success;
+
+tryDecompile:
+
+ const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
+ int n = string_length( path_get_extension( filename ) );
+ if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
+ StringBuffer output;
+ output.push_string( AppPath_get() );
+ output.push_string( "q3map2." );
+ output.push_string( RADIANT_EXECUTABLE );
+ output.push_string( " -v -game " );
+ output.push_string( ( type && *type ) ? type : "quake3" );
+ output.push_string( " -fs_basepath \"" );
+ output.push_string( EnginePath_get() );
+ output.push_string( "\" -fs_homepath \"" );
+ output.push_string( g_qeglobals.m_userEnginePath.c_str() );
+ output.push_string( "\" -fs_game " );
+ output.push_string( gamename_get() );
+ output.push_string( " -convert -format " );
+ output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
+ if ( extension_equal( path_get_extension( filename ), "map" ) ) {
+ output.push_string( " -readmap " );
+ }
+ output.push_string( " \"" );
+ output.push_string( filename );
+ output.push_string( "\"" );
+
+ // run
+ Q_Exec( NULL, output.c_str(), NULL, false, true );
+
+ // rebuild filename as "filenamewithoutext_converted.map"
+ output.clear();
+ output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
+ output.push_string( "_converted.map" );
+ filename = output.c_str();
+
+ // open
+ Resource* resource = GlobalReferenceCache().capture( filename );
+ resource->refresh(); // avoid loading old version if map has changed on disk since last import
+ if ( !resource->load() ) {
+ GlobalReferenceCache().release( filename );
+ goto tryDecompile;
+ }
+ NodeSmartReference clone( NewMapRoot( "" ) );
+ Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
+ Map_gatherNamespaced( clone );
+ Map_mergeClonedNames();
+ MergeMap( clone );
+ success = true;
+ GlobalReferenceCache().release( filename );
+ }
+
+ SceneChangeNotify();
+ return success;
+}
+
+/*
+ ===========
+ Map_SaveFile
+ ===========
+ */
+bool Map_SaveFile( const char* filename ){
+ ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
+ return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
+}
+
+//
+//===========
+//Map_SaveSelected
+//===========
+//
+// Saves selected world brushes and whole entities with partial/full selections
+//
+bool Map_SaveSelected( const char* filename ){
+ return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
+}
+
+
+class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker