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 "RotateObjectsHandle.h"
21 
22 #include "Macros.h"
23 #include "PreferenceManager.h"
24 #include "Preferences.h"
25 #include "Renderer/Camera.h"
26 #include "Renderer/Circle.h"
27 #include "Renderer/Renderable.h"
28 #include "Renderer/RenderBatch.h"
29 #include "Renderer/RenderContext.h"
30 #include "Renderer/RenderService.h"
31 #include "Renderer/RenderUtils.h"
32 #include "Renderer/Shaders.h"
33 #include "Renderer/ShaderManager.h"
34 #include "Renderer/Transformation.h"
35 #include "Renderer/VertexArray.h"
36 #include "View/InputState.h"
37 
38 #include <cassert>
39 
40 namespace TrenchBroom {
41     namespace View {
42         const Model::Hit::HitType RotateObjectsHandle::HandleHit = Model::Hit::freeHitType();
43 
position() const44         const Vec3& RotateObjectsHandle::position() const {
45             return m_position;
46         }
47 
setPosition(const Vec3 & position)48         void RotateObjectsHandle::setPosition(const Vec3& position) {
49             m_position = position;
50         }
51 
pick2D(const Ray3 & pickRay,const Renderer::Camera & camera) const52         Model::Hit RotateObjectsHandle::pick2D(const Ray3& pickRay, const Renderer::Camera& camera) const {
53             Vec3 xAxis, yAxis, zAxis;
54             computeAxes(pickRay.origin, xAxis, yAxis, zAxis);
55 
56             Model::Hit hit = pickPointHandle(pickRay, camera, m_position, HitArea_Center);
57             switch (camera.direction().firstComponent()) {
58                 case Math::Axis::AX:
59                     hit = selectHit(hit, pickPointHandle(pickRay, camera, getPointHandlePosition(yAxis), HitArea_YAxis));
60                     break;
61                 case Math::Axis::AY:
62                     hit = selectHit(hit, pickPointHandle(pickRay, camera, getPointHandlePosition(xAxis), HitArea_XAxis));
63                     break;
64                 case Math::Axis::AZ:
65                 default:
66                     hit = selectHit(hit, pickPointHandle(pickRay, camera, getPointHandlePosition(xAxis), HitArea_XAxis));
67                     break;
68             }
69             return hit;
70         }
71 
pick3D(const Ray3 & pickRay,const Renderer::Camera & camera) const72         Model::Hit RotateObjectsHandle::pick3D(const Ray3& pickRay, const Renderer::Camera& camera) const {
73             Vec3 xAxis, yAxis, zAxis;
74             computeAxes(pickRay.origin, xAxis, yAxis, zAxis);
75 
76             Model::Hit hit = pickPointHandle(pickRay, camera, m_position, HitArea_Center);
77             hit = selectHit(hit, pickPointHandle(pickRay, camera, getPointHandlePosition(xAxis), HitArea_XAxis));
78             hit = selectHit(hit, pickPointHandle(pickRay, camera, getPointHandlePosition(yAxis), HitArea_YAxis));
79             hit = selectHit(hit, pickPointHandle(pickRay, camera, getPointHandlePosition(zAxis), HitArea_ZAxis));
80             return hit;
81         }
82 
pointHandlePosition(const HitArea area,const Vec3 & cameraPos) const83         Vec3 RotateObjectsHandle::pointHandlePosition(const HitArea area, const Vec3& cameraPos) const {
84             Vec3 xAxis, yAxis, zAxis;
85             computeAxes(cameraPos, xAxis, yAxis, zAxis);
86             switch (area) {
87                 case HitArea_XAxis:
88                     return getPointHandlePosition(xAxis);
89                 case HitArea_YAxis:
90                     return getPointHandlePosition(yAxis);
91                 case HitArea_ZAxis:
92                     return getPointHandlePosition(zAxis);
93                 case HitArea_None:
94                 case HitArea_Center:
95                     return m_position;
96                 switchDefault()
97             }
98         }
99 
handleRadius() const100         FloatType RotateObjectsHandle::handleRadius() const {
101             return pref(Preferences::RotateHandleRadius);
102         }
103 
pointHandleAxis(const HitArea area,const Vec3 & cameraPos) const104         Vec3 RotateObjectsHandle::pointHandleAxis(const HitArea area, const Vec3& cameraPos) const {
105             Vec3 xAxis, yAxis, zAxis;
106             computeAxes(cameraPos, xAxis, yAxis, zAxis);
107             switch (area) {
108                 case HitArea_XAxis:
109                     return xAxis;
110                 case HitArea_YAxis:
111                     return yAxis;
112                 case HitArea_ZAxis:
113                     return zAxis;
114                 case HitArea_None:
115                 case HitArea_Center:
116                     return Vec3::PosZ;
117                 switchDefault()
118             }
119         }
120 
rotationAxis(const HitArea area) const121         Vec3 RotateObjectsHandle::rotationAxis(const HitArea area) const {
122             switch (area) {
123                 case HitArea_XAxis:
124                     return Vec3::PosZ;
125                 case HitArea_YAxis:
126                     return Vec3::PosX;
127                 case HitArea_ZAxis:
128                     return Vec3::PosY;
129                 case HitArea_None:
130                 case HitArea_Center:
131                     return Vec3::PosZ;
132                 switchDefault()
133             }
134         }
135 
renderHandle2D(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)136         void RotateObjectsHandle::renderHandle2D(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
137             const Renderer::Camera& camera = renderContext.camera();
138             const float radius = static_cast<float>(pref(Preferences::RotateHandleRadius));
139 
140             Renderer::RenderService renderService(renderContext, renderBatch);
141             renderService.setShowOccludedObjects();
142 
143             renderService.setForegroundColor(pref(Preferences::axisColor(camera.direction().firstComponent())));
144             renderService.renderCircle(m_position, camera.direction().firstComponent(), 64, radius);
145 
146             renderService.setForegroundColor(pref(Preferences::HandleColor));
147             renderService.renderPointHandle(m_position);
148             renderService.renderPointHandle(m_position + radius * camera.right());
149         }
150 
renderHandle3D(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)151         void RotateObjectsHandle::renderHandle3D(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
152             const float radius = static_cast<float>(pref(Preferences::RotateHandleRadius));
153 
154             Vec3f xAxis, yAxis, zAxis;
155             computeAxes(renderContext.camera().position(), xAxis, yAxis, zAxis);
156 
157             Renderer::RenderService renderService(renderContext, renderBatch);
158             renderService.setShowOccludedObjects();
159 
160             renderService.renderCoordinateSystem(BBox3f(radius).translated(m_position));
161 
162             renderService.setForegroundColor(pref(Preferences::XAxisColor));
163             renderService.renderCircle(m_position, Math::Axis::AX, 64, radius, zAxis, yAxis);
164             renderService.setForegroundColor(pref(Preferences::YAxisColor));
165             renderService.renderCircle(m_position, Math::Axis::AY, 64, radius, xAxis, zAxis);
166             renderService.setForegroundColor(pref(Preferences::ZAxisColor));
167             renderService.renderCircle(m_position, Math::Axis::AZ, 64, radius, xAxis, yAxis);
168 
169             /*
170             renderService.renderCircle(m_position, Math::Axis::AX, 8, radius,
171                                        Quatf(Vec3f::PosX, Math::radians(+15.0f)) * yAxis,
172                                        Quatf(Vec3f::PosX, Math::radians(-15.0f)) * yAxis);
173             renderService.renderCircle(m_position, Math::Axis::AY, 8, radius,
174                                        Quatf(Vec3f::PosY, Math::radians(+15.0f)) * zAxis,
175                                        Quatf(Vec3f::PosY, Math::radians(-15.0f)) * zAxis);
176             renderService.renderCircle(m_position, Math::Axis::AZ, 8, radius,
177                                        Quatf(Vec3f::PosZ, Math::radians(+15.0f)) * xAxis,
178                                        Quatf(Vec3f::PosZ, Math::radians(-15.0f)) * xAxis);
179 
180              */
181             renderService.setForegroundColor(pref(Preferences::HandleColor));
182             renderService.renderPointHandle(m_position);
183 
184             renderService.setForegroundColor(pref(Preferences::ZAxisColor));
185             renderService.renderPointHandle(m_position + radius * xAxis);
186 
187             renderService.setForegroundColor(pref(Preferences::XAxisColor));
188             renderService.renderPointHandle(m_position + radius * yAxis);
189 
190             renderService.setForegroundColor(pref(Preferences::YAxisColor));
191             renderService.renderPointHandle(m_position + radius * zAxis);
192 
193         }
194 
renderHighlight2D(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch,const HitArea area)195         void RotateObjectsHandle::renderHighlight2D(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch, const HitArea area) {
196             const float radius = static_cast<float>(pref(Preferences::RotateHandleRadius));
197             const Renderer::Camera& camera = renderContext.camera();
198 
199             Renderer::RenderService renderService(renderContext, renderBatch);
200             renderService.setForegroundColor(pref(Preferences::SelectedHandleColor));
201             renderService.setShowOccludedObjects();
202 
203             switch (area) {
204                 case RotateObjectsHandle::HitArea_Center:
205                     renderService.renderPointHandleHighlight(m_position);
206                     break;
207                 case RotateObjectsHandle::HitArea_XAxis:
208                 case RotateObjectsHandle::HitArea_YAxis:
209                 case RotateObjectsHandle::HitArea_ZAxis:
210                     renderService.renderPointHandleHighlight(m_position + radius * camera.right());
211                     break;
212                 case RotateObjectsHandle::HitArea_None:
213                     break;
214                     switchDefault()
215             };
216         }
217 
renderHighlight3D(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch,const HitArea area)218         void RotateObjectsHandle::renderHighlight3D(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch, const HitArea area) {
219             const float radius = static_cast<float>(pref(Preferences::RotateHandleRadius));
220             Vec3f xAxis, yAxis, zAxis;
221             computeAxes(renderContext.camera().position(), xAxis, yAxis, zAxis);
222 
223             Renderer::RenderService renderService(renderContext, renderBatch);
224             renderService.setForegroundColor(pref(Preferences::SelectedHandleColor));
225             renderService.setShowOccludedObjects();
226 
227             switch (area) {
228                 case RotateObjectsHandle::HitArea_Center:
229                     renderService.renderPointHandleHighlight(m_position);
230                     renderService.setForegroundColor(pref(Preferences::InfoOverlayTextColor));
231                     renderService.setBackgroundColor(pref(Preferences::InfoOverlayBackgroundColor));
232                     renderService.renderString(m_position.asString(), m_position);
233                     break;
234                 case RotateObjectsHandle::HitArea_XAxis:
235                     renderService.renderPointHandleHighlight(m_position + radius * xAxis);
236                     break;
237                 case RotateObjectsHandle::HitArea_YAxis:
238                     renderService.renderPointHandleHighlight(m_position + radius * yAxis);
239                     break;
240                 case RotateObjectsHandle::HitArea_ZAxis:
241                     renderService.renderPointHandleHighlight(m_position + radius * zAxis);
242                     break;
243                 case RotateObjectsHandle::HitArea_None:
244                     break;
245                     switchDefault()
246             };
247         }
248 
249         /*
250         void RotateObjectsHandle::renderAngle(Renderer::RenderContext& renderContext, const HitArea handle, const FloatType angle) {
251 
252             PreferenceManager& prefs = PreferenceManager::instance();
253             const float handleRadius = static_cast<float>(prefs.get(Preferences::RotateHandleRadius));
254             const Color& pointHandleColor = prefs.get(Preferences::RotateHandleColor);
255 
256             const Vec3f rotationAxis(getRotationAxis(handle));
257             const Vec3f startAxis(getPointHandleAxis(handle));
258             const Vec3f endAxis(Quat3(rotationAxis, angle) * startAxis);
259 
260             Renderer::SetVboState setVboState(m_vbo);
261             setVboState.active();
262 
263             glAssert(glDisable(GL_DEPTH_TEST));
264             {
265                 glAssert(glDisable(GL_CULL_FACE));
266                 glAssert(glPolygonMode(GL_FRONT_AND_BACK, GL_FILL));
267                 Renderer::MultiplyModelMatrix translation(renderContext.transformation(), translationMatrix(m_position));
268                 Renderer::ActiveShader shader(renderContext.shaderManager(), Renderer::Shaders::VaryingPUniformCShader);
269                 shader.set("Color", getAngleIndicatorColor(handle));
270 
271                 Renderer::Circle circle(handleRadius, 24, true, rotationAxis.firstComponent(), startAxis, endAxis);
272 
273                 setVboState.mapped();
274                 circle.prepare(m_vbo);
275 
276                 setVboState.active();
277                 circle.render();
278 
279                 glAssert(glPolygonMode(GL_FRONT, GL_FILL));
280                 glAssert(glEnable(GL_CULL_FACE));
281             }
282 
283             m_pointHandleRenderer.setColor(pointHandleColor);
284             m_pointHandleRenderer.renderSingleHandle(renderContext, m_position);
285             m_pointHandleRenderer.renderSingleHandle(renderContext, getPointHandlePosition(handle));
286 
287             glAssert(glEnable(GL_DEPTH_TEST));
288         }
289         */
290 
pickPointHandle(const Ray3 & pickRay,const Renderer::Camera & camera,const Vec3 & position,const HitArea area) const291         Model::Hit RotateObjectsHandle::pickPointHandle(const Ray3& pickRay, const Renderer::Camera& camera, const Vec3& position, const HitArea area) const {
292             const FloatType distance = camera.pickPointHandle(pickRay, position, pref(Preferences::HandleRadius));
293             if (Math::isnan(distance))
294                 return Model::Hit::NoHit;
295             return Model::Hit(HandleHit, distance, pickRay.pointAtDistance(distance), area);
296         }
297 
selectHit(const Model::Hit & closest,const Model::Hit & hit) const298         Model::Hit RotateObjectsHandle::selectHit(const Model::Hit& closest, const Model::Hit& hit) const {
299             if (!closest.isMatch())
300                 return hit;
301             if (hit.isMatch()) {
302                 if (hit.distance() < closest.distance())
303                     return hit;
304             }
305             return closest;
306         }
307 
getPointHandlePosition(const Vec3 & axis) const308         Vec3 RotateObjectsHandle::getPointHandlePosition(const Vec3& axis) const {
309             PreferenceManager& prefs = PreferenceManager::instance();
310             return m_position + axis * prefs.get(Preferences::RotateHandleRadius);
311         }
312 
getAngleIndicatorColor(const HitArea area) const313         Color RotateObjectsHandle::getAngleIndicatorColor(const HitArea area) const {
314             PreferenceManager& prefs = PreferenceManager::instance();
315             switch (area) {
316                 case HitArea_XAxis:
317                     return Color(prefs.get(Preferences::ZAxisColor), 0.5f);
318                 case HitArea_YAxis:
319                     return Color(prefs.get(Preferences::XAxisColor), 0.5f);
320                 case HitArea_ZAxis:
321                     return Color(prefs.get(Preferences::YAxisColor), 0.5f);
322                 case HitArea_Center:
323                 case HitArea_None:
324                     return Color(1.0f, 1.0f, 1.0f, 1.0f);
325                 switchDefault()
326             };
327         }
328     }
329 }
330