2022-05-05 05:58:47 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "QF/model.h"
|
2023-08-04 05:56:16 +00:00
|
|
|
#include "QF/render.h"
|
2022-05-05 05:58:47 +00:00
|
|
|
#include "QF/set.h"
|
2023-07-22 08:53:07 +00:00
|
|
|
#include "QF/scene/entity.h"
|
2022-05-05 05:58:47 +00:00
|
|
|
#include "QF/scene/light.h"
|
|
|
|
#include "QF/scene/scene.h"
|
|
|
|
#include "QF/simd/vec4f.h"
|
|
|
|
|
|
|
|
static void
|
2023-06-28 12:45:41 +00:00
|
|
|
expand_pvs (set_t *pvs, mod_brush_t *brush)
|
2022-05-05 05:58:47 +00:00
|
|
|
{
|
2023-06-28 12:45:41 +00:00
|
|
|
set_t base_pvs = SET_STATIC_INIT (brush->visleafs, alloca);
|
2022-05-05 05:58:47 +00:00
|
|
|
set_assign (&base_pvs, pvs);
|
2023-06-28 12:45:41 +00:00
|
|
|
for (unsigned i = 0; i < brush->visleafs; i++) {
|
2022-05-05 05:58:47 +00:00
|
|
|
if (set_is_member (&base_pvs, i)) {
|
2023-06-28 12:45:41 +00:00
|
|
|
Mod_LeafPVS_mix (brush->leafs + i + 1, brush, 0, pvs);
|
2022-05-05 05:58:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lightingdata_t *
|
|
|
|
Light_CreateLightingData (scene_t *scene)
|
|
|
|
{
|
|
|
|
lightingdata_t *ldata = calloc (1, sizeof (lightingdata_t));
|
|
|
|
|
|
|
|
ldata->scene = scene;
|
|
|
|
|
|
|
|
return ldata;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Light_DestroyLightingData (lightingdata_t *ldata)
|
|
|
|
{
|
|
|
|
free (ldata);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Light_ClearLights (lightingdata_t *ldata)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-07-22 11:08:15 +00:00
|
|
|
static bool
|
|
|
|
test_light_leaf (const light_t *light, const mleaf_t *leaf)
|
|
|
|
{
|
|
|
|
// FIXME directional lights should check the direction against the
|
|
|
|
// leaf's portals (need to find the portals first, though)
|
|
|
|
if (!light->position[3] || !light->attenuation[3]) {
|
|
|
|
// non-positional lights or lights with infinite radius always light
|
|
|
|
// the leafs they can see
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// use Minkowski difference to see if the light hits the leaf's bounding
|
|
|
|
// box (thanks to Nick Alger:
|
|
|
|
// https://stackoverflow.com/questions/5122228/box-to-sphere-collision)
|
|
|
|
float r = 1/light->attenuation[3];
|
|
|
|
vec4f_t c = light->position / light->position[3];
|
|
|
|
vec4f_t mins = loadvec3f (leaf->mins);
|
|
|
|
vec4f_t maxs = loadvec3f (leaf->maxs);
|
|
|
|
vec4i_t tmin = c < mins;
|
|
|
|
vec4i_t tmax = maxs < c;
|
|
|
|
vec4i_t tcen = ~tmin & ~tmax;
|
|
|
|
vec4f_t p = (vec4f_t)((tmin & (vec4i_t)mins)
|
|
|
|
+ (tcen & (vec4i_t)c)
|
|
|
|
+ (tmax & (vec4i_t)maxs));
|
|
|
|
p[3] = 1;
|
|
|
|
vec4f_t d = p - c;
|
|
|
|
if (dotf (d, d)[0] > r * r) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-08-04 05:56:16 +00:00
|
|
|
static void
|
|
|
|
link_light (lightingdata_t *ldata, const light_t *light, entity_t ent)
|
2022-05-05 11:36:25 +00:00
|
|
|
{
|
|
|
|
scene_t *scene = ldata->scene;
|
|
|
|
model_t *model = scene->worldmodel;
|
|
|
|
|
2023-07-22 08:53:07 +00:00
|
|
|
set_t _pvs = SET_STATIC_INIT (model->brush.visleafs, alloca);
|
|
|
|
set_t *pvs = &_pvs;
|
2023-07-27 06:50:53 +00:00
|
|
|
uint32_t leafnum = ~0u;
|
2022-05-05 11:36:25 +00:00
|
|
|
if (light->position[3]) {
|
|
|
|
// positional light
|
2023-06-28 12:45:41 +00:00
|
|
|
mleaf_t *leaf = Mod_PointInLeaf (light->position, &model->brush);
|
2023-07-22 08:53:07 +00:00
|
|
|
Mod_LeafPVS_set (leaf, &model->brush, 0, pvs);
|
2023-07-27 06:50:53 +00:00
|
|
|
leafnum = leaf - model->brush.leafs;
|
2023-07-22 08:53:07 +00:00
|
|
|
} else if (DotProduct (light->direction, light->direction)) {
|
|
|
|
// directional light (sun)
|
|
|
|
pvs = ldata->sun_pvs;
|
2023-07-27 06:50:53 +00:00
|
|
|
leafnum = 0;
|
2023-07-22 08:53:07 +00:00
|
|
|
} else {
|
2022-05-05 11:36:25 +00:00
|
|
|
// ambient light
|
2023-07-27 06:50:53 +00:00
|
|
|
Mod_LeafPVS_set (model->brush.leafs, &model->brush, 0xff, pvs);
|
2022-05-05 11:36:25 +00:00
|
|
|
}
|
2023-07-27 06:50:53 +00:00
|
|
|
Ent_SetComponent (ent.id, scene_lightleaf, ent.reg, &leafnum);
|
|
|
|
|
2023-07-22 08:53:07 +00:00
|
|
|
efrag_t *efrags = 0;
|
|
|
|
efrag_t **lastlink = &efrags;
|
|
|
|
for (auto li = set_first (pvs); li; li = set_next (li)) {
|
|
|
|
mleaf_t *leaf = model->brush.leafs + li->element + 1;
|
2023-07-22 11:08:15 +00:00
|
|
|
if (test_light_leaf (light, leaf)) {
|
|
|
|
lastlink = R_LinkEfrag (leaf, ent, mod_light, lastlink);
|
|
|
|
}
|
2023-07-22 08:53:07 +00:00
|
|
|
}
|
2023-08-04 05:56:16 +00:00
|
|
|
if (Ent_HasComponent (ent.id, scene_efrags, ent.reg)) {
|
|
|
|
Ent_RemoveComponent (ent.id, scene_efrags, ent.reg);
|
|
|
|
}
|
2023-07-22 08:53:07 +00:00
|
|
|
Ent_SetComponent (ent.id, scene_efrags, ent.reg, &efrags);
|
2022-05-05 11:36:25 +00:00
|
|
|
}
|
|
|
|
|
2023-08-04 05:56:16 +00:00
|
|
|
void
|
|
|
|
Light_AddLight (lightingdata_t *ldata, const light_t *light, uint32_t style)
|
|
|
|
{
|
|
|
|
scene_t *scene = ldata->scene;
|
|
|
|
|
|
|
|
entity_t ent = {
|
|
|
|
.reg = scene->reg,
|
|
|
|
.id = ECS_NewEntity (scene->reg),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ent_SetComponent (ent.id, scene_light, ent.reg, light);
|
|
|
|
Ent_SetComponent (ent.id, scene_lightstyle, ent.reg, &style);
|
|
|
|
|
|
|
|
link_light (ldata, light, ent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Light_LinkLight (lightingdata_t *ldata, uint32_t entid)
|
|
|
|
{
|
|
|
|
scene_t *scene = ldata->scene;
|
|
|
|
|
|
|
|
entity_t ent = {
|
|
|
|
.reg = scene->reg,
|
|
|
|
.id = entid,
|
|
|
|
};
|
|
|
|
dlight_t *dlight = Ent_GetComponent (ent.id, scene_dynlight, ent.reg);
|
|
|
|
if (!dlight) {
|
|
|
|
Sys_Error ("no dlight on entity to link");
|
|
|
|
}
|
|
|
|
light_t light = {
|
|
|
|
.color = dlight->color,
|
|
|
|
.position = dlight->origin,
|
|
|
|
.direction = {0, 0, 1, 1},
|
|
|
|
.attenuation = {0, 0, 1, 1/dlight->radius},
|
|
|
|
};
|
|
|
|
|
|
|
|
link_light (ldata, &light, ent);
|
|
|
|
}
|
|
|
|
|
2022-05-05 05:58:47 +00:00
|
|
|
void
|
|
|
|
Light_EnableSun (lightingdata_t *ldata)
|
|
|
|
{
|
|
|
|
scene_t *scene = ldata->scene;
|
2023-06-28 12:45:41 +00:00
|
|
|
auto brush = &scene->worldmodel->brush;
|
2022-05-05 05:58:47 +00:00
|
|
|
|
|
|
|
if (!ldata->sun_pvs) {
|
2023-06-28 12:45:41 +00:00
|
|
|
ldata->sun_pvs = set_new_size (brush->visleafs);
|
2022-05-05 05:58:47 +00:00
|
|
|
}
|
2023-06-28 12:45:41 +00:00
|
|
|
set_expand (ldata->sun_pvs, brush->visleafs);
|
2022-05-05 05:58:47 +00:00
|
|
|
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
|
2023-06-28 12:45:41 +00:00
|
|
|
for (unsigned l = 1; l < brush->modleafs; l++) {
|
|
|
|
if (brush->leaf_flags[l] & SURF_DRAWSKY) {
|
2022-05-05 05:58:47 +00:00
|
|
|
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
|
2023-06-28 12:45:41 +00:00
|
|
|
expand_pvs (ldata->sun_pvs, brush);
|
2022-05-05 05:58:47 +00:00
|
|
|
}
|
2023-08-04 05:56:16 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
Light_DecayLights (lightingdata_t *ldata, float frametime, double realtime)
|
|
|
|
{
|
|
|
|
auto reg = ldata->scene->reg;
|
|
|
|
auto dlight_pool = ®->comp_pools[scene_dynlight];
|
|
|
|
auto dlight_data = (dlight_t *) dlight_pool->data;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < dlight_pool->count; i++) {
|
|
|
|
auto dlight = &dlight_data[i];
|
|
|
|
dlight->radius -= frametime * dlight->decay;
|
|
|
|
if (dlight->radius <= 0 || dlight->die < realtime) {
|
|
|
|
uint32_t ent = dlight_pool->dense[i];
|
|
|
|
Ent_RemoveComponent (ent, scene_dynlight, reg);
|
|
|
|
if (!Ent_HasComponent (ent, scene_efrags, reg)) {
|
|
|
|
Sys_Error ("dlight with no efrags");
|
|
|
|
}
|
|
|
|
Ent_RemoveComponent (ent, scene_efrags, reg);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|