diff --git a/light/ltface.cc b/light/ltface.cc index ac6cc220..a1a2ec5f 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -1957,14 +1957,17 @@ LightFace_SurfaceLight(const mbsp_t *bsp, lightsurf_t *lightsurf, lightmapdict_t qvec3f pos = vpl.points[c]; qvec3f dir = lightsurf_pos - pos; float dist = qv::length(dir); + bool use_normal = true; - if (dist == 0.0f) + if (dist == 0.0f) { dir = lightsurf_normal; - else + use_normal = false; + } else { dir /= dist; + } const qvec3f indirect = GetSurfaceLighting( - cfg, &vpl, dir, dist, lightsurf_normal, true, standard_scale, sky_scale, hotspot_clamp); + cfg, &vpl, dir, dist, lightsurf_normal, use_normal, standard_scale, sky_scale, hotspot_clamp); if (!qv::gate(indirect, surflight_gate)) { // Each point contributes very little to the final result rs.pushRay(i, pos, dir, dist, &indirect); } diff --git a/testmaps/q2_light_divzero.map b/testmaps/q2_light_divzero.map new file mode 100644 index 00000000..6fd694b1 --- /dev/null +++ b/testmaps/q2_light_divzero.map @@ -0,0 +1,36 @@ +// Game: Quake 2 +// Format: Quake2 (Valve) +// entity 0 +{ +"classname" "worldspawn" +"_surflight_minlight_scale" "0" +"_minlight_mottle" "0" +"_tb_textures" "textures/e1u1" +// brush 0 +{ +( -1152 -224 -608 ) ( -1152 -223 -608 ) ( -1152 -224 -607 ) e1u1/skip [ 0 -1 0 0 ] [ 0 0 -1 16 ] 0 1 1 1 640 0 +( -1296 -224 -608 ) ( -1296 -224 -607 ) ( -1295 -224 -608 ) e1u1/skip [ 1 0 0 0 ] [ 0 0 -1 80 ] 0 1 1 1 640 0 +( -1296 -224 -608 ) ( -1295 -224 -608 ) ( -1296 -223 -608 ) e1u1/skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 1 640 0 +( -640 160 -592 ) ( -640 161 -592 ) ( -639 160 -592 ) e1u1/skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 1 640 0 +( -640 160 -592 ) ( -639 160 -592 ) ( -640 160 -591 ) e1u1/skip [ -1 0 0 0 ] [ 0 0 -1 80 ] 0 1 1 1 640 0 +( -848 160 -592 ) ( -848 160 -591 ) ( -848 161 -592 ) e1u1/skip [ 0 1 0 0 ] [ 0 0 -1 16 ] 0 1 1 1 640 0 +} +} +// entity 1 +{ +"classname" "func_group" +// brush 0 +{ +( -992 16 -472 ) ( -992 17 -472 ) ( -992 16 -471 ) e1u1/skip [ 0 0 -1.0000000000000002 -16 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +( -976 0 -480 ) ( -977 0 -480 ) ( -976 0 -479 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 16 ] 0 1 1 +( -976 0 -480 ) ( -976 1 -480 ) ( -977 0 -480 ) e1u1/baselt_5 [ 1.6081226496766366e-16 -1 0 0 ] [ 1 1.6081226496766366e-16 0 0 ] 90 1 1 0 1 2500 +( -992 16 -472 ) ( -993 16 -472 ) ( -992 17 -472 ) e1u1/skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -992 16 -472 ) ( -992 16 -471 ) ( -993 16 -472 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 0 ] 0 1 1 +( -976 0 -480 ) ( -976 0 -479 ) ( -976 1 -480 ) e1u1/skip [ 0 0 1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +} +} +// entity 2 +{ +"classname" "info_player_start" +"origin" "-1040 0 -568" +} diff --git a/tests/test_ltface.cc b/tests/test_ltface.cc index 886d1cbd..f05ef516 100644 --- a/tests/test_ltface.cc +++ b/tests/test_ltface.cc @@ -778,4 +778,13 @@ TEST_CASE("lit water opt-in") auto *ti = Face_Texinfo(&bsp, f); CHECK(ti->flags.native == TEX_SPECIAL); } +} + +TEST_CASE("q2_light_divzero") +{ + auto [bsp, bspx] = QbspVisLight_Q2("q2_light_divzero.map", {"-world_units_per_luxel", "8"}); + + INFO("should not have a black spot in the center of the light face"); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {255, 127, 63}, {-992, 0, -480}, {0, 0, -1}, nullptr, &bspx); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {255, 127, 63}, {-984, 8, -480}, {0, 0, -1}, nullptr, &bspx); } \ No newline at end of file