diff --git a/include/light/trace_embree.hh b/include/light/trace_embree.hh index 05f73115..d1335973 100644 --- a/include/light/trace_embree.hh +++ b/include/light/trace_embree.hh @@ -42,6 +42,10 @@ public: std::vector _ray_colors; std::vector _ray_normalcontribs; + std::vector _ray_hit_glass; + std::vector _ray_glass_color; + std::vector _ray_glass_opacity; + // This is set to the modelinfo's switchshadstyle if the ray hit // a dynamic shadow caster. (note that for rays that hit dynamic // shadow casters, all of the other hit data is assuming the ray went @@ -63,6 +67,9 @@ public: _point_indices.resize(size); _ray_colors.resize(size); _ray_normalcontribs.resize(size); + _ray_hit_glass.resize(size); + _ray_glass_color.resize(size); + _ray_glass_opacity.resize(size); _ray_dynamic_styles.resize(size); } @@ -74,10 +81,23 @@ public: return _point_indices[j]; } - inline qvec3f &getPushedRayColor(size_t j) + inline qvec3f getPushedRayColor(size_t j) { Q_assert(j < _maxrays); - return _ray_colors[j]; + qvec3f result = _ray_colors[j]; + + if (_ray_hit_glass[j]) { + const qvec3f glasscolor = _ray_glass_color[j]; + const float opacity = _ray_glass_opacity[j]; + + // multiply ray color by glass color + const qvec3f tinted = result * glasscolor; + + // lerp ray color between original ray color and fully tinted by the glass texture color, based on the glass opacity + result = mix(result, tinted, opacity); + } + + return result; } inline qvec3d &getPushedRayNormalContrib(size_t j) @@ -210,6 +230,7 @@ public: if (normalcontrib) { _ray_normalcontribs[_numrays] = *normalcontrib; } + _ray_hit_glass[_numrays] = false; _ray_dynamic_styles[_numrays] = 0; _numrays++; } @@ -296,6 +317,7 @@ public: if (normalcontrib) { _ray_normalcontribs[_numrays] = *normalcontrib; } + _ray_hit_glass[_numrays] = false; _ray_dynamic_styles[_numrays] = 0; _numrays++; } diff --git a/light/trace_embree.cc b/light/trace_embree.cc index 65766ead..1f74a6db 100644 --- a/light/trace_embree.cc +++ b/light/trace_embree.cc @@ -678,11 +678,9 @@ static void AddGlassToRay(RTCIntersectContext *context, unsigned rayIndex, float Q_assert(rayIndex < rs->_numrays); - // multiply ray color by glass color - qvec3f tinted = rs->_ray_colors[rayIndex] * glasscolor; - - // lerp ray color between original ray color and fully tinted by the glass texture color, based on the glass opacity - rs->_ray_colors[rayIndex] = mix(rs->_ray_colors[rayIndex], tinted, opacity); + rs->_ray_hit_glass[rayIndex] = true; + rs->_ray_glass_color[rayIndex] = glasscolor; + rs->_ray_glass_opacity[rayIndex] = opacity; } static void AddDynamicOccluderToRay(RTCIntersectContext *context, unsigned rayIndex, int style) diff --git a/testmaps/q2_light_translucency.map b/testmaps/q2_light_translucency.map new file mode 100644 index 00000000..84653c6e --- /dev/null +++ b/testmaps/q2_light_translucency.map @@ -0,0 +1,87 @@ +// Game: Quake 2 +// Format: Quake2 (Valve) +// entity 0 +{ +"mapversion" "220" +"classname" "worldspawn" +"_tb_textures" "textures/e1u1" +"_bounce" "0" +"_tb_def" "builtin:Quake2.fgd" +// brush 0 +{ +( -160 -256 16 ) ( -160 -255 16 ) ( -160 -256 17 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -384 16 ) ( 80 -384 17 ) ( 81 -384 16 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -256 16 ) ( 81 -256 16 ) ( 80 -255 16 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 -32 32 ) ( 496 -31 32 ) ( 497 -32 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 368 32 ) ( 497 368 32 ) ( 496 368 33 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 496 -32 32 ) ( 496 -32 33 ) ( 496 -31 32 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 1 +{ +( -160 -384 32 ) ( -160 -383 32 ) ( -160 -384 33 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -160 -384 32 ) ( -160 -384 33 ) ( -159 -384 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -160 -384 32 ) ( -159 -384 32 ) ( -160 -383 32 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 256 -320 288 ) ( 256 -319 288 ) ( 257 -320 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 256 -320 48 ) ( 257 -320 48 ) ( 256 -320 49 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 496 -320 48 ) ( 496 -320 49 ) ( 496 -319 48 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 2 +{ +( -160 304 32 ) ( -160 305 32 ) ( -160 304 33 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -160 304 32 ) ( -160 304 33 ) ( -159 304 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -160 304 32 ) ( -159 304 32 ) ( -160 305 32 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 256 368 288 ) ( 256 369 288 ) ( 257 368 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 256 368 48 ) ( 257 368 48 ) ( 256 368 49 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 496 368 48 ) ( 496 368 49 ) ( 496 369 48 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 3 +{ +( -160 240 32 ) ( -160 241 32 ) ( -160 240 33 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -784 -320 32 ) ( -784 -320 33 ) ( -783 -320 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -784 240 32 ) ( -783 240 32 ) ( -784 241 32 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -368 304 288 ) ( -368 305 288 ) ( -367 304 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -368 304 48 ) ( -367 304 48 ) ( -368 304 49 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -128 304 48 ) ( -128 304 49 ) ( -128 305 48 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 4 +{ +( -160 -256 288 ) ( -160 -255 288 ) ( -160 -256 289 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -320 288 ) ( 80 -320 289 ) ( 81 -320 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -256 288 ) ( 81 -256 288 ) ( 80 -255 288 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 -32 304 ) ( 496 -31 304 ) ( 497 -32 304 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 496 304 304 ) ( 497 304 304 ) ( 496 304 305 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 496 -32 304 ) ( 496 -32 305 ) ( 496 -31 304 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 5 +{ +( 496 240 32 ) ( 496 241 32 ) ( 496 240 33 ) e1u1/floor1_1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -128 -320 32 ) ( -128 -320 33 ) ( -127 -320 32 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -128 240 32 ) ( -127 240 32 ) ( -128 241 32 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 288 304 288 ) ( 288 305 288 ) ( 289 304 288 ) e1u1/floor1_1 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 288 304 48 ) ( 289 304 48 ) ( 288 304 49 ) e1u1/floor1_1 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 528 304 48 ) ( 528 304 49 ) ( 528 305 48 ) e1u1/floor1_1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 6 +{ +( 64 -176 176 ) ( 64 -175 176 ) ( 64 -176 177 ) e1u1/skip [ 0 -1 0 0 ] [ 0 0 -1 16 ] 0 1 1 32 128 0 +( 24 -176 176 ) ( 24 -176 177 ) ( 25 -176 176 ) e1u1/skip [ -1.0000000000000002 0 0 16 ] [ 0 0 -1.0000000000000002 16 ] 0 1 1 32 128 0 +( 24 -176 208 ) ( 25 -176 208 ) ( 24 -175 208 ) e1u1/skip [ 0 -1.0000000000000002 0 0 ] [ -1.0000000000000002 0 0 0 ] 0 1 1 32 128 0 +( 336 48 224 ) ( 336 49 224 ) ( 337 48 224 ) e1u1/test [ 1 0 0 0 ] [ 0 -1 0 32 ] 0 2 2 32 16 0 +( 336 0 184 ) ( 337 0 184 ) ( 336 0 185 ) e1u1/skip [ 1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 16 ] 0 1 1 32 128 0 +( 256 48 184 ) ( 256 48 185 ) ( 256 49 184 ) e1u1/skip [ 0 -1 0 0 ] [ 0 0 -1 16 ] 0 1 1 32 128 0 +} +} +// entity 1 +{ +"classname" "info_player_start" +"origin" "152 -256 96" +"angle" "90" +} +// entity 2 +{ +"classname" "light" +"origin" "152 -96 248" +"light" "150" +"delay" "3" +"_anglesense" "0" +} diff --git a/testmaps/q2_wal_metadata/textures/e1u1/test.wal b/testmaps/q2_wal_metadata/textures/e1u1/test.wal new file mode 100644 index 00000000..9d4c88ab Binary files /dev/null and b/testmaps/q2_wal_metadata/textures/e1u1/test.wal differ diff --git a/tests/test_ltface.cc b/tests/test_ltface.cc index 8ebb0304..edb45ab0 100644 --- a/tests/test_ltface.cc +++ b/tests/test_ltface.cc @@ -222,3 +222,17 @@ TEST_CASE("q2_dirt") { CHECK(sample == qvec3b(96)); }); } + +TEST_CASE("q2_light_translucency") { + INFO("liquids cast translucent colored shadows (sampling texture) by default"); + + auto [bsp, bspx] = LoadTestmap("q2_light_translucency.map", {}); + + auto *face_under_water = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {152, -96, 32}); + REQUIRE(face_under_water); + + CheckFaceLuxels(bsp, *face_under_water, [](qvec3b sample){ + INFO("green color from the texture"); + CHECK(sample == qvec3b(100, 150, 100)); + }); +}