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 "ParallelTexCoordSystem.h"
21 #include "Assets/Texture.h"
22 #include "Model/ParaxialTexCoordSystem.h"
23 #include "Model/BrushFace.h"
24 
25 namespace TrenchBroom {
26     namespace Model {
ParallelTexCoordSystemSnapshot(ParallelTexCoordSystem * coordSystem)27         ParallelTexCoordSystemSnapshot::ParallelTexCoordSystemSnapshot(ParallelTexCoordSystem* coordSystem) :
28         m_coordSystem(coordSystem),
29         m_xAxis(m_coordSystem->xAxis()),
30         m_yAxis(m_coordSystem->yAxis()) {}
31 
doRestore()32         void ParallelTexCoordSystemSnapshot::doRestore() {
33             m_coordSystem->m_xAxis = m_xAxis;
34             m_coordSystem->m_yAxis = m_yAxis;
35         }
36 
ParallelTexCoordSystem(const Vec3 & point0,const Vec3 & point1,const Vec3 & point2,const BrushFaceAttributes & attribs)37         ParallelTexCoordSystem::ParallelTexCoordSystem(const Vec3& point0, const Vec3& point1, const Vec3& point2, const BrushFaceAttributes& attribs) {
38             const Vec3 normal = crossed(point2 - point0, point1 - point0).normalized();
39             computeInitialAxes(normal, m_xAxis, m_yAxis);
40             applyRotation(normal, attribs.rotation());
41         }
42 
ParallelTexCoordSystem(const Vec3 & xAxis,const Vec3 & yAxis,const BrushFaceAttributes & attribs)43         ParallelTexCoordSystem::ParallelTexCoordSystem(const Vec3& xAxis, const Vec3& yAxis, const BrushFaceAttributes& attribs) :
44         m_xAxis(xAxis),
45         m_yAxis(yAxis) {}
46 
doClone() const47         TexCoordSystem* ParallelTexCoordSystem::doClone() const {
48             return new ParallelTexCoordSystem(*this);
49         }
50 
doTakeSnapshot()51         TexCoordSystemSnapshot* ParallelTexCoordSystem::doTakeSnapshot() {
52             return new ParallelTexCoordSystemSnapshot(this);
53         }
54 
getXAxis() const55         Vec3 ParallelTexCoordSystem::getXAxis() const {
56             return m_xAxis;
57         }
58 
getYAxis() const59         Vec3 ParallelTexCoordSystem::getYAxis() const {
60             return m_yAxis;
61         }
62 
getZAxis() const63         Vec3 ParallelTexCoordSystem::getZAxis() const {
64             return crossed(m_xAxis, m_yAxis).normalized();
65         }
66 
doResetTextureAxes(const Vec3 & normal)67         void ParallelTexCoordSystem::doResetTextureAxes(const Vec3& normal) {
68             computeInitialAxes(normal, m_xAxis, m_yAxis);
69         }
70 
doResetTextureAxesToParaxial(const Vec3 & normal,float angle)71         void ParallelTexCoordSystem::doResetTextureAxesToParaxial(const Vec3& normal, float angle) {
72             const size_t index = ParaxialTexCoordSystem::planeNormalIndex(normal);
73             ParaxialTexCoordSystem::axes(index, m_xAxis, m_yAxis);
74             applyRotation(normal, static_cast<FloatType>(angle));
75         }
76 
doResetTextureAxesToParallel(const Vec3 & normal,float angle)77         void ParallelTexCoordSystem::doResetTextureAxesToParallel(const Vec3& normal, float angle) {
78             computeInitialAxes(normal, m_xAxis, m_yAxis);
79             applyRotation(normal, static_cast<FloatType>(angle));
80         }
81 
isRotationInverted(const Vec3 & normal) const82         bool ParallelTexCoordSystem::isRotationInverted(const Vec3& normal) const {
83             return false;
84         }
85 
doGetTexCoords(const Vec3 & point,const BrushFaceAttributes & attribs) const86         Vec2f ParallelTexCoordSystem::doGetTexCoords(const Vec3& point, const BrushFaceAttributes& attribs) const {
87             return (computeTexCoords(point, attribs.scale()) + attribs.offset()) / attribs.textureSize();
88         }
89 
doSetRotation(const Vec3 & normal,const float oldAngle,const float newAngle)90         void ParallelTexCoordSystem::doSetRotation(const Vec3& normal, const float oldAngle, const float newAngle) {
91             const float angleDelta = newAngle - oldAngle;
92             if (angleDelta == 0.0f)
93                 return;
94 
95             const FloatType angle = static_cast<FloatType>(Math::radians(-angleDelta));
96             applyRotation(normal, angle);
97         }
98 
applyRotation(const Vec3 & normal,const FloatType angle)99         void ParallelTexCoordSystem::applyRotation(const Vec3& normal, const FloatType angle) {
100             const Quat3 rot(normal, angle);
101             m_xAxis = rot * m_xAxis;
102             m_yAxis = rot * m_yAxis;
103         }
104 
doTransform(const Plane3 & oldBoundary,const Mat4x4 & transformation,BrushFaceAttributes & attribs,bool lockTexture,const Vec3 & oldInvariant)105         void ParallelTexCoordSystem::doTransform(const Plane3& oldBoundary, const Mat4x4& transformation, BrushFaceAttributes& attribs, bool lockTexture, const Vec3& oldInvariant) {
106 
107             // compute the new texture axes
108             const Vec3 offset = transformation * Vec3::Null;
109             m_xAxis           = transformation * m_xAxis - offset;
110             m_yAxis           = transformation * m_yAxis - offset;
111             assert(!m_xAxis.nan());
112             assert(!m_yAxis.nan());
113 
114             if (!lockTexture || attribs.xScale() == 0.0f || attribs.yScale() == 0.0f)
115                 return;
116 
117             // determine the rotation by which the texture coordinate system will be rotated about its normal
118             const float angleDelta = computeTextureAngle(oldBoundary, transformation);
119             const float newAngle = Math::correct(Math::normalizeDegrees(attribs.rotation() + angleDelta), 4);
120             assert(!Math::isnan(newAngle));
121             attribs.setRotation(newAngle);
122 
123             // calculate the current texture coordinates of the face's center
124             const Vec2f oldInvariantTechCoords = computeTexCoords(oldInvariant, attribs.scale()) + attribs.offset();
125             assert(!oldInvariantTechCoords.nan());
126 
127             // determine the new texture coordinates of the transformed center of the face, sans offsets
128             const Vec3 newInvariant = transformation * oldInvariant;
129             const Vec2f newInvariantTexCoords = computeTexCoords(newInvariant, attribs.scale());
130 
131             // since the center should be invariant, the offsets are determined by the difference of the current and
132             // the original texture coordinates of the center
133             const Vec2f newOffset = attribs.modOffset(oldInvariantTechCoords - newInvariantTexCoords).corrected(4);
134             assert(!newOffset.nan());
135             attribs.setOffset(newOffset);
136         }
137 
computeTextureAngle(const Plane3 & oldBoundary,const Mat4x4 & transformation) const138         float ParallelTexCoordSystem::computeTextureAngle(const Plane3& oldBoundary, const Mat4x4& transformation) const {
139             const Mat4x4& rotation = stripTranslation(transformation);
140             const Vec3& oldNormal = oldBoundary.normal;
141             const Vec3  newNormal = rotation * oldNormal;
142 
143             const Mat4x4 nonRotation = computeNonTextureRotation(oldNormal, newNormal, rotation);
144             const Vec3 newXAxis = (rotation * m_xAxis).normalized();
145             const Vec3 nonXAxis = (nonRotation * m_xAxis).normalized();
146             const FloatType angle = Math::degrees(angleBetween(nonXAxis, newXAxis, newNormal));
147             return static_cast<float>(angle);
148         }
149 
computeNonTextureRotation(const Vec3 & oldNormal,const Vec3 & newNormal,const Mat4x4 & rotation) const150         Mat4x4 ParallelTexCoordSystem::computeNonTextureRotation(const Vec3& oldNormal, const Vec3& newNormal, const Mat4x4& rotation) const {
151             if (oldNormal.equals(newNormal))
152                 return Mat4x4::Identity;
153 
154             if (oldNormal.equals(-newNormal)) {
155                 const Vec3 minorAxis = oldNormal.majorAxis(2);
156                 const Vec3 axis = crossed(oldNormal, minorAxis).normalized();
157                 return rotationMatrix(axis, Math::C::pi());
158             }
159 
160             const Vec3 axis = crossed(newNormal, oldNormal).normalized();
161             const FloatType angle = angleBetween(newNormal, oldNormal, axis);
162             return rotationMatrix(axis, angle);
163         }
164 
doUpdateNormal(const Vec3 & oldNormal,const Vec3 & newNormal,const BrushFaceAttributes & attribs)165         void ParallelTexCoordSystem::doUpdateNormal(const Vec3& oldNormal, const Vec3& newNormal, const BrushFaceAttributes& attribs) {
166             Quat3 rotation;
167             const Vec3 cross = crossed(oldNormal, newNormal);
168             if (cross.null()) {
169                 rotation = Quat3(oldNormal.makePerpendicular(), Math::C::pi());
170             } else {
171                 const Vec3 axis = cross.normalized();
172                 const FloatType angle = angleBetween(newNormal, oldNormal, axis);
173                 rotation = Quat3(axis, angle);
174             }
175             m_xAxis = rotation * m_xAxis;
176             m_yAxis = rotation * m_yAxis;
177         }
178 
doShearTexture(const Vec3 & normal,const Vec2f & f)179         void ParallelTexCoordSystem::doShearTexture(const Vec3& normal, const Vec2f& f) {
180             const Mat4x4 shear( 1.0, f[0], 0.0, 0.0,
181                                f[1],  1.0, 0.0, 0.0,
182                                 0.0,  0.0, 1.0, 0.0,
183                                 0.0,  0.0, 0.0, 1.0);
184 
185             const Mat4x4 toMatrix = coordinateSystemMatrix(m_xAxis, m_yAxis, getZAxis(), Vec3::Null);
186             const Mat4x4 fromMatrix = invertedMatrix(toMatrix);
187 
188             const Mat4x4 transform = fromMatrix * shear * toMatrix;
189             m_xAxis = transform * m_xAxis;
190             m_yAxis = transform * m_yAxis;
191         }
192 
doMeasureAngle(const float currentAngle,const Vec2f & center,const Vec2f & point) const193         float ParallelTexCoordSystem::doMeasureAngle(const float currentAngle, const Vec2f& center, const Vec2f& point) const {
194             const Vec3 vec(point - center);
195             const FloatType angleInRadians = angleBetween(vec.normalized(), Vec3::PosX, Vec3::PosZ);
196             return static_cast<float>(currentAngle + Math::degrees(angleInRadians));
197         }
198 
computeInitialAxes(const Vec3 & normal,Vec3 & xAxis,Vec3 & yAxis) const199         void ParallelTexCoordSystem::computeInitialAxes(const Vec3& normal, Vec3& xAxis, Vec3& yAxis) const {
200             const Math::Axis::Type first = normal.firstComponent();
201 
202             switch (first) {
203                 case Math::Axis::AX:
204                 case Math::Axis::AY:
205                     xAxis = crossed(Vec3::PosZ, normal).normalized();
206                     break;
207                 case Math::Axis::AZ:
208                     xAxis = crossed(Vec3::PosY, normal).normalized();
209                     break;
210             }
211 
212             yAxis = crossed(m_xAxis, normal).normalized();
213         }
214     }
215 }
216