lot's of work on polishing the engine

This commit is contained in:
Tuomas Katajisto 2026-03-20 18:49:19 +02:00
parent 4697c016e1
commit 733cdd2760
35 changed files with 1339 additions and 644 deletions

View File

@ -6,8 +6,12 @@ Iprof :: #import "Iprof"(IMPORT_MODE = .METAPROGRAM);
Trueno_Build_Options :: struct {
wasm_build : bool;
release_build : bool;
tacoma_enabled : bool;
iprof_enabled : bool;
tacoma_enabled : bool;
iprof_enabled : bool;
demo_build : bool;
test_engine : bool;
test_game : bool;
short_tests : bool;
}
build_options_from_args :: (args: []string) -> Trueno_Build_Options {
@ -23,6 +27,17 @@ build_options_from_args :: (args: []string) -> Trueno_Build_Options {
opts.release_build = true;
case "iprof";
opts.iprof_enabled = true;
case "demo";
opts.demo_build = true;
case "test";
opts.test_game = true;
opts.test_engine = true;
case "test_game";
opts.test_game = true;
case "test_engine";
opts.test_engine = true;
case "test_short";
opts.short_tests = true;
}
}
@ -108,7 +123,7 @@ native_build :: (opts: Build_Options, trueno_opts: Trueno_Build_Options) {
// }
compiler_begin_intercept(w);
add_trueno_opts_to_compiler_strings(trueno_opts, w);
add_build_file("src/platform_specific/main_native.jai", w);
add_shaders_to_workspace(w);
@ -185,6 +200,8 @@ wasm_build :: (opts: Build_Options, trueno_opts: Trueno_Build_Options) {
}
exit(1);
}
file_delete("dist/main.o");
}
#run {

View File

