rt: group Material and ModelMetadata in Kusok

They have mismatching frequencies. ModelMetadata should be per-Model, there should be only a few dozen of these.
There maybe hundreds (or even thousands) of materials, but one material can be still referenced by many kusochki.

This only moves fields into new structs, which still belong to Kusok. The real extraction will happen later, see #52.
This commit is contained in:
Ivan 'provod' Avdeev 2023-04-24 10:44:01 -07:00 committed by Ivan Avdeev
parent aab689a37b
commit 5b370509fe
13 changed files with 90 additions and 74 deletions

View File

@ -16,19 +16,21 @@ layout(location = PAYLOAD_LOCATION_ADDITIVE) rayPayloadInEXT RayPayloadAdditive
hitAttributeEXT vec2 bary;
void main() {
const int instance_kusochki_offset = gl_InstanceCustomIndexEXT;
const int kusok_index = instance_kusochki_offset + gl_GeometryIndexEXT;
const uint first_index_offset = getKusok(kusok_index).index_offset + gl_PrimitiveID * 3;
const int instance_kusochki_offset = gl_InstanceCustomIndexEXT;
const int kusok_index = instance_kusochki_offset + gl_GeometryIndexEXT;
const uint first_index_offset = getKusok(kusok_index).index_offset + gl_PrimitiveID * 3;
const uint vi1 = uint(getIndex(first_index_offset+0)) + getKusok(kusok_index).vertex_offset;
const uint vi2 = uint(getIndex(first_index_offset+1)) + getKusok(kusok_index).vertex_offset;
const uint vi3 = uint(getIndex(first_index_offset+2)) + getKusok(kusok_index).vertex_offset;
const uint vi1 = uint(getIndex(first_index_offset+0)) + getKusok(kusok_index).vertex_offset;
const uint vi2 = uint(getIndex(first_index_offset+1)) + getKusok(kusok_index).vertex_offset;
const uint vi3 = uint(getIndex(first_index_offset+2)) + getKusok(kusok_index).vertex_offset;
const vec2 texture_uv = GET_VERTEX(vi1).gl_tc * (1. - bary.x - bary.y) + GET_VERTEX(vi2).gl_tc * bary.x + GET_VERTEX(vi3).gl_tc * bary.y + push_constants.time * getKusok(kusok_index).uv_speed;
// TODO mips
const uint tex_index = getKusok(kusok_index).tex_base_color;
const vec4 texture_color = texture(textures[nonuniformEXT(tex_index)], texture_uv);
const vec3 color = texture_color.rgb * getKusok(kusok_index).color.rgb * texture_color.a * getKusok(kusok_index).color.a;
const vec2 texture_uv = GET_VERTEX(vi1).gl_tc * (1. - bary.x - bary.y) + GET_VERTEX(vi2).gl_tc * bary.x + GET_VERTEX(vi3).gl_tc * bary.y;
const Kusok kusok = getKusok(kusok_index);
// TODO mips
const uint tex_index = kusok.material.tex_base_color;
const vec4 texture_color = texture(textures[nonuniformEXT(tex_index)], texture_uv);
const vec3 color = texture_color.rgb * kusok.model.color.rgb * texture_color.a * kusok.model.color.a;
const float overshoot = gl_HitTEXT - payload_additive.ray_distance;

View File

@ -19,7 +19,7 @@ void main() {
const uint vi3 = uint(getIndex(first_index_offset+2)) + getKusok(kusok_index).vertex_offset;
const vec2 texture_uv = GET_VERTEX(vi1).gl_tc * (1. - bary.x - bary.y) + GET_VERTEX(vi2).gl_tc * bary.x + GET_VERTEX(vi3).gl_tc * bary.y;
const uint tex_index = getKusok(kusok_index).tex_base_color;
const uint tex_index = getKusok(kusok_index).material.tex_base_color;
const vec4 texture_color = texture(textures[nonuniformEXT(tex_index)], texture_uv);
if (texture_color.a < 0.1) {

View File

@ -71,7 +71,7 @@ bool getHit(vec3 origin, vec3 direction, inout RayPayloadPrimary payload) {
// Separate pass could be more efficient as it'd be doing the same thing for every invocation.
// 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).tex_base_color;
const uint tex_base_color = getKusok(geom.kusok_index).material.tex_base_color;
const vec4 texture_color = texture(textures[nonuniformEXT(tex_base_color)], geom.uv);
const float alpha_mask_threshold = .1f;

View File

@ -63,7 +63,7 @@ bool shadowTestAlphaMask(vec3 pos, vec3 dir, float dist) {
};
const vec2 bary = rayQueryGetIntersectionBarycentricsEXT(rq, false);
const vec2 uv = baryMix(uvs[0], uvs[1], uvs[2], bary);
const vec4 texture_color = texture(textures[nonuniformEXT(kusok.tex_base_color)], uv);
const vec4 texture_color = texture(textures[nonuniformEXT(kusok.material.tex_base_color)], uv);
const float alpha_mask_threshold = .1f;
if (texture_color.a >= alpha_mask_threshold) {
@ -141,7 +141,7 @@ bool shadowedSky(vec3 pos, vec3 dir) {
const Kusok kusok = getKusok(kusok_index);
// TODO this flag can be encoded into custom index, so that we'd need no extra indirection
if ((kusok.flags & KUSOK_MATERIAL_FLAG_SKYBOX) == 0)
if ((kusok.material.flags & KUSOK_MATERIAL_FLAG_SKYBOX) == 0)
return true;
}

View File

@ -21,7 +21,7 @@ const float alpha_mask_threshold = .1f;
void main() {
const Geometry geom = readHitGeometry(bary, ubo.ubo.ray_cone_width);
const uint tex_index = getKusok(geom.kusok_index).tex_base_color;
const uint tex_index = getKusok(geom.kusok_index).material.tex_base_color;
const vec4 texture_color = texture(textures[nonuniformEXT(tex_index)], geom.uv);
if (texture_color.a < alpha_mask_threshold) {

View File

@ -66,33 +66,51 @@ LIST_SPECIALIZATION_CONSTANTS(DECLARE_SPECIALIZATION_CONSTANT)
#define KUSOK_MATERIAL_FLAG_SKYBOX (1<<0)
#define KUSOK_MATERIAL_FLAG_FIXME_GLOW (1<<1)
struct Kusok {
uint index_offset;
uint vertex_offset;
uint triangles;
struct Material {
uint flags;
// Material
uint tex_base_color;
// TODO the color is per-model, not per-kusok
vec4 color;
vec3 emissive;
// TODO can be combined into a single texture
uint tex_roughness;
vec2 uv_speed; // for conveyors; TODO this can definitely be done in software more efficiently (there only a handful of these per map)
uint tex_metalness;
uint tex_normalmap;
// TODO:
// uint tex_emissive;
// uint tex_detail;
float roughness;
float metalness;
float normal_scale;
uint flags;
};
// TODO per-model
struct ModelMetadata {
vec4 color;
mat4 prev_transform;
};
struct Kusok {
// Geometry data
uint index_offset;
uint vertex_offset;
uint triangles;
// material below consists of scalar fields only, so it's not aligned to vec4.
// Alignt it here to vec4 explicitly, so that later vector fields are properly aligned (for simplicity).
uint _padding0;
// TODO reference into material table
STRUCT Material material;
// Per-kusok because individual surfaces can be patched
vec3 emissive;
PAD(1)
// TODO move into a separate model array, and reference it by gl_GeometryIndexEXT/rayQueryGetIntersectionGeometryIndexEXT
STRUCT ModelMetadata model;
};
struct PointLight {
vec4 origin_r;
vec4 color_stopdot;

View File

@ -86,7 +86,7 @@ void main() {
// Separate pass could be more efficient as it'd be doing the same thing for every invocation.
// 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).tex_base_color;
const uint tex_base_color = getKusok(geom.kusok_index).material.tex_base_color;
const vec4 texture_color = texture(textures[nonuniformEXT(tex_base_color)], geom.uv);
const float alpha_mask_threshold = .1f;

View File

@ -28,17 +28,16 @@ void main() {
payload.prev_pos_t = vec4(geom.prev_pos, 0.);
const Kusok kusok = getKusok(geom.kusok_index);
const uint tex_base_color = kusok.tex_base_color;
if ((tex_base_color & KUSOK_MATERIAL_FLAG_SKYBOX) != 0) {
if ((kusok.material.flags & KUSOK_MATERIAL_FLAG_SKYBOX) != 0) {
payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, gl_WorldRayDirectionEXT).rgb);
return;
} else {
payload.base_color_a = sampleTexture(tex_base_color, geom.uv, geom.uv_lods) * kusok.color;
payload.material_rmxx.r = (kusok.tex_roughness > 0) ? sampleTexture(kusok.tex_roughness, geom.uv, geom.uv_lods).r : kusok.roughness;
payload.material_rmxx.g = (kusok.tex_metalness > 0) ? sampleTexture(kusok.tex_metalness, geom.uv, geom.uv_lods).r : kusok.metalness;
payload.base_color_a = sampleTexture(kusok.material.tex_base_color, geom.uv, geom.uv_lods) * kusok.model.color;
payload.material_rmxx.r = sampleTexture(kusok.material.tex_roughness, geom.uv, geom.uv_lods).r * kusok.material.roughness;
payload.material_rmxx.g = sampleTexture(kusok.material.tex_metalness, geom.uv, geom.uv_lods).r * kusok.material.metalness;
const uint tex_normal = kusok.tex_normalmap;
const uint tex_normal = kusok.material.tex_normalmap;
vec3 T = geom.tangent;
if (tex_normal > 0 && dot(T,T) > .5) {
T = normalize(T - dot(T, geom.normal_shading) * geom.normal_shading);

View File

@ -28,24 +28,25 @@ void primaryRayHit(rayQueryEXT rq, inout RayPayloadPrimary payload) {
payload.prev_pos_t = vec4(geom.prev_pos, 0.);
const Kusok kusok = getKusok(geom.kusok_index);
const Material material = kusok.material;
if ((kusok.flags & KUSOK_MATERIAL_FLAG_SKYBOX) != 0) {
if ((kusok.material.flags & KUSOK_MATERIAL_FLAG_SKYBOX) != 0) {
payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, rayDirection).rgb);
return;
} else {
payload.base_color_a = sampleTexture(kusok.tex_base_color, geom.uv, geom.uv_lods);
payload.material_rmxx.r = sampleTexture(kusok.tex_roughness, geom.uv, geom.uv_lods).r * kusok.roughness;
payload.material_rmxx.g = sampleTexture(kusok.tex_metalness, geom.uv, geom.uv_lods).r * kusok.metalness;
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;
#ifndef RAY_BOUNCE
const uint tex_normal = kusok.tex_normalmap;
const uint tex_normal = material.tex_normalmap;
vec3 T = geom.tangent;
if (tex_normal > 0 && dot(T,T) > .5) {
T = normalize(T - dot(T, geom.normal_shading) * geom.normal_shading);
const vec3 B = normalize(cross(geom.normal_shading, T));
const mat3 TBN = mat3(T, B, geom.normal_shading);
vec3 tnorm = sampleTexture(tex_normal, geom.uv, geom.uv_lods).xyz * 2. - 1.; // TODO is this sampling correct for normal data?
tnorm.xy *= kusok.normal_scale;
tnorm.xy *= material.normal_scale;
tnorm.z = sqrt(max(0., 1. - dot(tnorm.xy, tnorm.xy)));
geom.normal_shading = normalize(TBN * tnorm);
}
@ -68,7 +69,7 @@ void primaryRayHit(rayQueryEXT rq, inout RayPayloadPrimary payload) {
payload.emissive.rgb = payload.base_color_a.rgb;
#endif
payload.base_color_a *= kusok.color;
payload.base_color_a *= kusok.model.color;
}
#endif // ifndef RAY_PRIMARY_HIT_GLSL_INCLUDED

View File

@ -9,7 +9,8 @@ layout(location = PAYLOAD_LOCATION_SHADOW) rayPayloadInEXT RayPayloadShadow payl
void main() {
const int instance_kusochki_offset = gl_InstanceCustomIndexEXT;
const int kusok_index = instance_kusochki_offset + gl_GeometryIndexEXT;
const uint tex_base_color = getKusok(kusok_index).tex_base_color;
const Kusok kusok = getKusok(kusok_index);
const uint tex_base_color = kusok.material.tex_base_color;
payload_shadow.hit_type = ((tex_base_color & KUSOK_MATERIAL_FLAG_SKYBOX) == 0) ? SHADOW_HIT : SHADOW_SKY ;
payload_shadow.hit_type = ((kusok.material.flags & KUSOK_MATERIAL_FLAG_SKYBOX) == 0) ? SHADOW_HIT : SHADOW_SKY ;
}

View File

@ -89,9 +89,9 @@ Geometry readHitGeometry(vec2 bary, float ray_cone_width) {
};
const vec3 prev_pos[3] = {
(kusok.prev_transform * vec4(GET_VERTEX(vi1).prev_pos, 1.f)).xyz,
(kusok.prev_transform * vec4(GET_VERTEX(vi2).prev_pos, 1.f)).xyz,
(kusok.prev_transform * vec4(GET_VERTEX(vi3).prev_pos, 1.f)).xyz,
(kusok.model.prev_transform * vec4(GET_VERTEX(vi1).prev_pos, 1.f)).xyz,
(kusok.model.prev_transform * vec4(GET_VERTEX(vi2).prev_pos, 1.f)).xyz,
(kusok.model.prev_transform * vec4(GET_VERTEX(vi3).prev_pos, 1.f)).xyz,
};
const vec2 uvs[3] = {

View File

@ -15,15 +15,15 @@ vec3 traceAdditive(vec3 pos, vec3 dir, float L) {
const MiniGeometry geom = readCandidateMiniGeometry(rq);
const Kusok kusok = getKusok(geom.kusok_index);
const vec4 texture_color = texture(textures[nonuniformEXT(kusok.tex_base_color)], geom.uv);
const vec3 color = texture_color.rgb * kusok.emissive * texture_color.a * kusok.color.a * SRGBtoLINEAR(geom.color.rgb * geom.color.a);
const vec4 texture_color = texture(textures[nonuniformEXT(kusok.material.tex_base_color)], geom.uv);
const vec3 color = texture_color.rgb * kusok.emissive * texture_color.a * kusok.model.color.a * SRGBtoLINEAR(geom.color.rgb * geom.color.a);
const float hit_t = rayQueryGetIntersectionTEXT(rq, false);
const float overshoot = hit_t - L;
if (overshoot < 0.)
ret += color;
else if ((kusok.flags & KUSOK_MATERIAL_FLAG_FIXME_GLOW) != 0)
else if ((kusok.material.flags & KUSOK_MATERIAL_FLAG_FIXME_GLOW) != 0)
ret += color * smoothstep(additive_soft_overshoot, 0., overshoot);
}
return ret;

View File

@ -138,7 +138,7 @@ void XVK_RayModel_Validate( void ) {
for (int j = 0; j < num_geoms; j++) {
const vk_kusok_data_t *kusok = kusochki + j;
const vk_texture_t *tex = findTexture(kusok->tex_base_color);
const vk_texture_t *tex = findTexture(kusok->material.tex_base_color);
ASSERT(tex);
ASSERT(tex->vk.image.view != VK_NULL_HANDLE);
@ -165,30 +165,32 @@ static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometr
kusok->index_offset = geom->index_offset;
kusok->triangles = geom->element_count / 3;
kusok->tex_base_color = mat->tex_base_color;
kusok->tex_roughness = mat->tex_roughness;
kusok->tex_metalness = mat->tex_metalness;
kusok->tex_normalmap = mat->tex_normalmap;
kusok->material = (struct Material){
.flags = 0,
kusok->roughness = mat->roughness;
kusok->metalness = mat->metalness;
kusok->normal_scale = mat->normal_scale;
.tex_base_color = mat->tex_base_color,
.tex_roughness = mat->tex_roughness,
.tex_metalness = mat->tex_metalness,
.tex_normalmap = mat->tex_normalmap,
kusok->flags = 0;
.roughness = mat->roughness,
.metalness = mat->metalness,
.normal_scale = mat->normal_scale,
};
// HACK until there is a proper mechanism for patching materials, see https://github.com/w23/xash3d-fwgs/issues/213
// FIXME also this erases previous roughness unconditionally
if (HACK_reflective) {
kusok->tex_roughness = tglob.blackTexture;
kusok->material.tex_roughness = tglob.blackTexture;
} else if (!mat->set && geom->material == kXVkMaterialChrome) {
kusok->tex_roughness = tglob.grayTexture;
kusok->material.tex_roughness = tglob.grayTexture;
}
if (geom->material == kXVkMaterialSky)
kusok->flags |= KUSOK_MATERIAL_FLAG_SKYBOX;
kusok->material.flags |= KUSOK_MATERIAL_FLAG_SKYBOX;
if (geom->material == kXVkMaterialEmissiveGlow)
kusok->flags |= KUSOK_MATERIAL_FLAG_FIXME_GLOW;
kusok->material.flags |= KUSOK_MATERIAL_FLAG_FIXME_GLOW;
{
vec4_t gcolor;
@ -196,17 +198,10 @@ static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometr
gcolor[1] = color[1] * mat->base_color[1];
gcolor[2] = color[2] * mat->base_color[2];
gcolor[3] = color[3] * mat->base_color[3];
Vector4Copy(gcolor, kusok->color);
Vector4Copy(gcolor, kusok->model.color);
}
VectorCopy(geom->emissive, kusok->emissive);
/* FIXME these should be done in a different way
if (geom->material == kXVkMaterialConveyor) {
computeConveyorSpeed( entcolor, geom->texture, kusok->uv_speed );
} else */ {
kusok->uv_speed[0] = kusok->uv_speed[1] = 0.f;
}
}
vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
@ -277,7 +272,7 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
};
applyMaterialToKusok(kusochki + i, mg, args.model->color, false);
Matrix4x4_LoadIdentity(kusochki[i].prev_transform);
Matrix4x4_LoadIdentity(kusochki[i].model.prev_transform);
}
R_VkStagingUnlock(kusok_staging.handle);
@ -475,7 +470,7 @@ void VK_RayFrameAddModel( vk_ray_model_t *model, const vk_render_model_t *render
applyMaterialToKusok(kusochki + i, geom, render_model->color, HACK_reflective);
Matrix4x4_ToArrayFloatGL(render_model->prev_transform, (float*)(kusochki + i)->prev_transform);
Matrix4x4_ToArrayFloatGL(render_model->prev_transform, (float*)(kusochki + i)->model.prev_transform);
}
/* gEngine.Con_Reportf("model %s: geom=%d kuoffs=%d kustoff=%d kustsz=%d sthndl=%d\n", */