diff --git a/include/light/trace_embree.hh b/include/light/trace_embree.hh index af1e8a34..d0d17172 100644 --- a/include/light/trace_embree.hh +++ b/include/light/trace_embree.hh @@ -24,12 +24,15 @@ #include // for FError #include +#include struct mbsp_t; class modelinfo_t; +struct mface_t; void ResetEmbree(); void Embree_TraceInit(const mbsp_t *bsp); +const std::set &ShadowCastingSolidFacesSet(); class raystream_embree_common_t { diff --git a/light/bounce.cc b/light/bounce.cc index c90f7d81..f1c97795 100644 --- a/light/bounce.cc +++ b/light/bounce.cc @@ -28,6 +28,7 @@ #include #include #include // for Light_PointInLeaf +#include // for ShadowCastingSolidFacesSet #include #include @@ -42,10 +43,13 @@ static bool Face_ShouldBounce(const mbsp_t *bsp, const mface_t *face) { // make bounce light, only if this face is shadow casting - const modelinfo_t *mi = ModelInfoForFace(bsp, Face_GetNum(bsp, face)); - if (!mi || !mi->shadow.boolValue()) { + + // NOTE: this should be the same set of faces that are unconditionally shadow casting + // (so exclude fences, water, etc.) which is why we're currently fetching it from the embree + // code, because the condition is quite complex and we don't want to try to repeat it here. + auto &face_set = ShadowCastingSolidFacesSet(); + if (face_set.find(face) == face_set.end()) return false; - } if (!Face_IsLightmapped(bsp, face)) { return false; @@ -67,14 +71,6 @@ static bool Face_ShouldBounce(const mbsp_t *bsp, const mface_t *face) return false; } - // don't bounce from faces on non-default object channels - if (mi->object_channel_mask.value() != CHANNEL_MASK_DEFAULT) { - return false; - } - if (ext_info.object_channel_mask.value_or(CHANNEL_MASK_DEFAULT) != CHANNEL_MASK_DEFAULT) { - return false; - } - return true; } diff --git a/light/trace_embree.cc b/light/trace_embree.cc index 3a541c07..ce06b807 100644 --- a/light/trace_embree.cc +++ b/light/trace_embree.cc @@ -26,11 +26,15 @@ #include #include #include +#include sceneinfo skygeom; // sky. always occludes. sceneinfo solidgeom; // solids. always occludes. sceneinfo filtergeom; // conditional occluders.. needs to run ray intersection filter +// set of faces in `solidgeom`, +std::set shadow_casting_solid_faces; + static RTCDevice device; RTCScene scene; @@ -41,6 +45,7 @@ void ResetEmbree() skygeom = {}; solidgeom = {}; filtergeom = {}; + shadow_casting_solid_faces = {}; if (scene) { rtcReleaseScene(scene); @@ -55,6 +60,12 @@ void ResetEmbree() bsp_static = nullptr; } +const std::set &ShadowCastingSolidFacesSet() +{ + return shadow_casting_solid_faces; +} + + /** * Returns 1.0 unless a custom alpha value is set. * The priority is: "_light_alpha" (read from extended_texinfo_flags), then "alpha", then Q2 surface flags @@ -675,6 +686,11 @@ void Embree_TraceInit(const mbsp_t *bsp) rtcCommitScene(scene); + // keep a backup of solidfaces + for (const mface_t *face : solidfaces) { + shadow_casting_solid_faces.insert(face); + } + logging::funcprint("\n"); logging::print("\t{} sky faces\n", skyfaces.size()); logging::print("\t{} solid faces\n", solidfaces.size()); diff --git a/testmaps/q1_light_bounce_litwater.map b/testmaps/q1_light_bounce_litwater.map new file mode 100644 index 00000000..cdeb7c44 --- /dev/null +++ b/testmaps/q1_light_bounce_litwater.map @@ -0,0 +1,86 @@ +// 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 72" +} +// entity 2 +{ +"classname" "light" +"origin" "-136 24 148" +"delay" "2" +} +// entity 3 +{ +"classname" "func_detail" +// brush 0 +{ +( 0 112 48 ) ( 0 113 48 ) ( 0 112 49 ) *swater5 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 0 -208 48 ) ( 0 -208 49 ) ( 1 -208 48 ) *swater5 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 0 112 48 ) ( 1 112 48 ) ( 0 113 48 ) *swater5 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 128 256 288 ) ( 128 257 288 ) ( 129 256 288 ) *swater5 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 128 256 64 ) ( 129 256 64 ) ( 128 256 65 ) *swater5 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 128 256 64 ) ( 128 256 65 ) ( 128 257 64 ) *swater5 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +} diff --git a/testmaps/q1_light_bounce_noshadow.map b/testmaps/q1_light_bounce_noshadow.map new file mode 100644 index 00000000..e64dcbb4 --- /dev/null +++ b/testmaps/q1_light_bounce_noshadow.map @@ -0,0 +1,88 @@ +// Game: Quake +// Format: Valve +// entity 0 +{ +"classname" "worldspawn" +"wad" "deprecated/free_wad.wad;deprecated/fence.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 72" +} +// entity 2 +{ +"classname" "func_detail_illusionary" +"_mirrorinside" "0" +"_shadow" "-1" +// brush 0 +{ +( 0 823.1111111111112 48 ) ( 0 826.3333333333334 48 ) ( 0 823.1111111111112 49 ) grate1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 0 -192 48 ) ( 0 -192 49 ) ( 1 -192 48 ) grate1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 0 823.1111111111112 48 ) ( 1 823.1111111111112 48 ) ( 0 826.3333333333334 48 ) grate1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 128 1287.1111111111113 272 ) ( 128 1290.3333333333335 272 ) ( 129 1287.1111111111113 272 ) grate1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 128 224 64 ) ( 129 224 64 ) ( 128 224 65 ) grate1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 128 1287.1111111111113 64 ) ( 128 1287.1111111111113 65 ) ( 128 1290.3333333333335 64 ) grate1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +} +// entity 3 +{ +"classname" "light" +"origin" "-136 24 148" +"delay" "2" +} diff --git a/tests/test_ltface.cc b/tests/test_ltface.cc index 1f346aed..cf2470ff 100644 --- a/tests/test_ltface.cc +++ b/tests/test_ltface.cc @@ -852,3 +852,26 @@ TEST_CASE("q1_sunlight") auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_sunlight.map", {"-lit"}); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {49, 49, 49}, {0, 0, 0}, {0, 0, 1}, &lit); } + +TEST_CASE("q1_light_bounce_litwater without the water") +{ + auto [bsp, bspx] = QbspVisLight_Common("q1_light_bounce_litwater.map", {"-omitdetail"}, {"-lit", "-bounce", "4"}, runvis_t::no); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {118, 118, 118}, {128, 12, 156}, {-1, 0, 0}); +} + +TEST_CASE("q1_light_bounce_litwater") +{ + INFO("adding a water plane should not affect the amount of light bounced on to the walls"); + + auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_bounce_litwater.map", {"-lit", "-bounce", "4"}); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {118, 118, 118}, {128, 12, 156}, {-1, 0, 0}); +} + +TEST_CASE("q1_light_bounce_noshadow") +{ + INFO("make sure light doesn't both pass through and bounce off of a face with _shadow -1"); + + auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_bounce_noshadow.map", {"-lit", "-bounce", "4"}); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {118, 118, 118}, {128, 12, 156}, {-1, 0, 0}); +} +