light: experimenting with generating a lightgrid
currently a hardcoded 64^3 grid in a LIGHTGRID bspx lump non-finalized, likely going to change the lump
This commit is contained in:
parent
13481438e7
commit
999806f150
|
|
@ -383,6 +383,7 @@ public:
|
|||
setting_int32 facestyles;
|
||||
setting_bool exportobj;
|
||||
setting_int32 lmshift;
|
||||
setting_bool lightgrid;
|
||||
|
||||
setting_func dirtdebug;
|
||||
setting_func bouncedebug;
|
||||
|
|
|
|||
|
|
@ -57,4 +57,5 @@ void FinishLightmapSurface(const mbsp_t *bsp, lightsurf_t *lightsurf);
|
|||
void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup,
|
||||
bspx_decoupled_lm_perface *facesup_decoupled, lightsurf_t *lightsurf, const faceextents_t &extents,
|
||||
const faceextents_t &output_extents);
|
||||
qvec3d CalcLightgridAtPoint(const mbsp_t *bsp, const qvec3d &world_point);
|
||||
void ResetLtFace();
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <common/fs.hh>
|
||||
#include <common/imglib.hh>
|
||||
#include <common/parallel.hh>
|
||||
#include <tbb/blocked_range3d.h>
|
||||
|
||||
#if defined(HAVE_EMBREE) && defined(__SSE2__)
|
||||
#include <xmmintrin.h>
|
||||
|
|
@ -314,6 +315,8 @@ light_settings::light_settings()
|
|||
exportobj{this, "exportobj", false, &output_group, "export an .OBJ for inspection"},
|
||||
lmshift{this, "lmshift", 4, &output_group,
|
||||
"force a specified lmshift to be applied to the entire map; this is useful if you want to re-light a map with higher quality BSPX lighting without the sources. Will add the LMSHIFT lump to the BSP."},
|
||||
lightgrid{this, "lightgrid", false, &experimental_group, "experimental LIGHTGRID bspx lump"},
|
||||
|
||||
dirtdebug{this, {"dirtdebug", "debugdirt"},
|
||||
[&](source) {
|
||||
CheckNoDebugModeSet();
|
||||
|
|
@ -1042,6 +1045,51 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale)
|
|||
}
|
||||
}
|
||||
|
||||
static void LightGrid(bspdata_t *bspdata)
|
||||
{
|
||||
if (!light_options.lightgrid.value())
|
||||
return;
|
||||
|
||||
logging::funcheader();
|
||||
|
||||
auto &bsp = std::get<mbsp_t>(bspdata->bsp);
|
||||
auto world_size = bsp.dmodels[0].maxs - bsp.dmodels[0].mins;
|
||||
|
||||
constexpr int GRIDSIZE = 64;
|
||||
|
||||
std::vector<uint8_t> grid_result;
|
||||
grid_result.resize(GRIDSIZE * GRIDSIZE * GRIDSIZE * 3);
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range3d<int>(0, GRIDSIZE, 0, GRIDSIZE, 0, GRIDSIZE),
|
||||
[&](const tbb::blocked_range3d<int>& range) {
|
||||
const auto &z_range = range.pages();
|
||||
const auto &y_range = range.rows();
|
||||
const auto &x_range = range.cols();
|
||||
|
||||
for (int z = z_range.begin(); z < z_range.end(); ++z) {
|
||||
for (int y = y_range.begin(); y < y_range.end(); ++y) {
|
||||
for (int x = x_range.begin(); x < x_range.end(); ++x) {
|
||||
double max_grid_coord = GRIDSIZE - 1;
|
||||
qvec3d world_point = bsp.dmodels[0].mins + (world_size * qvec3d{x / max_grid_coord,
|
||||
y / max_grid_coord,
|
||||
z / max_grid_coord});
|
||||
|
||||
qvec3d color = CalcLightgridAtPoint(&bsp, world_point);
|
||||
|
||||
int sample_index = (GRIDSIZE * GRIDSIZE * z) + (GRIDSIZE * y) + x;
|
||||
|
||||
grid_result[sample_index * 3] = clamp((int)color[0], 0, 255);
|
||||
grid_result[sample_index * 3 + 1] = clamp((int)color[1], 0, 255);
|
||||
grid_result[sample_index * 3 + 2] = clamp((int)color[2], 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// non-final, experimental lump
|
||||
bspdata->bspx.transfer("LIGHTGRID", std::move(grid_result));
|
||||
}
|
||||
|
||||
static void LoadExtendedTexinfoFlags(const fs::path &sourcefilename, const mbsp_t *bsp)
|
||||
{
|
||||
// always create the zero'ed array
|
||||
|
|
@ -1676,6 +1724,8 @@ int light_main(int argc, const char **argv)
|
|||
|
||||
LightWorld(&bspdata, light_options.lightmap_scale.isChanged());
|
||||
|
||||
LightGrid(&bspdata);
|
||||
|
||||
// invalidate normals
|
||||
bspdata.bspx.entries.erase("FACENORMALS");
|
||||
|
||||
|
|
|
|||
224
light/ltface.cc
224
light/ltface.cc
|
|
@ -816,9 +816,15 @@ vec_t GetLightValue(const settings::worldspawn_keys &cfg, const light_t *entity,
|
|||
}
|
||||
|
||||
static float GetLightValueWithAngle(const settings::worldspawn_keys &cfg, const light_t *entity, const qvec3d &surfnorm,
|
||||
const qvec3d &surfpointToLightDir, float dist, bool twosided)
|
||||
bool use_surfnorm, const qvec3d &surfpointToLightDir, float dist, bool twosided)
|
||||
{
|
||||
vec_t angle = qv::dot(surfpointToLightDir, surfnorm);
|
||||
vec_t angle;
|
||||
if (use_surfnorm) {
|
||||
angle = qv::dot(surfpointToLightDir, surfnorm);
|
||||
} else {
|
||||
angle = 1.0f;
|
||||
}
|
||||
|
||||
if (entity->bleed.value() || twosided) {
|
||||
if (angle < 0) {
|
||||
angle = -angle; // ericw -- support "_bleed" option
|
||||
|
|
@ -919,7 +925,7 @@ static bool LightFace_SampleMipTex(
|
|||
}
|
||||
|
||||
static void GetLightContrib(const settings::worldspawn_keys &cfg, const light_t *entity, const qvec3d &surfnorm,
|
||||
const qvec3d &surfpoint, bool twosided, qvec3f &color_out, qvec3d &surfpointToLightDir_out,
|
||||
bool use_surfnorm, const qvec3d &surfpoint, bool twosided, qvec3f &color_out, qvec3d &surfpointToLightDir_out,
|
||||
qvec3d &normalmap_addition_out, float *dist_out)
|
||||
{
|
||||
float dist = GetDir(surfpoint, entity->origin.value(), surfpointToLightDir_out);
|
||||
|
|
@ -929,7 +935,7 @@ static void GetLightContrib(const settings::worldspawn_keys &cfg, const light_t
|
|||
dist = 0.1f;
|
||||
surfpointToLightDir_out = {0, 0, 1};
|
||||
}
|
||||
const float add = GetLightValueWithAngle(cfg, entity, surfnorm, surfpointToLightDir_out, dist, twosided);
|
||||
const float add = GetLightValueWithAngle(cfg, entity, surfnorm, use_surfnorm, surfpointToLightDir_out, dist, twosided);
|
||||
|
||||
/* write out the final color */
|
||||
if (entity->projectedmip) {
|
||||
|
|
@ -1180,7 +1186,7 @@ static void LightFace_Entity(
|
|||
qvec3f color;
|
||||
qvec3d normalcontrib;
|
||||
|
||||
GetLightContrib(cfg, entity, surfnorm, surfpoint, lightsurf->twosided, color, surfpointToLightDir,
|
||||
GetLightContrib(cfg, entity, surfnorm, true, surfpoint, lightsurf->twosided, color, surfpointToLightDir,
|
||||
normalcontrib, &surfpointToLightDist);
|
||||
|
||||
const float occlusion =
|
||||
|
|
@ -1243,6 +1249,45 @@ static void LightFace_Entity(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates light at a given point from an entity
|
||||
*/
|
||||
static void LightPoint_Entity(
|
||||
const mbsp_t *bsp, raystream_occlusion_t &rs, const light_t *entity, const qvec3d &surfpoint, qvec3d &result)
|
||||
{
|
||||
if (entity->style.value() != 0)
|
||||
return; // not doing style lightgrid for now
|
||||
|
||||
rs.clearPushedRays();
|
||||
|
||||
qvec3d surfpointToLightDir;
|
||||
float surfpointToLightDist;
|
||||
qvec3f color;
|
||||
qvec3d normalcontrib;
|
||||
|
||||
GetLightContrib(light_options, entity, {0,0,0}, false, surfpoint, false, color, surfpointToLightDir,
|
||||
normalcontrib, &surfpointToLightDist);
|
||||
|
||||
/* Quick distance check first */
|
||||
if (fabs(LightSample_Brightness(color)) <= light_options.gate.value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rs.pushRay(0, surfpoint, surfpointToLightDir, surfpointToLightDist, &color, &normalcontrib);
|
||||
|
||||
rs.tracePushedRaysOcclusion(nullptr, CHANNEL_MASK_DEFAULT);
|
||||
|
||||
// add result
|
||||
const int N = rs.numPushedRays();
|
||||
for (int j = 0; j < N; j++) {
|
||||
if (rs.getPushedRayOccluded(j)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result += rs.getPushedRayColor(j);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============
|
||||
* LightFace_Sky
|
||||
|
|
@ -1356,6 +1401,49 @@ static void LightFace_Sky(const sun_t *sun, lightsurf_t *lightsurf, lightmapdict
|
|||
}
|
||||
}
|
||||
|
||||
static void LightPoint_Sky(
|
||||
const mbsp_t *bsp, raystream_intersection_t &rs, const sun_t *sun, const qvec3d &surfpoint, qvec3d &result)
|
||||
{
|
||||
if (sun->style != 0)
|
||||
return; // not doing style lightgrid for now
|
||||
|
||||
// FIXME: Normalized sun vector should be stored in the sun_t. Also clarify which way the vector points (towards or
|
||||
// away..)
|
||||
// FIXME: Much of this is copied/pasted from LightFace_Entity, should probably be merged
|
||||
qvec3d incoming = qv::normalize(sun->sunvec);
|
||||
|
||||
rs.clearPushedRays();
|
||||
|
||||
// only 1 ray
|
||||
{
|
||||
float value = sun->sunlight;
|
||||
qvec3f color = sun->sunlight_color * (value / 255.0);
|
||||
|
||||
/* Quick distance check first */
|
||||
if (fabs(LightSample_Brightness(color)) <= light_options.gate.value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
qvec3d normalcontrib{}; // unused
|
||||
|
||||
rs.pushRay(0, surfpoint, incoming, MAX_SKY_DIST, &color, &normalcontrib);
|
||||
}
|
||||
|
||||
// We need to check if the first hit face is a sky face, so we need
|
||||
// to test intersection (not occlusion)
|
||||
rs.tracePushedRaysIntersection(nullptr, CHANNEL_MASK_DEFAULT);
|
||||
|
||||
// add result
|
||||
const int N = rs.numPushedRays();
|
||||
for (int j = 0; j < N; j++) {
|
||||
if (rs.getPushedRayHitType(j) != hittype_t::SKY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result += rs.getPushedRayColor(j);
|
||||
}
|
||||
}
|
||||
|
||||
// Mottle
|
||||
|
||||
static int mod_round_to_neg_inf(int x, int y) {
|
||||
|
|
@ -1660,14 +1748,14 @@ inline qvec3f SurfaceLight_ColorAtDist(
|
|||
// dir: vpl -> sample point direction
|
||||
// mxd. returns color in [0,255]
|
||||
inline qvec3f GetSurfaceLighting(const settings::worldspawn_keys &cfg, const surfacelight_t *vpl, const qvec3f &dir,
|
||||
const float dist, const qvec3f &normal, const vec_t &standard_scale, const vec_t &sky_scale, const float &hotspot_clamp)
|
||||
const float dist, const qvec3f &normal, bool use_normal, const vec_t &standard_scale, const vec_t &sky_scale, const float &hotspot_clamp)
|
||||
{
|
||||
qvec3f result;
|
||||
float dotProductFactor = 1.0f;
|
||||
|
||||
float dp1 = qv::dot(vpl->surfnormal, dir);
|
||||
const qvec3f sp_vpl = dir * -1.0f;
|
||||
float dp2 = qv::dot(sp_vpl, normal);
|
||||
float dp2 = use_normal ? qv::dot(sp_vpl, normal) : 1.0f;
|
||||
|
||||
if (!vpl->omnidirectional) {
|
||||
if (dp1 < -LIGHT_ANGLE_EPSILON)
|
||||
|
|
@ -1756,7 +1844,7 @@ LightFace_SurfaceLight(const mbsp_t *bsp, lightsurf_t *lightsurf, lightmapdict_t
|
|||
else
|
||||
dir /= dist;
|
||||
|
||||
const qvec3f indirect = GetSurfaceLighting(cfg, &vpl, dir, dist, lightsurf_normal, standard_scale, sky_scale, hotspot_clamp);
|
||||
const qvec3f indirect = GetSurfaceLighting(cfg, &vpl, dir, dist, lightsurf_normal, true, standard_scale, sky_scale, hotspot_clamp);
|
||||
if (!qv::gate(indirect, surflight_gate)) { // Each point contributes very little to the final result
|
||||
rs.pushRay(i, pos, dir, dist, &indirect);
|
||||
}
|
||||
|
|
@ -1800,6 +1888,54 @@ LightFace_SurfaceLight(const mbsp_t *bsp, lightsurf_t *lightsurf, lightmapdict_t
|
|||
}
|
||||
}
|
||||
|
||||
static void // mxd
|
||||
LightPoint_SurfaceLight(const mbsp_t *bsp, raystream_occlusion_t &rs, const std::vector<surfacelight_t> &surface_lights,
|
||||
const vec_t &standard_scale, const vec_t &sky_scale, const float &hotspot_clamp, const qvec3d &surfpoint, qvec3d &result)
|
||||
{
|
||||
const settings::worldspawn_keys &cfg = light_options;
|
||||
const float surflight_gate = 0.01f;
|
||||
|
||||
for (const surfacelight_t &vpl : surface_lights) {
|
||||
for (int c = 0; c < vpl.points.size(); c++) {
|
||||
rs.clearPushedRays();
|
||||
|
||||
// 1 ray
|
||||
{
|
||||
qvec3f pos = vpl.points[c];
|
||||
qvec3f dir = surfpoint - pos;
|
||||
float dist = qv::length(dir);
|
||||
|
||||
if (dist == 0.0f)
|
||||
dir = {0, 0, 1};
|
||||
else
|
||||
dir /= dist;
|
||||
|
||||
const qvec3f indirect = GetSurfaceLighting(cfg, &vpl, dir, dist, {0,0,0}, false, standard_scale, sky_scale, hotspot_clamp);
|
||||
if (!qv::gate(indirect, surflight_gate)) { // Each point contributes very little to the final result
|
||||
rs.pushRay(0, pos, dir, dist, &indirect);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rs.numPushedRays())
|
||||
continue;
|
||||
|
||||
rs.tracePushedRaysOcclusion(nullptr, CHANNEL_MASK_DEFAULT);
|
||||
|
||||
const int numrays = rs.numPushedRays();
|
||||
for (int j = 0; j < numrays; j++) {
|
||||
if (rs.getPushedRayOccluded(j))
|
||||
continue;
|
||||
|
||||
qvec3f indirect = rs.getPushedRayColor(j);
|
||||
|
||||
Q_assert(!std::isnan(indirect[0]));
|
||||
|
||||
result += indirect;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void LightFace_OccludedDebug(lightsurf_t *lightsurf, lightmapdict_t *lightmaps)
|
||||
{
|
||||
Q_assert(light_options.debugmode == debugmodes::debugoccluded);
|
||||
|
|
@ -3018,6 +3154,78 @@ void IndirectLightFace(const mbsp_t *bsp, lightsurf_t &lightsurf, const settings
|
|||
}
|
||||
}
|
||||
|
||||
// lightgrid
|
||||
|
||||
qvec3d CalcLightgridAtPoint(const mbsp_t *bsp, const qvec3d &world_point)
|
||||
{
|
||||
// TODO: use more than 1 ray for better performance
|
||||
raystream_occlusion_t rs(1);
|
||||
raystream_intersection_t rsi(1);
|
||||
|
||||
auto &cfg = light_options;
|
||||
|
||||
qvec3d result {0, 0, 0};
|
||||
|
||||
// from DirectLightFace
|
||||
|
||||
/*
|
||||
* The lighting procedure is: cast all positive lights, fix
|
||||
* minlight levels, then cast all negative lights. Finally, we
|
||||
* clamp any values that may have gone negative.
|
||||
*/
|
||||
|
||||
/* positive lights */
|
||||
for (const auto &entity : GetLights()) {
|
||||
if (entity->getFormula() == LF_LOCALMIN)
|
||||
continue;
|
||||
if (entity->nostaticlight.value())
|
||||
continue;
|
||||
if (entity->light.value() > 0)
|
||||
LightPoint_Entity(bsp, rs, entity.get(), world_point, result);
|
||||
}
|
||||
|
||||
for (const sun_t &sun : GetSuns())
|
||||
if (sun.sunlight > 0)
|
||||
LightPoint_Sky(bsp, rsi, &sun, world_point, result);
|
||||
|
||||
// mxd. Add surface lights...
|
||||
// FIXME: negative surface lights
|
||||
LightPoint_SurfaceLight(bsp, rs, GetSurfaceLights(), cfg.surflightscale.value(), cfg.surflightskyscale.value(), 16.0f, world_point, result);
|
||||
|
||||
#if 0
|
||||
// FIXME: port to lightgrid
|
||||
float minlight = cfg.minlight.value();
|
||||
qvec3d minlight_color = cfg.minlight_color.value();
|
||||
|
||||
if (minlight) {
|
||||
LightFace_Min(bsp, face, minlight_color, minlight, &lightsurf, lightmaps, 0);
|
||||
}
|
||||
|
||||
LightFace_LocalMin(bsp, face, &lightsurf, lightmaps);
|
||||
#endif
|
||||
|
||||
/* negative lights */
|
||||
for (const auto &entity : GetLights()) {
|
||||
if (entity->getFormula() == LF_LOCALMIN)
|
||||
continue;
|
||||
if (entity->nostaticlight.value())
|
||||
continue;
|
||||
if (entity->light.value() < 0)
|
||||
LightPoint_Entity(bsp, rs, entity.get(), world_point, result);
|
||||
}
|
||||
for (const sun_t &sun : GetSuns())
|
||||
if (sun.sunlight < 0)
|
||||
LightPoint_Sky(bsp, rsi, &sun, world_point, result);
|
||||
|
||||
// from IndirectLightFace
|
||||
|
||||
/* add bounce lighting */
|
||||
// note: scale here is just to keep it close-ish to the old code
|
||||
LightPoint_SurfaceLight(bsp, rs, BounceLights(), cfg.bouncescale.value() * 0.5, cfg.bouncescale.value(), 128.0f, world_point, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ResetLtFace()
|
||||
{
|
||||
total_light_rays = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue