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