Skip to content

Commit

Permalink
tests: add test for -hdr and -bspxhdr
Browse files Browse the repository at this point in the history
  • Loading branch information
ericwa committed May 26, 2024
1 parent 09cebf7 commit 45609c4
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 2 deletions.
34 changes: 34 additions & 0 deletions common/bsputils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,40 @@ qvec3b LM_Sample(const mbsp_t *bsp, const lit_variant_t *lit, const faceextents_
}
}

qvec3f LM_Sample_HDR(const mbsp_t *bsp,
const faceextents_t &faceextents,
int byte_offset_of_face, qvec2i coord,
const lit_variant_t *lit, const bspxentries_t *bspx)
{
if (byte_offset_of_face == -1) {
return {0, 0, 0};
}

Q_assert(coord[0] >= 0);
Q_assert(coord[1] >= 0);
Q_assert(coord[0] < faceextents.width());
Q_assert(coord[1] < faceextents.height());

int pixel = coord[0] + (coord[1] * faceextents.width());

assert(byte_offset_of_face >= 0);

const uint32_t *packed_samples = nullptr;
if (lit && std::holds_alternative<lit_hdr>(*lit)) {
packed_samples = std::get_if<lit_hdr>(lit)->samples.data();
} else if (bspx) {
if (auto it = bspx->find("LIGHTING_E5BGR9"); it != bspx->end()) {
// FIXME: alignment ignored
packed_samples = reinterpret_cast<const uint32_t *>(it->second.data());
}
}

if (!packed_samples)
throw std::runtime_error("LM_Sample_HDR requires either an HDR .lit file or BSPX lump");

return HDR_UnpackE5BRG9(packed_samples[byte_offset_of_face + pixel]);
}

