vk: patch func_wall ents by their index, not model name

Note that referencing them by ent->index inside the engine is not
possible, as this index is not stable enough.
This commit is contained in:
Ivan Avdeev 2023-04-19 11:40:08 -07:00 committed by Ivan Avdeev
parent ca2a794341
commit d7660cf358
4 changed files with 148 additions and 48 deletions

View File

@ -853,7 +853,7 @@ void R_VkBrushModelCollectEmissiveSurfaces( const struct model_s *mod, qboolean
polylight.dynamic = true;
matrix3x4 m;
Matrix3x4_LoadIdentity(m);
Matrix3x4_SetOrigin(m, func_wall->offset[0], func_wall->offset[1], func_wall->offset[2]);
Matrix3x4_SetOrigin(m, func_wall->origin[0], func_wall->origin[1], func_wall->origin[2]);
polylight.transform_row = &m;
}

View File

@ -299,6 +299,10 @@ static void addLightEntity( const entity_props_t *props, unsigned have_fields )
fillLightFromProps(le, props, have_fields, false, g_map_entities.entity_count);
le->entity_index = g_map_entities.entity_count;
g_map_entities.refs[g_map_entities.entity_count] = (xvk_mapent_ref_t){
.class = props->classname,
.index = g_map_entities.num_lights,
};
g_map_entities.num_lights++;
}
@ -316,25 +320,35 @@ static void addTargetEntity( const entity_props_t *props ) {
Q_strcpy(target->targetname, props->targetname);
VectorCopy(props->origin, target->origin);
g_map_entities.refs[g_map_entities.entity_count] = (xvk_mapent_ref_t){
.class = Xvk_Target,
.index = g_map_entities.num_targets,
};
++g_map_entities.num_targets;
}
static void readWorldspawn( const entity_props_t *props ) {
Q_strcpy(g_map_entities.wadlist, props->wad);
g_map_entities.refs[g_map_entities.entity_count] = (xvk_mapent_ref_t){
.class = Worldspawn,
.index = -1,
};
}
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);
gEngine.Con_Reportf("func_wall entity=%d model=\"%s\", props_count=%d\n", g_map_entities.entity_count, (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++);
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};
/* NOTE: not used
e->rendercolor.r = 255;
e->rendercolor.g = 255;
e->rendercolor.b = 255;
@ -355,8 +369,14 @@ static void readFuncWall( const entity_props_t *const props, uint32_t have_field
e->rendercolor.g = props->rendercolor[1];
e->rendercolor.b = props->rendercolor[2];
}
*/
e->entity_index = g_map_entities.entity_count;
g_map_entities.refs[g_map_entities.entity_count] = (xvk_mapent_ref_t){
.class = FuncWall,
.index = g_map_entities.func_walls_count,
};
++g_map_entities.func_walls_count;
}
static void addPatchSurface( const entity_props_t *props, uint32_t have_fields ) {
@ -443,47 +463,62 @@ static void addPatchSurface( const entity_props_t *props, uint32_t have_fields )
}
}
int findLightEntityWithIndex( int index ) {
// TODO could do binary search (entities are sorted by index) but why
for (int i = 0; i < g_map_entities.num_lights; ++i) {
if (g_map_entities.lights[i].entity_index == index)
return i;
static void patchLightEntity( const entity_props_t *props, int ent_id, uint32_t have_fields, int index ) {
ASSERT(index >= 0);
ASSERT(index < g_map_entities.num_lights);
vk_light_entity_t *const light = g_map_entities.lights + index;
if (have_fields == Field__xvk_ent_id) {
gEngine.Con_Reportf("Deleting light entity (%d of %d) with index=%d\n", index, g_map_entities.num_lights, ent_id);
// Mark it as deleted
light->entity_index = -1;
return;
}
return -1;
fillLightFromProps(light, props, have_fields, true, ent_id);
}
static void addPatchLightEntity( const entity_props_t *props, uint32_t have_fields ) {
static void patchFuncWallEntity( const entity_props_t *props, uint32_t have_fields, int index ) {
ASSERT(index >= 0);
ASSERT(index < g_map_entities.func_walls_count);
xvk_mapent_func_wall_t *const fw = g_map_entities.func_walls + index;
if (have_fields & Field_origin)
VectorCopy(props->origin, fw->origin);
gEngine.Con_Reportf("Patching ent=%d func_wall=%d %f %f %f\n", fw->entity_index, index, fw->origin[0], fw->origin[1], fw->origin[2]);
}
static void patchEntity( const entity_props_t *props, uint32_t have_fields ) {
ASSERT(have_fields & Field__xvk_ent_id);
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 );
if (light_index < 0) {
gEngine.Con_Printf(S_ERROR "Patch light entity with index=%d not found\n", ent_id);
const int ei = props->_xvk_ent_id.values[i];
if (ei < 0 || ei >= g_map_entities.entity_count) {
gEngine.Con_Printf(S_ERROR "_xvk_ent_id value %d is out of bounds, max=%d\n", ei, g_map_entities.entity_count);
continue;
}
if (have_fields == Field__xvk_ent_id) {
gEngine.Con_Reportf("Deleting light entity (%d of %d) with index=%d\n", light_index, g_map_entities.num_lights, ent_id);
g_map_entities.num_lights--;
memmove(g_map_entities.lights + light_index, g_map_entities.lights + light_index + 1, sizeof(*g_map_entities.lights) * g_map_entities.num_lights - light_index);
continue;
const xvk_mapent_ref_t *const ref = g_map_entities.refs + ei;
switch (ref->class) {
case Light:
case LightSpot:
case LightEnvironment:
patchLightEntity(props, ei, have_fields, ref->index);
break;
case FuncWall:
patchFuncWallEntity(props, have_fields, ref->index);
break;
default:
gEngine.Con_Printf(S_WARN "vk_mapents: trying to patch unsupported entity %d class %d\n", ei, ref->class);
}
fillLightFromProps(g_map_entities.lights + light_index, props, have_fields, true, props->_xvk_ent_id.values[i]);
}
}
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 ) {
static void parseEntities( char *string, qboolean is_patch ) {
unsigned have_fields = 0;
int props_count = 0;
entity_props_t values;
@ -502,6 +537,10 @@ static void parseEntities( char *string ) {
have_fields = None;
values = (entity_props_t){0};
props_count = 0;
g_map_entities.refs[g_map_entities.entity_count] = (xvk_mapent_ref_t){
.class = Unknown,
.index = -1,
};
continue;
} else if (key[0] == '}') {
const int target_fields = Field_targetname | Field_origin;
@ -523,20 +562,25 @@ static void parseEntities( char *string ) {
break;
case Unknown:
if (have_fields & Field__xvk_surface_id) {
addPatchSurface( &values, have_fields );
} else if (have_fields & Field__xvk_ent_id) {
addPatchLightEntity( &values, have_fields );
} else if ((have_fields & Field__xvk_offset) && (have_fields & Field_model)) {
patchFuncWallEntity( &values );
if (is_patch) {
if (have_fields & Field__xvk_surface_id) {
addPatchSurface( &values, have_fields );
} else if (have_fields & Field__xvk_ent_id) {
patchEntity( &values, have_fields );
}
}
break;
case Ignored:
case Xvk_Target:
// Skip
break;
}
g_map_entities.entity_count++;
if (g_map_entities.entity_count == MAX_MAP_ENTITIES) {
gEngine.Con_Printf(S_ERROR "vk_mapents: too many entities, skipping the rest");\
break;
}
continue;
}
@ -615,7 +659,7 @@ static void parsePatches( const model_t *const map ) {
return;
}
parseEntities( (char*)data );
parseEntities( (char*)data, true );
Mem_Free(data);
}
@ -630,7 +674,7 @@ void XVK_ParseMapEntities( void ) {
g_map_entities.entity_count = 0;
g_map_entities.func_walls_count = 0;
parseEntities( map->entities );
parseEntities( map->entities, false );
orientSpotlights();
}
@ -638,6 +682,24 @@ void XVK_ParseMapPatches( void ) {
const model_t* const map = gEngine.pfnGetModelByIndex( 1 );
parsePatches( map );
// Perform light deletion and compaction
{
int w = 0;
for (int r = 0; r < g_map_entities.num_lights; ++r) {
// Deleted
if (g_map_entities.lights[r].entity_index < 0) {
continue;
}
if (r != w)
memcpy(g_map_entities.lights + w, g_map_entities.lights + r, sizeof(vk_light_entity_t));
++w;
}
g_map_entities.num_lights = w;
}
orientSpotlights();
}

View File

@ -1,5 +1,7 @@
#pragma once
#include "xash3d_types.h"
#include "const.h" // typedef word, needed for bspfile.h
#include "bspfile.h" // MAX_MAP_ENTITIES
#define ENT_PROP_LIST(X) \
X(0, vec3_t, origin, Vec3) \
@ -24,11 +26,14 @@
X(19, vec2_t, _xvk_tex_offset, Vec2) \
X(20, vec2_t, _xvk_tex_scale, Vec2) \
X(21, string, model, String) \
/* NOTE: not used
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,
@ -38,6 +43,7 @@ typedef enum {
Worldspawn,
FuncWall,
Ignored,
Xvk_Target,
} class_name_e;
#define MAX_INT_ARRAY_SIZE 64
@ -88,13 +94,21 @@ typedef struct {
typedef struct {
int entity_index;
string model;
vec3_t origin;
/* NOTE: not used. Might be needed for #118 in the future.
int rendermode, renderamt, renderfx;
color24 rendercolor;
struct cl_entity_s *ent;
vec3_t offset;
*/
} xvk_mapent_func_wall_t;
typedef struct {
class_name_e class;
int index;
} xvk_mapent_ref_t;
typedef struct {
int num_lights;
vk_light_entity_t lights[256];
@ -110,6 +124,10 @@ typedef struct {
#define MAX_FUNC_WALL_ENTITIES 64
int func_walls_count;
xvk_mapent_func_wall_t func_walls[MAX_FUNC_WALL_ENTITIES];
// TODO find out how to read this from the engine, or make its size dynamic
//#define MAX_MAP_ENTITIES 2048
xvk_mapent_ref_t refs[MAX_MAP_ENTITIES];
} xvk_map_entities_t;
extern xvk_map_entities_t g_map_entities;

View File

@ -577,15 +577,35 @@ static void drawEntity( cl_entity_t *ent, int render_mode )
case mod_brush:
R_RotateForEntity( model, ent );
// Patch func_wall offsets
// TODO universal entity patching by index O(1); don't loop like that
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 (fw->entity_index == ent->index + 1) {
Matrix4x4_ConcatTranslate(model, fw->offset[0], fw->offset[1], fw->offset[2]);
break;
{
// Patch func_wall entities offsets
const int eindex = ent->index + 1; // TODO why is this off-by-1?
const xvk_mapent_func_wall_t *func_wall = NULL;
if (eindex >= 0 && eindex < g_map_entities.entity_count) {
const xvk_mapent_ref_t *const ref = g_map_entities.refs + eindex;
if (ref->class == FuncWall) {
func_wall = g_map_entities.func_walls + ref->index;
} else {
// Not found directly by index, find otherwise
// TODO this should be removed after we make sure the the index method above is enough
// FIXME nope, indexes are not stable
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(ent->model->name, fw->model) == 0) {
gEngine.Con_Printf(S_ERROR "Entity %s is func_wall=%d, but its ent->index=%d(+1=%d) is off (mapents: %d)\n", ent->model->name, i, ent->index, eindex, fw->entity_index);
//func_wall = fw;
break;
}
}
}
if (func_wall) {
Matrix3x4_LoadIdentity(model);
Matrix4x4_SetOrigin(model, func_wall->origin[0], func_wall->origin[1], func_wall->origin[2]);
}
}
}
VK_RenderStateSetMatrixModel( model );
VK_BrushModelDraw( ent, render_mode, blend, model );
break;