[vulkan] Rework lighting model to be more algebraic

This leaves only the one conditional in the shader code, that being the
distance check. It doesn't seem to make any noticeable difference to
performance, but other than explosion sprites being blue, lighting
quality seems to have improved. However, I really need to get shadows
working: marcher is just silly-bright without them, and light levels
changing as I move around is a bit disconcerting (but reasonable as
those lights' leaf nodes go in and out of visibility).
This commit is contained in:
Bill Currie 2022-05-05 08:40:02 +09:00
parent e1f4df27c3
commit e323fbbbed
3 changed files with 192 additions and 200 deletions

View file

@ -39,12 +39,10 @@
#include "QF/simd/types.h"
typedef struct qfv_light_s {
vec3_t color;
int data;
vec3_t position;
float light;
vec3_t direction;
float cone;
vec4f_t color;
vec4f_t position;
vec4f_t direction;
vec4f_t attenuation;
} qfv_light_t;
typedef struct qfv_lightset_s DARRAY_TYPE (qfv_light_t) qfv_lightset_t;
@ -54,32 +52,25 @@ typedef struct qfv_lightmatset_s DARRAY_TYPE (mat4f_t) qfv_lightmatset_t;
#define MaxLights 256
#define StyleMask 0x07f
#define ModelMask 0x380
#define ShadowMask 0xc00
#define LM_LINEAR 0 // light - dist (or radius + dist if -ve)
#define LM_INVERSE 1 // distFactor1 * light / dist
#define LM_INVERSE2 2 // distFactor2 * light / (dist * dist)
#define LM_INFINITE 3 // light
#define LM_AMBIENT 4 // light
#define LM_INVERSE3 5 // distFactor2 * light / (dist + distFactor2)**2
#define LM_LINEAR (0 << 7) // light - dist (or radius + dist if -ve)
#define LM_INVERSE (1 << 7) // distFactor1 * light / dist
#define LM_INVERSE2 (2 << 7) // distFactor2 * light / (dist * dist)
#define LM_INFINITE (3 << 7) // light
#define LM_AMBIENT (4 << 7) // light
#define LM_INVERSE3 (5 << 7) // distFactor2 * light / (dist + distFactor2)**2
#define ST_NONE (0 << 10) // no shadows
#define ST_PLANE (1 << 10) // single plane shadow map (small spotlight)
#define ST_CASCADE (2 << 10) // cascaded shadow maps
#define ST_CUBE (3 << 10) // cubemap (omni, large spotlight)
#define ST_NONE 0 // no shadows
#define ST_PLANE 1 // single plane shadow map (small spotlight)
#define ST_CASCADE 2 // cascaded shadow maps
#define ST_CUBE 3 // cubemap (omni, large spotlight)
#define NumStyles 64
typedef struct qfv_light_buffer_s {
float intensity[NumStyles + 4];
float distFactor1;
float distFactor2;
int lightCount;
qfv_light_t lights[MaxLights] __attribute__((aligned(16)));
mat4f_t shadowMat[MaxLights];
vec4f_t shadowCascade[MaxLights];
int lightCount;
//mat4f_t shadowMat[MaxLights];
//vec4f_t shadowCascade[MaxLights];
} qfv_light_buffer_t;
#define LIGHTING_BUFFER_INFOS 1
@ -119,6 +110,7 @@ typedef struct lightingctx_s {
VkDeviceMemory light_memory;
VkDeviceMemory shadow_memory;
qfv_lightset_t lights;
qfv_lightintset_t lightstyles;
qfv_lightintset_t lightleafs;
qfv_lightmatset_t lightmats;
qfv_imageset_t lightimages;

View file

@ -7,12 +7,10 @@ layout (input_attachment_index = 3, set = 0, binding = 3) uniform subpassInput n
layout (input_attachment_index = 4, set = 0, binding = 4) uniform subpassInput position;
struct LightData {
vec3 color;
int data;// bits 0-6: intensity key (however, values 0-66)
vec3 position;
float light; // doubles as radius for linear
vec3 direction;
float cone;
vec4 color; // .a is intensity
vec4 position; // .w = 0 -> directional, .w = 1 -> point/cone
vec4 direction; // .w = -cos(cone_angle/2) (1 for omni/dir)
vec4 attenuation;
};
#define StyleMask 0x07f
@ -38,13 +36,10 @@ layout (set = 2, binding = 0) uniform sampler2DShadow shadowPlane[MaxLights];
layout (set = 2, binding = 0) uniform samplerCubeShadow shadowCube[MaxLights];
layout (set = 1, binding = 0) uniform Lights {
vec4 intensity[17]; // 68 floats
float distFactor1; // for inverse
float distFactor2; // for inverse2 and inverse3
int lightCount;
LightData lights[MaxLights];
mat4 shadowMat[MaxLights];
vec4 shadowCascale[MaxLights];
int lightCount;
//mat4 shadowMat[MaxLights];
//vec4 shadowCascale[MaxLights];
};
layout (location = 0) out vec4 frag_color;
@ -52,8 +47,10 @@ layout (location = 0) out vec4 frag_color;
float
spot_cone (LightData light, vec3 incoming)
{
float spotdot = dot (incoming, light.direction);
return smoothstep (spotdot, 1 - (1 - spotdot) * 0.995, light.cone);
vec3 dir = light.direction.xyz;
float cone = light.direction.w;
float spotdot = dot (incoming, dir);
return 1 - smoothstep (cone, cone + 0.02, spotdot);
}
float
@ -63,50 +60,6 @@ diffuse (vec3 incoming, vec3 normal)
return clamp (lightdot, 0, 1);
}
float
light_linear (LightData light, float d)
{
float l = light.light;
if (l < 0) {
return min (l + d, 0);
} else {
return max (l - d, 0);
}
}
float
light_inverse (LightData light, float d)
{
float l = light.light;
return l / (distFactor1 * d);
}
float
light_inverse2 (LightData light, float d)
{
float l = light.light;
return l / (distFactor2 * d);
}
float
light_infinite (LightData light)
{
return light.light;
}
float
light_ambient (LightData light)
{
return light.light;
}
float
light_inverse3 (LightData light, float d)
{
float l = light.light;
return l / (distFactor2 * d + 1);
}
float
shadow_cascade (sampler2DArrayShadow map)
{
@ -138,55 +91,31 @@ main (void)
if (MaxLights > 0) {
vec3 minLight = vec3 (0);
for (int i = 0; i < lightCount; i++) {
vec3 dist = lights[i].position - p;
float d = dot (dist, dist);
int model = lights[i].data & ModelMask;
LightData l = lights[i];
vec3 dir = l.position.xyz - l.position.w * p;
float r2 = dot (dir, dir);
vec4 a = l.attenuation;
if (model != LM_INFINITE
&& d > lights[i].light * lights[i].light) {
if (l.position.w * a.w * a.w * r2 >= 1) {
continue;
}
vec4 r = vec4 (r2, sqrt(r2), 1, 0);
vec3 incoming = dir / r.y;
float I = (1 - a.w * r.y) / dot (a, r);
float l = 0;
if (model == LM_LINEAR) {
d = sqrt (d);
l = light_linear (lights[i], d);
} else if (model == LM_INVERSE) {
d = sqrt (d);
l = light_inverse (lights[i], d);
} else if (model == LM_INVERSE2) {
l = light_inverse2 (lights[i], d);
d = sqrt (d);
} else if (model == LM_INFINITE) {
l = light_infinite (lights[i]);
dist = lights[i].direction;
d = -1;
} else if (model == LM_AMBIENT) {
l = light_ambient (lights[i]);
} else if (model == LM_INVERSE3) {
l = light_inverse3 (lights[i], d);
d = sqrt (d);
}
int style = lights[i].data & StyleMask;
l *= intensity[style / 4][style % 4];
int shadow = lights[i].data & ShadowMask;
/*int shadow = lights[i].data & ShadowMask;
if (shadow == ST_CASCADE) {
l *= shadow_cascade (shadowCascade[i]);
I *= shadow_cascade (shadowCascade[i]);
} else if (shadow == ST_PLANE) {
l *= shadow_plane (shadowPlane[i]);
I *= shadow_plane (shadowPlane[i]);
} else if (shadow == ST_CUBE) {
l *= shadow_cube (shadowCube[i]);
}
I *= shadow_cube (shadowCube[i]);
}*/
if (model == LM_AMBIENT) {
minLight = max (l * lights[i].color, minLight);
} else {
vec3 incoming = dist / d;
l *= spot_cone (lights[i], incoming) * diffuse (incoming, n);
light += l * lights[i].color;
}
float namb = dot(l.direction.xyz, l.direction.xyz);
I *= spot_cone (l, incoming) * diffuse (incoming, n);
I = mix (1, I, namb);
light += I * l.color.w * l.color.xyz;
}
light = max (light, minLight);
}

View file

@ -147,17 +147,10 @@ update_lights (vulkan_ctx_t *ctx)
qfv_light_buffer_t *light_data = QFV_PacketExtend (packet,
sizeof (*light_data));
float style_intensities[NumStyles];
for (int i = 0; i < NumStyles; i++) {
light_data->intensity[i] = d_lightstylevalue[i] / 65536.0;
style_intensities[i] = d_lightstylevalue[i] / 65536.0;
}
// dynamic lights seem a tad faint, so 16x map lights
light_data->intensity[64] = 1 / 16.0;
light_data->intensity[65] = 1 / 16.0;
light_data->intensity[66] = 1 / 16.0;
light_data->intensity[67] = 1 / 16.0;
light_data->distFactor1 = 1 / 128.0;
light_data->distFactor2 = 1 / 16384.0;
light_data->lightCount = 0;
R_FindNearLights (r_refdef.frame.position, MaxLights - 1, lights);
@ -167,16 +160,20 @@ update_lights (vulkan_ctx_t *ctx)
}
light_data->lightCount++;
VectorCopy (lights[i]->color, light_data->lights[i].color);
// dynamic lights seem a tad faint, so 16x map lights
light_data->lights[i].color[3] = lights[i]->radius / 16;
VectorCopy (lights[i]->origin, light_data->lights[i].position);
light_data->lights[i].light = lights[i]->radius;
light_data->lights[i].data = 64; // default dynamic light
VectorZero (light_data->lights[i].direction);
light_data->lights[i].cone = 1;
light_data->lights[i].attenuation =
(vec4f_t) { 0, 0, 1, 1/lights[i]->radius };
light_data->lights[i].direction =
(vec4f_t) { 0, 0, 1, 1 };
}
for (size_t i = 0; (i < lframe->lightvis.size
&& light_data->lightCount < MaxLights); i++) {
if (lframe->lightvis.a[i]) {
light_data->lights[light_data->lightCount++] = lctx->lights.a[i];
qfv_light_t *light = &light_data->lights[light_data->lightCount++];
*light = lctx->lights.a[i];
light->color[3] *= style_intensities[lctx->lightstyles.a[i]];
}
}
@ -303,6 +300,7 @@ Vulkan_Lighting_Init (vulkan_ctx_t *ctx)
ctx->lighting_context = lctx;
DARRAY_INIT (&lctx->lights, 16);
DARRAY_INIT (&lctx->lightstyles, 16);
DARRAY_INIT (&lctx->lightleafs, 16);
DARRAY_INIT (&lctx->lightmats, 16);
DARRAY_INIT (&lctx->lightlayers, 16);
@ -462,6 +460,7 @@ Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx)
dfunc->vkFreeMemory (device->dev, lctx->light_memory, 0);
dfunc->vkDestroyPipeline (device->dev, lctx->pipeline, 0);
DARRAY_CLEAR (&lctx->lights);
DARRAY_CLEAR (&lctx->lightstyles);
DARRAY_CLEAR (&lctx->lightleafs);
DARRAY_CLEAR (&lctx->lightmats);
DARRAY_CLEAR (&lctx->lightimages);
@ -475,19 +474,17 @@ static void
dump_light (qfv_light_t *light, int leaf, mat4f_t mat)
{
Sys_MaskPrintf (SYS_vulkan,
"[%g, %g, %g] %d %d %d, "
"[%g %g %g] %g, [%g %g %g] %g, %d\n",
VectorExpand (light->color),
(light->data & 0x07f),
(light->data & 0x380) >> 7,
(light->data & 0xc00) >> 10,
VectorExpand (light->position), light->light,
VectorExpand (light->direction), light->cone,
"[%g, %g, %g] %g, "
"[%g, %g, %g, %g], [%g %g %g] %g, [%g, %g, %g, %g] %d\n",
VEC4_EXP (light->color),
VEC4_EXP (light->position),
VEC4_EXP (light->direction),
VEC4_EXP (light->attenuation),
leaf);
Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 0));
Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 1));
Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 2));
Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 3));
// Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 0));
// Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 1));
// Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 2));
// Sys_MaskPrintf (SYS_vulkan, " " VEC4F_FMT "\n", MAT4_ROW (mat, 3));
}
static float
@ -548,13 +545,17 @@ esin (float ang)
return sin (ang * M_PI / 180);
}
static void
sun_vector (const vec_t *ang, vec_t *vec)
static vec4f_t
sun_vector (const vec_t *ang)
{
// ang is yaw, pitch (maybe roll, but ignored
vec[0] = ecos (ang[1]) * ecos (ang[0]);
vec[1] = ecos (ang[1]) * esin (ang[0]);
vec[2] = esin (ang[1]);
vec4f_t vec = {
ecos (ang[1]) * ecos (ang[0]),
ecos (ang[1]) * esin (ang[0]),
esin (ang[1]),
0,
};
return vec;
}
static void
@ -577,11 +578,13 @@ parse_sun (lightingctx_t *lctx, plitem_t *entity, model_t *model)
return;
}
VectorSet (1, 1, 1, light.color);
light.data = LM_INFINITE | ST_CASCADE;
light.light = sunlight;
sun_vector (sunangle, light.direction);
light.cone = 1;
light.color[3] = sunlight;
light.position = sun_vector (sunangle);
light.direction = light.position;
light.direction[3] = 1;
light.attenuation = (vec4f_t) { 0, 0, 1, 0 };
DARRAY_APPEND (&lctx->lights, light);
DARRAY_APPEND (&lctx->lightstyles, 0);
DARRAY_APPEND (&lctx->lightleafs, -1);
// Any leaf with sky surfaces can potentially see the sun, thus put
@ -597,8 +600,16 @@ parse_sun (lightingctx_t *lctx, plitem_t *entity, model_t *model)
expand_pvs (lctx->sun_pvs, model);
}
static vec4f_t
parse_position (const char *str)
{
vec3_t vec = {};
sscanf (str, "%f %f %f", VectorExpandAddr (vec));
return (vec4f_t) {vec[0], vec[1], vec[2], 1};
}
static void
parse_light (qfv_light_t *light, const plitem_t *entity,
parse_light (qfv_light_t *light, int *style, const plitem_t *entity,
const plitem_t *targets)
{
const char *str;
@ -612,48 +623,47 @@ parse_light (qfv_light_t *light, const plitem_t *entity,
}
Sys_Printf ("}\n");*/
light->cone = 1;
light->data = 0;
light->light = 300;
VectorSet (1, 1, 1, light->color);
// omnidirectional light (unit length xyz so not treated as ambient)
light->direction = (vec4f_t) { 0, 0, 1, 1 };
// bright white
light->color = (vec4f_t) { 1, 1, 1, 300 };
if ((str = PL_String (PL_ObjectForKey (entity, "origin")))) {
sscanf (str, "%f %f %f", VectorExpandAddr (light->position));
light->position = parse_position (str);
}
if ((str = PL_String (PL_ObjectForKey (entity, "target")))) {
vec3_t position = {};
plitem_t *target = PL_ObjectForKey (targets, str);
vec4f_t dir = { 1, 0, 0, 0 };
if (target) {
if ((str = PL_String (PL_ObjectForKey (target, "origin")))) {
sscanf (str, "%f %f %f", VectorExpandAddr (position));
dir = parse_position (str);
dir = normalf (dir - light->position);
}
VectorSubtract (position, light->position, light->direction);
VectorNormalize (light->direction);
}
float angle = 40;
if ((str = PL_String (PL_ObjectForKey (entity, "angle")))) {
angle = atof (str);
}
light->cone = -cos (angle * M_PI / 360); // half angle
dir[3] = -cos (angle * M_PI / 360); // half angle
light->direction = dir;
}
if ((str = PL_String (PL_ObjectForKey (entity, "light_lev")))
|| (str = PL_String (PL_ObjectForKey (entity, "_light")))) {
light->light = atof (str);
light->color[3] = atof (str);
}
if ((str = PL_String (PL_ObjectForKey (entity, "style")))) {
light->data = atoi (str) & 0x3f;
*style = atoi (str) & 0x3f;
}
if ((str = PL_String (PL_ObjectForKey (entity, "delay")))) {
model = (atoi (str) & 0x7) << 7;
model = atoi (str) & 0x7;
if (model == LM_INVERSE2) {
model = LM_INVERSE3; //FIXME for marcher (need a map)
}
light->data |= model;
}
if ((str = PL_String (PL_ObjectForKey (entity, "color")))
@ -662,15 +672,29 @@ parse_light (qfv_light_t *light, const plitem_t *entity,
VectorScale (light->color, 1/255.0, light->color);
}
if (model == LM_INFINITE) {
light->data |= ST_CASCADE;
} else if (model != LM_AMBIENT) {
if (light->cone > -0.5) {
light->data |= ST_CUBE;
} else {
light->data |= ST_PLANE;
}
vec4f_t attenuation = { 1, 0, 0, 0 }; // inverse square
switch (model) {
case LM_LINEAR:
attenuation = (vec4f_t) { 0, 0, 1, 1 / fabsf (light->color[3]) };
break;
case LM_INVERSE:
attenuation = (vec4f_t) { 0, 1.0 / 128, 0, 0 };
break;
case LM_INVERSE2:
attenuation = (vec4f_t) { 1.0 / 16384, 0, 0, 0 };
break;
case LM_INFINITE:
attenuation = (vec4f_t) { 0, 0, 1, 0 };
break;
case LM_AMBIENT:
attenuation = (vec4f_t) { 0, 0, 1, 0 };
light->direction = (vec4f_t) { 0, 0, 0, 1 };
break;
case LM_INVERSE3:
attenuation = (vec4f_t) { 1.0 / 16384, 2.0 / 128, 1, 0 };
break;
}
light->attenuation = attenuation;
}
static void
@ -681,8 +705,18 @@ create_light_matrices (lightingctx_t *lctx)
qfv_light_t *light = &lctx->lights.a[i];
mat4f_t view;
mat4f_t proj;
int mode = ST_NONE;
switch (light->data & ShadowMask) {
if (!light->position[3]) {
mode = ST_CASCADE;
} else {
if (light->direction[3] > -0.5) {
mode = ST_CUBE;
} else {
mode = ST_PLANE;
}
}
switch (mode) {
default:
case ST_NONE:
case ST_CUBE:
@ -691,13 +725,14 @@ create_light_matrices (lightingctx_t *lctx)
case ST_CASCADE:
case ST_PLANE:
//FIXME will fail for -ref_direction
mat4fquat (view, qrotf (loadvec3f (light->direction),
ref_direction));
vec4f_t dir = light->direction;
dir[3] = 0;
mat4fquat (view, qrotf (dir, ref_direction));
break;
}
VectorNegate (light->position, view[3]);
switch (light->data & ShadowMask) {
switch (mode) {
case ST_NONE:
mat4fidentity (proj);
break;
@ -709,7 +744,7 @@ create_light_matrices (lightingctx_t *lctx)
mat4fidentity (proj);
break;
case ST_PLANE:
QFV_PerspectiveCos (proj, light->cone);
QFV_PerspectiveCos (proj, -light->direction[3]);
break;
}
mmulf (lctx->lightmats.a[i], proj, view);
@ -722,10 +757,11 @@ light_compare (const void *_l2, const void *_l1)
const qfv_light_t *l1 = _l1;
const qfv_light_t *l2 = _l2;
if (l1->light == l2->light) {
return (l1->data & ShadowMask) - (l2->data & ShadowMask);
if (l1->color[3] == l2->color[3]) {
return (l1->position[3] == l2->position[3])
&& (l1->direction[3] > -0.5) == (l2->direction[3] > -0.5);
}
return l1->light - l2->light;
return l1->color[3] - l2->color[3];
}
static VkImage
@ -758,7 +794,7 @@ create_map (int size, int layers, int cube, vulkan_ctx_t *ctx)
}
static VkImageView
create_view (VkImage image, int baseLayer, int data, int id, vulkan_ctx_t *ctx)
create_view (VkImage image, int baseLayer, int mode, int id, vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
@ -767,7 +803,7 @@ create_view (VkImage image, int baseLayer, int data, int id, vulkan_ctx_t *ctx)
VkImageViewType type = 0;
const char *viewtype = 0;
switch (data & ShadowMask) {
switch (mode) {
case ST_NONE:
return 0;
case ST_PLANE:
@ -827,8 +863,18 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
DARRAY_RESIZE (&lctx->lightlayers, numLights);
qsort (lights, numLights, sizeof (qfv_light_t), light_compare);
for (int i = 0; i < numLights; i++) {
int shadow = lights[i].data & ShadowMask;
int layers = 1;
int shadow = ST_NONE;
if (!lights[i].position[3]) {
shadow = ST_CASCADE;
} else {
if (lights[i].direction[3] > -0.5) {
shadow = ST_CUBE;
} else {
shadow = ST_PLANE;
}
}
if (shadow == ST_CASCADE || shadow == ST_NONE) {
// cascade shadows will be handled separately, and "none" has no
// shadow map at all
@ -838,13 +884,14 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
if (shadow == ST_CUBE) {
layers = 6;
}
if (size != (int) lights[i].light || numLayers + layers > maxLayers) {
if (size != (int) lights[i].color[3]
|| numLayers + layers > maxLayers) {
if (numLayers) {
VkImage shadow_map = create_map (size, numLayers, 1, ctx);
DARRAY_APPEND (&lctx->lightimages, shadow_map);
numLayers = 0;
}
size = lights[i].light;
size = lights[i].color[3];
}
imageMap[i] = lctx->lightimages.size;
lctx->lightlayers.a[i] = numLayers;
@ -859,8 +906,18 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
numLayers = 0;
size = 1024;
for (int i = 0; i < numLights; i++) {
int shadow = lights[i].data & ShadowMask;
int layers = 4;
int shadow = ST_NONE;
if (!lights[i].position[3]) {
shadow = ST_CASCADE;
} else {
if (lights[i].direction[3] > -0.5) {
shadow = ST_CUBE;
} else {
shadow = ST_PLANE;
}
}
if (shadow != ST_CASCADE) {
continue;
@ -902,9 +959,20 @@ build_shadow_maps (lightingctx_t *lctx, vulkan_ctx_t *ctx)
lctx->lightviews.a[i] = 0;
continue;
}
int mode = ST_NONE;
if (!lctx->lights.a[i].position[3]) {
mode = ST_CASCADE;
} else {
if (lctx->lights.a[i].direction[3] > -0.5) {
mode = ST_CUBE;
} else {
mode = ST_PLANE;
}
}
lctx->lightviews.a[i] = create_view (lctx->lightimages.a[imageMap[i]],
lctx->lightlayers.a[i],
lctx->lights.a[i].data, i, ctx);
mode, i, ctx);
}
Sys_MaskPrintf (SYS_vulkan, "shadow maps: %d layers in %zd images: %zd\n",
totalLayers, lctx->lightimages.size, memsize);
@ -932,6 +1000,7 @@ Vulkan_LoadLights (model_t *model, const char *entity_data, vulkan_ctx_t *ctx)
}
lctx->lights.size = 0;
lctx->lightstyles.size = 0;
lctx->lightleafs.size = 0;
lctx->lightmats.size = 0;
if (lctx->sun_pvs) {
@ -989,11 +1058,13 @@ Vulkan_LoadLights (model_t *model, const char *entity_data, vulkan_ctx_t *ctx)
parse_sun (lctx, entity, model);
} else if (strnequal (classname, "light", 5)) {
qfv_light_t light = {};
int style = 0;
parse_light (&light, entity, targets);
parse_light (&light, &style, entity, targets);
// some lights have 0 output, so drop them
if (light.light) {
if (light.color[3]) {
DARRAY_APPEND (&lctx->lights, light);
DARRAY_APPEND (&lctx->lightstyles, style);
}
}
}