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 "UVViewHelper.h" 21 22 #include "PreferenceManager.h" 23 #include "Preferences.h" 24 #include "Assets/Texture.h" 25 #include "Model/BrushFace.h" 26 #include "Model/BrushGeometry.h" 27 #include "Model/PickResult.h" 28 #include "Renderer/OrthographicCamera.h" 29 #include "Renderer/RenderContext.h" 30 #include "Renderer/ShaderManager.h" 31 #include "Renderer/VertexArray.h" 32 #include "Renderer/VertexSpec.h" 33 #include "View/UVView.h" 34 35 namespace TrenchBroom { 36 namespace View { UVViewHelper(Renderer::OrthographicCamera & camera)37 UVViewHelper::UVViewHelper(Renderer::OrthographicCamera& camera) : 38 m_camera(camera), 39 m_zoomValid(false), 40 m_face(NULL), 41 m_subDivisions(1, 1) {} 42 valid() const43 bool UVViewHelper::valid() const { 44 return m_face != NULL; 45 } 46 face() const47 Model::BrushFace* UVViewHelper::face() const { 48 return m_face; 49 } 50 texture() const51 const Assets::Texture* UVViewHelper::texture() const { 52 if (!valid()) 53 return NULL; 54 return m_face->texture(); 55 } 56 setFace(Model::BrushFace * face)57 void UVViewHelper::setFace(Model::BrushFace* face) { 58 if (face != m_face) { 59 m_face = face; 60 if (m_face != NULL) { 61 resetCamera(); 62 resetOrigin(); 63 } 64 } 65 } 66 cameraViewportChanged()67 void UVViewHelper::cameraViewportChanged() { 68 // If the user selects a face before the texturing view was shown for the first time, the size of the view 69 // might still have been off, resulting in invalid zoom factors. Therefore we must reset the zoom whenever 70 // the viewport changes until a valid zoom factor can be computed. 71 if (valid() && !m_zoomValid) 72 resetZoom(); 73 } 74 subDivisions() const75 const Vec2i& UVViewHelper::subDivisions() const { 76 return m_subDivisions; 77 } 78 stripeSize() const79 Vec2 UVViewHelper::stripeSize() const { 80 assert(valid()); 81 82 const Assets::Texture* texture = m_face->texture(); 83 if (texture == NULL) 84 return Vec2::Null; 85 const FloatType width = static_cast<FloatType>(texture->width()) / static_cast<FloatType>(m_subDivisions.x()); 86 const FloatType height = static_cast<FloatType>(texture->height()) / static_cast<FloatType>(m_subDivisions.y()); 87 return Vec2(width, height); 88 } 89 setSubDivisions(const Vec2i & subDivisions)90 void UVViewHelper::setSubDivisions(const Vec2i& subDivisions) { 91 m_subDivisions = subDivisions; 92 } 93 origin() const94 const Vec3 UVViewHelper::origin() const { 95 assert(valid()); 96 97 const Mat4x4 fromTex = m_face->fromTexCoordSystemMatrix(Vec2f::Null, Vec2f::One, true); 98 return fromTex * Vec3(m_origin); 99 } 100 originInFaceCoords() const101 const Vec2f UVViewHelper::originInFaceCoords() const { 102 return m_origin; 103 } 104 originInTexCoords() const105 const Vec2f UVViewHelper::originInTexCoords() const { 106 assert(valid()); 107 108 const Mat4x4 fromTex = m_face->fromTexCoordSystemMatrix(Vec2f::Null, Vec2f::One, true); 109 const Mat4x4 toFace = m_face->toTexCoordSystemMatrix(m_face->offset(), m_face->scale(), true); 110 return Vec2f(toFace * fromTex * Vec3(m_origin)); 111 } 112 setOrigin(const Vec2f & originInFaceCoords)113 void UVViewHelper::setOrigin(const Vec2f& originInFaceCoords) { 114 m_origin = originInFaceCoords; 115 } 116 camera() const117 const Renderer::Camera& UVViewHelper::camera() const { 118 return m_camera; 119 } 120 cameraZoom() const121 float UVViewHelper::cameraZoom() const { 122 return m_camera.zoom(); 123 } 124 pickTextureGrid(const Ray3 & ray,const Model::Hit::HitType hitTypes[2],Model::PickResult & pickResult) const125 void UVViewHelper::pickTextureGrid(const Ray3& ray, const Model::Hit::HitType hitTypes[2], Model::PickResult& pickResult) const { 126 assert(valid()); 127 128 const Assets::Texture* texture = m_face->texture(); 129 if (texture != NULL) { 130 131 const Plane3& boundary = m_face->boundary(); 132 const FloatType rayDistance = ray.intersectWithPlane(boundary.normal, boundary.anchor()); 133 const Vec3 hitPointInWorldCoords = ray.pointAtDistance(rayDistance); 134 const Vec3 hitPointInTexCoords = m_face->toTexCoordSystemMatrix(m_face->offset(), m_face->scale(), true) * hitPointInWorldCoords; 135 136 const FloatType maxDistance = 5.0 / cameraZoom(); 137 const Vec2 stripeSize = UVViewHelper::stripeSize(); 138 139 for (size_t i = 0; i < 2; ++i) { 140 const FloatType closestStrip = Math::roundToMultiple(hitPointInTexCoords[i], stripeSize[i]); 141 const FloatType error = Math::abs(hitPointInTexCoords[i] - closestStrip); 142 if (error <= maxDistance) { 143 const int index = static_cast<int>(Math::round(hitPointInTexCoords[i] / stripeSize[i])); 144 pickResult.addHit(Model::Hit(hitTypes[i], rayDistance, hitPointInWorldCoords, index, error)); 145 } 146 } 147 } 148 } 149 snapDelta(const Vec2f & delta,const Vec2f & distance) const150 Vec2f UVViewHelper::snapDelta(const Vec2f& delta, const Vec2f& distance) const { 151 const float zoom = cameraZoom(); 152 153 Vec2f result; 154 for (size_t i = 0; i < 2; ++i) { 155 if (Math::abs(distance[i]) < 4.0f / zoom) 156 result[i] = delta[i] + distance[i]; 157 else 158 result[i] = Math::round(delta[i]); 159 } 160 return result; 161 } 162 computeDistanceFromTextureGrid(const Vec3 & position) const163 Vec2f UVViewHelper::computeDistanceFromTextureGrid(const Vec3& position) const { 164 const Vec2 stripe = stripeSize(); 165 assert(stripe.x() != 0.0 && stripe.y() != 0); 166 167 const Vec2 closest = position.xy().roundToMultiple(stripe); 168 return Vec2f(closest - position.xy()); 169 } 170 computeOriginHandleVertices(Vec3 & x1,Vec3 & x2,Vec3 & y1,Vec3 & y2) const171 void UVViewHelper::computeOriginHandleVertices(Vec3& x1, Vec3& x2, Vec3& y1, Vec3& y2) const { 172 assert(valid()); 173 174 const Mat4x4 toTex = m_face->toTexCoordSystemMatrix(Vec2f::Null, Vec2f::One, true); 175 const Mat4x4 toWorld = m_face->fromTexCoordSystemMatrix(Vec2f::Null, Vec2f::One, true); 176 computeLineVertices(m_origin, x1, x2, y1, y2, toTex, toWorld); 177 } 178 computeScaleHandleVertices(const Vec2 & pos,Vec3 & x1,Vec3 & x2,Vec3 & y1,Vec3 & y2) const179 void UVViewHelper::computeScaleHandleVertices(const Vec2& pos, Vec3& x1, Vec3& x2, Vec3& y1, Vec3& y2) const { 180 assert(valid()); 181 182 const Mat4x4 toTex = m_face->toTexCoordSystemMatrix(m_face->offset(), m_face->scale(), true); 183 const Mat4x4 toWorld = m_face->fromTexCoordSystemMatrix(m_face->offset(), m_face->scale(), true); 184 computeLineVertices(pos, x1, x2, y1, y2, toTex, toWorld); 185 } 186 computeLineVertices(const Vec2 & pos,Vec3 & x1,Vec3 & x2,Vec3 & y1,Vec3 & y2,const Mat4x4 & toTex,const Mat4x4 & toWorld) const187 void UVViewHelper::computeLineVertices(const Vec2& pos, Vec3& x1, Vec3& x2, Vec3& y1, Vec3& y2, const Mat4x4& toTex, const Mat4x4& toWorld) const { 188 const Vec3::List viewportVertices = toTex * m_camera.viewportVertices(); 189 const BBox3 viewportBounds(viewportVertices); 190 const Vec3& min = viewportBounds.min; 191 const Vec3& max = viewportBounds.max; 192 193 x1 = toWorld * Vec3(pos.x(), min.y(), 0.0); 194 x2 = toWorld * Vec3(pos.x(), max.y(), 0.0); 195 y1 = toWorld * Vec3(min.x(), pos.y(), 0.0); 196 y2 = toWorld * Vec3(max.x(), pos.y(), 0.0); 197 } 198 resetOrigin()199 void UVViewHelper::resetOrigin() { 200 assert(valid()); 201 202 const Mat4x4 toTex = m_face->toTexCoordSystemMatrix(Vec2f::Null, Vec2f::One, true); 203 const BBox3 bounds(toTex * Vec3::asList(m_face->vertices().begin(), m_face->vertices().end(), Model::BrushGeometry::GetVertexPosition())); 204 205 const Vec3 vertices[] = { 206 bounds.vertex(BBox3::Corner_Min, BBox3::Corner_Min, BBox3::Corner_Min), 207 bounds.vertex(BBox3::Corner_Min, BBox3::Corner_Max, BBox3::Corner_Min), 208 bounds.vertex(BBox3::Corner_Max, BBox3::Corner_Max, BBox3::Corner_Min), 209 bounds.vertex(BBox3::Corner_Max, BBox3::Corner_Min, BBox3::Corner_Min) 210 }; 211 212 const Mat4x4 fromTex = m_face->fromTexCoordSystemMatrix(Vec2f::Null, Vec2f::One, true); 213 const Mat4x4 toCam(m_camera.viewMatrix()); 214 215 for (size_t i = 0; i < 4; ++i) { 216 const Vec3 vertex = toCam * fromTex * vertices[i]; 217 if (vertex.x() < 0.0 && vertex.y() < 0.0) { 218 m_origin = Vec2f(vertices[i]); 219 break; 220 } 221 } 222 } 223 resetCamera()224 void UVViewHelper::resetCamera() { 225 assert(valid()); 226 227 const Vec3& normal = m_face->boundary().normal; 228 Vec3 right; 229 230 if (Math::lt(Math::abs(Vec3::PosZ.dot(normal)), 1.0)) 231 right = crossed(Vec3::PosZ, normal).normalized(); 232 else 233 right = Vec3::PosX; 234 const Vec3 up = crossed(normal, right).normalized(); 235 236 m_camera.setNearPlane(-1.0); 237 m_camera.setFarPlane(1.0); 238 m_camera.setDirection(-normal, up); 239 240 const Vec3 position = m_face->boundsCenter(); 241 m_camera.moveTo(position); 242 resetZoom(); 243 } 244 resetZoom()245 void UVViewHelper::resetZoom() { 246 assert(valid()); 247 248 float w = static_cast<float>(m_camera.unzoomedViewport().width); 249 float h = static_cast<float>(m_camera.unzoomedViewport().height); 250 251 if (w <= 1.0f || h <= 1.0f) 252 return; 253 254 if (w > 80.0f) 255 w -= 80.0f; 256 if (h > 80.0f) 257 h -= 80.0f; 258 259 const BBox3 bounds = computeFaceBoundsInCameraCoords(); 260 const Vec3f size(bounds.size()); 261 262 float zoom = 3.0f; 263 zoom = Math::min(zoom, w / size.x()); 264 zoom = Math::min(zoom, h / size.y()); 265 if (zoom > 0.0f) { 266 m_camera.setZoom(zoom); 267 m_zoomValid = true; 268 } 269 } 270 computeFaceBoundsInCameraCoords() const271 BBox3 UVViewHelper::computeFaceBoundsInCameraCoords() const { 272 assert(valid()); 273 274 const Mat4x4 transform = coordinateSystemMatrix(m_camera.right(), m_camera.up(), -m_camera.direction(), m_camera.position()); 275 276 BBox3 result; 277 const Model::BrushFace::VertexList vertices = m_face->vertices(); 278 Model::BrushFace::VertexList::const_iterator it = vertices.begin(); 279 Model::BrushFace::VertexList::const_iterator end = vertices.end(); 280 281 result.min = result.max = transform * (*it++)->position(); 282 while (it != end) 283 result.mergeWith(transform * (*it++)->position()); 284 return result; 285 } 286 } 287 } 288