lot's of work on polishing the engine
This commit is contained in:
parent
4697c016e1
commit
733cdd2760
23
first.jai
23
first.jai
@ -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 {
|
||||
|
||||
@ -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 "";
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
Audio_Data :: struct {
|
||||
channels: s16;
|
||||
data: [..]float;
|
||||
channels : s16;
|
||||
sample_rate : s32;
|
||||
data : [..]float;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#if HAS_IPROF == false {
|
||||
#if FLAG_IPROF_ENABLED == false {
|
||||
|
||||
init_profiler :: () {}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
|
||||
#load "keybinds.jai";
|
||||
|
||||
182
src/input/keybinds.jai
Normal file
182
src/input/keybinds.jai
Normal 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
14
src/loading_screen.jai
Normal 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
61
src/logging.jai
Normal 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
|
||||
30
src/main.jai
30
src/main.jai
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
15
src/ray.jai
15
src/ray.jai
@ -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};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
189
src/settings_menu.jai
Normal 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
@ -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
4
src/tests/index.jai
Normal file
@ -0,0 +1,4 @@
|
||||
#run {
|
||||
// print("1st test!!!\n");
|
||||
// main();
|
||||
}
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
59
src/ui/demo.jai
Normal 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
|
||||
@ -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 :: () {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user