@ -67,12 +67,11 @@ fetch_callback :: (res: *sfetch_response_t) #c_call {
req := g_asset_manager.current_fetch;
g_asset_manager.is_fetching = false;
if req.type == {
case .PACK;
if res.failed {
print("Failed to load pack '%'\n", req.pack_name);
log_error("Failed to load pack '%'", req.pack_name);
return;
}
mem := NewArray(res.data.size.(s64), u8, false);
@ -81,13 +80,13 @@ fetch_callback :: (res: *sfetch_response_t) #c_call {
pack.nameHash = hash.get_hash(req.pack_name);
pack.name = req.pack_name;
success := init_from_memory(*pack.content, mem, sprint("%", req.pack_name));
if !success { print("Failed to load pack!!\n"); return; }
if !success { log_error("Failed to load pack!!"); return; }
add_resources_from_pack(*pack);
array_add(*g_asset_manager.loadedPacks, pack);
case .WORLD;
if res.failed {
print("Failed to load world '%'\n", req.world_name);
log_error("Failed to load world '%'", req.world_name);
return;
}
data: []u8;
@ -97,9 +96,9 @@ fetch_callback :: (res: *sfetch_response_t) #c_call {
if ok {
set_loaded_world(world);
rdm_loader_enqueue_world(*get_current_world().world);
print("Loaded world: %\n", world.name);
log_info("Loaded world: %", world.name);
} else {
print("Failed to parse world '%'\n", req.world_name);
log_error("Failed to parse world '%'", req.world_name);
}
case .RDM_ATLAS;
@ -107,17 +106,17 @@ fetch_callback :: (res: *sfetch_response_t) #c_call {
if !curworld.valid || curworld.world.name != req.world_name then return;
if res.failed {
print("RDM: failed to load atlas for chunk %\n", req.chunk_key);
log_error("RDM: failed to load atlas for chunk %", req.chunk_key);
return;
}
header_size := cast(s64) size_of(RDM_File_Header);
if res.data.size < cast(u64) header_size {
print("RDM: atlas too small for chunk %\n", req.chunk_key);
log_error("RDM: atlas too small for chunk %", req.chunk_key);
return;
}
header := cast(*RDM_File_Header) res.data.ptr;
if header.magic != RDM_FILE_MAGIC {
print("RDM: bad atlas magic for chunk %\n", req.chunk_key);
log_error("RDM: bad atlas magic for chunk %", req.chunk_key);
return;
}
atlas_pixel_bytes := cast(u64) header.width * cast(u64) header.height * 4 * size_of(float);
@ -145,19 +144,19 @@ fetch_callback :: (res: *sfetch_response_t) #c_call {
world_ok := curworld.valid && curworld.world.name == req.world_name;
if res.failed || !world_ok {
if res.failed then print("RDM: failed to load lookup for chunk %\n", req.chunk_key);
if res.failed then log_error("RDM: failed to load lookup for chunk %", req.chunk_key);
if req.rdm_pending_atlas.id != 0 then sg_destroy_image(req.rdm_pending_atlas);
return;
}
header_size := cast(s64) size_of(RDM_File_Header);
if res.data.size < cast(u64) header_size {
print("RDM: lookup too small for chunk %\n", req.chunk_key);
log_error("RDM: lookup too small for chunk %", req.chunk_key);
sg_destroy_image(req.rdm_pending_atlas);
return;
}
header := cast(*RDM_File_Header) res.data.ptr;
if header.magic != RDM_FILE_MAGIC {
print("RDM: bad lookup magic for chunk %\n", req.chunk_key);
log_error("RDM: bad lookup magic for chunk %", req.chunk_key);
sg_destroy_image(req.rdm_pending_atlas);
return;
}
@ -177,7 +176,7 @@ fetch_callback :: (res: *sfetch_response_t) #c_call {
chunk.rdm_atlas = req.rdm_pending_atlas;
chunk.rdm_lookup = sg_make_image(*lookup_desc);
chunk.rdm_valid = true;
print("RDM: loaded chunk %\n", req.chunk_key);
log_debug("RDM: loaded chunk %", req.chunk_key);
} else {
sg_destroy_image(req.rdm_pending_atlas);
}
@ -227,7 +226,7 @@ add_resources_from_pack :: (pack: *Loaded_Pack) {
s := create_string_from_memory(v.data);
success, sheet := Jaison.json_parse_string(s, Aseprite_Sheet,, temp);
if !success {
print("Failed to parse animation sheet JSON for sheet(%)\n", name);
log_error("Failed to parse animation sheet JSON for sheet(%)", name);
continue;
}
queuedSheet := table_find_pointer(*sheets_to_init, name);
@ -245,9 +244,9 @@ add_resources_from_pack :: (pack: *Loaded_Pack) {
case "ttf";
// Load into a font. Add to free list.
case;
print("There was file(%) in pack(%), that did not match any known fileformat...\n", v.name, pack);
log_warn("File(%) in pack(%) has unknown format", v.name, pack.name);
}
print("% -> %\n", v.name, extension);
log_debug("% -> %", v.name, extension);
}
// Properly initialize animations from the pack now that we have the images
@ -270,7 +269,7 @@ add_resources_from_pack :: (pack: *Loaded_Pack) {
});
}
table_add(*pack.animations, anim.name, anim);
print("Added anim(%)\n", anim.name);
log_debug("Added anim(%)", anim.name);
}
}
@ -285,7 +284,7 @@ find_pack_by_name :: (name: string) -> (bool, Loaded_Pack) {
return true, it;
}
}
print("[WARNING] Was unable to find pack: %\n", name);
log_warn("Unable to find pack: %", name);
return false, .{};
}
@ -316,7 +315,7 @@ show_loading_screen :: () -> bool {
}
asset_manager_tick :: () {
#if !RELEASE_BUILD && OS != .WASM {
#if !FLAG_RELEASE_BUILD && OS != .WASM {
if g_asset_manager.pending_hot_reload && !g_asset_manager.is_fetching && g_asset_manager.fetch_queue.count == 0 {
g_asset_manager.pending_hot_reload = false;
hot_reload_all_packs();
@ -386,7 +385,7 @@ get_texture_from_pack :: (pack: string, path: string) -> (sg_image) {
ok, img := table_find(*pack.textures, path);
if !ok {
print("[WARNING] Failed to find texture(%) from pack(%)\n", path, pack.name);
log_warn("Failed to find texture(%) from pack(%)", path, pack.name);
return invalid_img;
}
@ -403,7 +402,7 @@ get_audio_from_pack :: (pack: string, path: string) -> *Audio_Data {
found, pack := find_pack_by_name(pack);
if !found then return null;
audio := table_find_pointer(*pack.audio, path);
if !audio then print("Failed to find audio(%)\n", path);
if !audio then log_error("Failed to find audio(%)", path);
return audio;
}
@ -412,20 +411,20 @@ add_font_from_pack :: (pack: string, path: string) {
if !pack_ok then return;
ok, entry := table_find(*pack.content.lookup, path);
if !ok {
print("Failed to find font % from pack...\n", path);
log_error("Failed to find font % from pack", path);
return;
}
state.font_default.fons_font = fonsAddFontMem(state.fons, "sans", entry.data.data, xx entry.data.count, 0);
}
load_string_from_pack :: (pack: string, path: string) -> string {
print("Warning: you are circumventing the asset management system....\n");
log_warn("You are circumventing the asset management system");
pack_ok, pack := find_pack_by_name(pack);
if !pack_ok return "";
ok, entry := table_find(*pack.content.lookup, path);
if !ok {
print("Failed to load string from pack: %\n", path);
log_error("Failed to load string from pack: %", path);
return "";
}

View File

@ -37,15 +37,14 @@ load_wav_from_memory :: (data: []u8) -> Audio_Data {
dataString.data = data.data;
dataString.count = data.count;
format, samples, success := Wav.get_wav_header(dataString);
if !success print("Failed to load wav file!\n");
if !success log_error("Failed to load wav file!");
audio_samples : []s16;
audio_samples.data = cast(*s16)samples.data;
audio_samples.count = samples.count / 2;
for sample, i: audio_samples {
if i % 2 == 0 {
array_add(*audio.data, cast(float)sample / 32768.0);
}
for sample: audio_samples {
array_add(*audio.data, cast(float)sample / 32768.0);
}
audio.channels = format.nChannels;
audio.channels = format.nChannels;
audio.sample_rate = format.nSamplesPerSec;
return audio;
}

View File

@ -1,4 +1,5 @@
Audio_Data :: struct {
channels: s16;
data: [..]float;
channels : s16;
sample_rate : s32;
data : [..]float;
}

View File

@ -18,7 +18,7 @@ Mixer_Play_Task :: struct {
bus : Mixer_Bus;
mode : Play_Mode;
unique : bool = true;
curSample : s64 = 0;
curSample : s64 = 0; // output frames consumed
startTime : float64 = 0;
delay : float64 = 0;
@ -98,38 +98,52 @@ mixer_get_samples :: (buffer: *float, frame_count: s32, channel_count: s32) {
vol := bus_vol * g_mixer.config.masterVolume;
source_data := task.audio.data;
source_channels := task.audio.channels;
source_data := task.audio.data;
source_channels := cast(s64) task.audio.channels;
src_rate := cast(s64) task.audio.sample_rate;
out_rate := cast(s64) saudio_sample_rate();
num_src_frames := source_data.count / source_channels;
task_finished := false;
for f : 0..frame_count-1 {
if task.curSample >= source_data.count {
src_pos := cast(float64)(task.curSample + f) * cast(float64)src_rate / cast(float64)out_rate;
src_frame0 := cast(s64) src_pos;
src_frame1 := src_frame0 + 1;
t := cast(float)(src_pos - cast(float64)src_frame0);
if src_frame0 >= num_src_frames {
if task.mode == .ONESHOT {
task_finished = true;
break;
} else {
task.curSample = 0;
src_frame0 = src_frame0 % num_src_frames;
src_frame1 = src_frame1 % num_src_frames;
}
} else if src_frame1 >= num_src_frames {
// At the very last source frame: clamp for ONESHOT, wrap for REPEAT.
src_frame1 = ifx task.mode == .ONESHOT then src_frame0 else 0;
}
for ch : 0..channel_count-1 {
out_index := (f * channel_count) + ch;
offset := ifx source_channels == 1 then 0 else ch;
sample : float = 0;
if task.curSample + f + offset < source_data.count {
sample = source_data[task.curSample + f + offset];
}
src_ch := ifx source_channels == 1 then 0 else ch;
buffer[out_index] += sample * vol;
s0 := source_data[src_frame0 * source_channels + src_ch];
s1 := source_data[src_frame1 * source_channels + src_ch];
buffer[out_index] += (s0 + (s1 - s0) * t) * vol;
}
}
task.curSample += frame_count;
// Keep curSample bounded for REPEAT tracks so it never overflows.
if task.mode == .REPEAT && out_rate > 0 && src_rate > 0 {
loop_period := num_src_frames * out_rate / src_rate;
if loop_period > 0 then task.curSample = task.curSample % loop_period;
}
if task_finished {
array_unordered_remove_by_index(*g_mixer.tasks, i);
}

View File

@ -6,14 +6,16 @@ Console_State :: enum {
OPEN_FULL;
}
MAX_REPORT_ROWS :: 100;
MAX_REPORT_ROWS :: 5000;
console_state : Console_State = .CLOSED;
console_report : [..]string;
console_h : float = 0.0;
console_move_speed : float = 400.0;
console_h : float = 0.0;
console_move_speed : float = 400.0;
console_scroll_offset : int = 0; // lines scrolled up from the most recent
console_at_bottom : bool = true; // auto-follow new lines unless user has scrolled
get_console_h_target_from_state :: (state: Console_State) -> float {
if state == {
@ -79,7 +81,7 @@ verify_argument_count :: (range_start: s64, range_end: s64, count: s64) -> bool
}
tick_console :: () {
if input_button_states[Key_Code.F1] & .START {
if input_button_states[get_action_combo(Editor_Action.TOGGLE_CONSOLE).key] & .START {
if console_state != .CLOSED {
if input_button_states[Key_Code.SHIFT] & .DOWN {
if console_state == .OPEN_FULL {
@ -113,12 +115,21 @@ tick_console :: () {
}
}
while console_report.count > MAX_REPORT_ROWS {
array_ordered_remove_by_index(*console_report, 0);
}
if console_state != .CLOSED {
console_open_ignore_input = true;
line_h := ui_h(5, 4);
max_visible := cast(int)((console_h - line_h) / line_h);
max_scroll := max(0, console_report.count - max_visible);
scroll_delta := 0;
if input_button_states[Key_Code.PAGE_UP] & .START scroll_delta = max_visible;
if input_button_states[Key_Code.PAGE_DOWN] & .START scroll_delta = -max_visible;
if scroll_delta != 0 {
console_scroll_offset = clamp(console_scroll_offset + scroll_delta, 0, max_scroll);
console_at_bottom = (console_scroll_offset == 0);
}
} else {
console_open_ignore_input = false;
}
@ -126,6 +137,11 @@ tick_console :: () {
console_add_output_line :: (s: string) {
array_add(*console_report, s);
while console_report.count > MAX_REPORT_ROWS {
array_ordered_remove_by_index(*console_report, 0);
if console_scroll_offset > 0 then console_scroll_offset -= 1;
}
if console_at_bottom then console_scroll_offset = 0;
}
draw_console :: (theme: *GR.Overall_Theme) {
@ -157,9 +173,21 @@ draw_console :: (theme: *GR.Overall_Theme) {
if !found then array_add(*console_report, "Unknown command.");
}
for < console_report {
max_visible := cast(int)((console_h - r.h) / r.h);
max_scroll := max(0, console_report.count - max_visible);
console_scroll_offset = clamp(console_scroll_offset, 0, max_scroll);
start_idx := console_report.count - 1 - console_scroll_offset;
for i: 0..max_visible-1 {
idx := start_idx - i;
if idx < 0 break;
r.y -= r.h;
GR.label(r, it, *t_label_left(theme));
GR.label(r, console_report[idx], *t_label_left(theme));
}
if console_scroll_offset > 0 {
r.y -= r.h;
GR.label(r, tprint("[ ↑ % more ]", console_scroll_offset), *t_label_left(theme));
}
if !state.active then GR.activate(state);

View File

@ -27,6 +27,8 @@ in_editor_view : bool = false;
init_editor :: () {
#if OS != .WASM {init_profiler();}
init_console();
init_keybinds();
init_settings_menu();
}
// Used when the console is open, so typing doesn't cause all sorts of stuff.
@ -44,16 +46,11 @@ draw_editor_ui :: (theme: *GR.Overall_Theme) {
GR.label(r, tprint("Trueno v%.%", V_MAJOR, V_MINOR), *t_label_left(theme));
r.x = 100*vw - r.w;
if GR.button(r, "Level studio", *t_button_selectable(theme, current_editor_view == .Level_Editor))
then current_editor_view = .Level_Editor;
if keybind_button(r, "Level studio", .STUDIO_LEVEL, *t_button_selectable(theme, current_editor_view == .Level_Editor)) then current_editor_view = .Level_Editor;
r.x -= r.w;
if GR.button(r, "Trile studio", *t_button_selectable(theme, current_editor_view == .Trile_Editor))
then current_editor_view = .Trile_Editor;
if keybind_button(r, "Trile studio", .STUDIO_TRILE, *t_button_selectable(theme, current_editor_view == .Trile_Editor)) then current_editor_view = .Trile_Editor;
r.x -= r.w;
if GR.button(r, "Material studio", *t_button_selectable(theme, current_editor_view == .Material_Editor))
then current_editor_view = .Material_Editor;
if keybind_button(r, "Material studio", .STUDIO_MATERIAL, *t_button_selectable(theme, current_editor_view == .Material_Editor)) then current_editor_view = .Material_Editor;
if current_editor_view == {
@ -65,7 +62,6 @@ draw_editor_ui :: (theme: *GR.Overall_Theme) {
}
draw_profiler();
}
draw_console(theme);
draw_texture_debugger(theme);
draw_postprocess_popup(theme);
draw_lighting_popup(theme);
@ -89,15 +85,16 @@ draw_editor :: () {
}
tick_editor_ui :: () {
if input_button_states[Key_Code.F3] & .START {
if is_action_start(Editor_Action.TOGGLE_EDITOR) {
in_editor_view = !in_editor_view;
g_mixer.paused = in_editor_view;
if !in_editor_view then bypass_postprocess = false;
}
#if !RELEASE_BUILD && OS != .WASM {
if input_button_states[Key_Code.F5] & .START {
#if !FLAG_RELEASE_BUILD && OS != .WASM {
if is_action_start(Editor_Action.HOT_RELOAD) {
g_asset_manager.pending_hot_reload = true;
print("Hot-reload: queued, waiting for in-flight loads to finish...\n");
log_info("Hot-reload: queued, waiting for in-flight loads to finish...");
}
}
@ -113,4 +110,5 @@ tick_editor_ui :: () {
}
tick_console();
tick_settings_menu();
}

View File

@ -1,4 +1,4 @@
#if HAS_IPROF == false {
#if FLAG_IPROF_ENABLED == false {
init_profiler :: () {}

View File

@ -32,11 +32,13 @@ brush_radius : int = 2;
brush_height : int = 0;
area_active : bool;
area_delete : bool;
area_start_x : int;
area_start_y : int;
area_start_z : int;
line_active : bool;
line_delete : bool;
line_start_x : int;
line_start_y : int;
line_start_z : int;
@ -152,12 +154,12 @@ tick_level_editor_camera :: () {
cameraCenter += cameraSpeed * zoomCameraMovementMultiplier * (xx delta_time) * left2d;
}
if input_button_states[Key_Code.ARROW_UP] & .START {
if is_action_start(Editor_Action.LEVEL_HEIGHT_UP) {
lastInputTime = get_time();
editY = min(editY + 1, 100);
}
if input_button_states[Key_Code.ARROW_DOWN] & .START {
if is_action_start(Editor_Action.LEVEL_HEIGHT_DOWN) {
lastInputTime = get_time();
editY = max(editY - 1, 0);
}
@ -278,24 +280,17 @@ draw_tools_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) {
r := total_r;
r.h = ui_h(4, 0);
if keybind_button(r, "Save world", .SAVE, *theme.button_theme) then sworld();
r.y += r.h * 2;
// Tool mode buttons
if GR.button(r, "Point", *t_button_selectable(theme, current_tool_mode == .POINT)) {
current_tool_mode = .POINT;
}
if keybind_button(r, "Point", .LEVEL_TOOL_POINT, *t_button_selectable(theme, current_tool_mode == .POINT)) then current_tool_mode = .POINT;
r.y += r.h;
if GR.button(r, "Brush", *t_button_selectable(theme, current_tool_mode == .BRUSH)) {
current_tool_mode = .BRUSH;
}
if keybind_button(r, "Brush", .LEVEL_TOOL_BRUSH, *t_button_selectable(theme, current_tool_mode == .BRUSH)) then current_tool_mode = .BRUSH;
r.y += r.h;
if GR.button(r, "Area", *t_button_selectable(theme, current_tool_mode == .AREA)) {
current_tool_mode = .AREA;
area_active = false;
}
if keybind_button(r, "Area", .LEVEL_TOOL_AREA, *t_button_selectable(theme, current_tool_mode == .AREA)) { current_tool_mode = .AREA; area_active = false; }
r.y += r.h;
if GR.button(r, "Line", *t_button_selectable(theme, current_tool_mode == .LINE)) {
current_tool_mode = .LINE;
line_active = false;
}
if keybind_button(r, "Line", .LEVEL_TOOL_LINE, *t_button_selectable(theme, current_tool_mode == .LINE)) { current_tool_mode = .LINE; line_active = false; }
r.y += r.h;
// Brush radius/height (only for brush mode)
@ -342,7 +337,10 @@ draw_tools_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) {
twist_angle := current_orientation_twist * 90;
GR.label(r, tprint("Face: % Twist: %°", current_orientation_face, twist_angle), *t_label_left(theme));
r.y += r.h;
GR.label(r, "Q/E: twist R: face", *t_label_left(theme));
GR.label(r, tprint("%/%: twist %: face",
key_combo_string(get_action_combo(Editor_Action.LEVEL_TWIST_CCW)),
key_combo_string(get_action_combo(Editor_Action.LEVEL_TWIST_CW)),
key_combo_string(get_action_combo(Editor_Action.LEVEL_CYCLE_FACE))), *t_label_left(theme));
r.y += r.h;
}
@ -455,19 +453,23 @@ tick_level_editor :: () {
#if FLAG_TACOMA_ENABLED { rdm_bake_tick(); }
tick_level_editor_camera();
if !console_open_ignore_input {
if input_button_states[#char "Q"] & .START {
lastInputTime = get_time();
current_orientation_twist = (current_orientation_twist + 1) % 4;
}
if input_button_states[#char "E"] & .START {
lastInputTime = get_time();
current_orientation_twist = (current_orientation_twist + 3) % 4;
}
if input_button_states[#char "R"] & .START {
lastInputTime = get_time();
current_orientation_face = (current_orientation_face + 1) % 6;
}
if is_action_start(Editor_Action.SAVE) then sworld();
if is_action_start(Editor_Action.LEVEL_TOOL_POINT) then current_tool_mode = .POINT;
if is_action_start(Editor_Action.LEVEL_TOOL_BRUSH) then current_tool_mode = .BRUSH;
if is_action_start(Editor_Action.LEVEL_TOOL_AREA) { current_tool_mode = .AREA; area_active = false; }
if is_action_start(Editor_Action.LEVEL_TOOL_LINE) { current_tool_mode = .LINE; line_active = false; }
if is_action_start(Editor_Action.LEVEL_TWIST_CCW) {
lastInputTime = get_time();
current_orientation_twist = (current_orientation_twist + 1) % 4;
}
if is_action_start(Editor_Action.LEVEL_TWIST_CW) {
lastInputTime = get_time();
current_orientation_twist = (current_orientation_twist + 3) % 4;
}
if is_action_start(Editor_Action.LEVEL_CYCLE_FACE) {
lastInputTime = get_time();
current_orientation_face = (current_orientation_face + 1) % 6;
}
ray := get_mouse_ray(*get_level_editor_camera());
@ -502,33 +504,59 @@ tick_level_editor :: () {
if get_mouse_state(Key_Code.MOUSE_BUTTON_LEFT) & .START {
if !area_active {
area_active = true;
area_delete = false;
area_start_x = px;
area_start_y = py;
area_start_z = pz;
} else {
} else if !area_delete {
apply_area(px, py, pz, false);
area_active = false;
} else {
area_active = false; // cancel delete selection
}
}
if get_mouse_state(Key_Code.MOUSE_BUTTON_RIGHT) & .START {
if area_active then area_active = false;
else handle_tool_click(px, py, pz, true);
if !area_active {
area_active = true;
area_delete = true;
area_start_x = px;
area_start_y = py;
area_start_z = pz;
} else if area_delete {
apply_area(px, py, pz, true);
area_active = false;
} else {
area_active = false; // cancel add selection
}
}
} else if current_tool_mode == .LINE {
if get_mouse_state(Key_Code.MOUSE_BUTTON_LEFT) & .START {
if !line_active {
line_active = true;
line_delete = false;
line_start_x = px;
line_start_y = py;
line_start_z = pz;
} else {
} else if !line_delete {
apply_line(px, py, pz, false);
line_active = false;
} else {
line_active = false; // cancel delete selection
}
}
if get_mouse_state(Key_Code.MOUSE_BUTTON_RIGHT) & .START {
if line_active then line_active = false;
else handle_tool_click(px, py, pz, true);
if !line_active {
line_active = true;
line_delete = true;
line_start_x = px;
line_start_y = py;
line_start_z = pz;
} else if line_delete {
apply_line(px, py, pz, true);
line_active = false;
} else {
line_active = false; // cancel add selection
}
}
}
}
@ -596,11 +624,14 @@ create_level_editor_preview_tasks :: () {
if positions.count == 0 then return;
is_delete := (current_tool_mode == .AREA && area_active && area_delete)
|| (current_tool_mode == .LINE && line_active && line_delete);
task: Rendering_Task_Trile;
task.trile = editor_current_trile.name;
task.positions = positions;
task.worldConf = *curworld.world.conf;
task.is_preview = true;
task.trile = editor_current_trile.name;
task.positions = positions;
task.worldConf = *curworld.world.conf;
task.preview_mode = ifx is_delete then cast(s32)2 else cast(s32)1;
add_rendering_task(task);
}
@ -623,17 +654,11 @@ draw_level_editor_ui :: (theme: *GR.Overall_Theme) {
tab_r.h = ui_h(3,0);
tab_r.w = ui_w(20, 20) / 3;
if GR.button(tab_r, "Tools", *t_button_tab(theme, current_tab == .TOOLS)) {
current_tab = .TOOLS;
}
if keybind_button(tab_r, "Tools", .LEVEL_TAB_TOOLS, *t_button_tab(theme, current_tab == .TOOLS)) then current_tab = .TOOLS;
tab_r.x += tab_r.w;
if GR.button(tab_r, "Info", *t_button_tab(theme, current_tab == .INFO)) {
current_tab = .INFO;
}
if keybind_button(tab_r, "Info", .LEVEL_TAB_INFO, *t_button_tab(theme, current_tab == .INFO)) then current_tab = .INFO;
tab_r.x += tab_r.w;
if GR.button(tab_r, "Tacoma", *t_button_tab(theme, current_tab == .TACOMA)) {
current_tab = .TACOMA;
}
if keybind_button(tab_r, "Tacoma", .LEVEL_TAB_TACOMA, *t_button_tab(theme, current_tab == .TACOMA)) then current_tab = .TACOMA;
r.y += tab_r.h;
curworld := get_current_world();

View File

@ -280,7 +280,7 @@ rdm_bake_start :: (world: World, quality: s32, include_water: bool, chunk_keys:
}
atlas_w, atlas_h := rdm_calc_chunk_atlas_size(this_jobs);
print("RDM atlas for chunk %: %x% (%.1f MB, was % MB)\n",
log_info("RDM atlas for chunk %: %x% (%.1f MB, was % MB)",
chunk_key, atlas_w, atlas_h,
cast(float)(cast(s64) atlas_w * atlas_h * 4 * size_of(float)) / (1024.0 * 1024.0),
RDM_ATLAS_SIZE * RDM_ATLAS_SIZE * 4 * size_of(float) / (1024 * 1024));
@ -360,7 +360,7 @@ rdm_bake_tick :: () {
entry.h = h;
array_add(*bake.entries, entry);
} else {
print("Warning: RDM atlas overflow for chunk %, skipping (pos=%, roughness=%)\n", chunk_key, job.world_pos, job.roughness);
log_warn("RDM atlas overflow for chunk %, skipping (pos=%, roughness=%)", chunk_key, job.world_pos, job.roughness);
}
bake.cursor_x += w;
@ -461,7 +461,7 @@ rdm_bake_finish :: () {
chunk_count += 1;
}
print("RDM bake complete: % chunks, % total entries\n", chunk_count, total_entries);
log_info("RDM bake complete: % chunks, % total entries", chunk_count, total_entries);
// Save baked RDM data to disk.
rdm_save_all_chunks_to_disk();
@ -541,12 +541,12 @@ tacoma_start :: (world: World, include_water: bool) {
blases : Tacoma.Trile_Set = .{trile_list.data, cast(s32)trile_list.count};
for world_triles {
print("World trile %\n", it);
log_debug("World trile %", it);
}
tlas : Tacoma.World = .{world_triles.data, cast(s32)world_triles.count};
ctx = Tacoma.tacoma_init("./modules/Tacoma/");
print("CTX: %\n\n", ctx);
log_debug("CTX: %", ctx);
Tacoma.tacoma_load_scene(ctx, sky, blases, tlas, cast(s32) include_water);
}
@ -666,7 +666,7 @@ rdm_save_all_chunks_to_disk :: () {
lookup_path := rdm_chunk_filename(world_name, chunk_key, "rdm_lookup");
rdm_save_image_to_file(lookup_path, lookup_data.data, RDM_LOOKUP_SIZE, RDM_LOOKUP_SIZE);
print("Saved RDM data for chunk % to disk\n", chunk_key);
log_info("Saved RDM data for chunk % to disk", chunk_key);
}
}
}

View File

@ -254,34 +254,22 @@ trile_palette_scroll : float = 0.0;
draw_tool_tab :: (theme: *GR.Overall_Theme, area: GR.Rect) {
r := area;
r.h = ui_h(4, 0);
if GR.button(r, "Paint (P)", *t_button_selectable(theme, current_tool == .PAINT)) {
current_tool = .PAINT;
}
if keybind_button(r, "Paint", .TRIXEL_TOOL_PAINT, *t_button_selectable(theme, current_tool == .PAINT)) then current_tool = .PAINT;
r.y += r.h;
if GR.button(r, "Add (+)", *t_button_selectable(theme, current_tool == .ADD)) {
current_tool = .ADD;
}
if keybind_button(r, "Add", .TRIXEL_TOOL_ADD, *t_button_selectable(theme, current_tool == .ADD)) then current_tool = .ADD;
r.y += r.h;
if GR.button(r, "Remove (-)", *t_button_selectable(theme, current_tool == .REMOVE)) {
current_tool = .REMOVE;
}
if keybind_button(r, "Remove", .TRIXEL_TOOL_REMOVE, *t_button_selectable(theme, current_tool == .REMOVE)) then current_tool = .REMOVE;
r.y += r.h * 3;
if GR.button(r, "Point (c-P)", *t_button_selectable(theme, current_mode == .POINT)) {
current_mode = .POINT;
}
if keybind_button(r, "Point", .TRIXEL_MODE_POINT, *t_button_selectable(theme, current_mode == .POINT)) then current_mode = .POINT;
r.y += r.h;
if GR.button(r, "Area (c-A)", *t_button_selectable(theme, current_mode == .AREA)) {
current_mode = .AREA;
}
if keybind_button(r, "Area", .TRIXEL_MODE_AREA, *t_button_selectable(theme, current_mode == .AREA)) then current_mode = .AREA;
r.y += r.h;
if GR.button(r, "Brush (c-B)", *t_button_selectable(theme, current_mode == .BRUSH)) {
current_mode = .BRUSH;
}
if keybind_button(r, "Brush", .TRIXEL_MODE_BRUSH, *t_button_selectable(theme, current_mode == .BRUSH)) then current_mode = .BRUSH;
r.y += r.h;
GR.slider(r, *brush_radius, 0, 12, 1, *theme.slider_theme);
r.y += r.h * 2;
if GR.button(r, "Save and gen", *theme.button_theme) {
if keybind_button(r, "Save and gen", .SAVE, *theme.button_theme) {
set_trile_gfx(editor_current_trile.name, generate_trile_gfx_matias(editor_current_trile));
striles();
};
@ -327,38 +315,17 @@ draw_trile_editor :: () {
}
trile_editor_shortcuts :: () {
if console_open_ignore_input then return;
if input_button_states[Key_Code.CTRL] & .DOWN {
if input_button_states[#char "A"] & .START {
current_mode = .AREA;
}
if input_button_states[#char "P"] & .START {
current_mode = .POINT;
}
if input_button_states[#char "B"] & .START {
current_mode = .BRUSH;
}
} else {
if input_button_states[#char "T"] & .START {
current_tab = .TOOLSET;
}
if input_button_states[#char "M"] & .START {
current_tab = .MATERIAL;
}
if input_button_states[#char "I"] & .START {
current_tab = .METADATA;
}
if input_button_states[#char "P"] & .START {
current_tool = .PAINT;
}
// Hack: These will only work on nordic keyboards as expected...
if input_button_states[#char "-"] & .START {
current_tool = .ADD;
}
if input_button_states[#char "/"] & .START {
current_tool = .REMOVE;
}
// Tool/mode buttons are only rendered in the Toolset tab — handle shortcuts here so they work from any tab.
if is_action_start(Editor_Action.TRIXEL_TOOL_PAINT) then current_tool = .PAINT;
if is_action_start(Editor_Action.TRIXEL_TOOL_ADD) then current_tool = .ADD;
if is_action_start(Editor_Action.TRIXEL_TOOL_REMOVE) then current_tool = .REMOVE;
if is_action_start(Editor_Action.TRIXEL_MODE_POINT) then current_mode = .POINT;
if is_action_start(Editor_Action.TRIXEL_MODE_AREA) then current_mode = .AREA;
if is_action_start(Editor_Action.TRIXEL_MODE_BRUSH) then current_mode = .BRUSH;
// Save is in the Toolset tab only.
if is_action_start(Editor_Action.SAVE) {
set_trile_gfx(editor_current_trile.name, generate_trile_gfx_matias(editor_current_trile));
striles();
}
}
@ -370,17 +337,11 @@ draw_trile_editor_ui :: (theme: *GR.Overall_Theme) {
tab_r.h = ui_h(3,0);
tab_r.w = ui_w(20, 20) / 3;
if GR.button(tab_r, "Tool (T)", *t_button_tab(theme, current_tab == .TOOLSET)) {
current_tab = .TOOLSET;
}
if keybind_button(tab_r, "Tools", .TRIXEL_TAB_TOOLS, *t_button_tab(theme, current_tab == .TOOLSET)) then current_tab = .TOOLSET;
tab_r.x += tab_r.w;
if GR.button(tab_r, "Material (M)", *t_button_tab(theme, current_tab == .MATERIAL)) {
current_tab = .MATERIAL;
}
if keybind_button(tab_r, "Material", .TRIXEL_TAB_MATERIAL, *t_button_tab(theme, current_tab == .MATERIAL)) then current_tab = .MATERIAL;
tab_r.x += tab_r.w;
if GR.button(tab_r, "Meta (I)", *t_button_tab(theme, current_tab == .METADATA)) {
current_tab = .METADATA;
}
if keybind_button(tab_r, "Meta", .TRIXEL_TAB_INFO, *t_button_tab(theme, current_tab == .METADATA)) then current_tab = .METADATA;
r.y += tab_r.h;
@ -406,19 +367,11 @@ draw_trile_editor_ui :: (theme: *GR.Overall_Theme) {
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;
}
if keybind_button(r2, "Lit", .TRIXEL_VIEW_LIT, *t_button_selectable(theme, trixel_view_mode == 0)) then 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;
}
if keybind_button(r2, "Normals", .TRIXEL_VIEW_NORMALS, *t_button_selectable(theme, trixel_view_mode == 1)) then 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;
}
if keybind_button(r2, "Albedo", .TRIXEL_VIEW_ALBEDO, *t_button_selectable(theme, trixel_view_mode == 2)) then 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;
}
if keybind_button(r2, "Mixed", .TRIXEL_VIEW_MIXED, *t_button_selectable(theme, trixel_view_mode == 3)) then trixel_view_mode = 3;
}

View File

@ -1 +1 @@
#load "keybinds.jai";

182
src/input/keybinds.jai Normal file
View File

@ -0,0 +1,182 @@
Editor_Action :: enum {
TOGGLE_CONSOLE;
TOGGLE_EDITOR;
TOGGLE_SETTINGS;
HOT_RELOAD;
// Level editor
LEVEL_HEIGHT_UP;
LEVEL_HEIGHT_DOWN;
LEVEL_TWIST_CW;
LEVEL_TWIST_CCW;
LEVEL_CYCLE_FACE;
// Trile editor — tools
TRIXEL_TOOL_PAINT;
TRIXEL_TOOL_ADD;
TRIXEL_TOOL_REMOVE;
// Trile editor — modes
TRIXEL_MODE_POINT;
TRIXEL_MODE_AREA;
TRIXEL_MODE_BRUSH;
// Trile editor — tabs
TRIXEL_TAB_TOOLS;
TRIXEL_TAB_MATERIAL;
TRIXEL_TAB_INFO;
// Level editor — tool modes
LEVEL_TOOL_POINT;
LEVEL_TOOL_BRUSH;
LEVEL_TOOL_AREA;
LEVEL_TOOL_LINE;
// Level editor — tabs
LEVEL_TAB_TOOLS;
LEVEL_TAB_INFO;
LEVEL_TAB_TACOMA;
// Shared
SAVE;
// Top-level studio tabs
STUDIO_TRILE;
STUDIO_LEVEL;
STUDIO_MATERIAL;
// Trile editor — view modes
TRIXEL_VIEW_LIT;
TRIXEL_VIEW_NORMALS;
TRIXEL_VIEW_ALBEDO;
TRIXEL_VIEW_MIXED;
}
#scope_file;
EDITOR_ACTION_COUNT :: #run enum_highest_value(Editor_Action) + 1;
game_keybind_enum_type : Type;
Key_Combo :: struct {
key : Key_Code;
ctrl : bool;
shift : bool;
}
// 5000 different actions ought to be enough for anything right?
keys : [5000]Key_Combo;
check_action :: (action: $T, status: Key_Current_State) -> bool {
offset := 0;
if T != Editor_Action {
offset = EDITOR_ACTION_COUNT;
}
if console_open_ignore_input then return false;
combo := keys[cast(s32)action + offset];
ctrl_down := cast(bool)(input_button_states[Key_Code.CTRL] & .DOWN);
shift_down := cast(bool)(input_button_states[Key_Code.SHIFT] & .DOWN);
if combo.ctrl != ctrl_down then return false;
if combo.shift != shift_down then return false;
return cast(bool)(input_button_states[combo.key] & status);
}
#scope_export;
set_binding :: (action: $T, combo: Key_Combo) {
offset := 0;
if T != Editor_Action {
offset = EDITOR_ACTION_COUNT;
}
keys[cast(s32)action + offset] = combo;
}
is_action_down :: (action: $T) -> bool {
return check_action(action, .DOWN);
}
is_action_start :: (action: $T) -> bool {
return check_action(action, .START);
}
init_keybinds :: () {
set_default_bindings();
}
get_action_combo :: (action: $T) -> Key_Combo {
offset := 0;
#if T != Editor_Action {
offset = EDITOR_ACTION_COUNT;
}
return keys[cast(s32)action + offset];
}
keybind_button :: (r: GR.Rect, label: string, action: Editor_Action, theme: *GR.Button_Theme, loc := #caller_location) -> bool {
combo := get_action_combo(action);
full_label := tprint("% [%]", label, key_combo_string(combo));
clicked := GR.button(r, full_label, theme, loc = loc);
return clicked || is_action_start(action);
}
key_combo_string :: (combo: Key_Combo) -> string {
k := key_code_to_string(combo.key);
if combo.ctrl && combo.shift return tprint("C+S+%", k);
if combo.ctrl return tprint("C+%", k);
if combo.shift return tprint("S+%", k);
return k;
}
key_code_to_string :: (key: Key_Code) -> string {
if key == .ARROW_UP return "↑";
if key == .ARROW_DOWN return "↓";
if key == .ARROW_LEFT return "←";
if key == .ARROW_RIGHT return "→";
if key == .F1 return "F1"; if key == .F2 return "F2";
if key == .F3 return "F3"; if key == .F4 return "F4";
if key == .F5 return "F5"; if key == .F6 return "F6";
if key == .F7 return "F7"; if key == .F8 return "F8";
if key == .F9 return "F9"; if key == .F10 return "F10";
if key == .F11 return "F11"; if key == .F12 return "F12";
v := cast(s64) key;
if v >= 32 && v < 127 {
buf := cast(*u8) talloc(2);
buf[0] = cast(u8) v;
buf[1] = 0;
return string.{ count = 1, data = buf };
}
return "?";
}
#scope_file
set_default_bindings :: () {
set :: (a: Editor_Action, k: Key_Code, ctrl := false, shift := false) #expand {
set_binding(a, .{ k, ctrl, shift });
}
set(.TOGGLE_CONSOLE, .F1);
set(.TOGGLE_EDITOR, .F3);
set(.TOGGLE_SETTINGS, .ESCAPE);
set(.HOT_RELOAD, .F5);
set(.LEVEL_HEIGHT_UP, .ARROW_UP);
set(.LEVEL_HEIGHT_DOWN, .ARROW_DOWN);
set(.LEVEL_TWIST_CW, cast(Key_Code) #char "E");
set(.LEVEL_TWIST_CCW, cast(Key_Code) #char "Q");
set(.LEVEL_CYCLE_FACE, cast(Key_Code) #char "R");
set(.TRIXEL_TOOL_PAINT, cast(Key_Code) #char "P");
set(.TRIXEL_TOOL_ADD, cast(Key_Code) #char "-");
set(.TRIXEL_TOOL_REMOVE, cast(Key_Code) #char "/");
set(.TRIXEL_MODE_POINT, cast(Key_Code) #char "P", ctrl = true);
set(.TRIXEL_MODE_AREA, cast(Key_Code) #char "A", ctrl = true);
set(.TRIXEL_MODE_BRUSH, cast(Key_Code) #char "B", ctrl = true);
set(.TRIXEL_TAB_TOOLS, cast(Key_Code) #char "T");
set(.TRIXEL_TAB_MATERIAL, cast(Key_Code) #char "M");
set(.TRIXEL_TAB_INFO, cast(Key_Code) #char "I");
set(.LEVEL_TOOL_POINT, cast(Key_Code) #char "1");
set(.LEVEL_TOOL_BRUSH, cast(Key_Code) #char "2");
set(.LEVEL_TOOL_AREA, cast(Key_Code) #char "3");
set(.LEVEL_TOOL_LINE, cast(Key_Code) #char "4");
set(.LEVEL_TAB_TOOLS, cast(Key_Code) #char "T");
set(.LEVEL_TAB_INFO, cast(Key_Code) #char "I");
set(.LEVEL_TAB_TACOMA, cast(Key_Code) #char "X");
set(.SAVE, cast(Key_Code) #char "S", ctrl = true);
set(.STUDIO_TRILE, cast(Key_Code) #char "1", ctrl = true);
set(.STUDIO_LEVEL, cast(Key_Code) #char "2", ctrl = true);
set(.STUDIO_MATERIAL, cast(Key_Code) #char "3", ctrl = true);
set(.TRIXEL_VIEW_LIT, cast(Key_Code) #char "5");
set(.TRIXEL_VIEW_NORMALS, cast(Key_Code) #char "6");
set(.TRIXEL_VIEW_ALBEDO, cast(Key_Code) #char "7");
set(.TRIXEL_VIEW_MIXED, cast(Key_Code) #char "8");
}

14
src/loading_screen.jai Normal file
View File

@ -0,0 +1,14 @@
Loading_Screen_Config :: struct {
background_color : Vector4 = .{ 0.05, 0.05, 0.05, 1.0 };
bar_bg_color : Vector4 = .{ 0.15, 0.15, 0.15, 1.0 };
bar_fill_color : Vector4 = .{ 0.3, 0.7, 1.0, 1.0 };
}
loading_screen_config : Loading_Screen_Config;
set_loading_screen_config :: (config: Loading_Screen_Config) {
loading_screen_config = config;
}
draw_loading_screen :: () {
}

61
src/logging.jai Normal file
View File

@ -0,0 +1,61 @@
Log_Level :: enum {
DEBUG;
INFO;
WARN;
ERROR;
}
log_min_level : Log_Level = .INFO;
#scope_file
_emit :: (level: Log_Level, message: string) {
if level < log_min_level return;
prefix := ifx level == .DEBUG then "[DEBUG] "
else ifx level == .WARN then "[WARN] "
else ifx level == .ERROR then "[ERROR] "
else "[INFO] ";
// Always allocate on the heap regardless of context.allocator (e.g. mesh pool).
old_alloc := context.allocator;
context.allocator = default_context.allocator;
line := copy_string(tprint("%1%2", prefix, message));
context.allocator = old_alloc;
print("%\n", line);
console_add_output_line(line);
}
#scope_export
logger :: (level: Log_Level, fmt: string, args: ..Any) {
if level < log_min_level return;
_emit(level, tprint(fmt, ..args));
}
// Hook for Jai's context.logger — catches log() calls from Basic and libraries.
_logger_proc :: (message: string, data: *void, info: Log_Info) {
level : Log_Level;
if info.common_flags & .ERROR then level = .ERROR;
else if info.common_flags & .WARNING then level = .WARN;
else if (info.common_flags & .VERBOSE_ONLY) || (info.common_flags & .VERY_VERBOSE_ONLY) then level = .DEBUG;
else level = .INFO;
_emit(level, message);
}
log_debug :: (fmt: string, args: ..Any) { logger(.DEBUG, fmt, ..args); }
log_info :: (fmt: string, args: ..Any) { logger(.INFO, fmt, ..args); }
log_warn :: (fmt: string, args: ..Any) { logger(.WARN, fmt, ..args); }
log_error :: (fmt: string, args: ..Any) { logger(.ERROR, fmt, ..args); }
set_log_level :: (level_str: string) -> string {
if level_str == {
case "DEBUG"; log_min_level = .DEBUG;
case "INFO"; log_min_level = .INFO;
case "WARN"; log_min_level = .WARN;
case "ERROR"; log_min_level = .ERROR;
case; return tprint("Unknown log level '%'. Use DEBUG, INFO, WARN or ERROR.", level_str);
}
return tprint("Log level set to %.", level_str);
} @Command

View File

@ -15,7 +15,11 @@ String :: #import "String";
Jaison :: #import "Jaison";
stbi :: #import "stb_image";
#if FLAG_TEST_ENGINE { #load "tests/index.jai"; }
#load "logging.jai";
#load "pseudophysics/core.jai";
#load "pack_hotreload.jai";
#load "trile.jai";
#load "rendering/rendering.jai";
#load "input/hotkeys.jai";
@ -30,6 +34,8 @@ stbi :: #import "stb_image";
#load "utils.jai";
#load "audio/audio.jai";
#load "assets/asset_manager.jai";
#load "settings_menu.jai";
#load "ui/demo.jai";
#load "../game/game.jai";
@ -39,7 +45,7 @@ delta\ _time : float64;
latest_frametime : float64; // latest frame generation duration
V_MAJOR :: 0;
V_MINOR :: 6;
V_MINOR :: 7;
state: struct {
pass_action_clear : sg_pass_action;
@ -113,16 +119,12 @@ init :: () {
}
init_after_mandatory :: () {
}
init_after_core :: () {
init_plane_textures();
init_brdf_lut();
add_font_from_pack("boot", "DroidSerif-Regular.ttf");
ui_init_font_fields(*state.font_default);
init_ui();
ltriles();
tt := get_trile_table_ptr();
@ -163,13 +165,7 @@ frame :: () {
if !mandatory_loads_done() then return;
if !init_after_mandatory_done {
init_after_mandatory();
init_after_mandatory_done = true;
}
if show_loading_screen() {
print("Should show loading screen....\n");
return;
}
@ -190,11 +186,11 @@ frame :: () {
if !in_editor_view then delta_time_accumulator += delta_time;
if !in_editor_view {
// while delta_time_accumulator > (1.0/60.0) {
game_tick(1.0/144.0);
// delta_time_accumulator -= (1.0/60.0);
// }
if !in_editor_view && !settings_menu_blocks_game() {
while delta_time_accumulator > (1.0/480.0) {
game_tick(1.0/480.0);
delta_time_accumulator -= (1.0/480.0);
}
}
fonsClearState(state.fons);
for event: Input.events_this_frame {

View File

@ -1,4 +1,4 @@
#if !RELEASE_BUILD && OS != .WASM {
#if !FLAG_RELEASE_BUILD && OS != .WASM {
#import "String";
Pack_Writer :: #import "Simple_Package";
@ -25,7 +25,7 @@ _hotreload_visitor :: (info: *File_Util.File_Visit_Info, packs: *Table(string, P
file_content, ok := File_IO.read_entire_file(info.full_name);
if !ok {
print("Hot-reload: failed to read '%'\n", info.full_name);
log_warn("Hot-reload: failed to read '%'", info.full_name);
return;
}
data: []u8;
@ -40,7 +40,7 @@ recreate_packs_on_disk :: () {
File_Util.visit_files("./game/resources", true, *packs, _hotreload_visitor);
for pack, key: packs {
Pack_Writer.write(*pack, tprint("./packs/%.pack", key));
print("Hot-reload: wrote pack '%'\n", key);
log_info("Hot-reload: wrote pack '%'", key);
}
}
@ -72,13 +72,13 @@ hot_reload_all_packs :: () {
// is refreshed with the new content.
init_after_core_done = false;
print("Hot-reload: recreating packs on disk...\n");
log_info("Hot-reload: recreating packs on disk...");
recreate_packs_on_disk();
for name: pack_names {
load_pack(name, true, false);
}
print("Hot-reload: queued % pack(s) for reload\n", pack_names.count);
log_info("Hot-reload: queued % pack(s) for reload", pack_names.count);
}
} // #if !RELEASE_BUILD && OS != .WASM

View File

@ -5,14 +5,6 @@ main :: () {
sapp_init();
}
log_warn :: (format_string: string, args: .. Any, loc := #caller_location, flags := Log_Flags.NONE, user_flags : u32 = 0, section : *Log_Section = null) {
log(format_string, ..args, loc = loc, flags = flags | .WARNING, user_flags = user_flags, section = section);
} @PrintLike
log_content :: (format_string: string, args: .. Any, loc := #caller_location, flags := Log_Flags.NONE, user_flags : u32 = 0, section : *Log_Section = null) {
log(format_string, ..args, loc = loc, flags = flags | .CONTENT, user_flags = user_flags, section = section);
} @PrintLike
logger :: (message: string, data: *void, info: Log_Info) {
print("% % %\n", message, data, info);
_logger_proc(message, data, info);
}

View File

@ -40,15 +40,14 @@ Ray_Collision :: struct {
normal : Vector3;
}
// At which 2D XZ point does a ray hit the ground plane at a height of
// plane_height. Used for the level editor and maybe later the trile editor as well.
// At which 2D XZ point does a ray hit the horizontal plane at plane_height.
// Works whether the camera is above or below the plane.
ray_plane_collision_point :: (ray: Ray, plane_height: float, acceptable_radius: float = -1) -> (bool, Vector2) {
dist_to_plane := ray.origin.y - plane_height;
if ray.direction.y >= 0 then return false, .{0,0};
multi := dist_to_plane / abs(ray.direction.y);
ray_to_plane := ray.direction * multi;
planePoint := ray_to_plane + ray.origin;
if acceptable_radius > 0 && abs(length(Vector2.{planePoint.x, planePoint.z} - Vector2.{ray.origin.x, ray.origin.z})) > acceptable_radius {
if abs(ray.direction.y) < 0.0001 then return false, .{0,0}; // parallel to plane
multi := (plane_height - ray.origin.y) / ray.direction.y;
if multi < 0 then return false, .{0,0}; // plane is behind the camera
planePoint := ray.origin + ray.direction * multi;
if acceptable_radius > 0 && length(Vector2.{planePoint.x, planePoint.z} - Vector2.{ray.origin.x, ray.origin.z}) > acceptable_radius {
return false, .{};
}
return true, .{planePoint.x, planePoint.z};

View File

@ -4,7 +4,7 @@ g_animations: Table(string, Animation);
get_animation_from_string :: (animation: string) -> *Animation {
ok, pack, anim := split_from_left(animation, ".");
if !ok {
print("Malformed animation query: %\n", animation);
log_warn("Malformed animation query: %", animation);
return null;
}
return get_animation_from_pack(pack, anim);
@ -37,7 +37,7 @@ animation_player_tick :: (player: *Animation_Player) {
animation_draw :: (player: *Animation_Player, position: Vector3, flipX: bool = false, flipY: bool = false) {
animation_player_tick(player);
if player.current_animation == null then print("Trying to draw a null animation!!\n");
if player.current_animation == null then log_warn("Trying to draw a null animation!!");
create_billboard_rendering_task(position, player.current_animation, player.current_frame, flipX, flipY);
}

View File

@ -47,13 +47,14 @@ Render_Command_Add_Trile_Positions :: struct {
}
Render_Command_Draw_Trile_Positions :: struct {
#as using c : Render_Command;
c.type = .DRAW_TRILE_POSITIONS;
trile : string;
chunk_key : Chunk_Key;
amount : s32;
conf : *World_Config;
is_preview : bool = false;
#as using c : Render_Command;
c.type = .DRAW_TRILE_POSITIONS;
trile : string;
chunk_key : Chunk_Key;
amount : s32;
conf : *World_Config;
preview_mode : s32 = 0; // 0=normal, 1=add preview (blue), 2=delete preview (red)
offset_index : s32 = 0; // index into trile_offsets, assigned at task-conversion time
}
Render_Command_Update_Trixels :: struct {

View File

@ -3,9 +3,10 @@ camera: Camera;
trixel_count : s32 = 0;
trixel_view_mode : s32 = 0;
bypass_postprocess : bool = false;
bypass_shadows : bool = false;
bypass_reflections : bool = false;
trile_offsets : [..]s32;
current_trile_offset_index : s32 = 0;
current_world_config : *World_Config = null;
in_shadowmap_pass : bool = false;
@ -20,7 +21,7 @@ backend_handle_command :: (cmd: *Render_Command) {
backend_add_trile_positions(add_command.positions);
case .DRAW_TRILE_POSITIONS;
draw_command := cast(*Render_Command_Draw_Trile_Positions)cmd;
backend_draw_trile_positions(draw_command.trile, draw_command.amount, draw_command.conf, draw_command.chunk_key, draw_command.is_preview);
backend_draw_trile_positions(draw_command.trile, draw_command.amount, draw_command.conf, draw_command.chunk_key, draw_command.preview_mode, draw_command.offset_index);
case .DRAW_SKY;
sky_command := cast(*Render_Command_Sky)cmd;
backend_draw_sky(sky_command.worldConfig);
@ -115,15 +116,16 @@ backend_add_trile_positions :: (positions : []Vector4) {
array_add(*trile_offsets, offset);
}
backend_draw_trile_positions :: (trile : string, amount : s32, worldConf: *World_Config, chunk_key: Chunk_Key, is_preview: bool = false) {
backend_draw_trile_positions :: (trile : string, amount : s32, worldConf: *World_Config, chunk_key: Chunk_Key, preview_mode: s32 = 0, offset_index: s32 = 0) {
if in_gbuffer_pass {
backend_draw_trile_positions_gbuffer(trile, amount, worldConf);
backend_draw_trile_positions_gbuffer(trile, amount, worldConf, offset_index);
} else {
backend_draw_trile_positions_main(trile, amount, worldConf, chunk_key, is_preview);
backend_draw_trile_positions_main(trile, amount, worldConf, chunk_key, preview_mode, offset_index);
}
}
backend_draw_trile_positions_gbuffer :: (trile : string, amount : s32, worldConf: *World_Config) {
backend_draw_trile_positions_gbuffer :: (trile : string, amount : s32, worldConf: *World_Config, offset_index: s32) {
if offset_index >= trile_offsets.count then return;
mvp := create_viewproj(*camera);
view := create_lookat(*camera);
vs_params : Gbuffer_Vs_Params;
@ -133,8 +135,7 @@ backend_draw_trile_positions_gbuffer :: (trile : string, amount : s32, worldConf
world_conf : Trile_World_Config;
world_config_to_shader_type(worldConf, *world_conf);
offset := trile_offsets[current_trile_offset_index];
current_trile_offset_index += 1;
offset := trile_offsets[offset_index];
trilegfx := get_trile_gfx(trile);
bindings : sg_bindings;
@ -151,12 +152,15 @@ backend_draw_trile_positions_gbuffer :: (trile : string, amount : s32, worldConf
sg_draw(0, cast(s32) trilegfx.vertex_count, amount);
}
backend_draw_trile_positions_main :: (trile : string, amount : s32, worldConf: *World_Config, chunk_key: Chunk_Key, is_preview: bool = false) {
backend_draw_trile_positions_main :: (trile : string, amount : s32, worldConf: *World_Config, chunk_key: Chunk_Key, preview_mode: s32 = 0, offset_index: s32 = 0) {
start_frame_profiling_group("Draw trile positions");
if offset_index >= trile_offsets.count {
end_frame_profiling_group("Draw trile positions");
return;
}
trilegfx := get_trile_gfx(trile);
offset := trile_offsets[current_trile_offset_index];
current_trile_offset_index += 1;
offset := trile_offsets[offset_index];
if in_shadowmap_pass {
vs_params : Trile_Shadow_Vs_Params;
@ -226,7 +230,7 @@ backend_draw_trile_positions_main :: (trile : string, amount : s32, worldConf: *
fs_params.rdm_diff_scale = lc.rdm_diff_scale;
fs_params.rdm_spec_scale = lc.rdm_spec_scale;
fs_params.ambient_color = lc.ambient_color.component;
fs_params.is_preview = ifx is_preview then cast(s32)1 else cast(s32)0;
fs_params.is_preview = preview_mode;
fs_params.rdm_tint = lc.rdm_tint.component;
sg_apply_bindings(*bindings);
@ -377,25 +381,27 @@ backend_process_command_buckets :: () {
shadow_mvp = create_shadow_viewproj(*camera, current_world_config);
in_shadowmap_pass = true;
sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, attachments = g_shadowmap_attachments}));
for render_command_buckets.shadow {
backend_handle_command(it);
if !bypass_shadows {
for render_command_buckets.shadow {
backend_handle_command(it);
}
}
sg_end_pass();
in_shadowmap_pass = false;
}
current_trile_offset_index = 0; // This is not optimal, but it is nice and simple.
end_frame_profiling_group("Shadow pass");
start_frame_profiling_group("Reflection pass");
// 3. Reflection pass
sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, attachments = gPipelines.plane.attachments}));
in_reflection_pass = true;
for render_command_buckets.reflection {
backend_handle_command(it);
}
in_reflection_pass = false;
if !bypass_reflections {
in_reflection_pass = true;
for render_command_buckets.reflection {
backend_handle_command(it);
}
in_reflection_pass = false;
}
sg_end_pass();
current_trile_offset_index = 0; // This is not optimal, but it is nice and simple.
end_frame_profiling_group("Reflection pass");
// 4. G-Buffer pass
@ -407,7 +413,6 @@ backend_process_command_buckets :: () {
}
sg_end_pass();
in_gbuffer_pass = false;
current_trile_offset_index = 0; // This is not optimal, but it is nice and simple.
end_frame_profiling_group("G-Buffer pass");
start_frame_profiling_group("SSAO pass");
@ -447,7 +452,6 @@ backend_process_command_buckets :: () {
backend_handle_command(it);
}
sg_end_pass();
current_trile_offset_index = 0; // This is not optimal, but it is nice and simple.
end_frame_profiling_group("Main pass");
start_frame_profiling_group("Postprocess pass");
@ -469,6 +473,7 @@ backend_process_command_buckets :: () {
post_process_config_uniform : Post_Process_Config;
if bypass_postprocess {
neutral : Post_Process;
neutral.tonemap = current_post_process.tonemap;
fill_uniform_with_engine_data(*post_process_config_uniform, *neutral);
} else {
fill_uniform_with_engine_data(*post_process_config_uniform, *current_post_process);

View File

@ -18,9 +18,6 @@ Gathered_Positions :: struct {
positions: [..]Vector4;
}
// Extract 6 frustum planes from an MVP matrix using the Gribb-Hartmann method.
// Each plane is vec4(A,B,C,D) where dot(plane.xyz, point) + plane.w >= 0 means inside.
// Accounts for Jai row-major storage being transposed when uploaded to OpenGL.
extract_frustum_planes :: (mvp: Matrix4) -> [6]Vector4 {
planes : [6]Vector4;
m := mvp;
@ -33,7 +30,6 @@ extract_frustum_planes :: (mvp: Matrix4) -> [6]Vector4 {
return planes;
}
// Returns false if the AABB is completely outside any single frustum plane (fast rejection).
aabb_in_frustum :: (planes: [6]Vector4, bmin: Vector3, bmax: Vector3) -> bool {
for plane: planes {
px := ifx plane.x >= 0 then bmax.x else bmin.x;

View File

@ -419,7 +419,7 @@ generate_trile_gfx_matias :: (trileptr : *Trile) -> Trile_GFX {
trile_normal_buffer_info := sg_buffer_desc.{ data = .{ ptr = triangleNorms.data, size = xx (triangleNorms.count * 4) } };
trile_normal_buffer := sg_make_buffer(*trile_normal_buffer_info);
print("Successfully generated mesh for trile with % triangles.\n", triangleVecs.count / 3 / 3);
log_debug("Successfully generated mesh for trile with % triangles.", triangleVecs.count / 3 / 3);
centres : [..]float;
@ -492,7 +492,7 @@ generate_trile_gfx_matias :: (trileptr : *Trile) -> Trile_GFX {
img := sg_make_image(*texdesc);
state := sg_query_image_state(img);
print("IMG: %\n", state);
log_debug("IMG: %", state);
context.allocator = old_alloc;
vecsCopy := array_copy(triangleVecs);

View File

@ -83,7 +83,7 @@ create_final_image :: () {
if g_rendertex.id != INVALID_ID then sg_destroy_image(g_rendertex);
if g_rendertex_depth.id != INVALID_ID then sg_destroy_image(g_rendertex);
if g_rendertex_depth.id != INVALID_ID then sg_destroy_image(g_rendertex_depth);
img_desc := sg_image_desc.{
width = w,

View File

@ -56,8 +56,8 @@ Rendering_Task_Trile :: struct {
chunk_key : Chunk_Key;
positions : []Vector4;
worldConf : *World_Config;
is_preview : bool = false;
shadow_only : bool = false; // only submit to shadow bucket (frustum-culled from camera)
preview_mode : s32 = 0; // 0=normal, 1=add preview (blue), 2=delete preview (red)
shadow_only : bool = false; // only submit to shadow bucket (frustum-culled from camera)
}
Rendering_Task_Trixels :: struct {
@ -86,6 +86,7 @@ add_rendering_task :: (task: $T) {
rendering_tasklist : [..]*Rendering_Task;
tasks_to_commands :: () {
trile_add_counter: s32 = 0;
for rendering_tasklist {
if it.type == {
case .SET_LIGHT;
@ -111,14 +112,16 @@ tasks_to_commands :: () {
addPositionsCmd.positions = trileTask.positions;
array_add(*render_command_buckets.setup, addPositionsCmd);
drawPositionsCmd := New(Render_Command_Draw_Trile_Positions,, temp);
drawPositionsCmd.trile = trileTask.trile;
drawPositionsCmd.chunk_key = trileTask.chunk_key;
drawPositionsCmd.amount = cast(s32)trileTask.positions.count;
drawPositionsCmd.conf = trileTask.worldConf;
drawPositionsCmd.is_preview = trileTask.is_preview;
drawPositionsCmd.trile = trileTask.trile;
drawPositionsCmd.chunk_key = trileTask.chunk_key;
drawPositionsCmd.amount = cast(s32)trileTask.positions.count;
drawPositionsCmd.conf = trileTask.worldConf;
drawPositionsCmd.preview_mode = trileTask.preview_mode;
drawPositionsCmd.offset_index = trile_add_counter;
trile_add_counter += 1;
if trileTask.shadow_only {
array_add(*render_command_buckets.shadow, drawPositionsCmd);
} else if trileTask.is_preview {
} else if trileTask.preview_mode != 0 {
array_add(*render_command_buckets.main, drawPositionsCmd);
} else {
array_add(*render_command_buckets.reflection, drawPositionsCmd);

189
src/settings_menu.jai Normal file
View File

@ -0,0 +1,189 @@
#scope_file
TRANSITION_SPEED :: 3.0;
Settings_Page :: enum {
MAIN;
AUDIO;
}
Settings_State :: struct {
open : bool = false;
transition : float = 0.0;
page : Settings_Page = .MAIN;
cursor : s32 = 0;
title_font : *Font;
item_font : *Font;
}
g_settings : Settings_State;
MAIN_ITEMS :: string.["Audio"];
AUDIO_LABELS :: string.["Master Volume", "Music Volume", "Sound Effects"];
audio_get :: (i: s32) -> float {
if i == 0 return g_mixer.config.masterVolume;
if i == 1 return g_mixer.config.musicVolume;
if i == 2 return g_mixer.config.soundEffectVolume;
return 0.0;
}
audio_set :: (i: s32, v: float) {
if i == 0 then g_mixer.config.masterVolume = clamp(v, 0.0, 1.0);
if i == 1 then g_mixer.config.musicVolume = clamp(v, 0.0, 1.0);
if i == 2 then g_mixer.config.soundEffectVolume = clamp(v, 0.0, 1.0);
}
page_count :: () -> s32 {
if g_settings.page == .MAIN return MAIN_ITEMS.count;
if g_settings.page == .AUDIO return AUDIO_LABELS.count;
return 0;
}
#scope_export
settings_menu_blocks_game :: () -> bool {
return g_settings.open;
}
init_settings_menu :: () {
g_settings.title_font = get_font_at_size(60);
g_settings.item_font = get_font_at_size(30);
}
tick_settings_menu :: () {
if in_editor_view then return;
if is_action_start(Editor_Action.TOGGLE_SETTINGS) {
if !g_settings.open {
g_settings.open = true;
g_settings.page = .MAIN;
g_settings.cursor = 0;
} else if g_settings.page != .MAIN {
g_settings.page = .MAIN;
g_settings.cursor = 0;
} else {
g_settings.open = false;
}
}
dt := cast(float) delta_time;
if g_settings.open {
g_settings.transition = min(g_settings.transition + TRANSITION_SPEED * dt, 1.0);
} else {
g_settings.transition = max(g_settings.transition - TRANSITION_SPEED * dt, 0.0);
}
if !g_settings.open then return;
if console_open_ignore_input then return;
count := page_count();
up := cast(bool)(input_button_states[Key_Code.ARROW_UP] & .START);
down := cast(bool)(input_button_states[Key_Code.ARROW_DOWN] & .START);
left := cast(bool)(input_button_states[Key_Code.ARROW_LEFT] & .START);
right := cast(bool)(input_button_states[Key_Code.ARROW_RIGHT]& .START);
enter := cast(bool)(input_button_states[Key_Code.ENTER] & .START);
if up then g_settings.cursor = (g_settings.cursor - 1 + count) % count;
if down then g_settings.cursor = (g_settings.cursor + 1) % count;
if g_settings.page == .MAIN && enter {
if g_settings.cursor == 0 {
g_settings.page = .AUDIO;
g_settings.cursor = 0;
}
}
if g_settings.page == .AUDIO {
delta : float = 0.0;
if left then delta = -0.1;
if right then delta = 0.1;
if delta != 0.0 then audio_set(g_settings.cursor, audio_get(g_settings.cursor) + delta);
}
}
draw_settings_menu :: (theme: *GR.Overall_Theme) {
t := g_settings.transition;
if t < 0.001 then return;
fw := vw * 100.0;
fh := vh * 100.0;
half_h := fh * 0.5;
bar_h := t * half_h;
bg := Vector4.{0.04, 0.04, 0.06, 1.0};
set_shader_for_color();
// Top eyelid sliding down
immediate_quad(.{0, 0}, .{fw, 0}, .{fw, bar_h}, .{0, bar_h}, bg);
immediate_flush();
// Bottom eyelid sliding up
bottom_y := fh - bar_h;
immediate_quad(.{0, bottom_y}, .{fw, bottom_y}, .{fw, fh}, .{0, fh}, bg);
immediate_flush();
// Content fades in during the last quarter of the transition
content_alpha := clamp((t - 0.75) / 0.25, 0.0, 1.0);
if content_alpha < 0.01 then return;
white := Vector4.{1.0, 1.0, 1.0, content_alpha};
dim := Vector4.{0.45, 0.45, 0.45, content_alpha};
selected := Vector4.{1.0, 0.95, 0.65, content_alpha};
// Game title
prepare_text(g_settings.title_font, "BEACHBALL");
title_x := (fw - cast(float) gPreppedTextWidth) * 0.5;
draw_prepared_text(g_settings.title_font, xx title_x, xx (fh * 0.22), white);
item_h := cast(float)(g_settings.item_font.character_height) * 1.7;
if g_settings.page == .MAIN {
total_h := MAIN_ITEMS.count * item_h;
start_y := half_h - total_h * 0.5;
for i: 0..MAIN_ITEMS.count-1 {
col := ifx cast(s32)i == g_settings.cursor then selected else dim;
prepare_text(g_settings.item_font, MAIN_ITEMS[i]);
x := (fw - cast(float) gPreppedTextWidth) * 0.5;
draw_prepared_text(g_settings.item_font, xx x, xx (start_y + cast(float)i * item_h), col);
}
hint_str := "↑↓ navigate Enter select";
prepare_text(g_settings.item_font, hint_str);
hint_x := (fw - cast(float) gPreppedTextWidth) * 0.5;
draw_prepared_text(g_settings.item_font, xx hint_x, xx (fh * 0.82), dim);
} else if g_settings.page == .AUDIO {
total_h := AUDIO_LABELS.count * item_h;
start_y := half_h - total_h * 0.5;
for i: 0..AUDIO_LABELS.count-1 {
col := ifx cast(s32)i == g_settings.cursor then selected else dim;
pct := cast(s32)(audio_get(cast(s32)i) * 100.0 + 0.5);
bar := make_volume_bar(pct, cast(s32)i == g_settings.cursor);
label := tprint("% %", AUDIO_LABELS[i], bar);
prepare_text(g_settings.item_font, label);
x := (fw - cast(float) gPreppedTextWidth) * 0.5;
draw_prepared_text(g_settings.item_font, xx x, xx (start_y + cast(float)i * item_h), col);
}
hint_str := "↑↓ navigate ← → adjust Esc back";
prepare_text(g_settings.item_font, hint_str);
hint_x := (fw - cast(float) gPreppedTextWidth) * 0.5;
draw_prepared_text(g_settings.item_font, xx hint_x, xx (fh * 0.82), dim);
}
}
#scope_file
make_volume_bar :: (pct: s32, active: bool) -> string {
STEPS :: 10;
filled := (pct + 5) / STEPS;
builder : String_Builder;
append(*builder, "[ ");
for i: 0..STEPS-1 {
if i < filled then append(*builder, "■");
else append(*builder, "·");
}
append(*builder, tprint(" %", pct));
append(*builder, " ]");
return builder_to_string(*builder,, allocator = temp);
}

File diff suppressed because it is too large Load Diff

View File

@ -561,6 +561,9 @@ void main() {
vec3 kDiff = 1.0 - Frough;
kDiff *= 1.0 - metallic;
light += (kDiff * indirectDiff / PI * albedo) * ssao_sample * rdm_diff_scale;
if (rdm_diff_scale < 0.001) {
light += ambient_color * ambient_intensity * albedo * ssao_sample;
}
} else {
// Fallback: ambient + sky reflection when no RDM data (or RDM disabled).
light += ambient_color * ambient_intensity * albedo * ssao_sample;
@ -572,6 +575,8 @@ void main() {
frag_color = vec4(mix(deepColor, light + emissive, smoothstep(0.0, planeHeight, vpos.y)), 1.0);
if (is_preview == 1) {
frag_color.rgb = mix(frag_color.rgb, vec3(0.3, 0.7, 1.0), 0.5);
} else if (is_preview == 2) {
frag_color.rgb = mix(frag_color.rgb, vec3(1.0, 0.3, 0.2), 0.5);
}
}
@end

4
src/tests/index.jai Normal file
View File

@ -0,0 +1,4 @@
#run {
// print("1st test!!!\n");
// main();
}

View File

@ -50,14 +50,14 @@ set_trile_gfx :: (name: string, gfx: Trile_GFX, skip_preexist_check: bool = fals
sg_destroy_buffer(old_gfx.centre_buffer);
sg_destroy_image(old_gfx.trixel_colors);
array_reset(*old_gfx.vertices);
print("Destroyed old GFX buffers for trile: %\n", name);
log_debug("Destroyed old GFX buffers for trile: %", name);
}
}
table_set(*trile_gfx_table, name, gfx);
}
set_trile :: (name: string, trile: Trile) {
print("Setting trile with name: %\n",name);
log_debug("Setting trile with name: %", name);
// Destroy and remove any cached GFX so it gets regenerated from new data.
gfx_exists, old_gfx := table_find(*trile_gfx_table, name);
if gfx_exists {
@ -79,7 +79,7 @@ set_trile :: (name: string, trile: Trile) {
get_trile :: (name: string) -> (*Trile, success: bool) {
trileptr := table_find_pointer(*trile_table, name);
if !trileptr {
print("Failed to get trile with name: %\n", name);
log_error("Failed to get trile with name: %", name);
return null, false;
}
return trileptr, true;
@ -115,7 +115,7 @@ ltriles :: () {
success, triles := Jaison.json_parse_string(s, [..]TrileSerialize,, temp);
for triles {
set_trile(sprint("%",it.name), trile_from_serialize_form(it));
print("Loaded %\n", it.name);
log_debug("Loaded %", it.name);
}
} @Command

View File

@ -5,7 +5,7 @@
autoedit_scrolls : Table(u64, float);
loc_to_key :: (line: s32) -> string {
print("Creating key: %\n", line);
log_debug("Creating key: %", line);
return tprint("%", line);
}

59
src/ui/demo.jai Normal file
View File

@ -0,0 +1,59 @@
#if FLAG_DEMO_BUILD {
demo_saved_ssao : float = -1.0;
demo_saved_tonemap : float = -1.0;
demo_saved_rdm_diff : float = -1.0;
demo_saved_rdm_spec : float = -1.0;
demo_toggle_float :: (val: *float, saved: *float, default: float) {
if val.* > 0 {
saved.* = val.*;
val.* = 0.0;
} else {
val.* = ifx saved.* > 0 then saved.* else default;
}
}
draw_demo_ui :: (theme: *GR.Overall_Theme) {
r := GR.get_rect(ui_w(86,0), ui_h(1,0), ui_w(13,0), ui_h(3,0));
rdm_diff_on := current_lighting_config.rdm_diff_scale > 0;
if GR.button(r, ifx rdm_diff_on then "RDM Diffuse: ON" else "RDM Diffuse: OFF") {
demo_toggle_float(*current_lighting_config.rdm_diff_scale, *demo_saved_rdm_diff, 1.0);
}
r.y += r.h * 1.3;
rdm_spec_on := current_lighting_config.rdm_spec_scale > 0;
if GR.button(r, ifx rdm_spec_on then "RDM Specular: ON" else "RDM Specular: OFF") {
demo_toggle_float(*current_lighting_config.rdm_spec_scale, *demo_saved_rdm_spec, 1.0);
}
r.y += r.h * 1.3;
if GR.button(r, ifx !bypass_shadows then "Shadows: ON" else "Shadows: OFF") {
bypass_shadows = !bypass_shadows;
}
r.y += r.h * 1.3;
if GR.button(r, ifx !bypass_reflections then "Reflections: ON" else "Reflections: OFF") {
bypass_reflections = !bypass_reflections;
}
r.y += r.h * 1.3;
ssao_on := current_post_process.ssao > 0;
if GR.button(r, ifx ssao_on then "SSAO: ON" else "SSAO: OFF") {
demo_toggle_float(*current_post_process.ssao, *demo_saved_ssao, 1.0);
}
r.y += r.h * 1.3;
tonemap_on := current_post_process.tonemap > 0;
if GR.button(r, ifx tonemap_on then "Tonemapping: ON" else "Tonemapping: OFF") {
demo_toggle_float(*current_post_process.tonemap, *demo_saved_tonemap, 1.0);
}
r.y += r.h * 1.3;
if GR.button(r, ifx !bypass_postprocess then "Post FX: ON" else "Post FX: OFF") {
bypass_postprocess = !bypass_postprocess;
}
}
} // #if FLAG_DEMO_BUILD

View File

@ -183,6 +183,8 @@ prepare_text :: (font: *Ui_Type_Indicator.Font, text: string, effects: Ui_Type_I
return 0;
}
// print("Font: %\n", font);
fonsPushState(state.fons);
defer fonsPopState(state.fons);
fonsSetFont(state.fons, state.font_default.fons_font);
fonsSetSize(state.fons, xx font.character_height);
w := fonsTextBounds(state.fons, 0.0, 0.0, text.data, text.data + text.count, null);
@ -210,6 +212,10 @@ prepare_text :: (font: *Ui_Type_Indicator.Font, text: string, effects: Ui_Type_I
}
draw_prepared_text :: (font: *Ui_Type_Indicator.Font, x: s64, y: s64, text_color: Vector4, effects: Ui_Type_Indicator.Font_Effects = 0) {
if gPreppedText.count < 1 then return;
fonsPushState(state.fons);
defer fonsPopState(state.fons);
fonsSetFont(state.fons, state.font_default.fons_font);
fonsSetSize(state.fons, xx font.character_height);
color := sfons_rgba(xx (255.0 * text_color.x), xx (255.0 * text_color.y), xx (255.0 * text_color.z), xx (255.0 * text_color.w));
fonsSetColor(state.fons, color);
result := cast(*u8) temporary_alloc(gPreppedText.count + 1); // Add 1 for the zero.
@ -217,10 +223,9 @@ draw_prepared_text :: (font: *Ui_Type_Indicator.Font, x: s64, y: s64, text_color
result[gPreppedText.count] = 0;
sgl_layer(layer);
fonsDrawText(state.fons, xx x, xx y, result, null);
fonsPushState(state.fons);
arb_tri_command_add(.{
type = .DRAW_TEXT,
layer = layer
layer = layer
});
layer += 1;
}
@ -420,6 +425,9 @@ render_ui :: () {
GR.set_default_theme(my_theme);
draw_editor_ui(*my_theme);
if !in_editor_view then game_ui(*my_theme);
#if FLAG_DEMO_BUILD { if !in_editor_view then draw_demo_ui(*my_theme); }
draw_settings_menu(*my_theme);
draw_console(*my_theme);
}
ui_pass :: () {

View File

@ -252,7 +252,7 @@ read_string :: (data: []u8, cursor: *s64, len: s64) -> string {
sworld :: () {
if !current_world.valid {
print("Cannot save: no world loaded\n");
log_error("Cannot save: no world loaded");
return;
}
#if OS != .WASM {
@ -262,7 +262,7 @@ sworld :: () {
file.make_directory_if_it_does_not_exist(dir, recursive = true);
data := save_world(*current_world.world);
file.write_entire_file(tprint("%/index.world", dir), data);
print("Saved world '%' (% bytes)\n", name, data.count);
log_info("Saved world '%' (% bytes)", name, data.count);
}
} @Command
@ -350,20 +350,20 @@ load_world_from_data :: (data: []u8) -> (World, bool) {
cursor: s64 = 0;
if data.count < size_of(u32) + size_of(u16) {
print("World file too small\n");
log_error("World file too small");
return world, false;
}
// Header
magic := read_value(data, *cursor, u32);
if magic != WORLD_MAGIC {
print("Invalid world file magic\n");
log_error("Invalid world file magic");
return world, false;
}
version := read_value(data, *cursor, u16);
if version != WORLD_VERSION {
print("Unsupported world version: %\n", version);
log_error("Unsupported world version: %", version);
return world, false;
}