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 "RotateObjectsToolController.h" 21 22 #include "PreferenceManager.h" 23 #include "Preferences.h" 24 #include "Renderer/Camera.h" 25 #include "Renderer/RenderBatch.h" 26 #include "Renderer/RenderContext.h" 27 #include "Renderer/RenderService.h" 28 #include "Renderer/ShaderManager.h" 29 #include "Renderer/Shaders.h" 30 #include "View/RotateObjectsTool.h" 31 #include "View/InputState.h" 32 #include "View/MoveToolController.h" 33 34 namespace TrenchBroom { 35 namespace View { 36 class RotateObjectsToolController::RotateObjectsBase : public ToolControllerBase<NoPickingPolicy, NoKeyPolicy, MousePolicy, RestrictedDragPolicy, RenderPolicy, NoDropPolicy> { 37 protected: 38 RotateObjectsTool* m_tool; 39 private: 40 RotateObjectsHandle::HitArea m_area; 41 Vec3 m_center; 42 Vec3 m_start; 43 Vec3 m_axis; 44 FloatType m_angle; 45 protected: RotateObjectsBase(RotateObjectsTool * tool)46 RotateObjectsBase(RotateObjectsTool* tool) : 47 m_tool(tool) { 48 assert(m_tool != NULL); 49 } 50 private: doGetTool()51 Tool* doGetTool() { 52 return m_tool; 53 } 54 doMouseClick(const InputState & inputState)55 bool doMouseClick(const InputState& inputState) { 56 if (!inputState.mouseButtonsPressed(MouseButtons::MBLeft)) 57 return false; 58 59 const Model::Hit& hit = inputState.pickResult().query().type(RotateObjectsHandle::HandleHit).occluded().first(); 60 if (!hit.isMatch()) 61 return false; 62 63 const RotateObjectsHandle::HitArea area = hit.target<RotateObjectsHandle::HitArea>(); 64 if (area == RotateObjectsHandle::HitArea_Center) 65 return false; 66 67 m_tool->updateToolPageAxis(area); 68 return true; 69 } 70 doStartDrag(const InputState & inputState)71 DragInfo doStartDrag(const InputState& inputState) { 72 if (inputState.mouseButtons() != MouseButtons::MBLeft || 73 inputState.modifierKeys() != ModifierKeys::MKNone) 74 return DragInfo(); 75 76 const Model::Hit& hit = inputState.pickResult().query().type(RotateObjectsHandle::HandleHit).occluded().first(); 77 if (!hit.isMatch()) 78 return DragInfo(); 79 80 const RotateObjectsHandle::HitArea area = hit.target<RotateObjectsHandle::HitArea>(); 81 if (area == RotateObjectsHandle::HitArea_Center) 82 return DragInfo(); 83 84 m_tool->beginRotation(); 85 86 m_area = hit.target<RotateObjectsHandle::HitArea>(); 87 m_center = m_tool->rotationCenter(); 88 m_start = m_tool->rotationAxisHandle(m_area, inputState.camera().position()); 89 m_axis = m_tool->rotationAxis(m_area); 90 m_angle = 0.0; 91 const FloatType radius = m_tool->handleRadius(); 92 return DragInfo(new CircleDragRestricter(m_center, m_axis, radius), new CircleDragSnapper(m_tool->grid(), m_start, m_center, m_axis, radius)); 93 } 94 doDrag(const InputState & inputState,const Vec3 & lastPoint,const Vec3 & curPoint)95 DragResult doDrag(const InputState& inputState, const Vec3& lastPoint, const Vec3& curPoint) { 96 const Vec3 ref = (m_start - m_center).normalized(); 97 const Vec3 vec = (curPoint - m_center).normalized(); 98 m_angle = angleBetween(vec, ref, m_axis); 99 m_tool->applyRotation(m_center, m_axis, m_angle); 100 return DR_Continue; 101 } 102 doEndDrag(const InputState & inputState)103 void doEndDrag(const InputState& inputState) { 104 m_tool->commitRotation(); 105 } 106 doCancelDrag()107 void doCancelDrag() { 108 m_tool->cancelRotation(); 109 } 110 doRender(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)111 void doRender(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 112 if (thisToolDragging()) { 113 doRenderHighlight(inputState, renderContext, renderBatch, m_area); 114 renderAngleIndicator(renderContext, renderBatch); 115 renderAngleText(renderContext, renderBatch); 116 } else { 117 const Model::Hit& hit = inputState.pickResult().query().type(RotateObjectsHandle::HandleHit).occluded().first(); 118 if (hit.isMatch()) { 119 const RotateObjectsHandle::HitArea area = hit.target<RotateObjectsHandle::HitArea>(); 120 if (area != RotateObjectsHandle::HitArea_Center) 121 doRenderHighlight(inputState, renderContext, renderBatch, hit.target<RotateObjectsHandle::HitArea>()); 122 } 123 } 124 } 125 126 class AngleIndicatorRenderer : public Renderer::DirectRenderable { 127 private: 128 Vec3 m_position; 129 Renderer::Circle m_circle; 130 public: AngleIndicatorRenderer(const Vec3 & position,const float radius,const Math::Axis::Type axis,const Vec3 & startAxis,const Vec3 & endAxis)131 AngleIndicatorRenderer(const Vec3& position, const float radius, const Math::Axis::Type axis, const Vec3& startAxis, const Vec3& endAxis) : 132 m_position(position), 133 m_circle(radius, 24, true, axis, startAxis, endAxis) {} 134 private: doPrepareVertices(Renderer::Vbo & vertexVbo)135 void doPrepareVertices(Renderer::Vbo& vertexVbo) { 136 m_circle.prepare(vertexVbo); 137 } 138 doRender(Renderer::RenderContext & renderContext)139 void doRender(Renderer::RenderContext& renderContext) { 140 glAssert(glDisable(GL_DEPTH_TEST)); 141 glAssert(glDisable(GL_CULL_FACE)); 142 glAssert(glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)); 143 144 Renderer::MultiplyModelMatrix translation(renderContext.transformation(), translationMatrix(m_position)); 145 Renderer::ActiveShader shader(renderContext.shaderManager(), Renderer::Shaders::VaryingPUniformCShader); 146 shader.set("Color", Color(1.0f, 1.0f, 1.0f, 0.2f)); 147 m_circle.render(); 148 149 glAssert(glPolygonMode(GL_FRONT, GL_FILL)); 150 glAssert(glEnable(GL_CULL_FACE)); 151 glAssert(glEnable(GL_DEPTH_TEST)); 152 } 153 }; 154 renderAngleIndicator(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)155 void renderAngleIndicator(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 156 PreferenceManager& prefs = PreferenceManager::instance(); 157 const float handleRadius = static_cast<float>(prefs.get(Preferences::RotateHandleRadius)); 158 const Vec3 startAxis = (m_start - m_center).normalized(); 159 const Vec3 endAxis = Quat3(m_axis, m_angle) * startAxis; 160 161 renderBatch.addOneShot(new AngleIndicatorRenderer(m_center, handleRadius, m_axis.firstComponent(), startAxis, endAxis)); 162 } 163 renderAngleText(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)164 void renderAngleText(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 165 Renderer::RenderService renderService(renderContext, renderBatch); 166 167 renderService.setForegroundColor(pref(Preferences::SelectedInfoOverlayTextColor)); 168 renderService.setBackgroundColor(pref(Preferences::SelectedInfoOverlayBackgroundColor)); 169 renderService.renderString(angleString(Math::degrees(m_angle)), m_center); 170 } 171 angleString(const FloatType angle) const172 String angleString(const FloatType angle) const { 173 StringStream str; 174 str.precision(2); 175 str.setf(std::ios::fixed); 176 str << angle; 177 return str.str(); 178 } 179 doCancel()180 bool doCancel() { 181 return false; 182 } 183 private: 184 virtual void doRenderHighlight(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch, RotateObjectsHandle::HitArea area) = 0; 185 }; 186 187 class RotateObjectsToolController::MoveCenterBase : public MoveToolController<NoPickingPolicy, NoMousePolicy> { 188 protected: 189 RotateObjectsTool* m_tool; 190 protected: MoveCenterBase(RotateObjectsTool * tool)191 MoveCenterBase(RotateObjectsTool* tool) : 192 MoveToolController(tool->grid()), 193 m_tool(tool) { 194 assert(m_tool != NULL); 195 } 196 doGetTool()197 Tool* doGetTool() { 198 return m_tool; 199 } 200 doStartMove(const InputState & inputState)201 MoveInfo doStartMove(const InputState& inputState) { 202 if (inputState.mouseButtons() != MouseButtons::MBLeft || 203 inputState.modifierKeys() != ModifierKeys::MKNone) 204 return MoveInfo(); 205 206 const Model::Hit& hit = inputState.pickResult().query().type(RotateObjectsHandle::HandleHit).occluded().first(); 207 if (!hit.isMatch()) 208 return MoveInfo(); 209 210 if (hit.target<RotateObjectsHandle::HitArea>() != RotateObjectsHandle::HitArea_Center) 211 return MoveInfo(); 212 213 return MoveInfo(m_tool->rotationCenter()); 214 } 215 doMove(const InputState & inputState,const Vec3 & lastPoint,const Vec3 & curPoint)216 DragResult doMove(const InputState& inputState, const Vec3& lastPoint, const Vec3& curPoint) { 217 m_tool->setRotationCenter(curPoint); 218 return DR_Continue; 219 } 220 doEndMove(const InputState & inputState)221 void doEndMove(const InputState& inputState) {} 222 doCancelMove()223 void doCancelMove() { 224 m_tool->setRotationCenter(dragOrigin()); 225 } 226 doRender(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)227 void doRender(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 228 MoveToolController::doRender(inputState, renderContext, renderBatch); 229 if (thisToolDragging()) { 230 doRenderHighlight(inputState, renderContext, renderBatch, RotateObjectsHandle::HitArea_Center); 231 } else if (!anyToolDragging(inputState)) { 232 const Model::Hit& hit = inputState.pickResult().query().type(RotateObjectsHandle::HandleHit).occluded().first(); 233 if (hit.isMatch() && hit.target<RotateObjectsHandle::HitArea>() == RotateObjectsHandle::HitArea_Center) 234 doRenderHighlight(inputState, renderContext, renderBatch, RotateObjectsHandle::HitArea_Center); 235 } 236 } 237 doCancel()238 bool doCancel() { 239 return false; 240 } 241 private: 242 virtual void doRenderHighlight(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch, RotateObjectsHandle::HitArea area) = 0; 243 }; 244 RotateObjectsToolController(RotateObjectsTool * tool)245 RotateObjectsToolController::RotateObjectsToolController(RotateObjectsTool* tool) : 246 m_tool(tool) {} 247 ~RotateObjectsToolController()248 RotateObjectsToolController::~RotateObjectsToolController() {} 249 doGetTool()250 Tool* RotateObjectsToolController::doGetTool() { 251 return m_tool; 252 } 253 doPick(const InputState & inputState,Model::PickResult & pickResult)254 void RotateObjectsToolController::doPick(const InputState& inputState, Model::PickResult& pickResult) { 255 const Model::Hit hit = doPick(inputState); 256 if (hit.isMatch()) 257 pickResult.addHit(hit); 258 } 259 doSetRenderOptions(const InputState & inputState,Renderer::RenderContext & renderContext) const260 void RotateObjectsToolController::doSetRenderOptions(const InputState& inputState, Renderer::RenderContext& renderContext) const { 261 const Model::Hit& hit = inputState.pickResult().query().type(RotateObjectsHandle::HandleHit).occluded().first(); 262 if (thisToolDragging() || hit.isMatch()) 263 renderContext.setForceShowSelectionGuide(); 264 } 265 doRender(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)266 void RotateObjectsToolController::doRender(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 267 doRenderHandle(renderContext, renderBatch); 268 ToolControllerGroup::doRender(inputState, renderContext, renderBatch); 269 } 270 doCancel()271 bool RotateObjectsToolController::doCancel() { 272 return false; 273 } 274 275 class RotateObjectsToolController2D::MoveCenterPart : public MoveCenterBase { 276 public: MoveCenterPart(RotateObjectsTool * tool)277 MoveCenterPart(RotateObjectsTool* tool) : 278 MoveCenterBase(tool) {} 279 private: doRenderHighlight(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch,RotateObjectsHandle::HitArea area)280 void doRenderHighlight(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch, RotateObjectsHandle::HitArea area) { 281 m_tool->renderHighlight2D(renderContext, renderBatch, area); 282 } 283 }; 284 285 class RotateObjectsToolController2D::RotateObjectsPart : public RotateObjectsBase { 286 public: RotateObjectsPart(RotateObjectsTool * tool)287 RotateObjectsPart(RotateObjectsTool* tool) : 288 RotateObjectsBase(tool) {} 289 private: doRenderHighlight(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch,RotateObjectsHandle::HitArea area)290 void doRenderHighlight(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch, RotateObjectsHandle::HitArea area) { 291 m_tool->renderHighlight2D(renderContext, renderBatch, area); 292 } 293 }; 294 RotateObjectsToolController2D(RotateObjectsTool * tool)295 RotateObjectsToolController2D::RotateObjectsToolController2D(RotateObjectsTool* tool) : 296 RotateObjectsToolController(tool) { 297 addController(new MoveCenterPart(tool)); 298 addController(new RotateObjectsPart(tool)); 299 } 300 doPick(const InputState & inputState)301 Model::Hit RotateObjectsToolController2D::doPick(const InputState& inputState) { 302 return m_tool->pick2D(inputState.pickRay(), inputState.camera()); 303 } 304 doRenderHandle(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)305 void RotateObjectsToolController2D::doRenderHandle(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 306 m_tool->renderHandle2D(renderContext, renderBatch); 307 } 308 309 310 class RotateObjectsToolController3D::MoveCenterPart : public MoveCenterBase { 311 public: MoveCenterPart(RotateObjectsTool * tool)312 MoveCenterPart(RotateObjectsTool* tool) : 313 MoveCenterBase(tool) {} 314 private: doRenderHighlight(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch,RotateObjectsHandle::HitArea area)315 void doRenderHighlight(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch, RotateObjectsHandle::HitArea area) { 316 m_tool->renderHighlight3D(renderContext, renderBatch, area); 317 } 318 }; 319 320 class RotateObjectsToolController3D::RotateObjectsPart : public RotateObjectsBase { 321 public: RotateObjectsPart(RotateObjectsTool * tool)322 RotateObjectsPart(RotateObjectsTool* tool) : 323 RotateObjectsBase(tool) {} 324 private: doRenderHighlight(const InputState & inputState,Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch,RotateObjectsHandle::HitArea area)325 void doRenderHighlight(const InputState& inputState, Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch, RotateObjectsHandle::HitArea area) { 326 m_tool->renderHighlight3D(renderContext, renderBatch, area); 327 } 328 }; 329 RotateObjectsToolController3D(RotateObjectsTool * tool)330 RotateObjectsToolController3D::RotateObjectsToolController3D(RotateObjectsTool* tool) : 331 RotateObjectsToolController(tool) { 332 addController(new MoveCenterPart(tool)); 333 addController(new RotateObjectsPart(tool)); 334 } 335 doPick(const InputState & inputState)336 Model::Hit RotateObjectsToolController3D::doPick(const InputState& inputState) { 337 return m_tool->pick3D(inputState.pickRay(), inputState.camera()); 338 } 339 doRenderHandle(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)340 void RotateObjectsToolController3D::doRenderHandle(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 341 m_tool->renderHandle3D(renderContext, renderBatch); 342 } 343 } 344 } 345