From 147be8f87a4083ba688cab688936d1d48409bd1c Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sat, 16 Sep 2023 00:41:05 -0400 Subject: [PATCH] some optimizations in indirect lighting code multiple bounce support --- docs/light.rst | 7 ++-- include/light/bounce.hh | 4 +-- include/light/light.hh | 3 +- light/bounce.cc | 79 ++++++++++++++++++++++++++++------------- light/light.cc | 41 +++++++++++++-------- light/ltface.cc | 25 ++++++------- 6 files changed, 100 insertions(+), 59 deletions(-) diff --git a/docs/light.rst b/docs/light.rst index ed6863a5..84e5fdc8 100644 --- a/docs/light.rst +++ b/docs/light.rst @@ -282,7 +282,7 @@ Debug modes .. option:: -debugmottle - Save mottle pattern (used by Q2 minlight, unless opted out with :bmodel-key:`_minlight_mottle`) + Save mottle pattern (used by Q2 minlight, when opted in with :bmodel-key:`_minlight_mottle`) to lightmap. .. option:: -debugface x y z @@ -531,7 +531,8 @@ The following keys can be added to the *worldspawn* entity: .. worldspawn-key:: "_bounce" "n" - 1 enables bounce lighting, disabled by default. + Non-zero enables bounce lighting, disabled by default. The value is + the maximum number of bounces to perform. .. worldspawn-key:: "_bouncescale" "n" @@ -596,7 +597,7 @@ func_detail/func_group as well, if qbsp from these tools is used. "_minlightMottle" "n" Whether minlight should have a mottled pattern. Defaults - to 1 in Q2 mode and 0 otherwise. + to 0. .. bmodel-key:: "_minlight_exclude" "texname" diff --git a/include/light/bounce.hh b/include/light/bounce.hh index deba5c9d..d22cd570 100644 --- a/include/light/bounce.hh +++ b/include/light/bounce.hh @@ -28,5 +28,5 @@ struct mbsp_t; // public functions -void ResetBounce(); -void MakeBounceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp); +bool MakeBounceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp); +void ClearBounceLights(const mbsp_t *bsp); diff --git a/include/light/light.hh b/include/light/light.hh index 644389df..fab99da2 100644 --- a/include/light/light.hh +++ b/include/light/light.hh @@ -78,6 +78,7 @@ class lightmap_t public: int style; std::vector samples; + qvec3d bounce_color; }; using lightmapdict_t = std::vector; @@ -300,7 +301,7 @@ public: setting_scalar phongangle; /* bounce */ - setting_bool bounce; + setting_int32 bounce; setting_bool bouncestyled; setting_scalar bouncescale; setting_scalar bouncecolorscale; diff --git a/light/bounce.cc b/light/bounce.cc index 57a7256e..51669fad 100644 --- a/light/bounce.cc +++ b/light/bounce.cc @@ -39,13 +39,6 @@ #include #include -static std::atomic_size_t bouncelightpoints; - -void ResetBounce() -{ - bouncelightpoints = 0; -} - static bool Face_ShouldBounce(const mbsp_t *bsp, const mface_t *face) { // make bounce light, only if this face is shadow casting @@ -145,30 +138,30 @@ static void MakeBounceLight(const mbsp_t *bsp, const settings::worldspawn_keys & } } -static void MakeBounceLightsThread(const settings::worldspawn_keys &cfg, const mbsp_t *bsp, const mface_t &face) +static bool MakeBounceLightsThread(const settings::worldspawn_keys &cfg, const mbsp_t *bsp, const mface_t &face) { if (!Face_ShouldBounce(bsp, &face)) { - return; + return false; } auto &surf_ptr = LightSurfaces()[&face - bsp->dfaces.data()]; if (!surf_ptr) { - return; + return false; } auto &surf = *surf_ptr.get(); // no lights if (!surf.lightmapsByStyle.size()) { - return; + return false; } auto winding = polylib::winding_t::from_face(bsp, &face); vec_t area = winding.area(); if (area < 1.f) { - return; + return false; } // Create winding... @@ -181,27 +174,25 @@ static void MakeBounceLightsThread(const settings::worldspawn_keys &cfg, const m bool has_any_color = false; - for (const auto &lightmap : surf.lightmapsByStyle) { + for (auto &lightmap : surf.lightmapsByStyle) { if (lightmap.style && !cfg.bouncestyled.value()) { continue; } - for (auto &sample : lightmap.samples) { - sum[lightmap.style] += sample.color; - } - } - - for (auto &sample : sum) { - if (!qv::emptyExact(sample.second)) { - sample.second /= sample_divisor; + if (!qv::emptyExact(lightmap.bounce_color)) { + sum[lightmap.style] = lightmap.bounce_color / sample_divisor; has_any_color = true; } + + // clear bounced color from lightmap since we + // have "counted" it + lightmap.bounce_color = {}; } // no bounced color, we can leave early if (!has_any_color) { - return; + return false; } // lerp between gray and the texture color according to `bouncecolorscale` (0 = use gray, 1 = use texture color) @@ -240,13 +231,51 @@ static void MakeBounceLightsThread(const settings::worldspawn_keys &cfg, const m for (auto &style : emitcolors) { MakeBounceLight(bsp, cfg, surf, style.second, style.first, points, area, facenormal, facemidpoint); } + + return true; } -void MakeBounceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp) +static void ClearBounceLightsThread(const mbsp_t *bsp, const mface_t &face) +{ + if (!Face_ShouldBounce(bsp, &face)) { + return; + } + + auto &surf_ptr = LightSurfaces()[&face - bsp->dfaces.data()]; + + if (!surf_ptr) { + return; + } + + auto &surf = *surf_ptr.get(); + + // no bouncing yet + if (!surf.vpl) { + return; + } + + // remove all styles that are bounce + auto &l = *surf.vpl; + auto removed = std::remove_if(l.styles.begin(), l.styles.end(), [](surfacelight_t::per_style_t &p) { + return p.bounce; + }); + l.styles.erase(removed, l.styles.end()); +} + +bool MakeBounceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp) { logging::funcheader(); - logging::parallel_for_each(bsp->dfaces, [&](const mface_t &face) { MakeBounceLightsThread(cfg, bsp, face); }); + std::atomic_bool any_to_bounce = false; - // logging::print("{} bounce lights created, with {} points\n", bouncelights.size(), bouncelightpoints); + logging::parallel_for_each(bsp->dfaces, [&](const mface_t &face) { any_to_bounce = MakeBounceLightsThread(cfg, bsp, face) || any_to_bounce; }); + + return any_to_bounce.load(); } + +void ClearBounceLights(const mbsp_t *bsp) +{ + logging::funcheader(); + + logging::parallel_for_each(bsp->dfaces, [&](const mface_t &face) { ClearBounceLightsThread(bsp, face); }); +} \ No newline at end of file diff --git a/light/light.cc b/light/light.cc index e25e768e..e1cb2bdf 100644 --- a/light/light.cc +++ b/light/light.cc @@ -186,7 +186,7 @@ worldspawn_keys::worldspawn_keys() minlight_dirt{this, "minlight_dirt", false, &worldspawn_group}, phongallowed{this, "phong", true, &worldspawn_group}, phongangle{this, "phong_angle", 0, &worldspawn_group}, - bounce{this, "bounce", false, &worldspawn_group}, + bounce{this, "bounce", 0, &worldspawn_group}, bouncestyled{this, "bouncestyled", false, &worldspawn_group}, bouncescale{this, "bouncescale", 1.0, 0.0, 100.0, &worldspawn_group}, bouncecolorscale{this, "bouncecolorscale", 0.0, 0.0, 1.0, &worldspawn_group}, @@ -668,10 +668,12 @@ static void CacheTextures(const mbsp_t &bsp) face_textures[i] = {nullptr, {127}, {0.5}}; } else { auto tex = img::find(name); - face_textures[i] = {tex, tex->averageColor, + auto &ext = extended_texinfo_flags[bsp.dfaces[i].texinfo]; + auto avg = ext.surflight_color.value_or(tex->averageColor); + face_textures[i] = {tex, avg, // lerp between gray and the texture color according to `bouncecolorscale` (0 = use gray, 1 = use // texture color) - mix(qvec3d{127}, qvec3d(tex->averageColor), light_options.bouncecolorscale.value()) / 255.0}; + mix(qvec3d{127}, qvec3d(avg), light_options.bouncecolorscale.value()) / 255.0}; } } } @@ -936,18 +938,29 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale) }); if (bouncerequired && !light_options.nolighting.value()) { - MakeBounceLights(light_options, &bsp); - logging::header("Indirect Lighting"); // mxd - logging::parallel_for(static_cast(0), bsp.dfaces.size(), [&bsp](size_t i) { - if (light_surfaces[i] && Face_IsLightmapped(&bsp, &bsp.dfaces[i])) { -#if defined(HAVE_EMBREE) && defined(__SSE2__) - _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); -#endif + for (size_t i = 0; i < light_options.bounce.value(); i++) { - IndirectLightFace(&bsp, *light_surfaces[i].get(), light_options); + if (i != 0) { + ClearBounceLights(&bsp); } - }); + + if (!MakeBounceLights(light_options, &bsp)) { + logging::header("No bounces; indirect lighting halted"); + break; + } + + logging::header(fmt::format("Indirect Lighting (pass {0})", i).c_str()); // mxd + logging::parallel_for(static_cast(0), bsp.dfaces.size(), [&bsp](size_t i) { + if (light_surfaces[i] && Face_IsLightmapped(&bsp, &bsp.dfaces[i])) { + #if defined(HAVE_EMBREE) && defined(__SSE2__) + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + #endif + + IndirectLightFace(&bsp, *light_surfaces[i].get(), light_options); + } + }); + } } if (!light_options.nolighting.value()) { @@ -1470,7 +1483,6 @@ static void ResetLight() void light_reset() { - ResetBounce(); ResetLightEntities(); ResetLight(); ResetLtFace(); @@ -1561,9 +1573,10 @@ int light_main(int argc, const char **argv) img::load_textures(&bsp, light_options); + LoadExtendedTexinfoFlags(source, &bsp); + CacheTextures(bsp); - LoadExtendedTexinfoFlags(source, &bsp); LoadEntities(light_options, &bsp); light_options.postinitialize(argc, argv); diff --git a/light/ltface.cc b/light/ltface.cc index e4cc3a71..57b8f392 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -634,12 +634,7 @@ static std::unique_ptr Lightsurf_Init(const modelinfo_t *modelinfo, } else if (light_options.minlightMottle.is_changed()) { lightsurf->minlightMottle = light_options.minlightMottle.value(); } else { - // default value depends on game - if (bsp->loadversion->game->id == GAME_QUAKE_II) { - lightsurf->minlightMottle = true; - } else { - lightsurf->minlightMottle = false; - } + lightsurf->minlightMottle = false; } // Q2 uses a 0-1 range for minlight @@ -758,6 +753,7 @@ static void Lightmap_AllocOrClear(lightmap_t *lightmap, const lightsurf_t *light } else if (lightmap->style != INVALID_LIGHTSTYLE) { /* clear only the data that is going to be merged to it. there's no point clearing more */ std::fill_n(lightmap->samples.begin(), lightsurf->samples.size(), lightsample_t{}); + lightmap->bounce_color = {}; } } @@ -801,13 +797,6 @@ static lightmap_t *Lightmap_ForStyle(lightmapdict_t *lightmaps, const int style, return &newLightmap; } -static void Lightmap_ClearAll(lightmapdict_t *lightmaps) -{ - for (auto &lm : *lightmaps) { - lm.style = INVALID_LIGHTSTYLE; - } -} - /* * Lightmap_Save * @@ -1306,6 +1295,7 @@ static void LightFace_Entity( lightsample_t &sample = cached_lightmap->samples[i]; sample.color += rs.getPushedRayColor(j); + cached_lightmap->bounce_color += rs.getPushedRayColor(j); sample.direction += rs.getPushedRayNormalContrib(j); Lightmap_Save(bsp, lightmaps, lightsurf, cached_lightmap, cached_style); @@ -1478,6 +1468,7 @@ static void LightFace_Sky(const mbsp_t *bsp, const sun_t *sun, lightsurf_t *ligh lightsample_t &sample = cached_lightmap->samples[i]; sample.color += rs.getPushedRayColor(j); + cached_lightmap->bounce_color += rs.getPushedRayColor(j); sample.direction += rs.getPushedRayNormalContrib(j); total_light_ray_hits++; @@ -1949,6 +1940,11 @@ inline qvec3f GetSurfaceLighting(const settings::worldspawn_keys &cfg, const sur dotProductFactor = std::max(0.0f, dotProductFactor); + // quick exit + if (!dotProductFactor) { + return {0}; + } + if (vpl_settings.omnidirectional) { dist += cfg.surflightskydist.value(); } @@ -2034,7 +2030,7 @@ LightFace_SurfaceLight(const mbsp_t *bsp, lightsurf_t *lightsurf, lightmapdict_t const qvec3f &pos = vpl.points[c]; qvec3f dir = lightsurf_pos - pos; - float dist = qv::length(dir); + float dist = std::max(0.01f, qv::length(dir)); bool use_normal = true; if (lightsurf->twosided) { @@ -2081,6 +2077,7 @@ LightFace_SurfaceLight(const mbsp_t *bsp, lightsurf_t *lightsurf, lightmapdict_t lightsample_t &sample = lightmap->samples[i]; sample.color += indirect; + lightmap->bounce_color += indirect; hit = true; ++total_surflight_ray_hits;