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