From d7660cf358ff70c9f6c780c4f44066b69f4222f6 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Wed, 19 Apr 2023 11:40:08 -0700 Subject: [PATCH] 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. --- ref/vk/vk_brush.c | 2 +- ref/vk/vk_mapents.c | 140 ++++++++++++++++++++++++++++++++------------ ref/vk/vk_mapents.h | 20 ++++++- ref/vk/vk_scene.c | 34 ++++++++--- 4 files changed, 148 insertions(+), 48 deletions(-) diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 8b688916..082a667f 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -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; } diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index cfa3e46d..19c0cb10 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -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(); } diff --git a/ref/vk/vk_mapents.h b/ref/vk/vk_mapents.h index 61b81e37..501ed06b 100644 --- a/ref/vk/vk_mapents.h +++ b/ref/vk/vk_mapents.h @@ -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; diff --git a/ref/vk/vk_scene.c b/ref/vk/vk_scene.c index a7bd5964..5c315128 100644 --- a/ref/vk/vk_scene.c +++ b/ref/vk/vk_scene.c @@ -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;