From 07f1bac938423a81e68de4326179eb3a529cf3a7 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 19 Sep 2023 13:05:12 -0400 Subject: [PATCH 01/16] vk: pass material reference explicitly in geom structure This is needed to enable advanced material patching, where the material can be picked up based on source type, render mode, surface number, etc etc and all of the above combined if needed. Previous scheme was picking up materials very late when all of this info has been already lost. --- ref/vk/NOTES.md | 14 ++++++++++++++ ref/vk/vk_brush.c | 22 ++++++++++++---------- ref/vk/vk_materials.c | 28 ++++++++++++++++------------ ref/vk/vk_materials.h | 14 ++++++++++---- ref/vk/vk_ray_internal.h | 4 ++-- ref/vk/vk_ray_model.c | 25 ++++++++++++------------- ref/vk/vk_render.c | 10 +++++----- ref/vk/vk_render.h | 15 ++++++--------- ref/vk/vk_rtx.h | 3 ++- ref/vk/vk_scene.c | 2 +- ref/vk/vk_sprite.c | 6 +++--- ref/vk/vk_studio.c | 6 +++--- ref/vk/vk_triapi.c | 2 +- 13 files changed, 87 insertions(+), 64 deletions(-) diff --git a/ref/vk/NOTES.md b/ref/vk/NOTES.md index e1029bef..8ed2cead 100644 --- a/ref/vk/NOTES.md +++ b/ref/vk/NOTES.md @@ -398,3 +398,17 @@ TODO: can we not have a BLAS/model for each submodel? Can it be per-model instea # 2023-07-30 - ~~R_DrawStudioModel is the main func for drawing studio model. Called from scene code for each studio entity, with everything current (RI and stuff) set up~~ - `R_StudioDrawModelInternal()` is the main one. It is where it splits into renderer-vs-game rendering functions. + +# 2023-09-19 E298 +## SURF_DRAWSKY +- (context: want to remove kXVkMaterialSky, #474) +- qrad: + - uses textue name "sky" or "SKY" to check `IsSky()`. `IsSky()` surfaces do not get patches and do not participate in radiosity. + - uses CONTENTS_SKY node flag to detect whether a ray has hit skybox and can contribute sky light. +- xash/gl: + - CONTENTS_SKY is not used in any meaningful way + - sets SURF_DRAWSKY for surfaces with "sky" texture. + - uses SURF_DRAWSKY: + - to build skychain, and then draw it in Quake mode (the other branch does a bunch of math, which seemingly isn't used for anything at all). + - for dynamic lighting: if sky ray has hit sky surface then sky is contributing light + diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index c2fa4ae0..11a1bfee 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -275,9 +275,10 @@ static void brushComputeWaterPolys( compute_water_polys_t args ) { // Render const int tex_id = args.warp->texinfo->texture->gl_texturenum; + const r_vk_material_ref_t material_ref = R_VkMaterialGetForTexture(tex_id); *args.dst_geometry = (vk_render_geometry_t){ - .texture = tex_id, // FIXME assert >= 0 - .material = kXVkMaterialRegular, + .material = material_ref, + .material_type_deprecated = kXVkMaterialRegular, .surf_deprecate = args.warp, .max_vertex = vertices, @@ -704,7 +705,7 @@ void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, float blend, co // TODO also it will break switching render type from TransColor to anyting else -- textures will be stuck at white for (int i = 0; i < bmodel->render_model.num_geometries; ++i) { vk_render_geometry_t *geom = bmodel->render_model.geometries + i; - geom->texture = tglob.whiteTexture; + geom->material = R_VkMaterialGetForTexture(tglob.whiteTexture); } } else { APROF_SCOPE_DECLARE_BEGIN(brush_update_textures, "brush: update animated textures"); @@ -719,10 +720,11 @@ void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, float blend, co // Optionally patch by texture_s pointer and run animations const struct texture_s *texture_override = patch_surface ? patch_surface->tex : NULL; const texture_t *t = R_TextureAnimation(ent, geom->surf_deprecate, texture_override); - const int new_texture = t->gl_texturenum; + const int tex_id = t->gl_texturenum; - if (new_texture >= 0 && new_texture != geom->texture) { - geom->texture = t->gl_texturenum; + const r_vk_material_ref_t new_material = R_VkMaterialGetForTexture(tex_id); + if (new_material.index >= 0 && new_material.index != geom->material.index) { + geom->material = new_material; if (updated_textures_count < MAX_ANIMATED_TEXTURES) { g_brush.updated_textures[updated_textures_count++] = bmodel->animated_indexes[i]; } @@ -732,7 +734,7 @@ void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, float blend, co // Add them as dynamic lights for now. It would probably be better if they were static lights (for worldmodel), // but there's no easy way to do it for now. vec3_t *emissive = &bmodel->render_model.geometries[geom_index].emissive; - if (RT_GetEmissiveForTexture(*emissive, geom->texture)) { + if (RT_GetEmissiveForTexture(*emissive, tex_id)) { rt_light_add_polygon_t polylight = loadPolyLight(mod, surface_index, geom->surf_deprecate, *emissive); polylight.dynamic = true; polylight.transform_row = (const matrix3x4*)&transform; @@ -1075,7 +1077,7 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { VectorClear(model_geometry->emissive); model_geometry->surf_deprecate = surf; - model_geometry->texture = tex_id; + model_geometry->material = R_VkMaterialGetForTexture(tex_id); model_geometry->vertex_offset = args.base_vertex_offset; model_geometry->max_vertex = vertex_offset + surf->numedges; @@ -1083,9 +1085,9 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { model_geometry->index_offset = index_offset; if(type == BrushSurface_Sky) { - model_geometry->material = kXVkMaterialSky; + model_geometry->material_type_deprecated = kXVkMaterialSky; } else { - model_geometry->material = kXVkMaterialRegular; + model_geometry->material_type_deprecated = kXVkMaterialRegular; ASSERT(!FBitSet( surf->flags, SURF_DRAWTILED )); VK_CreateSurfaceLightmap( surf, args.mod ); } diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index e73cdd84..f3de6d1e 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -11,7 +11,7 @@ #define MAX_INCLUDE_DEPTH 4 -static xvk_material_t k_default_material = { +static r_vk_material_t k_default_material = { .tex_base_color = -1, .tex_metalness = 0, .tex_roughness = 0, @@ -26,7 +26,7 @@ static xvk_material_t k_default_material = { }; static struct { - xvk_material_t materials[MAX_TEXTURES]; + r_vk_material_t materials[MAX_TEXTURES]; } g_materials; static struct { @@ -70,7 +70,7 @@ static void loadMaterialsFromFile( const char *filename, int depth ) { g_stats.material_file_read_duration_ns += aprof_time_now_ns() - load_file_begin_ns; char *pos = (char*)data; - xvk_material_t current_material = k_default_material; + r_vk_material_t current_material = k_default_material; int current_material_index = -1; qboolean force_reload = false; qboolean create = false; @@ -215,7 +215,7 @@ static void loadMaterialsFromFileF( const char *fmt, ... ) { loadMaterialsFromFile( buffer, MAX_INCLUDE_DEPTH ); } -void XVK_ReloadMaterials( void ) { +void R_VkMaterialsReload( void ) { memset(&g_stats, 0, sizeof(g_stats)); const uint64_t begin_time_ns = aprof_time_now_ns(); @@ -223,7 +223,7 @@ void XVK_ReloadMaterials( void ) { k_default_material.tex_roughness = tglob.whiteTexture; for (int i = 0; i < MAX_TEXTURES; ++i) { - xvk_material_t *const mat = g_materials.materials + i; + r_vk_material_t *const mat = g_materials.materials + i; const vk_texture_t *const tex = findTexture( i ); *mat = k_default_material; @@ -264,14 +264,18 @@ void XVK_ReloadMaterials( void ) { } } -xvk_material_t* XVK_GetMaterialForTextureIndex( int tex_index ) { - xvk_material_t *mat = NULL; +r_vk_material_ref_t R_VkMaterialGetForTexture( int tex_index ) { ASSERT(tex_index >= 0); ASSERT(tex_index < MAX_TEXTURES); - mat = g_materials.materials + tex_index; - if (mat->base_color >= 0) - return mat; - - return NULL; + // TODO add versioning to detect reloads? + return (r_vk_material_ref_t){ .index = tex_index, }; +} + +const r_vk_material_t* R_VkMaterialGet( r_vk_material_ref_t ref ) { + ASSERT(ref.index >= 0); + ASSERT(ref.index < MAX_TEXTURES); + + // TODO verify version ? + return g_materials.materials + ref.index; } diff --git a/ref/vk/vk_materials.h b/ref/vk/vk_materials.h index 3eec91c7..294c6010 100644 --- a/ref/vk/vk_materials.h +++ b/ref/vk/vk_materials.h @@ -2,7 +2,7 @@ #include "xash3d_types.h" -typedef struct { +typedef struct r_vk_material_s { int tex_base_color; int tex_roughness; int tex_metalness; @@ -14,8 +14,14 @@ typedef struct { float normal_scale; qboolean set; -} xvk_material_t; +} r_vk_material_t; -void XVK_ReloadMaterials( void ); +typedef struct { int index; } r_vk_material_ref_t; -xvk_material_t* XVK_GetMaterialForTextureIndex( int tex_index ); +// Note: invalidates all previously issued material refs +// TODO: track "version" in high bits? +void R_VkMaterialsReload( void ); + +r_vk_material_ref_t R_VkMaterialGetForTexture( int tex_id ); + +const r_vk_material_t* R_VkMaterialGet( r_vk_material_ref_t ref ); diff --git a/ref/vk/vk_ray_internal.h b/ref/vk/vk_ray_internal.h index decf0f51..e191b2cc 100644 --- a/ref/vk/vk_ray_internal.h +++ b/ref/vk/vk_ray_internal.h @@ -101,8 +101,8 @@ rt_kusochki_t RT_KusochkiAllocLong(int count); uint32_t RT_KusochkiAllocOnce(int count); void RT_KusochkiFree(const rt_kusochki_t*); -struct vk_render_geometry_s; -qboolean RT_KusochkiUpload(uint32_t kusochki_offset, const struct vk_render_geometry_s *geoms, int geoms_count, int override_texture_id, const vec4_t *override_color); +//struct vk_render_geometry_s; +//qboolean RT_KusochkiUpload(uint32_t kusochki_offset, const struct vk_render_geometry_s *geoms, int geoms_count, int override_texture_id, const vec4_t *override_color); qboolean RT_DynamicModelInit(void); void RT_DynamicModelShutdown(void); diff --git a/ref/vk/vk_ray_model.c b/ref/vk/vk_ray_model.c index 0de8855a..379c1d78 100644 --- a/ref/vk/vk_ray_model.c +++ b/ref/vk/vk_ray_model.c @@ -25,9 +25,8 @@ typedef struct rt_model_s { rt_kusochki_t kusochki; } rt_model_t; -static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometry_t *geom, int override_texture_id, const vec4_t override_color) { - const int tex_id = override_texture_id > 0 ? override_texture_id : geom->texture; - const xvk_material_t *const mat = XVK_GetMaterialForTextureIndex( tex_id ); +static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometry_t *geom, const r_vk_material_t *override_material, const vec4_t override_color) { + const r_vk_material_t *const mat = override_material ? override_material : R_VkMaterialGet(geom->material); ASSERT(mat); // TODO split kusochki into static geometry data and potentially dynamic material data @@ -59,12 +58,12 @@ static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometr } // TODO should be patched by the Chrome material source itself to generate a static chrome material - const qboolean HACK_chrome = geom->material == kXVkMaterialChrome; + const qboolean HACK_chrome = geom->material_type_deprecated == kXVkMaterialChrome; if (!mat->set && HACK_chrome) kusok->material.tex_roughness = tglob.grayTexture; // Purely static. Once a sky forever a sky. - if (geom->material == kXVkMaterialSky) + if (geom->material_type_deprecated == kXVkMaterialSky) kusok->material.tex_base_color = TEX_BASE_SKYBOX; } @@ -180,7 +179,7 @@ void RT_KusochkiFree(const rt_kusochki_t *kusochki) { } // TODO this function can't really fail. It'd mean that staging is completely broken. -qboolean RT_KusochkiUpload(uint32_t kusochki_offset, const struct vk_render_geometry_s *geoms, int geoms_count, int override_texture_id, const vec4_t *override_colors) { +qboolean RT_KusochkiUpload(uint32_t kusochki_offset, const struct vk_render_geometry_s *geoms, int geoms_count, const r_vk_material_t *override_material, const vec4_t *override_colors) { const vk_staging_buffer_args_t staging_args = { .buffer = g_ray_model_state.kusochki_buffer.buffer, .offset = kusochki_offset * sizeof(vk_kusok_data_t), @@ -197,7 +196,7 @@ qboolean RT_KusochkiUpload(uint32_t kusochki_offset, const struct vk_render_geom vk_kusok_data_t *const p = kusok_staging.ptr; for (int i = 0; i < geoms_count; ++i) { const vk_render_geometry_t *geom = geoms + i; - applyMaterialToKusok(p + i, geom, override_texture_id, override_colors ? override_colors[i] : NULL); + applyMaterialToKusok(p + i, geom, override_material, override_colors ? override_colors[i] : NULL); } R_VkStagingUnlock(kusok_staging.handle); @@ -222,7 +221,7 @@ struct rt_model_s *RT_ModelCreate(rt_model_create_t args) { goto fail; } - RT_KusochkiUpload(kusochki.offset, args.geometries, args.geometries_count, -1, NULL); + RT_KusochkiUpload(kusochki.offset, args.geometries, args.geometries_count, NULL, NULL); { rt_model_t *const ret = Mem_Malloc(vk_core.pool, sizeof(*ret)); @@ -275,7 +274,7 @@ qboolean RT_ModelUpdateMaterials(struct rt_model_s *model, const struct vk_rende const int offset = geom_indices[begin]; const int count = i - begin; ASSERT(offset + count <= geometries_count); - if (!RT_KusochkiUpload(model->kusochki.offset + offset, geometries + offset, count, -1, NULL)) { + if (!RT_KusochkiUpload(model->kusochki.offset + offset, geometries + offset, count, NULL, NULL)) { APROF_SCOPE_END(update_materials); return false; } @@ -288,7 +287,7 @@ qboolean RT_ModelUpdateMaterials(struct rt_model_s *model, const struct vk_rende const int offset = geom_indices[begin]; const int count = geom_indices_count - begin; ASSERT(offset + count <= geometries_count); - if (!RT_KusochkiUpload(model->kusochki.offset + offset, geometries + offset, count, -1, NULL)) { + if (!RT_KusochkiUpload(model->kusochki.offset + offset, geometries + offset, count, NULL, NULL)) { APROF_SCOPE_END(update_materials); return false; @@ -314,12 +313,12 @@ void RT_FrameAddModel( struct rt_model_s *model, rt_frame_add_model_t args ) { uint32_t kusochki_offset = model->kusochki.offset; - if (args.override.textures > 0) { + if (args.override.material != NULL) { kusochki_offset = RT_KusochkiAllocOnce(args.override.geoms_count); if (kusochki_offset == ALO_ALLOC_FAILED) return; - if (!RT_KusochkiUpload(kusochki_offset, args.override.geoms, args.override.geoms_count, args.override.textures, NULL)) { + if (!RT_KusochkiUpload(kusochki_offset, args.override.geoms, args.override.geoms_count, args.override.material, NULL)) { gEngine.Con_Printf(S_ERROR "Couldn't upload kusochki for instanced model\n"); return; } @@ -413,7 +412,7 @@ void RT_DynamicModelProcessFrame(void) { } // FIXME override color - if (!RT_KusochkiUpload(kusochki_offset, dyn->geometries, dyn->geometries_count, -1, dyn->colors)) { + if (!RT_KusochkiUpload(kusochki_offset, dyn->geometries, dyn->geometries_count, NULL, dyn->colors)) { gEngine.Con_Printf(S_ERROR "Couldn't build blas for %d geoms of %s, skipping\n", dyn->geometries_count, group_names[i]); goto tail; } diff --git a/ref/vk/vk_render.c b/ref/vk/vk_render.c index 22683e40..ec01d265 100644 --- a/ref/vk/vk_render.c +++ b/ref/vk/vk_render.c @@ -723,7 +723,8 @@ static void submitToTraditionalRender( trad_submit_t args ) { for (int i = 0; i < args.geometries_count; ++i) { const vk_render_geometry_t *geom = args.geometries + i; - const int tex = args.textures_override > 0 ? args.textures_override : geom->texture; + const r_vk_material_t *material = R_VkMaterialGet(geom->material); + const int tex = args.textures_override > 0 ? args.textures_override : material->tex_base_color; const qboolean split = current_texture != tex || vertex_offset != geom->vertex_offset || (index_offset + element_count) != geom->index_offset; @@ -788,7 +789,7 @@ void R_RenderModelDraw(const vk_render_model_t *model, r_model_draw_t args) { .dynamic_polylights = model->dynamic_polylights, .dynamic_polylights_count = model->dynamic_polylights_count, .override = { - .textures = args.textures_override, + .material = args.material_override, .geoms = model->geometries, .geoms_count = model->num_geometries, }, @@ -802,7 +803,7 @@ void R_RenderModelDraw(const vk_render_model_t *model, r_model_draw_t args) { .transform = args.transform, .color = args.color, .render_type = args.render_type, - .textures_override = args.textures_override + .textures_override = args.material_override ? args.material_override->tex_base_color : -1, }); } } @@ -820,8 +821,7 @@ void R_RenderDrawOnce(r_draw_once_t args) { R_GeometryBufferUnlock( &buffer ); const vk_render_geometry_t geometry = { - .texture = args.texture, - .material = kXVkMaterialRegular, + .material = args.material, .material_type_deprecated = kXVkMaterialRegular, .max_vertex = args.vertices_count, .vertex_offset = buffer.vertices.unit_offset, diff --git a/ref/vk/vk_render.h b/ref/vk/vk_render.h index 24dc2779..d6eeccc1 100644 --- a/ref/vk/vk_render.h +++ b/ref/vk/vk_render.h @@ -1,4 +1,5 @@ #pragma once +#include "vk_materials.h" #include "vk_common.h" #include "vk_const.h" #include "vk_core.h" @@ -27,7 +28,7 @@ typedef enum { // Used: ray tracing sets gray roughness texture to get smooth surface look. // Remove: Have an explicit material for chrome surfaces. kXVkMaterialChrome, -} XVkMaterialType; +} XVkMaterialType_Deprecated; typedef struct vk_render_geometry_s { int index_offset, vertex_offset; @@ -42,11 +43,9 @@ typedef struct vk_render_geometry_s { // Remove: have an explicit list of surfaces with animated textures const struct msurface_s *surf_deprecate; - // Animated textures will be dynamic and change between frames - int texture; - // If this geometry is special, it will have a material type override - XVkMaterialType material; + XVkMaterialType_Deprecated material_type_deprecated; + r_vk_material_ref_t material; // for kXVkMaterialEmissive{,Glow} and others vec3_t emissive; @@ -145,9 +144,7 @@ typedef struct { const vec4_t *color; const matrix4x4 *transform, *prev_transform; - // Global texture override if > 0 - // Used by sprite+quad instancing - int textures_override; + const r_vk_material_t* material_override; } r_model_draw_t; void R_RenderModelDraw(const vk_render_model_t *model, r_model_draw_t args); @@ -159,7 +156,7 @@ typedef struct { int vertices_count, indices_count; int render_type; - int texture; + r_vk_material_ref_t material; const vec4_t *emissive; const vec4_t *color; } r_draw_once_t; diff --git a/ref/vk/vk_rtx.h b/ref/vk/vk_rtx.h index 35798ec4..5bf2d2af 100644 --- a/ref/vk/vk_rtx.h +++ b/ref/vk/vk_rtx.h @@ -34,6 +34,7 @@ void VK_RayShutdown( void ); struct vk_render_geometry_s; struct rt_model_s; +struct r_vk_material_s; typedef enum { kBlasBuildStatic, // builds slow for fast trace @@ -63,7 +64,7 @@ typedef struct { int dynamic_polylights_count; struct { - int textures; // Override kusochki/material textures if > 0 + const struct r_vk_material_s *material; // These are needed in order to recreate kusochki geometry data // TODO remove when material data is split from kusochki diff --git a/ref/vk/vk_scene.c b/ref/vk/vk_scene.c index 5477b61d..2bad00b2 100644 --- a/ref/vk/vk_scene.c +++ b/ref/vk/vk_scene.c @@ -148,7 +148,7 @@ static void loadMap(const model_t* const map) { XVK_ParseMapEntities(); // Load PBR materials (depends on wadlist from parsed map entities) - XVK_ReloadMaterials(); + R_VkMaterialsReload(); // Parse patch data // Depends on loaded materials. Must preceed loading brush models. diff --git a/ref/vk/vk_sprite.c b/ref/vk/vk_sprite.c index 2c598e6d..7df2de2e 100644 --- a/ref/vk/vk_sprite.c +++ b/ref/vk/vk_sprite.c @@ -103,8 +103,8 @@ static qboolean createQuadModel(void) { .element_count = 6, .index_offset = g_sprite.quad.geom.indices.unit_offset, - .material = kXVkMaterialRegular, - .texture = tglob.defaultTexture, + .material_type_deprecated = kXVkMaterialRegular, + .material = R_VkMaterialGetForTexture(tglob.defaultTexture), .emissive = {1,1,1}, }; @@ -804,7 +804,7 @@ static void R_DrawSpriteQuad( const char *debug_name, const mspriteframe_t *fram .color = (const vec4_t*)color, .transform = &transform, .prev_transform = &transform, - .textures_override = texture, + .material_override = R_VkMaterialGet(R_VkMaterialGetForTexture(texture)), }); } diff --git a/ref/vk/vk_studio.c b/ref/vk/vk_studio.c index 5de6c270..7326bcd8 100644 --- a/ref/vk/vk_studio.c +++ b/ref/vk/vk_studio.c @@ -1770,8 +1770,8 @@ static void buildSubmodelMeshGeometry( build_submodel_mesh_t args ) { ASSERT(vertex_offset == num_vertices); *args.out_geometry = (vk_render_geometry_t){ - .texture = args.texture, - .material = FBitSet( args.face_flags, STUDIO_NF_CHROME ) ? kXVkMaterialChrome : kXVkMaterialRegular, + .material = R_VkMaterialGetForTexture(args.texture), + .material_type_deprecated = FBitSet( args.face_flags, STUDIO_NF_CHROME ) ? kXVkMaterialChrome : kXVkMaterialRegular, .vertex_offset = args.vertices_offset, .max_vertex = num_vertices, @@ -2304,7 +2304,7 @@ static void R_StudioDrawPoints( void ) { .color = &color, .transform = &g_studio_current.entmodel->transform, .prev_transform = &g_studio_current.entmodel->prev_transform, - .textures_override = -1, + .material_override = NULL, }); ++g_studio_stats.submodels_total; diff --git a/ref/vk/vk_triapi.c b/ref/vk/vk_triapi.c index eab504e9..656914ac 100644 --- a/ref/vk/vk_triapi.c +++ b/ref/vk/vk_triapi.c @@ -170,7 +170,7 @@ void TriEndEx( const vec4_t color, const char* name ) { .vertices_count = g_triapi.num_vertices, .indices_count = num_indices, .render_type = g_triapi.render_type, - .texture = g_triapi.texture_index, + .material = R_VkMaterialGetForTexture(g_triapi.texture_index), .emissive = (const vec4_t*)color, .color = (const vec4_t*)color, }); From cd4014766bc8d18ed965c28746e464e3d324a215 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 19 Sep 2023 13:20:04 -0400 Subject: [PATCH 02/16] vk: explicitly pass old original texture for trad renderer Fixes #571 --- ref/vk/vk_brush.c | 16 +++++++++++----- ref/vk/vk_render.c | 7 ++++--- ref/vk/vk_render.h | 4 ++++ ref/vk/vk_sprite.c | 1 + ref/vk/vk_studio.c | 1 + ref/vk/vk_triapi.c | 1 + 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 11a1bfee..bde79110 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -279,6 +279,9 @@ static void brushComputeWaterPolys( compute_water_polys_t args ) { *args.dst_geometry = (vk_render_geometry_t){ .material = material_ref, .material_type_deprecated = kXVkMaterialRegular, + + .ye_olde_texture = tex_id, + .surf_deprecate = args.warp, .max_vertex = vertices, @@ -706,6 +709,7 @@ void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, float blend, co for (int i = 0; i < bmodel->render_model.num_geometries; ++i) { vk_render_geometry_t *geom = bmodel->render_model.geometries + i; geom->material = R_VkMaterialGetForTexture(tglob.whiteTexture); + geom->ye_olde_texture = tglob.whiteTexture; } } else { APROF_SCOPE_DECLARE_BEGIN(brush_update_textures, "brush: update animated textures"); @@ -720,11 +724,11 @@ void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, float blend, co // Optionally patch by texture_s pointer and run animations const struct texture_s *texture_override = patch_surface ? patch_surface->tex : NULL; const texture_t *t = R_TextureAnimation(ent, geom->surf_deprecate, texture_override); - const int tex_id = t->gl_texturenum; + const int new_tex_id = t->gl_texturenum; - const r_vk_material_ref_t new_material = R_VkMaterialGetForTexture(tex_id); - if (new_material.index >= 0 && new_material.index != geom->material.index) { - geom->material = new_material; + if (new_tex_id >= 0 && new_tex_id != geom->ye_olde_texture) { + geom->ye_olde_texture = new_tex_id; + geom->material = R_VkMaterialGetForTexture(new_tex_id); if (updated_textures_count < MAX_ANIMATED_TEXTURES) { g_brush.updated_textures[updated_textures_count++] = bmodel->animated_indexes[i]; } @@ -734,7 +738,7 @@ void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, float blend, co // Add them as dynamic lights for now. It would probably be better if they were static lights (for worldmodel), // but there's no easy way to do it for now. vec3_t *emissive = &bmodel->render_model.geometries[geom_index].emissive; - if (RT_GetEmissiveForTexture(*emissive, tex_id)) { + if (RT_GetEmissiveForTexture(*emissive, new_tex_id)) { rt_light_add_polygon_t polylight = loadPolyLight(mod, surface_index, geom->surf_deprecate, *emissive); polylight.dynamic = true; polylight.transform_row = (const matrix3x4*)&transform; @@ -1041,6 +1045,7 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { const float sample_size = gEngine.Mod_SampleSizeForFace( surf ); int index_count = 0; vec3_t tangent; + const int orig_tex_id = surf->texinfo->texture->gl_texturenum; int tex_id = surf->texinfo->texture->gl_texturenum; const xvk_patch_surface_t *const psurf = R_VkPatchGetSurface(surface_index); @@ -1078,6 +1083,7 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { model_geometry->surf_deprecate = surf; model_geometry->material = R_VkMaterialGetForTexture(tex_id); + model_geometry->ye_olde_texture = orig_tex_id; model_geometry->vertex_offset = args.base_vertex_offset; model_geometry->max_vertex = vertex_offset + surf->numedges; diff --git a/ref/vk/vk_render.c b/ref/vk/vk_render.c index ec01d265..0654d47b 100644 --- a/ref/vk/vk_render.c +++ b/ref/vk/vk_render.c @@ -723,8 +723,7 @@ static void submitToTraditionalRender( trad_submit_t args ) { for (int i = 0; i < args.geometries_count; ++i) { const vk_render_geometry_t *geom = args.geometries + i; - const r_vk_material_t *material = R_VkMaterialGet(geom->material); - const int tex = args.textures_override > 0 ? args.textures_override : material->tex_base_color; + const int tex = args.textures_override > 0 ? args.textures_override : geom->ye_olde_texture; const qboolean split = current_texture != tex || vertex_offset != geom->vertex_offset || (index_offset + element_count) != geom->index_offset; @@ -821,7 +820,9 @@ void R_RenderDrawOnce(r_draw_once_t args) { R_GeometryBufferUnlock( &buffer ); const vk_render_geometry_t geometry = { - .material = args.material, .material_type_deprecated = kXVkMaterialRegular, + .material = args.material, + .material_type_deprecated = kXVkMaterialRegular, + .ye_olde_texture = args.ye_olde_texture, .max_vertex = args.vertices_count, .vertex_offset = buffer.vertices.unit_offset, diff --git a/ref/vk/vk_render.h b/ref/vk/vk_render.h index d6eeccc1..6c23ab4a 100644 --- a/ref/vk/vk_render.h +++ b/ref/vk/vk_render.h @@ -47,6 +47,9 @@ typedef struct vk_render_geometry_s { XVkMaterialType_Deprecated material_type_deprecated; r_vk_material_ref_t material; + // Olde unpatched texture used for traditional renderer + int ye_olde_texture; + // for kXVkMaterialEmissive{,Glow} and others vec3_t emissive; } vk_render_geometry_t; @@ -157,6 +160,7 @@ typedef struct { int render_type; r_vk_material_ref_t material; + int ye_olde_texture; const vec4_t *emissive; const vec4_t *color; } r_draw_once_t; diff --git a/ref/vk/vk_sprite.c b/ref/vk/vk_sprite.c index 7df2de2e..6ad35ce9 100644 --- a/ref/vk/vk_sprite.c +++ b/ref/vk/vk_sprite.c @@ -105,6 +105,7 @@ static qboolean createQuadModel(void) { .material_type_deprecated = kXVkMaterialRegular, .material = R_VkMaterialGetForTexture(tglob.defaultTexture), + .ye_olde_texture = tglob.defaultTexture, .emissive = {1,1,1}, }; diff --git a/ref/vk/vk_studio.c b/ref/vk/vk_studio.c index 7326bcd8..afa7071f 100644 --- a/ref/vk/vk_studio.c +++ b/ref/vk/vk_studio.c @@ -1771,6 +1771,7 @@ static void buildSubmodelMeshGeometry( build_submodel_mesh_t args ) { *args.out_geometry = (vk_render_geometry_t){ .material = R_VkMaterialGetForTexture(args.texture), + .ye_olde_texture = args.texture, .material_type_deprecated = FBitSet( args.face_flags, STUDIO_NF_CHROME ) ? kXVkMaterialChrome : kXVkMaterialRegular, .vertex_offset = args.vertices_offset, diff --git a/ref/vk/vk_triapi.c b/ref/vk/vk_triapi.c index 656914ac..9d6b9fed 100644 --- a/ref/vk/vk_triapi.c +++ b/ref/vk/vk_triapi.c @@ -171,6 +171,7 @@ void TriEndEx( const vec4_t color, const char* name ) { .indices_count = num_indices, .render_type = g_triapi.render_type, .material = R_VkMaterialGetForTexture(g_triapi.texture_index), + .ye_olde_texture = g_triapi.texture_index, .emissive = (const vec4_t*)color, .color = (const vec4_t*)color, }); From dce05989626e3a5f7b04b83ecbf3ae1b9f89397e Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Thu, 21 Sep 2023 12:53:17 -0400 Subject: [PATCH 03/16] vk: remove special SURF_DRAWSKY handling 1. Completely remove them from geometries 2. Draw skybox where the ray hasn't hit anything 3. In the same fashion sky is not shadowed when there's nothing hit by the shadow ray. This allows completely removing the sky material flag. Also supposedly fixes #474 --- ref/vk/shaders/bounce.comp | 10 ++++++++-- ref/vk/shaders/light_common.glsl | 18 ++++++------------ ref/vk/shaders/ray_common.glsl | 1 - ref/vk/shaders/ray_interop.h | 2 -- ref/vk/shaders/ray_primary.comp | 6 +++++- ref/vk/shaders/ray_primary.rchit | 5 +---- ref/vk/shaders/ray_primary.rmiss | 12 +++++++++++- ref/vk/shaders/ray_primary_hit.glsl | 6 +----- ref/vk/shaders/ray_shadow.rchit | 7 +------ ref/vk/shaders/ray_shadow_interface.glsl | 1 - ref/vk/vk_brush.c | 8 +++----- ref/vk/vk_ray_model.c | 4 ---- ref/vk/vk_render.h | 5 ----- 13 files changed, 36 insertions(+), 49 deletions(-) diff --git a/ref/vk/shaders/bounce.comp b/ref/vk/shaders/bounce.comp index 094f24b0..9a43c8a1 100644 --- a/ref/vk/shaders/bounce.comp +++ b/ref/vk/shaders/bounce.comp @@ -20,6 +20,7 @@ RAY_LIGHT_DIRECT_INPUTS(X) layout(set=0, binding=20, rgba16f) uniform writeonly image2D out_indirect_diffuse; layout(set=0, binding=21, rgba16f) uniform writeonly image2D out_indirect_specular; +layout(set=0, binding=27) uniform samplerCube skybox; #include "ray_primary_common.glsl" #include "ray_primary_hit.glsl" @@ -72,7 +73,6 @@ bool getHit(vec3 origin, vec3 direction, inout RayPayloadPrimary payload) { // 2. Same as the above, but also with a completely independent TLAS. Why: no need to mask-check geometry for opaque-vs-alpha const MiniGeometry geom = readCandidateMiniGeometry(rq); const uint tex_base_color = getKusok(geom.kusok_index).material.tex_base_color; - // Should never happen: skybox is opaque if (tex_base_color == TEX_BASE_SKYBOX) const vec4 texture_color = texture(textures[nonuniformEXT(tex_base_color)], geom.uv); const float alpha_mask_threshold = .1f; @@ -149,8 +149,14 @@ void computeBounce(ivec2 pix, vec3 direction, out vec3 diffuse, out vec3 specula payload.base_color_a = vec4(0.); payload.emissive = vec4(0.); const vec3 pos = imageLoad(position_t, pix).xyz + geometry_normal * ray_normal_fudge; - if (!getHit(pos, bounce_direction, payload)) + if (!getHit(pos, bounce_direction, payload)) { + const vec3 skycolor = SRGBtoLINEAR(texture(skybox, bounce_direction).rgb); + if (brdf_type == DIFFUSE_TYPE) { + diffuse += skycolor; + } else + specular += skycolor; return; + } throughput *= payload.base_color_a.rgb; diff --git a/ref/vk/shaders/light_common.glsl b/ref/vk/shaders/light_common.glsl index 01db0564..fc6b9591 100644 --- a/ref/vk/shaders/light_common.glsl +++ b/ref/vk/shaders/light_common.glsl @@ -117,7 +117,7 @@ bool shadowedSky(vec3 pos, vec3 dir) { | gl_RayFlagsSkipClosestHitShaderEXT ; const uint hit_type = traceShadowRay(pos, dir, dist, flags); - return payload_shadow.hit_type != SHADOW_SKY; + return payload_shadow.hit_type != SHADOW_MISS; #elif defined(RAY_QUERY) rayQueryEXT rq; @@ -125,28 +125,22 @@ bool shadowedSky(vec3 pos, vec3 dir) { // Culling for shadows breaks more things (e.g. de_cbble slightly off the ground boxes) than it probably fixes. Keep it turned off. //| gl_RayFlagsCullFrontFacingTrianglesEXT | gl_RayFlagsOpaqueEXT - //| gl_RayFlagsTerminateOnFirstHitEXT + | gl_RayFlagsTerminateOnFirstHitEXT //| gl_RayFlagsSkipClosestHitShaderEXT ; const float L = 10000.; // TODO Why 10k? rayQueryInitializeEXT(rq, tlas, flags, GEOMETRY_BIT_OPAQUE, pos, 0., dir, L); - // Find closest intersection, and then check whether that was a skybox + // Any intersection results in a shadow while (rayQueryProceedEXT(rq)) {} if (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionTriangleEXT) { - const uint instance_kusochki_offset = rayQueryGetIntersectionInstanceCustomIndexEXT(rq, true); - const uint geometry_index = rayQueryGetIntersectionGeometryIndexEXT(rq, true); - const uint kusok_index = instance_kusochki_offset + geometry_index; - const Kusok kusok = getKusok(kusok_index); - - if (kusok.material.tex_base_color != TEX_BASE_SKYBOX) - return true; + return true; } // check for alpha-masked surfaces separately - const float hit_t = rayQueryGetIntersectionTEXT(rq, true); - return shadowTestAlphaMask(pos, dir, hit_t); + // TODO compare with doing it in the same loop as above + return shadowTestAlphaMask(pos, dir, L); #else #error RAY_TRACE or RAY_QUERY diff --git a/ref/vk/shaders/ray_common.glsl b/ref/vk/shaders/ray_common.glsl index bf9ad41f..b48590d7 100644 --- a/ref/vk/shaders/ray_common.glsl +++ b/ref/vk/shaders/ray_common.glsl @@ -23,7 +23,6 @@ struct RayPayloadOpaque { #define SHADOW_MISS 0 #define SHADOW_HIT 1 -#define SHADOW_SKY 2 struct RayPayloadShadow { uint hit_type; diff --git a/ref/vk/shaders/ray_interop.h b/ref/vk/shaders/ray_interop.h index 5124b260..fefbd40c 100644 --- a/ref/vk/shaders/ray_interop.h +++ b/ref/vk/shaders/ray_interop.h @@ -71,8 +71,6 @@ LIST_SPECIALIZATION_CONSTANTS(DECLARE_SPECIALIZATION_CONSTANT) #define MATERIAL_MODE_BLEND_GLOW 5 #define MATERIAL_MODE_COUNT 6 -#define TEX_BASE_SKYBOX 0xffffffffu - struct Material { uint tex_base_color; diff --git a/ref/vk/shaders/ray_primary.comp b/ref/vk/shaders/ray_primary.comp index a4247b1b..94d4fda3 100644 --- a/ref/vk/shaders/ray_primary.comp +++ b/ref/vk/shaders/ray_primary.comp @@ -24,6 +24,8 @@ RAY_PRIMARY_OUTPUTS(X) layout(set = 0, binding = 1) uniform accelerationStructureEXT tlas; +layout(set = 0, binding = 7) uniform samplerCube skybox; + #include "trace_simple_blending.glsl" struct Ray { @@ -88,7 +90,6 @@ void main() { // 2. Same as the above, but also with a completely independent TLAS. Why: no need to mask-check geometry for opaque-vs-alpha const MiniGeometry geom = readCandidateMiniGeometry(rq); const uint tex_base_color = getKusok(geom.kusok_index).material.tex_base_color; - // Should never happen: skybox is opaque if (tex_base_color == TEX_BASE_SKYBOX) const vec4 texture_color = texture(textures[nonuniformEXT(tex_base_color)], geom.uv); const float alpha_mask_threshold = .1f; @@ -104,6 +105,9 @@ void main() { //debug_geometry_index = rayQueryGetIntersectionPrimitiveIndexEXT(rq, true); primaryRayHit(rq, payload); L = rayQueryGetIntersectionTEXT(rq, true); + } else { + payload.hit_t = payload.prev_pos_t = vec4(ray.origin + ray.direction * ray.dist, ray.dist); + payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, ray.direction).rgb); } traceSimpleBlending(ray.origin, ray.direction, L, payload.emissive.rgb, payload.base_color_a.rgb); diff --git a/ref/vk/shaders/ray_primary.rchit b/ref/vk/shaders/ray_primary.rchit index d52a9e53..54826a94 100644 --- a/ref/vk/shaders/ray_primary.rchit +++ b/ref/vk/shaders/ray_primary.rchit @@ -29,10 +29,7 @@ void main() { const Kusok kusok = getKusok(geom.kusok_index); - if (kusok.material.tex_base_color == TEX_BASE_SKYBOX) { - payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, gl_WorldRayDirectionEXT).rgb); - return; - } else { + { const vec4 color = getModelHeader(gl_InstanceID).color * kusok.material.base_color; payload.base_color_a = sampleTexture(kusok.material.tex_base_color, geom.uv, geom.uv_lods) * color; payload.material_rmxx.r = sampleTexture(kusok.material.tex_roughness, geom.uv, geom.uv_lods).r * kusok.material.roughness; diff --git a/ref/vk/shaders/ray_primary.rmiss b/ref/vk/shaders/ray_primary.rmiss index 4b072ab7..134e1d39 100644 --- a/ref/vk/shaders/ray_primary.rmiss +++ b/ref/vk/shaders/ray_primary.rmiss @@ -1,4 +1,14 @@ #version 460 core #extension GL_EXT_ray_tracing: require -void main() {} +#include "ray_primary_common.glsl" +#include "color_spaces.glsl" + +layout(set = 0, binding = 7) uniform samplerCube skybox; + +layout(location = PAYLOAD_LOCATION_PRIMARY) rayPayloadEXT RayPayloadPrimary payload; + +void main() { + // TODO payload.prev_pos_t = payload.hit_t = vec4(geom.pos, gl_HitTEXT); + payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, gl_WorldRayDirectionEXT).rgb); +} diff --git a/ref/vk/shaders/ray_primary_hit.glsl b/ref/vk/shaders/ray_primary_hit.glsl index 1a115042..8d6c38d8 100644 --- a/ref/vk/shaders/ray_primary_hit.glsl +++ b/ref/vk/shaders/ray_primary_hit.glsl @@ -10,7 +10,6 @@ layout(set = 0, binding = 6) uniform sampler2D textures[MAX_TEXTURES]; layout(set = 0, binding = 2) uniform UBO { UniformBuffer ubo; } ubo; -layout(set = 0, binding = 7) uniform samplerCube skybox; vec4 sampleTexture(uint tex_index, vec2 uv, vec4 uv_lods) { #ifndef RAY_BOUNCE @@ -30,10 +29,7 @@ void primaryRayHit(rayQueryEXT rq, inout RayPayloadPrimary payload) { const Kusok kusok = getKusok(geom.kusok_index); const Material material = kusok.material; - if (kusok.material.tex_base_color == TEX_BASE_SKYBOX) { - payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, rayDirection).rgb); - return; - } else { + { payload.base_color_a = sampleTexture(material.tex_base_color, geom.uv, geom.uv_lods); payload.material_rmxx.r = sampleTexture(material.tex_roughness, geom.uv, geom.uv_lods).r * material.roughness; payload.material_rmxx.g = sampleTexture(material.tex_metalness, geom.uv, geom.uv_lods).r * material.metalness; diff --git a/ref/vk/shaders/ray_shadow.rchit b/ref/vk/shaders/ray_shadow.rchit index b63f7208..f6b1f70b 100644 --- a/ref/vk/shaders/ray_shadow.rchit +++ b/ref/vk/shaders/ray_shadow.rchit @@ -7,10 +7,5 @@ layout(location = PAYLOAD_LOCATION_SHADOW) rayPayloadInEXT RayPayloadShadow payload_shadow; void main() { - const int instance_kusochki_offset = gl_InstanceCustomIndexEXT; - const int kusok_index = instance_kusochki_offset + gl_GeometryIndexEXT; - const Kusok kusok = getKusok(kusok_index); - const uint tex_base_color = kusok.material.tex_base_color; - - payload_shadow.hit_type = (kusok.material.tex_base_color != TEX_BASE_SKYBOX) ? SHADOW_HIT : SHADOW_SKY ; + payload_shadow.hit_type = SHADOW_HIT; } diff --git a/ref/vk/shaders/ray_shadow_interface.glsl b/ref/vk/shaders/ray_shadow_interface.glsl index 73d83be7..9b386926 100644 --- a/ref/vk/shaders/ray_shadow_interface.glsl +++ b/ref/vk/shaders/ray_shadow_interface.glsl @@ -3,7 +3,6 @@ #define SHADOW_MISS 0 #define SHADOW_HIT 1 -#define SHADOW_SKY 2 struct RayPayloadShadow { uint hit_type; diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index bde79110..75c2c665 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -779,13 +779,13 @@ static model_sizes_t computeSizes( const model_t *mod ) { case BrushSurface_Water: sizes.water_surfaces++; addWarpVertIndCounts(surf, &sizes.water_vertices, &sizes.water_indices); + case BrushSurface_Sky: case BrushSurface_Hidden: continue; case BrushSurface_Animated: sizes.animated_count++; case BrushSurface_Regular: - case BrushSurface_Sky: break; } @@ -1059,11 +1059,11 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { switch (type) { case BrushSurface_Water: case BrushSurface_Hidden: + case BrushSurface_Sky: continue; case BrushSurface_Animated: args.bmodel->animated_indexes[animated_count++] = num_geometries; case BrushSurface_Regular: - case BrushSurface_Sky: break; } @@ -1090,9 +1090,7 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { model_geometry->index_offset = index_offset; - if(type == BrushSurface_Sky) { - model_geometry->material_type_deprecated = kXVkMaterialSky; - } else { + { model_geometry->material_type_deprecated = kXVkMaterialRegular; ASSERT(!FBitSet( surf->flags, SURF_DRAWTILED )); VK_CreateSurfaceLightmap( surf, args.mod ); diff --git a/ref/vk/vk_ray_model.c b/ref/vk/vk_ray_model.c index 379c1d78..9543c03d 100644 --- a/ref/vk/vk_ray_model.c +++ b/ref/vk/vk_ray_model.c @@ -61,10 +61,6 @@ static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometr const qboolean HACK_chrome = geom->material_type_deprecated == kXVkMaterialChrome; if (!mat->set && HACK_chrome) kusok->material.tex_roughness = tglob.grayTexture; - - // Purely static. Once a sky forever a sky. - if (geom->material_type_deprecated == kXVkMaterialSky) - kusok->material.tex_base_color = TEX_BASE_SKYBOX; } // TODO utilize uploadKusochki([1]) to avoid 2 copies of staging code diff --git a/ref/vk/vk_render.h b/ref/vk/vk_render.h index 6c23ab4a..3e9a2dc2 100644 --- a/ref/vk/vk_render.h +++ b/ref/vk/vk_render.h @@ -19,11 +19,6 @@ void VK_RenderSetupCamera( const struct ref_viewpass_s *rvp ); typedef enum { kXVkMaterialRegular = 0, - // Set for SURF_DRAWSKY surfaces in vk_brush.c. - // Used: for setting TEX_BASE_SKYBOX for skybox texture sampling and environment shadows. - // Remove: pass it as a special texture/material index (e.g. -2). - kXVkMaterialSky, - // Set for chrome studio submodels. // Used: ray tracing sets gray roughness texture to get smooth surface look. // Remove: Have an explicit material for chrome surfaces. From 638bd163aff6c1c1394133c28e8adf555f10f12c Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Fri, 22 Sep 2023 09:43:27 -0400 Subject: [PATCH 04/16] vk: remove chrome material type; patch roughness manually instead Also, material struct is now embedded into geometry, so it can be individually patched. --- ref/vk/vk_brush.c | 6 ++---- ref/vk/vk_materials.c | 13 ++----------- ref/vk/vk_materials.h | 5 ++--- ref/vk/vk_ray_model.c | 7 +------ ref/vk/vk_render.c | 1 - ref/vk/vk_render.h | 20 ++------------------ ref/vk/vk_sprite.c | 4 ++-- ref/vk/vk_studio.c | 6 +++++- 8 files changed, 16 insertions(+), 46 deletions(-) diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 75c2c665..cd84eb66 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -275,10 +275,9 @@ static void brushComputeWaterPolys( compute_water_polys_t args ) { // Render const int tex_id = args.warp->texinfo->texture->gl_texturenum; - const r_vk_material_ref_t material_ref = R_VkMaterialGetForTexture(tex_id); + const r_vk_material_t material = R_VkMaterialGetForTexture(tex_id); *args.dst_geometry = (vk_render_geometry_t){ - .material = material_ref, - .material_type_deprecated = kXVkMaterialRegular, + .material = material, .ye_olde_texture = tex_id, @@ -1091,7 +1090,6 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { model_geometry->index_offset = index_offset; { - model_geometry->material_type_deprecated = kXVkMaterialRegular; ASSERT(!FBitSet( surf->flags, SURF_DRAWTILED )); VK_CreateSurfaceLightmap( surf, args.mod ); } diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index f3de6d1e..fd3e6d09 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -264,18 +264,9 @@ void R_VkMaterialsReload( void ) { } } -r_vk_material_ref_t R_VkMaterialGetForTexture( int tex_index ) { +r_vk_material_t R_VkMaterialGetForTexture( int tex_index ) { ASSERT(tex_index >= 0); ASSERT(tex_index < MAX_TEXTURES); - // TODO add versioning to detect reloads? - return (r_vk_material_ref_t){ .index = tex_index, }; -} - -const r_vk_material_t* R_VkMaterialGet( r_vk_material_ref_t ref ) { - ASSERT(ref.index >= 0); - ASSERT(ref.index < MAX_TEXTURES); - - // TODO verify version ? - return g_materials.materials + ref.index; + return g_materials.materials[tex_index]; } diff --git a/ref/vk/vk_materials.h b/ref/vk/vk_materials.h index 294c6010..bf7cfec1 100644 --- a/ref/vk/vk_materials.h +++ b/ref/vk/vk_materials.h @@ -13,6 +13,7 @@ typedef struct r_vk_material_s { float metalness; float normal_scale; + // TODO this should be internal qboolean set; } r_vk_material_t; @@ -22,6 +23,4 @@ typedef struct { int index; } r_vk_material_ref_t; // TODO: track "version" in high bits? void R_VkMaterialsReload( void ); -r_vk_material_ref_t R_VkMaterialGetForTexture( int tex_id ); - -const r_vk_material_t* R_VkMaterialGet( r_vk_material_ref_t ref ); +r_vk_material_t R_VkMaterialGetForTexture( int tex_id ); diff --git a/ref/vk/vk_ray_model.c b/ref/vk/vk_ray_model.c index 9543c03d..26bf3173 100644 --- a/ref/vk/vk_ray_model.c +++ b/ref/vk/vk_ray_model.c @@ -26,7 +26,7 @@ typedef struct rt_model_s { } rt_model_t; static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometry_t *geom, const r_vk_material_t *override_material, const vec4_t override_color) { - const r_vk_material_t *const mat = override_material ? override_material : R_VkMaterialGet(geom->material); + const r_vk_material_t *const mat = override_material ? override_material : &geom->material; ASSERT(mat); // TODO split kusochki into static geometry data and potentially dynamic material data @@ -56,11 +56,6 @@ static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometr kusok->material.base_color[2] *= override_color[2]; kusok->material.base_color[3] *= override_color[3]; } - - // TODO should be patched by the Chrome material source itself to generate a static chrome material - const qboolean HACK_chrome = geom->material_type_deprecated == kXVkMaterialChrome; - if (!mat->set && HACK_chrome) - kusok->material.tex_roughness = tglob.grayTexture; } // TODO utilize uploadKusochki([1]) to avoid 2 copies of staging code diff --git a/ref/vk/vk_render.c b/ref/vk/vk_render.c index 0654d47b..6980a8f6 100644 --- a/ref/vk/vk_render.c +++ b/ref/vk/vk_render.c @@ -821,7 +821,6 @@ void R_RenderDrawOnce(r_draw_once_t args) { const vk_render_geometry_t geometry = { .material = args.material, - .material_type_deprecated = kXVkMaterialRegular, .ye_olde_texture = args.ye_olde_texture, .max_vertex = args.vertices_count, diff --git a/ref/vk/vk_render.h b/ref/vk/vk_render.h index 3e9a2dc2..1803a3aa 100644 --- a/ref/vk/vk_render.h +++ b/ref/vk/vk_render.h @@ -10,21 +10,6 @@ void VK_RenderShutdown( void ); struct ref_viewpass_s; void VK_RenderSetupCamera( const struct ref_viewpass_s *rvp ); -// Quirk for passing surface type to the renderer -// xash3d does not really have a notion of materials. Instead there are custom code paths -// for different things. There's also render_mode for entities which determine blending mode -// and stuff. -// For ray tracing we do need to assing a material to each rendered surface, so we need to -// figure out what it is given heuristics like render_mode, texture name, surface flags, source entity type, etc. -typedef enum { - kXVkMaterialRegular = 0, - - // Set for chrome studio submodels. - // Used: ray tracing sets gray roughness texture to get smooth surface look. - // Remove: Have an explicit material for chrome surfaces. - kXVkMaterialChrome, -} XVkMaterialType_Deprecated; - typedef struct vk_render_geometry_s { int index_offset, vertex_offset; @@ -39,8 +24,7 @@ typedef struct vk_render_geometry_s { const struct msurface_s *surf_deprecate; // If this geometry is special, it will have a material type override - XVkMaterialType_Deprecated material_type_deprecated; - r_vk_material_ref_t material; + r_vk_material_t material; // Olde unpatched texture used for traditional renderer int ye_olde_texture; @@ -154,7 +138,7 @@ typedef struct { int vertices_count, indices_count; int render_type; - r_vk_material_ref_t material; + r_vk_material_t material; int ye_olde_texture; const vec4_t *emissive; const vec4_t *color; diff --git a/ref/vk/vk_sprite.c b/ref/vk/vk_sprite.c index 6ad35ce9..d593464a 100644 --- a/ref/vk/vk_sprite.c +++ b/ref/vk/vk_sprite.c @@ -103,7 +103,6 @@ static qboolean createQuadModel(void) { .element_count = 6, .index_offset = g_sprite.quad.geom.indices.unit_offset, - .material_type_deprecated = kXVkMaterialRegular, .material = R_VkMaterialGetForTexture(tglob.defaultTexture), .ye_olde_texture = tglob.defaultTexture, .emissive = {1,1,1}, @@ -799,13 +798,14 @@ static void R_DrawSpriteQuad( const char *debug_name, const mspriteframe_t *fram Matrix4x4_CreateFromVectors(transform, right, up, v_normal, org); const vk_render_type_e render_type = spriteRenderModeToRenderType(render_mode); + const r_vk_material_t material_override = R_VkMaterialGetForTexture(texture); R_RenderModelDraw(&g_sprite.quad.model, (r_model_draw_t){ .render_type = render_type, .color = (const vec4_t*)color, .transform = &transform, .prev_transform = &transform, - .material_override = R_VkMaterialGet(R_VkMaterialGetForTexture(texture)), + .material_override = &material_override, }); } diff --git a/ref/vk/vk_studio.c b/ref/vk/vk_studio.c index afa7071f..ace22623 100644 --- a/ref/vk/vk_studio.c +++ b/ref/vk/vk_studio.c @@ -1772,7 +1772,6 @@ static void buildSubmodelMeshGeometry( build_submodel_mesh_t args ) { *args.out_geometry = (vk_render_geometry_t){ .material = R_VkMaterialGetForTexture(args.texture), .ye_olde_texture = args.texture, - .material_type_deprecated = FBitSet( args.face_flags, STUDIO_NF_CHROME ) ? kXVkMaterialChrome : kXVkMaterialRegular, .vertex_offset = args.vertices_offset, .max_vertex = num_vertices, @@ -1783,6 +1782,11 @@ static void buildSubmodelMeshGeometry( build_submodel_mesh_t args ) { .emissive = {0, 0, 0}, }; + if (!args.out_geometry->material.set && FBitSet( args.face_flags, STUDIO_NF_CHROME )) { + // TODO configurable + args.out_geometry->material.roughness = tglob.grayTexture; + } + *args.out_vertices_count += num_vertices; *args.out_indices_count += num_indices; } From 9ac7340974192f269ea09bda6aabfd70d0dfaf89 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Fri, 22 Sep 2023 10:15:34 -0400 Subject: [PATCH 05/16] vk: bring back SURF_DRAWSKY geometries Needed to hide invisible geometry on e.g. c5a1. Breaks de_dust2 roofs (#474). That will need to be addressed some other way. Still uses special value for base_color texture to signal SKY SURFACE. A supposedly better way to do this would be to have them have a special material. --- ref/vk/shaders/bounce.comp | 10 ++-------- ref/vk/shaders/light_common.glsl | 18 ++++++++++++------ ref/vk/shaders/ray_common.glsl | 1 + ref/vk/shaders/ray_interop.h | 2 ++ ref/vk/shaders/ray_primary.comp | 7 ++----- ref/vk/shaders/ray_primary.rchit | 5 ++++- ref/vk/shaders/ray_primary.rmiss | 12 +----------- ref/vk/shaders/ray_primary_hit.glsl | 6 +++++- ref/vk/shaders/ray_shadow.rchit | 7 ++++++- ref/vk/shaders/ray_shadow_interface.glsl | 1 + ref/vk/vk_brush.c | 9 ++++++--- 11 files changed, 42 insertions(+), 36 deletions(-) diff --git a/ref/vk/shaders/bounce.comp b/ref/vk/shaders/bounce.comp index 9a43c8a1..094f24b0 100644 --- a/ref/vk/shaders/bounce.comp +++ b/ref/vk/shaders/bounce.comp @@ -20,7 +20,6 @@ RAY_LIGHT_DIRECT_INPUTS(X) layout(set=0, binding=20, rgba16f) uniform writeonly image2D out_indirect_diffuse; layout(set=0, binding=21, rgba16f) uniform writeonly image2D out_indirect_specular; -layout(set=0, binding=27) uniform samplerCube skybox; #include "ray_primary_common.glsl" #include "ray_primary_hit.glsl" @@ -73,6 +72,7 @@ bool getHit(vec3 origin, vec3 direction, inout RayPayloadPrimary payload) { // 2. Same as the above, but also with a completely independent TLAS. Why: no need to mask-check geometry for opaque-vs-alpha const MiniGeometry geom = readCandidateMiniGeometry(rq); const uint tex_base_color = getKusok(geom.kusok_index).material.tex_base_color; + // Should never happen: skybox is opaque if (tex_base_color == TEX_BASE_SKYBOX) const vec4 texture_color = texture(textures[nonuniformEXT(tex_base_color)], geom.uv); const float alpha_mask_threshold = .1f; @@ -149,14 +149,8 @@ void computeBounce(ivec2 pix, vec3 direction, out vec3 diffuse, out vec3 specula payload.base_color_a = vec4(0.); payload.emissive = vec4(0.); const vec3 pos = imageLoad(position_t, pix).xyz + geometry_normal * ray_normal_fudge; - if (!getHit(pos, bounce_direction, payload)) { - const vec3 skycolor = SRGBtoLINEAR(texture(skybox, bounce_direction).rgb); - if (brdf_type == DIFFUSE_TYPE) { - diffuse += skycolor; - } else - specular += skycolor; + if (!getHit(pos, bounce_direction, payload)) return; - } throughput *= payload.base_color_a.rgb; diff --git a/ref/vk/shaders/light_common.glsl b/ref/vk/shaders/light_common.glsl index fc6b9591..01db0564 100644 --- a/ref/vk/shaders/light_common.glsl +++ b/ref/vk/shaders/light_common.glsl @@ -117,7 +117,7 @@ bool shadowedSky(vec3 pos, vec3 dir) { | gl_RayFlagsSkipClosestHitShaderEXT ; const uint hit_type = traceShadowRay(pos, dir, dist, flags); - return payload_shadow.hit_type != SHADOW_MISS; + return payload_shadow.hit_type != SHADOW_SKY; #elif defined(RAY_QUERY) rayQueryEXT rq; @@ -125,22 +125,28 @@ bool shadowedSky(vec3 pos, vec3 dir) { // Culling for shadows breaks more things (e.g. de_cbble slightly off the ground boxes) than it probably fixes. Keep it turned off. //| gl_RayFlagsCullFrontFacingTrianglesEXT | gl_RayFlagsOpaqueEXT - | gl_RayFlagsTerminateOnFirstHitEXT + //| gl_RayFlagsTerminateOnFirstHitEXT //| gl_RayFlagsSkipClosestHitShaderEXT ; const float L = 10000.; // TODO Why 10k? rayQueryInitializeEXT(rq, tlas, flags, GEOMETRY_BIT_OPAQUE, pos, 0., dir, L); - // Any intersection results in a shadow + // Find closest intersection, and then check whether that was a skybox while (rayQueryProceedEXT(rq)) {} if (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionTriangleEXT) { - return true; + const uint instance_kusochki_offset = rayQueryGetIntersectionInstanceCustomIndexEXT(rq, true); + const uint geometry_index = rayQueryGetIntersectionGeometryIndexEXT(rq, true); + const uint kusok_index = instance_kusochki_offset + geometry_index; + const Kusok kusok = getKusok(kusok_index); + + if (kusok.material.tex_base_color != TEX_BASE_SKYBOX) + return true; } // check for alpha-masked surfaces separately - // TODO compare with doing it in the same loop as above - return shadowTestAlphaMask(pos, dir, L); + const float hit_t = rayQueryGetIntersectionTEXT(rq, true); + return shadowTestAlphaMask(pos, dir, hit_t); #else #error RAY_TRACE or RAY_QUERY diff --git a/ref/vk/shaders/ray_common.glsl b/ref/vk/shaders/ray_common.glsl index b48590d7..bf9ad41f 100644 --- a/ref/vk/shaders/ray_common.glsl +++ b/ref/vk/shaders/ray_common.glsl @@ -23,6 +23,7 @@ struct RayPayloadOpaque { #define SHADOW_MISS 0 #define SHADOW_HIT 1 +#define SHADOW_SKY 2 struct RayPayloadShadow { uint hit_type; diff --git a/ref/vk/shaders/ray_interop.h b/ref/vk/shaders/ray_interop.h index fefbd40c..5124b260 100644 --- a/ref/vk/shaders/ray_interop.h +++ b/ref/vk/shaders/ray_interop.h @@ -71,6 +71,8 @@ LIST_SPECIALIZATION_CONSTANTS(DECLARE_SPECIALIZATION_CONSTANT) #define MATERIAL_MODE_BLEND_GLOW 5 #define MATERIAL_MODE_COUNT 6 +#define TEX_BASE_SKYBOX 0xffffffffu + struct Material { uint tex_base_color; diff --git a/ref/vk/shaders/ray_primary.comp b/ref/vk/shaders/ray_primary.comp index 94d4fda3..768e8fa7 100644 --- a/ref/vk/shaders/ray_primary.comp +++ b/ref/vk/shaders/ray_primary.comp @@ -24,8 +24,6 @@ RAY_PRIMARY_OUTPUTS(X) layout(set = 0, binding = 1) uniform accelerationStructureEXT tlas; -layout(set = 0, binding = 7) uniform samplerCube skybox; - #include "trace_simple_blending.glsl" struct Ray { @@ -78,6 +76,7 @@ void main() { ; rayQueryInitializeEXT(rq, tlas, flags, GEOMETRY_BIT_OPAQUE | GEOMETRY_BIT_ALPHA_TEST | GEOMETRY_BIT_REFRACTIVE, ray.origin, 0., ray.direction, ray.dist); while (rayQueryProceedEXT(rq)) { + // FIXME this is a no-op. It doesn't do what I though it did. Should check for SBT index for alpha-test material instead. if (0 != (rayQueryGetRayFlagsEXT(rq) & gl_RayFlagsOpaqueEXT)) continue; @@ -90,6 +89,7 @@ void main() { // 2. Same as the above, but also with a completely independent TLAS. Why: no need to mask-check geometry for opaque-vs-alpha const MiniGeometry geom = readCandidateMiniGeometry(rq); const uint tex_base_color = getKusok(geom.kusok_index).material.tex_base_color; + // Should never happen: skybox is opaque if (tex_base_color == TEX_BASE_SKYBOX) const vec4 texture_color = texture(textures[nonuniformEXT(tex_base_color)], geom.uv); const float alpha_mask_threshold = .1f; @@ -105,9 +105,6 @@ void main() { //debug_geometry_index = rayQueryGetIntersectionPrimitiveIndexEXT(rq, true); primaryRayHit(rq, payload); L = rayQueryGetIntersectionTEXT(rq, true); - } else { - payload.hit_t = payload.prev_pos_t = vec4(ray.origin + ray.direction * ray.dist, ray.dist); - payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, ray.direction).rgb); } traceSimpleBlending(ray.origin, ray.direction, L, payload.emissive.rgb, payload.base_color_a.rgb); diff --git a/ref/vk/shaders/ray_primary.rchit b/ref/vk/shaders/ray_primary.rchit index 54826a94..d52a9e53 100644 --- a/ref/vk/shaders/ray_primary.rchit +++ b/ref/vk/shaders/ray_primary.rchit @@ -29,7 +29,10 @@ void main() { const Kusok kusok = getKusok(geom.kusok_index); - { + if (kusok.material.tex_base_color == TEX_BASE_SKYBOX) { + payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, gl_WorldRayDirectionEXT).rgb); + return; + } else { const vec4 color = getModelHeader(gl_InstanceID).color * kusok.material.base_color; payload.base_color_a = sampleTexture(kusok.material.tex_base_color, geom.uv, geom.uv_lods) * color; payload.material_rmxx.r = sampleTexture(kusok.material.tex_roughness, geom.uv, geom.uv_lods).r * kusok.material.roughness; diff --git a/ref/vk/shaders/ray_primary.rmiss b/ref/vk/shaders/ray_primary.rmiss index 134e1d39..4b072ab7 100644 --- a/ref/vk/shaders/ray_primary.rmiss +++ b/ref/vk/shaders/ray_primary.rmiss @@ -1,14 +1,4 @@ #version 460 core #extension GL_EXT_ray_tracing: require -#include "ray_primary_common.glsl" -#include "color_spaces.glsl" - -layout(set = 0, binding = 7) uniform samplerCube skybox; - -layout(location = PAYLOAD_LOCATION_PRIMARY) rayPayloadEXT RayPayloadPrimary payload; - -void main() { - // TODO payload.prev_pos_t = payload.hit_t = vec4(geom.pos, gl_HitTEXT); - payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, gl_WorldRayDirectionEXT).rgb); -} +void main() {} diff --git a/ref/vk/shaders/ray_primary_hit.glsl b/ref/vk/shaders/ray_primary_hit.glsl index 8d6c38d8..1a115042 100644 --- a/ref/vk/shaders/ray_primary_hit.glsl +++ b/ref/vk/shaders/ray_primary_hit.glsl @@ -10,6 +10,7 @@ layout(set = 0, binding = 6) uniform sampler2D textures[MAX_TEXTURES]; layout(set = 0, binding = 2) uniform UBO { UniformBuffer ubo; } ubo; +layout(set = 0, binding = 7) uniform samplerCube skybox; vec4 sampleTexture(uint tex_index, vec2 uv, vec4 uv_lods) { #ifndef RAY_BOUNCE @@ -29,7 +30,10 @@ void primaryRayHit(rayQueryEXT rq, inout RayPayloadPrimary payload) { const Kusok kusok = getKusok(geom.kusok_index); const Material material = kusok.material; - { + if (kusok.material.tex_base_color == TEX_BASE_SKYBOX) { + payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, rayDirection).rgb); + return; + } else { payload.base_color_a = sampleTexture(material.tex_base_color, geom.uv, geom.uv_lods); payload.material_rmxx.r = sampleTexture(material.tex_roughness, geom.uv, geom.uv_lods).r * material.roughness; payload.material_rmxx.g = sampleTexture(material.tex_metalness, geom.uv, geom.uv_lods).r * material.metalness; diff --git a/ref/vk/shaders/ray_shadow.rchit b/ref/vk/shaders/ray_shadow.rchit index f6b1f70b..b63f7208 100644 --- a/ref/vk/shaders/ray_shadow.rchit +++ b/ref/vk/shaders/ray_shadow.rchit @@ -7,5 +7,10 @@ layout(location = PAYLOAD_LOCATION_SHADOW) rayPayloadInEXT RayPayloadShadow payload_shadow; void main() { - payload_shadow.hit_type = SHADOW_HIT; + const int instance_kusochki_offset = gl_InstanceCustomIndexEXT; + const int kusok_index = instance_kusochki_offset + gl_GeometryIndexEXT; + const Kusok kusok = getKusok(kusok_index); + const uint tex_base_color = kusok.material.tex_base_color; + + payload_shadow.hit_type = (kusok.material.tex_base_color != TEX_BASE_SKYBOX) ? SHADOW_HIT : SHADOW_SKY ; } diff --git a/ref/vk/shaders/ray_shadow_interface.glsl b/ref/vk/shaders/ray_shadow_interface.glsl index 9b386926..73d83be7 100644 --- a/ref/vk/shaders/ray_shadow_interface.glsl +++ b/ref/vk/shaders/ray_shadow_interface.glsl @@ -3,6 +3,7 @@ #define SHADOW_MISS 0 #define SHADOW_HIT 1 +#define SHADOW_SKY 2 struct RayPayloadShadow { uint hit_type; diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index cd84eb66..5e7d6da7 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -778,13 +778,13 @@ static model_sizes_t computeSizes( const model_t *mod ) { case BrushSurface_Water: sizes.water_surfaces++; addWarpVertIndCounts(surf, &sizes.water_vertices, &sizes.water_indices); - case BrushSurface_Sky: case BrushSurface_Hidden: continue; case BrushSurface_Animated: sizes.animated_count++; case BrushSurface_Regular: + case BrushSurface_Sky: break; } @@ -1058,11 +1058,11 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { switch (type) { case BrushSurface_Water: case BrushSurface_Hidden: - case BrushSurface_Sky: continue; case BrushSurface_Animated: args.bmodel->animated_indexes[animated_count++] = num_geometries; case BrushSurface_Regular: + case BrushSurface_Sky: break; } @@ -1089,7 +1089,10 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { model_geometry->index_offset = index_offset; - { + if ( type == BrushSurface_Sky ) { +#define TEX_BASE_SKYBOX 0xffffffffu // FIXME ray_interop.h + model_geometry->material.tex_base_color = TEX_BASE_SKYBOX; + } else { ASSERT(!FBitSet( surf->flags, SURF_DRAWTILED )); VK_CreateSurfaceLightmap( surf, args.mod ); } From e26ce740f150e60bf55d1065d4da21f032bc7dc4 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 25 Sep 2023 12:44:41 -0400 Subject: [PATCH 06/16] vk: cut orig file extension from material files E.g. load `pbr/halflife.wad/halflife.mat` not `pbr/halflife.wad/halflife.wad.mat` Requires changes to the PBR repo. Reference: #578 --- ref/vk/vk_materials.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index fd3e6d09..387bd207 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -236,17 +236,41 @@ void R_VkMaterialsReload( void ) { loadMaterialsFromFile( "pbr/sprites/sprites.mat", MAX_INCLUDE_DEPTH ); { - const char *wad = g_map_entities.wadlist; - for (; *wad;) { - const char *const wad_end = Q_strchr(wad, ';'); - loadMaterialsFromFileF("pbr/%.*s/%.*s.mat", wad_end - wad, wad, wad_end - wad, wad); + for(const char *wad = g_map_entities.wadlist; *wad;) { + const char *wad_end = wad; + const char *ext = NULL; + while (*wad_end && *wad_end != ';') { + if (*wad_end == '.') + ext = wad_end; + ++wad_end; + } + + const int full_length = wad_end - wad; + + // Length without extension + const int short_length = ext ? ext - wad : full_length; + + loadMaterialsFromFileF("pbr/%.*s/%.*s.mat", full_length, wad, short_length, wad); wad = wad_end + 1; } } { const model_t *map = gEngine.pfnGetModelByIndex( 1 ); - loadMaterialsFromFileF("pbr/%s/%s.mat", map->name, COM_FileWithoutPath(map->name)); + const char *filename = COM_FileWithoutPath(map->name); + const char *ext = NULL; + + // Find extension (if any) + { + const char *p = filename; + for(; *p; ++p) + if (*p == '.') + ext = p; + if (!ext) + ext = p; + } + + loadMaterialsFromFileF("pbr/%s/%.*s.mat", map->name, ext - filename, filename); } // Print out statistics From e8a09c85e54faa5b641975f64b40081e4d895edc Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 25 Sep 2023 12:53:58 -0400 Subject: [PATCH 07/16] vk: load map patches without extra .bsp extension e.g. `c1a0d.patch`, not `c1a0d.bsp.patch` Ref: #578 --- .../luchiki/maps/{c0a0.bsp.patch => c0a0.patch} | 0 .../maps/{c0a0a.bsp.patch => c0a0a.patch} | 0 .../maps/{c0a0b.bsp.patch => c0a0b.patch} | 0 .../maps/{c0a0c.bsp.patch => c0a0c.patch} | 0 .../maps/{c0a0d.bsp.patch => c0a0d.patch} | 0 .../maps/{c0a0e.bsp.patch => c0a0e.patch} | 0 .../luchiki/maps/{c1a0.bsp.patch => c1a0.patch} | 0 .../maps/{c1a0a.bsp.patch => c1a0a.patch} | 0 .../maps/{c1a0b.bsp.patch => c1a0b.patch} | 0 .../maps/{c1a0c.bsp.patch => c1a0c.patch} | 0 .../maps/{c1a0d.bsp.patch => c1a0d.patch} | 0 .../maps/{c1a0e.bsp.patch => c1a0e.patch} | 0 .../luchiki/maps/{c1a1.bsp.patch => c1a1.patch} | 0 .../maps/{c1a1a.bsp.patch => c1a1a.patch} | 0 .../maps/{c1a1b.bsp.patch => c1a1b.patch} | 0 .../maps/{c1a1c.bsp.patch => c1a1c.patch} | 0 .../maps/{c1a1d.bsp.patch => c1a1d.patch} | 0 .../maps/{c1a1f.bsp.patch => c1a1f.patch} | 0 .../luchiki/maps/{c1a2.bsp.patch => c1a2.patch} | 0 .../maps/{c1a2a.bsp.patch => c1a2a.patch} | 0 .../maps/{c1a2b.bsp.patch => c1a2b.patch} | 0 .../maps/{c1a2c.bsp.patch => c1a2c.patch} | 0 .../maps/{c1a2d.bsp.patch => c1a2d.patch} | 0 .../luchiki/maps/{c1a3.bsp.patch => c1a3.patch} | 0 .../maps/{c1a3a.bsp.patch => c1a3a.patch} | 0 ...1a3b-dayone.bsp.patch => c1a3b-dayone.patch} | 0 .../maps/{c1a3b.bsp.patch => c1a3b.patch} | 0 ...1a3c-dayone.bsp.patch => c1a3c-dayone.patch} | 0 .../maps/{c1a3d.bsp.patch => c1a3d.patch} | 0 .../maps/{c1a4d.bsp.patch => c1a4d.patch} | 0 .../maps/{c1a4k.bsp.patch => c1a4k.patch} | 0 .../luchiki/maps/{c2a5.bsp.patch => c2a5.patch} | 0 .../maps/{hldemo1.bsp.patch => hldemo1.patch} | 0 .../maps/{hldemo2.bsp.patch => hldemo2.patch} | 0 ref/vk/vk_mapents.c | 17 ++++++++++++++++- 35 files changed, 16 insertions(+), 1 deletion(-) rename ref/vk/data/valve/luchiki/maps/{c0a0.bsp.patch => c0a0.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c0a0a.bsp.patch => c0a0a.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c0a0b.bsp.patch => c0a0b.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c0a0c.bsp.patch => c0a0c.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c0a0d.bsp.patch => c0a0d.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c0a0e.bsp.patch => c0a0e.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a0.bsp.patch => c1a0.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a0a.bsp.patch => c1a0a.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a0b.bsp.patch => c1a0b.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a0c.bsp.patch => c1a0c.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a0d.bsp.patch => c1a0d.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a0e.bsp.patch => c1a0e.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a1.bsp.patch => c1a1.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a1a.bsp.patch => c1a1a.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a1b.bsp.patch => c1a1b.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a1c.bsp.patch => c1a1c.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a1d.bsp.patch => c1a1d.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a1f.bsp.patch => c1a1f.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a2.bsp.patch => c1a2.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a2a.bsp.patch => c1a2a.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a2b.bsp.patch => c1a2b.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a2c.bsp.patch => c1a2c.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a2d.bsp.patch => c1a2d.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a3.bsp.patch => c1a3.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a3a.bsp.patch => c1a3a.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a3b-dayone.bsp.patch => c1a3b-dayone.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a3b.bsp.patch => c1a3b.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a3c-dayone.bsp.patch => c1a3c-dayone.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a3d.bsp.patch => c1a3d.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a4d.bsp.patch => c1a4d.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c1a4k.bsp.patch => c1a4k.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{c2a5.bsp.patch => c2a5.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{hldemo1.bsp.patch => hldemo1.patch} (100%) rename ref/vk/data/valve/luchiki/maps/{hldemo2.bsp.patch => hldemo2.patch} (100%) diff --git a/ref/vk/data/valve/luchiki/maps/c0a0.bsp.patch b/ref/vk/data/valve/luchiki/maps/c0a0.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c0a0.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c0a0.patch diff --git a/ref/vk/data/valve/luchiki/maps/c0a0a.bsp.patch b/ref/vk/data/valve/luchiki/maps/c0a0a.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c0a0a.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c0a0a.patch diff --git a/ref/vk/data/valve/luchiki/maps/c0a0b.bsp.patch b/ref/vk/data/valve/luchiki/maps/c0a0b.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c0a0b.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c0a0b.patch diff --git a/ref/vk/data/valve/luchiki/maps/c0a0c.bsp.patch b/ref/vk/data/valve/luchiki/maps/c0a0c.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c0a0c.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c0a0c.patch diff --git a/ref/vk/data/valve/luchiki/maps/c0a0d.bsp.patch b/ref/vk/data/valve/luchiki/maps/c0a0d.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c0a0d.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c0a0d.patch diff --git a/ref/vk/data/valve/luchiki/maps/c0a0e.bsp.patch b/ref/vk/data/valve/luchiki/maps/c0a0e.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c0a0e.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c0a0e.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a0.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a0.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a0.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a0.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a0a.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a0a.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a0a.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a0a.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a0b.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a0b.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a0b.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a0b.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a0c.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a0c.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a0c.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a0c.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a0d.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a0d.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a0d.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a0d.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a0e.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a0e.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a0e.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a0e.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a1.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a1.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a1.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a1.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a1a.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a1a.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a1a.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a1a.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a1b.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a1b.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a1b.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a1b.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a1c.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a1c.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a1c.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a1c.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a1d.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a1d.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a1d.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a1d.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a1f.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a1f.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a1f.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a1f.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a2.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a2.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a2.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a2.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a2a.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a2a.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a2a.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a2a.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a2b.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a2b.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a2b.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a2b.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a2c.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a2c.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a2c.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a2c.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a2d.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a2d.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a2d.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a2d.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a3.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a3.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a3.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a3.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a3a.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a3a.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a3a.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a3a.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a3b-dayone.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a3b-dayone.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a3b-dayone.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a3b-dayone.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a3b.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a3b.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a3b.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a3b.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a3c-dayone.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a3c-dayone.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a3c-dayone.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a3c-dayone.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a3d.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a3d.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a3d.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a3d.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a4d.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a4d.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a4d.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a4d.patch diff --git a/ref/vk/data/valve/luchiki/maps/c1a4k.bsp.patch b/ref/vk/data/valve/luchiki/maps/c1a4k.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c1a4k.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c1a4k.patch diff --git a/ref/vk/data/valve/luchiki/maps/c2a5.bsp.patch b/ref/vk/data/valve/luchiki/maps/c2a5.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/c2a5.bsp.patch rename to ref/vk/data/valve/luchiki/maps/c2a5.patch diff --git a/ref/vk/data/valve/luchiki/maps/hldemo1.bsp.patch b/ref/vk/data/valve/luchiki/maps/hldemo1.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/hldemo1.bsp.patch rename to ref/vk/data/valve/luchiki/maps/hldemo1.patch diff --git a/ref/vk/data/valve/luchiki/maps/hldemo2.bsp.patch b/ref/vk/data/valve/luchiki/maps/hldemo2.patch similarity index 100% rename from ref/vk/data/valve/luchiki/maps/hldemo2.bsp.patch rename to ref/vk/data/valve/luchiki/maps/hldemo2.patch diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index d0965489..6317eafb 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -699,7 +699,22 @@ static void parsePatches( const model_t *const map ) { g_patch.surfaces_count = 0; } - Q_snprintf(filename, sizeof(filename), "luchiki/%s.patch", map->name); + { + const char *ext = NULL; + + // Find extension (if any) + { + const char *p = map->name; + for(; *p; ++p) + if (*p == '.') + ext = p; + if (!ext) + ext = p; + } + + Q_snprintf(filename, sizeof(filename), "luchiki/%.*s.patch", (int)(ext - map->name), map->name); + } + DEBUG("Loading patches from file \"%s\"", filename); data = gEngine.fsapi->LoadFile( filename, 0, false ); if (!data) { From 26224b4acae4a7c52e53c5903594ee29fe2b6237 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 25 Sep 2023 13:57:43 -0400 Subject: [PATCH 08/16] vk: materials: add a few stream E301 notes --- ref/vk/NOTES.md | 93 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/ref/vk/NOTES.md b/ref/vk/NOTES.md index 8ed2cead..c556eed7 100644 --- a/ref/vk/NOTES.md +++ b/ref/vk/NOTES.md @@ -412,3 +412,96 @@ TODO: can we not have a BLAS/model for each submodel? Can it be per-model instea - to build skychain, and then draw it in Quake mode (the other branch does a bunch of math, which seemingly isn't used for anything at all). - for dynamic lighting: if sky ray has hit sky surface then sky is contributing light +# 2023-09-25 #301 +## Materials format +Define new material, independently of any existing textures, etc +This can be .vmat compatible, primext compatbile, etc. +The important parts: +- It has a unique name that we can reference it with +- It has all the fields that we use for our PBR shading model +- (? Material mode can be specified) +``` +{ + "material" "MAT_NAME" + "map_base_color" "base.png" + "map_normal" "irregular.ktx2" + "base_color" "1 .5 0" + // ... +} + +{ + "material" "mirror" + "map_base_color" "white" + "base_color" "1 1 1" + "roughness" "0" + "metalness" "1" + // ... +} +``` + +Then, we can map existing textures to new materials: +``` +{ + "for_texture" "+EXIT" + "use" "MAT_NAME" +} +``` + +Or, with more context: +``` +{ + "for_model_type" "brush" + "for_rendermode" "kRenderTransAlpha" + "for_texture" "wood" + "use" "mat_glass" + "mode" "translucent" + "map_base_color" "glass2.ktx" +} + +// ??? meh, see the better _xvk_ example below +{ + "for_model_type" "brush" + "for_surface_id" "584" + "use" "mirror" +} + +// This example: use previously specified material (e.g. via _xvk stuff below) +// (Depends on applying multiple matching rules, see questions below) +{ + "for_model_type" "brush" + "for_rendermode" "kRenderTransAlpha" + "mode" "translucent" + "map_normal" "glass2.ktx" +} + +// We also want this (for maps, not globally ofc), see https://github.com/w23/xash3d-fwgs/issues/526 +{ + "for_entity_id" "39" + "for_texture" "generic028" + "use" "generic_metal1" +} + +{ + "for_entity_id" "39" + "for_texture" "generic029" + "use" "generic_metal2" +} +``` + +What it does is: +1. If all `"for_"` fields match, apply values from `"use"` material (in this case `"wood"`) +2. Additionally, override any extra fields/values with ones specified in this block + +As we already have surface-patching ability, can just use that for patching materials directly for brush surfaces: +``` +// mirror in toilet +{ + "_xvk_surface_id" "2057" + "_xvk_material" "mirror" +} +``` + +Questions: +- Should it apply the first found rule that matches a given geometry and stop? + Or should it apply updates to the material using all the rules that matched in their specified order? Doing the first rule and stopping is more readable and perofrmant, but also might be verbose in some cases. +- Should we do "automatic" materials? I.e. if there's no manually specified material for a texture named `""`, then we try to load `"_basecolor.ktx"`, `"_normalmap.ktx"`, etc automatically. From b549ac76f6f223548b6054df12dd6c4a7ad54016 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 26 Sep 2023 12:25:40 -0400 Subject: [PATCH 09/16] vk: add logs and notes about mod/spr load sequence --- ref/vk/NOTES.md | 39 +++++++++++++++++++++++++++++++++++++++ ref/vk/vk_logs.c | 2 ++ ref/vk/vk_logs.h | 2 ++ ref/vk/vk_materials.c | 2 +- ref/vk/vk_rmain.c | 4 +++- ref/vk/vk_sprite.c | 5 +++++ 6 files changed, 52 insertions(+), 2 deletions(-) diff --git a/ref/vk/NOTES.md b/ref/vk/NOTES.md index c556eed7..12a3d456 100644 --- a/ref/vk/NOTES.md +++ b/ref/vk/NOTES.md @@ -505,3 +505,42 @@ Questions: - Should it apply the first found rule that matches a given geometry and stop? Or should it apply updates to the material using all the rules that matched in their specified order? Doing the first rule and stopping is more readable and perofrmant, but also might be verbose in some cases. - Should we do "automatic" materials? I.e. if there's no manually specified material for a texture named `""`, then we try to load `"_basecolor.ktx"`, `"_normalmap.ktx"`, etc automatically. + +# 2023-09-26 E302 +Map loading sequence +``` +[2023:09:26|11:30:31] Couldn't open file overviews/c1a0d.txt. Using default values for overiew mode. +[2023:09:26|11:30:31] CL_SignonReply: 2 +[2023:09:26|11:30:31] Signon network traffic: 10.380 Kb from server, 349 bytes to server +[2023:09:26|11:30:31] client connected at 0.07 sec +[2023:09:26|11:30:31] Error: SDL_GL_SetSwapInterval: No OpenGL context has been made current +[2023:09:26|11:30:31] vk: Mod_ProcessRenderData(sprites/640_pain.spr, create=1) + +[2023:09:26|11:30:43] Loading game from save/autosave01.sav... +[2023:09:26|11:30:43] Spawn Server: c2a5 +[2023:09:26|11:30:43] vk: Mod_ProcessRenderData(maps/c1a0d.bsp, create=0) + +[2023:09:26|11:30:43] Warning: VK FIXME Trying to unload brush model maps/c1a0d.bsp +[2023:09:26|11:30:43] Error: VK NOT_IMPLEMENTED(x0): RT_KusochkiFree +[2023:09:26|11:30:43] loading maps/c2a5.bsp +[2023:09:26|11:30:43] Warning: FS_LoadImage: couldn't load "alpha_sky" +[2023:09:26|11:30:43] Warning: FS_LoadImage: couldn't load "solid_sky" +[2023:09:26|11:30:43] lighting: colored +[2023:09:26|11:30:43] Wad files required to run the map: "halflife.wad; liquids.wad; xeno.wad" +[2023:09:26|11:30:43] vk: Mod_ProcessRenderData(maps/c2a5.bsp, create=1) + +[2023:09:26|11:30:43] Loading game from save/c2a5.HL1... +[2023:09:26|11:30:43] +GAME SKILL LEVEL:1 +[2023:09:26|11:30:43] Loading CGraph in GRAPH_VERSION 16 compatibility mode +[2023:09:26|11:30:43] Loading CLink array in GRAPH_VERSION 16 compatibility mode +[2023:09:26|11:30:43] +*Graph Loaded! +[2023:09:26|11:30:43] **Graph Pointers Set! +[2023:09:26|11:30:43] loading sprites/flare1.spr +[2023:09:26|11:30:43] vk: Mod_ProcessRenderData(sprites/flare1.spr, create=1) +.. more Mod_ProcessRenderData +.. and only then R_NewMap +``` + + diff --git a/ref/vk/vk_logs.c b/ref/vk/vk_logs.c index 970b4022..af5603e4 100644 --- a/ref/vk/vk_logs.c +++ b/ref/vk/vk_logs.c @@ -16,6 +16,8 @@ static const struct log_pair_t { {"mat", LogModule_Material}, {"meat", LogModule_Meatpipe}, {"rt", LogModule_RT}, + {"rmain", LogModule_RMain}, + {"sprite", LogModule_Sprite}, }; void VK_LogsReadCvar(void) { diff --git a/ref/vk/vk_logs.h b/ref/vk/vk_logs.h index 0f95914f..ae2d7c7a 100644 --- a/ref/vk/vk_logs.h +++ b/ref/vk/vk_logs.h @@ -12,6 +12,8 @@ enum { LogModule_Material = (1<<6), LogModule_Meatpipe = (1<<7), LogModule_RT = (1<<8), + LogModule_RMain = (1<<9), + LogModule_Sprite = (1<<10), }; extern uint32_t g_log_debug_bits; diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index 387bd207..f7591e0c 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -78,7 +78,7 @@ static void loadMaterialsFromFile( const char *filename, int depth ) { string basecolor_map, normal_map, metal_map, roughness_map; - DEBUG("Loading materials from %s", filename); + DEBUG("Loading materials from %s (exists=%d)", filename, data != 0); if ( !data ) return; diff --git a/ref/vk/vk_rmain.c b/ref/vk/vk_rmain.c index 24a830f3..642335df 100644 --- a/ref/vk/vk_rmain.c +++ b/ref/vk/vk_rmain.c @@ -21,6 +21,8 @@ #include +#define LOG_MODULE LogModule_RMain + ref_api_t gEngine = {0}; ref_globals_t *gpGlobals = NULL; @@ -117,7 +119,7 @@ static qboolean Mod_ProcessRenderData( model_t *mod, qboolean create, const byte { qboolean loaded = true; - //gEngine.Con_Reportf("%s(%s, create=%d)\n", __FUNCTION__, mod->name, create); + DEBUG("%s(%s, create=%d)", __FUNCTION__, mod->name, create); // TODO does this ever happen? if (!create && mod->type == mod_brush) diff --git a/ref/vk/vk_sprite.c b/ref/vk/vk_sprite.c index d593464a..4818eb3a 100644 --- a/ref/vk/vk_sprite.c +++ b/ref/vk/vk_sprite.c @@ -6,6 +6,7 @@ #include "vk_scene.h" #include "r_speeds.h" #include "vk_math.h" +#include "vk_logs.h" #include "sprite.h" #include "xash3d_mathlib.h" @@ -16,6 +17,7 @@ #include #define MODULE_NAME "sprite" +#define LOG_MODULE LogModule_Sprite // it's a Valve default value for LoadMapSprite (probably must be power of two) #define MAPSPRITE_SIZE 128 @@ -404,6 +406,7 @@ Loading a bitmap image as sprite with multiple frames as pieces of input image ==================== */ +// IS NOT CALLED BY ANYTHING?! void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean *loaded ) { byte *src, *dst; @@ -417,6 +420,8 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean msprite_t *psprite; SpriteLoadContext ctx = {0}; + DEBUG("%s(%s, %p, %d, %d)", __FUNCTION__, mod->name, buffer, (int)size, (int)*loaded); + if( loaded ) *loaded = false; Q_snprintf( texname, sizeof( texname ), "#%s", mod->name ); gEngine.Image_SetForceFlags( IL_OVERVIEW ); From acd87043fbf6b878330058728a5ab6112744438e Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 26 Sep 2023 12:27:24 -0400 Subject: [PATCH 10/16] vk: materials: load by mdl/spr individually, not by global {models,sprites}.mat --- ref/vk/vk_materials.c | 39 ++++++++++++++++++++++++--------------- ref/vk/vk_materials.h | 3 +++ ref/vk/vk_scene.c | 2 ++ 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index f7591e0c..866566ed 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -215,6 +215,18 @@ static void loadMaterialsFromFileF( const char *fmt, ... ) { loadMaterialsFromFile( buffer, MAX_INCLUDE_DEPTH ); } +static int findFilenameExtension(const char *s, int len) { + if (len < 0) + len = Q_strlen(s); + + for (int i = len - 1; i >= 0; --i) { + if (s[i] == '.') + return i; + } + + return len; +} + void R_VkMaterialsReload( void ) { memset(&g_stats, 0, sizeof(g_stats)); const uint64_t begin_time_ns = aprof_time_now_ns(); @@ -232,8 +244,6 @@ void R_VkMaterialsReload( void ) { } loadMaterialsFromFile( "pbr/materials.mat", MAX_INCLUDE_DEPTH ); - loadMaterialsFromFile( "pbr/models/models.mat", MAX_INCLUDE_DEPTH ); - loadMaterialsFromFile( "pbr/sprites/sprites.mat", MAX_INCLUDE_DEPTH ); { for(const char *wad = g_map_entities.wadlist; *wad;) { @@ -258,19 +268,8 @@ void R_VkMaterialsReload( void ) { { const model_t *map = gEngine.pfnGetModelByIndex( 1 ); const char *filename = COM_FileWithoutPath(map->name); - const char *ext = NULL; - - // Find extension (if any) - { - const char *p = filename; - for(; *p; ++p) - if (*p == '.') - ext = p; - if (!ext) - ext = p; - } - - loadMaterialsFromFileF("pbr/%s/%.*s.mat", map->name, ext - filename, filename); + const int no_ext_len = findFilenameExtension(map->name, -1); + loadMaterialsFromFileF("pbr/%s/%.*s.mat", map->name, no_ext_len, filename); } // Print out statistics @@ -288,6 +287,16 @@ void R_VkMaterialsReload( void ) { } } +void R_VkMaterialsLoadForModel( const struct model_s* mod ) { + // Brush models are loaded separately + if (mod->type == mod_brush) + return; + + const char *filename = COM_FileWithoutPath(mod->name); + const int no_ext_len = findFilenameExtension(filename, -1); + loadMaterialsFromFileF("pbr/%s/%.*s.mat", mod->name, no_ext_len, filename); +} + r_vk_material_t R_VkMaterialGetForTexture( int tex_index ) { ASSERT(tex_index >= 0); ASSERT(tex_index < MAX_TEXTURES); diff --git a/ref/vk/vk_materials.h b/ref/vk/vk_materials.h index bf7cfec1..b171a3e8 100644 --- a/ref/vk/vk_materials.h +++ b/ref/vk/vk_materials.h @@ -23,4 +23,7 @@ typedef struct { int index; } r_vk_material_ref_t; // TODO: track "version" in high bits? void R_VkMaterialsReload( void ); +struct model_s; +void R_VkMaterialsLoadForModel( const struct model_s* mod ); + r_vk_material_t R_VkMaterialGetForTexture( int tex_id ); diff --git a/ref/vk/vk_scene.c b/ref/vk/vk_scene.c index 2bad00b2..d6ed7dad 100644 --- a/ref/vk/vk_scene.c +++ b/ref/vk/vk_scene.c @@ -121,6 +121,8 @@ static void preloadModels( void ) { DEBUG( " %d: name=%s, type=%d, submodels=%d, nodes=%d, surfaces=%d, nummodelsurfaces=%d", i, m->name, m->type, m->numsubmodels, m->numnodes, m->numsurfaces, m->nummodelsurfaces); + R_VkMaterialsLoadForModel(m); + switch (m->type) { case mod_brush: if (!VK_BrushModelLoad(m)) From f63dcd14ce721873d36132f514e70ebe261ba637 Mon Sep 17 00:00:00 2001 From: Ivan 'provod' Avdeev Date: Tue, 26 Sep 2023 12:41:10 -0400 Subject: [PATCH 11/16] vk: rt: draw skybox where ray hasn't hit anything fixes #579 --- ref/vk/shaders/ray_primary.comp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ref/vk/shaders/ray_primary.comp b/ref/vk/shaders/ray_primary.comp index 768e8fa7..6bca6892 100644 --- a/ref/vk/shaders/ray_primary.comp +++ b/ref/vk/shaders/ray_primary.comp @@ -105,6 +105,9 @@ void main() { //debug_geometry_index = rayQueryGetIntersectionPrimitiveIndexEXT(rq, true); primaryRayHit(rq, payload); L = rayQueryGetIntersectionTEXT(rq, true); + } else { + // Draw skybox when nothing is hit + payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, ray.direction).rgb); } traceSimpleBlending(ray.origin, ray.direction, L, payload.emissive.rgb, payload.base_color_a.rgb); From b07c5c3740583021dd50c440e92ac4879d618e61 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 26 Sep 2023 13:14:28 -0400 Subject: [PATCH 12/16] vk: rt: pass material mode directly from draw command Different render sources (model types, render types, etc) might require different material modes. E.g. brush should be translucent (refractive+mirror) for blend mix modes. However, smoke particles should not be mirror/refractive for the same blend mix render type. --- ref/vk/vk_brush.c | 31 +++++++++++++++++++++++++++++++ ref/vk/vk_ray_model.c | 7 ++++--- ref/vk/vk_render.c | 2 +- ref/vk/vk_render.h | 17 ++++++++++++++++- ref/vk/vk_rtx.h | 2 +- ref/vk/vk_sprite.c | 2 ++ ref/vk/vk_studio.c | 5 ++++- 7 files changed, 59 insertions(+), 7 deletions(-) diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 5e7d6da7..852300cb 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -534,6 +534,33 @@ static qboolean brushCreateWaterModel(const model_t *mod, vk_brush_model_t *bmod return true; } +material_mode_e brushMaterialModeForRenderType(vk_render_type_e render_type) { + switch (render_type) { + case kVkRenderTypeSolid: + return kMaterialMode_Opaque; + break; + case kVkRenderType_A_1mA_RW: // blend: scr*a + dst*(1-a), depth: RW + case kVkRenderType_A_1mA_R: // blend: scr*a + dst*(1-a), depth test + return kMaterialMode_Translucent; + break; + case kVkRenderType_A_1: // blend: scr*a + dst, no depth test or write; sprite:kRenderGlow only + return kMaterialMode_BlendGlow; + break; + case kVkRenderType_A_1_R: // blend: scr*a + dst, depth test + case kVkRenderType_1_1_R: // blend: scr + dst, depth test + return kMaterialMode_BlendAdd; + break; + case kVkRenderType_AT: // no blend, depth RW, alpha test + return kMaterialMode_AlphaTest; + break; + + default: + gEngine.Host_Error("Unexpected render type %d\n", render_type); + } + + return kMaterialMode_Opaque; +} + static void brushDrawWater(vk_brush_model_t *bmodel, const cl_entity_t *ent, int render_type, const vec4_t color, const matrix4x4 transform) { APROF_SCOPE_DECLARE_BEGIN(brush_draw_water, __FUNCTION__); ASSERT(bmodel->water.surfaces_count > 0); @@ -543,8 +570,10 @@ static void brushDrawWater(vk_brush_model_t *bmodel, const cl_entity_t *ent, int ERR("Failed to update brush model \"%s\" water", bmodel->render_model.debug_name); } + const material_mode_e material_mode = brushMaterialModeForRenderType(render_type); R_RenderModelDraw(&bmodel->water.render_model, (r_model_draw_t){ .render_type = render_type, + .material_mode = material_mode, .color = (const vec4_t*)color, .transform = (const matrix4x4*)transform, .prev_transform = &bmodel->prev_transform, @@ -751,8 +780,10 @@ void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, float blend, co APROF_SCOPE_END(brush_update_textures); } + const material_mode_e material_mode = brushMaterialModeForRenderType(render_type); R_RenderModelDraw(&bmodel->render_model, (r_model_draw_t){ .render_type = render_type, + .material_mode = material_mode, .color = &color, .transform = &transform, .prev_transform = &bmodel->prev_transform, diff --git a/ref/vk/vk_ray_model.c b/ref/vk/vk_ray_model.c index 26bf3173..3b773571 100644 --- a/ref/vk/vk_ray_model.c +++ b/ref/vk/vk_ray_model.c @@ -99,7 +99,7 @@ static qboolean uploadKusochkiSubset(const vk_ray_model_t *const model, const vk // TODO this material mapping is context dependent. I.e. different entity types might need different ray tracing behaviours for // same render_mode/type and even texture. -static uint32_t materialModeFromRenderType(vk_render_type_e render_type) { +uint32_t R_VkMaterialModeFromRenderType(vk_render_type_e render_type) { switch (render_type) { case kVkRenderTypeSolid: return MATERIAL_MODE_OPAQUE; @@ -328,7 +328,7 @@ void RT_FrameAddModel( struct rt_model_s *model, rt_frame_add_model_t args ) { draw_instance->blas_addr = model->blas_addr; draw_instance->kusochki_offset = kusochki_offset; - draw_instance->material_mode = materialModeFromRenderType(args.render_type); + draw_instance->material_mode = args.material_mode; Vector4Copy(*args.color, draw_instance->color); Matrix3x4_Copy(draw_instance->transform_row, args.transform); Matrix4x4_Copy(draw_instance->prev_transform_row, args.prev_transform); @@ -430,7 +430,8 @@ tail: } void RT_FrameAddOnce( rt_frame_add_once_t args ) { - const int material_mode = materialModeFromRenderType(args.render_type); + // TODO pass material_mode explicitly + const int material_mode = R_VkMaterialModeFromRenderType(args.render_type); rt_dynamic_t *const dyn = g_dyn.groups + material_mode; for (int i = 0; i < args.geometries_count; ++i) { diff --git a/ref/vk/vk_render.c b/ref/vk/vk_render.c index 6980a8f6..edc76323 100644 --- a/ref/vk/vk_render.c +++ b/ref/vk/vk_render.c @@ -781,7 +781,7 @@ void R_RenderModelDraw(const vk_render_model_t *model, r_model_draw_t args) { if (g_render_state.current_frame_is_ray_traced) { ASSERT(model->rt_model); RT_FrameAddModel(model->rt_model, (rt_frame_add_model_t){ - .render_type = args.render_type, + .material_mode = args.material_mode, .transform = (const matrix3x4*)args.transform, .prev_transform = (const matrix3x4*)args.prev_transform, .color = args.color, diff --git a/ref/vk/vk_render.h b/ref/vk/vk_render.h index 1803a3aa..93c2098e 100644 --- a/ref/vk/vk_render.h +++ b/ref/vk/vk_render.h @@ -77,6 +77,20 @@ typedef enum { kVkRenderType_COUNT } vk_render_type_e; +typedef enum { + // MUST be congruent to MATERIAL_MODE_* definitions in shaders/ray_interop.h + kMaterialMode_Opaque = 0, + kMaterialMode_AlphaTest = 1, + kMaterialMode_Translucent = 2, + kMaterialMode_BlendAdd = 3, + kMaterialMode_BlendMix = 4, + kMaterialMode_BlendGlow = 5, + + kMaterialMode_COUNT = 6, +} material_mode_e; + +uint32_t R_VkMaterialModeFromRenderType(vk_render_type_e render_type); + struct rt_light_add_polygon_s; struct rt_model_s; @@ -120,7 +134,8 @@ qboolean R_RenderModelUpdate( const vk_render_model_t *model ); qboolean R_RenderModelUpdateMaterials( const vk_render_model_t *model, const int *geom_indices, int geom_indices_count); typedef struct { - vk_render_type_e render_type; + vk_render_type_e render_type; // TODO rename legacy + material_mode_e material_mode; // These are "consumed": copied into internal storage and can be pointers to stack vars const vec4_t *color; diff --git a/ref/vk/vk_rtx.h b/ref/vk/vk_rtx.h index 5bf2d2af..95078730 100644 --- a/ref/vk/vk_rtx.h +++ b/ref/vk/vk_rtx.h @@ -56,7 +56,7 @@ qboolean RT_ModelUpdate(struct rt_model_s *model, const struct vk_render_geometr qboolean RT_ModelUpdateMaterials(struct rt_model_s *model, const struct vk_render_geometry_s *geometries, int geometries_count, const int *geom_indices, int geom_indices_count); typedef struct { - int render_type; // TODO material_mode + int material_mode; const matrix3x4 *transform, *prev_transform; const vec4_t *color; diff --git a/ref/vk/vk_sprite.c b/ref/vk/vk_sprite.c index 4818eb3a..20e85df7 100644 --- a/ref/vk/vk_sprite.c +++ b/ref/vk/vk_sprite.c @@ -804,9 +804,11 @@ static void R_DrawSpriteQuad( const char *debug_name, const mspriteframe_t *fram const vk_render_type_e render_type = spriteRenderModeToRenderType(render_mode); const r_vk_material_t material_override = R_VkMaterialGetForTexture(texture); + const material_mode_e material_mode = R_VkMaterialModeFromRenderType(render_type); R_RenderModelDraw(&g_sprite.quad.model, (r_model_draw_t){ .render_type = render_type, + .material_mode = material_mode, .color = (const vec4_t*)color, .transform = &transform, .prev_transform = &transform, diff --git a/ref/vk/vk_studio.c b/ref/vk/vk_studio.c index ace22623..3372402f 100644 --- a/ref/vk/vk_studio.c +++ b/ref/vk/vk_studio.c @@ -2304,8 +2304,11 @@ static void R_StudioDrawPoints( void ) { Vector4Set(color, g_studio.blend, g_studio.blend, g_studio.blend, 1.f); // TODO r_model_draw_t.transform should be matrix3x4 + const vk_render_type_e render_type = studioRenderModeToRenderType(RI.currententity->curstate.rendermode); + const material_mode_e material_mode = R_VkMaterialModeFromRenderType(render_type); R_RenderModelDraw(&render_submodel->model, (r_model_draw_t){ - .render_type = studioRenderModeToRenderType(RI.currententity->curstate.rendermode), + .render_type = render_type, + .material_mode = material_mode, .color = &color, .transform = &g_studio_current.entmodel->transform, .prev_transform = &g_studio_current.entmodel->prev_transform, From 7839792964694ac4b646d89a616a6d46cac9ed9d Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Fri, 29 Sep 2023 12:39:36 -0400 Subject: [PATCH 13/16] vk: materials: fix bsp/mat file extension culling --- ref/vk/vk_materials.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index 866566ed..e910b925 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -268,7 +268,7 @@ void R_VkMaterialsReload( void ) { { const model_t *map = gEngine.pfnGetModelByIndex( 1 ); const char *filename = COM_FileWithoutPath(map->name); - const int no_ext_len = findFilenameExtension(map->name, -1); + const int no_ext_len = findFilenameExtension(filename, -1); loadMaterialsFromFileF("pbr/%s/%.*s.mat", map->name, no_ext_len, filename); } From e97691b27cbcd414d8ed66afaf4422f8762780be Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Fri, 29 Sep 2023 12:48:32 -0400 Subject: [PATCH 14/16] vk: patch: allow mapping texture to materials for brush entities ``` { "_xvk_ent_id" "39" "_xvk_map_material" "generic028 mirror" } ``` NOTES: - might severely degrade performance in some cases/under debug as we're doing N^2 search with strcmp for rendered entities. --- ref/vk/NOTES.md | 43 +++++++++++++++++++ ref/vk/vk_brush.c | 50 +++++++++++++--------- ref/vk/vk_mapents.c | 96 +++++++++++++++++++++++++++++++++---------- ref/vk/vk_mapents.h | 25 ++++++++--- ref/vk/vk_materials.c | 15 +++++++ ref/vk/vk_materials.h | 3 ++ ref/vk/vk_scene.c | 8 ++-- 7 files changed, 189 insertions(+), 51 deletions(-) diff --git a/ref/vk/NOTES.md b/ref/vk/NOTES.md index 12a3d456..6787f224 100644 --- a/ref/vk/NOTES.md +++ b/ref/vk/NOTES.md @@ -543,4 +543,47 @@ GAME SKILL LEVEL:1 .. and only then R_NewMap ``` +# 2023-09-28 E303 +## #526 +Replace textures for specific brush entities. +For a single texture it might be as easy as: +``` +{ + "_xvk_ent_id" "39" + "_xvk_texture" "generic028" + "_xvk_material" "generic_metal1" +} +``` +For multiple replacements: +0. Multiple entries +``` +{ + "_xvk_ent_id" "39" + "_xvk_texture" "generic028" + "_xvk_material" "generic_metal1" +} + +{ + "_xvk_ent_id" "39" + "_xvk_texture" "generic029" + "_xvk_material" "generic_metal2" +} +``` + +1. Pairwise +``` +{ + "_xvk_ent_id" "39" + "_xvk_texture" "generic028 generic029 ..." + "_xvk_material" "generic_metal1 generic_metal2 ..." +} +``` + +2. Pair list <-- preferred +``` +{ + "_xvk_ent_id" "39" + "_xvk_texture_material" "generic028 generic_metal1 generic029 generic_metal2 ... ..." +} +``` diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 852300cb..30b96f44 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -1053,6 +1053,17 @@ static qboolean getSmoothedNormalFor(const model_t* mod, int vertex_index, int s return true; } +static const xvk_mapent_func_any_t *getModelFuncAnyPatch( const model_t *const mod ) { + for (int i = 0; i < g_map_entities.func_any_count; ++i) { + const xvk_mapent_func_any_t *const fw = g_map_entities.func_any + i; + if (Q_strcmp(mod->name, fw->model) == 0) { + return fw; + } + } + + return NULL; +} + static qboolean fillBrushSurfaces(fill_geometries_args_t args) { int vertex_offset = 0; int num_geometries = 0; @@ -1064,6 +1075,8 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { connectVertices(args.mod); + const xvk_mapent_func_any_t *const entity_patch = getModelFuncAnyPatch(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) { @@ -1076,12 +1089,11 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { int index_count = 0; vec3_t tangent; const int orig_tex_id = surf->texinfo->texture->gl_texturenum; - int tex_id = surf->texinfo->texture->gl_texturenum; - const xvk_patch_surface_t *const psurf = R_VkPatchGetSurface(surface_index); - - if (t != tex_id) + if (t != orig_tex_id) continue; + int tex_id = orig_tex_id; + const xvk_patch_surface_t *const psurf = R_VkPatchGetSurface(surface_index); if (psurf && psurf->tex_id >= 0) tex_id = psurf->tex_id; @@ -1109,6 +1121,15 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { return false; } + if (entity_patch) { + for (int i = 0; i < entity_patch->matmap_count; ++i) { + if (entity_patch->matmap[i].from_tex == tex_id) { + tex_id = entity_patch->matmap[i].to_mat.index; + break; + } + } + } + VectorClear(model_geometry->emissive); model_geometry->surf_deprecate = surf; @@ -1234,17 +1255,6 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { 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; -} - static qboolean createRenderModel( const model_t *mod, vk_brush_model_t *bmodel, const model_sizes_t sizes ) { bmodel->geometry = R_GeometryRangeAlloc(sizes.num_vertices, sizes.num_indices); if (!bmodel->geometry.block_handle.size) { @@ -1414,8 +1424,8 @@ void R_VkBrushModelCollectEmissiveSurfaces( const struct model_s *mod, qboolean 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; + const xvk_mapent_func_any_t *func_any = getModelFuncAnyPatch(mod); + const qboolean is_static = is_worldmodel || (func_any && func_any->origin_patched); typedef struct { int model_surface_index; @@ -1487,14 +1497,14 @@ void R_VkBrushModelCollectEmissiveSurfaces( const struct model_s *mod, qboolean 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 + // func_any surfaces do not really belong to BSP+PVS system, so they can't be used // for lights visibility calculation directly. - if (func_wall) { + if (func_any && func_any->origin_patched) { // 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->origin[0], func_wall->origin[1], func_wall->origin[2]); + Matrix3x4_SetOrigin(m, func_any->origin[0], func_any->origin[1], func_any->origin[2]); polylight.transform_row = &m; } diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index 6317eafb..a8ba8366 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -138,8 +138,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 if (Q_strncmp(value, "func_", 5) == 0) { + *out = FuncAny; } else { *out = Ignored; } @@ -339,17 +339,17 @@ static void readWorldspawn( const entity_props_t *props ) { }; } -static void readFuncWall( const entity_props_t *const props, uint32_t have_fields, int props_count ) { - DEBUG("func_wall entity=%d model=\"%s\", props_count=%d", g_map_entities.entity_count, (have_fields & Field_model) ? props->model : "N/A", props_count); +static void readFuncAny( const entity_props_t *const props, uint32_t have_fields, int props_count ) { + DEBUG("func_any entity=%d model=\"%s\", props_count=%d", 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) { - ERR("Too many func_wall entities, max supported = %d", MAX_FUNC_WALL_ENTITIES); + if (g_map_entities.func_any_count >= MAX_FUNC_ANY_ENTITIES) { + ERR("Too many func_any entities, max supported = %d", MAX_FUNC_ANY_ENTITIES); return; } - xvk_mapent_func_wall_t *const e = g_map_entities.func_walls + g_map_entities.func_walls_count; + xvk_mapent_func_any_t *const e = g_map_entities.func_any + g_map_entities.func_any_count; - *e = (xvk_mapent_func_wall_t){0}; + *e = (xvk_mapent_func_any_t){0}; Q_strncpy( e->model, props->model, sizeof( e->model )); @@ -376,10 +376,10 @@ static void readFuncWall( const entity_props_t *const props, uint32_t have_field 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, + .class = FuncAny, + .index = g_map_entities.func_any_count, }; - ++g_map_entities.func_walls_count; + ++g_map_entities.func_any_count; } static void addPatchSurface( const entity_props_t *props, uint32_t have_fields ) { @@ -483,15 +483,67 @@ static void patchLightEntity( const entity_props_t *props, int ent_id, uint32_t fillLightFromProps(light, props, have_fields, true, ent_id); } -static void patchFuncWallEntity( const entity_props_t *props, uint32_t have_fields, int index ) { +static void patchFuncAnyEntity( 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; + ASSERT(index < g_map_entities.func_any_count); + xvk_mapent_func_any_t *const e = g_map_entities.func_any + index; - if (have_fields & Field_origin) - VectorCopy(props->origin, fw->origin); + if (have_fields & Field_origin) { + VectorCopy(props->origin, e->origin); + e->origin_patched = true; - DEBUG("Patching ent=%d func_wall=%d %f %f %f", fw->entity_index, index, fw->origin[0], fw->origin[1], fw->origin[2]); + DEBUG("Patching ent=%d func_any=%d %f %f %f", e->entity_index, index, e->origin[0], e->origin[1], e->origin[2]); + } + + if (have_fields & Field__xvk_map_material) { + const char *s = props->_xvk_map_material; + while (*s) { + while (*s && isspace(*s)) ++s; // skip space + const char *from_begin = s; + while (*s && !isspace(*s)) ++s; // find first space or end + const int from_len = s - from_begin; + if (!from_len) + break; + + while (*s && isspace(*s)) ++s; // skip space + const char *to_begin = s; + while (*s && !isspace(*s)) ++s; // find first space or end + const int to_len = s - to_begin; + if (!to_len) + break; + + string from_tex, to_mat; + Q_strncpy(from_tex, from_begin, Q_min(sizeof from_tex, from_len + 1)); + Q_strncpy(to_mat, to_begin, Q_min(sizeof to_mat, to_len + 1)); + + const int from_tex_index = XVK_FindTextureNamedLike(from_tex); + const r_vk_material_ref_t to_mat_ref = R_VkMaterialGetForName(to_mat); + + DEBUG("Adding mapping from tex \"%s\"(%d) to mat \"%s\"(%d) for entity=%d", + from_tex, from_tex_index, to_mat, to_mat_ref.index, e->entity_index); + + if (from_tex_index <= 0) { + ERR("When patching entity=%d couldn't find map-from texture \"%s\"", e->entity_index, from_tex); + continue; + } + + if (to_mat_ref.index <= 0) { + ERR("When patching entity=%d couldn't find map-to material \"%s\"", e->entity_index, to_mat); + continue; + } + + if (e->matmap_count == MAX_MATERIAL_MAPPINGS) { + ERR("Cannot map tex \"%s\"(%d) to mat \"%s\"(%d) for entity=%d: too many mappings, " + "consider increasing MAX_MATERIAL_MAPPINGS", + from_tex, from_tex_index, to_mat, to_mat_ref.index, e->entity_index); + continue; + } + + e->matmap[e->matmap_count].from_tex = from_tex_index; + e->matmap[e->matmap_count].to_mat = to_mat_ref; + ++e->matmap_count; + } + } } static void patchEntity( const entity_props_t *props, uint32_t have_fields ) { @@ -511,8 +563,8 @@ static void patchEntity( const entity_props_t *props, uint32_t have_fields ) { case LightEnvironment: patchLightEntity(props, ei, have_fields, ref->index); break; - case FuncWall: - patchFuncWallEntity(props, have_fields, ref->index); + case FuncAny: + patchFuncAnyEntity(props, have_fields, ref->index); break; default: WARN("vk_mapents: trying to patch unsupported entity %d class %d", ei, ref->class); @@ -593,8 +645,8 @@ static void parseEntities( char *string, qboolean is_patch ) { readWorldspawn( &values ); break; - case FuncWall: - readFuncWall( &values, have_fields, props_count ); + case FuncAny: + readFuncAny( &values, have_fields, props_count ); break; case Unknown: @@ -735,7 +787,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; + g_map_entities.func_any_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) diff --git a/ref/vk/vk_mapents.h b/ref/vk/vk_mapents.h index 25698682..e31b9077 100644 --- a/ref/vk/vk_mapents.h +++ b/ref/vk/vk_mapents.h @@ -1,8 +1,13 @@ #pragma once +#include "vk_materials.h" + #include "xash3d_types.h" #include "const.h" // typedef word, needed for bspfile.h #include "bspfile.h" // MAX_MAP_ENTITIES +// TODO string_view instead of string. map entities string/buffer is supposed to be alive for the entire map duration +// NOTE that the above is not true for string in patches. but we can change that in parsePatches + #define ENT_PROP_LIST(X) \ X(0, vec3_t, origin, Vec3) \ X(1, vec3_t, angles, Vec3) \ @@ -29,6 +34,7 @@ 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) \ + X(25, string, _xvk_map_material, String) \ /* NOTE: not used X(22, int, rendermode, Int) \ @@ -44,7 +50,7 @@ typedef enum { LightSpot, LightEnvironment, Worldspawn, - FuncWall, + FuncAny, Ignored, Xvk_Target, } class_name_e; @@ -99,13 +105,22 @@ typedef struct { string model; vec3_t origin; + qboolean origin_patched; + +#define MAX_MATERIAL_MAPPINGS 8 + int matmap_count; + struct { + int from_tex; + r_vk_material_ref_t to_mat; + } matmap[MAX_MATERIAL_MAPPINGS]; + /* NOTE: not used. Might be needed for #118 in the future. int rendermode, renderamt, renderfx; color24 rendercolor; struct cl_entity_s *ent; */ -} xvk_mapent_func_wall_t; +} xvk_mapent_func_any_t; typedef struct { class_name_e class; @@ -130,9 +145,9 @@ 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]; +#define MAX_FUNC_ANY_ENTITIES 1024 + int func_any_count; + xvk_mapent_func_any_t func_any[MAX_FUNC_ANY_ENTITIES]; // TODO find out how to read this from the engine, or make its size dynamic //#define MAX_MAP_ENTITIES 2048 diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index e910b925..c064b49d 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -303,3 +303,18 @@ r_vk_material_t R_VkMaterialGetForTexture( int tex_index ) { return g_materials.materials[tex_index]; } + +r_vk_material_ref_t R_VkMaterialGetForName( const char *name ) { + // TODO separate material table + // For now it depends on 1-to-1 mapping between materials and textures + return (r_vk_material_ref_t){.index = VK_FindTexture(name)}; +} + +r_vk_material_t R_VkMaterialGetForRef( r_vk_material_ref_t ref ) { + // TODO separate material table + // For now it depends on 1-to-1 mapping between materials and textures + ASSERT(ref.index >= 0); + ASSERT(ref.index < MAX_TEXTURES); + + return g_materials.materials[ref.index]; +} diff --git a/ref/vk/vk_materials.h b/ref/vk/vk_materials.h index b171a3e8..2c809e46 100644 --- a/ref/vk/vk_materials.h +++ b/ref/vk/vk_materials.h @@ -27,3 +27,6 @@ struct model_s; void R_VkMaterialsLoadForModel( const struct model_s* mod ); r_vk_material_t R_VkMaterialGetForTexture( int tex_id ); + +r_vk_material_ref_t R_VkMaterialGetForName( const char *name ); +r_vk_material_t R_VkMaterialGetForRef( r_vk_material_ref_t ref ); diff --git a/ref/vk/vk_scene.c b/ref/vk/vk_scene.c index d6ed7dad..6fc130f6 100644 --- a/ref/vk/vk_scene.c +++ b/ref/vk/vk_scene.c @@ -591,11 +591,11 @@ static void drawEntity( cl_entity_t *ent, int render_mode ) case mod_brush: R_RotateForEntity( model, ent ); - // If this is potentially a func_wall model + // If this is potentially a func_any model if (ent->model->name[0] == '*') { - 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) { + for (int i = 0; i < g_map_entities.func_any_count; ++i) { + xvk_mapent_func_any_t *const fw = g_map_entities.func_any + i; + if (Q_strcmp(ent->model->name, fw->model) == 0 && fw->origin_patched) { /* DEBUG("ent->index=%d (%s) mapent:%d off=%f %f %f", */ /* ent->index, ent->model->name, fw->entity_index, */ /* fw->origin[0], fw->origin[1], fw->origin[2]); */ From 4e01947b6bc70dd94cbe65f78fee62534139faf4 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Fri, 29 Sep 2023 13:02:51 -0400 Subject: [PATCH 15/16] vk: fix crashing on reloading materials Needed to drop entity data cache before clearing studio model cache Fixes #577 --- ref/vk/vk_scene.c | 3 +++ ref/vk/vk_studio_model.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ref/vk/vk_scene.c b/ref/vk/vk_scene.c index 6fc130f6..efc99eaa 100644 --- a/ref/vk/vk_scene.c +++ b/ref/vk/vk_scene.c @@ -89,6 +89,9 @@ static void loadLights( const model_t *const map ) { // Clears all old map data static void mapLoadBegin( const model_t *const map ) { + VK_EntityDataClear(); + + // Depends on VK_EntityDataClear() R_StudioCacheClear(); R_GeometryBuffer_MapClear(); diff --git a/ref/vk/vk_studio_model.c b/ref/vk/vk_studio_model.c index 3b804391..950fed48 100644 --- a/ref/vk/vk_studio_model.c +++ b/ref/vk/vk_studio_model.c @@ -285,7 +285,8 @@ r_studio_submodel_render_t *studioSubmodelRenderModelAcquire(r_studio_submodel_i } subinfo->render_refcount++; - DEBUG("%s: submodel=%p(%s) \"%s\" rendermodel=%p refcount=%d", __FUNCTION__, subinfo->submodel_key, mode, subinfo->submodel_key->name, render, subinfo->render_refcount); + DEBUG("%s: submodel=%p(%s) %s rendermodel=%p refcount=%d", + __FUNCTION__, subinfo->submodel_key, subinfo->submodel_key->name, mode, render, subinfo->render_refcount); return render; } @@ -297,7 +298,8 @@ void studioSubmodelRenderModelRelease(r_studio_submodel_render_t *render_submode render_submodel->_.info->render_refcount--; const r_studio_submodel_info_t* const subinfo = render_submodel->_.info; - DEBUG("%s: submodel=%p(%s) rendermodel=%p refcount=%d", __FUNCTION__, subinfo->submodel_key, subinfo->submodel_key->name, render_submodel, subinfo->render_refcount); + DEBUG("%s: submodel=%p(%s) rendermodel=%p refcount=%d", + __FUNCTION__, subinfo->submodel_key, subinfo->submodel_key->name, render_submodel, subinfo->render_refcount); if (!render_submodel->_.info->is_dynamic) return; From 8ff89bd5efc12714fe7c58213bdde8a108be9025 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Fri, 29 Sep 2023 13:58:02 -0400 Subject: [PATCH 16/16] vk: materials: add rendermode-specific overrides Example: ```c1a0d.mat { "for_rendermode" "kRenderTransTexture" "for" "table2" "basecolor_map" "/colors/white.png" "metal_map" "/colors/white.png" "roughness_map" "/colors/black.png" } ``` Fixes #213 --- ref/vk/vk_brush.c | 15 +++++++++-- ref/vk/vk_mapents.c | 18 ++++++++++--- ref/vk/vk_mapents.h | 8 ++++-- ref/vk/vk_materials.c | 62 +++++++++++++++++++++++++++++++++++++++++-- ref/vk/vk_materials.h | 2 ++ 5 files changed, 96 insertions(+), 9 deletions(-) diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index 30b96f44..1f3bec6a 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -1121,19 +1121,30 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { return false; } + qboolean material_assigned = false; if (entity_patch) { for (int i = 0; i < entity_patch->matmap_count; ++i) { if (entity_patch->matmap[i].from_tex == tex_id) { - tex_id = entity_patch->matmap[i].to_mat.index; + model_geometry->material = R_VkMaterialGetForTexture(entity_patch->matmap[i].to_mat.index); + material_assigned = true; break; } } + + if (!material_assigned && entity_patch->rendermode > 0) { + model_geometry->material = R_VkMaterialGetEx(tex_id, entity_patch->rendermode); + material_assigned = true; + } + } + + if (!material_assigned) { + model_geometry->material = R_VkMaterialGetForTexture(tex_id); + material_assigned = true; } VectorClear(model_geometry->emissive); model_geometry->surf_deprecate = surf; - model_geometry->material = R_VkMaterialGetForTexture(tex_id); model_geometry->ye_olde_texture = orig_tex_id; model_geometry->vertex_offset = args.base_vertex_offset; diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index a8ba8366..e37baf68 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -339,6 +339,17 @@ static void readWorldspawn( const entity_props_t *props ) { }; } +int R_VkRenderModeFromString( const char *s ) { +#define CHECK_IF_MODE(mode) if (Q_strcmp(s, #mode) == 0) { return mode; } + CHECK_IF_MODE(kRenderNormal) + else CHECK_IF_MODE(kRenderTransColor) + else CHECK_IF_MODE(kRenderTransTexture) + else CHECK_IF_MODE(kRenderGlow) + else CHECK_IF_MODE(kRenderTransAlpha) + else CHECK_IF_MODE(kRenderTransAdd) + return -1; +} + static void readFuncAny( const entity_props_t *const props, uint32_t have_fields, int props_count ) { DEBUG("func_any entity=%d model=\"%s\", props_count=%d", g_map_entities.entity_count, (have_fields & Field_model) ? props->model : "N/A", props_count); @@ -350,9 +361,13 @@ static void readFuncAny( const entity_props_t *const props, uint32_t have_fields xvk_mapent_func_any_t *const e = g_map_entities.func_any + g_map_entities.func_any_count; *e = (xvk_mapent_func_any_t){0}; + e->rendermode = -1; Q_strncpy( e->model, props->model, sizeof( e->model )); + if (have_fields & Field_rendermode) + e->rendermode = props->rendermode; + /* NOTE: not used e->rendercolor.r = 255; e->rendercolor.g = 255; @@ -364,9 +379,6 @@ static void readFuncAny( const entity_props_t *const props, uint32_t have_fields 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]; diff --git a/ref/vk/vk_mapents.h b/ref/vk/vk_mapents.h index e31b9077..b1e5c3d9 100644 --- a/ref/vk/vk_mapents.h +++ b/ref/vk/vk_mapents.h @@ -35,9 +35,9 @@ X(23, int_array_t, _xvk_smoothing_excluded_pairs, IntArray) \ X(24, int_array_t, _xvk_smoothing_group, IntArray) \ X(25, string, _xvk_map_material, String) \ + X(26, int, rendermode, Int) \ /* NOTE: not used - X(22, int, rendermode, Int) \ X(23, int, renderamt, Int) \ X(24, vec3_t, rendercolor, Vec3) \ X(25, int, renderfx, Int) \ @@ -114,8 +114,10 @@ typedef struct { r_vk_material_ref_t to_mat; } matmap[MAX_MATERIAL_MAPPINGS]; + int rendermode; + /* NOTE: not used. Might be needed for #118 in the future. - int rendermode, renderamt, renderfx; + int renderamt, renderfx; color24 rendercolor; struct cl_entity_s *ent; @@ -203,3 +205,5 @@ typedef struct { const xvk_patch_surface_t* R_VkPatchGetSurface( int surface_index ); +// -1 if failed +int R_VkRenderModeFromString( const char *s ); diff --git a/ref/vk/vk_materials.c b/ref/vk/vk_materials.c index c064b49d..13daeca7 100644 --- a/ref/vk/vk_materials.c +++ b/ref/vk/vk_materials.c @@ -25,8 +25,19 @@ static r_vk_material_t k_default_material = { .set = false, }; +#define MAX_RENDERMODE_MATERIALS 32 +typedef struct { + struct { + int tex_id; + r_vk_material_t mat; + } materials[MAX_RENDERMODE_MATERIALS]; + int count; +} r_vk_material_per_mode_t; + static struct { r_vk_material_t materials[MAX_TEXTURES]; + + r_vk_material_per_mode_t rendermode[kRenderTransAdd+1]; } g_materials; static struct { @@ -78,6 +89,8 @@ static void loadMaterialsFromFile( const char *filename, int depth ) { string basecolor_map, normal_map, metal_map, roughness_map; + int rendermode = 0; + DEBUG("Loading materials from %s (exists=%d)", filename, data != 0); if ( !data ) @@ -105,6 +118,7 @@ static void loadMaterialsFromFile( const char *filename, int depth ) { create = false; metalness_set = false; basecolor_map[0] = normal_map[0] = metal_map[0] = roughness_map[0] = '\0'; + rendermode = 0; continue; } @@ -146,8 +160,28 @@ static void loadMaterialsFromFile( const char *filename, int depth ) { DEBUG("Creating%s material for texture %s(%d)", create?" new":"", findTexture(current_material_index)->name, current_material_index); - g_materials.materials[current_material_index] = current_material; - g_materials.materials[current_material_index].set = true; + // Assign rendermode-specific materials + if (rendermode > 0) { + r_vk_material_per_mode_t* const rm = g_materials.rendermode + rendermode; + if (rm->count == COUNTOF(rm->materials)) { + ERR("Too many rendermode/tex_id mappings"); + continue; + } + + DEBUG("Adding material %d for rendermode %d", current_material_index, rendermode); + + // TODO proper texid-vs-material-index + rm->materials[rm->count].tex_id = current_material_index; + rm->materials[rm->count].mat = current_material; + rm->materials[rm->count].mat.set = true; + rm->count++; + } else { + DEBUG("Creating%s material for texture %s(%d)", create?" new":"", + findTexture(current_material_index)->name, current_material_index); + + g_materials.materials[current_material_index] = current_material; + g_materials.materials[current_material_index].set = true; + } continue; } @@ -193,6 +227,11 @@ static void loadMaterialsFromFile( const char *filename, int depth ) { sscanf(value, "%f", ¤t_material.normal_scale); } else if (Q_stricmp(key, "base_color") == 0) { sscanf(value, "%f %f %f %f", ¤t_material.base_color[0], ¤t_material.base_color[1], ¤t_material.base_color[2], ¤t_material.base_color[3]); + } else if (Q_stricmp(key, "for_rendermode") == 0) { + rendermode = R_VkRenderModeFromString(value); + if (rendermode < 0) + ERR("Invalid rendermode \"%s\"", value); + ASSERT(rendermode < COUNTOF(g_materials.rendermode[0].materials)); } else { ERR("Unknown material key \"%s\" on line `%.*s`", key, (int)(pos - line_begin), line_begin); continue; @@ -231,6 +270,9 @@ void R_VkMaterialsReload( void ) { memset(&g_stats, 0, sizeof(g_stats)); const uint64_t begin_time_ns = aprof_time_now_ns(); + for (int i = 0; i < COUNTOF(g_materials.rendermode); ++i) + g_materials.rendermode[i].count = 0; + k_default_material.tex_metalness = tglob.blackTexture; k_default_material.tex_roughness = tglob.whiteTexture; @@ -318,3 +360,19 @@ r_vk_material_t R_VkMaterialGetForRef( r_vk_material_ref_t ref ) { return g_materials.materials[ref.index]; } + +r_vk_material_t R_VkMaterialGetEx( int tex_id, int rendermode ) { + DEBUG("Getting material for tex_id=%d rendermode=%d", tex_id, rendermode); + + if (rendermode > 0) { + ASSERT(rendermode < COUNTOF(g_materials.rendermode)); + const r_vk_material_per_mode_t* const mode = &g_materials.rendermode[rendermode]; + for (int i = 0; i < mode->count; ++i) { + if (mode->materials[i].tex_id == tex_id) + return mode->materials[i].mat; + } + } + + DEBUG("Fallback to regular tex_id=%d", tex_id); + return R_VkMaterialGetForTexture(tex_id); +} diff --git a/ref/vk/vk_materials.h b/ref/vk/vk_materials.h index 2c809e46..16844902 100644 --- a/ref/vk/vk_materials.h +++ b/ref/vk/vk_materials.h @@ -28,5 +28,7 @@ void R_VkMaterialsLoadForModel( const struct model_s* mod ); r_vk_material_t R_VkMaterialGetForTexture( int tex_id ); +r_vk_material_t R_VkMaterialGetEx( int tex_id, int rendermode ); + r_vk_material_ref_t R_VkMaterialGetForName( const char *name ); r_vk_material_t R_VkMaterialGetForRef( r_vk_material_ref_t ref );