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 "VertexToolController.h"
21 
22 #include "Renderer/Camera.h"
23 #include "Renderer/RenderContext.h"
24 #include "View/InputState.h"
25 #include "View/Lasso.h"
26 #include "View/MapDocument.h"
27 #include "View/VertexTool.h"
28 
29 #include <cassert>
30 
31 namespace TrenchBroom {
32     namespace View {
33         const FloatType VertexToolController::MaxVertexDistance = 0.25;
34 
35         class VertexToolController::VertexPartBase {
36         protected:
37             VertexTool* m_tool;
38         public:
VertexPartBase(VertexTool * tool)39             VertexPartBase(VertexTool* tool) :
40             m_tool(tool) {
41                 assert(m_tool != NULL);
42             }
43         protected:
firstHits(const Model::PickResult & pickResult) const44             Model::Hit::List firstHits(const Model::PickResult& pickResult) const {
45                 Model::Hit::List result;
46                 Model::BrushSet brushes;
47 
48                 static const Model::Hit::HitType any = VertexHandleManager::VertexHandleHit | VertexHandleManager::EdgeHandleHit | VertexHandleManager::FaceHandleHit;
49                 const Model::Hit& first = pickResult.query().type(any).occluded().first();
50                 if (first.isMatch()) {
51                     const Vec3 firstHitPosition = first.target<Vec3>();
52 
53                     const Model::Hit::List matches = pickResult.query().type(any).all();
54                     Model::Hit::List::const_iterator hIt, hEnd;
55                     for (hIt = matches.begin(), hEnd = matches.end(); hIt != hEnd; ++hIt) {
56                         const Model::Hit& hit = *hIt;
57                         const Vec3 hitPosition = hit.target<Vec3>();
58 
59                         if (hitPosition.distanceTo(firstHitPosition) < MaxVertexDistance) {
60                             const bool newBrush = m_tool->handleBrushes(hitPosition, brushes);
61                             if (newBrush)
62                                 result.push_back(hit);
63                         }
64                     }
65                 }
66 
67                 return result;
68             }
69         };
70 
71         class VertexToolController::SelectVertexPart : public ToolControllerBase<PickingPolicy, NoKeyPolicy, MousePolicy, RestrictedDragPolicy, RenderPolicy, NoDropPolicy>, public VertexPartBase {
72         private:
73             Lasso* m_lasso;
74         public:
SelectVertexPart(VertexTool * tool)75             SelectVertexPart(VertexTool* tool) :
76             VertexPartBase(tool),
77             m_lasso(NULL) {}
78 
~SelectVertexPart()79             ~SelectVertexPart() {
80                 delete m_lasso;
81             }
82         private:
doGetTool()83             Tool* doGetTool() {
84                 return m_tool;
85             }
86 
doPick(const InputState & inputState,Model::PickResult & pickResult)87             void doPick(const InputState& inputState, Model::PickResult& pickResult) {
88                 m_tool->pick(inputState.pickRay(), inputState.camera(), pickResult);
89             }
90 
doMouseClick(const InputState & inputState)91             bool doMouseClick(const InputState& inputState) {
92                 if (!inputState.mouseButtonsPressed(MouseButtons::MBLeft) ||
93                     !inputState.checkModifierKeys(MK_DontCare, MK_No, MK_No))
94                     return false;
95 
96                 const Model::Hit::List hits = firstHits(inputState.pickResult());
97                 if (hits.empty())
98                     return m_tool->deselectAll();
99                 else
100                     return m_tool->select(hits, inputState.modifierKeysPressed(ModifierKeys::MKCtrlCmd));
101             }
102 
doStartDrag(const InputState & inputState)103             DragInfo doStartDrag(const InputState& inputState) {
104                 if (!inputState.mouseButtonsPressed(MouseButtons::MBLeft) ||
105                     !inputState.checkModifierKeys(MK_DontCare, MK_No, MK_No))
106                     return DragInfo();
107 
108                 const Renderer::Camera& camera = inputState.camera();
109                 const FloatType distance = 64.0f;
110                 const Plane3 plane = orthogonalDragPlane(camera.defaultPoint(distance), camera.direction());
111                 const Vec3 initialPoint = inputState.pickRay().pointAtDistance(plane.intersectWithRay(inputState.pickRay()));
112 
113                 m_lasso = new Lasso(camera, distance, initialPoint);
114                 return DragInfo(new PlaneDragRestricter(plane), new NoDragSnapper(), initialPoint);
115             }
116 
doDrag(const InputState & inputState,const Vec3 & lastPoint,const Vec3 & curPoint)117             DragResult doDrag(const InputState& inputState, const Vec3& lastPoint, const Vec3& curPoint) {
118                 assert(m_lasso != NULL);
119                 m_lasso->setPoint(curPoint);
120                 return DR_Continue;
121             }
122 
doEndDrag(const InputState & inputState)123             void doEndDrag(const InputState& inputState) {
124                 assert(m_lasso != NULL);
125                 m_tool->select(*m_lasso, inputState.modifierKeysDown(ModifierKeys::MKCtrlCmd));
126                 delete m_lasso;
127                 m_lasso = NULL;
128             }
129 
doCancelDrag()130             void doCancelDrag() {
131                 assert(m_lasso != NULL);
132                 delete m_lasso;
133                 m_lasso = NULL;
134             }
135 
doSetRenderOptions(const InputState & inputState,Renderer::RenderContext & renderContext) const136             void doSetRenderOptions(const InputState& inputState, Renderer::RenderContext& renderContext) const {
137                 renderContext.setForceHideSelectionGuide();
138             }
139 
doRender(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)140             void doRender(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
141                 m_tool->renderHandles(renderContext, renderBatch);
142                 if (m_lasso != NULL)
143                     m_lasso->render(renderContext, renderBatch);
144             }
145 
doCancel()146             bool doCancel() {
147                 return false;
148             }
149         };
150 
151         class VertexToolController::MoveVertexPart : public MoveToolController<NoPickingPolicy, MousePolicy>, public VertexPartBase {
152         public:
MoveVertexPart(VertexTool * tool)153             MoveVertexPart(VertexTool* tool) :
154             MoveToolController(tool->grid()),
155             VertexPartBase(tool) {}
156         private:
doGetTool()157             Tool* doGetTool() {
158                 return m_tool;
159             }
160 
doMouseDoubleClick(const InputState & inputState)161             bool doMouseDoubleClick(const InputState& inputState) {
162                 if (!inputState.mouseButtonsPressed(MouseButtons::MBLeft) ||
163                     !inputState.checkModifierKeys(MK_No, MK_No, MK_No))
164                     return false;
165 
166                 const Model::Hit::List hits = firstHits(inputState.pickResult());
167                 if (hits.empty())
168                     return false;
169 
170                 const Model::Hit& hit = hits.front();
171                 return m_tool->handleDoubleClicked(hit);
172             }
173 
doStartMove(const InputState & inputState)174             MoveInfo doStartMove(const InputState& inputState) {
175                 if (!(inputState.mouseButtonsPressed(MouseButtons::MBLeft) &&
176                       (inputState.modifierKeysPressed(ModifierKeys::MKNone) ||
177                        inputState.modifierKeysPressed(ModifierKeys::MKAlt))))
178                     return MoveInfo();
179 
180                 static const Model::Hit::HitType any = VertexHandleManager::VertexHandleHit | VertexHandleManager::EdgeHandleHit | VertexHandleManager::FaceHandleHit;
181                 const Model::Hit& hit = inputState.pickResult().query().type(any).occluded().first();
182                 if (!hit.isMatch())
183                     return MoveInfo();
184 
185                 if (!m_tool->beginMove(hit))
186                     return MoveInfo();
187 
188                 return MoveInfo(hit.target<Vec3>());
189             }
190 
doMove(const InputState & inputState,const Vec3 & lastPoint,const Vec3 & curPoint)191             DragResult doMove(const InputState& inputState, const Vec3& lastPoint, const Vec3& curPoint) {
192                 switch (m_tool->move(curPoint - lastPoint)) {
193                     case VertexTool::MR_Continue:
194                         return DR_Continue;
195                     case VertexTool::MR_Deny:
196                         return DR_Deny;
197                     case VertexTool::MR_Cancel:
198                         return DR_Cancel;
199                     switchDefault()
200                 }
201             }
202 
doEndMove(const InputState & inputState)203             void doEndMove(const InputState& inputState) {
204                 m_tool->endMove();
205             }
206 
doCancelMove()207             void doCancelMove() {
208                 m_tool->cancelMove();
209             }
210 
doRender(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)211             void doRender(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
212                 MoveToolController::doRender(inputState, renderContext, renderBatch);
213 
214                 if (thisToolDragging()) {
215                     m_tool->renderHighlight(renderContext, renderBatch);
216                     m_tool->renderGuide(renderContext, renderBatch);
217                 } else if (!anyToolDragging(inputState)) {
218                     static const Model::Hit::HitType any = VertexHandleManager::VertexHandleHit | VertexHandleManager::EdgeHandleHit | VertexHandleManager::FaceHandleHit;
219                     const Model::Hit& hit = inputState.pickResult().query().type(any).occluded().first();
220                     if (hit.isMatch()) {
221                         const Vec3 position = hit.target<Vec3>();
222                         m_tool->renderHighlight(renderContext, renderBatch, position);
223                         if (!m_tool->handleSelected(position)) {
224                             if (hit.type() == VertexHandleManager::EdgeHandleHit)
225                                 m_tool->renderEdgeHighlight(renderContext, renderBatch, position);
226                             else if (hit.type() == VertexHandleManager::FaceHandleHit)
227                                 m_tool->renderFaceHighlight(renderContext, renderBatch, position);
228                         }
229                         if (inputState.mouseButtonsPressed(MouseButtons::MBLeft))
230                             m_tool->renderGuide(renderContext, renderBatch, position);
231                     }
232                 }
233             }
234 
doCancel()235             bool doCancel() {
236                 return m_tool->cancel();
237             }
238         };
239 
240         class VertexToolController::SnapVertexPart : public ToolControllerBase<NoPickingPolicy, NoKeyPolicy, MousePolicy, RestrictedDragPolicy, RenderPolicy, NoDropPolicy>, public VertexPartBase {
241         public:
SnapVertexPart(VertexTool * tool)242             SnapVertexPart(VertexTool* tool) :
243             VertexPartBase(tool) {}
244         private:
doGetTool()245             Tool* doGetTool() {
246                 return m_tool;
247             }
248 
doMouseClick(const InputState & inputState)249             bool doMouseClick(const InputState& inputState) {
250                 if (!inputState.mouseButtonsPressed(MouseButtons::MBLeft) ||
251                     !inputState.checkModifierKeys(MK_No, MK_Yes, MK_Yes))
252                     return false;
253 
254                 const Model::Hit& hit = inputState.pickResult().query().type(VertexHandleManager::VertexHandleHit).occluded().first();
255                 if (!hit.isMatch())
256                     return false;
257 
258                 m_tool->mergeVertices(hit);
259                 return true;
260             }
261 
262             class VertexDragRestricter : public DragRestricter {
263             private:
264                 VertexTool* m_tool;
265             public:
VertexDragRestricter(VertexTool * tool)266                 VertexDragRestricter(VertexTool* tool) :
267                 m_tool(tool) {
268                     assert(m_tool != NULL);
269                 }
270             private:
doComputeHitPoint(const InputState & inputState,Vec3 & point) const271                 bool doComputeHitPoint(const InputState& inputState, Vec3& point) const {
272                     const Model::Hit& hit = inputState.pickResult().query().type(VertexHandleManager::VertexHandleHit).occluded().first();
273                     if (!hit.isMatch())
274                         return false;
275                     const Vec3 position = hit.target<Vec3>();
276                     if (m_tool->handleSelected(position))
277                         return false;
278                     point = position;
279                     return true;
280                 }
281             };
282 
doStartDrag(const InputState & inputState)283             DragInfo doStartDrag(const InputState& inputState) {
284                 if (!inputState.mouseButtonsPressed(MouseButtons::MBLeft) ||
285                     !inputState.checkModifierKeys(MK_No, MK_Yes, MK_Yes))
286                     return DragInfo();
287 
288                 const Model::Hit& hit = inputState.pickResult().query().type(VertexHandleManager::VertexHandleHit).occluded().first();
289                 if (!hit.isMatch())
290                     return DragInfo();
291 
292                 if (!m_tool->beginMove(hit))
293                     return DragInfo();
294 
295                 return DragInfo(new VertexDragRestricter(m_tool), new NoDragSnapper(), hit.target<Vec3>());
296             }
297 
doDrag(const InputState & inputState,const Vec3 & lastPoint,const Vec3 & curPoint)298             DragResult doDrag(const InputState& inputState, const Vec3& lastPoint, const Vec3& curPoint) {
299                 switch (m_tool->move(curPoint - lastPoint)) {
300                     case VertexTool::MR_Continue:
301                         return DR_Continue;
302                     case VertexTool::MR_Deny:
303                         return DR_Deny;
304                     case VertexTool::MR_Cancel:
305                         return DR_Cancel;
306                         switchDefault()
307                 }
308             }
309 
doEndDrag(const InputState & inputState)310             void doEndDrag(const InputState& inputState) {
311                 m_tool->endMove();
312             }
313 
doCancelDrag()314             void doCancelDrag() {
315                 m_tool->cancelMove();
316             }
317 
doRender(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)318             void doRender(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
319                 if (thisToolDragging()) {
320                     m_tool->renderHighlight(renderContext, renderBatch);
321                     m_tool->renderGuide(renderContext, renderBatch);
322                 }
323             }
324 
doCancel()325             bool doCancel() {
326                 return m_tool->cancel();
327             }
328         };
329 
VertexToolController(VertexTool * tool)330         VertexToolController::VertexToolController(VertexTool* tool) :
331         m_tool(tool) {
332             assert(m_tool != NULL);
333             addController(new MoveVertexPart(tool));
334             addController(new SnapVertexPart(tool));
335             addController(new SelectVertexPart(tool));
336         }
337 
~VertexToolController()338         VertexToolController::~VertexToolController() {}
339 
doGetTool()340         Tool* VertexToolController::doGetTool() {
341             return m_tool;
342         }
343     }
344 }
345