466 lines
16 KiB
Plaintext
466 lines
16 KiB
Plaintext
#scope_file
|
|
rotation : float = 0.0;
|
|
tilt : float = 0.0;
|
|
zoom : float = 3.0;
|
|
|
|
brush_radius : int = 5;
|
|
|
|
roughness : u8 = 0;
|
|
metallic : u8 = 0;
|
|
emittance : u8 = 0;
|
|
|
|
hovered_trixel_x : int = -1;
|
|
hovered_trixel_y : int = -1;
|
|
hovered_trixel_z : int = -1;
|
|
|
|
|
|
TRILE_ROTATION_SPEED :: 2.0;
|
|
|
|
Trile_Editor_Tab :: enum {
|
|
TOOLSET;
|
|
METADATA;
|
|
MATERIAL;
|
|
};
|
|
|
|
Trile_Editor_Tool :: enum {
|
|
PAINT;
|
|
ADD;
|
|
REMOVE;
|
|
}
|
|
|
|
Trile_Editor_Tool_Mode :: enum {
|
|
POINT;
|
|
BRUSH;
|
|
AREA;
|
|
}
|
|
|
|
current_tab : Trile_Editor_Tab = .TOOLSET;
|
|
current_tool : Trile_Editor_Tool = .PAINT;
|
|
current_mode : Trile_Editor_Tool_Mode = .AREA;
|
|
|
|
colorMuls : [16][16][16]Vector3;
|
|
|
|
#scope_export
|
|
|
|
ntrile :: (name: string) {
|
|
set_trile_gfx(editor_current_trile.name, generate_trile_gfx_matias(editor_current_trile));
|
|
newt : Trile;
|
|
newt.name = sprint("%", name);
|
|
set_trile(newt.name, newt);
|
|
editor_current_trile = get_trile(newt.name);
|
|
} @Command
|
|
|
|
cpytrile :: (trile: string, destination: string) {
|
|
set_trile_gfx(editor_current_trile.name, generate_trile_gfx_matias(editor_current_trile));
|
|
newt : Trile;
|
|
newt.name = sprint("%", destination);
|
|
newt.trixels = get_trile(trile).trixels;
|
|
set_trile(newt.name, newt);
|
|
editor_current_trile = get_trile(newt.name);
|
|
} @Command
|
|
|
|
ltrile :: (name: string) {
|
|
set_trile_gfx(editor_current_trile.name, generate_trile_gfx_matias(editor_current_trile));
|
|
nt := get_trile(name);
|
|
if !nt {
|
|
console_add_output_line("Failed to load a trile with that name...");
|
|
return;
|
|
}
|
|
editor_current_trile = nt;
|
|
} @Command
|
|
|
|
#scope_file
|
|
|
|
apply_tool_to_trixel :: (x: s64, y: s64, z: s64) {
|
|
if current_tool == .PAINT {
|
|
editor_current_trile.trixels[x][y][z].material.color = current_color;
|
|
editor_current_trile.trixels[x][y][z].material.metallic = metallic;
|
|
editor_current_trile.trixels[x][y][z].material.roughness = roughness;
|
|
editor_current_trile.trixels[x][y][z].material.emittance = emittance;
|
|
}
|
|
if current_tool == .ADD {
|
|
editor_current_trile.trixels[x][y][z].empty = false;
|
|
}
|
|
if current_tool == .REMOVE {
|
|
editor_current_trile.trixels[x][y][z].empty = true;
|
|
}
|
|
}
|
|
|
|
area_start_x : int;
|
|
area_start_y : int;
|
|
area_start_z : int;
|
|
area_active : bool;
|
|
|
|
handle_tool_click :: () {
|
|
if hovered_trixel_x < 0 && hovered_trixel_y < 0 && hovered_trixel_z < 0 then return;
|
|
if current_mode == .POINT {
|
|
apply_tool_to_trixel(hovered_trixel_x, hovered_trixel_y, hovered_trixel_z);
|
|
}
|
|
if current_mode == .BRUSH {
|
|
is_in_radius :: (point : Vector3, radius: float) -> bool {
|
|
center : Vector3 = .{xx hovered_trixel_x, xx hovered_trixel_y, xx hovered_trixel_z};
|
|
return length(point - center) <= radius;
|
|
}
|
|
|
|
for x: 0..15 {
|
|
for y: 0..15 {
|
|
for z: 0..15 {
|
|
if is_in_radius(.{xx x, xx y, xx z}, xx brush_radius) {
|
|
apply_tool_to_trixel(x,y,z);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if current_mode == .AREA {
|
|
if area_active {
|
|
// Apply tool to all trixels in the area.
|
|
for x: min(area_start_x, hovered_trixel_x)..max(hovered_trixel_x, area_start_x) {
|
|
for y: min(area_start_y, hovered_trixel_y)..max(hovered_trixel_y, area_start_y) {
|
|
for z: min(area_start_z, hovered_trixel_z)..max(hovered_trixel_z, area_start_z) {
|
|
apply_tool_to_trixel(x,y,z);
|
|
}
|
|
}
|
|
}
|
|
area_active = false;
|
|
} else {
|
|
area_active = true;
|
|
area_start_x = hovered_trixel_x;
|
|
area_start_y = hovered_trixel_y;
|
|
area_start_z = hovered_trixel_z;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#scope_export
|
|
|
|
current_color : Vector3 = .{1.0, 0.0, 0.0};
|
|
|
|
reset_trile :: () {
|
|
for x: 0..15 {
|
|
for y: 0..15 {
|
|
for z: 0..15 {
|
|
editor_current_trile.trixels[x][y][z].empty = false;
|
|
}
|
|
}
|
|
}
|
|
} @Command
|
|
|
|
trile_copy :: (from: string) {
|
|
src := get_trile(from);
|
|
for x: 0..15 {
|
|
for y: 0..15 {
|
|
for z: 0..15 {
|
|
editor_current_trile.trixels[x][y][z] = src.trixels[x][y][z];
|
|
}
|
|
}
|
|
}
|
|
} @Command
|
|
|
|
tick_trile_editor :: () {
|
|
if console_open_ignore_input then return;
|
|
|
|
if !editor_current_trile then editor_current_trile = get_trile("test");
|
|
|
|
if input_button_states[Key_Code.MOUSE_BUTTON_LEFT] & .START {
|
|
handle_tool_click();
|
|
}
|
|
|
|
mindist : float = 999999;
|
|
hovered_trixel_x = -1;
|
|
hovered_trixel_y = -1;
|
|
hovered_trixel_z = -1;
|
|
|
|
for x: 0..15 {
|
|
for y: 0..15 {
|
|
for z: 0..15 {
|
|
t := editor_current_trile.trixels[x][y][z];
|
|
if t.empty then continue;
|
|
|
|
cube : Collision_Cube;
|
|
cube.position = Vector3.{cast(float) x, cast(float) y, cast(float) z} * TRIXEL_SIZE;
|
|
cube.size = Vector3.{1,1,1} * TRIXEL_SIZE;
|
|
|
|
ray := get_mouse_ray(*get_trile_editor_camera());
|
|
collision := does_ray_hit_cube(ray, cube);
|
|
if collision.hit && collision.distance < mindist {
|
|
mindist = collision.distance;
|
|
hovered_trixel_x = x;
|
|
hovered_trixel_y = y;
|
|
hovered_trixel_z = z;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for x: 0..15 {
|
|
for y: 0..15 {
|
|
for z: 0..15 {
|
|
if x == hovered_trixel_x && y == hovered_trixel_y && z == hovered_trixel_z {
|
|
colorMuls[x][y][z] = .{0.6, 0.0, 0.0};
|
|
} else {
|
|
colorMuls[x][y][z] = .{1.0, 1.0, 1.0};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !input_button_states[Key_Code.CTRL] & .DOWN {
|
|
if input_button_states[#char "D"] & .DOWN {
|
|
rotation += TRILE_ROTATION_SPEED * cast(float) delta_time;
|
|
}
|
|
if input_button_states[#char "A"] & .DOWN {
|
|
rotation -= TRILE_ROTATION_SPEED * cast(float) delta_time;
|
|
}
|
|
if input_button_states[#char "W"] & .DOWN {
|
|
tilt += TRILE_ROTATION_SPEED * cast(float) delta_time;
|
|
}
|
|
if input_button_states[#char "S"] & .DOWN {
|
|
tilt -= TRILE_ROTATION_SPEED * cast(float) delta_time;
|
|
}
|
|
}
|
|
|
|
trile_editor_shortcuts();
|
|
|
|
tilt = clamp(tilt, -(PI/2.0) + 0.01, PI/2.0 - 0.01);
|
|
zoom = clamp(zoom + mouse_delta_z * -0.2, 1.0, 3.0);
|
|
|
|
cam := get_trile_editor_camera();
|
|
ray := get_mouse_ray(*cam);
|
|
}
|
|
|
|
get_trile_editor_camera :: () -> Camera {
|
|
camera: Camera;
|
|
camera.near = 0.1;
|
|
camera.far = 5000;
|
|
|
|
cameraDir : Vector3 = .{1, 0, 0};
|
|
qrotation : Quaternion = .{cos(rotation/2.0),0,sin(rotation/2.0),0};
|
|
qtilt : Quaternion = .{cos(tilt/2.0),sin(tilt/2.0), 0, 0};
|
|
|
|
rotate(*cameraDir, qrotation * qtilt);
|
|
camera.position = .{0.5, 0.5, 0.5};
|
|
|
|
camera.position += cameraDir * zoom;
|
|
camera.target = .{0.5, 0.5, 0.5};
|
|
|
|
return camera;
|
|
}
|
|
|
|
global_palette_scroll : float = 0.0;
|
|
trile_palette_scroll : float = 0.0;
|
|
use_color_picker : bool = false;
|
|
selected_palette : s32 = 0;
|
|
|
|
draw_tool_tab :: (theme: *GR.Overall_Theme, area: GR.Rect) {
|
|
r := area;
|
|
r.h = ui_h(4, 0);
|
|
if keybind_button(r, "Paint", .TRIXEL_TOOL_PAINT, *t_button_selectable(theme, current_tool == .PAINT)) then current_tool = .PAINT;
|
|
r.y += r.h;
|
|
if keybind_button(r, "Add", .TRIXEL_TOOL_ADD, *t_button_selectable(theme, current_tool == .ADD)) then current_tool = .ADD;
|
|
r.y += r.h;
|
|
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 keybind_button(r, "Point", .TRIXEL_MODE_POINT, *t_button_selectable(theme, current_mode == .POINT)) then current_mode = .POINT;
|
|
r.y += r.h;
|
|
if keybind_button(r, "Area", .TRIXEL_MODE_AREA, *t_button_selectable(theme, current_mode == .AREA)) then current_mode = .AREA;
|
|
r.y += r.h;
|
|
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 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();
|
|
};
|
|
}
|
|
|
|
draw_swatches :: (entries: []Palette_Entry, inside: GR.Rect, block: float, scroll: *float, id_offset: s32, theme: *GR.Overall_Theme) -> (end_y: float) {
|
|
cols := max(1, cast(int)(inside.w / block));
|
|
for entry, idx: entries {
|
|
er : GR.Rect;
|
|
er.x = inside.x + (idx % cols) * block;
|
|
er.y = inside.y - scroll.* + (idx / cols) * block;
|
|
er.w = block; er.h = block;
|
|
btn := theme.button_theme;
|
|
btn.surface_color = .{entry.r, entry.g, entry.b, 1.0};
|
|
if GR.button(er, "", *btn, id_offset + cast(s32)idx) {
|
|
current_color = .{entry.r, entry.g, entry.b};
|
|
roughness = xx entry.roughness;
|
|
metallic = xx entry.metallic;
|
|
emittance = xx entry.emittance;
|
|
}
|
|
}
|
|
rows := ifx entries.count > 0 then (entries.count + cols - 1) / cols else 0;
|
|
return inside.y + rows * block;
|
|
}
|
|
|
|
draw_material_tab :: (theme: *GR.Overall_Theme, area: GR.Rect) {
|
|
r := area;
|
|
row_h := ui_h(4, 0);
|
|
block := ui_h(3, 2);
|
|
bottom := area.y + area.h;
|
|
sliders_h := ui_h(18, 0);
|
|
|
|
// Toggle: Palette | Color Picker
|
|
r.h = row_h;
|
|
half := r.w / 2;
|
|
r.w = half;
|
|
if GR.button(r, "Palette", *t_button_selectable(theme, !use_color_picker)) then use_color_picker = false;
|
|
r.x += half;
|
|
if GR.button(r, "Color Picker", *t_button_selectable(theme, use_color_picker)) then use_color_picker = true;
|
|
r.x = area.x; r.w = area.w;
|
|
r.y += row_h;
|
|
|
|
available := bottom - r.y - sliders_h;
|
|
top_h := ifx use_color_picker then ui_h(42, 0) else available / 2;
|
|
bot_h := available - top_h;
|
|
|
|
r.h = top_h;
|
|
if use_color_picker {
|
|
GR.color_picker(r, *current_color, *theme.color_picker_theme);
|
|
} else {
|
|
// Palette file selector
|
|
if g_palettes.count > 1 {
|
|
r.h = row_h;
|
|
pal_w := r.w / cast(float)g_palettes.count;
|
|
for pal, idx: g_palettes {
|
|
pr := r;
|
|
pr.x = area.x + idx * pal_w;
|
|
pr.w = pal_w;
|
|
if GR.button(pr, pal.name, *t_button_selectable(theme, cast(s32)idx == selected_palette), cast(s32)idx + 20000) {
|
|
selected_palette = cast(s32)idx;
|
|
global_palette_scroll = 0;
|
|
}
|
|
}
|
|
r.y += row_h;
|
|
top_h -= row_h;
|
|
r.h = top_h;
|
|
}
|
|
entries : []Palette_Entry;
|
|
if g_palettes.count > 0 {
|
|
selected_palette = clamp(selected_palette, 0, cast(s32)g_palettes.count - 1);
|
|
entries = g_palettes[selected_palette].entries;
|
|
}
|
|
region, inside := GR.begin_scrollable_region(r, *theme.scrollable_region_theme);
|
|
end_y := draw_swatches(entries, inside, block, *global_palette_scroll, 0, theme);
|
|
GR.end_scrollable_region(region, inside.x + inside.w, end_y, *global_palette_scroll);
|
|
}
|
|
r.y += top_h;
|
|
|
|
// Trile material picker
|
|
unique_mats : [..]Material;
|
|
unique_mats.allocator = temp;
|
|
for x: 0..15 for y: 0..15 for z: 0..15 {
|
|
trix := editor_current_trile.trixels[x][y][z];
|
|
if trix.empty then continue;
|
|
mr, mg, mb, ma := material_to_rgba(trix.material);
|
|
key := (cast(u32)mr << 24) | (cast(u32)mg << 16) | (cast(u32)mb << 8) | cast(u32)ma;
|
|
already := false;
|
|
for um: unique_mats {
|
|
ur, ug, ub, ua := material_to_rgba(um);
|
|
if ((cast(u32)ur << 24) | (cast(u32)ug << 16) | (cast(u32)ub << 8) | cast(u32)ua) == key { already = true; break; }
|
|
}
|
|
if !already array_add(*unique_mats, trix.material);
|
|
}
|
|
as_entries : [..]Palette_Entry;
|
|
as_entries.allocator = temp;
|
|
for mat: unique_mats {
|
|
e : Palette_Entry;
|
|
e.r = mat.color.x; e.g = mat.color.y; e.b = mat.color.z;
|
|
e.roughness = mat.roughness; e.metallic = mat.metallic; e.emittance = mat.emittance;
|
|
array_add(*as_entries, e);
|
|
}
|
|
|
|
r.h = bot_h;
|
|
region2, inside2 := GR.begin_scrollable_region(r, *theme.scrollable_region_theme);
|
|
end_y2 := draw_swatches(as_entries, inside2, block, *trile_palette_scroll, 10000, theme);
|
|
GR.end_scrollable_region(region2, inside2.x + inside2.w, end_y2, *trile_palette_scroll);
|
|
r.y += bot_h;
|
|
|
|
r.h = ui_h(3, 2);
|
|
GR.label(r, "Roughness", *t_label_left(theme));
|
|
r.y += r.h;
|
|
GR.slider(r, *roughness, 0, 7, 1, *theme.slider_theme);
|
|
r.y += r.h;
|
|
GR.label(r, "Metallic", *t_label_left(theme));
|
|
r.y += r.h;
|
|
GR.slider(r, *metallic, 0, 3, 1, *theme.slider_theme);
|
|
r.y += r.h;
|
|
GR.label(r, "Emittance", *t_label_left(theme));
|
|
r.y += r.h;
|
|
GR.slider(r, *emittance, 0, 2, 1, *theme.slider_theme);
|
|
}
|
|
|
|
|
|
draw_trile_editor :: () {
|
|
create_set_cam_rendering_task(get_trile_editor_camera(), 0.0);
|
|
w := New(World,, temp);
|
|
create_sky_rendering_task(*w.conf);
|
|
create_trixel_rendering_task(editor_current_trile, *colorMuls);
|
|
}
|
|
|
|
trile_editor_shortcuts :: () {
|
|
// 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();
|
|
}
|
|
}
|
|
|
|
draw_trile_editor_ui :: (theme: *GR.Overall_Theme) {
|
|
r := GR.get_rect(0, ui_h(5,0), ui_w(20, 20), ui_h(95, 0));
|
|
ui_add_mouse_occluder(r);
|
|
draw_bg_rectangle(r, theme);
|
|
tab_r := r;
|
|
tab_r.h = ui_h(3,0);
|
|
tab_r.w = ui_w(20, 20) / 3;
|
|
|
|
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 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 keybind_button(tab_r, "Meta", .TRIXEL_TAB_INFO, *t_button_tab(theme, current_tab == .METADATA)) then current_tab = .METADATA;
|
|
|
|
r.y += tab_r.h;
|
|
|
|
if current_tab == {
|
|
case .MATERIAL;
|
|
draw_material_tab(theme, r);
|
|
case .TOOLSET;
|
|
draw_tool_tab(theme, r);
|
|
}
|
|
|
|
draw_picker(theme);
|
|
|
|
// View mode buttons — bottom center of the 3D viewport.
|
|
btn_w := 11.0 * vw;
|
|
btn_h := cast(float) ui_h(4, 0);
|
|
bar_w := btn_w * 4.0;
|
|
bar_x := 100.0 * vw / 2.0 - bar_w / 2.0;
|
|
bar_y := 100.0 * vh - btn_h - 1.5 * vh;
|
|
|
|
bar_r : GR.Rect;
|
|
bar_r.x = bar_x; bar_r.y = bar_y; bar_r.w = bar_w; bar_r.h = btn_h;
|
|
ui_add_mouse_occluder(bar_r);
|
|
|
|
r2 : GR.Rect;
|
|
r2.x = bar_x; r2.y = bar_y; r2.w = btn_w; r2.h = btn_h;
|
|
if 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 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 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 keybind_button(r2, "Mixed", .TRIXEL_VIEW_MIXED, *t_button_selectable(theme, trixel_view_mode == 3)) then trixel_view_mode = 3;
|
|
}
|