world fixes and water improvements

This commit is contained in:
Tuomas Katajisto 2026-09-29 21:02:08 +03:00
parent 279b7ca81d
commit 0d44c75fa9
8 changed files with 928 additions and 841 deletions

View File

@ -26,6 +26,10 @@ current_editor_view : Editor_View = .Trile_Editor;
in_editor_view : bool = false; in_editor_view : bool = false;
editor_shows_water :: () -> bool {
return current_editor_view == .Level_Editor;
}
init_editor :: () { init_editor :: () {
#if !FLAG_RELEASE_BUILD { #if !FLAG_RELEASE_BUILD {
#if OS != .WASM { #if OS != .WASM {

View File

@ -65,8 +65,15 @@ Key_Combo :: struct {
shift : bool; shift : bool;
} }
MAX_BINDINGS_PER_ACTION :: 4;
Action_Bindings :: struct {
combos : [MAX_BINDINGS_PER_ACTION]Key_Combo;
count : s32;
}
// 5000 different actions ought to be enough for anything right? // 5000 different actions ought to be enough for anything right?
keys : [5000]Key_Combo; keys : [5000]Action_Bindings;
check_action :: (action: $T, status: Key_Current_State) -> bool { check_action :: (action: $T, status: Key_Current_State) -> bool {
offset := 0; offset := 0;
@ -75,12 +82,16 @@ check_action :: (action: $T, status: Key_Current_State) -> bool {
} }
if console_open_ignore_input then return false; if console_open_ignore_input then return false;
if ui_keyboard_captured then return false; if ui_keyboard_captured then return false;
combo := keys[cast(s32)action + offset]; bindings := *keys[cast(s32)action + offset];
ctrl_down := cast(bool)(input_button_states[Key_Code.CTRL] & .DOWN); ctrl_down := cast(bool)(input_button_states[Key_Code.CTRL] & .DOWN);
shift_down := cast(bool)(input_button_states[Key_Code.SHIFT] & .DOWN); shift_down := cast(bool)(input_button_states[Key_Code.SHIFT] & .DOWN);
if combo.ctrl != ctrl_down then return false; for 0..bindings.count-1 {
if combo.shift != shift_down then return false; combo := bindings.combos[it];
return cast(bool)(input_button_states[combo.key] & status); if combo.ctrl != ctrl_down then continue;
if combo.shift != shift_down then continue;
if input_button_states[combo.key] & status then return true;
}
return false;
} }
@ -92,7 +103,17 @@ set_binding :: (action: $T, combo: Key_Combo) {
offset = EDITOR_ACTION_COUNT; offset = EDITOR_ACTION_COUNT;
} }
keys[cast(s32)action + offset] = combo; bindings := *keys[cast(s32)action + offset];
for 0..bindings.count-1 {
c := bindings.combos[it];
if c.key == combo.key && c.ctrl == combo.ctrl && c.shift == combo.shift return;
}
if bindings.count >= MAX_BINDINGS_PER_ACTION {
log_warn("Action % already has % bindings, ignoring %", action, MAX_BINDINGS_PER_ACTION, combo);
return;
}
bindings.combos[bindings.count] = combo;
bindings.count += 1;
} }
is_action_down :: (action: $T) -> bool { is_action_down :: (action: $T) -> bool {
@ -116,7 +137,7 @@ get_action_combo :: (action: $T) -> Key_Combo {
#if T != Editor_Action { #if T != Editor_Action {
offset = EDITOR_ACTION_COUNT; offset = EDITOR_ACTION_COUNT;
} }
return keys[cast(s32)action + offset]; return keys[cast(s32)action + offset].combos[0];
} }
keybind_button :: (r: GR.Rect, label: string, action: Editor_Action, theme: *GR.Button_Theme, loc := #caller_location) -> bool { keybind_button :: (r: GR.Rect, label: string, action: Editor_Action, theme: *GR.Button_Theme, loc := #caller_location) -> bool {

View File

@ -112,9 +112,12 @@ add_particle_render_tasks :: () {
batches : [..] Batch; batches : [..] Batch;
batches.allocator = temp; batches.allocator = temp;
has_water, water_y := particle_water_height();
for *p: g_particles { for *p: g_particles {
if !p.alive then continue; if !p.alive then continue;
if p.definition == null then continue; if p.definition == null then continue;
if has_water && p.position.y < water_y then continue;
def := p.definition; def := p.definition;
if def.animation_name.count == 0 then continue; if def.animation_name.count == 0 then continue;
@ -194,6 +197,15 @@ add_particle_render_tasks :: () {
} }
} }
particle_water_height :: () -> (has_water: bool, height: float) {
#if !FLAG_RELEASE_BUILD {
if in_editor_view && !editor_shows_water() return false, 0;
}
curworld := get_current_world();
if !curworld.valid return false, 0;
return true, effective_plane_height(*curworld.world.conf);
}
get_emitter_def :: (name: string) -> *Particle_Emitter_Config { get_emitter_def :: (name: string) -> *Particle_Emitter_Config {
for *def: g_emitter_defs { for *def: g_emitter_defs {
if def.name == name then return def; if def.name == name then return def;
@ -238,14 +250,6 @@ save_particle_defs :: () {
} }
} }
#scope_file
Random :: #import "Random";
rng :: inline (lo: float, hi: float) -> float {
return lo + Random.random_get_zero_to_one() * (hi - lo);
}
spawn_one_particle :: (position: Vector3, def: *Particle_Emitter_Config) { spawn_one_particle :: (position: Vector3, def: *Particle_Emitter_Config) {
for *p: g_particles { for *p: g_particles {
if p.alive then continue; if p.alive then continue;
@ -268,6 +272,14 @@ spawn_one_particle :: (position: Vector3, def: *Particle_Emitter_Config) {
} }
} }
#scope_file
Random :: #import "Random";
rng :: inline (lo: float, hi: float) -> float {
return lo + Random.random_get_zero_to_one() * (hi - lo);
}
lerp_color :: (a: Vector4, b: Vector4, t: float) -> Vector4 { lerp_color :: (a: Vector4, b: Vector4, t: float) -> Vector4 {
return a * (1.0 - t) + b * t; return a * (1.0 - t) + b * t;
} }

View File

@ -468,7 +468,7 @@ backend_draw_ground :: (wc: *World_Config) {
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;
plane_data.reflectionDistortion = 0.1; plane_data.reflectionDistortion = wc.waterDistortion;
plane_data.shininess = wc.waterShininess; plane_data.shininess = wc.waterShininess;
world_config_to_shader_type(wc, *world_conf); world_config_to_shader_type(wc, *world_conf);

File diff suppressed because it is too large Load Diff

View File

@ -45,6 +45,7 @@ layout(binding=1) uniform plane_world_config {
vec3 waterColor; vec3 waterColor;
vec3 deepColor; vec3 deepColor;
float time; float time;
float waterReflectivity;
}; };
layout(binding=2) uniform plane_data { layout(binding=2) uniform plane_data {
@ -136,12 +137,12 @@ void main() {
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;
vec2 distortion = normal.xz * 0.007; vec2 distortion = normal.xz * reflectionDistortion;
vec3 reflected_color = texture(sampler2D(reftex, refsmp), screen_uv + distortion).rgb; vec3 reflected_color = texture(sampler2D(reftex, refsmp), screen_uv + distortion).rgb;
vec3 surface_color = mix(refracted_color, reflected_color, fresnel); vec3 surface_color = mix(refracted_color, reflected_color, mix(fresnel, 1.0, waterReflectivity));
vec3 final_color = surface_color + specular_highlight; vec3 final_color = surface_color + specular_highlight;
float alpha = mix(0.3, 0.5, fresnel); float alpha = mix(mix(0.3, 0.5, fresnel), 1.0, waterReflectivity);
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

@ -149,6 +149,7 @@ autoedit :: (rect: GR.Rect, value: *$T, theme: *GR.Overall_Theme, identifier: s3
for ti.members { for ti.members {
if it.flags & .CONSTANT continue; if it.flags & .CONSTANT continue;
if it.flags & .USING continue; // e.g. an entity's Entity base if it.flags & .USING continue; // e.g. an entity's Entity base
if array_find(it.notes, "NoSave") continue; // runtime state, not for editing
if !autoedit_supports_type(it.type) continue; if !autoedit_supports_type(it.type) continue;
print_to_builder(*builder, "%\n", input_code_from_type_and_notes(it.name, it.type, it.notes, it_index)); print_to_builder(*builder, "%\n", input_code_from_type_and_notes(it.name, it.type, it.notes, it_index));
} }

View File

@ -55,6 +55,36 @@ Chunk_Trile_Group :: struct {
is_buried : [..]bool; // Runtime-only, parallel to instances. Not serialized. is_buried : [..]bool; // Runtime-only, parallel to instances. Not serialized.
} }
// Chunk-local, like the instances. Call after changing group.instances.
recompute_group_bounds :: (group: *Chunk_Trile_Group) {
if group.instances.count == 0 {
group.bounding_min = .{};
group.bounding_max = .{};
group.average_pos = .{};
return;
}
gmin := Vector3.{31,31,31};
gmax := Vector3.{0,0,0};
gavg := Vector3.{0,0,0};
for i : group.instances {
gmin.x = min(gmin.x, xx i.x);
gmin.y = min(gmin.y, xx i.y);
gmin.z = min(gmin.z, xx i.z);
gmax.x = max(gmax.x, xx i.x);
gmax.y = max(gmax.y, xx i.y);
gmax.z = max(gmax.z, xx i.z);
gavg += Vector3.{xx i.x, xx i.y, xx i.z};
}
gavg /= cast(float) group.instances.count;
group.bounding_min = gmin;
group.bounding_max = gmax;
group.average_pos = gavg;
}
Chunk :: struct { Chunk :: struct {
coord: Chunk_Key; coord: Chunk_Key;
groups: [..]Chunk_Trile_Group; groups: [..]Chunk_Trile_Group;
@ -524,6 +554,8 @@ World_Json_Config :: struct {
waterColor : [3]float; waterColor : [3]float;
deepColor : [3]float; deepColor : [3]float;
waterShininess : float; waterShininess : float;
waterReflectivity : float = 0.0;
waterDistortion : float = 0.007;
} }
World_Json_Chunk :: struct { World_Json_Chunk :: struct {
@ -685,6 +717,8 @@ world_config_to_json :: (conf: *World_Config) -> World_Json_Config {
jc.waterColor = conf.waterColor.component; jc.waterColor = conf.waterColor.component;
jc.deepColor = conf.deepColor.component; jc.deepColor = conf.deepColor.component;
jc.waterShininess = conf.waterShininess; jc.waterShininess = conf.waterShininess;
jc.waterReflectivity = conf.waterReflectivity;
jc.waterDistortion = conf.waterDistortion;
return jc; return jc;
} }
@ -705,6 +739,8 @@ world_config_from_json :: (jc: *World_Json_Config) -> World_Config {
conf.waterColor.component = jc.waterColor; conf.waterColor.component = jc.waterColor;
conf.deepColor.component = jc.deepColor; conf.deepColor.component = jc.deepColor;
conf.waterShininess = ifx jc.waterShininess > 0 then jc.waterShininess else 64.0; conf.waterShininess = ifx jc.waterShininess > 0 then jc.waterShininess else 64.0;
conf.waterReflectivity = jc.waterReflectivity;
conf.waterDistortion = jc.waterDistortion;
return conf; return conf;
} }
@ -837,25 +873,7 @@ load_world_from_json :: (json_str: string, chunk_bin: []u8) -> (World, bool) {
inst := read_value(chunk_data, *chunk_cursor, Trile_Instance); inst := read_value(chunk_data, *chunk_cursor, Trile_Instance);
array_add(*group.instances, inst); array_add(*group.instances, inst);
} }
gmin := Vector3.{31,31,31}; recompute_group_bounds(*group);
gmax := Vector3.{0,0,0};
gavg := Vector3.{0,0,0};
for i : group.instances {
gmin.x = min(gmin.x, xx i.x);
gmin.y = min(gmin.y, xx i.y);
gmin.z = min(gmin.z, xx i.z);
gmax.x = max(gmax.x, xx i.x);
gmax.y = max(gmax.y, xx i.y);
gmax.z = max(gmax.z, xx i.z);
gavg += Vector3.{xx i.x, xx i.y, xx i.z};
}
gavg /= cast(float) group.instances.count;
group.bounding_min = gmin;
group.bounding_max = gmax;
group.average_pos = gavg;
array_add(*chunk.groups, group); array_add(*chunk.groups, group);
} }
calculate_and_set_chunk_occupancy_mask(*chunk); calculate_and_set_chunk_occupancy_mask(*chunk);
@ -915,6 +933,8 @@ World_Config :: struct {
waterColor : Vector3 = .{1.0, 1.0, 1.0}; @Color // @ToDo: sensible default values. waterColor : Vector3 = .{1.0, 1.0, 1.0}; @Color // @ToDo: sensible default values.
deepColor : Vector3 = .{1.0, 1.0, 1.0}; @Color // @ToDo: sensible default values. deepColor : Vector3 = .{1.0, 1.0, 1.0}; @Color // @ToDo: sensible default values.
waterShininess : float = 64.0; @Slider,1,512,8 waterShininess : float = 64.0; @Slider,1,512,8
waterReflectivity : float = 0.0; @Slider,0,1,0.05
waterDistortion : float = 0.007; @Slider,0,0.05,0.001
hsv_lighting : s32 = 1; @Slider,0,1,1 hsv_lighting : s32 = 1; @Slider,0,1,1
// ambientColor : Vector3 = .{1.0, 1.0, 1.0}; @Color // ambientColor : Vector3 = .{1.0, 1.0, 1.0}; @Color