some optimizations in indirect lighting code
multiple bounce support
This commit is contained in:
parent
04044dd76d
commit
147be8f87a
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ class lightmap_t
|
|||
public:
|
||||
int style;
|
||||
std::vector<lightsample_t> samples;
|
||||
qvec3d bounce_color;
|
||||
};
|
||||
|
||||
using lightmapdict_t = std::vector<lightmap_t>;
|
||||
|
|
@ -300,7 +301,7 @@ public:
|
|||
setting_scalar phongangle;
|
||||
|
||||
/* bounce */
|
||||
setting_bool bounce;
|
||||
setting_int32 bounce;
|
||||
setting_bool bouncestyled;
|
||||
setting_scalar bouncescale;
|
||||
setting_scalar bouncecolorscale;
|
||||
|
|
|
|||
|
|
@ -39,13 +39,6 @@
|
|||
#include <common/qvec.hh>
|
||||
#include <common/parallel.hh>
|
||||
|
||||
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); });
|
||||
}
|
||||
|
|
@ -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<size_t>(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<size_t>(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);
|
||||
|
|
|
|||
|
|
@ -634,12 +634,7 @@ static std::unique_ptr<lightsurf_t> 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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue