quakeforge/libs/scene/light.c
Bill Currie d3965af2ae [scene] Improve directional light handling
While it wasn't the root cause of the disappearing lights (even after
sorting out the light limit issue), because the cause of that was
everything working as designed, I suspect sunlight wasn't reaching as
far as it should. Even it it was, this should be slightly faster
(especially for larger maps) as leafs can be tested 32 or 64 at a time
rather than individually.
2022-05-19 13:26:45 +09:00

162 lines
3.9 KiB
C

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include "QF/model.h"
#include "QF/set.h"
#include "QF/scene/light.h"
#include "QF/scene/scene.h"
#include "QF/simd/vec4f.h"
static void
expand_pvs (set_t *pvs, model_t *model)
{
set_t base_pvs = SET_STATIC_INIT (model->brush.visleafs, alloca);
set_assign (&base_pvs, pvs);
for (unsigned i = 0; i < model->brush.visleafs; i++) {
if (set_is_member (&base_pvs, i)) {
Mod_LeafPVS_mix (model->brush.leafs + i + 1, model, 0, pvs);
}
}
}
lightingdata_t *
Light_CreateLightingData (scene_t *scene)
{
lightingdata_t *ldata = calloc (1, sizeof (lightingdata_t));
DARRAY_INIT (&ldata->lights, 16);
DARRAY_INIT (&ldata->lightstyles, 16);
DARRAY_INIT (&ldata->lightleafs, 16);
DARRAY_INIT (&ldata->lightvis, 16);
ldata->scene = scene;
return ldata;
}
void
Light_DestroyLightingData (lightingdata_t *ldata)
{
DARRAY_CLEAR (&ldata->lights);
DARRAY_CLEAR (&ldata->lightstyles);
DARRAY_CLEAR (&ldata->lightleafs);
DARRAY_CLEAR (&ldata->lightvis);
free (ldata);
}
void
Light_ClearLights (lightingdata_t *ldata)
{
ldata->lights.size = 0;
ldata->lightstyles.size = 0;
ldata->lightleafs.size = 0;
ldata->lightvis.size = 0;
if (ldata->sun_pvs) {
set_delete (ldata->sun_pvs);
}
ldata->sun_pvs = 0;
if (ldata->pvs) {
set_delete (ldata->pvs);
}
ldata->pvs = 0;
ldata->leaf = 0;
}
void
Light_AddLight (lightingdata_t *ldata, const light_t *light, int style)
{
scene_t *scene = ldata->scene;
model_t *model = scene->worldmodel;
DARRAY_APPEND (&ldata->lights, *light);
DARRAY_APPEND (&ldata->lightstyles, style);
int visleaf = -1; // directional light
if (light->position[3]) {
// positional light
mleaf_t *leaf = Mod_PointInLeaf (&light->position[0], model);
visleaf = leaf - model->brush.leafs - 1;
} else if (!DotProduct (light->direction, light->direction)) {
// ambient light
visleaf = -2;
}
DARRAY_APPEND (&ldata->lightleafs, visleaf);
DARRAY_APPEND (&ldata->lightvis, 0);
}
void
Light_EnableSun (lightingdata_t *ldata)
{
scene_t *scene = ldata->scene;
model_t *model = scene->worldmodel;
if (!ldata->sun_pvs) {
ldata->sun_pvs = set_new_size (model->brush.visleafs);
}
set_expand (ldata->sun_pvs, model->brush.visleafs);
set_empty (ldata->sun_pvs);
// Any leaf with sky surfaces can potentially see the sun, thus put
// the sun "in" every leaf with a sky surface
// however, skip leaf 0 as it is the exterior solid leaf
for (unsigned l = 1; l < model->brush.modleafs; l++) {
if (model->brush.leaf_flags[l] & SURF_DRAWSKY) {
set_add (ldata->sun_pvs, l - 1); //pvs is 1-based
}
}
// any leaf visible from a leaf with a sky surface (and thus the sun)
// can receive shadows from the sun
expand_pvs (ldata->sun_pvs, model);
}
void
Light_FindVisibleLights (lightingdata_t *ldata)
{
scene_t *scene = ldata->scene;
mleaf_t *leaf = scene->viewleaf;
model_t *model = scene->worldmodel;
if (!leaf) {
return;
}
if (!ldata->pvs) {
ldata->pvs = set_new_size (model->brush.visleafs);
}
if (leaf != ldata->leaf) {
//double start = Sys_DoubleTime ();
int flags = 0;
if (leaf == model->brush.leafs) {
set_everything (ldata->pvs);
flags = SURF_DRAWSKY;
} else {
Mod_LeafPVS_set (leaf, model, 0, ldata->pvs);
if (set_is_intersecting (ldata->pvs, ldata->sun_pvs)) {
flags |= SURF_DRAWSKY;
}
expand_pvs (ldata->pvs, model);
}
ldata->leaf = leaf;
//double end = Sys_DoubleTime ();
//Sys_Printf ("find_visible_lights: %.5gus\n", (end - start) * 1e6);
int visible = 0;
memset (ldata->lightvis.a, 0, ldata->lightvis.size * sizeof (byte));
for (size_t i = 0; i < ldata->lightleafs.size; i++) {
int l = ldata->lightleafs.a[i];
if ((l == -2) || (l == -1 && (flags & SURF_DRAWSKY))
|| set_is_member (ldata->pvs, l)) {
ldata->lightvis.a[i] = 1;
visible++;
}
}
Sys_MaskPrintf (SYS_lighting,
"find_visible_lights: %d / %zd visible\n", visible,
ldata->lightvis.size);
}
}