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