Merge pull request #547 from w23/brush-smoothing
Smooth normals between selected brush surfaces if the angle between them is small enough. - [x] Per-vertex-per-surface smoothing functionality (differs from qrad, which is per-edge; this gives artifacts as (supposedly) we have higher than lightmap lighting resolution) - [x] Automatically select surfaces with less than X degrees between normals. - [x] Make this X threshold adjustable from <map>.bsp.patch - [ ] ~~Try not joining coplanar surfaces~~ -- doesn't seem to be affecting anything. - [ ] ~~Think about linking surfaces more. Should we link distant surfaces w/o direct edges to this one?~~ -- it seems that we should be fine for now. Per-surface+vertex vs per-edge smoothing has no clear winner, just different tradeoffs. - [ ] ~~Scale normals according to surfaces areas, so larger surfaces have more weight (experimental; may improve some artifacts)~~ -- also, non trivial to compute, and may not affect things too much. - [x] Patch: add explicit smoothing or no-smoothing for given surfaces. Fixes #139, supersedes #348
This commit is contained in:
commit
29508cd324
|
@ -36,11 +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 = 16, rgba16f) uniform image2D out_temporal_specular;
|
||||||
layout(set = 0, binding = 17, rgba16f) uniform image2D prev_temporal_specular;
|
layout(set = 0, binding = 17, rgba16f) uniform image2D prev_temporal_specular;
|
||||||
|
|
||||||
|
|
||||||
const int INDIRECT_SCALE = 2;
|
const int INDIRECT_SCALE = 2;
|
||||||
|
|
||||||
//#define DEBUG_TEXTURE normals_gs
|
//#define DEBUG_TEXTURE normals_gs
|
||||||
//#define DEBUG_TEXTURE emissive
|
//#define DEBUG_TEXTURE emissive
|
||||||
//#define DEBUG_TEXTURE light_point_diffuse
|
//#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
|
// Blatantly copypasted from https://www.shadertoy.com/view/XsGfWV
|
||||||
vec3 aces_tonemap(vec3 color){
|
vec3 aces_tonemap(vec3 color){
|
||||||
|
@ -197,11 +200,15 @@ void main() {
|
||||||
//imageStore(out_dest, pix, vec4(fract(imageLoad(position_t, pix).rgb/10.), 0.)); return;
|
//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;
|
//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;
|
vec3 geometry_normal, shading_normal;
|
||||||
readNormals(pix, 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 + 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
|
#endif
|
||||||
|
|
||||||
/* const uint mi = uint(material_index); */
|
/* const uint mi = uint(material_index); */
|
||||||
|
|
|
@ -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_common.glsl"
|
||||||
#include "ray_primary_hit.glsl"
|
#include "ray_primary_hit.glsl"
|
||||||
|
//#include "noise.glsl"
|
||||||
|
|
||||||
#define RAY_PRIMARY_OUTPUTS(X) \
|
#define RAY_PRIMARY_OUTPUTS(X) \
|
||||||
X(10, base_color_a, rgba8) \
|
X(10, base_color_a, rgba8) \
|
||||||
|
@ -97,8 +98,10 @@ void main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float L = ray.dist;
|
float L = ray.dist;
|
||||||
|
//uint debug_geometry_index = 0;
|
||||||
if (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionTriangleEXT) {
|
if (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionTriangleEXT) {
|
||||||
|
//debug_geometry_index = rayQueryGetIntersectionGeometryIndexEXT(rq, true);
|
||||||
|
//debug_geometry_index = rayQueryGetIntersectionPrimitiveIndexEXT(rq, true);
|
||||||
primaryRayHit(rq, payload);
|
primaryRayHit(rq, payload);
|
||||||
L = rayQueryGetIntersectionTEXT(rq, true);
|
L = rayQueryGetIntersectionTEXT(rq, true);
|
||||||
}
|
}
|
||||||
|
@ -108,6 +111,7 @@ void main() {
|
||||||
imageStore(out_position_t, pix, payload.hit_t);
|
imageStore(out_position_t, pix, payload.hit_t);
|
||||||
imageStore(out_base_color_a, pix, payload.base_color_a);
|
imageStore(out_base_color_a, pix, payload.base_color_a);
|
||||||
imageStore(out_normals_gs, pix, payload.normals_gs);
|
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_material_rmxx, pix, payload.material_rmxx);
|
||||||
imageStore(out_emissive, pix, payload.emissive);
|
imageStore(out_emissive, pix, payload.emissive);
|
||||||
imageStore(out_geometry_prev_position, pix, payload.prev_pos_t);
|
imageStore(out_geometry_prev_position, pix, payload.prev_pos_t);
|
||||||
|
|
|
@ -58,6 +58,21 @@ typedef struct {
|
||||||
int water_indices;
|
int water_indices;
|
||||||
} model_sizes_t;
|
} 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 {
|
static struct {
|
||||||
struct {
|
struct {
|
||||||
int total_vertices, total_indices;
|
int total_vertices, total_indices;
|
||||||
|
@ -76,6 +91,16 @@ static struct {
|
||||||
|
|
||||||
#define MAX_ANIMATED_TEXTURES 256
|
#define MAX_ANIMATED_TEXTURES 256
|
||||||
int updated_textures[MAX_ANIMATED_TEXTURES];
|
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;
|
} g_brush;
|
||||||
|
|
||||||
void VK_InitRandomTable( void )
|
void VK_InitRandomTable( void )
|
||||||
|
@ -107,8 +132,9 @@ qboolean VK_BrushInit( void )
|
||||||
return true;
|
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
|
// speed up sin calculations
|
||||||
|
@ -763,6 +789,218 @@ typedef struct {
|
||||||
uint16_t *out_indices;
|
uint16_t *out_indices;
|
||||||
} fill_geometries_args_t;
|
} 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 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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
const float dot = DotProduct(n1, n2);
|
||||||
|
DEBUG("Smoothing: dot(%d, %d) = %f (t=%f)", surf1, surf2, 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) {
|
||||||
|
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) {
|
static qboolean fillBrushSurfaces(fill_geometries_args_t args) {
|
||||||
int vertex_offset = 0;
|
int vertex_offset = 0;
|
||||||
int num_geometries = 0;
|
int num_geometries = 0;
|
||||||
|
@ -772,13 +1010,15 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) {
|
||||||
uint16_t *p_ind = args.out_indices;
|
uint16_t *p_ind = args.out_indices;
|
||||||
int index_offset = args.base_index_offset;
|
int index_offset = args.base_index_offset;
|
||||||
|
|
||||||
|
connectVertices(args.mod);
|
||||||
|
|
||||||
// Load sorted by gl_texturenum
|
// Load sorted by gl_texturenum
|
||||||
// TODO this does not make that much sense in vulkan (can sort later)
|
// 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 t = 0; t <= args.sizes.max_texture_id; ++t) {
|
||||||
for( int i = 0; i < args.mod->nummodelsurfaces; ++i) {
|
for( int i = 0; i < args.mod->nummodelsurfaces; ++i) {
|
||||||
const int surface_index = args.mod->firstmodelsurface + i;
|
const int surface_index = args.mod->firstmodelsurface + i;
|
||||||
msurface_t *surf = args.mod->surfaces + surface_index;
|
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;
|
vk_render_geometry_t *model_geometry = args.out_geometries + num_geometries;
|
||||||
const float sample_size = gEngine.Mod_SampleSizeForFace( surf );
|
const float sample_size = gEngine.Mod_SampleSizeForFace( surf );
|
||||||
int index_count = 0;
|
int index_count = 0;
|
||||||
|
@ -841,11 +1081,18 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) {
|
||||||
VectorCopy(surf->texinfo->vecs[0], tangent);
|
VectorCopy(surf->texinfo->vecs[0], tangent);
|
||||||
VectorNormalize(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++ )
|
for( int k = 0; k < surf->numedges; k++ )
|
||||||
{
|
{
|
||||||
const int iedge = args.mod->surfedges[surf->firstedge + k];
|
const int iedge_dir = args.mod->surfedges[surf->firstedge + k];
|
||||||
const medge_t *edge = args.mod->edges + (iedge >= 0 ? iedge : -iedge);
|
const int iedge = iedge_dir >= 0 ? iedge_dir : -iedge_dir;
|
||||||
const mvertex_t *in_vertex = args.mod->vertexes + (iedge >= 0 ? edge->v[0] : edge->v[1]);
|
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 = {
|
vk_vertex_t vertex = {
|
||||||
{in_vertex->position[0], in_vertex->position[1], in_vertex->position[2]},
|
{in_vertex->position[0], in_vertex->position[1], in_vertex->position[2]},
|
||||||
};
|
};
|
||||||
|
@ -902,9 +1149,10 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) {
|
||||||
vertex.lm_tc[1] = t;
|
vertex.lm_tc[1] = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( FBitSet( surf->flags, SURF_PLANEBACK ))
|
// Compute smoothed normal if needed
|
||||||
VectorNegate( surf->plane->normal, vertex.normal );
|
if (!getSmoothedNormalFor(args.mod, vertex_index, surface_index, vertex.normal)) {
|
||||||
else VectorCopy( surf->plane->normal, vertex.normal );
|
VectorCopy(surf_normal, vertex.normal);
|
||||||
|
}
|
||||||
|
|
||||||
VectorCopy(tangent, vertex.tangent);
|
VectorCopy(tangent, vertex.tangent);
|
||||||
|
|
||||||
|
@ -920,11 +1168,11 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) {
|
||||||
index_count += 3;
|
index_count += 3;
|
||||||
index_offset += 3;
|
index_offset += 3;
|
||||||
}
|
}
|
||||||
}
|
} // for surf->numedges
|
||||||
|
|
||||||
model_geometry->element_count = index_count;
|
model_geometry->element_count = index_count;
|
||||||
vertex_offset += surf->numedges;
|
vertex_offset += surf->numedges;
|
||||||
}
|
} // for mod->nummodelsurfaces
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(args.sizes.num_surfaces == num_geometries);
|
ASSERT(args.sizes.num_surfaces == num_geometries);
|
||||||
|
@ -1081,6 +1329,10 @@ void VK_BrushModelDestroyAll( void ) {
|
||||||
g_brush.stat.total_vertices = 0;
|
g_brush.stat.total_vertices = 0;
|
||||||
g_brush.stat.total_indices = 0;
|
g_brush.stat.total_indices = 0;
|
||||||
g_brush.models_count = 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) {
|
static rt_light_add_polygon_t loadPolyLight(const model_t *mod, const int surface_index, const msurface_t *surf, const vec3_t emissive) {
|
||||||
|
|
|
@ -518,7 +518,40 @@ 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);
|
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 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 ) {
|
static void parseEntities( char *string, qboolean is_patch ) {
|
||||||
|
@ -570,6 +603,18 @@ static void parseEntities( char *string, qboolean is_patch ) {
|
||||||
addPatchSurface( &values, have_fields );
|
addPatchSurface( &values, have_fields );
|
||||||
} else if (have_fields & Field__xvk_ent_id) {
|
} else if (have_fields & Field__xvk_ent_id) {
|
||||||
patchEntity( &values, have_fields );
|
patchEntity( &values, have_fields );
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_fields & Field__xvk_smoothing_group) {
|
||||||
|
addSmoothingGroup(&values);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -676,6 +721,11 @@ void XVK_ParseMapEntities( void ) {
|
||||||
g_map_entities.single_environment_index = NoEnvironmentLights;
|
g_map_entities.single_environment_index = NoEnvironmentLights;
|
||||||
g_map_entities.entity_count = 0;
|
g_map_entities.entity_count = 0;
|
||||||
g_map_entities.func_walls_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;
|
||||||
|
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 );
|
parseEntities( map->entities, false );
|
||||||
orientSpotlights();
|
orientSpotlights();
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
X(19, vec2_t, _xvk_tex_offset, Vec2) \
|
X(19, vec2_t, _xvk_tex_offset, Vec2) \
|
||||||
X(20, vec2_t, _xvk_tex_scale, Vec2) \
|
X(20, vec2_t, _xvk_tex_scale, Vec2) \
|
||||||
X(21, string, model, String) \
|
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
|
/* NOTE: not used
|
||||||
X(22, int, rendermode, Int) \
|
X(22, int, rendermode, Int) \
|
||||||
|
@ -109,6 +112,12 @@ typedef struct {
|
||||||
int index;
|
int index;
|
||||||
} xvk_mapent_ref_t;
|
} 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 {
|
typedef struct {
|
||||||
int num_lights;
|
int num_lights;
|
||||||
vk_light_entity_t lights[256];
|
vk_light_entity_t lights[256];
|
||||||
|
@ -128,6 +137,18 @@ typedef struct {
|
||||||
// TODO find out how to read this from the engine, or make its size dynamic
|
// TODO find out how to read this from the engine, or make its size dynamic
|
||||||
//#define MAX_MAP_ENTITIES 2048
|
//#define MAX_MAP_ENTITIES 2048
|
||||||
xvk_mapent_ref_t refs[MAX_MAP_ENTITIES];
|
xvk_mapent_ref_t refs[MAX_MAP_ENTITIES];
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float threshold;
|
||||||
|
|
||||||
|
#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;
|
} xvk_map_entities_t;
|
||||||
|
|
||||||
extern xvk_map_entities_t g_map_entities;
|
extern xvk_map_entities_t g_map_entities;
|
||||||
|
|
Loading…
Reference in New Issue