/
TextureMatrix.cpp
219 lines (185 loc) · 6.72 KB
/
TextureMatrix.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include "TextureMatrix.h"
#include "texturelib.h"
#include "math/Vector2.h"
// Constructor with empty arguments
TextureMatrix::TextureMatrix()
{
coords[0][0] = 2.0f;
coords[0][1] = 0.f;
coords[0][2] = 0.f;
coords[1][0] = 0.f;
coords[1][1] = 2.0f;
coords[1][2] = 0.f;
}
// Construct the BP Definition out of the transformation matrix
// Basically copies over the values from the according components
TextureMatrix::TextureMatrix(const Matrix4& transform)
{
coords[0][0] = transform.xx();
coords[0][1] = transform.yx();
coords[0][2] = transform.tx();
coords[1][0] = transform.xy();
coords[1][1] = transform.yy();
coords[1][2] = transform.ty();
}
// Construct a TextureMatrix out of "fake" shift scale rot definitions
TextureMatrix::TextureMatrix(const TexDef& texdef)
{
auto r = degrees_to_radians(-texdef.getRotation());
auto c = cos(r);
auto s = sin(r);
auto scale = texdef.getScale();
auto x = 1.0 / scale[0];
auto y = 1.0 / scale[1];
auto shift = texdef.getShift();
coords[0][0] = x * c;
coords[1][0] = x * s;
coords[0][1] = y * -s;
coords[1][1] = y * c;
coords[0][2] = -shift[0];
coords[1][2] = shift[1];
}
// shift a texture (texture adjustments) along it's current texture axes
void TextureMatrix::shift(double s, double t)
{
// x and y are geometric values, which we must compute as ST increments
// this depends on the texture size and the pixel/texel ratio
// as a ratio against texture size
// the scale of the texture is not relevant here (we work directly on a transformation from the base vectors)
coords[0][2] -= s;
coords[1][2] += t;
}
// apply same scale as the spinner button of the surface inspector
void TextureMatrix::scale(double s, double t, std::size_t shaderWidth, std::size_t shaderHeight)
{
// We need to have the shader dimensions applied before calling getFakeTexCoords()
applyShaderDimensions(shaderWidth, shaderHeight);
// compute fake shift scale rot
TexDef texdef = getFakeTexCoords();
auto scale = texdef.getScale();
double newXScale = scale[0] + s;
double newYScale = scale[1] + t;
// Don't allow zero (or almost zero) scale values
if (float_equal_epsilon(newXScale, 0, 1e-5) ||
float_equal_epsilon(newYScale, 0, 1e-5))
{
return;
}
// Don't allow sign changes
if ((newXScale*scale[0]) < 0.0 ||
(newYScale*scale[1]) < 0.0)
{
return;
}
// update
scale[0] = newXScale;
scale[1] = newYScale;
texdef.setScale(scale);
// compute new normalized texture matrix
*this = TextureMatrix(texdef);
// Undo the previous step of adding the texture scale
addScale(shaderWidth, shaderHeight);
}
// apply same rotation as the spinner button of the surface inspector
void TextureMatrix::rotate(double angle, std::size_t shaderWidth, std::size_t shaderHeight)
{
// We need to have the shader dimensions applied before calling getFakeTexCoords()
applyShaderDimensions(shaderWidth, shaderHeight);
// compute fake shift scale rot
TexDef texdef = getFakeTexCoords();
// update
texdef.setRotation(texdef.getRotation() + angle);
// compute new normalized texture matrix
*this = TextureMatrix(texdef);
// Undo the previous step of adding the texture scale
addScale(shaderWidth, shaderHeight);
}
/* greebo: This removes the texture scaling from the
* coordinates. The resulting coordinates are absolute
* values within the shader image.
*
* An 128x256 texture with scaled coordinates 0.5,0.5
* would be translated into the coordinates 64,128,
* pointing to a defined pixel within the texture image.
*/
void TextureMatrix::applyShaderDimensions(std::size_t width, std::size_t height) {
coords[0][0] *= width;
coords[0][1] *= width;
coords[0][2] *= width;
coords[1][0] *= height;
coords[1][1] *= height;
coords[1][2] *= height;
}
/* greebo: this converts absolute coordinates into
* relative ones, where everything is measured
* in multiples of the texture x/y dimensions. */
void TextureMatrix::addScale(std::size_t width, std::size_t height) {
coords[0][0] /= width;
coords[0][1] /= width;
coords[0][2] /= width;
coords[1][0] /= height;
coords[1][1] /= height;
coords[1][2] /= height;
}
// compute a fake shift scale rot representation from the texture matrix
// these shift scale rot values are to be understood in the local axis base
// Note: this code looks similar to Texdef_fromTransform, but the algorithm is slightly different.
TexDef TextureMatrix::getFakeTexCoords() const
{
TexDef texdef;
texdef._scale[0] = 1.0 / Vector2(coords[0][0], coords[1][0]).getLength();
texdef._scale[1] = 1.0 / Vector2(coords[0][1], coords[1][1]).getLength();
texdef._rotate = -radians_to_degrees(arctangent_yx(coords[1][0], coords[0][0]));
texdef._shift[0] = -coords[0][2];
texdef._shift[1] = coords[1][2];
// determine whether or not an axis is flipped using a 2d cross-product
auto cross = Vector2(coords[0][0], coords[0][1]).crossProduct(Vector2(coords[1][0], coords[1][1]));
if (cross < 0)
{
// This is a bit of a compromise when using BPs--since we don't know *which* axis was flipped,
// we pick one (rather arbitrarily) using the following convention: If the X-axis is between
// 0 and 180, we assume it's the Y-axis that flipped, otherwise we assume it's the X-axis and
// subtract out 180 degrees to compensate.
if (texdef._rotate >= 180.0)
{
texdef._rotate -= 180.0;
texdef._scale[0] = -texdef._scale[0];
}
else
{
texdef._scale[1] = -texdef._scale[1];
}
}
return texdef;
}
// All texture-projection translation (shift) values are congruent modulo the dimensions of the texture.
// This function normalises shift values to the smallest positive congruent values.
void TextureMatrix::normalise(float width, float height) {
coords[0][2] = float_mod(coords[0][2], width);
coords[1][2] = float_mod(coords[1][2], height);
}
/* greebo: This returns the transformation matrix.
* As the member variables already ARE the matrix
* components, they are just copied into the right places.
*/
Matrix4 TextureMatrix::getTransform() const {
// Initialise the return value with the identity matrix
Matrix4 transform = Matrix4::getIdentity();
// Just copy the member variables to the according matrix components
transform.xx() = coords[0][0];
transform.yx() = coords[0][1];
transform.tx() = coords[0][2];
transform.xy() = coords[1][0];
transform.yy() = coords[1][1];
transform.ty() = coords[1][2];
return transform;
}
bool TextureMatrix::isSane() const
{
return !std::isnan(coords[0][0]) && !std::isinf(coords[0][0]) &&
!std::isnan(coords[0][1]) && !std::isinf(coords[0][1]) &&
!std::isnan(coords[0][2]) && !std::isinf(coords[0][2]) &&
!std::isnan(coords[1][0]) && !std::isinf(coords[1][0]) &&
!std::isnan(coords[1][1]) && !std::isinf(coords[1][1]) &&
!std::isnan(coords[1][2]) && !std::isinf(coords[1][2]);
}