diff --git a/include/common/bsputils.hh b/include/common/bsputils.hh index 2a6f37b5..a70d94db 100644 --- a/include/common/bsputils.hh +++ b/include/common/bsputils.hh @@ -65,7 +65,7 @@ std::vector BSP_FindFacesAtPoint( * is used to disambiguate these. */ const mface_t *BSP_FindFaceAtPoint( - const mbsp_t *bsp, const dmodelh2_t *model, const qvec3d &point, const qvec3d &wantedNormal); + const mbsp_t *bsp, const dmodelh2_t *model, const qvec3d &point, const qvec3d &wantedNormal = qvec3d(0, 0, 0)); /** * Searches for a decision node in hull0 that contains `point`, and has a plane normal of either * wanted_normal or -wanted_normal. diff --git a/light/ltface.cc b/light/ltface.cc index 1295e48f..91aa5734 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -1709,9 +1709,9 @@ inline qvec3f GetSurfaceLighting(const settings::worldspawn_keys &cfg, const sur float dp2 = qv::dot(sp_vpl, normal); if (!vpl->omnidirectional) { - if (dp1 < 0.0f) + if (dp1 < -LIGHT_ANGLE_EPSILON) return {0}; // sample point behind vpl - if (dp2 < 0.0f) + if (dp2 < -LIGHT_ANGLE_EPSILON) return {0}; // vpl behind sample face // Rescale a bit to brighten the faces nearly-perpendicular to the surface light plane... diff --git a/testmaps/q2_light_flush.map b/testmaps/q2_light_flush.map new file mode 100644 index 00000000..e0e49c59 --- /dev/null +++ b/testmaps/q2_light_flush.map @@ -0,0 +1,69 @@ +// Game: Quake 2 +// Format: Quake2 (Valve) +// entity 0 +{ +"mapversion" "220" +"classname" "worldspawn" +"_tb_textures" "textures/e1u1" +"_bounce" "0" +// brush 0 +{ +( 304 -32 176 ) ( 288 -48 304 ) ( 288 -48 176 ) e1u1/color1_6 [ -0.7071067811865476 -0.7071067811865476 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 240 -384 32 ) ( 240 -384 33 ) ( 241 -384 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 0 ] 0 1 1 +( 240 -48 32 ) ( 241 -48 32 ) ( 240 -47 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( 432 -32 160 ) ( 432 -31 160 ) ( 433 -32 160 ) e1u1/skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 160 -176 160 ) ( 160 -256 176 ) ( 160 -256 304 ) e1u1/skip [ 0 0 1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +} +// brush 1 +{ +( 160 -176 160 ) ( 160 -256 304 ) ( 160 -256 176 ) e1u1/skip [ 0 0 -1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( 304 -32 176 ) ( 288 -48 304 ) ( 288 -48 176 ) e1u1/baselt_5 [ 1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 32 ] 0 1 1 0 1 1000 +( 240 -384 32 ) ( 240 -384 33 ) ( 241 -384 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 0 ] 0 1 1 +( 240 -48 32 ) ( 241 -48 32 ) ( 240 -47 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( 432 -32 160 ) ( 432 -31 160 ) ( 433 -32 160 ) e1u1/skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 192 -144 112 ) ( 192 -144 144 ) ( 192 -16 144 ) e1u1/skip [ 0 0 1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +} +// brush 2 +{ +( 192 -144 112 ) ( 192 -16 144 ) ( 192 -144 144 ) e1u1/skip [ 0 0 -1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( 304 -32 176 ) ( 288 -48 304 ) ( 288 -48 176 ) e1u1/color1_6 [ -0.7071067811865476 -0.7071067811865476 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 240 -384 32 ) ( 240 -384 33 ) ( 241 -384 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 0 ] 0 1 1 +( 240 -48 32 ) ( 241 -48 32 ) ( 240 -47 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( 432 -32 160 ) ( 432 -31 160 ) ( 433 -32 160 ) e1u1/skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 432 -32 48 ) ( 433 -32 48 ) ( 432 -32 49 ) e1u1/color1_6 [ 1.0000000000000002 0 0 -48 ] [ 0 0 -1.0000000000000002 0 ] 0 1 1 +( 352 -32 128 ) ( 352 96 96 ) ( 352 -32 96 ) e1u1/skip [ 0 0 1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +} +// brush 3 +{ +( 352 -32 128 ) ( 352 -32 96 ) ( 352 96 96 ) e1u1/skip [ 0 0 -1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( 240 -384 32 ) ( 240 -384 33 ) ( 241 -384 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 0 ] 0 1 1 +( 240 -48 32 ) ( 241 -48 32 ) ( 240 -47 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( 432 -32 160 ) ( 432 -31 160 ) ( 433 -32 160 ) e1u1/skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 432 -32 48 ) ( 433 -32 48 ) ( 432 -32 49 ) e1u1/baselt_5 [ 1.0000000000000002 0 0 -64 ] [ 0 0 -1.0000000000000002 32 ] 0 1 1 0 1 1000 +( 384 -32 160 ) ( 384 96 128 ) ( 384 -32 128 ) e1u1/skip [ 0 0 1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +} +// brush 4 +{ +( 384 -32 160 ) ( 384 -32 128 ) ( 384 96 128 ) e1u1/skip [ 0 0 -1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( 240 -384 32 ) ( 240 -384 33 ) ( 241 -384 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 0 ] 0 1 1 +( 240 -48 32 ) ( 241 -48 32 ) ( 240 -47 32 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( 432 -32 160 ) ( 432 -31 160 ) ( 433 -32 160 ) e1u1/skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 432 -32 48 ) ( 433 -32 48 ) ( 432 -32 49 ) e1u1/color1_6 [ 1.0000000000000002 0 0 -48 ] [ 0 0 -1.0000000000000002 0 ] 0 1 1 +( 496 -32 48 ) ( 496 -32 49 ) ( 496 -31 48 ) e1u1/skip [ 0 0 1.0000000000000002 16 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +} +// brush 5 +{ +( -160 -256 16 ) ( -160 -255 16 ) ( -160 -256 17 ) e1u1/skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -384 16 ) ( 80 -384 17 ) ( 81 -384 16 ) e1u1/skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -256 16 ) ( 81 -256 16 ) ( 80 -255 16 ) e1u1/skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 -32 32 ) ( 496 -31 32 ) ( 497 -32 32 ) e1u1/skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 368 32 ) ( 497 368 32 ) ( 496 368 33 ) e1u1/skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 496 -32 32 ) ( 496 -32 33 ) ( 496 -31 32 ) e1u1/skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +} +// entity 1 +{ +"classname" "info_player_start" +"origin" "288 144 56" +"angle" "270" +} diff --git a/tests/test_ltface.cc b/tests/test_ltface.cc index 2ef5aba9..c95218a9 100644 --- a/tests/test_ltface.cc +++ b/tests/test_ltface.cc @@ -147,3 +147,19 @@ TEST_CASE("-novanilla + -world_units_per_luxel") } CHECK(bsp.dlightdata.size() == expected_dlightdata_bytes); } + +TEST_CASE("emissive lights") { + auto [bsp, bspx] = LoadTestmap("q2_light_flush.map", {}); + + // all of this face should be receiving some light + auto *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {244, -92, 92}); + const faceextents_t extents(*face, bsp, LMSCALE_DEFAULT); + + for (int x = 0; x < extents.width(); ++x) { + for (int y = 0; y < extents.height(); ++y) { + auto sample = LM_Sample(&bsp, extents, face->lightofs, {x, y}); + INFO("sample ", x, ", ", y); + CHECK(sample[0] > 0); + } + } +}