#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; #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; world : World; 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; world.ground[500][500] = .GRASS; 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_RIGHT) & .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_MIDDLE) & .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) { 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, .{tacomaExposure, tacomaContrast, tacomaSaturation}, .{cam.target, cam.position, tacomaSamples, true}, world); } 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; GROUND; } groundType : Ground_Tile; editMode : Edit_Mode; #scope_export draw_tools_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) { r := total_r; r.h = ui_h(3,0); r.w = r.w/2.0; if GR.button(r, "Edit triles", *t_button_selectable(theme, editMode == .TRILES)) { editMode = .TRILES; } r.x += r.w; if GR.button(r, "Edit ground", *t_button_selectable(theme, editMode == .GROUND)) { editMode = .GROUND; } r.x -= r.w; r.w *= 2.0; r.y += r.h * 1.5; if editMode == .GROUND { if GR.button(r, "Grass", *t_button_selectable(theme, groundType == .GRASS)) { groundType = .GRASS; } r.y += r.h; if GR.button(r, "Water", *t_button_selectable(theme, groundType == .WATER)) { groundType = .WATER; } r.y += r.h; if GR.button(r, "Sand", *t_button_selectable(theme, groundType == .WATER)) { groundType = .SAND; } } } handle_tool_click :: (x: int, y: int, z: int) { if editMode == { case .TRILES; if editor_current_trile != null then add_trile(editor_current_trile.name, cast(float)x, cast(float)y, cast(float)z); case .GROUND; ray := get_mouse_ray(*get_level_editor_camera()); hit, point := ray_plane_collision_point(ray, 0, 100); if hit { world.ground[floor(point.y).(int) + 500][floor(point.x).(int) + 500] = groundType; } } } add_trile :: (name: string, x: float, y: float, z: float) { loose_float_comp :: (a: float, b: float) -> bool { return abs(a-b) < 0.001; } // Check if the position is already occupied. @ToDo: we would probably like to // have some acceleration structure like a hashmap to speed up this check when // we are checking for collisions. for world.positions { for v, idx: it.positions { if loose_float_comp(v.x, x) && loose_float_comp(v.y, y) && loose_float_comp(v.z, z) { array_unordered_remove_by_index(*it.positions, idx); break; } } } for world.positions { if it.trileName == name { array_add(*it.positions, Vector4.{x,y,z,1}); return; } } poses := TrilePositions.{ trileName = sprint("%", name), }; array_add(*poses.positions, Vector4.{x,y,z,1}); array_add(*world.positions, poses); } @Command tick_level_editor :: () { tick_level_editor_camera(); 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); if get_mouse_state(Key_Code.MOUSE_BUTTON_LEFT) & .START { handle_tool_click(xx floor(point.x), xx editY, xx floor(point.y)); } } } draw_level_editor :: () { create_set_cam_rendering_task(get_level_editor_camera()); create_world_rendering_tasks(*world); } 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; if current_tab == { case .TOOLS; draw_tools_tab(theme, r); case .TACOMA; draw_tacoma_tab(theme, r); case .INFO; autoedit(r, *world.conf, theme); } draw_trile_picker(theme); }