Skip to content

Commit

Permalink
rt: refactor loading surface lights, fix #417
Browse files Browse the repository at this point in the history
Do not entangle brush model loading with loading surface lights.
Do a separate pass over brush model surfaces for the sole purpose of
finding light sources. Enables consistent live-reloading light data
after patching entities/surface/rad files.
  • Loading branch information
w23 committed Mar 3, 2023
1 parent 58c9a99 commit a284567
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 164 deletions.
152 changes: 84 additions & 68 deletions ref/vk/vk_brush.c
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,6 @@ typedef struct {
int num_surfaces, num_vertices, num_indices;
int max_texture_id;
int water_surfaces;
//int sky_surfaces;
int emissive_surfaces;
} model_sizes_t;

static model_sizes_t computeSizes( const model_t *mod ) {
Expand All @@ -482,39 +480,11 @@ static model_sizes_t computeSizes( const model_t *mod ) {
sizes.num_indices += 3 * (surf->numedges - 1);
if (tex_id > sizes.max_texture_id)
sizes.max_texture_id = tex_id;

{
const xvk_patch_surface_t *const psurf = R_VkPatchGetSurface(i);
vec3_t emissive;
if ((psurf && (psurf->flags & Patch_Surface_Emissive)) || (RT_GetEmissiveForTexture(emissive, tex_id)))
++sizes.emissive_surfaces;
}
}

return sizes;
}

static rt_light_add_polygon_t loadPolyLight(const model_t *mod, const int surface_index, const msurface_t *surf, const vec3_t emissive) {
rt_light_add_polygon_t lpoly = {0};
lpoly.num_vertices = Q_min(7, surf->numedges);

// TODO split, don't clip
if (surf->numedges > 7)
gEngine.Con_Printf(S_WARN "emissive surface %d has %d vertices; clipping to 7\n", surface_index, surf->numedges);

VectorCopy(emissive, lpoly.emissive);

for (int i = 0; i < lpoly.num_vertices; ++i) {
const int iedge = mod->surfedges[surf->firstedge + i];
const medge_t *edge = mod->edges + (iedge >= 0 ? iedge : -iedge);
const mvertex_t *vertex = mod->vertexes + (iedge >= 0 ? edge->v[0] : edge->v[1]);
VectorCopy(vertex->position, lpoly.vertices[i]);
}

lpoly.surface = surf;
return lpoly;
}

static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
vk_brush_model_t *bmodel = mod->cache.data;
uint32_t vertex_offset = 0;
Expand Down Expand Up @@ -556,30 +526,6 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
if (t != tex_id)
continue;

// FIXME move this to rt_light_bsp and static loading
{
qboolean is_emissive = false;
vec3_t emissive = {0};
rt_light_add_polygon_t polylight;

if (psurf && (psurf->flags & Patch_Surface_Emissive)) {
is_emissive = true;
VectorCopy(psurf->emissive, emissive);
} else if (RT_GetEmissiveForTexture(emissive, tex_id)) {
is_emissive = true;
}

if (is_emissive) {
if (bmodel->render_model.polylights) {
ASSERT(bmodel->render_model.polylights_count < sizes.emissive_surfaces);
bmodel->render_model.polylights[bmodel->render_model.polylights_count++] = loadPolyLight(mod, surface_index, surf, emissive);
} else {
polylight = loadPolyLight(mod, surface_index, surf, emissive);
RT_LightAddPolygon(&polylight);
}
}
}

++num_geometries;

//gEngine.Con_Reportf( "surface %d: numverts=%d numedges=%d\n", i, surf->polys ? surf->polys->numverts : -1, surf->numedges );
Expand Down Expand Up @@ -680,21 +626,17 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {

R_GeometryBufferUnlock( &buffer );

if (bmodel->render_model.polylights) {
gEngine.Con_Reportf("Dynamic polylights %d %d \n", sizes.emissive_surfaces, bmodel->render_model.polylights_count);
ASSERT(sizes.emissive_surfaces == bmodel->render_model.polylights_count);
}
bmodel->render_model.dynamic_polylights = NULL;
bmodel->render_model.dynamic_polylights_count = 0;

ASSERT(sizes.num_surfaces == num_geometries);
bmodel->render_model.num_geometries = num_geometries;

return true;
}

