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 "CreateComplexBrushToolController3D.h" 21 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/HitAdapter.h" 28 #include "Model/HitQuery.h" 29 #include "Model/PickResult.h" 30 #include "Renderer/Camera.h" 31 #include "Renderer/RenderService.h" 32 #include "View/CreateComplexBrushTool.h" 33 #include "View/Grid.h" 34 #include "View/InputState.h" 35 #include "View/MapDocument.h" 36 37 #include <cassert> 38 #include <algorithm> 39 40 namespace TrenchBroom { 41 namespace View { 42 class CreateComplexBrushToolController3D::Part { 43 protected: 44 CreateComplexBrushTool* m_tool; 45 Polyhedron3 m_oldPolyhedron; 46 protected: Part(CreateComplexBrushTool * tool)47 Part(CreateComplexBrushTool* tool) : 48 m_tool(tool), 49 m_oldPolyhedron() { 50 assert(m_tool != NULL); 51 } 52 public: ~Part()53 virtual ~Part() {} 54 }; 55 56 class CreateComplexBrushToolController3D::DrawFacePart : public Part, public ToolControllerBase<NoPickingPolicy, NoKeyPolicy, NoMousePolicy, RestrictedDragPolicy, NoRenderPolicy, NoDropPolicy> { 57 private: 58 Plane3 m_plane; 59 Vec3 m_initialPoint; 60 public: DrawFacePart(CreateComplexBrushTool * tool)61 DrawFacePart(CreateComplexBrushTool* tool) : 62 Part(tool) {} 63 private: doGetTool()64 Tool* doGetTool() { return m_tool; } 65 doStartDrag(const InputState & inputState)66 DragInfo doStartDrag(const InputState& inputState) { 67 if (inputState.modifierKeysDown(ModifierKeys::MKShift)) 68 return DragInfo(); 69 70 const Model::PickResult& pickResult = inputState.pickResult(); 71 const Model::Hit& hit = pickResult.query().pickable().type(Model::Brush::BrushHit).occluded().first(); 72 if (!hit.isMatch()) 73 return DragInfo(); 74 75 m_oldPolyhedron = m_tool->polyhedron(); 76 77 const Model::BrushFace* face = Model::hitToFace(hit); 78 m_plane = face->boundary(); 79 m_initialPoint = hit.hitPoint(); 80 updatePolyhedron(m_initialPoint); 81 82 SurfaceDragRestricter* restricter = new SurfaceDragRestricter(); 83 restricter->setPickable(true); 84 restricter->setType(Model::Brush::BrushHit); 85 restricter->setOccluded(true); 86 return DragInfo(restricter, new NoDragSnapper(), m_initialPoint); 87 } 88 doDrag(const InputState & inputState,const Vec3 & lastPoint,const Vec3 & curPoint)89 DragResult doDrag(const InputState& inputState, const Vec3& lastPoint, const Vec3& curPoint) { 90 updatePolyhedron(curPoint); 91 return DR_Continue; 92 } 93 doEndDrag(const InputState & inputState)94 void doEndDrag(const InputState& inputState) { 95 } 96 doCancelDrag()97 void doCancelDrag() { 98 m_tool->update(m_oldPolyhedron); 99 } 100 doCancel()101 bool doCancel() { return false; } 102 private: updatePolyhedron(const Vec3 & current)103 void updatePolyhedron(const Vec3& current) { 104 const Grid& grid = m_tool->grid(); 105 106 const Math::Axis::Type axis = m_plane.normal.firstComponent(); 107 const Plane3 swizzledPlane(swizzle(m_plane.anchor(), axis), swizzle(m_plane.normal, axis)); 108 const Vec3 theMin = swizzle(grid.snapDown(min(m_initialPoint, current)), axis); 109 const Vec3 theMax = swizzle(grid.snapUp (max(m_initialPoint, current)), axis); 110 111 const Vec2 topLeft2(theMin.x(), theMin.y()); 112 const Vec2 topRight2(theMax.x(), theMin.y()); 113 const Vec2 bottomLeft2(theMin.x(), theMax.y()); 114 const Vec2 bottomRight2(theMax.x(), theMax.y()); 115 116 const Vec3 topLeft3 = unswizzle(Vec3(topLeft2, swizzledPlane.zAt(topLeft2)), axis); 117 const Vec3 topRight3 = unswizzle(Vec3(topRight2, swizzledPlane.zAt(topRight2)), axis); 118 const Vec3 bottomLeft3 = unswizzle(Vec3(bottomLeft2, swizzledPlane.zAt(bottomLeft2)), axis); 119 const Vec3 bottomRight3 = unswizzle(Vec3(bottomRight2, swizzledPlane.zAt(bottomRight2)), axis); 120 121 Polyhedron3 polyhedron = m_oldPolyhedron; 122 polyhedron.addPoint(topLeft3); 123 polyhedron.addPoint(bottomLeft3); 124 polyhedron.addPoint(bottomRight3); 125 polyhedron.addPoint(topRight3); 126 m_tool->update(polyhedron); 127 } 128 }; 129 130 class CreateComplexBrushToolController3D::DuplicateFacePart : public Part, public ToolControllerBase<NoPickingPolicy, NoKeyPolicy, NoMousePolicy, RestrictedDragPolicy, NoRenderPolicy, NoDropPolicy> { 131 private: 132 Vec3 m_dragDir; 133 public: DuplicateFacePart(CreateComplexBrushTool * tool)134 DuplicateFacePart(CreateComplexBrushTool* tool) : 135 Part(tool) {} 136 private: doGetTool()137 Tool* doGetTool() { return m_tool; } 138 doStartDrag(const InputState & inputState)139 DragInfo doStartDrag(const InputState& inputState) { 140 if (!inputState.modifierKeysDown(ModifierKeys::MKShift)) 141 return DragInfo(); 142 143 if (!m_tool->polyhedron().polygon()) 144 return DragInfo(); 145 146 m_oldPolyhedron = m_tool->polyhedron(); 147 148 const Polyhedron3::FaceHit hit = m_oldPolyhedron.pickFace(inputState.pickRay()); 149 const Vec3 origin = inputState.pickRay().pointAtDistance(hit.distance); 150 const Vec3 direction = hit.face->normal(); 151 152 const Line3 line(origin, direction); 153 m_dragDir = line.direction; 154 155 return DragInfo(new LineDragRestricter(line), new NoDragSnapper(), origin); 156 } 157 doDrag(const InputState & inputState,const Vec3 & lastPoint,const Vec3 & curPoint)158 DragResult doDrag(const InputState& inputState, const Vec3& lastPoint, const Vec3& curPoint) { 159 Polyhedron3 polyhedron = m_oldPolyhedron; 160 assert(polyhedron.polygon()); 161 162 const Grid& grid = m_tool->grid(); 163 164 const Vec3 rayDelta = curPoint - dragOrigin(); 165 const Vec3 rayAxis = m_dragDir.firstAxis(); 166 const FloatType axisDistance = rayDelta.dot(rayAxis); 167 const FloatType snappedDistance = grid.snap(axisDistance); 168 const FloatType snappedRayDist = rayAxis.inverseDot(snappedDistance, m_dragDir); 169 const Vec3 snappedRayDelta = snappedRayDist * m_dragDir; 170 171 const Polyhedron3::Face* face = m_oldPolyhedron.faces().front(); 172 const Vec3::List points = face->vertexPositions() + snappedRayDelta; 173 174 polyhedron.addPoints(points); 175 m_tool->update(polyhedron); 176 177 return DR_Continue; 178 } 179 doEndDrag(const InputState & inputState)180 void doEndDrag(const InputState& inputState) { 181 } 182 doCancelDrag()183 void doCancelDrag() { 184 m_tool->update(m_oldPolyhedron); 185 } 186 doCancel()187 bool doCancel() { return false; } 188 }; 189 CreateComplexBrushToolController3D(CreateComplexBrushTool * tool)190 CreateComplexBrushToolController3D::CreateComplexBrushToolController3D(CreateComplexBrushTool* tool) : 191 m_tool(tool) { 192 assert(m_tool != NULL); 193 addController(new DrawFacePart(m_tool)); 194 addController(new DuplicateFacePart(m_tool)); 195 } 196 doGetTool()197 Tool* CreateComplexBrushToolController3D::doGetTool() { 198 return m_tool; 199 } 200 doMouseClick(const InputState & inputState)201 bool CreateComplexBrushToolController3D::doMouseClick(const InputState& inputState) { 202 if (!inputState.mouseButtonsDown(MouseButtons::MBLeft)) 203 return false; 204 if (!inputState.checkModifierKeys(MK_No, MK_No, MK_No)) 205 return false; 206 207 const Model::PickResult& pickResult = inputState.pickResult(); 208 const Model::Hit& hit = pickResult.query().pickable().type(Model::Brush::BrushHit).occluded().first(); 209 if (!hit.isMatch()) 210 return false; 211 212 const Grid& grid = m_tool->grid(); 213 214 const Model::BrushFace* face = Model::hitToFace(hit); 215 const Vec3 snapped = grid.snap(hit.hitPoint(), face->boundary()); 216 217 Polyhedron3 polyhedron = m_tool->polyhedron(); 218 polyhedron.addPoint(snapped); 219 m_tool->update(polyhedron); 220 221 return true; 222 } 223 doMouseDoubleClick(const InputState & inputState)224 bool CreateComplexBrushToolController3D::doMouseDoubleClick(const InputState& inputState) { 225 if (!inputState.mouseButtonsDown(MouseButtons::MBLeft)) 226 return false; 227 if (!inputState.checkModifierKeys(MK_No, MK_No, MK_No)) 228 return false; 229 230 const Model::PickResult& pickResult = inputState.pickResult(); 231 const Model::Hit& hit = pickResult.query().pickable().type(Model::Brush::BrushHit).occluded().first(); 232 if (!hit.isMatch()) 233 return false; 234 235 Polyhedron3 polyhedron = m_tool->polyhedron(); 236 const Model::BrushFace* face = Model::hitToFace(hit); 237 238 const Model::BrushFace::VertexList vertices = face->vertices(); 239 Model::BrushFace::VertexList::const_iterator it, end; 240 for (it = vertices.begin(), end = vertices.end(); it != end; ++it) 241 polyhedron.addPoint((*it)->position()); 242 m_tool->update(polyhedron); 243 244 return true; 245 } 246 doShouldHandleMouseDrag(const InputState & inputState) const247 bool CreateComplexBrushToolController3D::doShouldHandleMouseDrag(const InputState& inputState) const { 248 if (!inputState.mouseButtonsDown(MouseButtons::MBLeft)) 249 return false; 250 if (!inputState.checkModifierKeys(MK_No, MK_No, MK_DontCare)) 251 return false; 252 return true; 253 } 254 doRender(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)255 void CreateComplexBrushToolController3D::doRender(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 256 m_tool->render(renderContext, renderBatch); 257 258 const Polyhedron3& polyhedron = m_tool->polyhedron(); 259 if (!polyhedron.empty()) { 260 Renderer::RenderService renderService(renderContext, renderBatch); 261 renderService.setForegroundColor(pref(Preferences::HandleColor)); 262 renderService.setLineWidth(2.0f); 263 264 const Polyhedron3::EdgeList& edges = polyhedron.edges(); 265 Polyhedron3::EdgeList::const_iterator eIt, eEnd; 266 for (eIt = edges.begin(), eEnd = edges.end(); eIt != eEnd; ++eIt) { 267 const Polyhedron3::Edge* edge = *eIt; 268 renderService.renderLine(edge->firstVertex()->position(), edge->secondVertex()->position()); 269 } 270 271 const Polyhedron3::VertexList& vertices = polyhedron.vertices(); 272 Polyhedron3::VertexList::const_iterator vIt, vEnd; 273 for (vIt = vertices.begin(), vEnd = vertices.end(); vIt != vEnd; ++vIt) { 274 const Polyhedron3::Vertex* vertex = *vIt; 275 renderService.renderPointHandle(vertex->position()); 276 } 277 278 if (polyhedron.polygon() && inputState.modifierKeysDown(ModifierKeys::MKShift)) { 279 const Polyhedron3::Face* face = polyhedron.faces().front(); 280 const Vec3::List pos3 = face->vertexPositions(); 281 Vec3f::List pos3f(pos3.size()); 282 for (size_t i = 0; i < pos3.size(); ++i) 283 pos3f[i] = Vec3f(pos3[i]); 284 285 renderService.setForegroundColor(Color(pref(Preferences::HandleColor), 0.5f)); 286 renderService.renderFilledPolygon(pos3f); 287 288 std::reverse(pos3f.begin(), pos3f.end()); 289 renderService.renderFilledPolygon(pos3f); 290 } 291 } 292 } 293 doCancel()294 bool CreateComplexBrushToolController3D::doCancel() { 295 const Polyhedron3& polyhedron = m_tool->polyhedron(); 296 if (polyhedron.empty()) 297 return false; 298 299 m_tool->update(Polyhedron3()); 300 return true; 301 } 302 } 303 } 304