static void AddLeafs(const mbsp_t *bsp, int nodenum, std::map<int, std::vector<int>> &cluster_to_leafnums)
{
if (nodenum < 0) {
Expand Down
6 changes: 5 additions & 1 deletion common/litfile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,13 @@ qvec3f HDR_UnpackE5BRG9(uint32_t packed)
return qvec3f(red_int, green_int, blue_int) * multiplier;
}

std::variant<lit1_t, lit_hdr> LoadLitFile(const fs::path &path)
lit_variant_t LoadLitFile(const fs::path &path)
{
std::ifstream stream(path, std::ios_base::in | std::ios_base::binary);
if (!stream.good()) {
return { lit_none() };
}

stream >> endianness<std::endian::little>;

std::array<char, 4> ident;
Expand Down
5 changes: 5 additions & 0 deletions include/common/bsputils.hh
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,9 @@ public:
qvec3b LM_Sample(const mbsp_t *bsp, const lit_variant_t *lit, const faceextents_t &faceextents,
int byte_offset_of_face, qvec2i coord);

qvec3f LM_Sample_HDR(const mbsp_t *bsp,
const faceextents_t &faceextents,
int byte_offset_of_face, qvec2i coord,
const lit_variant_t *lit = nullptr, const bspxentries_t *bspx = nullptr);

std::map<int, std::vector<int>> ClusterToLeafnumsMap(const mbsp_t *bsp);
4 changes: 3 additions & 1 deletion include/common/litfile.hh
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ struct lit_hdr {
std::vector<uint32_t> samples;
};

using lit_variant_t = std::variant<lit1_t, lit_hdr>;
struct lit_none {};

using lit_variant_t = std::variant<lit1_t, lit_hdr, lit_none>;

lit_variant_t LoadLitFile(const fs::path &path);
75 changes: 75 additions & 0 deletions testmaps/q1_hdrtest.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Game: Quake
// Format: Valve
// entity 0
{
"classname" "worldspawn"
"wad" "deprecated/free_wad.wad"
"_tb_def" "builtin:Quake.fgd"
// brush 0
{
( -416 -128 32 ) ( -416 -127 32 ) ( -416 -128 33 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
( -416 -208 32 ) ( -416 -208 33 ) ( -415 -208 32 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -416 -128 32 ) ( -415 -128 32 ) ( -416 -127 32 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 128 272 48 ) ( 128 273 48 ) ( 129 272 48 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 128 272 48 ) ( 129 272 48 ) ( 128 272 49 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 128 272 48 ) ( 128 272 49 ) ( 128 273 48 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 1
{
( -416 -128 288 ) ( -416 -127 288 ) ( -416 -128 289 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
( -416 -208 288 ) ( -416 -208 289 ) ( -415 -208 288 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -416 -128 288 ) ( -415 -128 288 ) ( -416 -127 288 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 128 272 304 ) ( 128 273 304 ) ( 129 272 304 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 128 272 304 ) ( 129 272 304 ) ( 128 272 305 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 128 272 304 ) ( 128 272 305 ) ( 128 273 304 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 2
{
( -432 -128 272 ) ( -432 -127 272 ) ( -432 -128 273 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
( -432 -208 272 ) ( -432 -208 273 ) ( -431 -208 272 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -432 -128 48 ) ( -431 -128 48 ) ( -432 -127 48 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 112 272 288 ) ( 112 273 288 ) ( 113 272 288 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 112 272 288 ) ( 113 272 288 ) ( 112 272 289 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -416 272 288 ) ( -416 272 289 ) ( -416 273 288 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 3
{
( 128 -128 272 ) ( 128 -127 272 ) ( 128 -128 273 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
( 128 -208 272 ) ( 128 -208 273 ) ( 129 -208 272 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 128 -128 48 ) ( 129 -128 48 ) ( 128 -127 48 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 672 272 288 ) ( 672 273 288 ) ( 673 272 288 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 672 272 288 ) ( 673 272 288 ) ( 672 272 289 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 144 272 288 ) ( 144 272 289 ) ( 144 273 288 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 4
{
( -416 -144 272 ) ( -416 -143 272 ) ( -416 -144 273 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
( 96 -224 272 ) ( 96 -224 273 ) ( 97 -224 272 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 96 -144 48 ) ( 97 -144 48 ) ( 96 -143 48 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 640 256 288 ) ( 640 257 288 ) ( 641 256 288 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 640 -208 288 ) ( 641 -208 288 ) ( 640 -208 289 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 128 256 288 ) ( 128 256 289 ) ( 128 257 288 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 5
{
( -416 256 48 ) ( -416 257 48 ) ( -416 256 49 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
( -432 256 48 ) ( -432 256 49 ) ( -431 256 48 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -432 256 48 ) ( -431 256 48 ) ( -432 257 48 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 128 272 288 ) ( 128 273 288 ) ( 129 272 288 ) bolt3 [ 1 0 0 0 ] [ 0 -1 0 -32 ] 0 1 1
( 128 272 64 ) ( 129 272 64 ) ( 128 272 65 ) bolt3 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 128 272 64 ) ( 128 272 65 ) ( 128 273 64 ) bolt3 [ 0 1 0 32 ] [ 0 0 -1 0 ] 0 1 1
}
}
// entity 1
{
"classname" "info_player_start"
"origin" "-292 0 156"
}
// entity 2
{
"classname" "light"
"origin" "-360 200 56"
"light" "15"
"delay" "2"
"_color" "0.75 0.65 0.44"
}
77 changes: 77 additions & 0 deletions tests/test_ltface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,43 @@ static void CheckFaceLuxelAtPoint(const mbsp_t *bsp, const dmodelh2_t *model, co
CHECK(delta[2] <= 1);
}

static void CheckFaceLuxelAtPoint_HDR(const mbsp_t *bsp, const dmodelh2_t *model, const qvec3f &expected_color,
const qvec3f &allowed_delta,
const qvec3d &point, const qvec3d &normal = {0, 0, 0}, const lit_variant_t *lit = nullptr,
const bspxentries_t *bspx = nullptr)
{
auto *face = BSP_FindFaceAtPoint(bsp, model, point, normal);
REQUIRE(face);

faceextents_t extents;
int offset;

if (bspx && bspx->find("DECOUPLED_LM") != bspx->end()) {
auto lm_info = BSPX_DecoupledLM(*bspx, Face_GetNum(bsp, face));
extents = faceextents_t(*face, *bsp, lm_info.lmwidth, lm_info.lmheight, lm_info.world_to_lm_space);
offset = lm_info.offset;
} else {
// vanilla lightmap
extents = faceextents_t(*face, *bsp, LMSCALE_DEFAULT);
offset = face->lightofs;
}

const auto coord = extents.worldToLMCoord(point);
const auto int_coord = qvec2i(round(coord[0]), round(coord[1]));

const qvec3f sample = LM_Sample_HDR(bsp, extents, offset, int_coord, lit, bspx);
INFO("world point: ", point);
INFO("lm coord: ", coord[0], ", ", coord[1]);
INFO("lm int_coord: ", int_coord[0], ", ", int_coord[1]);
INFO("face num: ", Face_GetNum(bsp, face));
INFO("actual sample: ", sample[0], " ", sample[1], " ", sample[2]);

qvec3f delta = qv::abs(sample - expected_color);
CHECK(delta[0] <= allowed_delta[0]);
CHECK(delta[1] <= allowed_delta[1]);
CHECK(delta[2] <= allowed_delta[2]);
}

TEST_CASE("emissive lights")
{
auto [bsp, bspx] = QbspVisLight_Q2("q2_light_flush.map", {});
Expand Down Expand Up @@ -1065,3 +1102,43 @@ TEST_CASE("hl_light_black")
// confirmed that this renders as expected (black lightmaps) in the Dec 2023 HL build
}
}

TEST_CASE("q1 hdr")
{
// center of the room on the floor.
// in the non-HDR lightmap this is pure black (0, 0, 0), but in the HDR one it's still receiving a bit of light
const qvec3f testpoint = {0, 0, 48};
const qvec3f testnormal = {0, 0, 1};
const qvec3f expected_hdr_color = {0.00215912, 0.0018692, 0.00126648};

SUBCASE("lit")
{
auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_hdrtest.map", {"-hdr"});

CHECK(bspx.empty());
CHECK(std::holds_alternative<lit_hdr>(lit));

// check hdr .lit file
CheckFaceLuxelAtPoint_HDR(&bsp, &bsp.dmodels[0], expected_hdr_color, {1e-5, 1e-5, 1e-5}, testpoint, testnormal,
&lit, &bspx);

// check internal lightmap - greyscale, since Q1
CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, testpoint, testnormal);
}

SUBCASE("bspx")
{
auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_hdrtest.map", {"-bspxhdr"});

CHECK(bspx.size() == 1);
CHECK(bspx.find("LIGHTING_E5BGR9") != bspx.end());
CHECK(std::holds_alternative<lit_none>(lit));

// check hdr BSPX lump
CheckFaceLuxelAtPoint_HDR(&bsp, &bsp.dmodels[0], expected_hdr_color, {1e-5, 1e-5, 1e-5}, testpoint, testnormal,
&lit, &bspx);

// check internal lightmap - greyscale, since Q1
CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, testpoint, testnormal);
}
}

0 comments on commit 45609c4

Please sign in to comment.