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 "MapView3D.h"
21 #include "Logger.h"
22 #include "PreferenceManager.h"
23 #include "Preferences.h"
24 #include "Model/Brush.h"
25 #include "Model/BrushFace.h"
26 #include "Model/BrushGeometry.h"
27 #include "Model/Entity.h"
28 #include "Model/HitAdapter.h"
29 #include "Model/HitQuery.h"
30 #include "Model/PickResult.h"
31 #include "Model/PointFile.h"
32 #include "Renderer/BoundsGuideRenderer.h"
33 #include "Renderer/Compass3D.h"
34 #include "Renderer/MapRenderer.h"
35 #include "Renderer/RenderBatch.h"
36 #include "Renderer/RenderContext.h"
37 #include "Renderer/SelectionBoundsRenderer.h"
38 #include "View/ActionManager.h"
39 #include "View/Animation.h"
40 #include "View/CameraAnimation.h"
41 #include "View/CameraTool3D.h"
42 #include "View/ClipToolController.h"
43 #include "View/CommandIds.h"
44 #include "View/CreateComplexBrushToolController3D.h"
45 #include "View/CreateEntityToolController.h"
46 #include "View/CreateSimpleBrushToolController3D.h"
47 #include "View/FlashSelectionAnimation.h"
48 #include "View/FlyModeHelper.h"
49 #include "View/GLContextManager.h"
50 #include "View/Grid.h"
51 #include "View/InputState.h"
52 #include "View/MapDocument.h"
53 #include "View/MapViewToolBox.h"
54 #include "View/MoveObjectsToolController.h"
55 #include "View/ResizeBrushesToolController.h"
56 #include "View/RotateObjectsToolController.h"
57 #include "View/SelectionTool.h"
58 #include "View/SetBrushFaceAttributesTool.h"
59 #include "View/VertexTool.h"
60 #include "View/VertexToolController.h"
61 #include "View/wxUtils.h"
62 
63 namespace TrenchBroom {
64     namespace View {
MapView3D(wxWindow * parent,Logger * logger,MapDocumentWPtr document,MapViewToolBox & toolBox,Renderer::MapRenderer & renderer,GLContextManager & contextManager)65         MapView3D::MapView3D(wxWindow* parent, Logger* logger, MapDocumentWPtr document, MapViewToolBox& toolBox, Renderer::MapRenderer& renderer, GLContextManager& contextManager) :
66         MapViewBase(parent, logger, document, toolBox, renderer, contextManager),
67         m_flyModeHelper(new FlyModeHelper(this, m_camera)) {
68             bindEvents();
69             bindObservers();
70             initializeCamera();
71             initializeToolChain(toolBox);
72             setCompass(new Renderer::Compass3D());
73         }
74 
~MapView3D()75         MapView3D::~MapView3D() {
76             m_flyModeHelper->Delete();
77             unbindObservers();
78         }
79 
initializeCamera()80         void MapView3D::initializeCamera() {
81             m_camera.moveTo(Vec3f(-80.0f, -128.0f, 96.0f));
82             m_camera.lookAt(Vec3::Null, Vec3::PosZ);
83         }
84 
initializeToolChain(MapViewToolBox & toolBox)85         void MapView3D::initializeToolChain(MapViewToolBox& toolBox) {
86             addTool(new CameraTool3D(m_document, m_camera));
87             addTool(new MoveObjectsToolController(toolBox.moveObjectsTool()));
88             addTool(new RotateObjectsToolController3D(toolBox.rotateObjectsTool()));
89             addTool(new ResizeBrushesToolController3D(toolBox.resizeBrushesTool()));
90             addTool(new CreateComplexBrushToolController3D(toolBox.createComplexBrushTool()));
91             addTool(new ClipToolController3D(toolBox.clipTool()));
92             addTool(new VertexToolController(toolBox.vertexTool()));
93             addTool(new CreateEntityToolController3D(toolBox.createEntityTool()));
94             addTool(new SetBrushFaceAttributesTool(m_document));
95             addTool(new SelectionTool(m_document));
96             addTool(new CreateSimpleBrushToolController3D(toolBox.createSimpleBrushTool(), m_document));
97         }
98 
cameraFlyModeActive() const99         bool MapView3D::cameraFlyModeActive() const {
100             return m_flyModeHelper->enabled();
101         }
102 
toggleCameraFlyMode()103         void MapView3D::toggleCameraFlyMode() {
104             if (!cameraFlyModeActive()) {
105                 m_toolBox.disable();
106                 m_flyModeHelper->enable();
107             } else {
108                 m_flyModeHelper->disable();
109                 m_toolBox.enable();
110             }
111             updateAcceleratorTable();
112             Refresh();
113         }
114 
bindObservers()115         void MapView3D::bindObservers() {
116             m_camera.cameraDidChangeNotifier.addObserver(this, &MapView3D::cameraDidChange);
117         }
118 
unbindObservers()119         void MapView3D::unbindObservers() {
120             m_camera.cameraDidChangeNotifier.removeObserver(this, &MapView3D::cameraDidChange);
121         }
122 
cameraDidChange(const Renderer::Camera * camera)123         void MapView3D::cameraDidChange(const Renderer::Camera* camera) {
124             Refresh();
125         }
126 
bindEvents()127         void MapView3D::bindEvents() {
128             Bind(wxEVT_KEY_DOWN, &MapView3D::OnKeyDown, this);
129             Bind(wxEVT_KEY_UP, &MapView3D::OnKeyUp, this);
130             Bind(wxEVT_MOTION, &MapView3D::OnMouseMotion, this);
131 
132             Bind(wxEVT_KILL_FOCUS, &MapView3D::OnKillFocus, this);
133 
134             Bind(wxEVT_MENU, &MapView3D::OnPerformCreateBrush,           this, CommandIds::Actions::PerformCreateBrush);
135 
136             Bind(wxEVT_MENU, &MapView3D::OnMoveTexturesUp,               this, CommandIds::Actions::MoveTexturesUp);
137             Bind(wxEVT_MENU, &MapView3D::OnMoveTexturesDown,             this, CommandIds::Actions::MoveTexturesDown);
138             Bind(wxEVT_MENU, &MapView3D::OnMoveTexturesLeft,             this, CommandIds::Actions::MoveTexturesLeft);
139             Bind(wxEVT_MENU, &MapView3D::OnMoveTexturesRight,            this, CommandIds::Actions::MoveTexturesRight);
140 
141             Bind(wxEVT_MENU, &MapView3D::OnRotateTexturesCW,             this, CommandIds::Actions::RotateTexturesCW);
142             Bind(wxEVT_MENU, &MapView3D::OnRotateTexturesCCW,            this, CommandIds::Actions::RotateTexturesCCW);
143 
144             Bind(wxEVT_MENU, &MapView3D::OnToggleFlyMode,                this, CommandIds::Actions::ToggleFlyMode);
145 
146             wxFrame* frame = findFrame(this);
147             frame->Bind(wxEVT_ACTIVATE, &MapView3D::OnActivateFrame, this);
148         }
149 
OnKeyDown(wxKeyEvent & event)150         void MapView3D::OnKeyDown(wxKeyEvent& event) {
151             if (IsBeingDeleted()) return;
152 
153             if (!m_flyModeHelper->keyDown(event))
154                 event.Skip();
155         }
156 
OnKeyUp(wxKeyEvent & event)157         void MapView3D::OnKeyUp(wxKeyEvent& event) {
158             if (IsBeingDeleted()) return;
159 
160             if (!m_flyModeHelper->keyUp(event))
161                 event.Skip();
162         }
163 
OnMouseMotion(wxMouseEvent & event)164         void MapView3D::OnMouseMotion(wxMouseEvent& event) {
165             if (IsBeingDeleted()) return;
166 
167             m_flyModeHelper->motion(event);
168             event.Skip();
169         }
170 
OnPerformCreateBrush(wxCommandEvent & event)171         void MapView3D::OnPerformCreateBrush(wxCommandEvent& event) {
172             if (IsBeingDeleted()) return;
173 
174             if (m_toolBox.createComplexBrushToolActive())
175                 m_toolBox.performCreateComplexBrush();
176         }
177 
OnMoveTexturesUp(wxCommandEvent & event)178         void MapView3D::OnMoveTexturesUp(wxCommandEvent& event) {
179             if (IsBeingDeleted()) return;
180 
181             moveTextures(Vec2f(0.0f, moveTextureDistance()));
182         }
183 
OnMoveTexturesDown(wxCommandEvent & event)184         void MapView3D::OnMoveTexturesDown(wxCommandEvent& event) {
185             if (IsBeingDeleted()) return;
186 
187             moveTextures(Vec2f(0.0f, -moveTextureDistance()));
188         }
189 
OnMoveTexturesLeft(wxCommandEvent & event)190         void MapView3D::OnMoveTexturesLeft(wxCommandEvent& event) {
191             if (IsBeingDeleted()) return;
192 
193             moveTextures(Vec2f(-moveTextureDistance(), 0.0f));
194         }
195 
OnMoveTexturesRight(wxCommandEvent & event)196         void MapView3D::OnMoveTexturesRight(wxCommandEvent& event) {
197             if (IsBeingDeleted()) return;
198 
199             moveTextures(Vec2f(moveTextureDistance(), 0.0f));
200         }
201 
OnRotateTexturesCW(wxCommandEvent & event)202         void MapView3D::OnRotateTexturesCW(wxCommandEvent& event) {
203             if (IsBeingDeleted()) return;
204 
205             rotateTextures(rotateTextureAngle(true));
206         }
207 
OnRotateTexturesCCW(wxCommandEvent & event)208         void MapView3D::OnRotateTexturesCCW(wxCommandEvent& event) {
209             if (IsBeingDeleted()) return;
210 
211             rotateTextures(rotateTextureAngle(false));
212         }
213 
moveTextureDistance() const214         float MapView3D::moveTextureDistance() const {
215             const Grid& grid = lock(m_document)->grid();
216             const float gridSize = static_cast<float>(grid.actualSize());
217 
218             const wxMouseState mouseState = wxGetMouseState();
219             switch (mouseState.GetModifiers()) {
220                 case wxMOD_CMD:
221                     return 1.0f;
222                 case wxMOD_SHIFT:
223                     return 2.0f * gridSize;
224                 default:
225                     return gridSize;
226             }
227         }
228 
moveTextures(const Vec2f & offset)229         void MapView3D::moveTextures(const Vec2f& offset) {
230             MapDocumentSPtr document = lock(m_document);
231             if (document->hasSelectedBrushFaces())
232                 document->moveTextures(m_camera.up(), m_camera.right(), offset);
233         }
234 
rotateTextureAngle(const bool clockwise) const235         float MapView3D::rotateTextureAngle(const bool clockwise) const {
236             const Grid& grid = lock(m_document)->grid();
237             const float gridAngle = static_cast<float>(Math::degrees(grid.angle()));
238             float angle = 0.0f;
239 
240             const wxMouseState mouseState = wxGetMouseState();
241             switch (mouseState.GetModifiers()) {
242                 case wxMOD_CMD:
243                     angle = 1.0f;
244                     break;
245                 case wxMOD_SHIFT:
246                     angle = 90.0f;
247                     break;
248                 default:
249                     angle = gridAngle;
250                     break;
251             }
252 
253             return clockwise ? angle : -angle;
254         }
255 
rotateTextures(const float angle)256         void MapView3D::rotateTextures(const float angle) {
257             MapDocumentSPtr document = lock(m_document);
258             if (document->hasSelectedBrushFaces())
259                 document->rotateTextures(angle);
260         }
261 
OnToggleFlyMode(wxCommandEvent & event)262         void MapView3D::OnToggleFlyMode(wxCommandEvent& event) {
263             if (IsBeingDeleted()) return;
264 
265             toggleCameraFlyMode();
266         }
267 
OnKillFocus(wxFocusEvent & event)268         void MapView3D::OnKillFocus(wxFocusEvent& event) {
269             if (IsBeingDeleted()) return;
270 
271             if (cameraFlyModeActive())
272                 toggleCameraFlyMode();
273             event.Skip();
274         }
275 
OnActivateFrame(wxActivateEvent & event)276         void MapView3D::OnActivateFrame(wxActivateEvent& event) {
277             if (IsBeingDeleted()) return;
278 
279             if (cameraFlyModeActive())
280                 toggleCameraFlyMode();
281             event.Skip();
282         }
283 
doGetPickRequest(const int x,const int y) const284         PickRequest MapView3D::doGetPickRequest(const int x, const int y) const {
285             return PickRequest(Ray3(m_camera.pickRay(x, y)), m_camera);
286         }
287 
doPick(const Ray3 & pickRay) const288         Model::PickResult MapView3D::doPick(const Ray3& pickRay) const {
289             MapDocumentSPtr document = lock(m_document);
290             const Model::EditorContext& editorContext = document->editorContext();
291             Model::PickResult pickResult = Model::PickResult::byDistance(editorContext);
292 
293             document->pick(pickRay, pickResult);
294             return pickResult;
295         }
296 
doUpdateViewport(const int x,const int y,const int width,const int height)297         void MapView3D::doUpdateViewport(const int x, const int y, const int width, const int height) {
298             m_camera.setViewport(Renderer::Camera::Viewport(x, y, width, height));
299         }
300 
doGetPasteObjectsDelta(const BBox3 & bounds,const BBox3 & referenceBounds) const301         Vec3 MapView3D::doGetPasteObjectsDelta(const BBox3& bounds, const BBox3& referenceBounds) const {
302             MapDocumentSPtr document = lock(m_document);
303             const Grid& grid = document->grid();
304 
305             const wxMouseState mouseState = wxGetMouseState();
306             const wxPoint clientCoords = ScreenToClient(mouseState.GetPosition());
307 
308             if (HitTest(clientCoords) == wxHT_WINDOW_INSIDE) {
309                 const Ray3f pickRay = m_camera.pickRay(clientCoords.x, clientCoords.y);
310 
311                 const Model::EditorContext& editorContext = document->editorContext();
312                 Model::PickResult pickResult = Model::PickResult::byDistance(editorContext);
313 
314                 document->pick(Ray3(pickRay), pickResult);
315                 const Model::Hit& hit = pickResult.query().pickable().type(Model::Brush::BrushHit).first();
316 
317                 if (hit.isMatch()) {
318                     const Model::BrushFace* face = Model::hitToFace(hit);
319                     const Plane3 dragPlane = alignedOrthogonalDragPlane(hit.hitPoint(), face->boundary().normal);
320                     return grid.moveDeltaForBounds(dragPlane, bounds, document->worldBounds(), pickRay, hit.hitPoint());
321                 } else {
322                     const Vec3 point = m_camera.defaultPoint(pickRay);
323                     const Plane3 dragPlane = alignedOrthogonalDragPlane(point, -Vec3(m_camera.direction()));
324                     return grid.moveDeltaForBounds(dragPlane, bounds, document->worldBounds(), pickRay, point);
325                 }
326             } else {
327                 const Vec3 oldMin = bounds.min;
328                 const Vec3 oldCenter = bounds.center();
329                 const Vec3 newCenter = m_camera.defaultPoint();
330                 const Vec3 newMin = oldMin + (newCenter - oldCenter);
331                 return grid.snap(newMin);
332             }
333         }
334 
doCanSelectTall()335         bool MapView3D::doCanSelectTall() {
336             return false;
337         }
338 
doSelectTall()339         void MapView3D::doSelectTall() {}
340 
doFocusCameraOnSelection(const bool animate)341         void MapView3D::doFocusCameraOnSelection(const bool animate) {
342             MapDocumentSPtr document = lock(m_document);
343             const Model::NodeList& nodes = document->selectedNodes().nodes();
344             if (!nodes.empty()) {
345                 const Vec3 newPosition = focusCameraOnObjectsPosition(nodes);
346                 moveCameraToPosition(newPosition, animate);
347             }
348         }
349 
350         class MapView3D::ComputeCameraCenterPositionVisitor : public Model::ConstNodeVisitor {
351         private:
352             const Vec3 m_cameraPosition;
353             const Vec3 m_cameraDirection;
354             FloatType m_minDist;
355             Vec3 m_center;
356             size_t m_count;
357         public:
ComputeCameraCenterPositionVisitor(const Vec3 & cameraPosition,const Vec3 & cameraDirection)358             ComputeCameraCenterPositionVisitor(const Vec3& cameraPosition, const Vec3& cameraDirection) :
359             m_cameraPosition(cameraPosition),
360             m_cameraDirection(cameraDirection),
361             m_minDist(std::numeric_limits<FloatType>::max()),
362             m_count(0) {}
363 
position() const364             Vec3 position() const {
365                 return m_center / static_cast<FloatType>(m_count);
366             }
367         private:
doVisit(const Model::World * world)368             void doVisit(const Model::World* world)   {}
doVisit(const Model::Layer * layer)369             void doVisit(const Model::Layer* layer)   {}
doVisit(const Model::Group * group)370             void doVisit(const Model::Group* group)   {}
371 
doVisit(const Model::Entity * entity)372             void doVisit(const Model::Entity* entity) {
373                 if (!entity->hasChildren()) {
374                     const Vec3::List vertices = bBoxVertices(entity->bounds());
375                     for (size_t i = 0; i < vertices.size(); ++i)
376                         addPoint(vertices[i]);
377                 }
378             }
379 
doVisit(const Model::Brush * brush)380             void doVisit(const Model::Brush* brush)   {
381                 const Model::Brush::VertexList vertices = brush->vertices();
382                 Model::Brush::VertexList::const_iterator it, end;
383                 for (it = vertices.begin(), end = vertices.end(); it != end; ++it)
384                     addPoint((*it)->position());
385             }
386 
addPoint(const Vec3 & point)387             void addPoint(const Vec3& point) {
388                 const Vec3 toPosition = point - m_cameraPosition;
389                 m_minDist = std::min(m_minDist, toPosition.dot(m_cameraDirection));
390                 m_center += point;
391                 ++m_count;
392             }
393         };
394 
395         class MapView3D::ComputeCameraCenterOffsetVisitor : public Model::ConstNodeVisitor {
396         private:
397             const Vec3f m_cameraPosition;
398             const Vec3f m_cameraDirection;
399             Plane3f m_frustumPlanes[4];
400             float m_offset;
401         public:
ComputeCameraCenterOffsetVisitor(const Vec3f & cameraPosition,const Vec3f & cameraDirection,const Plane3f frustumPlanes[4])402             ComputeCameraCenterOffsetVisitor(const Vec3f& cameraPosition, const Vec3f& cameraDirection, const Plane3f frustumPlanes[4]) :
403             m_cameraPosition(cameraPosition),
404             m_cameraDirection(cameraDirection),
405             m_offset(std::numeric_limits<float>::min()) {
406                 for (size_t i = 0; i < 4; ++i)
407                     m_frustumPlanes[i] = frustumPlanes[i];
408             }
409 
offset() const410             float offset() const {
411                 return m_offset;
412             }
413         private:
doVisit(const Model::World * world)414             void doVisit(const Model::World* world)   {}
doVisit(const Model::Layer * layer)415             void doVisit(const Model::Layer* layer)   {}
doVisit(const Model::Group * group)416             void doVisit(const Model::Group* group)   {}
417 
doVisit(const Model::Entity * entity)418             void doVisit(const Model::Entity* entity) {
419                 if (!entity->hasChildren()) {
420                     const Vec3::List vertices = bBoxVertices(entity->bounds());
421                     for (size_t i = 0; i < vertices.size(); ++i) {
422                         for (size_t j = 0; j < 4; ++j)
423                             addPoint(vertices[i], m_frustumPlanes[j]);
424                     }
425                 }
426             }
427 
doVisit(const Model::Brush * brush)428             void doVisit(const Model::Brush* brush)   {
429                 const Model::Brush::VertexList vertices = brush->vertices();
430                 Model::Brush::VertexList::const_iterator it, end;
431                 for (it = vertices.begin(), end = vertices.end(); it != end; ++it) {
432                     const Model::BrushVertex* vertex = *it;
433                     for (size_t j = 0; j < 4; ++j)
434                         addPoint(vertex->position(), m_frustumPlanes[j]);
435                 }
436             }
437 
addPoint(const Vec3f point,const Plane3f & plane)438             void addPoint(const Vec3f point, const Plane3f& plane) {
439                 const Ray3f ray(m_cameraPosition, -m_cameraDirection);
440                 const Plane3f newPlane(point + 64.0f * plane.normal, plane.normal);
441                 const float dist = newPlane.intersectWithRay(ray);
442                 if (!Math::isnan(dist) && dist > 0.0f)
443                     m_offset = std::max(m_offset, dist);
444             }
445         };
446 
focusCameraOnObjectsPosition(const Model::NodeList & nodes)447         Vec3f MapView3D::focusCameraOnObjectsPosition(const Model::NodeList& nodes) {
448             ComputeCameraCenterPositionVisitor center(m_camera.position(), m_camera.direction());
449             Model::Node::acceptAndRecurse(nodes.begin(), nodes.end(), center);
450 
451             const Vec3 newPosition = center.position();
452 
453             // act as if the camera were there already:
454             const Vec3f oldPosition = m_camera.position();
455             m_camera.moveTo(Vec3f(newPosition));
456 
457             Plane3f frustumPlanes[4];
458             m_camera.frustumPlanes(frustumPlanes[0], frustumPlanes[1], frustumPlanes[2], frustumPlanes[3]);
459 
460             ComputeCameraCenterOffsetVisitor offset(m_camera.position(), m_camera.direction(), frustumPlanes);
461             Model::Node::acceptAndRecurse(nodes.begin(), nodes.end(), offset);
462 
463             // jump back
464             m_camera.moveTo(oldPosition);
465             return newPosition - m_camera.direction() * offset.offset();
466         }
467 
doMoveCameraToPosition(const Vec3 & position,const bool animate)468         void MapView3D::doMoveCameraToPosition(const Vec3& position, const bool animate) {
469             if (animate)
470                 animateCamera(position, m_camera.direction(), m_camera.up());
471             else
472                 m_camera.moveTo(position);
473         }
474 
animateCamera(const Vec3f & position,const Vec3f & direction,const Vec3f & up,const wxLongLong duration)475         void MapView3D::animateCamera(const Vec3f& position, const Vec3f& direction, const Vec3f& up, const wxLongLong duration) {
476             CameraAnimation* animation = new CameraAnimation(m_camera, position, direction, up, duration);
477             m_animationManager->runAnimation(animation, true);
478         }
479 
doMoveCameraToCurrentTracePoint()480         void MapView3D::doMoveCameraToCurrentTracePoint() {
481             MapDocumentSPtr document = lock(m_document);
482 
483             assert(document->isPointFileLoaded());
484             Model::PointFile* pointFile = document->pointFile();
485             assert(pointFile->hasNextPoint());
486 
487             const Vec3f position = pointFile->currentPoint() + Vec3f(0.0f, 0.0f, 16.0f);
488             const Vec3f direction = pointFile->currentDirection();
489             animateCamera(position, direction, Vec3f::PosZ);
490         }
491 
doGetMoveDirection(const Math::Direction direction) const492         Vec3 MapView3D::doGetMoveDirection(const Math::Direction direction) const {
493             switch (direction) {
494                 case Math::Direction_Forward: {
495                     Vec3 dir = m_camera.direction().firstAxis();
496                     if (dir.z() < 0.0)
497                         dir = m_camera.up().firstAxis();
498                     else if (dir.z() > 0.0)
499                         dir = -m_camera.up().firstAxis();
500                     return dir;
501                 }
502                 case Math::Direction_Backward:
503                     return -doGetMoveDirection(Math::Direction_Forward);
504                 case Math::Direction_Left:
505                     return -doGetMoveDirection(Math::Direction_Right);
506                 case Math::Direction_Right: {
507                     Vec3 dir = m_camera.right().firstAxis();
508                     if (dir == doGetMoveDirection(Math::Direction_Forward))
509                         dir = crossed(dir, Vec3::PosZ);
510                     return dir;
511                 }
512                 case Math::Direction_Up:
513                     return Vec3::PosZ;
514                 case Math::Direction_Down:
515                     return Vec3::NegZ;
516                     switchDefault()
517             }
518         }
519 
doComputePointEntityPosition(const BBox3 & bounds) const520         Vec3 MapView3D::doComputePointEntityPosition(const BBox3& bounds) const {
521             MapDocumentSPtr document = lock(m_document);
522 
523             Vec3 delta;
524             View::Grid& grid = document->grid();
525 
526             const BBox3& worldBounds = document->worldBounds();
527 
528             const Model::Hit& hit = pickResult().query().pickable().type(Model::Brush::BrushHit).occluded().first();
529             if (hit.isMatch()) {
530                 const Model::BrushFace* face = Model::hitToFace(hit);
531                 return grid.moveDeltaForBounds(face->boundary(), bounds, worldBounds, pickRay(), hit.hitPoint());
532             } else {
533                 const Vec3 newPosition = Renderer::Camera::defaultPoint(pickRay());
534                 const Vec3 defCenter = bounds.center();
535                 return grid.moveDeltaForPoint(defCenter, worldBounds, newPosition - defCenter);
536             }
537         }
538 
doGetActionContext() const539         ActionContext MapView3D::doGetActionContext() const {
540             if (cameraFlyModeActive())
541                 return ActionContext_FlyMode;
542             return ActionContext_Default;
543         }
544 
doCreateAccelerationTable(ActionContext context) const545         wxAcceleratorTable MapView3D::doCreateAccelerationTable(ActionContext context) const {
546             ActionManager& actionManager = ActionManager::instance();
547             return actionManager.createViewAcceleratorTable(context, ActionView_Map3D);
548         }
549 
doCancel()550         bool MapView3D::doCancel() {
551             if (cameraFlyModeActive()) {
552                 toggleCameraFlyMode();
553                 return true;
554             }
555             return false;
556         }
557 
doCreateRenderContext()558         Renderer::RenderContext MapView3D::doCreateRenderContext() {
559             return Renderer::RenderContext(Renderer::RenderContext::RenderMode_3D, m_camera, fontManager(), shaderManager());
560         }
561 
doRenderGrid(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)562         void MapView3D::doRenderGrid(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {}
563 
doRenderMap(Renderer::MapRenderer & renderer,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)564         void MapView3D::doRenderMap(Renderer::MapRenderer& renderer, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
565             renderer.render(renderContext, renderBatch);
566 
567             MapDocumentSPtr document = lock(m_document);
568             if (renderContext.showSelectionGuide() && document->hasSelectedNodes()) {
569                 const BBox3& bounds = document->selectionBounds();
570                 Renderer::SelectionBoundsRenderer boundsRenderer(bounds);
571                 boundsRenderer.render(renderContext, renderBatch);
572 
573                 Renderer::BoundsGuideRenderer* guideRenderer = new Renderer::BoundsGuideRenderer(m_document);
574                 guideRenderer->setColor(pref(Preferences::SelectionBoundsColor));
575                 guideRenderer->setBounds(bounds);
576                 renderBatch.addOneShot(guideRenderer);
577             }
578         }
579 
doRenderTools(MapViewToolBox & toolBox,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)580         void MapView3D::doRenderTools(MapViewToolBox& toolBox, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
581             renderTools(renderContext, renderBatch);
582         }
583 
doLinkCamera(CameraLinkHelper & helper)584         void MapView3D::doLinkCamera(CameraLinkHelper& helper) {}
585     }
586 }
587