rt: extract basic func_wall patching support from PR #506

That PR has more stuff in it which we decided to drop.

This commit only contains:
- Detection of func_wall models, and adding their lights as static
  (fixes #415).
- Patching func_wall models offsets, #335. Does not yet patch them
  visually, but patches their light sources. Will be addressed in next
  commits. Patching is done via model name, not entity id, also will be
  fixed.
- Does not address culling (#118). This is the part dropped from the PR.
  Needs different approach. TBD
This commit is contained in:
Ivan Avdeev 2023-04-18 11:32:34 -07:00
parent cfddb75bc5
commit 7c6b45b5f8
4 changed files with 116 additions and 13 deletions

View File

@ -682,6 +682,17 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
return true;
}
static const xvk_mapent_func_wall_t *getModelFuncWallPatch( const model_t *const mod ) {
for (int i = 0; i < g_map_entities.func_walls_count; ++i) {
const xvk_mapent_func_wall_t *const fw = g_map_entities.func_walls + i;
if (Q_strcmp(mod->name, fw->model) == 0) {
return fw;
}
}
return NULL;
}
qboolean VK_BrushModelLoad( model_t *mod ) {
if (mod->cache.data) {
gEngine.Con_Reportf( S_WARN "Model %s was already loaded\n", mod->name );
@ -765,6 +776,12 @@ static rt_light_add_polygon_t loadPolyLight(const model_t *mod, const int surfac
}
void R_VkBrushModelCollectEmissiveSurfaces( const struct model_s *mod, qboolean is_worldmodel ) {
vk_brush_model_t *const bmodel = mod->cache.data;
ASSERT(bmodel);
const xvk_mapent_func_wall_t *func_wall = getModelFuncWallPatch(mod);
const qboolean is_static = is_worldmodel || func_wall;
typedef struct {
int model_surface_index;
int surface_index;
@ -809,17 +826,14 @@ void R_VkBrushModelCollectEmissiveSurfaces( const struct model_s *mod, qboolean
VectorCopy(emissive, surface->emissive);
}
vk_brush_model_t *const bmodel = mod->cache.data;
ASSERT(bmodel);
// Clear old per-geometry emissive values. The new emissive values will be assigned by the loop below only to the relevant geoms
for (int i = 0; i < bmodel->render_model.num_geometries; ++i) {
vk_render_geometry_t *const geom = bmodel->render_model.geometries + i;
VectorClear(geom->emissive);
}
// Non-worldmodel brush models may move around and so must have their emissive surfaces treated as dynamic
if (!is_worldmodel) {
// Non-static brush models may move around and so must have their emissive surfaces treated as dynamic
if (!is_static) {
if (bmodel->render_model.dynamic_polylights)
Mem_Free(bmodel->render_model.dynamic_polylights);
bmodel->render_model.dynamic_polylights_count = emissive_surfaces_count;
@ -829,11 +843,23 @@ void R_VkBrushModelCollectEmissiveSurfaces( const struct model_s *mod, qboolean
// Apply all emissive surfaces found
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);
// Worldmodel emissive surfaces are added immediately, as the worldmodel is always drawn and is static.
// Non-worldmodel ones will be applied later when the model is actually rendered
if (is_worldmodel) {
rt_light_add_polygon_t polylight = loadPolyLight(mod, s->surface_index, s->surf, s->emissive);
// func_wall surfaces do not really belong to BSP+PVS system, so they can't be used
// for lights visibility calculation directly.
if (func_wall) {
// TODO this is not really dynamic, but this flag signals using MovingSurface visibility calc
polylight.dynamic = true;
matrix3x4 m;
Matrix3x4_LoadIdentity(m);
Matrix3x4_SetOrigin(m, func_wall->offset[0], func_wall->offset[1], func_wall->offset[2]);
polylight.transform_row = &m;
}
// Static emissive surfaces are added immediately, as they are drawn all the time.
// Non-static ones will be applied later when the model is actually rendered
if (is_static) {
RT_LightAddPolygon(&polylight);
} else {
bmodel->render_model.dynamic_polylights[i] = polylight;
@ -843,5 +869,5 @@ void R_VkBrushModelCollectEmissiveSurfaces( const struct model_s *mod, qboolean
VectorCopy(polylight.emissive, bmodel->render_model.geometries[bmodel->surface_to_geometry_index[s->model_surface_index]].emissive);
}
gEngine.Con_Reportf("Loaded %d polylights for %smodel %s\n", emissive_surfaces_count, is_worldmodel ? "world" : "movable ", mod->name);
gEngine.Con_Reportf("Loaded %d polylights for %s model %s\n", emissive_surfaces_count, is_static ? "static" : "movable", mod->name);
}

View File

@ -135,6 +135,8 @@ static unsigned parseEntPropClassname(const char* value, class_name_e *out, unsi
*out = LightEnvironment;
} else if (Q_strcmp(value, "worldspawn") == 0) {
*out = Worldspawn;
} else if (Q_strcmp(value, "func_wall") == 0) {
*out = FuncWall;
} else {
*out = Ignored;
}
@ -321,6 +323,40 @@ static void readWorldspawn( const entity_props_t *props ) {
Q_strcpy(g_map_entities.wadlist, props->wad);
}
static void readFuncWall( const entity_props_t *const props, uint32_t have_fields, int props_count ) {
gEngine.Con_Reportf("func_wall entity model=\"%s\", props_count=%d\n", (have_fields & Field_model) ? props->model : "N/A", props_count);
if (g_map_entities.func_walls_count >= MAX_FUNC_WALL_ENTITIES) {
gEngine.Con_Printf(S_ERROR "Too many func_wall entities, max supported = %d\n", MAX_FUNC_WALL_ENTITIES);
return;
}
xvk_mapent_func_wall_t *const e = g_map_entities.func_walls + (g_map_entities.func_walls_count++);
*e = (xvk_mapent_func_wall_t){0};
e->rendercolor.r = 255;
e->rendercolor.g = 255;
e->rendercolor.b = 255;
Q_strcpy(e->model, props->model);
if (have_fields & Field_renderamt)
e->renderamt = props->renderamt;
if (have_fields & Field_renderfx)
e->renderfx = props->renderfx;
if (have_fields & Field_rendermode)
e->rendermode = props->rendermode;
if (have_fields & Field_rendercolor) {
e->rendercolor.r = props->rendercolor[0];
e->rendercolor.g = props->rendercolor[1];
e->rendercolor.b = props->rendercolor[2];
}
}
static void addPatchSurface( const entity_props_t *props, uint32_t have_fields ) {
const model_t* const map = gEngine.pfnGetModelByIndex( 1 );
const int num_surfaces = map->numsurfaces;
@ -415,7 +451,7 @@ int findLightEntityWithIndex( int index ) {
return -1;
}
static void addPatchEntity( const entity_props_t *props, uint32_t have_fields ) {
static void addPatchLightEntity( const entity_props_t *props, uint32_t have_fields ) {
for (int i = 0; i < props->_xvk_ent_id.num; ++i) {
const int ent_id = props->_xvk_ent_id.values[i];
const int light_index = findLightEntityWithIndex( ent_id );
@ -435,8 +471,19 @@ static void addPatchEntity( const entity_props_t *props, uint32_t have_fields )
}
}
static void patchFuncWallEntity( const entity_props_t *props ) {
for (int i = 0; i < g_map_entities.func_walls_count; ++i) {
xvk_mapent_func_wall_t *const fw = g_map_entities.func_walls + i;
if (Q_strcmp(props->model, fw->model) != 0)
continue;
VectorCopy(props->_xvk_offset, fw->offset);
}
}
static void parseEntities( char *string ) {
unsigned have_fields = 0;
int props_count = 0;
entity_props_t values;
char *pos = string;
//gEngine.Con_Reportf("ENTITIES: %s\n", pos);
@ -452,6 +499,7 @@ static void parseEntities( char *string ) {
if (key[0] == '{') {
have_fields = None;
values = (entity_props_t){0};
props_count = 0;
continue;
} else if (key[0] == '}') {
const int target_fields = Field_targetname | Field_origin;
@ -468,11 +516,17 @@ static void parseEntities( char *string ) {
readWorldspawn( &values );
break;
case FuncWall:
readFuncWall( &values, have_fields, props_count );
break;
case Unknown:
if (have_fields & Field__xvk_surface_id) {
addPatchSurface( &values, have_fields );
} else if (have_fields & Field__xvk_ent_id) {
addPatchEntity( &values, have_fields );
addPatchLightEntity( &values, have_fields );
} else if ((have_fields & Field__xvk_offset) && (have_fields & Field_model)) {
patchFuncWallEntity( &values );
}
break;
case Ignored:
@ -500,6 +554,7 @@ static void parseEntities( char *string ) {
{
//gEngine.Con_Reportf("Unknown field %s with value %s\n", key, value);
}
++props_count;
#undef CHECK_FIELD
}
}
@ -571,6 +626,7 @@ void XVK_ParseMapEntities( void ) {
g_map_entities.num_lights = 0;
g_map_entities.single_environment_index = NoEnvironmentLights;
g_map_entities.entity_count = 0;
g_map_entities.func_walls_count = 0;
parseEntities( map->entities );
orientSpotlights();

View File

@ -23,6 +23,12 @@
X(18, vec4_t, _xvk_tvec, Vec4) \
X(19, vec2_t, _xvk_tex_offset, Vec2) \
X(20, vec2_t, _xvk_tex_scale, Vec2) \
X(21, string, model, String) \
X(22, int, rendermode, Int) \
X(23, int, renderamt, Int) \
X(24, vec3_t, rendercolor, Vec3) \
X(25, int, renderfx, Int) \
X(26, vec3_t, _xvk_offset, Vec3) \
typedef enum {
Unknown = 0,
@ -30,6 +36,7 @@ typedef enum {
LightSpot,
LightEnvironment,
Worldspawn,
FuncWall,
Ignored,
} class_name_e;
@ -78,6 +85,15 @@ typedef struct {
#define MAX_MAPENT_TARGETS 256
typedef struct {
string model;
int rendermode, renderamt, renderfx;
color24 rendercolor;
struct cl_entity_s *ent;
vec3_t offset;
} xvk_mapent_func_wall_t;
typedef struct {
int num_lights;
vk_light_entity_t lights[256];
@ -89,6 +105,10 @@ typedef struct {
int num_targets;
xvk_mapent_target_t targets[MAX_MAPENT_TARGETS];
#define MAX_FUNC_WALL_ENTITIES 64
int func_walls_count;
xvk_mapent_func_wall_t func_walls[MAX_FUNC_WALL_ENTITIES];
} xvk_map_entities_t;
extern xvk_map_entities_t g_map_entities;

View File

@ -700,7 +700,8 @@ qboolean VK_RenderModelInit( vk_render_model_t *model ) {
void VK_RenderModelDestroy( vk_render_model_t* model ) {
// FIXME why the condition? we should do the cleanup anyway
if (vk_core.rtx && (g_render_state.current_frame_is_ray_traced || !model->dynamic)) {
VK_RayModelDestroy(model->ray_model);
if (model->ray_model)
VK_RayModelDestroy(model->ray_model);
if (model->dynamic_polylights)
Mem_Free(model->dynamic_polylights);
}