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 "CreateEntityTool.h"
21 
22 #include "PreferenceManager.h"
23 #include "Preferences.h"
24 #include "Assets/EntityDefinition.h"
25 #include "Assets/EntityDefinitionManager.h"
26 #include "Assets/EntityModelManager.h"
27 #include "Assets/ModelDefinition.h"
28 #include "Model/Brush.h"
29 #include "Model/BrushFace.h"
30 #include "Model/Entity.h"
31 #include "Model/HitAdapter.h"
32 #include "Model/Hit.h"
33 #include "Model/HitQuery.h"
34 #include "Model/Layer.h"
35 #include "Model/PickResult.h"
36 #include "Model/World.h"
37 #include "Renderer/Camera.h"
38 #include "View/Grid.h"
39 #include "View/MapDocument.h"
40 
41 #include <cassert>
42 
43 namespace TrenchBroom {
44     namespace View {
CreateEntityTool(MapDocumentWPtr document)45         CreateEntityTool::CreateEntityTool(MapDocumentWPtr document) :
46         Tool(true),
47         m_document(document),
48         m_entity(NULL) {}
49 
createEntity(const String & classname)50         bool CreateEntityTool::createEntity(const String& classname) {
51             MapDocumentSPtr document = lock(m_document);
52             const Assets::EntityDefinitionManager& definitionManager = document->entityDefinitionManager();
53             Assets::EntityDefinition* definition = definitionManager.definition(classname);
54             if (definition == NULL)
55                 return false;
56 
57             if (definition->type() != Assets::EntityDefinition::Type_PointEntity)
58                 return false;
59 
60             const Model::World* world = document->world();
61             m_entity = world->createEntity();
62             m_entity->addOrUpdateAttribute(Model::AttributeNames::Classname, definition->name());
63 
64             m_referenceBounds = document->referenceBounds();
65 
66             document->beginTransaction("Create '" + definition->name() + "'");
67             document->deselectAll();
68             document->addNode(m_entity, document->currentParent());
69             document->select(m_entity);
70 
71             return true;
72         }
73 
removeEntity()74         void CreateEntityTool::removeEntity() {
75             assert(m_entity != NULL);
76             MapDocumentSPtr document = lock(m_document);
77             document->cancelTransaction();
78             m_entity = NULL;
79         }
80 
commitEntity()81         void CreateEntityTool::commitEntity() {
82             assert(m_entity != NULL);
83             MapDocumentSPtr document = lock(m_document);
84             document->commitTransaction();
85             m_entity = NULL;
86         }
87 
updateEntityPosition2D(const Ray3 & pickRay)88         void CreateEntityTool::updateEntityPosition2D(const Ray3& pickRay) {
89             assert(m_entity != NULL);
90 
91             MapDocumentSPtr document = lock(m_document);
92 
93             const Vec3 toMin = m_referenceBounds.min - pickRay.origin;
94             const Vec3 toMax = m_referenceBounds.max - pickRay.origin;
95             const Vec3 anchor = toMin.dot(pickRay.direction) > toMax.dot(pickRay.direction) ? m_referenceBounds.min : m_referenceBounds.max;
96             const Plane3 dragPlane(anchor, -pickRay.direction);
97 
98             const FloatType distance = dragPlane.intersectWithRay(pickRay);
99             if (Math::isnan(distance))
100                 return;
101 
102             const Vec3 hitPoint = pickRay.pointAtDistance(distance);
103 
104             const Grid& grid = document->grid();
105             const Vec3 delta = grid.moveDeltaForBounds(dragPlane, m_entity->bounds(), document->worldBounds(), pickRay, hitPoint);
106 
107             if (delta.null())
108                 return;
109 
110             document->translateObjects(delta);
111         }
112 
updateEntityPosition3D(const Ray3 & pickRay,const Model::PickResult & pickResult)113         void CreateEntityTool::updateEntityPosition3D(const Ray3& pickRay, const Model::PickResult& pickResult) {
114             assert(m_entity != NULL);
115 
116             MapDocumentSPtr document = lock(m_document);
117 
118             Vec3 delta;
119             const Grid& grid = document->grid();
120             const Model::Hit& hit = pickResult.query().pickable().type(Model::Brush::BrushHit).occluded().first();
121             if (hit.isMatch()) {
122                 const Model::BrushFace* face = Model::hitToFace(hit);
123                 const Plane3 dragPlane = alignedOrthogonalDragPlane(hit.hitPoint(), face->boundary().normal);
124                 delta = grid.moveDeltaForBounds(dragPlane, m_entity->bounds(), document->worldBounds(), pickRay, hit.hitPoint());
125             } else {
126                 const Vec3 newPosition = pickRay.pointAtDistance(Renderer::Camera::DefaultPointDistance);
127                 const Vec3 center = m_entity->bounds().center();
128                 delta = grid.moveDeltaForPoint(center, document->worldBounds(), newPosition - center);
129             }
130 
131             if (delta.null())
132                 return;
133 
134             document->translateObjects(delta);
135         }
136     }
137 }
138