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 "UVView.h" 21 #include "PreferenceManager.h" 22 #include "Preferences.h" 23 #include "Assets/Texture.h" 24 #include "Model/BrushFace.h" 25 #include "Model/BrushGeometry.h" 26 #include "Renderer/Camera.h" 27 #include "Renderer/EdgeRenderer.h" 28 #include "Renderer/Renderable.h" 29 #include "Renderer/RenderBatch.h" 30 #include "Renderer/RenderContext.h" 31 #include "Renderer/Shaders.h" 32 #include "Renderer/ShaderManager.h" 33 #include "Renderer/Vbo.h" 34 #include "Renderer/VertexArray.h" 35 #include "Renderer/VertexSpec.h" 36 #include "View/GLAttribs.h" 37 #include "View/Grid.h" 38 #include "View/MapDocument.h" 39 #include "View/UVCameraTool.h" 40 #include "View/UVOffsetTool.h" 41 #include "View/UVRotateTool.h" 42 #include "View/UVScaleTool.h" 43 #include "View/UVShearTool.h" 44 #include "View/UVOriginTool.h" 45 46 #include <cassert> 47 #include <iostream> 48 49 namespace TrenchBroom { 50 namespace View { 51 const Model::Hit::HitType UVView::FaceHit = Model::Hit::freeHitType(); 52 UVView(wxWindow * parent,MapDocumentWPtr document,GLContextManager & contextManager)53 UVView::UVView(wxWindow* parent, MapDocumentWPtr document, GLContextManager& contextManager) : 54 RenderView(parent, contextManager, buildAttribs()), 55 ToolBoxConnector(this), 56 m_document(document), 57 m_helper(m_camera) { 58 setToolBox(m_toolBox); 59 m_toolBox.setClickToActivate(false); 60 createTools(); 61 m_toolBox.disable(); 62 bindObservers(); 63 } 64 ~UVView()65 UVView::~UVView() { 66 unbindObservers(); 67 } 68 setSubDivisions(const Vec2i & subDivisions)69 void UVView::setSubDivisions(const Vec2i& subDivisions) { 70 m_helper.setSubDivisions(subDivisions); 71 Refresh(); 72 } 73 createTools()74 void UVView::createTools() { 75 addTool(new UVRotateTool(m_document, m_helper)); 76 addTool(new UVOriginTool(m_helper)); 77 addTool(new UVScaleTool(m_document, m_helper)); 78 addTool(new UVShearTool(m_document, m_helper)); 79 addTool(new UVOffsetTool(m_document, m_helper)); 80 addTool(new UVCameraTool(m_camera)); 81 } 82 bindObservers()83 void UVView::bindObservers() { 84 MapDocumentSPtr document = lock(m_document); 85 document->nodesDidChangeNotifier.addObserver(this, &UVView::nodesDidChange); 86 document->brushFacesDidChangeNotifier.addObserver(this, &UVView::brushFacesDidChange); 87 document->selectionDidChangeNotifier.addObserver(this, &UVView::selectionDidChange); 88 document->grid().gridDidChangeNotifier.addObserver(this, &UVView::gridDidChange); 89 90 PreferenceManager& prefs = PreferenceManager::instance(); 91 prefs.preferenceDidChangeNotifier.addObserver(this, &UVView::preferenceDidChange); 92 93 m_camera.cameraDidChangeNotifier.addObserver(this, &UVView::cameraDidChange); 94 } 95 unbindObservers()96 void UVView::unbindObservers() { 97 if (!expired(m_document)) { 98 MapDocumentSPtr document = lock(m_document); 99 document->nodesDidChangeNotifier.removeObserver(this, &UVView::nodesDidChange); 100 document->brushFacesDidChangeNotifier.removeObserver(this, &UVView::brushFacesDidChange); 101 document->selectionDidChangeNotifier.removeObserver(this, &UVView::selectionDidChange); 102 document->grid().gridDidChangeNotifier.removeObserver(this, &UVView::gridDidChange); 103 } 104 105 PreferenceManager& prefs = PreferenceManager::instance(); 106 prefs.preferenceDidChangeNotifier.removeObserver(this, &UVView::preferenceDidChange); 107 108 m_camera.cameraDidChangeNotifier.removeObserver(this, &UVView::cameraDidChange); 109 } 110 selectionDidChange(const Selection & selection)111 void UVView::selectionDidChange(const Selection& selection) { 112 MapDocumentSPtr document = lock(m_document); 113 const Model::BrushFaceList& faces = document->selectedBrushFaces(); 114 if (faces.size() != 1) 115 m_helper.setFace(NULL); 116 else 117 m_helper.setFace(faces.back()); 118 119 if (m_helper.valid()) 120 m_toolBox.enable(); 121 else 122 m_toolBox.disable(); 123 Refresh(); 124 } 125 nodesDidChange(const Model::NodeList & nodes)126 void UVView::nodesDidChange(const Model::NodeList& nodes) { 127 Refresh(); 128 } 129 brushFacesDidChange(const Model::BrushFaceList & faces)130 void UVView::brushFacesDidChange(const Model::BrushFaceList& faces) { 131 Refresh(); 132 } 133 gridDidChange()134 void UVView::gridDidChange() { 135 Refresh(); 136 } 137 preferenceDidChange(const IO::Path & path)138 void UVView::preferenceDidChange(const IO::Path& path) { 139 Refresh(); 140 } 141 cameraDidChange(const Renderer::Camera * camera)142 void UVView::cameraDidChange(const Renderer::Camera* camera) { 143 Refresh(); 144 } 145 doUpdateViewport(int x,int y,int width,int height)146 void UVView::doUpdateViewport(int x, int y, int width, int height) { 147 m_camera.setViewport(Renderer::Camera::Viewport(x, y, width, height)); 148 m_helper.cameraViewportChanged(); 149 } 150 doRender()151 void UVView::doRender() { 152 if (m_helper.valid()) { 153 MapDocumentSPtr document = lock(m_document); 154 document->commitPendingAssets(); 155 156 Renderer::RenderContext renderContext(Renderer::RenderContext::RenderMode_2D, m_camera, fontManager(), shaderManager()); 157 Renderer::RenderBatch renderBatch(vertexVbo(), indexVbo()); 158 159 setupGL(renderContext); 160 renderTexture(renderContext, renderBatch); 161 renderFace(renderContext, renderBatch); 162 renderToolBox(renderContext, renderBatch); 163 renderTextureAxes(renderContext, renderBatch); 164 165 renderBatch.render(renderContext); 166 } 167 } 168 doShouldRenderFocusIndicator() const169 bool UVView::doShouldRenderFocusIndicator() const { 170 return false; 171 } 172 setupGL(Renderer::RenderContext & renderContext)173 void UVView::setupGL(Renderer::RenderContext& renderContext) { 174 const Renderer::Camera::Viewport& viewport = renderContext.camera().unzoomedViewport(); 175 glAssert(glViewport(viewport.x, viewport.y, viewport.width, viewport.height)); 176 177 glAssert(glEnable(GL_MULTISAMPLE)); 178 glAssert(glEnable(GL_BLEND)); 179 glAssert(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); 180 glAssert(glShadeModel(GL_SMOOTH)); 181 glAssert(glDisable(GL_DEPTH_TEST)); 182 } 183 184 class UVView::RenderTexture : public Renderer::DirectRenderable { 185 private: 186 typedef Renderer::VertexSpecs::P3NT2::Vertex Vertex; 187 188 const UVViewHelper& m_helper; 189 Renderer::VertexArray m_vertexArray; 190 public: RenderTexture(const UVViewHelper & helper)191 RenderTexture(const UVViewHelper& helper) : 192 m_helper(helper) { 193 Vertex::List vertices = getVertices(); 194 m_vertexArray = Renderer::VertexArray::swap(vertices); 195 } 196 private: getVertices()197 Vertex::List getVertices() { 198 const Model::BrushFace* face = m_helper.face(); 199 const Vec3& normal = face->boundary().normal; 200 201 Vertex::List vertices; 202 vertices.reserve(4); 203 204 const Renderer::Camera& camera = m_helper.camera(); 205 const Renderer::Camera::Viewport& v = camera.zoomedViewport(); 206 const float w2 = static_cast<float>(v.width) / 2.0f; 207 const float h2 = static_cast<float>(v.height) / 2.0f; 208 209 const Vec3f& p = camera.position(); 210 const Vec3f& r = camera.right(); 211 const Vec3f& u = camera.up(); 212 213 const Vec3f pos1 = -w2 * r +h2 * u + p; 214 const Vec3f pos2 = +w2 * r +h2 * u + p; 215 const Vec3f pos3 = +w2 * r -h2 * u + p; 216 const Vec3f pos4 = -w2 * r -h2 * u + p; 217 218 vertices.push_back(Vertex(pos1, normal, face->textureCoords(pos1))); 219 vertices.push_back(Vertex(pos2, normal, face->textureCoords(pos2))); 220 vertices.push_back(Vertex(pos3, normal, face->textureCoords(pos3))); 221 vertices.push_back(Vertex(pos4, normal, face->textureCoords(pos4))); 222 223 return vertices; 224 } 225 private: doPrepareVertices(Renderer::Vbo & vertexVbo)226 void doPrepareVertices(Renderer::Vbo& vertexVbo) { 227 m_vertexArray.prepare(vertexVbo); 228 } 229 doRender(Renderer::RenderContext & renderContext)230 void doRender(Renderer::RenderContext& renderContext) { 231 const Model::BrushFace* face = m_helper.face(); 232 const Vec2f& offset = face->offset(); 233 const Vec2f& scale = face->scale(); 234 const Mat4x4 toTex = face->toTexCoordSystemMatrix(offset, scale, true); 235 236 const Assets::Texture* texture = face->texture(); 237 assert(texture != NULL); 238 239 texture->activate(); 240 241 Renderer::ActiveShader shader(renderContext.shaderManager(), Renderer::Shaders::UVViewShader); 242 shader.set("ApplyTexture", true); 243 shader.set("Color", texture->averageColor()); 244 shader.set("Brightness", pref(Preferences::Brightness)); 245 shader.set("RenderGrid", true); 246 shader.set("GridSizes", Vec2f(texture->width(), texture->height())); 247 shader.set("GridColor", Color(0.6f, 0.6f, 0.6f, 1.0f)); // TODO: make this a preference 248 shader.set("GridScales", scale); 249 shader.set("GridMatrix", toTex); 250 shader.set("GridDivider", Vec2f(m_helper.subDivisions())); 251 shader.set("CameraZoom", m_helper.cameraZoom()); 252 shader.set("Texture", 0); 253 254 m_vertexArray.render(GL_QUADS); 255 256 texture->deactivate(); 257 } 258 }; 259 renderTexture(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)260 void UVView::renderTexture(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 261 const Model::BrushFace* face = m_helper.face(); 262 const Assets::Texture* texture = face->texture(); 263 if (texture == NULL) 264 return; 265 266 renderBatch.addOneShot(new RenderTexture(m_helper)); 267 } 268 renderFace(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)269 void UVView::renderFace(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 270 assert(m_helper.valid()); 271 272 const Model::BrushFace* face = m_helper.face(); 273 const Model::BrushFace::VertexList faceVertices = face->vertices(); 274 275 typedef Renderer::VertexSpecs::P3::Vertex Vertex; 276 Vertex::List edgeVertices; 277 edgeVertices.reserve(faceVertices.size()); 278 279 Model::BrushFace::VertexList::const_iterator it, end; 280 for (it = faceVertices.begin(), end = faceVertices.end(); it != end; ++it) 281 edgeVertices.push_back(Vertex((*it)->position())); 282 283 const Color edgeColor(1.0f, 1.0f, 1.0f, 1.0f); // TODO: make this a preference 284 285 Renderer::DirectEdgeRenderer edgeRenderer(Renderer::VertexArray::swap(edgeVertices), GL_LINE_LOOP); 286 edgeRenderer.renderOnTop(renderBatch, edgeColor, 2.5f); 287 } 288 renderTextureAxes(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)289 void UVView::renderTextureAxes(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 290 assert(m_helper.valid()); 291 292 const Model::BrushFace* face = m_helper.face(); 293 const Vec3& normal = face->boundary().normal; 294 295 const Vec3 xAxis = face->textureXAxis() - face->textureXAxis().dot(normal) * normal; 296 const Vec3 yAxis = face->textureYAxis() - face->textureYAxis().dot(normal) * normal; 297 const Vec3 center = face->boundsCenter(); 298 299 typedef Renderer::VertexSpecs::P3C4::Vertex Vertex; 300 Vertex::List vertices; 301 vertices.reserve(4); 302 303 const FloatType length = 32.0 / FloatType(m_helper.cameraZoom()); 304 305 vertices.push_back(Vertex(center, pref(Preferences::XAxisColor))); 306 vertices.push_back(Vertex(center + length * xAxis, pref(Preferences::XAxisColor))); 307 vertices.push_back(Vertex(center, pref(Preferences::YAxisColor))); 308 vertices.push_back(Vertex(center + length * yAxis, pref(Preferences::YAxisColor))); 309 310 Renderer::DirectEdgeRenderer edgeRenderer(Renderer::VertexArray::swap(vertices), GL_LINES); 311 edgeRenderer.renderOnTop(renderBatch, 2.0f); 312 } 313 renderToolBox(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)314 void UVView::renderToolBox(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 315 renderTools(renderContext, renderBatch); 316 } 317 doGetPickRequest(const int x,const int y) const318 PickRequest UVView::doGetPickRequest(const int x, const int y) const { 319 return PickRequest(Ray3(m_camera.pickRay(x, y)), m_camera); 320 } 321 doPick(const Ray3 & pickRay) const322 Model::PickResult UVView::doPick(const Ray3& pickRay) const { 323 Model::PickResult pickResult = Model::PickResult::byDistance(lock(m_document)->editorContext()); 324 if (!m_helper.valid()) 325 return pickResult; 326 327 Model::BrushFace* face = m_helper.face(); 328 const FloatType distance = face->intersectWithRay(pickRay); 329 if (!Math::isnan(distance)) { 330 const Vec3 hitPoint = pickRay.pointAtDistance(distance); 331 pickResult.addHit(Model::Hit(UVView::FaceHit, distance, hitPoint, face)); 332 } 333 return pickResult; 334 } 335 } 336 } 337