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
|
.. 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.
|
to lightmap.
|
||||||
|
|
||||||
.. option:: -debugface x y z
|
.. option:: -debugface x y z
|
||||||
|
|
@ -531,7 +531,8 @@ The following keys can be added to the *worldspawn* entity:
|
||||||
|
|
||||||
.. worldspawn-key:: "_bounce" "n"
|
.. 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"
|
.. worldspawn-key:: "_bouncescale" "n"
|
||||||
|
|
||||||
|
|
@ -596,7 +597,7 @@ func_detail/func_group as well, if qbsp from these tools is used.
|
||||||
"_minlightMottle" "n"
|
"_minlightMottle" "n"
|
||||||
|
|
||||||
Whether minlight should have a mottled pattern. Defaults
|
Whether minlight should have a mottled pattern. Defaults
|
||||||
to 1 in Q2 mode and 0 otherwise.
|
to 0.
|
||||||
|
|
||||||
.. bmodel-key:: "_minlight_exclude" "texname"
|
.. bmodel-key:: "_minlight_exclude" "texname"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,5 +28,5 @@ struct mbsp_t;
|
||||||
|
|
||||||
// public functions
|
// public functions
|
||||||
|
|
||||||
void ResetBounce();
|
bool MakeBounceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp);
|
||||||
void MakeBounceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp);
|
void ClearBounceLights(const mbsp_t *bsp);
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ class lightmap_t
|
||||||
public:
|
public:
|
||||||
int style;
|
int style;
|
||||||
std::vector<lightsample_t> samples;
|
std::vector<lightsample_t> samples;
|
||||||
|
qvec3d bounce_color;
|
||||||
};
|
};
|
||||||
|
|
||||||
using lightmapdict_t = std::vector<lightmap_t>;
|
using lightmapdict_t = std::vector<lightmap_t>;
|
||||||
|
|
@ -300,7 +301,7 @@ public:
|
||||||
setting_scalar phongangle;
|
setting_scalar phongangle;
|
||||||
|
|
||||||
/* bounce */
|
/* bounce */
|
||||||
setting_bool bounce;
|
setting_int32 bounce;
|
||||||
setting_bool bouncestyled;
|
setting_bool bouncestyled;
|
||||||
setting_scalar bouncescale;
|
setting_scalar bouncescale;
|
||||||
setting_scalar bouncecolorscale;
|
setting_scalar bouncecolorscale;
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,6 @@
|
||||||
#include <common/qvec.hh>
|
#include <common/qvec.hh>
|
||||||
#include <common/parallel.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)
|
static bool Face_ShouldBounce(const mbsp_t *bsp, const mface_t *face)
|
||||||
{
|
{
|
||||||
// make bounce light, only if this face is shadow casting
|
// 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)) {
|
if (!Face_ShouldBounce(bsp, &face)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &surf_ptr = LightSurfaces()[&face - bsp->dfaces.data()];
|
auto &surf_ptr = LightSurfaces()[&face - bsp->dfaces.data()];
|
||||||
|
|
||||||
if (!surf_ptr) {
|
if (!surf_ptr) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &surf = *surf_ptr.get();
|
auto &surf = *surf_ptr.get();
|
||||||
|
|
||||||
// no lights
|
// no lights
|
||||||
if (!surf.lightmapsByStyle.size()) {
|
if (!surf.lightmapsByStyle.size()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto winding = polylib::winding_t::from_face(bsp, &face);
|
auto winding = polylib::winding_t::from_face(bsp, &face);
|
||||||
vec_t area = winding.area();
|
vec_t area = winding.area();
|
||||||
|
|
||||||
if (area < 1.f) {
|
if (area < 1.f) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create winding...
|
// Create winding...
|
||||||
|
|
@ -181,27 +174,25 @@ static void MakeBounceLightsThread(const settings::worldspawn_keys &cfg, const m
|
||||||
|
|
||||||
bool has_any_color = false;
|
bool has_any_color = false;
|
||||||
|
|
||||||
for (const auto &lightmap : surf.lightmapsByStyle) {
|
for (auto &lightmap : surf.lightmapsByStyle) {
|
||||||
|
|
||||||
if (lightmap.style && !cfg.bouncestyled.value()) {
|
if (lightmap.style && !cfg.bouncestyled.value()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &sample : lightmap.samples) {
|
if (!qv::emptyExact(lightmap.bounce_color)) {
|
||||||
sum[lightmap.style] += sample.color;
|
sum[lightmap.style] = lightmap.bounce_color / sample_divisor;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &sample : sum) {
|
|
||||||
if (!qv::emptyExact(sample.second)) {
|
|
||||||
sample.second /= sample_divisor;
|
|
||||||
has_any_color = true;
|
has_any_color = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear bounced color from lightmap since we
|
||||||
|
// have "counted" it
|
||||||
|
lightmap.bounce_color = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// no bounced color, we can leave early
|
// no bounced color, we can leave early
|
||||||
if (!has_any_color) {
|
if (!has_any_color) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// lerp between gray and the texture color according to `bouncecolorscale` (0 = use gray, 1 = use texture color)
|
// 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) {
|
for (auto &style : emitcolors) {
|
||||||
MakeBounceLight(bsp, cfg, surf, style.second, style.first, points, area, facenormal, facemidpoint);
|
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::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},
|
minlight_dirt{this, "minlight_dirt", false, &worldspawn_group},
|
||||||
phongallowed{this, "phong", true, &worldspawn_group},
|
phongallowed{this, "phong", true, &worldspawn_group},
|
||||||
phongangle{this, "phong_angle", 0, &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},
|
bouncestyled{this, "bouncestyled", false, &worldspawn_group},
|
||||||
bouncescale{this, "bouncescale", 1.0, 0.0, 100.0, &worldspawn_group},
|
bouncescale{this, "bouncescale", 1.0, 0.0, 100.0, &worldspawn_group},
|
||||||
bouncecolorscale{this, "bouncecolorscale", 0.0, 0.0, 1.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}};
|
face_textures[i] = {nullptr, {127}, {0.5}};
|
||||||
} else {
|
} else {
|
||||||
auto tex = img::find(name);
|
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
|
// lerp between gray and the texture color according to `bouncecolorscale` (0 = use gray, 1 = use
|
||||||
// texture color)
|
// 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()) {
|
if (bouncerequired && !light_options.nolighting.value()) {
|
||||||
MakeBounceLights(light_options, &bsp);
|
|
||||||
|
|
||||||
logging::header("Indirect Lighting"); // mxd
|
for (size_t i = 0; i < light_options.bounce.value(); i++) {
|
||||||
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 (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()) {
|
if (!light_options.nolighting.value()) {
|
||||||
|
|
@ -1470,7 +1483,6 @@ static void ResetLight()
|
||||||
|
|
||||||
void light_reset()
|
void light_reset()
|
||||||
{
|
{
|
||||||
ResetBounce();
|
|
||||||
ResetLightEntities();
|
ResetLightEntities();
|
||||||
ResetLight();
|
ResetLight();
|
||||||
ResetLtFace();
|
ResetLtFace();
|
||||||
|
|
@ -1561,9 +1573,10 @@ int light_main(int argc, const char **argv)
|
||||||
|
|
||||||
img::load_textures(&bsp, light_options);
|
img::load_textures(&bsp, light_options);
|
||||||
|
|
||||||
|
LoadExtendedTexinfoFlags(source, &bsp);
|
||||||
|
|
||||||
CacheTextures(bsp);
|
CacheTextures(bsp);
|
||||||
|
|
||||||
LoadExtendedTexinfoFlags(source, &bsp);
|
|
||||||
LoadEntities(light_options, &bsp);
|
LoadEntities(light_options, &bsp);
|
||||||
|
|
||||||
light_options.postinitialize(argc, argv);
|
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()) {
|
} else if (light_options.minlightMottle.is_changed()) {
|
||||||
lightsurf->minlightMottle = light_options.minlightMottle.value();
|
lightsurf->minlightMottle = light_options.minlightMottle.value();
|
||||||
} else {
|
} else {
|
||||||
// default value depends on game
|
lightsurf->minlightMottle = false;
|
||||||
if (bsp->loadversion->game->id == GAME_QUAKE_II) {
|
|
||||||
lightsurf->minlightMottle = true;
|
|
||||||
} else {
|
|
||||||
lightsurf->minlightMottle = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Q2 uses a 0-1 range for minlight
|
// 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) {
|
} else if (lightmap->style != INVALID_LIGHTSTYLE) {
|
||||||
/* clear only the data that is going to be merged to it. there's no point clearing more */
|
/* 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{});
|
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;
|
return &newLightmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Lightmap_ClearAll(lightmapdict_t *lightmaps)
|
|
||||||
{
|
|
||||||
for (auto &lm : *lightmaps) {
|
|
||||||
lm.style = INVALID_LIGHTSTYLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lightmap_Save
|
* Lightmap_Save
|
||||||
*
|
*
|
||||||
|
|
@ -1306,6 +1295,7 @@ static void LightFace_Entity(
|
||||||
lightsample_t &sample = cached_lightmap->samples[i];
|
lightsample_t &sample = cached_lightmap->samples[i];
|
||||||
|
|
||||||
sample.color += rs.getPushedRayColor(j);
|
sample.color += rs.getPushedRayColor(j);
|
||||||
|
cached_lightmap->bounce_color += rs.getPushedRayColor(j);
|
||||||
sample.direction += rs.getPushedRayNormalContrib(j);
|
sample.direction += rs.getPushedRayNormalContrib(j);
|
||||||
|
|
||||||
Lightmap_Save(bsp, lightmaps, lightsurf, cached_lightmap, cached_style);
|
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];
|
lightsample_t &sample = cached_lightmap->samples[i];
|
||||||
|
|
||||||
sample.color += rs.getPushedRayColor(j);
|
sample.color += rs.getPushedRayColor(j);
|
||||||
|
cached_lightmap->bounce_color += rs.getPushedRayColor(j);
|
||||||
sample.direction += rs.getPushedRayNormalContrib(j);
|
sample.direction += rs.getPushedRayNormalContrib(j);
|
||||||
total_light_ray_hits++;
|
total_light_ray_hits++;
|
||||||
|
|
||||||
|
|
@ -1949,6 +1940,11 @@ inline qvec3f GetSurfaceLighting(const settings::worldspawn_keys &cfg, const sur
|
||||||
|
|
||||||
dotProductFactor = std::max(0.0f, dotProductFactor);
|
dotProductFactor = std::max(0.0f, dotProductFactor);
|
||||||
|
|
||||||
|
// quick exit
|
||||||
|
if (!dotProductFactor) {
|
||||||
|
return {0};
|
||||||
|
}
|
||||||
|
|
||||||
if (vpl_settings.omnidirectional) {
|
if (vpl_settings.omnidirectional) {
|
||||||
dist += cfg.surflightskydist.value();
|
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];
|
const qvec3f &pos = vpl.points[c];
|
||||||
qvec3f dir = lightsurf_pos - pos;
|
qvec3f dir = lightsurf_pos - pos;
|
||||||
float dist = qv::length(dir);
|
float dist = std::max(0.01f, qv::length(dir));
|
||||||
bool use_normal = true;
|
bool use_normal = true;
|
||||||
|
|
||||||
if (lightsurf->twosided) {
|
if (lightsurf->twosided) {
|
||||||
|
|
@ -2081,6 +2077,7 @@ LightFace_SurfaceLight(const mbsp_t *bsp, lightsurf_t *lightsurf, lightmapdict_t
|
||||||
|
|
||||||
lightsample_t &sample = lightmap->samples[i];
|
lightsample_t &sample = lightmap->samples[i];
|
||||||
sample.color += indirect;
|
sample.color += indirect;
|
||||||
|
lightmap->bounce_color += indirect;
|
||||||
|
|
||||||
hit = true;
|
hit = true;
|
||||||
++total_surflight_ray_hits;
|
++total_surflight_ray_hits;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue