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