qboolean VK_BrushModelLoad( model_t *mod, qboolean map )
{
if (mod->cache.data)
{
qboolean VK_BrushModelLoad( model_t *mod ) {
if (mod->cache.data) {
gEngine.Con_Reportf( S_WARN "Model %s was already loaded\n", mod->name );
return true;
}
Expand All @@ -711,17 +653,13 @@ qboolean VK_BrushModelLoad( model_t *mod, qboolean map )
mod->cache.data = bmodel;
Q_strncpy(bmodel->render_model.debug_name, mod->name, sizeof(bmodel->render_model.debug_name));
bmodel->render_model.render_type = kVkRenderTypeSolid;
bmodel->render_model.static_map = map;

bmodel->num_water_surfaces = sizes.water_surfaces;
Vector4Set(bmodel->render_model.color, 1, 1, 1, 1);

if (sizes.num_surfaces != 0) {
bmodel->render_model.geometries = (vk_render_geometry_t*)((char*)(bmodel + 1));

if (!map && sizes.emissive_surfaces)
bmodel->render_model.polylights = Mem_Malloc(vk_core.pool, sizeof(bmodel->render_model.polylights[0]) * sizes.emissive_surfaces);

if (!loadBrushSurfaces(sizes, mod) || !VK_RenderModelInit(&bmodel->render_model)) {
gEngine.Con_Printf(S_ERROR "Could not load model %s\n", mod->name);
Mem_Free(bmodel);
Expand All @@ -745,8 +683,6 @@ void VK_BrushModelDestroy( model_t *mod ) {
return;

VK_RenderModelDestroy(&bmodel->render_model);
if (bmodel->render_model.polylights)
Mem_Free(bmodel->render_model.polylights);
Mem_Free(bmodel);
mod->cache.data = NULL;
}
Expand All @@ -757,3 +693,83 @@ void VK_BrushStatsClear( void )
g_brush.stat.num_vertices = 0;
g_brush.stat.num_indices = 0;
}

static rt_light_add_polygon_t loadPolyLight(const model_t *mod, const int surface_index, const msurface_t *surf, const vec3_t emissive) {
rt_light_add_polygon_t lpoly = {0};
lpoly.num_vertices = Q_min(7, surf->numedges);

// TODO split, don't clip
if (surf->numedges > 7)
gEngine.Con_Printf(S_WARN "emissive surface %d has %d vertices; clipping to 7\n", surface_index, surf->numedges);

VectorCopy(emissive, lpoly.emissive);

for (int i = 0; i < lpoly.num_vertices; ++i) {
const int iedge = mod->surfedges[surf->firstedge + i];
const medge_t *edge = mod->edges + (iedge >= 0 ? iedge : -iedge);
const mvertex_t *vertex = mod->vertexes + (iedge >= 0 ? edge->v[0] : edge->v[1]);
VectorCopy(vertex->position, lpoly.vertices[i]);
}

lpoly.surface = surf;
return lpoly;
}

void R_VkBrushModelCollectEmissiveSurfaces( const struct model_s *mod, qboolean is_worldmodel ) {
typedef struct {
int model_surface_index;
int surface_index;
const msurface_t *surf;
vec3_t emissive;
} emissive_surface_t;
emissive_surface_t emissive_surfaces[MAX_SURFACE_LIGHTS];
int emissive_surfaces_count = 0;

for( int i = 0; i < mod->nummodelsurfaces; ++i) {
const int surface_index = mod->firstmodelsurface + i;
const msurface_t *surf = mod->surfaces + surface_index;

if (!renderableSurface(surf, surface_index))
continue;

const int tex_id = surf->texinfo->texture->gl_texturenum; // TODO animation?
const xvk_patch_surface_t *const psurf = R_VkPatchGetSurface(surface_index);

vec3_t emissive;
if ((psurf && (psurf->flags & Patch_Surface_Emissive)) || (RT_GetEmissiveForTexture(emissive, tex_id))) {
if (emissive_surfaces_count == MAX_SURFACE_LIGHTS) {
gEngine.Con_Printf(S_ERROR "Too many emissive surfaces for model %s: max=%d\n", mod->name, MAX_SURFACE_LIGHTS);
break;
}

emissive_surface_t* const surface = &emissive_surfaces[emissive_surfaces_count++];
surface->model_surface_index = i;
surface->surface_index = surface_index;
surface->surf = surf;
VectorCopy(emissive, surface->emissive);
}
}

vk_brush_model_t *const bmodel = mod->cache.data;
ASSERT(bmodel);

if (!is_worldmodel) {
if (bmodel->render_model.dynamic_polylights)
Mem_Free(bmodel->render_model.dynamic_polylights);
bmodel->render_model.dynamic_polylights_count = emissive_surfaces_count;
bmodel->render_model.dynamic_polylights = Mem_Malloc(vk_core.pool, sizeof(bmodel->render_model.dynamic_polylights[0]) * emissive_surfaces_count);
}

for (int i = 0; i < emissive_surfaces_count; ++i) {
const emissive_surface_t* const s = emissive_surfaces + i;

const rt_light_add_polygon_t polylight = loadPolyLight(mod, s->surface_index, s->surf, s->emissive);
if (!is_worldmodel) {
bmodel->render_model.dynamic_polylights[i] = polylight;
} else {
RT_LightAddPolygon(&polylight);
}
}

gEngine.Con_Reportf("Loaded %d polylights for %smodel %s\n", emissive_surfaces_count, is_worldmodel ? "world" : "movable ", mod->name);
}
4 changes: 3 additions & 1 deletion ref/vk/vk_brush.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ struct cl_entity_s;
qboolean VK_BrushInit( void );
void VK_BrushShutdown( void );

qboolean VK_BrushModelLoad(struct model_s *mod, qboolean map);
qboolean VK_BrushModelLoad(struct model_s *mod);
void VK_BrushModelDestroy(struct model_s *mod);

void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, float blend, const matrix4x4 model );
void VK_BrushStatsClear( void );

const texture_t *R_TextureAnimation( const cl_entity_t *ent, const msurface_t *s, const struct texture_s *base_override );

void R_VkBrushModelCollectEmissiveSurfaces( const struct model_s *mod, qboolean is_worldmodel );
91 changes: 46 additions & 45 deletions ref/vk/vk_light.c
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ static void prepareSurfacesLeafVisibilityCache( const struct model_s *map ) {
g_lights_bsp.surfaces[i].potentially_visible_leafs = NULL;
}

void RT_LightsNewMapBegin( const struct model_s *map ) {
void RT_LightsNewMap( const struct model_s *map ) {
// 1. Determine map bounding box (and optimal grid size?)
// map->mins, maxs
vec3_t map_size, min_cell, max_cell;
Expand Down Expand Up @@ -504,48 +504,6 @@ void RT_LightsNewMapBegin( const struct model_s *map ) {
g_lights_.visited_cells = bitArrayCreate(g_lights.map.grid_cells);

prepareSurfacesLeafVisibilityCache( map );

// Load RAD data based on map name
memset(g_lights_.map.emissive_textures, 0, sizeof(g_lights_.map.emissive_textures));
loadRadData( map, "maps/lights.rad" );

{
int name_len = Q_strlen(map->name);

// Strip ".bsp" suffix
if (name_len > 4 && 0 == Q_stricmp(map->name + name_len - 4, ".bsp"))
name_len -= 4;

loadRadData( map, "%.*s.rad", name_len, map->name );
}

// Clear static lights counts
{
g_lights_.num_polygons = g_lights_.num_static.polygons = 0;
g_lights_.num_point_lights = g_lights_.num_static.point_lights = 0;
g_lights_.num_polygon_vertices = g_lights_.num_static.polygon_vertices = 0;

for (int i = 0; i < g_lights.map.grid_cells; ++i) {
vk_lights_cell_t *const cell = g_lights.cells + i;
cell->num_point_lights = cell->num_static.point_lights = 0;
cell->num_polygons = cell->num_static.polygons = 0;
cell->frame_sequence = g_lights_.frame_sequence;
}
}
}

void RT_LightsFrameBegin( void ) {
g_lights_.num_polygons = g_lights_.num_static.polygons;
g_lights_.num_point_lights = g_lights_.num_static.point_lights;
g_lights_.num_polygon_vertices = g_lights_.num_static.polygon_vertices;

g_lights.stats.dirty_cells = 0;

for (int i = 0; i < g_lights.map.grid_cells; ++i) {
vk_lights_cell_t *const cell = g_lights.cells + i;
cell->num_polygons = cell->num_static.polygons;
cell->num_point_lights = cell->num_static.point_lights;
}
}

static qboolean addSurfaceLightToCell( int cell_index, int polygon_light_index ) {
Expand Down Expand Up @@ -931,10 +889,39 @@ static void processStaticPointLights( void ) {
APROF_SCOPE_END(static_lights);
}

void RT_LightsNewMapEnd( const struct model_s *map ) {
//debug_dump_lights.enabled = true;
void RT_LightsLoadBegin( const struct model_s *map ) {
// Load RAD data based on map name
{
int name_len = Q_strlen(map->name);

// Strip ".bsp" suffix
if (name_len > 4 && 0 == Q_stricmp(map->name + name_len - 4, ".bsp"))
name_len -= 4;

memset(g_lights_.map.emissive_textures, 0, sizeof(g_lights_.map.emissive_textures));
loadRadData( map, "maps/lights.rad" );
loadRadData( map, "%.*s.rad", name_len, map->name );
}

// Clear static lights counts
{
g_lights_.num_polygons = g_lights_.num_static.polygons = 0;
g_lights_.num_point_lights = g_lights_.num_static.point_lights = 0;
g_lights_.num_polygon_vertices = g_lights_.num_static.polygon_vertices = 0;

for (int i = 0; i < g_lights.map.grid_cells; ++i) {
vk_lights_cell_t *const cell = g_lights.cells + i;
cell->num_point_lights = cell->num_static.point_lights = 0;
cell->num_polygons = cell->num_static.polygons = 0;
cell->frame_sequence = g_lights_.frame_sequence;
}
}

processStaticPointLights();
}

void RT_LightsLoadEnd( void ) {
//debug_dump_lights.enabled = true;

// Fix static counts
{
Expand Down Expand Up @@ -1131,6 +1118,20 @@ int RT_LightAddPolygon(const rt_light_add_polygon_t *addpoly) {
}
}

void RT_LightsFrameBegin( void ) {
g_lights_.num_polygons = g_lights_.num_static.polygons;
g_lights_.num_point_lights = g_lights_.num_static.point_lights;
g_lights_.num_polygon_vertices = g_lights_.num_static.polygon_vertices;

g_lights.stats.dirty_cells = 0;

for (int i = 0; i < g_lights.map.grid_cells; ++i) {
vk_lights_cell_t *const cell = g_lights.cells + i;
cell->num_polygons = cell->num_static.polygons;
cell->num_point_lights = cell->num_static.point_lights;
}
}

static void uploadGridRange( int begin, int end ) {
const int count = end - begin;
ASSERT( count > 0 );
Expand Down
10 changes: 8 additions & 2 deletions ref/vk/vk_light.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,15 @@ extern vk_lights_t g_lights;
qboolean VK_LightsInit( void );
void VK_LightsShutdown( void );

// Allocate clusters and vis data for the new map
struct model_s;
void RT_LightsNewMapBegin( const struct model_s *map );
void RT_LightsNewMapEnd( const struct model_s *map );
void RT_LightsNewMap( const struct model_s *map );

// Clear light data and prepare for loading
// RT_LightsNewMap should have been already called for current map
void RT_LightsLoadBegin( const struct model_s *map );
// Finalize loading light data, i.e. mark everything loaded so far as static light data
void RT_LightsLoadEnd( void );

void RT_LightsFrameBegin( void );
void RT_LightsFrameEnd( void );
Expand Down
Loading

0 comments on commit a284567

Please sign in to comment.