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 "MapView2D.h"
21 #include "Algorithms.h"
22 #include "Logger.h"
23 #include "Model/Brush.h"
24 #include "Model/BrushBuilder.h"
25 #include "Model/BrushFace.h"
26 #include "Model/CollectContainedNodesVisitor.h"
27 #include "Model/CompareHits.h"
28 #include "Model/Entity.h"
29 #include "Model/HitAdapter.h"
30 #include "Model/HitQuery.h"
31 #include "Model/PickResult.h"
32 #include "Model/PointFile.h"
33 #include "Model/World.h"
34 #include "Renderer/Compass2D.h"
35 #include "Renderer/GridRenderer.h"
36 #include "Renderer/MapRenderer.h"
37 #include "Renderer/RenderContext.h"
38 #include "Renderer/SelectionBoundsRenderer.h"
39 #include "View/ActionManager.h"
40 #include "View/Animation.h"
41 #include "View/CameraAnimation.h"
42 #include "View/CameraLinkHelper.h"
43 #include "View/CameraTool2D.h"
44 #include "View/ClipToolController.h"
45 #include "View/CommandIds.h"
46 #include "View/CreateEntityToolController.h"
47 #include "View/CreateSimpleBrushToolController2D.h"
48 #include "View/FlashSelectionAnimation.h"
49 #include "View/GLContextManager.h"
50 #include "View/Grid.h"
51 #include "View/MapDocument.h"
52 #include "View/MapViewToolBox.h"
53 #include "View/MoveObjectsToolController.h"
54 #include "View/ResizeBrushesToolController.h"
55 #include "View/RotateObjectsToolController.h"
56 #include "View/SelectionTool.h"
57 #include "View/VertexTool.h"
58 #include "View/VertexToolController.h"
59 #include "View/wxUtils.h"
60 
61 namespace TrenchBroom {
62     namespace View {
MapView2D(wxWindow * parent,Logger * logger,MapDocumentWPtr document,MapViewToolBox & toolBox,Renderer::MapRenderer & renderer,GLContextManager & contextManager,const ViewPlane viewPlane)63         MapView2D::MapView2D(wxWindow* parent, Logger* logger, MapDocumentWPtr document, MapViewToolBox& toolBox, Renderer::MapRenderer& renderer, GLContextManager& contextManager, const ViewPlane viewPlane) :
64         MapViewBase(parent, logger, document, toolBox, renderer, contextManager),
65         m_camera(){
66             bindEvents();
67             bindObservers();
68             initializeCamera(viewPlane);
69             initializeToolChain(toolBox);
70             setCompass(new Renderer::Compass2D());
71         }
72 
~MapView2D()73         MapView2D::~MapView2D() {
74             unbindObservers();
75         }
76 
initializeCamera(const ViewPlane viewPlane)77         void MapView2D::initializeCamera(const ViewPlane viewPlane) {
78             switch (viewPlane) {
79                 case MapView2D::ViewPlane_XY:
80                     m_camera.setDirection(Vec3f::NegZ, Vec3f::PosY);
81                     m_camera.moveTo(Vec3f(0.0f, 0.0f, 16384.0f));
82                     break;
83                 case MapView2D::ViewPlane_XZ:
84                     m_camera.setDirection(Vec3f::PosY, Vec3f::PosZ);
85                     m_camera.moveTo(Vec3f(0.0f, -16384.0f, 0.0f));
86                     break;
87                 case MapView2D::ViewPlane_YZ:
88                     m_camera.setDirection(Vec3f::NegX, Vec3f::PosZ);
89                     m_camera.moveTo(Vec3f(16384.0f, 0.0f, 0.0f));
90                     break;
91             }
92             m_camera.setNearPlane(1.0f);
93             m_camera.setFarPlane(32768.0f);
94 
95         }
96 
initializeToolChain(MapViewToolBox & toolBox)97         void MapView2D::initializeToolChain(MapViewToolBox& toolBox) {
98             addTool(new CameraTool2D(m_camera));
99             addTool(new MoveObjectsToolController(toolBox.moveObjectsTool()));
100             addTool(new RotateObjectsToolController2D(toolBox.rotateObjectsTool()));
101             addTool(new ResizeBrushesToolController2D(toolBox.resizeBrushesTool()));
102             addTool(new ClipToolController2D(toolBox.clipTool()));
103             addTool(new VertexToolController(toolBox.vertexTool()));
104             addTool(new CreateEntityToolController2D(toolBox.createEntityTool()));
105             addTool(new SelectionTool(m_document));
106             addTool(new CreateSimpleBrushToolController2D(toolBox.createSimpleBrushTool(), m_document));
107         }
108 
bindObservers()109         void MapView2D::bindObservers() {
110             m_camera.cameraDidChangeNotifier.addObserver(this, &MapView2D::cameraDidChange);
111         }
112 
unbindObservers()113         void MapView2D::unbindObservers() {
114             m_camera.cameraDidChangeNotifier.removeObserver(this, &MapView2D::cameraDidChange);
115         }
116 
cameraDidChange(const Renderer::Camera * camera)117         void MapView2D::cameraDidChange(const Renderer::Camera* camera) {
118             Refresh();
119         }
120 
bindEvents()121         void MapView2D::bindEvents() {
122             /*
123             Bind(wxEVT_KEY_DOWN, &MapView2D::OnKey, this);
124             Bind(wxEVT_KEY_UP, &MapView2D::OnKey, this);
125             */
126 
127             /*
128             Bind(wxEVT_MENU, &MapView2D::OnPopupReparentBrushes,         this, CommandIds::MapViewPopupMenu::ReparentBrushes);
129             Bind(wxEVT_MENU, &MapView2D::OnPopupMoveBrushesToWorld,      this, CommandIds::MapViewPopupMenu::MoveBrushesToWorld);
130             Bind(wxEVT_MENU, &MapView2D::OnPopupCreatePointEntity,       this, CommandIds::MapViewPopupMenu::LowestPointEntityItem, CommandIds::MapViewPopupMenu::HighestPointEntityItem);
131             Bind(wxEVT_MENU, &MapView2D::OnPopupCreateBrushEntity,       this, CommandIds::MapViewPopupMenu::LowestBrushEntityItem, CommandIds::MapViewPopupMenu::HighestBrushEntityItem);
132 
133             Bind(wxEVT_UPDATE_UI, &MapView2D::OnUpdatePopupMenuItem, this, CommandIds::MapViewPopupMenu::ReparentBrushes);
134             Bind(wxEVT_UPDATE_UI, &MapView2D::OnUpdatePopupMenuItem, this, CommandIds::MapViewPopupMenu::MoveBrushesToWorld);
135             Bind(wxEVT_UPDATE_UI, &MapView2D::OnUpdatePopupMenuItem, this, CommandIds::MapViewPopupMenu::LowestPointEntityItem, CommandIds::MapViewPopupMenu::HighestPointEntityItem);
136             Bind(wxEVT_UPDATE_UI, &MapView2D::OnUpdatePopupMenuItem, this, CommandIds::MapViewPopupMenu::LowestBrushEntityItem, CommandIds::MapViewPopupMenu::HighestBrushEntityItem);
137             */
138         }
139 
doGetPickRequest(const int x,const int y) const140         PickRequest MapView2D::doGetPickRequest(const int x, const int y) const {
141             return PickRequest(Ray3(m_camera.pickRay(x, y)), m_camera);
142         }
143 
doPick(const Ray3 & pickRay) const144         Model::PickResult MapView2D::doPick(const Ray3& pickRay) const {
145             MapDocumentSPtr document = lock(m_document);
146             const Model::EditorContext& editorContext = document->editorContext();
147             const Math::Axis::Type axis = pickRay.direction.firstComponent();
148 
149             Model::PickResult pickResult = Model::PickResult::bySize(editorContext, axis);
150             document->pick(pickRay, pickResult);
151 
152             return pickResult;
153         }
154 
doUpdateViewport(const int x,const int y,const int width,const int height)155         void MapView2D::doUpdateViewport(const int x, const int y, const int width, const int height) {
156             m_camera.setViewport(Renderer::Camera::Viewport(x, y, width, height));
157         }
158 
doGetPasteObjectsDelta(const BBox3 & bounds,const BBox3 & referenceBounds) const159         Vec3 MapView2D::doGetPasteObjectsDelta(const BBox3& bounds, const BBox3& referenceBounds) const {
160             MapDocumentSPtr document = lock(m_document);
161             View::Grid& grid = document->grid();
162             const BBox3& worldBounds = document->worldBounds();
163 
164             const Ray3& pickRay = MapView2D::pickRay();
165 
166             const Vec3 toMin = referenceBounds.min - pickRay.origin;
167             const Vec3 toMax = referenceBounds.max - pickRay.origin;
168             const Vec3 anchor = toMin.dot(pickRay.direction) > toMax.dot(pickRay.direction) ? referenceBounds.min : referenceBounds.max;
169             const Plane3 dragPlane(anchor, -pickRay.direction);
170 
171             const FloatType distance = dragPlane.intersectWithRay(pickRay);
172             if (Math::isnan(distance))
173                 return Vec3::Null;
174 
175             const Vec3 hitPoint = pickRay.pointAtDistance(distance);
176             return grid.moveDeltaForBounds(dragPlane, bounds, worldBounds, pickRay, hitPoint);
177         }
178 
doCanSelectTall()179         bool MapView2D::doCanSelectTall() {
180             return true;
181         }
182 
doSelectTall()183         void MapView2D::doSelectTall() {
184             const MapDocumentSPtr document = lock(m_document);
185             const BBox3& worldBounds = document->worldBounds();
186 
187             const FloatType min = worldBounds.min.dot(m_camera.direction());
188             const FloatType max = worldBounds.max.dot(m_camera.direction());
189 
190             const Plane3 minPlane(min, Vec3(m_camera.direction()));
191             const Plane3 maxPlane(max, Vec3(m_camera.direction()));
192 
193             const Model::BrushList& selectionBrushes = document->selectedNodes().brushes();
194             assert(!selectionBrushes.empty());
195 
196             const Model::BrushBuilder brushBuilder(document->world(), worldBounds);
197             Model::BrushList tallBrushes(0);
198             tallBrushes.reserve(selectionBrushes.size());
199 
200             Model::BrushList::const_iterator sIt, sEnd;
201             for (sIt = selectionBrushes.begin(), sEnd = selectionBrushes.end(); sIt != sEnd; ++sIt) {
202                 const Model::Brush* selectionBrush = *sIt;
203                 const Model::Brush::VertexList& vertices = selectionBrush->vertices();
204 
205                 Vec3::List tallVertices(0);
206                 tallVertices.reserve(2 * vertices.size());
207 
208                 Model::Brush::VertexList::const_iterator vIt, vEnd;
209                 for (vIt = vertices.begin(), vEnd = vertices.end(); vIt != vEnd; ++vIt) {
210                     const Model::BrushVertex* vertex = *vIt;
211                     tallVertices.push_back(minPlane.project(vertex->position()));
212                     tallVertices.push_back(maxPlane.project(vertex->position()));
213                 }
214 
215                 Model::Brush* tallBrush = brushBuilder.createBrush(tallVertices, Model::BrushFace::NoTextureName);
216                 tallBrushes.push_back(tallBrush);
217             }
218 
219             Transaction transaction(document, "Select Tall");
220             document->deleteObjects();
221 
222             const Model::NodeList nodes = Model::collectMatchingNodes<Model::CollectContainedNodesVisitor>(tallBrushes.begin(), tallBrushes.end(), document->world());
223             document->select(nodes);
224 
225             VectorUtils::clearAndDelete(tallBrushes);
226         }
227 
doFocusCameraOnSelection(const bool animate)228         void MapView2D::doFocusCameraOnSelection(const bool animate) {
229             const MapDocumentSPtr document = lock(m_document);
230             if (document->selectedNodes().empty()) {
231                 const BBox3& bounds = document->selectionBounds();
232                 moveCameraToPosition(bounds.center(), animate);
233             }
234         }
235 
doMoveCameraToPosition(const Vec3 & position,const bool animate)236         void MapView2D::doMoveCameraToPosition(const Vec3& position, const bool animate) {
237             if (animate)
238                 animateCamera(Vec3f(position), m_camera.direction(), m_camera.up());
239             else
240                 m_camera.moveTo(position);
241         }
242 
animateCamera(const Vec3f & position,const Vec3f & direction,const Vec3f & up,const wxLongLong duration)243         void MapView2D::animateCamera(const Vec3f& position, const Vec3f& direction, const Vec3f& up, const wxLongLong duration) {
244             const Vec3f actualPosition = position.dot(m_camera.up()) * m_camera.up() + position.dot(m_camera.right()) * m_camera.right() + m_camera.position().dot(m_camera.direction()) * m_camera.direction();
245             CameraAnimation* animation = new CameraAnimation(m_camera, actualPosition, m_camera.direction(), m_camera.up(), duration);
246             m_animationManager->runAnimation(animation, true);
247         }
248 
doMoveCameraToCurrentTracePoint()249         void MapView2D::doMoveCameraToCurrentTracePoint() {
250             MapDocumentSPtr document = lock(m_document);
251 
252             assert(document->isPointFileLoaded());
253             Model::PointFile* pointFile = document->pointFile();
254             assert(pointFile->hasNextPoint());
255 
256             const Vec3f position = pointFile->currentPoint();
257             moveCameraToPosition(position, true);
258         }
259 
doGetMoveDirection(const Math::Direction direction) const260         Vec3 MapView2D::doGetMoveDirection(const Math::Direction direction) const {
261             switch (direction) {
262                 case Math::Direction_Forward:
263                     return m_camera.direction().firstAxis();
264                 case Math::Direction_Backward:
265                     return -m_camera.direction().firstAxis();
266                 case Math::Direction_Left:
267                     return -m_camera.right().firstAxis();
268                 case Math::Direction_Right:
269                     return m_camera.right().firstAxis();
270                 case Math::Direction_Up:
271                     return m_camera.up().firstAxis();
272                 case Math::Direction_Down:
273                     return -m_camera.up().firstAxis();
274                 switchDefault()
275             }
276         }
277 
doComputePointEntityPosition(const BBox3 & bounds) const278         Vec3 MapView2D::doComputePointEntityPosition(const BBox3& bounds) const {
279             MapDocumentSPtr document = lock(m_document);
280 
281             Vec3 delta;
282             View::Grid& grid = document->grid();
283 
284             const BBox3& worldBounds = document->worldBounds();
285 
286             const Model::Hit& hit = pickResult().query().pickable().type(Model::Brush::BrushHit).occluded().selected().first();
287             if (hit.isMatch()) {
288                 const Model::BrushFace* face = Model::hitToFace(hit);
289                 return grid.moveDeltaForBounds(face->boundary(), bounds, worldBounds, pickRay(), hit.hitPoint());
290             } else {
291                 const BBox3 referenceBounds = document->referenceBounds();
292                 const Ray3& pickRay = MapView2D::pickRay();
293 
294                 const Vec3 toMin = referenceBounds.min - pickRay.origin;
295                 const Vec3 toMax = referenceBounds.max - pickRay.origin;
296                 const Vec3 anchor = toMin.dot(pickRay.direction) > toMax.dot(pickRay.direction) ? referenceBounds.min : referenceBounds.max;
297                 const Plane3 dragPlane(anchor, -pickRay.direction);
298 
299                 const FloatType distance = dragPlane.intersectWithRay(pickRay);
300                 if (Math::isnan(distance))
301                     return Vec3::Null;
302 
303                 const Vec3 hitPoint = pickRay.pointAtDistance(distance);
304                 return grid.moveDeltaForBounds(dragPlane, bounds, worldBounds, pickRay, hitPoint);
305             }
306         }
307 
doGetActionContext() const308         ActionContext MapView2D::doGetActionContext() const {
309             return ActionContext_Default;
310         }
311 
doCreateAccelerationTable(ActionContext context) const312         wxAcceleratorTable MapView2D::doCreateAccelerationTable(ActionContext context) const {
313             ActionManager& actionManager = ActionManager::instance();
314             return actionManager.createViewAcceleratorTable(context, ActionView_Map2D);
315         }
316 
doCancel()317         bool MapView2D::doCancel() {
318             return false;
319         }
320 
doCreateRenderContext()321         Renderer::RenderContext MapView2D::doCreateRenderContext() {
322             return Renderer::RenderContext(Renderer::RenderContext::RenderMode_2D, m_camera, fontManager(), shaderManager());
323         }
324 
doRenderGrid(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)325         void MapView2D::doRenderGrid(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
326             MapDocumentSPtr document = lock(m_document);
327             renderBatch.addOneShot(new Renderer::GridRenderer(m_camera, document->worldBounds()));
328         }
329 
doRenderMap(Renderer::MapRenderer & renderer,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)330         void MapView2D::doRenderMap(Renderer::MapRenderer& renderer, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
331             renderer.render(renderContext, renderBatch);
332 
333             MapDocumentSPtr document = lock(m_document);
334             if (renderContext.showSelectionGuide() && document->hasSelectedNodes()) {
335                 const BBox3& bounds = document->selectionBounds();
336                 Renderer::SelectionBoundsRenderer boundsRenderer(bounds);
337                 boundsRenderer.render(renderContext, renderBatch);
338             }
339         }
340 
doRenderTools(MapViewToolBox & toolBox,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)341         void MapView2D::doRenderTools(MapViewToolBox& toolBox, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
342             renderTools(renderContext, renderBatch);
343         }
344 
doRenderExtras(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)345         void MapView2D::doRenderExtras(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {}
346 
doLinkCamera(CameraLinkHelper & helper)347         void MapView2D::doLinkCamera(CameraLinkHelper& helper) {
348             helper.addCamera(&m_camera);
349         }
350     }
351 }
352