improve lighting and trile editing

This commit is contained in:
Tuomas Katajisto 2026-02-27 20:20:02 +02:00
parent 9098550523
commit 3a7f19c89d
13 changed files with 5555 additions and 5066 deletions

View File

@ -277,7 +277,11 @@ add_resources_from_pack :: (pack: *Loaded_Pack) {
} }
free_resources_from_pack :: (pack: *Loaded_Pack) { free_resources_from_pack :: (pack: *Loaded_Pack) {
for pack.textures sg_destroy_image(it);
for *pack.audio array_free(it.data);
table_reset(*pack.textures);
table_reset(*pack.audio);
table_reset(*pack.animations);
} }
find_pack_by_name :: (name: string) -> (bool, Loaded_Pack) { find_pack_by_name :: (name: string) -> (bool, Loaded_Pack) {

View File

@ -77,6 +77,8 @@ draw_editor :: () {
#if OS != .WASM { #if OS != .WASM {
if !in_editor_view then return; if !in_editor_view then return;
bypass_postprocess = (current_editor_view == .Trile_Editor);
if current_editor_view == { if current_editor_view == {
case .Trile_Editor; case .Trile_Editor;
draw_trile_editor(); draw_trile_editor();

View File

@ -540,6 +540,7 @@ gen_rdm :: (quality: s32, include_water: bool, world: World) {
rdm_save_image_to_file :: (path: string, data: *float, width: s32, height: s32) { rdm_save_image_to_file :: (path: string, data: *float, width: s32, height: s32) {
#if OS != .WASM { #if OS != .WASM {
file :: #import "File";
builder: String_Builder; builder: String_Builder;
header := RDM_File_Header.{ header := RDM_File_Header.{
magic = RDM_FILE_MAGIC, magic = RDM_FILE_MAGIC,
@ -549,7 +550,7 @@ rdm_save_image_to_file :: (path: string, data: *float, width: s32, height: s32)
write_bytes(*builder, *header, size_of(RDM_File_Header)); write_bytes(*builder, *header, size_of(RDM_File_Header));
pixel_bytes := cast(s64) width * cast(s64) height * 4 * size_of(float); pixel_bytes := cast(s64) width * cast(s64) height * 4 * size_of(float);
write_bytes(*builder, data, pixel_bytes); write_bytes(*builder, data, pixel_bytes);
rdm_file.write_entire_file(path, builder_to_string(*builder)); file.write_entire_file(path, builder_to_string(*builder));
} }
} }

View File

@ -376,4 +376,33 @@ draw_trile_editor_ui :: (theme: *GR.Overall_Theme) {
} }
draw_picker(theme); draw_picker(theme);
// View mode buttons — bottom center of the 3D viewport.
btn_w := 11.0 * vw;
btn_h := cast(float) ui_h(4, 0);
bar_w := btn_w * 4.0;
bar_x := 100.0 * vw / 2.0 - bar_w / 2.0;
bar_y := 100.0 * vh - btn_h - 1.5 * vh;
bar_r : GR.Rect;
bar_r.x = bar_x; bar_r.y = bar_y; bar_r.w = bar_w; bar_r.h = btn_h;
ui_add_mouse_occluder(bar_r);
r2 : GR.Rect;
r2.x = bar_x; r2.y = bar_y; r2.w = btn_w; r2.h = btn_h;
if GR.button(r2, "Lit", *t_button_selectable(theme, trixel_view_mode == 0)) {
trixel_view_mode = 0;
}
r2.x += btn_w;
if GR.button(r2, "Normals", *t_button_selectable(theme, trixel_view_mode == 1)) {
trixel_view_mode = 1;
}
r2.x += btn_w;
if GR.button(r2, "Albedo", *t_button_selectable(theme, trixel_view_mode == 2)) {
trixel_view_mode = 2;
}
r2.x += btn_w;
if GR.button(r2, "Mixed", *t_button_selectable(theme, trixel_view_mode == 3)) {
trixel_view_mode = 3;
}
} }

View File

@ -1,6 +1,8 @@
camera: Camera; camera: Camera;
trixel_count : s32 = 0; trixel_count : s32 = 0;
trixel_view_mode : s32 = 0;
bypass_postprocess : bool = false;
trile_offsets : [..]s32; trile_offsets : [..]s32;
current_trile_offset_index : s32 = 0; current_trile_offset_index : s32 = 0;
@ -64,8 +66,11 @@ backend_update_trixels :: (cmd: Render_Command_Update_Trixels) {
trixels[trixel_count].pos.x = x * (1.0 / 16.0) + TRIXEL_SIZE_HALF; trixels[trixel_count].pos.x = x * (1.0 / 16.0) + TRIXEL_SIZE_HALF;
trixels[trixel_count].pos.y = y * (1.0 / 16.0) + TRIXEL_SIZE_HALF; trixels[trixel_count].pos.y = y * (1.0 / 16.0) + TRIXEL_SIZE_HALF;
trixels[trixel_count].pos.z = z * (1.0 / 16.0) + TRIXEL_SIZE_HALF; trixels[trixel_count].pos.z = z * (1.0 / 16.0) + TRIXEL_SIZE_HALF;
trixels[trixel_count].pos.w = 1.0; cm := (cmd.colMultipliers.*)[x][y][z];
trixel_color := cmd.trile.trixels[x][y][z].material.color * (cmd.colMultipliers.*)[x][y][z]; // Encode highlight state in inst.w: non-identity colorMul means highlighted.
// 0=normal, 1=hovered, (future: 2=selected, 3=in-brush).
trixels[trixel_count].pos.w = ifx (cm.x == 1.0 && cm.y == 1.0 && cm.z == 1.0) then 0.0 else 1.0;
trixel_color := cmd.trile.trixels[x][y][z].material.color * cm;
trixels[trixel_count].col = .{trixel_color.x, trixel_color.y, trixel_color.z, material_encode_to_float(cmd.trile.trixels[x][y][z].material)}; trixels[trixel_count].col = .{trixel_color.x, trixel_color.y, trixel_color.z, material_encode_to_float(cmd.trile.trixels[x][y][z].material)};
trixel_count += 1; trixel_count += 1;
@ -91,10 +96,14 @@ backend_draw_trixels :: () {
wc : *World_Config = *(World_Config.{}); wc : *World_Config = *(World_Config.{});
world_config_to_shader_type(wc, *world_conf); world_config_to_shader_type(wc, *world_conf);
fs_params : Trixel_Fs_Params;
fs_params.view_mode = trixel_view_mode;
sg_apply_pipeline(gPipelines.trixel.pipeline); sg_apply_pipeline(gPipelines.trixel.pipeline);
sg_apply_bindings(*gPipelines.trixel.bind); sg_apply_bindings(*gPipelines.trixel.bind);
sg_apply_uniforms(UB_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) })); sg_apply_uniforms(UB_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) }));
sg_apply_uniforms(UB_trixel_world_config, *(sg_range.{ptr = *world_conf, size = size_of(type_of(world_conf))})); sg_apply_uniforms(UB_trixel_world_config, *(sg_range.{ptr = *world_conf, size = size_of(type_of(world_conf))}));
sg_apply_uniforms(UB_trixel_fs_params, *(sg_range.{ ptr = *fs_params, size = size_of(type_of(fs_params)) }));
sg_draw(0, 36, trixel_count); sg_draw(0, 36, trixel_count);
} }
@ -210,6 +219,7 @@ backend_draw_trile_positions_main :: (trile : string, amount : s32, worldConf: *
fs_params.rdm_diff_scale = lc.rdm_diff_scale; fs_params.rdm_diff_scale = lc.rdm_diff_scale;
fs_params.rdm_spec_scale = lc.rdm_spec_scale; fs_params.rdm_spec_scale = lc.rdm_spec_scale;
fs_params.ambient_color = lc.ambient_color.component; fs_params.ambient_color = lc.ambient_color.component;
fs_params.rdm_tint = lc.rdm_tint.component;
sg_apply_bindings(*bindings); sg_apply_bindings(*bindings);
sg_apply_uniforms(UB_trile_fs_params, *(sg_range.{ ptr = *fs_params, size = size_of(type_of(fs_params)) })); sg_apply_uniforms(UB_trile_fs_params, *(sg_range.{ ptr = *fs_params, size = size_of(type_of(fs_params)) }));
@ -241,6 +251,7 @@ backend_draw_ground :: (wc: *World_Config) {
world_conf : Plane_World_Config; world_conf : Plane_World_Config;
plane_data : Plane_Data; plane_data : Plane_Data;
w, h := get_render_size(); w, h := get_render_size();
plane_data.mvp_shadow = shadow_mvp.floats;
plane_data.screen_w = w; plane_data.screen_w = w;
plane_data.screen_h = h; plane_data.screen_h = h;
plane_data.cameraPosition = camera.position.component; plane_data.cameraPosition = camera.position.component;
@ -249,9 +260,6 @@ backend_draw_ground :: (wc: *World_Config) {
world_config_to_shader_type(wc, *world_conf); world_config_to_shader_type(wc, *world_conf);
fs_params : Plane_Fs_Params;
fs_params.mvp_shadow = shadow_mvp.floats;
fs_params.is_reflection = ifx in_reflection_pass then cast(s32)1 else cast(s32)0;
vs_params.mvp = mvp.floats; vs_params.mvp = mvp.floats;
vs_params.planeHeight = wc.planeHeight; vs_params.planeHeight = wc.planeHeight;
sg_apply_pipeline(gPipelines.plane.pipeline); sg_apply_pipeline(gPipelines.plane.pipeline);
@ -259,7 +267,6 @@ backend_draw_ground :: (wc: *World_Config) {
gPipelines.plane.bind.images[1] = g_shadowmap; gPipelines.plane.bind.images[1] = g_shadowmap;
sg_apply_bindings(*gPipelines.plane.bind); sg_apply_bindings(*gPipelines.plane.bind);
sg_apply_uniforms(UB_plane_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) })); sg_apply_uniforms(UB_plane_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) }));
sg_apply_uniforms(UB_plane_fs_params, *(sg_range.{ ptr = *fs_params, size = size_of(type_of(fs_params)) }));
sg_apply_uniforms(UB_plane_world_config, *(sg_range.{ptr = *world_conf, size = size_of(type_of(world_conf))})); sg_apply_uniforms(UB_plane_world_config, *(sg_range.{ptr = *world_conf, size = size_of(type_of(world_conf))}));
sg_apply_uniforms(UB_plane_data, *(sg_range.{ptr = *plane_data, size = size_of(type_of(plane_data))})); sg_apply_uniforms(UB_plane_data, *(sg_range.{ptr = *plane_data, size = size_of(type_of(plane_data))}));
sg_draw(0, 6, 2); sg_draw(0, 6, 2);
@ -436,8 +443,10 @@ backend_process_command_buckets :: () {
end_frame_profiling_group("Main pass"); end_frame_profiling_group("Main pass");
start_frame_profiling_group("Postprocess pass"); start_frame_profiling_group("Postprocess pass");
if !bypass_postprocess {
bloom_process(); bloom_process();
dof_process(); dof_process();
}
end_frame_profiling_group("Postprocess pass"); end_frame_profiling_group("Postprocess pass");
start_frame_profiling_group("Final pass"); start_frame_profiling_group("Final pass");
@ -446,11 +455,16 @@ backend_process_command_buckets :: () {
// Draw the render texture and do post processing: // Draw the render texture and do post processing:
sg_apply_pipeline(gPipelines.postprocess.pipeline); sg_apply_pipeline(gPipelines.postprocess.pipeline);
gPipelines.postprocess.bind.images[0] = g_postprocess_a; gPipelines.postprocess.bind.images[0] = ifx bypass_postprocess then g_rendertex else g_postprocess_a;
gPipelines.postprocess.bind.images[1] = LUT_list[g_current_lut_texture_index].image; gPipelines.postprocess.bind.images[1] = LUT_list[g_current_lut_texture_index].image;
sg_apply_bindings(*gPipelines.postprocess.bind); sg_apply_bindings(*gPipelines.postprocess.bind);
post_process_config_uniform : Post_Process_Config; post_process_config_uniform : Post_Process_Config;
if bypass_postprocess {
neutral : Post_Process;
fill_uniform_with_engine_data(*post_process_config_uniform, *neutral);
} else {
fill_uniform_with_engine_data(*post_process_config_uniform, *current_post_process); fill_uniform_with_engine_data(*post_process_config_uniform, *current_post_process);
}
sg_apply_uniforms(UB_post_process_config, *(sg_range.{ ptr = *post_process_config_uniform, size = size_of(type_of(post_process_config_uniform)) })); sg_apply_uniforms(UB_post_process_config, *(sg_range.{ ptr = *post_process_config_uniform, size = size_of(type_of(post_process_config_uniform)) }));
sg_draw(0, 6, 1); sg_draw(0, 6, 1);

View File

@ -1,28 +1,28 @@
Post_Process :: struct { Post_Process :: struct {
exposure : float = 0.0; @Slider,0,3,0.1; exposure : float = 1.0; @Slider,0,3,0.1;
contrast : float = 1.0; @Slider,0,6,0.1; contrast : float = 1.0; @Slider,0,6,0.1;
saturation : float = 1.0; @Slider,0.0,2.0,0.1; saturation : float = 1.0; @Slider,0.0,2.0,0.1;
gamma : float = 2.2; @Slider,0.3,3.0,0.1; gamma : float = 1.0; @Slider,0.3,3.0,0.1;
tonemap : float = 1.0; @Slider,0,1,1; tonemap : float = 0.0; @Slider,0,1,1;
ssao : float = 1.0; @Slider,0,5,0.1; ssao : float = 0.0; @Slider,0,5,0.1;
ssao_size : s32 = 1; @Slider,0,5,1; ssao_size : s32 = 1; @Slider,0,5,1;
dilate_separation : float = 1.0; @Slider,0,6,0.1; dilate_separation : float = 1.0; @Slider,0,6,0.1;
dilate_size : s32 = 2; @Slider,0,10,1; dilate_size : s32 = 0; @Slider,0,10,1;
dilate_min : float = 0.1; @Slider,0,1,0.1; dilate_min : float = 0.1; @Slider,0,1,0.1;
dilate_max : float = 0.3; @Slider,0,1,0.1; dilate_max : float = 0.3; @Slider,0,1,0.1;
dof_blur_size : s32 = 2; @Slider,0,10,1; dof_blur_size : s32 = 0; @Slider,0,10,1;
dof_min : float = 1.0; @Slider,0,10,1; dof_min : float = 1.0; @Slider,0,10,1;
dof_max : float = 3.0; @Slider,0,50,1; dof_max : float = 3.0; @Slider,0,50,1;
dof_point : float = 5.0; @Slider,0,30,1; dof_point : float = 5.0; @Slider,0,30,1;
bloom_size : s32 = 5; @Slider,0,10,1; bloom_size : s32 = 5; @Slider,0,10,1;
bloom_separation : float = 3.0; @Slider,0,10,1; bloom_separation : float = 3.0; @Slider,0,10,1;
bloom_treshold : float = 0.4; @Slider,0,1,0.1; bloom_treshold : float = 0.4; @Slider,0,1,0.1;
bloom_amount : float = 1.0; @Slider,0,5,0.1; bloom_amount : float = 0.0; @Slider,0,5,0.1;
vignette_intensity: float = 0.5; @Slider,0,1,0.1; vignette_intensity: float = 0.0; @Slider,0,1,0.1;
vignette_radius : float = 0.5; @Slider,0,1,0.1; vignette_radius : float = 0.5; @Slider,0,1,0.1;
scanlines_intensity: float = 0.1; @Slider,0,1,0.1; scanlines_intensity: float = 0.0; @Slider,0,1,0.1;
scanlines_density: float = 1; @Slider,0,10,0.1; scanlines_density : float = 1.0; @Slider,0,10,0.1;
chromatic_aberration_intensity: float = 0.0; @Slider,0,0.05,0.001; chromatic_aberration_intensity: float = 0.0; @Slider,0,0.05,0.001;
film_grain_intensity: float = 0.0; @Slider,0,0.5,0.001; film_grain_intensity: float = 0.0; @Slider,0,0.5,0.001;
barrel_distortion_intensity: float = 0.0; @Slider,-2,2,0.1; barrel_distortion_intensity: float = 0.0; @Slider,-2,2,0.1;
@ -40,6 +40,7 @@ Lighting_Config :: struct {
rdm_diff_scale : float = 1.0; @Slider,0,3,0.1 rdm_diff_scale : float = 1.0; @Slider,0,3,0.1
rdm_spec_scale : float = 1.0; @Slider,0,3,0.1 rdm_spec_scale : float = 1.0; @Slider,0,3,0.1
ambient_color : Vector3 = .{0.3,0.3,0.4}; @Color ambient_color : Vector3 = .{0.3,0.3,0.4}; @Color
rdm_tint : Vector3 = .{1.05,1.0,0.9}; @Color
} }
current_lighting_config : Lighting_Config; current_lighting_config : Lighting_Config;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -29,12 +29,6 @@ in flat int idx;
in float depth; in float depth;
out vec4 frag_color; out vec4 frag_color;
// Uniform bindings from the original shader
layout(binding=3) uniform plane_fs_params {
mat4 mvp_shadow;
int is_reflection;
};
layout(binding=1) uniform plane_world_config { layout(binding=1) uniform plane_world_config {
vec3 skyBase; vec3 skyBase;
vec3 skyTop; vec3 skyTop;
@ -54,6 +48,7 @@ layout(binding=1) uniform plane_world_config {
}; };
layout(binding=2) uniform plane_data { layout(binding=2) uniform plane_data {
mat4 mvp_shadow;
int screen_w; int screen_w;
int screen_h; int screen_h;
int is_reflection_pass; int is_reflection_pass;
@ -69,7 +64,7 @@ layout(binding = 2) uniform texture2D normal_map;
// Sampler bindings // Sampler bindings
layout(binding = 0) uniform sampler refsmp; layout(binding = 0) uniform sampler refsmp;
layout(binding = 1) uniform sampler shadowsmp; layout(binding = 1) uniform sampler plane_shadowsmp;
layout(binding = 2) uniform sampler normalsmp; layout(binding = 2) uniform sampler normalsmp;
vec3 fresnelSchlick(float cosTheta) { vec3 fresnelSchlick(float cosTheta) {
@ -120,31 +115,33 @@ void main() {
vec3 normal3 = texture(sampler2D(normal_map, normalsmp), uv3).xzy * 2.0 - 1.0; vec3 normal3 = texture(sampler2D(normal_map, normalsmp), uv3).xzy * 2.0 - 1.0;
vec3 normal4 = texture(sampler2D(normal_map, normalsmp), uv4).xzy * 2.0 - 1.0; vec3 normal4 = texture(sampler2D(normal_map, normalsmp), uv4).xzy * 2.0 - 1.0;
// vec3 normal = normalize(vec3(0.0, 1.0, 0.0));
vec3 normal = normalize(normal1 + normal2 + normal3 + normal4); vec3 normal = normalize(normal1 + normal2 + normal3 + normal4);
vec3 view_dir = normalize(cameraPosition - pos.xyz); vec3 view_dir = normalize(cameraPosition - pos.xyz);
vec3 light_dir = normalize(sunPosition); vec3 light_dir = normalize(sunPosition);
vec3 halfway_dir = normalize(light_dir + view_dir); vec3 halfway_dir = normalize(light_dir + view_dir);
float shadow_factor = 1.0; // shadowmap to be implemented
vec3 base_water_color = waterColor; float diffuse = max(0.0, dot(normal, light_dir));
float diffuse = (dot(normal, light_dir)) + 0.000001 * is_reflection * shininess;
float spec = pow(max(0.0, dot(halfway_dir, normal)), 32); float spec = pow(max(0.0, dot(halfway_dir, normal)), 32);
float fresnel = min(1.0, fresnelSchlick(dot(view_dir, vec3(0.0, 1.0, 0.0))).x + 0.3); float fresnel = min(1.0, fresnelSchlick(dot(view_dir, vec3(0.0, 1.0, 0.0))).x + 0.3);
vec3 refracted_color = base_water_color * diffuse * sunLightColor * sunIntensity;
vec3 specular_highlight = sunLightColor * sunIntensity * spec; vec4 shadow_proj_pos = mvp_shadow * vec4(pos.xyz, 1.0);
vec3 shadow_pos = shadow_proj_pos.xyz / shadow_proj_pos.w;
shadow_pos = shadow_pos * 0.5 + 0.5;
shadow_pos.z -= 0.001;
float shadowp = texture(sampler2DShadow(shadow, plane_shadowsmp), shadow_pos);
vec3 refracted_color = waterColor * diffuse * sunLightColor * sunIntensity * shadowp;
vec3 specular_highlight = sunLightColor * sunIntensity * spec * shadowp;
vec2 screen_uv = gl_FragCoord.xy / vec2(screen_w, screen_h); vec2 screen_uv = gl_FragCoord.xy / vec2(screen_w, screen_h);
screen_uv.y = 1.0 - screen_uv.y; screen_uv.y = 1.0 - screen_uv.y;
vec3 reflected_color = texture(sampler2D(reftex, refsmp), screen_uv).rgb; vec2 distortion = normal.xz * 0.005;
vec3 surface_color = mix(refracted_color, reflected_color, min(1.0, fresnel * 1.0)); vec3 reflected_color = texture(sampler2D(reftex, refsmp), screen_uv + distortion).rgb;
vec3 final_color = reflected_color + specular_highlight * 0.2 + diffuse * 0.2 + 0.00001 * surface_color;
// vec3 final_color = (surface_color + specular_highlight) * shadow_factor; vec3 surface_color = mix(refracted_color, reflected_color, fresnel);
float refraction_alpha = 0.4; vec3 final_color = surface_color + specular_highlight;
float reflection_alpha = 1.0; float alpha = mix(0.3, 0.5, fresnel);
float alpha = mix(refraction_alpha, reflection_alpha, fresnel);
// float alpha = 1.0;
vec3 fog = skyIntensity * sky(normalize(pos.xyz), sunPosition); vec3 fog = skyIntensity * sky(normalize(pos.xyz), sunPosition);
float fogFactor = smoothstep(750.0, 1000.0, length(pos.xz)); float fogFactor = smoothstep(750.0, 1000.0, length(pos.xz));

View File

@ -74,6 +74,7 @@ layout(binding=3) uniform trile_fs_params {
float rdm_diff_scale; float rdm_diff_scale;
float rdm_spec_scale; float rdm_spec_scale;
vec3 ambient_color; vec3 ambient_color;
vec3 rdm_tint;
}; };
layout(binding = 0) uniform texture2D triletex; layout(binding = 0) uniform texture2D triletex;
@ -518,12 +519,18 @@ void main() {
// Indirect specular // Indirect specular
vec3 indirectSpec = sample_rdm(N, -cv, vec3 indirectSpec = sample_rdm(N, -cv,
hemispherePos, vpos - hemispherePos, roughnessInt, local); hemispherePos, vpos - hemispherePos, roughnessInt, local) * rdm_tint;
// For metallic surfaces: desaturate the reflection so Frough (which uses albedo
// as F0 for metals) applies the metal tint cleanly without double-tinting.
float specLum = dot(indirectSpec, vec3(0.2126, 0.7152, 0.0722));
indirectSpec = mix(indirectSpec, vec3(specLum), metallic);
vec2 envBRDF = texture(sampler2D(brdf_lut, rdmsmp), vec2(max(dot(N, V), 0.0), roughness)).rg; vec2 envBRDF = texture(sampler2D(brdf_lut, rdmsmp), vec2(max(dot(N, V), 0.0), roughness)).rg;
light += indirectSpec * (Frough * envBRDF.x + envBRDF.y) * rdm_spec_scale; light += indirectSpec * (Frough * envBRDF.x + envBRDF.y) * rdm_spec_scale;
// Indirect diffuse (interpolated from neighbor probes) // Indirect diffuse (interpolated from neighbor probes)
vec3 indirectDiff = sample_rdm_diff(N, vpos - hemispherePos, local); vec3 indirectDiff = sample_rdm_diff(N, vpos - hemispherePos, local) * rdm_tint;
vec3 kDiff = 1.0 - Frough; vec3 kDiff = 1.0 - Frough;
kDiff *= 1.0 - metallic; kDiff *= 1.0 - metallic;
light += (kDiff * indirectDiff / PI * albedo) * ssao_sample * rdm_diff_scale; light += (kDiff * indirectDiff / PI * albedo) * ssao_sample * rdm_diff_scale;

View File

@ -14,6 +14,7 @@ out vec4 color;
out vec4 fnormal; out vec4 fnormal;
out vec4 pos; out vec4 pos;
out vec3 cam; out vec3 cam;
out float vtrixel_state;
void main() { void main() {
vec3 instancepos = inst.xyz; vec3 instancepos = inst.xyz;
@ -22,11 +23,16 @@ void main() {
color = inst_col; color = inst_col;
pos = gl_Position; pos = gl_Position;
cam = camera; cam = camera;
vtrixel_state = inst.w;
} }
@end @end
@fs fs_trixel @fs fs_trixel
layout(binding=2) uniform trixel_fs_params {
int view_mode;
};
layout(binding=1) uniform trixel_world_config { layout(binding=1) uniform trixel_world_config {
vec3 skyBase; vec3 skyBase;
vec3 skyTop; vec3 skyTop;
@ -52,6 +58,7 @@ in vec4 color;
in vec4 fnormal; in vec4 fnormal;
in vec4 pos; in vec4 pos;
in vec3 cam; in vec3 cam;
in float vtrixel_state;
out vec4 frag_color; out vec4 frag_color;
const float PI = 3.1412854; const float PI = 3.1412854;
@ -123,8 +130,29 @@ void main() {
light += (kD * albedo / PI + specular) * NdotL * vec3(1.0, 1.0, 1.0); light += (kD * albedo / PI + specular) * NdotL * vec3(1.0, 1.0, 1.0);
if (view_mode == 1) {
// Normals: map face normals to RGB so each axis gets a distinct color.
frag_color = vec4(N * 0.5 + 0.5, 1.0);
} else if (view_mode == 2) {
// Albedo: simple diffuse shading, no specular/roughness/metallic eval.
float diffuse = max(dot(N, L), 0.0) * 0.6 + 0.4;
frag_color = vec4(albedo * diffuse + emissive, 1.0);
} else if (view_mode == 3) {
// Normal+Albedo: normal color used as a per-face tint on the albedo.
// Shows paint color and face orientation simultaneously.
vec3 normal_tint = N * 0.5 + 0.5;
float diffuse = max(dot(N, L), 0.0) * 0.4 + 0.6;
frag_color = vec4(albedo * normal_tint * diffuse + emissive, 1.0);
} else {
frag_color = vec4(light + emissive, 1.0); frag_color = vec4(light + emissive, 1.0);
} }
// Overlay highlight for hovered / selected / brush-radius trixels in all view modes.
// State is encoded per-instance in inst.w: 0=normal, 1=hovered, 2=selected, 3=in-brush.
if (vtrixel_state > 0.5) {
frag_color.rgb = mix(frag_color.rgb, vec3(1.0, 1.0, 0.5), 0.5);
}
}
@end @end
@program trixel vs_trixel fs_trixel @program trixel vs_trixel fs_trixel

View File

@ -47,6 +47,7 @@ set_trile_gfx :: (name: string, gfx: Trile_GFX, skip_preexist_check: bool = fals
if success { if success {
sg_destroy_buffer(old_gfx.vertex_buffer); sg_destroy_buffer(old_gfx.vertex_buffer);
sg_destroy_buffer(old_gfx.normal_buffer); sg_destroy_buffer(old_gfx.normal_buffer);
sg_destroy_buffer(old_gfx.centre_buffer);
sg_destroy_image(old_gfx.trixel_colors); sg_destroy_image(old_gfx.trixel_colors);
array_reset(*old_gfx.vertices); array_reset(*old_gfx.vertices);
print("Destroyed old GFX buffers for trile: %\n", name); print("Destroyed old GFX buffers for trile: %\n", name);