Files
SDL3CPlusPlus/scripts/shader_variants.lua

386 lines
12 KiB
Lua

local M = {}
local string_format = string.format
local function get_logger(log_debug)
if type(log_debug) == "function" then
return log_debug
end
return function()
end
end
local function resolve_color3(value, fallback)
if type(value) == "table" then
local r = tonumber(value[1])
local g = tonumber(value[2])
local b = tonumber(value[3])
if r and g and b then
return {r, g, b}
end
end
return {fallback[1], fallback[2], fallback[3]}
end
local function resolve_number_optional(value)
if type(value) == "number" then
return value
end
return nil
end
local function resolve_color3_optional(value)
if type(value) == "table" then
local r = tonumber(value[1])
local g = tonumber(value[2])
local b = tonumber(value[3])
if r and g and b then
return {r, g, b}
end
end
return nil
end
local function format_optional_number(value)
if type(value) == "number" then
return string_format("%.3f", value)
end
return "default"
end
local function format_optional_color(value)
if type(value) == "table"
and type(value[1]) == "number"
and type(value[2]) == "number"
and type(value[3]) == "number" then
return string_format("{%.2f, %.2f, %.2f}", value[1], value[2], value[3])
end
return "default"
end
local function build_shader_parameter_overrides(config, log_debug)
if type(config) ~= "table" or type(config.atmospherics) ~= "table" then
return {}
end
local atmospherics = config.atmospherics
local ambient_strength = resolve_number_optional(atmospherics.ambient_strength)
local light_intensity = resolve_number_optional(atmospherics.light_intensity)
local key_intensity = resolve_number_optional(atmospherics.key_light_intensity)
local fill_intensity = resolve_number_optional(atmospherics.fill_light_intensity)
local light_color = resolve_color3_optional(atmospherics.light_color)
local pbr_roughness = resolve_number_optional(atmospherics.pbr_roughness)
local pbr_metallic = resolve_number_optional(atmospherics.pbr_metallic)
local parameters = {}
local function apply_common(key)
if ambient_strength == nil and light_intensity == nil and light_color == nil then
return
end
local entry = {}
if ambient_strength ~= nil then
entry.ambient_strength = ambient_strength
end
if light_intensity ~= nil then
entry.light_intensity = light_intensity
end
if light_color ~= nil then
entry.light_color = light_color
end
parameters[key] = entry
end
apply_common("solid")
apply_common("floor")
apply_common("wall")
apply_common("ceiling")
if ambient_strength ~= nil or key_intensity ~= nil or fill_intensity ~= nil or light_intensity ~= nil then
local entry = {}
if ambient_strength ~= nil then
entry.ambient_strength = ambient_strength
end
if key_intensity ~= nil then
entry.key_intensity = key_intensity
elseif light_intensity ~= nil then
entry.key_intensity = light_intensity
end
if fill_intensity ~= nil then
entry.fill_intensity = fill_intensity
elseif light_intensity ~= nil then
entry.fill_intensity = light_intensity * 0.45
end
parameters.default = entry
end
if light_intensity ~= nil or light_color ~= nil or pbr_roughness ~= nil or pbr_metallic ~= nil then
local entry = {}
if light_intensity ~= nil then
entry.light_intensity = light_intensity
end
if light_color ~= nil then
entry.light_color = light_color
end
if pbr_roughness ~= nil then
entry.material_roughness = pbr_roughness
end
if pbr_metallic ~= nil then
entry.material_metallic = pbr_metallic
end
parameters.pbr = entry
end
if next(parameters) ~= nil then
log_debug("Shader lighting overrides: ambient=%s light_intensity=%s light_color=%s",
format_optional_number(ambient_strength),
format_optional_number(light_intensity),
format_optional_color(light_color))
end
return parameters
end
local function load_materialx_parameters(config, log_debug)
if type(config) ~= "table" then
return nil
end
local materialx = config.materialx
if type(materialx) ~= "table" then
return nil
end
local enabled = materialx.enabled
if type(materialx.parameters_enabled) == "boolean" then
enabled = materialx.parameters_enabled
end
if not enabled then
return nil
end
if type(materialx.document) ~= "string" or materialx.document == "" then
log_debug("MaterialX enabled but document path is missing")
return nil
end
if type(materialx_get_surface_parameters) ~= "function" then
log_debug("MaterialX loader unavailable (materialx_get_surface_parameters missing)")
return nil
end
local material_name = nil
if type(materialx.material) == "string" and materialx.material ~= "" then
material_name = materialx.material
end
local ok, result, err = pcall(materialx_get_surface_parameters, materialx.document, material_name)
if not ok then
log_debug("MaterialX parameter load failed: %s", tostring(result))
return nil
end
if result == nil then
log_debug("MaterialX parameter load failed: %s", tostring(err))
return nil
end
if type(result) ~= "table" then
log_debug("MaterialX parameter load returned unexpected result")
return nil
end
return result
end
local function resolve_skybox_color(config, default_color)
if type(config) ~= "table" then
return default_color
end
local atmospherics = config.atmospherics
if type(atmospherics) ~= "table" then
return default_color
end
return resolve_color3(atmospherics.sky_color, default_color)
end
local function build_static_cube_variants()
local fallback_vertex_source = [[
#version 450
layout(location = 0) in vec3 inPos;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec3 inColor;
layout(location = 0) out vec3 fragColor;
layout(push_constant) uniform PushConstants {
mat4 model;
mat4 viewProj;
mat4 view;
mat4 proj;
mat4 lightViewProj;
vec3 cameraPos;
float time;
float ambientStrength;
float fogDensity;
float fogStart;
float fogEnd;
vec3 fogColor;
float gamma;
float exposure;
int enableShadows;
int enableFog;
} pushConstants;
void main() {
fragColor = inColor;
gl_Position = pushConstants.viewProj * pushConstants.model * vec4(inPos, 1.0);
}
]]
local fallback_fragment_source = [[
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}
]]
return {
default = {
vertex_source = fallback_vertex_source,
fragment_source = fallback_fragment_source,
},
solid = {
vertex_source = fallback_vertex_source,
fragment_source = fallback_fragment_source,
},
floor = {
vertex_source = fallback_vertex_source,
fragment_source = fallback_fragment_source,
},
wall = {
vertex_source = fallback_vertex_source,
fragment_source = fallback_fragment_source,
},
ceiling = {
vertex_source = fallback_vertex_source,
fragment_source = fallback_fragment_source,
},
pbr = {
vertex_source = fallback_vertex_source,
fragment_source = fallback_fragment_source,
},
skybox = {
vertex_source = fallback_vertex_source,
fragment_source = fallback_fragment_source,
},
}
end
local function count_shader_variants(variants)
local count = 0
for _ in pairs(variants) do
count = count + 1
end
return count
end
function M.build_cube_variants(config, log_debug, base_skybox_color)
local logger = get_logger(log_debug)
local skybox_color = resolve_skybox_color(config, base_skybox_color or {0.04, 0.05, 0.08})
local shader_parameters = build_shader_parameter_overrides(config, logger)
local materialx_parameters = load_materialx_parameters(config, logger)
if materialx_parameters then
local entry = shader_parameters.pbr or {}
local albedo = resolve_color3_optional(materialx_parameters.material_albedo)
local roughness = resolve_number_optional(materialx_parameters.material_roughness)
local metallic = resolve_number_optional(materialx_parameters.material_metallic)
if albedo ~= nil then
entry.material_albedo = albedo
end
if roughness ~= nil then
entry.material_roughness = roughness
end
if metallic ~= nil then
entry.material_metallic = metallic
end
if next(entry) ~= nil then
shader_parameters.pbr = entry
logger("MaterialX PBR overrides: albedo=%s roughness=%s metallic=%s",
format_optional_color(albedo),
format_optional_number(roughness),
format_optional_number(metallic))
end
end
local ok, toolkit = pcall(require, "shader_toolkit")
if not ok then
logger("Shader toolkit unavailable: %s", tostring(toolkit))
return build_static_cube_variants(), skybox_color
end
local output_mode = "source"
local compile = false
local ok_generate, generated = pcall(toolkit.generate_cube_demo_variants,
{compile = compile, output_mode = output_mode, parameters = shader_parameters})
if not ok_generate then
logger("Shader generation failed: %s", tostring(generated))
return build_static_cube_variants(), skybox_color
end
local ok_skybox, skybox_variant = pcall(toolkit.generate_variant, {
key = "skybox",
template = "solid_color",
output_mode = output_mode,
compile = compile,
parameters = {color = skybox_color},
})
if ok_skybox then
generated.skybox = skybox_variant
else
logger("Skybox shader generation failed: %s", tostring(skybox_variant))
end
logger("Generated %d shader variants", count_shader_variants(generated))
return generated, skybox_color
end
function M.build_gui_variants()
local ok, toolkit = pcall(require, "shader_toolkit")
if not ok then
error("Shader toolkit unavailable: " .. tostring(toolkit))
end
local ok_generate, variant = pcall(toolkit.generate_variant, {
key = "default",
template = "gui_2d",
output_mode = "source",
compile = false,
})
if not ok_generate then
error("Shader generation failed: " .. tostring(variant))
end
return {default = variant}
end
function M.build_soundboard_variants()
local ok, toolkit = pcall(require, "shader_toolkit")
if not ok then
error("Shader toolkit unavailable: " .. tostring(toolkit))
end
local ok_generate, variant = pcall(toolkit.generate_variant, {
key = "default",
template = "cube_rainbow",
output_name = "cube",
output_mode = "source",
compile = false,
})
if not ok_generate then
error("Shader generation failed: " .. tostring(variant))
end
return {default = variant}
end
return M