-
Notifications
You must be signed in to change notification settings - Fork 47
/
Brush.cpp
180 lines (137 loc) · 5.21 KB
/
Brush.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
#include "RadiantTest.h"
#include "ibrush.h"
#include "imap.h"
#include "iselection.h"
#include "itransformable.h"
#include "scenelib.h"
#include "math/Quaternion.h"
namespace test
{
// Fuzzy equality assertion for Plane3
void expectNear(const Plane3& p1, const Plane3& p2, double epsilon)
{
EXPECT_NEAR(p1.normal().x(), p2.normal().x(), epsilon);
EXPECT_NEAR(p1.normal().y(), p2.normal().y(), epsilon);
EXPECT_NEAR(p1.normal().z(), p2.normal().z(), epsilon);
EXPECT_NEAR(p1.dist(), p2.dist(), epsilon);
}
class BrushTest: public RadiantTest
{
protected:
scene::INodePtr _brushNode;
void testFacePlane(const std::function<bool(const IBrushNodePtr&)>& functor)
{
auto worldspawn = GlobalMapModule().findOrInsertWorldspawn();
_brushNode = GlobalBrushCreator().createBrush();
worldspawn->addChildNode(_brushNode);
GlobalSelectionSystem().setSelectedAll(false);
Node_setSelected(_brushNode, true);
const double size = 15;
Vector3 startPos(-size, -size, -size);
Vector3 endPos(size, size, size);
GlobalCommandSystem().executeCommand("ResizeSelectedBrushesToBounds",
startPos, endPos, std::string("shader"));
auto result = functor(std::dynamic_pointer_cast<IBrushNode>(_brushNode));
scene::removeNodeFromParent(_brushNode);
_brushNode.reset();
EXPECT_TRUE(result) << "Test failed to perform anything.";
}
};
inline bool isSane(const Matrix4& matrix)
{
for (std::size_t i = 0; i < 15; ++i)
{
if (std::isnan(matrix[i]) || std::isinf(matrix[i]))
{
return false;
}
}
return true;
}
TEST_F(BrushTest, FitTextureWithZeroScale)
{
auto worldspawn = GlobalMapModule().findOrInsertWorldspawn();
auto brushNode = GlobalBrushCreator().createBrush();
worldspawn->addChildNode(brushNode);
GlobalSelectionSystem().setSelectedAll(false);
Node_setSelected(brushNode, true);
const double size = 15;
Vector3 startPos(-size, -size, -size);
Vector3 endPos(size, size, size);
GlobalCommandSystem().executeCommand("ResizeSelectedBrushesToBounds",
startPos, endPos, std::string("shader"));
GlobalSelectionSystem().setSelectedAll(false);
auto brush = std::dynamic_pointer_cast<IBrushNode>(brushNode);
brush->getIBrush().evaluateBRep();
// Apply a texdef with a 0 scale component to the first face
ShiftScaleRotation scr;
scr.shift[0] = 0.0f;
scr.shift[1] = 0.0f;
scr.scale[0] = 1.0f;
scr.scale[1] = 0.0f; // zero scale
scr.rotate = 0.0f;
auto& face = brush->getIBrush().getFace(0);
face.setShiftScaleRotation(scr);
auto matrix = face.getProjectionMatrix();
EXPECT_FALSE(isSane(matrix)); // 5th matrix component is INF at the least
// Now fit the texture
face.fitTexture(1, 1);
matrix = face.getProjectionMatrix();
// The whole matrix should be sane now
EXPECT_TRUE(isSane(matrix)) << "Texture Projection Matrix is not sane after fitting";
scene::removeNodeFromParent(brushNode);
}
TEST_F(BrushTest, FacePlaneRotateWithMatrix)
{
testFacePlane([this](const IBrushNodePtr& brush)
{
// Get the plane facing down the x axis and check it
for (std::size_t i = 0; i < brush->getIBrush().getNumFaces(); ++i)
{
auto& face = brush->getIBrush().getFace(i);
if (face.getPlane3().normal().isParallel(Vector3(1, 0, 0)))
{
Plane3 orig = face.getPlane3();
// Transform the plane with a rotation matrix
const double ANGLE = 2.0;
Matrix4 rot = Matrix4::getRotation(Vector3(0, 1, 0), ANGLE);
Node_getTransformable(_brushNode)->setRotation(
Quaternion::createForY(-ANGLE)
);
Node_getTransformable(_brushNode)->freezeTransform();
double EPSILON = 0.001;
EXPECT_NE(face.getPlane3(), orig);
expectNear(face.getPlane3(), orig.transformed(rot), EPSILON);
EXPECT_NEAR(face.getPlane3().normal().getLength(), 1, EPSILON);
return true;
}
}
return false;
});
}
TEST_F(BrushTest, FacePlaneTranslate)
{
testFacePlane([this](const IBrushNodePtr& brush)
{
// Get the plane facing down the x axis and check it
for (std::size_t i = 0; i < brush->getIBrush().getNumFaces(); ++i)
{
auto& face = brush->getIBrush().getFace(i);
if (face.getPlane3().normal().isParallel(Vector3(0, 1, 0)))
{
Plane3 orig = face.getPlane3();
// Translate in the Y direction
const Vector3 translation(0, 3, 0);
Node_getTransformable(_brushNode)->setTranslation(translation);
Node_getTransformable(_brushNode)->freezeTransform();
EXPECT_NE(face.getPlane3(), orig);
EXPECT_EQ(face.getPlane3().normal(), orig.normal());
EXPECT_EQ(face.getPlane3().dist(), orig.dist() + translation.y());
EXPECT_NEAR(face.getPlane3().normal().getLength(), 1, 0.001);
return true;
}
}
return false;
});
}
}