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