From a9dcf94f1bcc1ae5e3984b8ebaed6a96330a81f0 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 4 Sep 2023 13:48:58 -0400 Subject: [PATCH 1/7] vk: add initial support for brush surface normal smoothing Lots of artifacts Relevant: #139, #348 --- ref/vk/vk_brush.c | 241 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 230 insertions(+), 11 deletions(-) diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 5c79ad0c..6e8cbfe7 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -58,6 +58,21 @@ typedef struct { int water_indices; } model_sizes_t; +typedef struct conn_edge_s { + int first_surface; + int count; +} conn_edge_t; + +typedef struct linked_value_s { + int value, link; +} linked_value_t; + +#define MAX_VERTEX_SURFACES 16 +typedef struct conn_vertex_s { + int count; + linked_value_t surfs[MAX_VERTEX_SURFACES]; +} conn_vertex_t; + static struct { struct { int total_vertices, total_indices; @@ -76,6 +91,16 @@ static struct { #define MAX_ANIMATED_TEXTURES 256 int updated_textures[MAX_ANIMATED_TEXTURES]; + + // Smoothed normals comptutation + // Connectome for edges and vertices + struct { + int edges_capacity; + conn_edge_t *edges; + + int vertices_capacity; + conn_vertex_t *vertices; + } conn; } g_brush; void VK_InitRandomTable( void ) @@ -107,8 +132,9 @@ qboolean VK_BrushInit( void ) return true; } -void VK_BrushShutdown( void ) -{ +void VK_BrushShutdown( void ) { + if (g_brush.conn.edges) + Mem_Free(g_brush.conn.edges); } // speed up sin calculations @@ -763,6 +789,185 @@ typedef struct { uint16_t *out_indices; } fill_geometries_args_t; +static void getSurfaceNormal( const msurface_t *surf, vec3_t out_normal) { + if( FBitSet( surf->flags, SURF_PLANEBACK )) + VectorNegate( surf->plane->normal, out_normal ); + else + VectorCopy( surf->plane->normal, out_normal ); + + // TODO scale normal by area -- bigger surfaces should have bigger impact + //VectorScale(normal, surf->plane. +} + +static qboolean shouldSmoothLinkSurfaces(const model_t* mod, int surf1, int surf2) { + //return Q_min(surf1, surf2) == 741 && Q_max(surf1, surf2) == 743; + + vec3_t n1, n2; + getSurfaceNormal(mod->surfaces + surf1, n1); + getSurfaceNormal(mod->surfaces + surf2, n2); + + // TODO patch filtering + const float threshold = .7f; + return DotProduct(n1, n2) > threshold; + + /* + if ( + ((cedge->surfs[0] == 743 || cedge->surfs[1] == 743) && + (cedge->surfs[0] == 741 || cedge->surfs[1] == 741)) || + ((cedge->surfs[0] == 367 || cedge->surfs[1] == 367) && + (cedge->surfs[0] == 404 || cedge->surfs[1] == 404))) { + */ +} + +static int lvFindValue(const linked_value_t *li, int count, int needle) { + for (int i = 0; i < count; ++i) + if (li[i].value == needle) + return i; + return -1; +} +static int lvFindOrAddValue(linked_value_t *li, int *count, int capacity, int needle) { + const int found = lvFindValue(li, *count, needle); + if (found >= 0) + return found; + if (*count == capacity) + return -1; + li[*count].value = needle; + li[*count].link = *count; + return (*count)++; +} + +static int lvFindBaseIndex(const linked_value_t *li, int index) { + while (li[index].link != index) + index = li[index].link; + return index; +} + +static void lvFlatten(linked_value_t *li, int count) { + for (int i = 0; i < count; ++i) { + for (int j = i; j < count; ++j) { + if (lvFindBaseIndex(li, j) == i) { + li[j].link = i; + } + } + } +} + +static void linkSmoothSurfaces(const model_t* mod, int surf1, int surf2, int vertex_index) { + conn_vertex_t *v = g_brush.conn.vertices + vertex_index; + + int i1 = lvFindOrAddValue(v->surfs, &v->count, COUNTOF(v->surfs), surf1); + int i2 = lvFindOrAddValue(v->surfs, &v->count, COUNTOF(v->surfs), surf2); + + DEBUG("Link %d(%d)<->%d(%d) v=%d", surf1, i1, surf2, i2, vertex_index); + + if (i1 < 0 || i2 < 0) { + ERR("Model %s cannot smooth link surf %d<->%d for vertex %d", mod->name, surf1, surf2, vertex_index); + return; + } + + i1 = lvFindBaseIndex(v->surfs, i1); + i2 = lvFindBaseIndex(v->surfs, i2); + + // Link them + v->surfs[Q_max(i1, i2)].link = Q_min(i1, i2); +} + +static void connectVertices( const model_t *mod ) { + if (mod->numedges > g_brush.conn.edges_capacity) { + if (g_brush.conn.edges) + Mem_Free(g_brush.conn.edges); + + g_brush.conn.edges_capacity = mod->numedges; + g_brush.conn.edges = Mem_Calloc(vk_core.pool, sizeof(*g_brush.conn.edges) * g_brush.conn.edges_capacity); + } + + if (mod->numvertexes > g_brush.conn.vertices_capacity) { + if (g_brush.conn.vertices) + Mem_Free(g_brush.conn.vertices); + + g_brush.conn.vertices_capacity = mod->numvertexes; + g_brush.conn.vertices = Mem_Calloc(vk_core.pool, sizeof(*g_brush.conn.vertices) * g_brush.conn.vertices_capacity); + } + + // Find connection edges + for (int i = 0; i < mod->nummodelsurfaces; ++i) { + const int surface_index = mod->firstmodelsurface + i; + const msurface_t *surf = mod->surfaces + surface_index; + + for(int k = 0; k < surf->numedges; k++) { + const int iedge_dir = mod->surfedges[surf->firstedge + k]; + const int iedge = iedge_dir >= 0 ? iedge_dir : -iedge_dir; + + ASSERT(iedge >= 0); + ASSERT(iedge < mod->numedges); + + conn_edge_t *cedge = g_brush.conn.edges + iedge; + if (cedge->count == 0) { + cedge->first_surface = surface_index; + } else { + const medge_t *edge = mod->edges + iedge; + if (shouldSmoothLinkSurfaces(mod, cedge->first_surface, surface_index)) { + linkSmoothSurfaces(mod, cedge->first_surface, surface_index, edge->v[0]); + linkSmoothSurfaces(mod, cedge->first_surface, surface_index, edge->v[1]); + } + + if (cedge->count > 1) { + WARN("Model %s edge %d has %d surfaces", mod->name, i, cedge->count); + } + } + cedge->count++; + } // for surf->numedges + } // for mod->nummodelsurfaces + + int hist[17] = {0}; + for (int i = 0; i < mod->numvertexes; ++i) { + conn_vertex_t *vtx = g_brush.conn.vertices + i; + if (vtx->count < 16) { + hist[vtx->count]++; + } else { + hist[16]++; + } + + lvFlatten(vtx->surfs, vtx->count); + +// Too verbose +#if 0 + if (vtx->count) { + DEBUG("Vertex %d linked count %d", i, vtx->count); + for (int j = 0; j < vtx->count; ++j) { + DEBUG(" %d: l=%d v=%d", j, vtx->surfs[j].link, vtx->surfs[j].value); + } + } +#endif + } + + for (int i = 0; i < COUNTOF(hist); ++i) { + DEBUG("VTX hist[%d] = %d", i, hist[i]); + } +} + +static qboolean getSmoothedNormalFor(const model_t* mod, int vertex_index, int surface_index, vec3_t out_normal) { + const conn_vertex_t *v = g_brush.conn.vertices + vertex_index; + const int index = lvFindValue(v->surfs, v->count, surface_index); + if (index < 0) + return false; + const int base = lvFindBaseIndex(v->surfs, index); + + vec3_t normal = {0}; + for (int i = 0; i < v->count; ++i) { + if (v->surfs[i].link == base) { + const int surface = v->surfs[i].value; + vec3_t surf_normal = {0}; + getSurfaceNormal(mod->surfaces + surface, surf_normal); + VectorAdd(normal, surf_normal, normal); + } + } + + VectorNormalize(normal); + VectorCopy(normal, out_normal); + return true; +} + static qboolean fillBrushSurfaces(fill_geometries_args_t args) { int vertex_offset = 0; int num_geometries = 0; @@ -772,13 +977,15 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { uint16_t *p_ind = args.out_indices; int index_offset = args.base_index_offset; + connectVertices(args.mod); + // Load sorted by gl_texturenum // TODO this does not make that much sense in vulkan (can sort later) for (int t = 0; t <= args.sizes.max_texture_id; ++t) { for( int i = 0; i < args.mod->nummodelsurfaces; ++i) { const int surface_index = args.mod->firstmodelsurface + i; msurface_t *surf = args.mod->surfaces + surface_index; - mextrasurf_t *info = surf->info; + const mextrasurf_t *info = surf->info; vk_render_geometry_t *model_geometry = args.out_geometries + num_geometries; const float sample_size = gEngine.Mod_SampleSizeForFace( surf ); int index_count = 0; @@ -841,11 +1048,18 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { VectorCopy(surf->texinfo->vecs[0], tangent); VectorNormalize(tangent); + vec3_t surf_normal; + getSurfaceNormal(surf, surf_normal); + + vk_vertex_t *const pvert_begin = p_vert; for( int k = 0; k < surf->numedges; k++ ) { - const int iedge = args.mod->surfedges[surf->firstedge + k]; - const medge_t *edge = args.mod->edges + (iedge >= 0 ? iedge : -iedge); - const mvertex_t *in_vertex = args.mod->vertexes + (iedge >= 0 ? edge->v[0] : edge->v[1]); + const int iedge_dir = args.mod->surfedges[surf->firstedge + k]; + const int iedge = iedge_dir >= 0 ? iedge_dir : -iedge_dir; + const medge_t *edge = args.mod->edges + iedge; + const int vertex_index = iedge_dir >= 0 ? edge->v[0] : edge->v[1]; + const mvertex_t *in_vertex = args.mod->vertexes + vertex_index; + vk_vertex_t vertex = { {in_vertex->position[0], in_vertex->position[1], in_vertex->position[2]}, }; @@ -902,9 +1116,10 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { vertex.lm_tc[1] = t; } - if( FBitSet( surf->flags, SURF_PLANEBACK )) - VectorNegate( surf->plane->normal, vertex.normal ); - else VectorCopy( surf->plane->normal, vertex.normal ); + // Compute smoothed normal if needed + if (!getSmoothedNormalFor(args.mod, vertex_index, surface_index, vertex.normal)) { + VectorCopy(surf_normal, vertex.normal); + } VectorCopy(tangent, vertex.tangent); @@ -920,11 +1135,11 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { index_count += 3; index_offset += 3; } - } + } // for surf->numedges model_geometry->element_count = index_count; vertex_offset += surf->numedges; - } + } // for mod->nummodelsurfaces } ASSERT(args.sizes.num_surfaces == num_geometries); @@ -1081,6 +1296,10 @@ void VK_BrushModelDestroyAll( void ) { g_brush.stat.total_vertices = 0; g_brush.stat.total_indices = 0; g_brush.models_count = 0; + + memset(g_brush.conn.edges, 0, sizeof(*g_brush.conn.edges) * g_brush.conn.edges_capacity); + + memset(g_brush.conn.vertices, 0, sizeof(*g_brush.conn.vertices) * g_brush.conn.vertices_capacity); } static rt_light_add_polygon_t loadPolyLight(const model_t *mod, const int surface_index, const msurface_t *surf, const vec3_t emissive) { From 0856e9e70d569b4603d5ffb1b1b5408e3e2b3998 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 4 Sep 2023 14:35:19 -0400 Subject: [PATCH 2/7] vk: add patchable smoothing threshold Automatically smooth normals between surfaces with normals less than 45 degrees off. Can be adjusted from map.bsp.patch file like this (e.g. to 50 degrees): ``` { "_xvk_smoothing_threshold" "50" } ``` --- ref/vk/shaders/denoiser.comp | 3 ++- ref/vk/vk_brush.c | 12 +++--------- ref/vk/vk_mapents.c | 3 +++ ref/vk/vk_mapents.h | 3 +++ 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ref/vk/shaders/denoiser.comp b/ref/vk/shaders/denoiser.comp index c7f584e6..4e08d68c 100644 --- a/ref/vk/shaders/denoiser.comp +++ b/ref/vk/shaders/denoiser.comp @@ -41,6 +41,7 @@ const int INDIRECT_SCALE = 2; //#define DEBUG_TEXTURE normals_gs //#define DEBUG_TEXTURE emissive //#define DEBUG_TEXTURE light_point_diffuse +//#define DEBUG_NORMAL // Blatantly copypasted from https://www.shadertoy.com/view/XsGfWV vec3 aces_tonemap(vec3 color){ @@ -197,7 +198,7 @@ void main() { //imageStore(out_dest, pix, vec4(fract(imageLoad(position_t, pix).rgb/10.), 0.)); return; //imageStore(out_dest, pix, vec4(fract(imageLoad(geometry_prev_position, pix).rgb/50.), 0.)); return; -#if 0 +#if defined(DEBUG_NORMAL) vec3 geometry_normal, shading_normal; readNormals(pix, geometry_normal, shading_normal); //imageStore(out_dest, pix, vec4(.5 + geometry_normal * .5, 0.)); return; diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 6e8cbfe7..dcf2f4f6 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -807,16 +807,10 @@ static qboolean shouldSmoothLinkSurfaces(const model_t* mod, int surf1, int surf getSurfaceNormal(mod->surfaces + surf2, n2); // TODO patch filtering - const float threshold = .7f; - return DotProduct(n1, n2) > threshold; + const float dot = DotProduct(n1, n2); + DEBUG("Smoothing: dot(%d, %d) = %f (t=%f)", surf1, surf2, dot, g_map_entities.smoothing_threshold); - /* - if ( - ((cedge->surfs[0] == 743 || cedge->surfs[1] == 743) && - (cedge->surfs[0] == 741 || cedge->surfs[1] == 741)) || - ((cedge->surfs[0] == 367 || cedge->surfs[1] == 367) && - (cedge->surfs[0] == 404 || cedge->surfs[1] == 404))) { - */ + return dot >= g_map_entities.smoothing_threshold; } static int lvFindValue(const linked_value_t *li, int count, int needle) { diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index fde3ee2c..4833603f 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -570,6 +570,8 @@ static void parseEntities( char *string, qboolean is_patch ) { addPatchSurface( &values, have_fields ); } else if (have_fields & Field__xvk_ent_id) { patchEntity( &values, have_fields ); + } else if (have_fields & Field__xvk_smoothing_threshold) { + g_map_entities.smoothing_threshold = cosf(DEG2RAD(values._xvk_smoothing_threshold)); } } break; @@ -676,6 +678,7 @@ void XVK_ParseMapEntities( void ) { g_map_entities.single_environment_index = NoEnvironmentLights; g_map_entities.entity_count = 0; g_map_entities.func_walls_count = 0; + g_map_entities.smoothing_threshold = cosf(DEG2RAD(45.f)); parseEntities( map->entities, false ); orientSpotlights(); diff --git a/ref/vk/vk_mapents.h b/ref/vk/vk_mapents.h index 501ed06b..e3e07ab0 100644 --- a/ref/vk/vk_mapents.h +++ b/ref/vk/vk_mapents.h @@ -26,6 +26,7 @@ X(19, vec2_t, _xvk_tex_offset, Vec2) \ X(20, vec2_t, _xvk_tex_scale, Vec2) \ X(21, string, model, String) \ + X(22, float, _xvk_smoothing_threshold, Float) \ /* NOTE: not used X(22, int, rendermode, Int) \ @@ -128,6 +129,8 @@ typedef struct { // 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]; + + float smoothing_threshold; } xvk_map_entities_t; extern xvk_map_entities_t g_map_entities; From d8d5019971ed48540b0e0bc022dd3aa7275abef8 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 5 Sep 2023 12:06:59 -0400 Subject: [PATCH 3/7] vk: filter smoothing normals by texture thanks to G.I.F --- ref/vk/vk_brush.c | 20 +++++++++++++++++--- ref/vk/vk_mapents.c | 4 ++-- ref/vk/vk_mapents.h | 4 +++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index dcf2f4f6..eaba8753 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -799,18 +799,32 @@ static void getSurfaceNormal( const msurface_t *surf, vec3_t out_normal) { //VectorScale(normal, surf->plane. } +static int getSurfaceTexture(const msurface_t *surf, int surface_index) { + const xvk_patch_surface_t *const psurf = R_VkPatchGetSurface(surface_index); + if (psurf && psurf->tex_id >= 0) + return psurf->tex_id; + return surf->texinfo->texture->gl_texturenum; +} + static qboolean shouldSmoothLinkSurfaces(const model_t* mod, int surf1, int surf2) { //return Q_min(surf1, surf2) == 741 && Q_max(surf1, surf2) == 743; + // TODO patch filtering + + // Do not join surfaces with different textures. Assume they belong to different objects. + const int t1 = getSurfaceTexture(mod->surfaces + surf1, surf1); + const int t2 = getSurfaceTexture(mod->surfaces + surf2, surf2); + if (t1 != t2) + return false; + vec3_t n1, n2; getSurfaceNormal(mod->surfaces + surf1, n1); getSurfaceNormal(mod->surfaces + surf2, n2); - // TODO patch filtering const float dot = DotProduct(n1, n2); - DEBUG("Smoothing: dot(%d, %d) = %f (t=%f)", surf1, surf2, dot, g_map_entities.smoothing_threshold); + DEBUG("Smoothing: dot(%d, %d) = %f (t=%f)", surf1, surf2, dot, g_map_entities.smoothing.threshold); - return dot >= g_map_entities.smoothing_threshold; + return dot >= g_map_entities.smoothing.threshold; } static int lvFindValue(const linked_value_t *li, int count, int needle) { diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index 4833603f..a884431e 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -571,7 +571,7 @@ static void parseEntities( char *string, qboolean is_patch ) { } else if (have_fields & Field__xvk_ent_id) { patchEntity( &values, have_fields ); } else if (have_fields & Field__xvk_smoothing_threshold) { - g_map_entities.smoothing_threshold = cosf(DEG2RAD(values._xvk_smoothing_threshold)); + g_map_entities.smoothing.threshold = cosf(DEG2RAD(values._xvk_smoothing_threshold)); } } break; @@ -678,7 +678,7 @@ void XVK_ParseMapEntities( void ) { g_map_entities.single_environment_index = NoEnvironmentLights; g_map_entities.entity_count = 0; g_map_entities.func_walls_count = 0; - g_map_entities.smoothing_threshold = cosf(DEG2RAD(45.f)); + g_map_entities.smoothing.threshold = cosf(DEG2RAD(45.f)); parseEntities( map->entities, false ); orientSpotlights(); diff --git a/ref/vk/vk_mapents.h b/ref/vk/vk_mapents.h index e3e07ab0..68278174 100644 --- a/ref/vk/vk_mapents.h +++ b/ref/vk/vk_mapents.h @@ -130,7 +130,9 @@ typedef struct { //#define MAX_MAP_ENTITIES 2048 xvk_mapent_ref_t refs[MAX_MAP_ENTITIES]; - float smoothing_threshold; + struct { + float threshold; + } smoothing; } xvk_map_entities_t; extern xvk_map_entities_t g_map_entities; From 61416cfc661f9c47ef2d99e9a02e00b666ff933b Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 5 Sep 2023 12:35:28 -0400 Subject: [PATCH 4/7] vk: brush: do not link pairs that were explicitly excluded in patches This doesn't work as expected in some cases, as surfaces might still get linked transitively by neighbours. Solving this seems non-trivial for now, but maybe we can just live with it --- ref/vk/vk_brush.c | 12 ++++++++++-- ref/vk/vk_mapents.c | 26 ++++++++++++++++++++++++-- ref/vk/vk_mapents.h | 5 +++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index eaba8753..7fe389f3 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -809,14 +809,22 @@ static int getSurfaceTexture(const msurface_t *surf, int surface_index) { static qboolean shouldSmoothLinkSurfaces(const model_t* mod, int surf1, int surf2) { //return Q_min(surf1, surf2) == 741 && Q_max(surf1, surf2) == 743; - // TODO patch filtering - // Do not join surfaces with different textures. Assume they belong to different objects. const int t1 = getSurfaceTexture(mod->surfaces + surf1, surf1); const int t2 = getSurfaceTexture(mod->surfaces + surf2, surf2); if (t1 != t2) return false; + // Filter explicit exclusion + for (int i = 0; i < g_map_entities.smoothing.excluded_count; i+=2) { + const int cand1 = g_map_entities.smoothing.excluded[i]; + const int cand2 = g_map_entities.smoothing.excluded[i+1]; + + if ((cand1 == surf1 && cand2 == surf2) + || (cand1 == surf2 && cand2 == surf1)) + return false; + } + vec3_t n1, n2; getSurfaceNormal(mod->surfaces + surf1, n1); getSurfaceNormal(mod->surfaces + surf2, n2); diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index a884431e..db593a75 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -518,7 +518,22 @@ static void patchEntity( const entity_props_t *props, uint32_t have_fields ) { WARN("vk_mapents: trying to patch unsupported entity %d class %d", ei, ref->class); } } +} +static void appendExludedPairs(const entity_props_t *props) { + if (props->_xvk_smoothing_excluded_pairs.num % 2 != 0) { + ERR("vk_mapents: smoothing group exclusion list should be list of pairs -- divisible by 2; cutting the tail"); + } + + int count = props->_xvk_smoothing_excluded_pairs.num & ~1; + if (g_map_entities.smoothing.excluded_count + count > COUNTOF(g_map_entities.smoothing.excluded)) { + ERR("vk_mapents: smoothing exclusion group capacity exceeded, go complain in github issues"); + count = COUNTOF(g_map_entities.smoothing.excluded) - g_map_entities.smoothing.excluded_count; + } + + memcpy(g_map_entities.smoothing.excluded + g_map_entities.smoothing.excluded_count, props->_xvk_smoothing_excluded_pairs.values, count * sizeof(int)); + + g_map_entities.smoothing.excluded_count += count; } static void parseEntities( char *string, qboolean is_patch ) { @@ -570,8 +585,14 @@ static void parseEntities( char *string, qboolean is_patch ) { addPatchSurface( &values, have_fields ); } else if (have_fields & Field__xvk_ent_id) { patchEntity( &values, have_fields ); - } else if (have_fields & Field__xvk_smoothing_threshold) { - g_map_entities.smoothing.threshold = cosf(DEG2RAD(values._xvk_smoothing_threshold)); + } else { + if (have_fields & Field__xvk_smoothing_threshold) { + g_map_entities.smoothing.threshold = cosf(DEG2RAD(values._xvk_smoothing_threshold)); + } + + if (have_fields & Field__xvk_smoothing_excluded_pairs) { + appendExludedPairs(&values); + } } } break; @@ -679,6 +700,7 @@ void XVK_ParseMapEntities( void ) { g_map_entities.entity_count = 0; g_map_entities.func_walls_count = 0; g_map_entities.smoothing.threshold = cosf(DEG2RAD(45.f)); + g_map_entities.smoothing.excluded_count = 0; parseEntities( map->entities, false ); orientSpotlights(); diff --git a/ref/vk/vk_mapents.h b/ref/vk/vk_mapents.h index 68278174..aa00900c 100644 --- a/ref/vk/vk_mapents.h +++ b/ref/vk/vk_mapents.h @@ -27,6 +27,7 @@ X(20, vec2_t, _xvk_tex_scale, Vec2) \ X(21, string, model, String) \ X(22, float, _xvk_smoothing_threshold, Float) \ + X(23, int_array_t, _xvk_smoothing_excluded_pairs, IntArray) \ /* NOTE: not used X(22, int, rendermode, Int) \ @@ -132,6 +133,10 @@ typedef struct { struct { float threshold; + +#define MAX_EXCLUDED_SMOOTHING_SURFACES_PAIRS 32 + int excluded[MAX_EXCLUDED_SMOOTHING_SURFACES_PAIRS * 2]; + int excluded_count; } smoothing; } xvk_map_entities_t; From 58ed5e7277df49162963eb3d76a08993863ed39c Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 5 Sep 2023 12:54:57 -0400 Subject: [PATCH 5/7] vk: brush: add explicit smoothing group inclusion --- ref/vk/vk_brush.c | 29 +++++++++++++++++++++++------ ref/vk/vk_mapents.c | 22 ++++++++++++++++++++++ ref/vk/vk_mapents.h | 11 +++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 7fe389f3..632efbe0 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -809,12 +809,6 @@ static int getSurfaceTexture(const msurface_t *surf, int surface_index) { static qboolean shouldSmoothLinkSurfaces(const model_t* mod, int surf1, int surf2) { //return Q_min(surf1, surf2) == 741 && Q_max(surf1, surf2) == 743; - // Do not join surfaces with different textures. Assume they belong to different objects. - const int t1 = getSurfaceTexture(mod->surfaces + surf1, surf1); - const int t2 = getSurfaceTexture(mod->surfaces + surf2, surf2); - if (t1 != t2) - return false; - // Filter explicit exclusion for (int i = 0; i < g_map_entities.smoothing.excluded_count; i+=2) { const int cand1 = g_map_entities.smoothing.excluded[i]; @@ -825,6 +819,29 @@ static qboolean shouldSmoothLinkSurfaces(const model_t* mod, int surf1, int surf return false; } + for (int i = 0; i < g_map_entities.smoothing.groups_count; ++i) { + const xvk_smoothing_group_t *g = g_map_entities.smoothing.groups + i; + uint32_t bits = 0; + for (int j = 0; j < g->count; ++j) { + if (g->surfaces[j] == surf1) { + bits |= 1; + if (bits == 3) + return true; + } + else if (g->surfaces[j] == surf2) { + bits |= 2; + if (bits == 3) + return true; + } + } + } + + // Do not join surfaces with different textures. Assume they belong to different objects. + const int t1 = getSurfaceTexture(mod->surfaces + surf1, surf1); + const int t2 = getSurfaceTexture(mod->surfaces + surf2, surf2); + if (t1 != t2) + return false; + vec3_t n1, n2; getSurfaceNormal(mod->surfaces + surf1, n1); getSurfaceNormal(mod->surfaces + surf2, n2); diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index db593a75..7765e856 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -536,6 +536,24 @@ static void appendExludedPairs(const entity_props_t *props) { g_map_entities.smoothing.excluded_count += count; } +static void addSmoothingGroup(const entity_props_t *props) { + if (g_map_entities.smoothing.groups_count == MAX_INCLUDED_SMOOTHING_GROUPS) { + ERR("vk_mapents: limit of %d smoothing groups reached", MAX_INCLUDED_SMOOTHING_GROUPS); + return; + } + + xvk_smoothing_group_t *g = g_map_entities.smoothing.groups + (g_map_entities.smoothing.groups_count++); + + int count = props->_xvk_smoothing_group.num; + if (count > MAX_INCLUDED_SMOOTHING_SURFACES_IN_A_GROUP) { + ERR("vk_mapents: too many surfaces in a smoothing group. Max %d, got %d. Culling", MAX_INCLUDED_SMOOTHING_SURFACES_IN_A_GROUP, props->_xvk_smoothing_group.num); + count = MAX_INCLUDED_SMOOTHING_SURFACES_IN_A_GROUP; + } + + memcpy(g->surfaces, props->_xvk_smoothing_group.values, sizeof(int) * count); + g->count = count; +} + static void parseEntities( char *string, qboolean is_patch ) { unsigned have_fields = 0; int props_count = 0; @@ -593,6 +611,10 @@ static void parseEntities( char *string, qboolean is_patch ) { if (have_fields & Field__xvk_smoothing_excluded_pairs) { appendExludedPairs(&values); } + + if (have_fields & Field__xvk_smoothing_group) { + addSmoothingGroup(&values); + } } } break; diff --git a/ref/vk/vk_mapents.h b/ref/vk/vk_mapents.h index aa00900c..25698682 100644 --- a/ref/vk/vk_mapents.h +++ b/ref/vk/vk_mapents.h @@ -28,6 +28,7 @@ X(21, string, model, String) \ X(22, float, _xvk_smoothing_threshold, Float) \ X(23, int_array_t, _xvk_smoothing_excluded_pairs, IntArray) \ + X(24, int_array_t, _xvk_smoothing_group, IntArray) \ /* NOTE: not used X(22, int, rendermode, Int) \ @@ -111,6 +112,12 @@ typedef struct { int index; } xvk_mapent_ref_t; +#define MAX_INCLUDED_SMOOTHING_SURFACES_IN_A_GROUP 16 +typedef struct { + int count; + int surfaces[MAX_INCLUDED_SMOOTHING_SURFACES_IN_A_GROUP]; +} xvk_smoothing_group_t; + typedef struct { int num_lights; vk_light_entity_t lights[256]; @@ -137,6 +144,10 @@ typedef struct { #define MAX_EXCLUDED_SMOOTHING_SURFACES_PAIRS 32 int excluded[MAX_EXCLUDED_SMOOTHING_SURFACES_PAIRS * 2]; int excluded_count; + +#define MAX_INCLUDED_SMOOTHING_GROUPS 32 + int groups_count; + xvk_smoothing_group_t groups[MAX_INCLUDED_SMOOTHING_GROUPS]; } smoothing; } xvk_map_entities_t; From f42ea011f1cd0250ff50b71f85b3a5dfa7f5ad4a Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 5 Sep 2023 13:03:53 -0400 Subject: [PATCH 6/7] vk: mapents: fix not clearing smoothing groups --- ref/vk/vk_mapents.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index 7765e856..d0965489 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -723,6 +723,9 @@ void XVK_ParseMapEntities( void ) { g_map_entities.func_walls_count = 0; g_map_entities.smoothing.threshold = cosf(DEG2RAD(45.f)); g_map_entities.smoothing.excluded_count = 0; + for (int i = 0; i < g_map_entities.smoothing.groups_count; ++i) + g_map_entities.smoothing.groups[i].count = 0; + g_map_entities.smoothing.groups_count = 0; parseEntities( map->entities, false ); orientSpotlights(); From bdbfbef8a289be9bae3dc463cfc342e1253db87c Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 5 Sep 2023 13:06:00 -0400 Subject: [PATCH 7/7] vk: add commented out normal debugging code --- ref/vk/shaders/denoiser.comp | 8 +++++++- ref/vk/shaders/ray_primary.comp | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ref/vk/shaders/denoiser.comp b/ref/vk/shaders/denoiser.comp index 4e08d68c..4b143a9c 100644 --- a/ref/vk/shaders/denoiser.comp +++ b/ref/vk/shaders/denoiser.comp @@ -36,12 +36,14 @@ layout(set = 0, binding = 15, rgba16f) uniform image2D prev_temporal_diffuse; layout(set = 0, binding = 16, rgba16f) uniform image2D out_temporal_specular; layout(set = 0, binding = 17, rgba16f) uniform image2D prev_temporal_specular; + const int INDIRECT_SCALE = 2; //#define DEBUG_TEXTURE normals_gs //#define DEBUG_TEXTURE emissive //#define DEBUG_TEXTURE light_point_diffuse //#define DEBUG_NORMAL +//layout(set = 0, binding = 18, rgba8) uniform readonly image2D material_rmxx; // Blatantly copypasted from https://www.shadertoy.com/view/XsGfWV vec3 aces_tonemap(vec3 color){ @@ -202,7 +204,11 @@ void main() { vec3 geometry_normal, shading_normal; readNormals(pix, geometry_normal, shading_normal); //imageStore(out_dest, pix, vec4(.5 + geometry_normal * .5, 0.)); return; - imageStore(out_dest, pix, vec4(.5 + shading_normal * .5, 0.)); return; + //const vec4 mat_rmxx = imageLoad(material_rmxx, pix); + //imageStore(out_dest, pix, vec4((.5 + shading_normal * .5)*.8 + .2 * mat_rmxx.a , 0.)); return; + + vec3 normal = pix.x < res.x / 2 ? shading_normal : geometry_normal; + imageStore(out_dest, pix, vec4(.5 + normal * .5, 0.)); return; #endif /* const uint mi = uint(material_index); */ diff --git a/ref/vk/shaders/ray_primary.comp b/ref/vk/shaders/ray_primary.comp index ec2465cd..a4247b1b 100644 --- a/ref/vk/shaders/ray_primary.comp +++ b/ref/vk/shaders/ray_primary.comp @@ -8,6 +8,7 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; #include "ray_primary_common.glsl" #include "ray_primary_hit.glsl" +//#include "noise.glsl" #define RAY_PRIMARY_OUTPUTS(X) \ X(10, base_color_a, rgba8) \ @@ -97,8 +98,10 @@ void main() { } float L = ray.dist; - + //uint debug_geometry_index = 0; if (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionTriangleEXT) { + //debug_geometry_index = rayQueryGetIntersectionGeometryIndexEXT(rq, true); + //debug_geometry_index = rayQueryGetIntersectionPrimitiveIndexEXT(rq, true); primaryRayHit(rq, payload); L = rayQueryGetIntersectionTEXT(rq, true); } @@ -108,6 +111,7 @@ void main() { imageStore(out_position_t, pix, payload.hit_t); imageStore(out_base_color_a, pix, payload.base_color_a); imageStore(out_normals_gs, pix, payload.normals_gs); + //imageStore(out_material_rmxx, pix, vec4(payload.material_rmxx.rg, 0, uintToFloat01(xxhash32(debug_geometry_index)))); imageStore(out_material_rmxx, pix, payload.material_rmxx); imageStore(out_emissive, pix, payload.emissive); imageStore(out_geometry_prev_position, pix, payload.prev_pos_t);