1 /* 2 Copyright (C) 2010-2014 Kristian Duske 3 4 This file is part of TrenchBroom. 5 6 TrenchBroom is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 TrenchBroom is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "View/MapDocument.h" 21 22 #include "PreferenceManager.h" 23 #include "Preferences.h" 24 #include "Polyhedron.h" 25 #include "Assets/EntityDefinitionManager.h" 26 #include "Assets/EntityModelManager.h" 27 #include "Assets/TextureCollectionSpec.h" 28 #include "Assets/Texture.h" 29 #include "Assets/TextureManager.h" 30 #include "IO/DiskFileSystem.h" 31 #include "IO/SystemPaths.h" 32 #include "Model/Brush.h" 33 #include "Model/BrushBuilder.h" 34 #include "Model/BrushFace.h" 35 #include "Model/BrushGeometry.h" 36 #include "Model/ChangeBrushFaceAttributesRequest.h" 37 #include "Model/CollectAttributableNodesVisitor.h" 38 #include "Model/CollectContainedNodesVisitor.h" 39 #include "Model/CollectMatchingBrushFacesVisitor.h" 40 #include "Model/CollectNodesVisitor.h" 41 #include "Model/CollectNodesByVisibilityVisitor.h" 42 #include "Model/CollectSelectableNodesVisitor.h" 43 #include "Model/CollectSelectableNodesWithFilePositionVisitor.h" 44 #include "Model/CollectSelectedNodesVisitor.h" 45 #include "Model/CollectTouchingNodesVisitor.h" 46 #include "Model/CollectUniqueNodesVisitor.h" 47 #include "Model/ComputeNodeBoundsVisitor.h" 48 #include "Model/EditorContext.h" 49 #include "Model/EmptyBrushEntityIssueGenerator.h" 50 #include "Model/Entity.h" 51 #include "Model/EntityLinkSourceIssueGenerator.h" 52 #include "Model/EntityLinkTargetIssueGenerator.h" 53 #include "Model/FindLayerVisitor.h" 54 #include "Model/Game.h" 55 #include "Model/GameFactory.h" 56 #include "Model/Group.h" 57 #include "Model/MergeNodesIntoWorldVisitor.h" 58 #include "Model/MissingEntityClassnameIssueGenerator.h" 59 #include "Model/MissingEntityDefinitionIssueGenerator.h" 60 #include "Model/MixedBrushContentsIssueGenerator.h" 61 #include "Model/Node.h" 62 #include "Model/NodeVisitor.h" 63 #include "Model/NonIntegerPlanePointsIssueGenerator.h" 64 #include "Model/NonIntegerVerticesIssueGenerator.h" 65 #include "Model/WorldBoundsIssueGenerator.h" 66 #include "Model/PointEntityWithBrushesIssueGenerator.h" 67 #include "Model/PointFile.h" 68 #include "Model/World.h" 69 #include "View/AddRemoveNodesCommand.h" 70 #include "View/ChangeBrushFaceAttributesCommand.h" 71 #include "View/ChangeEntityAttributesCommand.h" 72 #include "View/ConvertEntityColorCommand.h" 73 #include "View/CurrentGroupCommand.h" 74 #include "View/DuplicateNodesCommand.h" 75 #include "View/EntityDefinitionFileCommand.h" 76 #include "View/FindPlanePointsCommand.h" 77 #include "View/Grid.h" 78 #include "View/MapViewConfig.h" 79 #include "View/MoveBrushEdgesCommand.h" 80 #include "View/MoveBrushFacesCommand.h" 81 #include "View/MoveBrushVerticesCommand.h" 82 #include "View/MoveTexturesCommand.h" 83 #include "View/RenameGroupsCommand.h" 84 #include "View/ReparentNodesCommand.h" 85 #include "View/ResizeBrushesCommand.h" 86 #include "View/RotateTexturesCommand.h" 87 #include "View/SelectionCommand.h" 88 #include "View/SetLockStateCommand.h" 89 #include "View/SetModsCommand.h" 90 #include "View/SetVisibilityCommand.h" 91 #include "View/ShearTexturesCommand.h" 92 #include "View/SimpleParserStatus.h" 93 #include "View/SnapBrushVerticesCommand.h" 94 #include "View/SplitBrushEdgesCommand.h" 95 #include "View/SplitBrushFacesCommand.h" 96 #include "View/TextureCollectionCommand.h" 97 #include "View/TransformObjectsCommand.h" 98 #include "View/VertexHandleManager.h" 99 #include "View/ViewEffectsService.h" 100 101 #include <cassert> 102 103 namespace TrenchBroom { 104 namespace View { 105 const BBox3 MapDocument::DefaultWorldBounds(-16384.0, 16384.0); 106 const String MapDocument::DefaultDocumentName("unnamed.map"); 107 MapDocument()108 MapDocument::MapDocument() : 109 m_worldBounds(DefaultWorldBounds), 110 m_world(NULL), 111 m_currentLayer(NULL), 112 m_pointFile(NULL), 113 m_editorContext(new Model::EditorContext()), 114 m_entityDefinitionManager(new Assets::EntityDefinitionManager()), 115 m_entityModelManager(new Assets::EntityModelManager(this, pref(Preferences::TextureMinFilter), pref(Preferences::TextureMagFilter))), 116 m_textureManager(new Assets::TextureManager(this, pref(Preferences::TextureMinFilter), pref(Preferences::TextureMagFilter))), 117 m_mapViewConfig(new MapViewConfig(*m_editorContext)), 118 m_grid(new Grid(4)), 119 m_path(DefaultDocumentName), 120 m_lastSaveModificationCount(0), 121 m_modificationCount(0), 122 m_currentTextureName(Model::BrushFace::NoTextureName), 123 m_lastSelectionBounds(0.0, 32.0), 124 m_selectionBoundsValid(true), 125 m_viewEffectsService(NULL) { 126 bindObservers(); 127 } 128 ~MapDocument()129 MapDocument::~MapDocument() { 130 unbindObservers(); 131 132 if (isPointFileLoaded()) 133 unloadPointFile(); 134 clearWorld(); 135 136 delete m_grid; 137 delete m_mapViewConfig; 138 delete m_textureManager; 139 delete m_entityModelManager; 140 delete m_entityDefinitionManager; 141 delete m_editorContext; 142 } 143 game() const144 Model::GamePtr MapDocument::game() const { 145 return m_game; 146 } 147 worldBounds() const148 const BBox3& MapDocument::worldBounds() const { 149 return m_worldBounds; 150 } 151 world() const152 Model::World* MapDocument::world() const { 153 return m_world; 154 } 155 isGamePathPreference(const IO::Path & path) const156 bool MapDocument::isGamePathPreference(const IO::Path& path) const { 157 return m_game.get() != NULL && m_game->isGamePathPreference(path); 158 } 159 currentLayer() const160 Model::Layer* MapDocument::currentLayer() const { 161 assert(m_currentLayer != NULL); 162 return m_currentLayer; 163 } 164 setCurrentLayer(Model::Layer * currentLayer)165 void MapDocument::setCurrentLayer(Model::Layer* currentLayer) { 166 assert(currentLayer != NULL); 167 assert(!currentLayer->locked()); 168 assert(!currentLayer->hidden()); 169 m_currentLayer = currentLayer; 170 currentLayerDidChangeNotifier(); 171 } 172 currentGroup() const173 Model::Group* MapDocument::currentGroup() const { 174 return m_editorContext->currentGroup(); 175 } 176 currentParent() const177 Model::Node* MapDocument::currentParent() const { 178 Model::Node* result = currentGroup(); 179 if (result == NULL) 180 result = currentLayer(); 181 return result; 182 } 183 editorContext() const184 Model::EditorContext& MapDocument::editorContext() const { 185 return *m_editorContext; 186 } 187 textureLock()188 bool MapDocument::textureLock() { 189 return m_editorContext->textureLock(); 190 } 191 setTextureLock(const bool textureLock)192 void MapDocument::setTextureLock(const bool textureLock) { 193 m_editorContext->setTextureLock(textureLock); 194 } 195 entityDefinitionManager()196 Assets::EntityDefinitionManager& MapDocument::entityDefinitionManager() { 197 return *m_entityDefinitionManager; 198 } 199 entityModelManager()200 Assets::EntityModelManager& MapDocument::entityModelManager() { 201 return *m_entityModelManager; 202 } 203 textureManager()204 Assets::TextureManager& MapDocument::textureManager() { 205 return *m_textureManager; 206 } 207 mapViewConfig() const208 View::MapViewConfig& MapDocument::mapViewConfig() const { 209 return *m_mapViewConfig; 210 } 211 grid() const212 Grid& MapDocument::grid() const { 213 return *m_grid; 214 } 215 pointFile() const216 Model::PointFile* MapDocument::pointFile() const { 217 return m_pointFile; 218 } 219 setViewEffectsService(ViewEffectsService * viewEffectsService)220 void MapDocument::setViewEffectsService(ViewEffectsService* viewEffectsService) { 221 m_viewEffectsService = viewEffectsService; 222 } 223 newDocument(const Model::MapFormat::Type mapFormat,const BBox3 & worldBounds,Model::GamePtr game)224 void MapDocument::newDocument(const Model::MapFormat::Type mapFormat, const BBox3& worldBounds, Model::GamePtr game) { 225 info("Creating new document"); 226 227 clearDocument(); 228 createWorld(mapFormat, worldBounds, game); 229 230 loadAssets(); 231 registerIssueGenerators(); 232 233 initializeWorld(worldBounds); 234 clearModificationCount(); 235 236 documentWasNewedNotifier(this); 237 } 238 loadDocument(const Model::MapFormat::Type mapFormat,const BBox3 & worldBounds,Model::GamePtr game,const IO::Path & path)239 void MapDocument::loadDocument(const Model::MapFormat::Type mapFormat, const BBox3& worldBounds, Model::GamePtr game, const IO::Path& path) { 240 info("Loading document from " + path.asString()); 241 242 clearDocument(); 243 loadWorld(mapFormat, worldBounds, game, path); 244 245 loadAssets(); 246 registerIssueGenerators(); 247 248 documentWasLoadedNotifier(this); 249 } 250 saveDocument()251 void MapDocument::saveDocument() { 252 doSaveDocument(m_path); 253 } 254 saveDocumentAs(const IO::Path & path)255 void MapDocument::saveDocumentAs(const IO::Path& path) { 256 doSaveDocument(path); 257 } 258 saveDocumentTo(const IO::Path & path)259 void MapDocument::saveDocumentTo(const IO::Path& path) { 260 assert(m_game.get() != NULL); 261 assert(m_world != NULL); 262 m_game->writeMap(m_world, path); 263 } 264 doSaveDocument(const IO::Path & path)265 void MapDocument::doSaveDocument(const IO::Path& path) { 266 saveDocumentTo(path); 267 setLastSaveModificationCount(); 268 setPath(path); 269 documentWasSavedNotifier(this); 270 } 271 clearDocument()272 void MapDocument::clearDocument() { 273 if (m_world != NULL) { 274 documentWillBeClearedNotifier(this); 275 276 clearSelection(); 277 unloadAssets(); 278 clearWorld(); 279 clearModificationCount(); 280 281 documentWasClearedNotifier(this); 282 } 283 } 284 serializeSelectedNodes()285 String MapDocument::serializeSelectedNodes() { 286 StringStream stream; 287 m_game->writeNodesToStream(m_world, m_selectedNodes.nodes(), stream); 288 return stream.str(); 289 } 290 serializeSelectedBrushFaces()291 String MapDocument::serializeSelectedBrushFaces() { 292 StringStream stream; 293 m_game->writeBrushFacesToStream(m_world, m_selectedBrushFaces, stream); 294 return stream.str(); 295 } 296 paste(const String & str)297 PasteType MapDocument::paste(const String& str) { 298 try { 299 const Model::NodeList nodes = m_game->parseNodes(str, m_world, m_worldBounds, this); 300 if (!nodes.empty() && pasteNodes(nodes)) 301 return PT_Node; 302 } catch (const ParserException& e) { 303 try { 304 const Model::BrushFaceList faces = m_game->parseBrushFaces(str, m_world, m_worldBounds, this); 305 if (!faces.empty() && pasteBrushFaces(faces)) 306 return PT_BrushFace; 307 } catch (const ParserException&) { 308 error("Unable to parse clipboard contents: %s", e.what()); 309 } 310 } 311 return PT_Failed; 312 } 313 pasteNodes(const Model::NodeList & nodes)314 bool MapDocument::pasteNodes(const Model::NodeList& nodes) { 315 Model::MergeNodesIntoWorldVisitor mergeNodes(m_world, currentLayer()); 316 Model::Node::accept(nodes.begin(), nodes.end(), mergeNodes); 317 318 const Model::NodeList addedNodes = addNodes(mergeNodes.result()); 319 if (addedNodes.empty()) 320 return false; 321 322 deselectAll(); 323 324 Model::CollectSelectableNodesVisitor collectSelectables(editorContext()); 325 Model::Node::acceptAndRecurse(addedNodes.begin(), addedNodes.end(), collectSelectables); 326 select(collectSelectables.nodes()); 327 328 return true; 329 } 330 pasteBrushFaces(const Model::BrushFaceList & faces)331 bool MapDocument::pasteBrushFaces(const Model::BrushFaceList& faces) { 332 assert(!faces.empty()); 333 const Model::BrushFace* face = faces.back(); 334 335 const bool result = setFaceAttributes(face->attribs()); 336 VectorUtils::deleteAll(faces); 337 338 return result; 339 } 340 canLoadPointFile() const341 bool MapDocument::canLoadPointFile() const { 342 if (m_path.isEmpty()) 343 return false; 344 const IO::Path pointFilePath = Model::PointFile::pointFilePath(m_path); 345 return pointFilePath.isAbsolute() && IO::Disk::fileExists(pointFilePath); 346 } 347 loadPointFile()348 void MapDocument::loadPointFile() { 349 assert(canLoadPointFile()); 350 if (isPointFileLoaded()) 351 unloadPointFile(); 352 m_pointFile = new Model::PointFile(m_path); 353 info("Loaded point file"); 354 pointFileWasLoadedNotifier(); 355 } 356 isPointFileLoaded() const357 bool MapDocument::isPointFileLoaded() const { 358 return m_pointFile != NULL; 359 } 360 unloadPointFile()361 void MapDocument::unloadPointFile() { 362 assert(isPointFileLoaded()); 363 delete m_pointFile; 364 m_pointFile = NULL; 365 366 info("Unloaded point file"); 367 pointFileWasUnloadedNotifier(); 368 } 369 hasSelection() const370 bool MapDocument::hasSelection() const { 371 return hasSelectedNodes() || hasSelectedBrushFaces(); 372 } 373 hasSelectedNodes() const374 bool MapDocument::hasSelectedNodes() const { 375 return !m_selectedNodes.empty(); 376 } 377 hasSelectedBrushFaces() const378 bool MapDocument::hasSelectedBrushFaces() const { 379 return !m_selectedBrushFaces.empty(); 380 } 381 allSelectedAttributableNodes() const382 const Model::AttributableNodeList MapDocument::allSelectedAttributableNodes() const { 383 Model::CollectAttributableNodesVisitor visitor; 384 Model::Node::accept(m_selectedNodes.begin(), m_selectedNodes.end(), visitor); 385 return visitor.nodes(); 386 } 387 selectedNodes() const388 const Model::NodeCollection& MapDocument::selectedNodes() const { 389 return m_selectedNodes; 390 } 391 allSelectedBrushFaces() const392 const Model::BrushFaceList MapDocument::allSelectedBrushFaces() const { 393 if (hasSelectedBrushFaces()) 394 return selectedBrushFaces(); 395 Model::CollectBrushFacesVisitor visitor; 396 Model::Node::acceptAndRecurse(m_selectedNodes.begin(), m_selectedNodes.end(), visitor); 397 return visitor.faces(); 398 } 399 selectedBrushFaces() const400 const Model::BrushFaceList& MapDocument::selectedBrushFaces() const { 401 return m_selectedBrushFaces; 402 } 403 referenceBounds() const404 const BBox3& MapDocument::referenceBounds() const { 405 if (hasSelectedNodes()) 406 return selectionBounds(); 407 return lastSelectionBounds(); 408 } 409 lastSelectionBounds() const410 const BBox3& MapDocument::lastSelectionBounds() const { 411 return m_lastSelectionBounds; 412 } 413 selectionBounds() const414 const BBox3& MapDocument::selectionBounds() const { 415 if (!m_selectionBoundsValid) 416 validateSelectionBounds(); 417 return m_selectionBounds; 418 } 419 currentTextureName() const420 const String& MapDocument::currentTextureName() const { 421 return m_currentTextureName; 422 } 423 selectAllNodes()424 void MapDocument::selectAllNodes() { 425 submit(UndoableCommand::Ptr(SelectionCommand::selectAllNodes())); 426 } 427 selectSiblings()428 void MapDocument::selectSiblings() { 429 const Model::NodeList& nodes = selectedNodes().nodes(); 430 if (nodes.empty()) 431 return; 432 433 Model::CollectSelectableUniqueNodesVisitor visitor(*m_editorContext); 434 Model::NodeList::const_iterator it, end; 435 for (it = nodes.begin(), end = nodes.end(); it != end; ++it) { 436 Model::Node* node = *it; 437 Model::Node* parent = node->parent(); 438 parent->iterate(visitor); 439 } 440 441 Transaction transaction(this, "Select Siblings"); 442 deselectAll(); 443 select(visitor.nodes()); 444 } 445 selectTouching(const bool del)446 void MapDocument::selectTouching(const bool del) { 447 const Model::BrushList& brushes = m_selectedNodes.brushes(); 448 const Model::NodeList nodes = Model::collectMatchingNodes<Model::CollectTouchingNodesVisitor>(brushes.begin(), brushes.end(), m_world); 449 450 Transaction transaction(this, "Select Touching"); 451 if (del) 452 deleteObjects(); 453 else 454 deselectAll(); 455 select(nodes); 456 } 457 selectInside(const bool del)458 void MapDocument::selectInside(const bool del) { 459 const Model::BrushList& brushes = m_selectedNodes.brushes(); 460 const Model::NodeList nodes = Model::collectMatchingNodes<Model::CollectContainedNodesVisitor>(brushes.begin(), brushes.end(), m_world); 461 462 Transaction transaction(this, "Select Inside"); 463 if (del) 464 deleteObjects(); 465 else 466 deselectAll(); 467 select(nodes); 468 } 469 selectNodesWithFilePosition(const std::vector<size_t> & positions)470 void MapDocument::selectNodesWithFilePosition(const std::vector<size_t>& positions) { 471 Model::CollectSelectableNodesWithFilePositionVisitor visitor(*m_editorContext, positions); 472 m_world->acceptAndRecurse(visitor); 473 474 Transaction transaction(this, "Select by Line Number"); 475 deselectAll(); 476 select(visitor.nodes()); 477 } 478 select(const Model::NodeList & nodes)479 void MapDocument::select(const Model::NodeList& nodes) { 480 submit(UndoableCommand::Ptr(SelectionCommand::select(nodes))); 481 } 482 select(Model::Node * node)483 void MapDocument::select(Model::Node* node) { 484 submit(UndoableCommand::Ptr(SelectionCommand::select(Model::NodeList(1, node)))); 485 } 486 select(const Model::BrushFaceList & faces)487 void MapDocument::select(const Model::BrushFaceList& faces) { 488 submit(UndoableCommand::Ptr(SelectionCommand::select(faces))); 489 } 490 select(Model::BrushFace * face)491 void MapDocument::select(Model::BrushFace* face) { 492 submit(UndoableCommand::Ptr(SelectionCommand::select(Model::BrushFaceList(1, face)))); 493 m_currentTextureName = face->textureName(); 494 } 495 convertToFaceSelection()496 void MapDocument::convertToFaceSelection() { 497 submit(UndoableCommand::Ptr(SelectionCommand::convertToFaces())); 498 } 499 deselectAll()500 void MapDocument::deselectAll() { 501 if (hasSelection()) 502 submit(UndoableCommand::Ptr(SelectionCommand::deselectAll())); 503 } 504 deselect(Model::Node * node)505 void MapDocument::deselect(Model::Node* node) { 506 deselect(Model::NodeList(1, node)); 507 } 508 deselect(const Model::NodeList & nodes)509 void MapDocument::deselect(const Model::NodeList& nodes) { 510 submit(UndoableCommand::Ptr(SelectionCommand::deselect(nodes))); 511 } 512 deselect(Model::BrushFace * face)513 void MapDocument::deselect(Model::BrushFace* face) { 514 submit(UndoableCommand::Ptr(SelectionCommand::deselect(Model::BrushFaceList(1, face)))); 515 } 516 updateLastSelectionBounds()517 void MapDocument::updateLastSelectionBounds() { 518 m_lastSelectionBounds = selectionBounds(); 519 } 520 invalidateSelectionBounds()521 void MapDocument::invalidateSelectionBounds() { 522 m_selectionBoundsValid = false; 523 } 524 validateSelectionBounds() const525 void MapDocument::validateSelectionBounds() const { 526 Model::ComputeNodeBoundsVisitor visitor; 527 Model::Node::accept(m_selectedNodes.begin(), m_selectedNodes.end(), visitor); 528 m_selectionBounds = visitor.bounds(); 529 m_selectionBoundsValid = true; 530 } 531 clearSelection()532 void MapDocument::clearSelection() { 533 m_selectedNodes.clear(); 534 m_selectedBrushFaces.clear(); 535 } 536 addNode(Model::Node * node,Model::Node * parent)537 void MapDocument::addNode(Model::Node* node, Model::Node* parent) { 538 assert(node != NULL); 539 assert(node->parent() == NULL); 540 assert(parent != NULL); 541 assert(parent != node); 542 543 Model::ParentChildrenMap map; 544 map[parent].push_back(node); 545 addNodes(map); 546 } 547 removeNode(Model::Node * node)548 void MapDocument::removeNode(Model::Node* node) { 549 removeNodes(Model::NodeList(1, node)); 550 } 551 addNodes(const Model::ParentChildrenMap & nodes)552 Model::NodeList MapDocument::addNodes(const Model::ParentChildrenMap& nodes) { 553 Transaction transaction(this, "Add Objects"); 554 AddRemoveNodesCommand::Ptr command = AddRemoveNodesCommand::add(nodes); 555 if (!submit(command)) 556 return Model::EmptyNodeList; 557 558 const Model::NodeList& addedNodes = command->addedNodes(); 559 ensureVisible(addedNodes); 560 return addedNodes; 561 } 562 addNodes(const Model::NodeList & nodes,Model::Node * parent)563 Model::NodeList MapDocument::addNodes(const Model::NodeList& nodes, Model::Node* parent) { 564 AddRemoveNodesCommand::Ptr command = AddRemoveNodesCommand::add(parent, nodes); 565 if (!submit(command)) 566 return Model::EmptyNodeList; 567 568 const Model::NodeList& addedNodes = command->addedNodes(); 569 ensureVisible(addedNodes); 570 return addedNodes; 571 } 572 removeNodes(const Model::NodeList & nodes)573 void MapDocument::removeNodes(const Model::NodeList& nodes) { 574 submit(AddRemoveNodesCommand::remove(nodes)); 575 } 576 reparentNodes(Model::Node * newParent,const Model::NodeList & children)577 void MapDocument::reparentNodes(Model::Node* newParent, const Model::NodeList& children) { 578 submit(ReparentNodesCommand::reparent(newParent, children)); 579 } 580 reparentNodes(const Model::ParentChildrenMap & nodes)581 void MapDocument::reparentNodes(const Model::ParentChildrenMap& nodes) { 582 submit(ReparentNodesCommand::reparent(nodes)); 583 } 584 deleteObjects()585 bool MapDocument::deleteObjects() { 586 Transaction transaction(this, "Delete Objects"); 587 const Model::NodeList nodes = m_selectedNodes.nodes(); 588 deselectAll(); 589 return submit(AddRemoveNodesCommand::remove(nodes)); 590 } 591 duplicateObjects()592 bool MapDocument::duplicateObjects() { 593 if (submit(DuplicateNodesCommand::duplicate())) { 594 m_viewEffectsService->flashSelection(); 595 return true; 596 } 597 return false; 598 } 599 groupSelection(const String & name)600 void MapDocument::groupSelection(const String& name) { 601 if (!hasSelectedNodes()) 602 return; 603 604 const Model::NodeList nodes = m_selectedNodes.nodes(); 605 Model::Group* group = new Model::Group(name); 606 607 const Transaction transaction(this, "Group Selected Objects"); 608 deselectAll(); 609 addNode(group, currentParent()); 610 reparentNodes(group, nodes); 611 select(group); 612 } 613 ungroupSelection()614 void MapDocument::ungroupSelection() { 615 if (!hasSelectedNodes() || !m_selectedNodes.hasOnlyGroups()) 616 return; 617 618 const Model::NodeList groups = m_selectedNodes.nodes(); 619 Model::NodeList allChildren; 620 621 const Transaction transaction(this, "Ungroup"); 622 deselectAll(); 623 624 Model::NodeList::const_iterator it, end; 625 for (it = groups.begin(), end = groups.end(); it != end; ++it) { 626 Model::Node* group = *it; 627 Model::Layer* layer = Model::findLayer(group); 628 const Model::NodeList& children = group->children(); 629 reparentNodes(layer, children); 630 VectorUtils::append(allChildren, children); 631 } 632 633 select(allChildren); 634 } 635 renameGroups(const String & name)636 void MapDocument::renameGroups(const String& name) { 637 submit(RenameGroupsCommand::rename(name)); 638 } 639 openGroup(Model::Group * group)640 void MapDocument::openGroup(Model::Group* group) { 641 const Transaction transaction(this, "Open Group"); 642 643 deselectAll(); 644 Model::Group* previousGroup = m_editorContext->currentGroup(); 645 if (previousGroup == NULL) 646 lock(Model::NodeList(1, m_world)); 647 else 648 resetLock(Model::NodeList(1, previousGroup)); 649 unlock(Model::NodeList(1, group)); 650 submit(CurrentGroupCommand::push(group)); 651 } 652 closeGroup()653 void MapDocument::closeGroup() { 654 const Transaction transaction(this, "Close Group"); 655 656 deselectAll(); 657 Model::Group* previousGroup = m_editorContext->currentGroup(); 658 resetLock(Model::NodeList(1, previousGroup)); 659 submit(CurrentGroupCommand::pop()); 660 661 Model::Group* currentGroup = m_editorContext->currentGroup(); 662 if (currentGroup != NULL) 663 unlock(Model::NodeList(1, currentGroup)); 664 else 665 unlock(Model::NodeList(1, m_world)); 666 } 667 isolate(const Model::NodeList & nodes)668 void MapDocument::isolate(const Model::NodeList& nodes) { 669 const Model::LayerList& layers = m_world->allLayers(); 670 671 Model::CollectTransitivelyUnselectedNodesVisitor collectUnselected; 672 Model::Node::recurse(layers.begin(), layers.end(), collectUnselected); 673 674 Model::CollectTransitivelySelectedNodesVisitor collectSelected; 675 Model::Node::recurse(layers.begin(), layers.end(), collectSelected); 676 677 Transaction transaction(this, "Isolate Objects"); 678 submit(SetVisibilityCommand::hide(collectUnselected.nodes())); 679 submit(SetVisibilityCommand::show(collectSelected.nodes())); 680 } 681 hide(const Model::NodeList nodes)682 void MapDocument::hide(const Model::NodeList nodes) { 683 Model::CollectSelectedNodesVisitor collect; 684 Model::Node::acceptAndRecurse(nodes.begin(), nodes.end(), collect); 685 686 const Transaction transaction(this, "Hide Objects"); 687 deselect(collect.nodes()); 688 submit(SetVisibilityCommand::hide(nodes)); 689 } 690 hideSelection()691 void MapDocument::hideSelection() { 692 hide(m_selectedNodes.nodes()); 693 } 694 show(const Model::NodeList & nodes)695 void MapDocument::show(const Model::NodeList& nodes) { 696 submit(SetVisibilityCommand::show(nodes)); 697 } 698 showAll()699 void MapDocument::showAll() { 700 const Model::LayerList& layers = m_world->allLayers(); 701 Model::CollectNodesVisitor collect; 702 Model::Node::recurse(layers.begin(), layers.end(), collect); 703 resetVisibility(collect.nodes()); 704 } 705 ensureVisible(const Model::NodeList & nodes)706 void MapDocument::ensureVisible(const Model::NodeList& nodes) { 707 submit(SetVisibilityCommand::ensureVisible(nodes)); 708 } 709 resetVisibility(const Model::NodeList & nodes)710 void MapDocument::resetVisibility(const Model::NodeList& nodes) { 711 submit(SetVisibilityCommand::reset(nodes)); 712 } 713 lock(const Model::NodeList & nodes)714 void MapDocument::lock(const Model::NodeList& nodes) { 715 Model::CollectSelectedNodesVisitor collect; 716 Model::Node::acceptAndRecurse(nodes.begin(), nodes.end(), collect); 717 718 const Transaction transaction(this, "Lock Objects"); 719 submit(SetLockStateCommand::lock(nodes)); 720 deselect(collect.nodes()); 721 } 722 unlock(const Model::NodeList & nodes)723 void MapDocument::unlock(const Model::NodeList& nodes) { 724 submit(SetLockStateCommand::unlock(nodes)); 725 } 726 resetLock(const Model::NodeList & nodes)727 void MapDocument::resetLock(const Model::NodeList& nodes) { 728 submit(SetLockStateCommand::reset(nodes)); 729 } 730 translateObjects(const Vec3 & delta)731 bool MapDocument::translateObjects(const Vec3& delta) { 732 return submit(TransformObjectsCommand::translate(delta, textureLock())); 733 } 734 rotateObjects(const Vec3 & center,const Vec3 & axis,const FloatType angle)735 bool MapDocument::rotateObjects(const Vec3& center, const Vec3& axis, const FloatType angle) { 736 return submit(TransformObjectsCommand::rotate(center, axis, angle, textureLock())); 737 } 738 flipObjects(const Vec3 & center,const Math::Axis::Type axis)739 bool MapDocument::flipObjects(const Vec3& center, const Math::Axis::Type axis) { 740 return submit(TransformObjectsCommand::flip(center, axis, textureLock())); 741 } 742 createBrush(const Vec3::List & points)743 bool MapDocument::createBrush(const Vec3::List& points) { 744 Model::BrushBuilder builder(m_world, m_worldBounds); 745 Model::Brush* brush = builder.createBrush(points, currentTextureName()); 746 if (!brush->fullySpecified()) { 747 delete brush; 748 return false; 749 } 750 751 Transaction transaction(this, "Create Brush"); 752 deselectAll(); 753 addNode(brush, currentParent()); 754 select(brush); 755 return true; 756 } 757 csgConvexMerge()758 bool MapDocument::csgConvexMerge() { 759 if (!hasSelectedBrushFaces() && !selectedNodes().hasOnlyBrushes()) 760 return false; 761 762 Polyhedron3 polyhedron; 763 764 if (hasSelectedBrushFaces()) { 765 const Model::BrushFaceList& faces = selectedBrushFaces(); 766 Model::BrushFaceList::const_iterator fIt, fEnd; 767 for (fIt = faces.begin(), fEnd = faces.end(); fIt != fEnd; ++fIt) { 768 const Model::BrushFace* face = *fIt; 769 const Model::BrushFace::VertexList vertices = face->vertices(); 770 Model::BrushFace::VertexList::const_iterator vIt, vEnd; 771 for (vIt = vertices.begin(), vEnd = vertices.end(); vIt != vEnd; ++vIt) { 772 const Model::BrushVertex* vertex = *vIt; 773 polyhedron.addPoint(vertex->position()); 774 } 775 } 776 } else if (selectedNodes().hasOnlyBrushes()) { 777 const Model::BrushList& brushes = selectedNodes().brushes(); 778 Model::BrushList::const_iterator bIt, bEnd; 779 for (bIt = brushes.begin(), bEnd = brushes.end(); bIt != bEnd; ++bIt) { 780 const Model::Brush* brush = *bIt; 781 const Model::Brush::VertexList vertices = brush->vertices(); 782 Model::Brush::VertexList::const_iterator vIt, vEnd; 783 for (vIt = vertices.begin(), vEnd = vertices.end(); vIt != vEnd; ++vIt) { 784 const Model::BrushVertex* vertex = *vIt; 785 polyhedron.addPoint(vertex->position()); 786 } 787 } 788 } 789 790 if (!polyhedron.polyhedron() || !polyhedron.closed()) 791 return false; 792 793 const Model::BrushBuilder builder(m_world, m_worldBounds); 794 Model::Brush* brush = builder.createBrush(polyhedron, currentTextureName()); 795 brush->cloneFaceAttributesFrom(selectedNodes().brushes()); 796 797 // The nodelist is either empty or contains only brushes. 798 const Model::NodeList toRemove = selectedNodes().nodes(); 799 800 const Transaction transaction(this, "CSG Convex Merge"); 801 deselectAll(); 802 removeNodes(toRemove); 803 addNode(brush, currentParent()); 804 select(brush); 805 return true; 806 } 807 csgSubtract()808 bool MapDocument::csgSubtract() { 809 const Model::BrushList brushes = selectedNodes().brushes(); 810 if (brushes.size() < 2) 811 return false; 812 813 const Model::BrushList minuends(brushes.begin(), brushes.end() - 1); 814 Model::Brush* subtrahend = brushes.back(); 815 816 Model::ParentChildrenMap toAdd; 817 Model::NodeList toRemove; 818 toRemove.push_back(subtrahend); 819 820 Model::BrushList::const_iterator it, end; 821 for (it = minuends.begin(), end = minuends.end(); it != end; ++it) { 822 Model::Brush* minuend = *it; 823 const Model::BrushList result = minuend->subtract(*m_world, m_worldBounds, currentTextureName(), subtrahend); 824 if (!result.empty()) { 825 VectorUtils::append(toAdd[minuend->parent()], result); 826 toRemove.push_back(minuend); 827 } 828 } 829 830 Transaction transaction(this, "CSG Subtract"); 831 deselectAll(); 832 removeNodes(toRemove); 833 select(addNodes(toAdd)); 834 835 return true; 836 } 837 csgIntersect()838 bool MapDocument::csgIntersect() { 839 const Model::BrushList brushes = selectedNodes().brushes(); 840 if (brushes.size() < 2) 841 return false; 842 843 Model::Brush* result = brushes.front()->clone(m_worldBounds); 844 845 bool valid = true; 846 Model::BrushList::const_iterator it, end; 847 for (it = brushes.begin(), end = brushes.end(); it != end && valid; ++it) { 848 Model::Brush* brush = *it; 849 try { 850 result->intersect(m_worldBounds, brush); 851 } catch (const GeometryException&) { 852 valid = false; 853 } 854 } 855 856 const Model::NodeList toRemove(brushes.begin(), brushes.end()); 857 858 Transaction transaction(this, "CSG Intersect"); 859 deselect(toRemove); 860 removeNodes(toRemove); 861 862 if (valid) { 863 addNode(result, currentParent()); 864 select(result); 865 } else { 866 delete result; 867 } 868 869 return true; 870 } 871 setAttribute(const Model::AttributeName & name,const Model::AttributeValue & value)872 bool MapDocument::setAttribute(const Model::AttributeName& name, const Model::AttributeValue& value) { 873 return submit(ChangeEntityAttributesCommand::set(name, value)); 874 } 875 renameAttribute(const Model::AttributeName & oldName,const Model::AttributeName & newName)876 bool MapDocument::renameAttribute(const Model::AttributeName& oldName, const Model::AttributeName& newName) { 877 return submit(ChangeEntityAttributesCommand::rename(oldName, newName)); 878 } 879 removeAttribute(const Model::AttributeName & name)880 bool MapDocument::removeAttribute(const Model::AttributeName& name) { 881 return submit(ChangeEntityAttributesCommand::remove(name)); 882 } 883 convertEntityColorRange(const Model::AttributeName & name,Assets::ColorRange::Type range)884 bool MapDocument::convertEntityColorRange(const Model::AttributeName& name, Assets::ColorRange::Type range) { 885 return submit(ConvertEntityColorCommand::convert(name, range)); 886 } 887 resizeBrushes(const Model::BrushFaceList & faces,const Vec3 & delta)888 bool MapDocument::resizeBrushes(const Model::BrushFaceList& faces, const Vec3& delta) { 889 return submit(ResizeBrushesCommand::resize(faces, delta)); 890 } 891 setTexture(Assets::Texture * texture)892 bool MapDocument::setTexture(Assets::Texture* texture) { 893 if (hasSelectedBrushFaces() || m_selectedNodes.hasOnlyBrushes()) { 894 Model::ChangeBrushFaceAttributesRequest request; 895 request.setTexture(texture); 896 if (submit(ChangeBrushFaceAttributesCommand::command(request))) { 897 if (texture != NULL) 898 m_currentTextureName = texture->name(); 899 return true; 900 } 901 } else if (texture != NULL) { 902 m_currentTextureName = texture->name(); 903 return true; 904 } 905 return false; 906 } 907 setFaceAttributes(const Model::BrushFaceAttributes & attributes)908 bool MapDocument::setFaceAttributes(const Model::BrushFaceAttributes& attributes) { 909 Model::ChangeBrushFaceAttributesRequest request; 910 request.setAll(attributes); 911 912 // try to find the texture if it is null, maybe it just wasn't set? 913 if (attributes.texture() == NULL) { 914 Assets::Texture* texture = m_textureManager->texture(attributes.textureName()); 915 request.setTexture(texture); 916 } 917 918 return setFaceAttributes(request); 919 } 920 setFaceAttributes(const Model::ChangeBrushFaceAttributesRequest & request)921 bool MapDocument::setFaceAttributes(const Model::ChangeBrushFaceAttributesRequest& request) { 922 return submit(ChangeBrushFaceAttributesCommand::command(request)); 923 } 924 moveTextures(const Vec3f & cameraUp,const Vec3f & cameraRight,const Vec2f & delta)925 bool MapDocument::moveTextures(const Vec3f& cameraUp, const Vec3f& cameraRight, const Vec2f& delta) { 926 return submit(MoveTexturesCommand::move(cameraUp, cameraRight, delta)); 927 } 928 rotateTextures(const float angle)929 bool MapDocument::rotateTextures(const float angle) { 930 return submit(RotateTexturesCommand::rotate(angle)); 931 } 932 shearTextures(const Vec2f & factors)933 bool MapDocument::shearTextures(const Vec2f& factors) { 934 return submit(ShearTexturesCommand::shear(factors)); 935 } 936 rebuildBrushGeometry(const Model::BrushList & brushes)937 void MapDocument::rebuildBrushGeometry(const Model::BrushList& brushes) { 938 performRebuildBrushGeometry(brushes); 939 } 940 snapVertices(const Model::VertexToBrushesMap & vertices,const size_t snapTo)941 bool MapDocument::snapVertices(const Model::VertexToBrushesMap& vertices, const size_t snapTo) { 942 if (vertices.empty()) { 943 assert(m_selectedNodes.hasOnlyBrushes()); 944 return submit(SnapBrushVerticesCommand::snap(m_selectedNodes.brushes(), snapTo)); 945 } 946 return submit(SnapBrushVerticesCommand::snap(vertices, snapTo)); 947 } 948 findPlanePoints()949 bool MapDocument::findPlanePoints() { 950 return submit(FindPlanePointsCommand::findPlanePoints()); 951 } 952 moveVertices(const Model::VertexToBrushesMap & vertices,const Vec3 & delta)953 MapDocument::MoveVerticesResult MapDocument::moveVertices(const Model::VertexToBrushesMap& vertices, const Vec3& delta) { 954 MoveBrushVerticesCommand::Ptr command = MoveBrushVerticesCommand::move(vertices, delta); 955 const bool success = submit(command); 956 const bool hasRemainingVertices = command->hasRemainingVertices(); 957 return MoveVerticesResult(success, hasRemainingVertices); 958 } 959 moveEdges(const Model::VertexToEdgesMap & edges,const Vec3 & delta)960 bool MapDocument::moveEdges(const Model::VertexToEdgesMap& edges, const Vec3& delta) { 961 return submit(MoveBrushEdgesCommand::move(edges, delta)); 962 } 963 moveFaces(const Model::VertexToFacesMap & faces,const Vec3 & delta)964 bool MapDocument::moveFaces(const Model::VertexToFacesMap& faces, const Vec3& delta) { 965 return submit(MoveBrushFacesCommand::move(faces, delta)); 966 } 967 splitEdges(const Model::VertexToEdgesMap & edges,const Vec3 & delta)968 bool MapDocument::splitEdges(const Model::VertexToEdgesMap& edges, const Vec3& delta) { 969 return submit(SplitBrushEdgesCommand::split(edges, delta)); 970 } 971 splitFaces(const Model::VertexToFacesMap & faces,const Vec3 & delta)972 bool MapDocument::splitFaces(const Model::VertexToFacesMap& faces, const Vec3& delta) { 973 return submit(SplitBrushFacesCommand::split(faces, delta)); 974 } 975 printVertices()976 void MapDocument::printVertices() { 977 if (hasSelectedBrushFaces()) { 978 Model::BrushFaceList::const_iterator fIt, fEnd; 979 for (fIt = m_selectedBrushFaces.begin(), fEnd = m_selectedBrushFaces.end(); fIt != fEnd; ++fIt) { 980 const Model::BrushFace* face = *fIt; 981 const Model::BrushFace::VertexList vertices = face->vertices(); 982 StringStream str; 983 Model::BrushFace::VertexList::const_iterator vIt, vEnd; 984 for (vIt = vertices.begin(), vEnd = vertices.end(); vIt != vEnd; ++vIt) { 985 const Model::BrushVertex* vertex = *vIt; 986 str << "(" << vertex->position().asString() << ") "; 987 } 988 info(str.str()); 989 } 990 } else if (selectedNodes().hasBrushes()) { 991 const Model::BrushList& brushes = selectedNodes().brushes(); 992 Model::BrushList::const_iterator bIt, bEnd; 993 for (bIt = brushes.begin(), bEnd = brushes.end(); bIt != bEnd; ++bIt) { 994 const Model::Brush* brush = *bIt; 995 const Model::Brush::VertexList vertices = brush->vertices(); 996 StringStream str; 997 Model::Brush::VertexList::const_iterator vIt, vEnd; 998 for (vIt = vertices.begin(), vEnd = vertices.end(); vIt != vEnd; ++vIt) { 999 const Model::BrushVertex* vertex = *vIt; 1000 str << "(" << vertex->position().asString() << ") "; 1001 } 1002 info(str.str()); 1003 } 1004 } 1005 } 1006 canUndoLastCommand() const1007 bool MapDocument::canUndoLastCommand() const { 1008 return doCanUndoLastCommand(); 1009 } 1010 canRedoNextCommand() const1011 bool MapDocument::canRedoNextCommand() const { 1012 return doCanRedoNextCommand(); 1013 } 1014 lastCommandName() const1015 const String& MapDocument::lastCommandName() const { 1016 return doGetLastCommandName(); 1017 } 1018 nextCommandName() const1019 const String& MapDocument::nextCommandName() const { 1020 return doGetNextCommandName(); 1021 } 1022 undoLastCommand()1023 void MapDocument::undoLastCommand() { 1024 doUndoLastCommand(); 1025 } 1026 redoNextCommand()1027 void MapDocument::redoNextCommand() { 1028 doRedoNextCommand(); 1029 } 1030 repeatLastCommands()1031 bool MapDocument::repeatLastCommands() { 1032 return doRepeatLastCommands(); 1033 } 1034 clearRepeatableCommands()1035 void MapDocument::clearRepeatableCommands() { 1036 doClearRepeatableCommands(); 1037 } 1038 beginTransaction(const String & name)1039 void MapDocument::beginTransaction(const String& name) { 1040 doBeginTransaction(name); 1041 } 1042 rollbackTransaction()1043 void MapDocument::rollbackTransaction() { 1044 doRollbackTransaction(); 1045 } 1046 commitTransaction()1047 void MapDocument::commitTransaction() { 1048 doEndTransaction(); 1049 } 1050 cancelTransaction()1051 void MapDocument::cancelTransaction() { 1052 doRollbackTransaction(); 1053 doEndTransaction(); 1054 } 1055 submit(UndoableCommand::Ptr command)1056 bool MapDocument::submit(UndoableCommand::Ptr command) { 1057 return doSubmit(command); 1058 } 1059 commitPendingAssets()1060 void MapDocument::commitPendingAssets() { 1061 m_textureManager->commitChanges(); 1062 } 1063 pick(const Ray3 & pickRay,Model::PickResult & pickResult) const1064 void MapDocument::pick(const Ray3& pickRay, Model::PickResult& pickResult) const { 1065 if (m_world != NULL) 1066 m_world->pick(pickRay, pickResult); 1067 } 1068 findNodesContaining(const Vec3 & point) const1069 Model::NodeList MapDocument::findNodesContaining(const Vec3& point) const { 1070 Model::NodeList result; 1071 if (m_world != NULL) 1072 m_world->findNodesContaining(point, result); 1073 return result; 1074 } 1075 createWorld(const Model::MapFormat::Type mapFormat,const BBox3 & worldBounds,Model::GamePtr game)1076 void MapDocument::createWorld(const Model::MapFormat::Type mapFormat, const BBox3& worldBounds, Model::GamePtr game) { 1077 m_worldBounds = worldBounds; 1078 m_game = game; 1079 m_world = m_game->newMap(mapFormat, m_worldBounds); 1080 setCurrentLayer(m_world->defaultLayer()); 1081 1082 updateGameSearchPaths(); 1083 setPath(DefaultDocumentName); 1084 } 1085 loadWorld(const Model::MapFormat::Type mapFormat,const BBox3 & worldBounds,Model::GamePtr game,const IO::Path & path)1086 void MapDocument::loadWorld(const Model::MapFormat::Type mapFormat, const BBox3& worldBounds, Model::GamePtr game, const IO::Path& path) { 1087 m_worldBounds = worldBounds; 1088 m_game = game; 1089 m_world = m_game->loadMap(mapFormat, m_worldBounds, path, this); 1090 setCurrentLayer(m_world->defaultLayer()); 1091 1092 updateGameSearchPaths(); 1093 setPath(path); 1094 } 1095 clearWorld()1096 void MapDocument::clearWorld() { 1097 delete m_world; 1098 m_world = NULL; 1099 m_currentLayer = NULL; 1100 } 1101 initializeWorld(const BBox3 & worldBounds)1102 void MapDocument::initializeWorld(const BBox3& worldBounds) { 1103 const Model::BrushBuilder builder(m_world, worldBounds); 1104 Model::Brush* brush = builder.createCuboid(Vec3(128.0, 128.0, 32.0), Model::BrushFace::NoTextureName); 1105 addNode(brush, m_world->defaultLayer()); 1106 } 1107 entityDefinitionFile() const1108 Assets::EntityDefinitionFileSpec MapDocument::entityDefinitionFile() const { 1109 return m_game->extractEntityDefinitionFile(m_world); 1110 } 1111 allEntityDefinitionFiles() const1112 Assets::EntityDefinitionFileSpec::List MapDocument::allEntityDefinitionFiles() const { 1113 return m_game->allEntityDefinitionFiles(); 1114 } 1115 setEntityDefinitionFile(const Assets::EntityDefinitionFileSpec & spec)1116 void MapDocument::setEntityDefinitionFile(const Assets::EntityDefinitionFileSpec& spec) { 1117 submit(EntityDefinitionFileCommand::set(spec)); 1118 } 1119 externalTextureCollectionNames() const1120 const StringList MapDocument::externalTextureCollectionNames() const { 1121 return m_textureManager->externalCollectionNames(); 1122 } 1123 addTextureCollection(const String & name)1124 void MapDocument::addTextureCollection(const String& name) { 1125 submit(TextureCollectionCommand::add(name)); 1126 } 1127 moveTextureCollectionUp(const String & name)1128 void MapDocument::moveTextureCollectionUp(const String& name) { 1129 submit(TextureCollectionCommand::moveUp(name)); 1130 } 1131 moveTextureCollectionDown(const String & name)1132 void MapDocument::moveTextureCollectionDown(const String& name) { 1133 submit(TextureCollectionCommand::moveDown(name)); 1134 } 1135 removeTextureCollections(const StringList & names)1136 void MapDocument::removeTextureCollections(const StringList& names) { 1137 submit(TextureCollectionCommand::remove(names)); 1138 } 1139 loadAssets()1140 void MapDocument::loadAssets() { 1141 loadEntityDefinitions(); 1142 setEntityDefinitions(); 1143 loadEntityModels(); 1144 setEntityModels(); 1145 loadTextures(); 1146 setTextures(); 1147 } 1148 unloadAssets()1149 void MapDocument::unloadAssets() { 1150 unloadEntityDefinitions(); 1151 unloadEntityModels(); 1152 unloadTextures(); 1153 } 1154 loadEntityDefinitions()1155 void MapDocument::loadEntityDefinitions() { 1156 const Assets::EntityDefinitionFileSpec spec = entityDefinitionFile(); 1157 const IO::Path path = m_game->findEntityDefinitionFile(spec, externalSearchPaths()); 1158 SimpleParserStatus status(this); 1159 1160 m_entityDefinitionManager->loadDefinitions(path, *m_game, status); 1161 info("Loaded entity definition file " + path.lastComponent().asString()); 1162 } 1163 unloadEntityDefinitions()1164 void MapDocument::unloadEntityDefinitions() { 1165 unsetEntityDefinitions(); 1166 m_entityDefinitionManager->clear(); 1167 } 1168 loadEntityModels()1169 void MapDocument::loadEntityModels() { 1170 m_entityModelManager->setLoader(m_game.get()); 1171 } 1172 unloadEntityModels()1173 void MapDocument::unloadEntityModels() { 1174 clearEntityModels(); 1175 m_entityModelManager->setLoader(NULL); 1176 } 1177 loadTextures()1178 void MapDocument::loadTextures() { 1179 m_textureManager->setLoader(m_game.get()); 1180 loadBuiltinTextures(); 1181 loadExternalTextures(); 1182 } 1183 loadBuiltinTextures()1184 void MapDocument::loadBuiltinTextures() { 1185 const IO::Path::List paths = m_game->findBuiltinTextureCollections(); 1186 const String names(StringUtils::join(IO::Path::asStrings(paths), ", ")); 1187 try { 1188 m_textureManager->setBuiltinTextureCollections(paths); 1189 info("Loaded builtin texture collections '%s'", names.c_str()); 1190 } catch (Exception e) { 1191 error("Unable to load internal texture collections '%s': %s", names.c_str(), e.what()); 1192 } 1193 } 1194 loadExternalTextures()1195 void MapDocument::loadExternalTextures() { 1196 const StringList names = m_game->extractExternalTextureCollections(m_world); 1197 addExternalTextureCollections(names); 1198 } 1199 unloadTextures()1200 void MapDocument::unloadTextures() { 1201 unsetTextures(); 1202 m_textureManager->clear(); 1203 m_textureManager->setLoader(NULL); 1204 } 1205 reloadTextures()1206 void MapDocument::reloadTextures() { 1207 unloadTextures(); 1208 loadTextures(); 1209 setTextures(); 1210 } 1211 addExternalTextureCollections(const StringList & names)1212 void MapDocument::addExternalTextureCollections(const StringList& names) { 1213 const IO::Path::List searchPaths = externalSearchPaths(); 1214 1215 StringList::const_iterator it, end; 1216 for (it = names.begin(), end = names.end(); it != end; ++it) { 1217 const String& name = *it; 1218 const IO::Path texturePath(name); 1219 const IO::Path absPath = IO::Disk::resolvePath(searchPaths, texturePath); 1220 1221 try { 1222 const Assets::TextureCollectionSpec spec(name, absPath); 1223 m_textureManager->addExternalTextureCollection(spec); 1224 info("Loaded external texture collection '%s'", name.c_str()); 1225 } catch (std::exception& e) { 1226 error("Unable to load external texture collection '%s': %s", name.c_str(), e.what()); 1227 } 1228 } 1229 } 1230 updateExternalTextureCollectionProperty()1231 void MapDocument::updateExternalTextureCollectionProperty() { 1232 m_game->updateExternalTextureCollections(m_world, m_textureManager->externalCollectionNames()); 1233 } 1234 1235 class SetEntityDefinition : public Model::NodeVisitor { 1236 private: 1237 Assets::EntityDefinitionManager& m_manager; 1238 public: SetEntityDefinition(Assets::EntityDefinitionManager & manager)1239 SetEntityDefinition(Assets::EntityDefinitionManager& manager) : 1240 m_manager(manager) {} 1241 private: doVisit(Model::World * world)1242 void doVisit(Model::World* world) { handle(world); } doVisit(Model::Layer * layer)1243 void doVisit(Model::Layer* layer) {} doVisit(Model::Group * group)1244 void doVisit(Model::Group* group) {} doVisit(Model::Entity * entity)1245 void doVisit(Model::Entity* entity) { handle(entity); } doVisit(Model::Brush * brush)1246 void doVisit(Model::Brush* brush) {} handle(Model::AttributableNode * attributable)1247 void handle(Model::AttributableNode* attributable) { 1248 Assets::EntityDefinition* definition = m_manager.definition(attributable); 1249 attributable->setDefinition(definition); 1250 } 1251 }; 1252 1253 class UnsetEntityDefinition : public Model::NodeVisitor { 1254 private: doVisit(Model::World * world)1255 void doVisit(Model::World* world) { world->setDefinition(NULL); } doVisit(Model::Layer * layer)1256 void doVisit(Model::Layer* layer) {} doVisit(Model::Group * group)1257 void doVisit(Model::Group* group) {} doVisit(Model::Entity * entity)1258 void doVisit(Model::Entity* entity) { entity->setDefinition(NULL); } doVisit(Model::Brush * brush)1259 void doVisit(Model::Brush* brush) {} 1260 }; 1261 setEntityDefinitions()1262 void MapDocument::setEntityDefinitions() { 1263 SetEntityDefinition visitor(*m_entityDefinitionManager); 1264 m_world->acceptAndRecurse(visitor); 1265 } 1266 setEntityDefinitions(const Model::NodeList & nodes)1267 void MapDocument::setEntityDefinitions(const Model::NodeList& nodes) { 1268 SetEntityDefinition visitor(*m_entityDefinitionManager); 1269 Model::Node::acceptAndRecurse(nodes.begin(), nodes.end(), visitor); 1270 } 1271 unsetEntityDefinitions()1272 void MapDocument::unsetEntityDefinitions() { 1273 UnsetEntityDefinition visitor; 1274 m_world->acceptAndRecurse(visitor); 1275 } 1276 unsetEntityDefinitions(const Model::NodeList & nodes)1277 void MapDocument::unsetEntityDefinitions(const Model::NodeList& nodes) { 1278 UnsetEntityDefinition visitor; 1279 Model::Node::acceptAndRecurse(nodes.begin(), nodes.end(), visitor); 1280 } 1281 reloadEntityDefinitions()1282 void MapDocument::reloadEntityDefinitions() { 1283 unloadEntityDefinitions(); 1284 clearEntityModels(); 1285 loadEntityDefinitions(); 1286 setEntityDefinitions(); 1287 setEntityModels(); 1288 } 1289 1290 class SetEntityModel : public Model::NodeVisitor { 1291 private: 1292 Assets::EntityModelManager& m_manager; 1293 Logger& m_logger; 1294 public: SetEntityModel(Assets::EntityModelManager & manager,Logger & logger)1295 SetEntityModel(Assets::EntityModelManager& manager, Logger& logger) : 1296 m_manager(manager), 1297 m_logger(logger) {} 1298 private: doVisit(Model::World * world)1299 void doVisit(Model::World* world) {} doVisit(Model::Layer * layer)1300 void doVisit(Model::Layer* layer) {} doVisit(Model::Group * group)1301 void doVisit(Model::Group* group) {} doVisit(Model::Entity * entity)1302 void doVisit(Model::Entity* entity) { 1303 try { 1304 const Assets::ModelSpecification spec = entity->modelSpecification(); 1305 if (spec.path.isEmpty()) { 1306 entity->setModel(NULL); 1307 } else { 1308 Assets::EntityModel* model = m_manager.model(spec.path); 1309 entity->setModel(model); 1310 } 1311 } catch (const GameException& e) { 1312 m_logger.error(String(e.what())); 1313 } 1314 1315 } doVisit(Model::Brush * brush)1316 void doVisit(Model::Brush* brush) {} 1317 }; 1318 1319 class UnsetEntityModel : public Model::NodeVisitor { 1320 private: doVisit(Model::World * world)1321 void doVisit(Model::World* world) {} doVisit(Model::Layer * layer)1322 void doVisit(Model::Layer* layer) {} doVisit(Model::Group * group)1323 void doVisit(Model::Group* group) {} doVisit(Model::Entity * entity)1324 void doVisit(Model::Entity* entity) { entity->setModel(NULL); } doVisit(Model::Brush * brush)1325 void doVisit(Model::Brush* brush) {} 1326 }; 1327 setEntityModels()1328 void MapDocument::setEntityModels() { 1329 SetEntityModel visitor(*m_entityModelManager, *this); 1330 m_world->acceptAndRecurse(visitor); 1331 } 1332 setEntityModels(const Model::NodeList & nodes)1333 void MapDocument::setEntityModels(const Model::NodeList& nodes) { 1334 SetEntityModel visitor(*m_entityModelManager, *this); 1335 Model::Node::accept(nodes.begin(), nodes.end(), visitor); 1336 } 1337 clearEntityModels()1338 void MapDocument::clearEntityModels() { 1339 unsetEntityModels(); 1340 m_entityModelManager->clear(); 1341 } 1342 unsetEntityModels()1343 void MapDocument::unsetEntityModels() { 1344 UnsetEntityModel visitor; 1345 m_world->acceptAndRecurse(visitor); 1346 } 1347 1348 class SetTextures : public Model::NodeVisitor { 1349 private: 1350 Assets::TextureManager* m_manager; 1351 public: SetTextures(Assets::TextureManager * manager)1352 SetTextures(Assets::TextureManager* manager) : 1353 m_manager(manager) {} 1354 private: doVisit(Model::World * world)1355 void doVisit(Model::World* world) {} doVisit(Model::Layer * layer)1356 void doVisit(Model::Layer* layer) {} doVisit(Model::Group * group)1357 void doVisit(Model::Group* group) {} doVisit(Model::Entity * entity)1358 void doVisit(Model::Entity* entity) {} doVisit(Model::Brush * brush)1359 void doVisit(Model::Brush* brush) { 1360 const Model::BrushFaceList& faces = brush->faces(); 1361 Model::BrushFaceList::const_iterator it, end; 1362 for (it = faces.begin(), end = faces.end(); it != end; ++it) { 1363 Model::BrushFace* face = *it; 1364 face->updateTexture(m_manager); 1365 } 1366 } 1367 }; 1368 setTextures()1369 void MapDocument::setTextures() { 1370 SetTextures visitor(m_textureManager); 1371 m_world->acceptAndRecurse(visitor); 1372 } 1373 setTextures(const Model::NodeList & nodes)1374 void MapDocument::setTextures(const Model::NodeList& nodes) { 1375 SetTextures visitor(m_textureManager); 1376 Model::Node::acceptAndRecurse(nodes.begin(), nodes.end(), visitor); 1377 } 1378 setTextures(const Model::BrushFaceList & faces)1379 void MapDocument::setTextures(const Model::BrushFaceList& faces) { 1380 Model::BrushFaceList::const_iterator it, end; 1381 for (it = faces.begin(), end = faces.end(); it != end; ++it) { 1382 Model::BrushFace* face = *it; 1383 face->updateTexture(m_textureManager); 1384 } 1385 } 1386 1387 class UnsetTextures : public Model::NodeVisitor { 1388 private: doVisit(Model::World * world)1389 void doVisit(Model::World* world) {} doVisit(Model::Layer * layer)1390 void doVisit(Model::Layer* layer) {} doVisit(Model::Group * group)1391 void doVisit(Model::Group* group) {} doVisit(Model::Entity * entity)1392 void doVisit(Model::Entity* entity) {} doVisit(Model::Brush * brush)1393 void doVisit(Model::Brush* brush) { 1394 const Model::BrushFaceList& faces = brush->faces(); 1395 Model::BrushFaceList::const_iterator it, end; 1396 for (it = faces.begin(), end = faces.end(); it != end; ++it) { 1397 Model::BrushFace* face = *it; 1398 face->setTexture(NULL); 1399 } 1400 } 1401 }; 1402 unsetTextures()1403 void MapDocument::unsetTextures() { 1404 UnsetTextures visitor; 1405 m_world->acceptAndRecurse(visitor); 1406 } 1407 unsetTextures(const Model::NodeList & nodes)1408 void MapDocument::unsetTextures(const Model::NodeList& nodes) { 1409 UnsetTextures visitor; 1410 Model::Node::acceptAndRecurse(nodes.begin(), nodes.end(), visitor); 1411 } 1412 externalSearchPaths() const1413 IO::Path::List MapDocument::externalSearchPaths() const { 1414 IO::Path::List searchPaths; 1415 if (!m_path.isEmpty() && m_path.isAbsolute()) 1416 searchPaths.push_back(m_path.deleteLastComponent()); 1417 1418 const IO::Path gamePath = m_game->gamePath(); 1419 if (!gamePath.isEmpty()) 1420 searchPaths.push_back(gamePath); 1421 1422 searchPaths.push_back(IO::SystemPaths::appDirectory()); 1423 return searchPaths; 1424 } 1425 updateGameSearchPaths()1426 void MapDocument::updateGameSearchPaths() { 1427 const StringList modNames = mods(); 1428 IO::Path::List additionalSearchPaths; 1429 additionalSearchPaths.reserve(modNames.size()); 1430 1431 StringList::const_iterator it, end; 1432 for (it = modNames.begin(), end = modNames.end(); it != end; ++it) 1433 additionalSearchPaths.push_back(IO::Path(*it)); 1434 m_game->setAdditionalSearchPaths(additionalSearchPaths); 1435 } 1436 mods() const1437 StringList MapDocument::mods() const { 1438 return m_game->extractEnabledMods(m_world); 1439 } 1440 setMods(const StringList & mods)1441 void MapDocument::setMods(const StringList& mods) { 1442 submit(SetModsCommand::set(mods)); 1443 } 1444 setIssueHidden(Model::Issue * issue,const bool hidden)1445 void MapDocument::setIssueHidden(Model::Issue* issue, const bool hidden) { 1446 doSetIssueHidden(issue, hidden); 1447 } 1448 registerIssueGenerators()1449 void MapDocument::registerIssueGenerators() { 1450 assert(m_world != NULL); 1451 1452 m_world->registerIssueGenerator(new Model::MissingEntityClassnameIssueGenerator()); 1453 m_world->registerIssueGenerator(new Model::MissingEntityDefinitionIssueGenerator()); 1454 m_world->registerIssueGenerator(new Model::EmptyBrushEntityIssueGenerator()); 1455 m_world->registerIssueGenerator(new Model::PointEntityWithBrushesIssueGenerator()); 1456 m_world->registerIssueGenerator(new Model::EntityLinkSourceIssueGenerator()); 1457 m_world->registerIssueGenerator(new Model::EntityLinkTargetIssueGenerator()); 1458 m_world->registerIssueGenerator(new Model::NonIntegerPlanePointsIssueGenerator()); 1459 m_world->registerIssueGenerator(new Model::NonIntegerVerticesIssueGenerator()); 1460 m_world->registerIssueGenerator(new Model::MixedBrushContentsIssueGenerator()); 1461 m_world->registerIssueGenerator(new Model::WorldBoundsIssueGenerator(m_worldBounds)); 1462 } 1463 filename() const1464 const String MapDocument::filename() const { 1465 if (m_path.isEmpty()) 1466 return EmptyString; 1467 return m_path.lastComponent().asString(); 1468 } 1469 path() const1470 const IO::Path& MapDocument::path() const { 1471 return m_path; 1472 } 1473 setPath(const IO::Path & path)1474 void MapDocument::setPath(const IO::Path& path) { 1475 m_path = path; 1476 } 1477 modified() const1478 bool MapDocument::modified() const { 1479 return m_modificationCount != m_lastSaveModificationCount; 1480 } 1481 modificationCount() const1482 size_t MapDocument::modificationCount() const { 1483 return m_modificationCount; 1484 } 1485 setLastSaveModificationCount()1486 void MapDocument::setLastSaveModificationCount() { 1487 m_lastSaveModificationCount = m_modificationCount; 1488 documentModificationStateDidChangeNotifier(); 1489 } 1490 clearModificationCount()1491 void MapDocument::clearModificationCount() { 1492 m_lastSaveModificationCount = m_modificationCount = 0; 1493 documentModificationStateDidChangeNotifier(); 1494 } 1495 bindObservers()1496 void MapDocument::bindObservers() { 1497 PreferenceManager& prefs = PreferenceManager::instance(); 1498 prefs.preferenceDidChangeNotifier.addObserver(this, &MapDocument::preferenceDidChange); 1499 m_editorContext->editorContextDidChangeNotifier.addObserver(editorContextDidChangeNotifier); 1500 m_mapViewConfig->mapViewConfigDidChangeNotifier.addObserver(mapViewConfigDidChangeNotifier); 1501 commandDoneNotifier.addObserver(this, &MapDocument::commandDone); 1502 commandUndoneNotifier.addObserver(this, &MapDocument::commandUndone); 1503 } 1504 unbindObservers()1505 void MapDocument::unbindObservers() { 1506 PreferenceManager& prefs = PreferenceManager::instance(); 1507 prefs.preferenceDidChangeNotifier.removeObserver(this, &MapDocument::preferenceDidChange); 1508 m_editorContext->editorContextDidChangeNotifier.removeObserver(editorContextDidChangeNotifier); 1509 m_mapViewConfig->mapViewConfigDidChangeNotifier.removeObserver(mapViewConfigDidChangeNotifier); 1510 commandDoneNotifier.removeObserver(this, &MapDocument::commandDone); 1511 commandUndoneNotifier.removeObserver(this, &MapDocument::commandUndone); 1512 } 1513 preferenceDidChange(const IO::Path & path)1514 void MapDocument::preferenceDidChange(const IO::Path& path) { 1515 if (isGamePathPreference(path)) { 1516 const Model::GameFactory& gameFactory = Model::GameFactory::instance(); 1517 const IO::Path newGamePath = gameFactory.gamePath(m_game->gameName()); 1518 m_game->setGamePath(newGamePath); 1519 1520 clearEntityModels(); 1521 setEntityModels(); 1522 1523 unsetTextures(); 1524 loadBuiltinTextures(); 1525 setTextures(); 1526 1527 //reloadIssues(); 1528 } else if (path == Preferences::TextureMinFilter.path() || 1529 path == Preferences::TextureMagFilter.path()) { 1530 m_entityModelManager->setTextureMode(pref(Preferences::TextureMinFilter), pref(Preferences::TextureMagFilter)); 1531 m_textureManager->setTextureMode(pref(Preferences::TextureMinFilter), pref(Preferences::TextureMagFilter)); 1532 } 1533 } 1534 commandDone(Command::Ptr command)1535 void MapDocument::commandDone(Command::Ptr command) { 1536 debug("Command '%s' executed", command->name().c_str()); 1537 } 1538 commandUndone(UndoableCommand::Ptr command)1539 void MapDocument::commandUndone(UndoableCommand::Ptr command) { 1540 debug("Command '%s' undone", command->name().c_str()); 1541 } 1542 Transaction(MapDocumentWPtr document,const String & name)1543 Transaction::Transaction(MapDocumentWPtr document, const String& name) : 1544 m_document(lock(document).get()), 1545 m_cancelled(false) { 1546 begin(name); 1547 } 1548 Transaction(MapDocumentSPtr document,const String & name)1549 Transaction::Transaction(MapDocumentSPtr document, const String& name) : 1550 m_document(document.get()), 1551 m_cancelled(false) { 1552 begin(name); 1553 } 1554 Transaction(MapDocument * document,const String & name)1555 Transaction::Transaction(MapDocument* document, const String& name) : 1556 m_document(document), 1557 m_cancelled(false) { 1558 begin(name); 1559 } 1560 ~Transaction()1561 Transaction::~Transaction() { 1562 if (!m_cancelled) 1563 commit(); 1564 } 1565 rollback()1566 void Transaction::rollback() { 1567 m_document->rollbackTransaction(); 1568 } 1569 cancel()1570 void Transaction::cancel() { 1571 m_document->cancelTransaction(); 1572 m_cancelled = true; 1573 } 1574 begin(const String & name)1575 void Transaction::begin(const String& name) { 1576 m_document->beginTransaction(name); 1577 } 1578 commit()1579 void Transaction::commit() { 1580 m_document->commitTransaction(); 1581 } 1582 } 1583 } 1584