650 lines
18 KiB
Plaintext
650 lines
18 KiB
Plaintext
#scope_file
|
|
|
|
// @ToDo do a config tweak file thing like jblow has and add this there.
|
|
CAMERA_INACTIVE_TIME_TO_ORBIT :: 200000000.0;
|
|
MAX_CAMERA_DIST :: 25.0;
|
|
MIN_CAMERA_DIST :: 2.0;
|
|
DIST_SCROLL_SPEED :: 0.8;
|
|
|
|
mouse2Active : bool;
|
|
mouse2ActivationPosition : Vector2;
|
|
mouse3Active : bool;
|
|
mouse3ActivationPosition : Vector2;
|
|
|
|
cameraTilt : float = 0.2;
|
|
cameraDist : float = 10.0;
|
|
cameraRotation : float;
|
|
oldCameraRotation : float;
|
|
oldCameraTilt : float;
|
|
cameraCenter : Vector2;
|
|
|
|
trile_preview_disabled : bool = false;
|
|
|
|
Level_Editor_Tool_Mode :: enum {
|
|
POINT;
|
|
BRUSH;
|
|
AREA;
|
|
LINE;
|
|
}
|
|
|
|
current_tool_mode : Level_Editor_Tool_Mode = .POINT;
|
|
brush_radius : int = 2;
|
|
brush_height : int = 0;
|
|
|
|
area_active : bool;
|
|
area_start_x : int;
|
|
area_start_y : int;
|
|
area_start_z : int;
|
|
|
|
line_active : bool;
|
|
line_start_x : int;
|
|
line_start_y : int;
|
|
line_start_z : int;
|
|
|
|
current_orientation_face : u8 = 0;
|
|
current_orientation_twist : u8 = 0;
|
|
|
|
get_current_orientation :: () -> u8 {
|
|
return current_orientation_face * 4 + current_orientation_twist;
|
|
}
|
|
|
|
#scope_export
|
|
|
|
toggle_preview :: () {
|
|
trile_preview_disabled = !trile_preview_disabled;
|
|
} @Command
|
|
|
|
set_dist :: (dist: float) {
|
|
cameraDist = dist;
|
|
} @Command
|
|
|
|
#scope_file
|
|
|
|
tacomaSamples : s32 = 100;
|
|
tacomaResolution : s32 = 500;
|
|
tacomaExposure : float = 1.0;
|
|
tacomaContrast : float = 1.0;
|
|
tacomaSaturation : float = 1.0;
|
|
|
|
lastInputTime : float64;
|
|
|
|
editY : s32;
|
|
|
|
pointerHit : bool;
|
|
pointerX : s32;
|
|
pointerY : s32;
|
|
|
|
show_trile_preview : bool = true;
|
|
trile_preview_x : int = 0;
|
|
trile_preview_y : int = 0;
|
|
trile_preview_z : int = 0;
|
|
|
|
Level_Editor_Tab :: enum {
|
|
TACOMA;
|
|
TOOLS;
|
|
INFO;
|
|
};
|
|
|
|
current_tab : Level_Editor_Tab = .TOOLS;
|
|
get_level_editor_camera :: () -> Camera {
|
|
camera: Camera;
|
|
camera.near = 0.1;
|
|
camera.far = 5000;
|
|
camera.target = .{cameraCenter.x, xx editY, cameraCenter.y};
|
|
cameraDir : Vector3 = .{1, 0, 0};
|
|
qrotation : Quaternion = .{cos(-cameraRotation/2.0),0,sin(-cameraRotation/2.0),0};
|
|
qtilt : Quaternion = .{cos(-cameraTilt/2.0),sin(-cameraTilt/2.0), 0, 0};
|
|
|
|
rotate(*cameraDir, qrotation * qtilt);
|
|
|
|
camera.position = camera.target;
|
|
camera.position += cameraDir * cameraDist;
|
|
|
|
if is_in_reflection_pass {
|
|
camera.position.y *= -1;
|
|
camera.target.y *= -1;
|
|
}
|
|
|
|
return camera;
|
|
}
|
|
|
|
|
|
tick_level_editor_camera :: () {
|
|
if console_open_ignore_input then return;
|
|
|
|
if get_time() - lastInputTime > CAMERA_INACTIVE_TIME_TO_ORBIT { // idle rotating camera
|
|
cameraRotation += cast(float) delta_time;
|
|
}
|
|
|
|
cameraSpeed :: 150;
|
|
|
|
forward : Vector3 = .{1, 0, 0};
|
|
qrotation : Quaternion = .{cos(-cameraRotation/2.0),0,sin(-cameraRotation/2.0),0};
|
|
|
|
rotate(*forward, qrotation);
|
|
forward2d := Vector2.{forward.x, forward.z};
|
|
|
|
left : Vector3 = .{0, 0, 1};
|
|
qrotation_left : Quaternion = .{cos(-cameraRotation/2.0),0,sin(-cameraRotation/2.0),0};
|
|
rotate(*left, qrotation_left);
|
|
left2d := Vector2.{left.x, left.z};
|
|
|
|
cameraDist = clamp(cameraDist - mouse_delta_z * DIST_SCROLL_SPEED, 0.0, MAX_CAMERA_DIST);
|
|
|
|
distRange : float = MAX_CAMERA_DIST - MIN_CAMERA_DIST;
|
|
zoomCameraMovementMultiplier := 0.03 + ((cameraDist - MIN_CAMERA_DIST) / distRange) * 0.15;
|
|
|
|
if input_button_states[#char "W"] & .DOWN {
|
|
lastInputTime = get_time();
|
|
cameraCenter += -cameraSpeed * zoomCameraMovementMultiplier * (xx delta_time) * forward2d;
|
|
}
|
|
if input_button_states[#char "S"] & .DOWN {
|
|
lastInputTime = get_time();
|
|
cameraCenter += cameraSpeed * zoomCameraMovementMultiplier * (xx delta_time) * forward2d;
|
|
}
|
|
|
|
if input_button_states[#char "A"] & .DOWN {
|
|
lastInputTime = get_time();
|
|
cameraCenter += -cameraSpeed * zoomCameraMovementMultiplier * (xx delta_time) * left2d;
|
|
}
|
|
if input_button_states[#char "D"] & .DOWN {
|
|
lastInputTime = get_time();
|
|
cameraCenter += cameraSpeed * zoomCameraMovementMultiplier * (xx delta_time) * left2d;
|
|
}
|
|
|
|
if input_button_states[Key_Code.ARROW_UP] & .START {
|
|
lastInputTime = get_time();
|
|
editY = min(editY + 1, 100);
|
|
}
|
|
|
|
if input_button_states[Key_Code.ARROW_DOWN] & .START {
|
|
lastInputTime = get_time();
|
|
editY = max(editY - 1, 0);
|
|
}
|
|
|
|
if get_mouse_state(Key_Code.MOUSE_BUTTON_MIDDLE) & .DOWN {
|
|
if mouse2Active {
|
|
lastInputTime = get_time();
|
|
diff := mouse2ActivationPosition - Vector2.{input_mouse_x, input_mouse_y};
|
|
diff *= 0.5;
|
|
cameraRotation = oldCameraRotation + diff.x / 100;
|
|
cameraTilt = oldCameraTilt - diff.y / 100;
|
|
cameraTilt = max(0.1, cameraTilt);
|
|
cameraTilt = min(PI/2.2, cameraTilt);
|
|
} else {
|
|
mouse2Active = true;
|
|
mouse2ActivationPosition = Vector2.{input_mouse_x, input_mouse_y};
|
|
oldCameraRotation = cameraRotation;
|
|
oldCameraTilt = cameraTilt;
|
|
}
|
|
} else {
|
|
mouse2Active = false;
|
|
}
|
|
|
|
|
|
if get_mouse_state(Key_Code.MOUSE_BUTTON_RIGHT) & .DOWN {
|
|
if mouse3Active {
|
|
lastInputTime = get_time();
|
|
diff := mouse3ActivationPosition - Vector2.{input_mouse_x, input_mouse_y};
|
|
cameraCenter = cameraCenter + forward2d * -diff.y * cast(float) delta_time * zoomCameraMovementMultiplier;
|
|
cameraCenter = cameraCenter + left2d * -diff.x * cast(float) delta_time * zoomCameraMovementMultiplier;
|
|
} else {
|
|
mouse3Active = true;
|
|
mouse3ActivationPosition = Vector2.{input_mouse_x, input_mouse_y};
|
|
}
|
|
} else {
|
|
mouse3Active = false;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
draw_tacoma_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) {
|
|
curworld := get_current_world();
|
|
r := total_r;
|
|
r.h = ui_h(3,0);
|
|
#if HAS_TACOMA {
|
|
if GR.button(r, "Render with Tacoma", *theme.button_theme) {
|
|
cam := get_level_editor_camera();
|
|
gen_reference(tacomaResolution, tacomaResolution, cam.position, cam.target, tacomaSamples, true, curworld.world);
|
|
}
|
|
r.y += r.h;
|
|
if GR.button(r, "Render a RDM", *theme.button_theme) {
|
|
gen_rdm(tacomaSamples, true, curworld.world);
|
|
}
|
|
r.y += r.h;
|
|
if GR.button(r, "Bake all chunk RDMs", *theme.button_theme) {
|
|
if curworld.valid then rdm_bake_all_chunks(curworld.world, tacomaSamples, true);
|
|
}
|
|
r.y += r.h;
|
|
if rdm_bake.active {
|
|
total := cast(s32) rdm_bake.jobs.count;
|
|
done := rdm_bake.current_job;
|
|
pct := ifx total > 0 then done * 100 / total else 0;
|
|
GR.label(r, tprint("Baking RDMs: %/% (\%%)", done, total, pct), *t_label_left(theme));
|
|
r.y += r.h;
|
|
}
|
|
if current_screenshot.valid {
|
|
aspect := cast(float)current_screenshot.width / cast(float)current_screenshot.height;
|
|
|
|
r.h = r.w / aspect;
|
|
uiTex := New(Ui_Texture,, temp);
|
|
uiTex.tex = current_screenshot.image;
|
|
set_shader_for_images(uiTex);
|
|
immediate_quad(.{r.x, r.y}, .{r.x + r.w, r.y}, .{r.x + r.w, r.y + r.h}, .{r.x, r.y + r.h});
|
|
set_shader_for_color();
|
|
r.y += r.h;
|
|
}
|
|
r.h = ui_h(4,0);
|
|
|
|
GR.label(r, "Samples", *t_label_left(theme));
|
|
r.y += r.h;
|
|
GR.slider(r, *tacomaSamples, 10, 10000, 10, *theme.slider_theme);
|
|
r.y += r.h;
|
|
GR.label(r, "Resolution", *t_label_left(theme));
|
|
r.y += r.h;
|
|
GR.slider(r, *tacomaResolution, 10, 5000, 10, *theme.slider_theme);
|
|
r.y += r.h;
|
|
GR.label(r, "Exposure", *t_label_left(theme));
|
|
r.y += r.h;
|
|
GR.slider(r, *tacomaExposure, 0.5, 3.0, 0.1, *theme.slider_theme);
|
|
r.y += r.h;
|
|
GR.label(r, "Contrast", *t_label_left(theme));
|
|
r.y += r.h;
|
|
GR.slider(r, *tacomaContrast, 0.5, 3.0, 0.1, *theme.slider_theme);
|
|
r.y += r.h;
|
|
GR.label(r, "Saturation", *t_label_left(theme));
|
|
r.y += r.h;
|
|
GR.slider(r, *tacomaSaturation, 0.5, 3.0, 0.1, *theme.slider_theme);
|
|
r.y += r.h;
|
|
|
|
|
|
} else {
|
|
GR.label(r, "Tacoma is not enabled in this build.", *theme.label_theme);
|
|
}
|
|
}
|
|
|
|
#scope_file
|
|
|
|
Edit_Mode :: enum {
|
|
TRILES;
|
|
}
|
|
|
|
editMode : Edit_Mode;
|
|
|
|
#scope_export
|
|
|
|
draw_tools_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) {
|
|
r := total_r;
|
|
r.h = ui_h(4, 0);
|
|
|
|
// Tool mode buttons
|
|
if GR.button(r, "Point", *t_button_selectable(theme, current_tool_mode == .POINT)) {
|
|
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;
|
|
}
|
|
r.y += r.h;
|
|
if GR.button(r, "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;
|
|
}
|
|
r.y += r.h;
|
|
|
|
// Brush radius/height (only for brush mode)
|
|
if current_tool_mode == .BRUSH {
|
|
r.h = ui_h(3, 2);
|
|
GR.label(r, "Brush Radius", *t_label_left(theme));
|
|
r.y += r.h;
|
|
r.h = ui_h(4, 0);
|
|
GR.slider(r, *brush_radius, 1, 8, 1, *theme.slider_theme);
|
|
r.y += r.h;
|
|
r.h = ui_h(3, 2);
|
|
GR.label(r, "Brush Height (±Y layers)", *t_label_left(theme));
|
|
r.y += r.h;
|
|
r.h = ui_h(4, 0);
|
|
GR.slider(r, *brush_height, 0, 8, 1, *theme.slider_theme);
|
|
r.y += r.h * 2;
|
|
} else {
|
|
r.y += r.h;
|
|
}
|
|
|
|
// Status hints for multi-click tools
|
|
r.h = ui_h(3, 2);
|
|
if current_tool_mode == .AREA {
|
|
if area_active {
|
|
GR.label(r, "Click second corner to fill", *t_label_left(theme));
|
|
} else {
|
|
GR.label(r, "Click first corner to start", *t_label_left(theme));
|
|
}
|
|
r.y += r.h;
|
|
} else if current_tool_mode == .LINE {
|
|
if line_active {
|
|
GR.label(r, "Click endpoint to draw line", *t_label_left(theme));
|
|
} else {
|
|
GR.label(r, "Click start of line", *t_label_left(theme));
|
|
}
|
|
r.y += r.h;
|
|
}
|
|
|
|
// Orientation controls
|
|
r.y += r.h;
|
|
r.h = ui_h(3, 2);
|
|
GR.label(r, "-- Orientation --", *t_label_left(theme));
|
|
r.y += r.h;
|
|
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));
|
|
r.y += r.h;
|
|
}
|
|
|
|
handle_tool_click :: (x: int, y: int, z: int, delete: bool = false) {
|
|
curworld := get_current_world();
|
|
if delete {
|
|
remove_trile(cast(s32)x, cast(s32)y, cast(s32)z);
|
|
} else {
|
|
if editor_current_trile != null then add_trile(editor_current_trile.name, cast(s32)x, cast(s32)y, cast(s32)z, get_current_orientation());
|
|
}
|
|
}
|
|
|
|
apply_brush :: (cx: int, cy: int, cz: int, delete: bool) {
|
|
for dy: -brush_height..brush_height {
|
|
for dx: -brush_radius..brush_radius {
|
|
for dz: -brush_radius..brush_radius {
|
|
dist := sqrt(cast(float)(dx*dx + dz*dz));
|
|
if dist <= cast(float)brush_radius {
|
|
handle_tool_click(cx + dx, cy + dy, cz + dz, delete);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
apply_area :: (x2: int, y2: int, z2: int, delete: bool) {
|
|
for x: min(area_start_x, x2)..max(area_start_x, x2) {
|
|
for y: min(area_start_y, y2)..max(area_start_y, y2) {
|
|
for z: min(area_start_z, z2)..max(area_start_z, z2) {
|
|
handle_tool_click(x, y, z, delete);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
apply_line :: (x2: int, y2: int, z2: int, delete: bool) {
|
|
x1 := line_start_x; y1 := line_start_y; z1 := line_start_z;
|
|
dx := abs(x2 - x1); dy := abs(y2 - y1); dz := abs(z2 - z1);
|
|
sx := ifx x1 < x2 then 1 else -1;
|
|
sy := ifx y1 < y2 then 1 else -1;
|
|
sz := ifx z1 < z2 then 1 else -1;
|
|
// 3D Bresenham: drive along the longest axis
|
|
dm := max(dx, max(dy, dz));
|
|
x := x1; y := y1; z := z1;
|
|
ex := dm / 2; ey := dm / 2; ez := dm / 2;
|
|
count := 0;
|
|
while count <= dm && count < 2000 {
|
|
handle_tool_click(x, y, z, delete);
|
|
ex -= dx; if ex < 0 { x += sx; ex += dm; }
|
|
ey -= dy; if ey < 0 { y += sy; ey += dm; }
|
|
ez -= dz; if ez < 0 { z += sz; ez += dm; }
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
add_trile :: (name: string, x: s32, y: s32, z: s32, orientation: u8 = 0) {
|
|
curworld := get_current_world();
|
|
|
|
// Remove any existing trile at this position first.
|
|
remove_trile(x, y, z);
|
|
|
|
// Find or create the chunk.
|
|
key := world_to_chunk_coord(x, y, z);
|
|
chunk := table_find_pointer(*curworld.world.chunks, key);
|
|
if !chunk {
|
|
new_chunk: Chunk;
|
|
new_chunk.coord = key;
|
|
table_set(*curworld.world.chunks, key, new_chunk);
|
|
chunk = table_find_pointer(*curworld.world.chunks, key);
|
|
}
|
|
|
|
lx, ly, lz := world_to_local(x, y, z);
|
|
inst := Trile_Instance.{x = lx, y = ly, z = lz, orientation = orientation};
|
|
|
|
// Find existing group for this trile type, or create one.
|
|
for *group: chunk.groups {
|
|
if group.trile_name == name {
|
|
array_add(*group.instances, inst);
|
|
return;
|
|
}
|
|
}
|
|
group: Chunk_Trile_Group;
|
|
group.trile_name = sprint("%", name);
|
|
array_add(*group.instances, inst);
|
|
array_add(*chunk.groups, group);
|
|
} @Command
|
|
|
|
remove_trile :: (x: s32, y: s32, z: s32) {
|
|
curworld := get_current_world();
|
|
|
|
key := world_to_chunk_coord(x, y, z);
|
|
chunk := table_find_pointer(*curworld.world.chunks, key);
|
|
if !chunk then return;
|
|
|
|
lx, ly, lz := world_to_local(x, y, z);
|
|
|
|
for *group: chunk.groups {
|
|
for inst, idx: group.instances {
|
|
if inst.x == lx && inst.y == ly && inst.z == lz {
|
|
array_unordered_remove_by_index(*group.instances, idx);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} @Command
|
|
|
|
|
|
|
|
tick_level_editor :: () {
|
|
#if HAS_TACOMA { 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;
|
|
}
|
|
}
|
|
|
|
ray := get_mouse_ray(*get_level_editor_camera());
|
|
hit, point := ray_plane_collision_point(ray, xx editY, 20);
|
|
|
|
show_trile_preview = false;
|
|
if hit {
|
|
show_trile_preview = true;
|
|
trile_preview_x = xx floor(point.x);
|
|
trile_preview_y = editY;
|
|
trile_preview_z = xx floor(point.y);
|
|
|
|
px := trile_preview_x;
|
|
py := trile_preview_y;
|
|
pz := trile_preview_z;
|
|
|
|
if current_tool_mode == .POINT {
|
|
if get_mouse_state(Key_Code.MOUSE_BUTTON_LEFT) & .START {
|
|
handle_tool_click(px, py, pz);
|
|
}
|
|
if get_mouse_state(Key_Code.MOUSE_BUTTON_RIGHT) & .START {
|
|
handle_tool_click(px, py, pz, true);
|
|
}
|
|
} else if current_tool_mode == .BRUSH {
|
|
if get_mouse_state(Key_Code.MOUSE_BUTTON_LEFT) & .DOWN {
|
|
apply_brush(px, py, pz, false);
|
|
}
|
|
if get_mouse_state(Key_Code.MOUSE_BUTTON_RIGHT) & .DOWN {
|
|
apply_brush(px, py, pz, true);
|
|
}
|
|
} else if current_tool_mode == .AREA {
|
|
if get_mouse_state(Key_Code.MOUSE_BUTTON_LEFT) & .START {
|
|
if !area_active {
|
|
area_active = true;
|
|
area_start_x = px;
|
|
area_start_y = py;
|
|
area_start_z = pz;
|
|
} else {
|
|
apply_area(px, py, pz, false);
|
|
area_active = false;
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
} else if current_tool_mode == .LINE {
|
|
if get_mouse_state(Key_Code.MOUSE_BUTTON_LEFT) & .START {
|
|
if !line_active {
|
|
line_active = true;
|
|
line_start_x = px;
|
|
line_start_y = py;
|
|
line_start_z = pz;
|
|
} else {
|
|
apply_line(px, py, pz, false);
|
|
line_active = false;
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
create_level_editor_preview_tasks :: () {
|
|
curworld := get_current_world();
|
|
if !curworld.valid then return;
|
|
if !editor_current_trile then return;
|
|
|
|
positions: [..]Vector4;
|
|
positions.allocator = temp;
|
|
ori := cast(float) get_current_orientation();
|
|
|
|
px := trile_preview_x; py := trile_preview_y; pz := trile_preview_z;
|
|
|
|
if current_tool_mode == .POINT {
|
|
array_add(*positions, .{cast(float)px, cast(float)py, cast(float)pz, ori});
|
|
} else if current_tool_mode == .BRUSH {
|
|
for dy: -brush_height..brush_height {
|
|
for dx: -brush_radius..brush_radius {
|
|
for dz: -brush_radius..brush_radius {
|
|
dist := sqrt(cast(float)(dx*dx + dz*dz));
|
|
if dist <= cast(float)brush_radius {
|
|
array_add(*positions, .{cast(float)(px+dx), cast(float)(py+dy), cast(float)(pz+dz), ori});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if current_tool_mode == .AREA {
|
|
if area_active {
|
|
for x: min(area_start_x, px)..max(area_start_x, px) {
|
|
for y: min(area_start_y, py)..max(area_start_y, py) {
|
|
for z: min(area_start_z, pz)..max(area_start_z, pz) {
|
|
array_add(*positions, .{cast(float)x, cast(float)y, cast(float)z, ori});
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
array_add(*positions, .{cast(float)px, cast(float)py, cast(float)pz, ori});
|
|
}
|
|
} else if current_tool_mode == .LINE {
|
|
if line_active {
|
|
x1 := line_start_x; y1 := line_start_y; z1 := line_start_z;
|
|
x2 := px; y2 := py; z2 := pz;
|
|
dx := abs(x2-x1); dy := abs(y2-y1); dz := abs(z2-z1);
|
|
sx := ifx x1 < x2 then 1 else -1;
|
|
sy := ifx y1 < y2 then 1 else -1;
|
|
sz := ifx z1 < z2 then 1 else -1;
|
|
dm := max(dx, max(dy, dz));
|
|
x := x1; y := y1; z := z1;
|
|
ex := dm/2; ey := dm/2; ez := dm/2;
|
|
count := 0;
|
|
while count <= dm && count < 2000 {
|
|
array_add(*positions, .{cast(float)x, cast(float)y, cast(float)z, ori});
|
|
ex -= dx; if ex < 0 { x += sx; ex += dm; }
|
|
ey -= dy; if ey < 0 { y += sy; ey += dm; }
|
|
ez -= dz; if ez < 0 { z += sz; ez += dm; }
|
|
count += 1;
|
|
}
|
|
} else {
|
|
array_add(*positions, .{cast(float)px, cast(float)py, cast(float)pz, ori});
|
|
}
|
|
}
|
|
|
|
if positions.count == 0 then return;
|
|
|
|
task: Rendering_Task_Trile;
|
|
task.trile = editor_current_trile.name;
|
|
task.positions = positions;
|
|
task.worldConf = *curworld.world.conf;
|
|
task.is_preview = true;
|
|
add_rendering_task(task);
|
|
}
|
|
|
|
draw_level_editor :: () {
|
|
curworld := get_current_world();
|
|
if !curworld.valid then return;
|
|
cam := get_level_editor_camera();
|
|
create_set_cam_rendering_task(cam, effective_plane_height(*curworld.world.conf));
|
|
create_world_rendering_tasks(*curworld.world, cam);
|
|
if show_trile_preview && !trile_preview_disabled {
|
|
create_level_editor_preview_tasks();
|
|
}
|
|
}
|
|
|
|
draw_level_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 GR.button(tab_r, "Tools", *t_button_tab(theme, current_tab == .TOOLS)) {
|
|
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;
|
|
}
|
|
tab_r.x += tab_r.w;
|
|
if GR.button(tab_r, "Tacoma", *t_button_tab(theme, current_tab == .TACOMA)) {
|
|
current_tab = .TACOMA;
|
|
}
|
|
r.y += tab_r.h;
|
|
|
|
curworld := get_current_world();
|
|
if current_tab == {
|
|
case .TOOLS;
|
|
draw_tools_tab(theme, r);
|
|
case .TACOMA;
|
|
draw_tacoma_tab(theme, r);
|
|
case .INFO;
|
|
if curworld.valid then autoedit(r, *curworld.world.conf, theme);
|
|
}
|
|
draw_picker(theme);
|
|
}
|