qbsp: add failing tests for healing invalid texture projections

This commit is contained in:
Eric Wasylishen 2018-01-28 01:54:58 -07:00
parent 5a90667738
commit 23b25a0a0e
5 changed files with 116 additions and 0 deletions

View File

@ -106,6 +106,10 @@ public:
return true;
}
bool operator!=(const qvec<N,T> &other) const {
return !(*this == other);
}
T operator[](const int idx) const {
assert(idx >= 0 && idx < N);
return v[idx];

View File

@ -195,4 +195,7 @@ void ExportObj_Surfaces(const surface_t *surfaces);
void WriteBspBrushMap(const char *name, const std::vector<const brush_t *> &list);
bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec);
#endif

View File

@ -1394,6 +1394,26 @@ void mapface_t::set_texvecs(const std::array<qvec4f, 2> &vecs)
this->texinfo = FindTexinfo( &texInfoNew, texInfoNew.flags );
}
bool
IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec)
{
// TODO: This doesn't match how light does it (TexSpaceToWorld)
const qvec3f tex_normal = qv::normalize(qv::cross(s_vec, t_vec));
for (int i=0;i<3;i++)
if (std::isnan(tex_normal[i]))
return false;
const float cosangle = qv::dot(tex_normal, faceNormal);
if (std::isnan(cosangle))
return false;
if (fabs(cosangle) < ZERO_EPSILON)
return false;
return true;
}
static std::unique_ptr<mapface_t>
ParseBrushFace(parser_t *parser, const mapbrush_t *brush, const mapentity_t *entity)
{

View File

@ -307,6 +307,94 @@ TEST(qbsp, MemLeaks) {
}
#endif
/**
* Test that this skip face gets auto-corrected.
*/
TEST(qbsp, InvalidTextureProjection) {
const char *map = R"(
// entity 0
{
"classname" "worldspawn"
// brush 0
{
( -64 -64 -16 ) ( -64 -63 -16 ) ( -64 -64 -15 ) +2butn [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 64 64 16 ) ( 64 64 17 ) ( 64 65 16 ) +2butn [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -64 -64 -16 ) ( -64 -64 -15 ) ( -63 -64 -16 ) +2butn [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 64 64 16 ) ( 65 64 16 ) ( 64 64 17 ) +2butn [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 64 64 64 ) ( 64 65 64 ) ( 65 64 64 ) +2butn [ 1 0 0 -0 ] [ 0 -1 0 -0 ] -0 1 1
( -64 -64 -0 ) ( -63 -64 -0 ) ( -64 -63 -0 ) skip [ 0 0 0 0 ] [ 0 0 0 0 ] -0 1 1
}
}
)";
mapentity_t worldspawn = LoadMap(map);
Q_assert(1 == worldspawn.nummapbrushes);
const mapface_t *face = &worldspawn.mapbrush(0).face(5);
ASSERT_EQ("skip", face->texname);
const auto texvecs = face->get_texvecs();
EXPECT_TRUE(IsValidTextureProjection(vec3_t_to_glm(face->plane.normal), texvecs.at(0), texvecs.at(1)));
}
/**
* Same as above but the texture scales are 0
*/
TEST(qbsp, InvalidTextureProjection2) {
const char *map = R"(
// entity 0
{
"classname" "worldspawn"
// brush 0
{
( -64 -64 -16 ) ( -64 -63 -16 ) ( -64 -64 -15 ) +2butn [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 64 64 16 ) ( 64 64 17 ) ( 64 65 16 ) +2butn [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -64 -64 -16 ) ( -64 -64 -15 ) ( -63 -64 -16 ) +2butn [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 64 64 16 ) ( 65 64 16 ) ( 64 64 17 ) +2butn [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 64 64 64 ) ( 64 65 64 ) ( 65 64 64 ) +2butn [ 1 0 0 -0 ] [ 0 -1 0 -0 ] -0 1 1
( -64 -64 -0 ) ( -63 -64 -0 ) ( -64 -63 -0 ) skip [ 0 0 0 0 ] [ 0 0 0 0 ] -0 0 0
}
}
)";
mapentity_t worldspawn = LoadMap(map);
Q_assert(1 == worldspawn.nummapbrushes);
const mapface_t *face = &worldspawn.mapbrush(0).face(5);
ASSERT_EQ("skip", face->texname);
const auto texvecs = face->get_texvecs();
EXPECT_TRUE(IsValidTextureProjection(vec3_t_to_glm(face->plane.normal), texvecs.at(0), texvecs.at(1)));
}
/**
* More realistic: *lava1 has tex vecs perpendicular to face
*/
TEST(qbsp, InvalidTextureProjection3) {
const char *map = R"(
// entity 0
{
"classname" "worldspawn"
"wad" "Q.wad"
// brush 0
{
( 512 512 64 ) ( 512 512 -0 ) ( 512 448 64 ) *04mwat1 [ 0 1 0 0 ] [ 0 0 -1 0 ] -0 1 1
( -0 448 -0 ) ( -0 512 -0 ) ( -0 448 64 ) *04mwat1 [ 0 -1 0 0 ] [ -0 -0 -1 0 ] -0 1 1
( 512 512 64 ) ( -0 512 64 ) ( 512 512 -0 ) *04mwat1 [ -1 0 0 0 ] [ 0 0 -1 0 ] -0 1 1
( -0 448 -0 ) ( -0 448 64 ) ( 512 448 -0 ) *lava1 [ 0 1 0 0 ] [ 0 0 -1 0 ] -0 1 1
( 512 512 64 ) ( 512 448 64 ) ( -0 512 64 ) *04mwat1 [ 1 0 0 0 ] [ 0 -1 0 0 ] -0 1 1
( -0 448 -0 ) ( 512 448 -0 ) ( -0 512 -0 ) *04mwat1 [ -1 0 0 0 ] [ -0 -1 -0 -0 ] -0 1 1
}
}
)";
mapentity_t worldspawn = LoadMap(map);
Q_assert(1 == worldspawn.nummapbrushes);
const mapface_t *face = &worldspawn.mapbrush(0).face(3);
ASSERT_EQ("*lava1", face->texname);
const auto texvecs = face->get_texvecs();
EXPECT_TRUE(IsValidTextureProjection(vec3_t_to_glm(face->plane.normal), texvecs.at(0), texvecs.at(1)));
}
TEST(mathlib, WindingArea) {
winding_t w;
w.numpoints = 5;

View File

@ -71,6 +71,7 @@ AllocMem(int Type, int cElements, bool fZero)
if (Type == FACE && cElements == 1)
((face_t *)pTemp)->planenum = -1;
if (Type == WINDING) {
// FIXME: Remove this! Causing UBSan warnings
*(int *)pTemp = cSize;
pTemp = (char *)pTemp + sizeof(int);
}