From aa0f945b0cde55d4fa8d973a5ab41455062cd124 Mon Sep 17 00:00:00 2001 From: sergeypdev Date: Sat, 4 Jan 2025 00:06:55 +0400 Subject: [PATCH] Many editor improvements --- game/game.odin | 136 +++++++++++++++++++++++++++++++++++++++++------- game/track.odin | 36 ++++++++++++- 2 files changed, 152 insertions(+), 20 deletions(-) diff --git a/game/game.odin b/game/game.odin index 0c0b9a7..49ebf38 100644 --- a/game/game.odin +++ b/game/game.odin @@ -15,6 +15,9 @@ package game import "assets" +import "core:c" +import "core:fmt" +import "core:log" import "core:math" import "core:math/linalg" import rl "vendor:raylib" @@ -57,12 +60,13 @@ Move_Axis :: enum { } Editor_State :: struct { - world: World, - mouse_captured: bool, - point_selection: map[int]bool, - track_edit_state: Track_Edit_State, - move_axis: Move_Axis, - initial_point_pos: rl.Vector3, + world: World, + mouse_captured: bool, + point_selection: map[int]bool, + track_edit_state: Track_Edit_State, + move_axis: Move_Axis, + total_movement_world: rl.Vector3, + initial_point_pos: rl.Vector3, } g_mem: ^Game_Memory @@ -218,10 +222,17 @@ update_editor :: proc() { if is_point_selected() { if rl.IsKeyPressed(.X) { - #reverse for _, i in get_world().track.points { - if i in es.point_selection { + + if len(es.point_selection) <= 1 { + for i in es.point_selection { ordered_remove(&get_world().track.points, i) } + } else { + #reverse for _, i in get_world().track.points { + if i in es.point_selection { + ordered_remove(&get_world().track.points, i) + } + } } clear(&es.point_selection) @@ -229,6 +240,7 @@ update_editor :: proc() { if rl.IsKeyPressed(.G) { es.track_edit_state = .Move es.move_axis = .None + es.total_movement_world = {} // es.initial_point_pos = g_mem.track.points[es.selected_track_point] } } @@ -297,6 +309,8 @@ update_editor :: proc() { for k in es.point_selection { get_world().track.points[k] += movement_world } + + es.total_movement_world += movement_world } } } @@ -380,6 +394,12 @@ draw :: proc() { // road_uvs.allocator = context.temp_allocator // road_indices.allocator = context.temp_allocator + interpolated_points := calculate_spline_interpolated_points( + points[:], + context.temp_allocator, + ) + + { // Debug draw spline road { @@ -388,11 +408,6 @@ draw :: proc() { rlgl.Color3f(1, 0, 0) - interpolated_points := calculate_spline_interpolated_points( - points[:], - context.temp_allocator, - ) - debug_draw_spline(interpolated_points) debug_draw_spline_mesh(interpolated_points) } @@ -432,6 +447,18 @@ draw :: proc() { if g_mem.editor { rl.DrawText("Editor", 5, 5, 8, rl.ORANGE) + + switch g_mem.es.track_edit_state { + case .Select: + case .Move: + rl.DrawText( + fmt.ctprintf("%v %v", g_mem.es.move_axis, g_mem.es.total_movement_world), + 5, + 16, + 8, + rl.ORANGE, + ) + } } } @@ -441,8 +468,67 @@ draw :: proc() { points := &get_world().track.points points_len := len(points) + if points_len > 0 { + // Add point before first + { + tangent: rl.Vector3 + + if points_len > 1 { + tangent = linalg.normalize0(points[1] - points[0]) + } else { + tangent = rl.Vector3{-1, 0, 0} + } + + new_point_pos := points[0] - tangent * 4 + + if (spline_handle( + new_point_pos, + camera, + false, + rl.GuiIconName.ICON_TARGET_POINT, + )) { + inject_at(&get_world().track.points, 0, new_point_pos) + log.debugf("add point before 0") + } + } + + // Add point after last + { + tangent: rl.Vector3 + + if points_len > 1 { + tangent = linalg.normalize0(points[points_len - 1] - points[points_len - 2]) + } else { + tangent = rl.Vector3{-1, 0, 0} + } + + new_point_pos := points[points_len - 1] + tangent * 4 + + if (spline_handle( + new_point_pos, + camera, + false, + rl.GuiIconName.ICON_TARGET_POINT, + )) { + inject_at(&get_world().track.points, points_len - 1 + 1, new_point_pos) + log.debugf("add point before 0") + } + } + } + selected_point := false for i in 0 ..< points_len { + + if i < points_len - 1 { + t := (f32(i) + 0.5) / f32(points_len) + middle_pos := sample_spline(points[:], t) + + if (spline_handle(middle_pos, camera, false, rl.GuiIconName.ICON_TARGET_POINT)) { + inject_at(&get_world().track.points, i + 1, middle_pos) + log.debugf("add point after %d", i) + } + } + if spline_handle(get_world().track.points[i], camera, es.point_selection[i]) { if !rl.IsKeyDown(.LEFT_CONTROL) { clear(&g_mem.es.point_selection) @@ -485,6 +571,8 @@ spline_handle :: proc( world_pos: rl.Vector3, camera: rl.Camera, selected: bool, + icon := rl.GuiIconName.ICON_NONE, + size := f32(20), ) -> ( clicked: bool, ) { @@ -492,18 +580,28 @@ spline_handle :: proc( return } pos := rl.GetWorldToScreen(world_pos, camera) - size := rl.Vector2{10, 10} min, max := pos - size, pos + size mouse_pos := rl.GetMousePosition() is_hover := - mouse_pos.x >= min.x && - mouse_pos.y >= min.y && - mouse_pos.x <= max.x && - mouse_pos.y <= max.y + (mouse_pos.x >= min.x && + mouse_pos.y >= min.y && + mouse_pos.x <= max.x && + mouse_pos.y <= max.y) - rl.DrawRectangleV(pos, size, selected ? rl.BLUE : (is_hover ? rl.ORANGE : rl.WHITE)) + + rl.DrawCircleV(pos, size / 2, selected ? rl.BLUE : (is_hover ? rl.ORANGE : rl.WHITE)) + if icon != .ICON_NONE { + rl.GuiDrawIcon( + icon, + c.int(pos.x) - 7, + c.int(pos.y) - 7, + 1, + selected || is_hover ? rl.WHITE : rl.BLACK, + ) + } + // rl.DrawRectangleV(pos, size, selected ? rl.BLUE : (is_hover ? rl.ORANGE : rl.WHITE)) return rl.IsMouseButtonPressed(.LEFT) && is_hover } diff --git a/game/track.odin b/game/track.odin index 121112b..041b594 100644 --- a/game/track.odin +++ b/game/track.odin @@ -1,5 +1,7 @@ package game +import "base:builtin" +import "core:math" import lg "core:math/linalg" import rl "vendor:raylib" import "vendor:raylib/rlgl" @@ -39,6 +41,39 @@ Interpolated_Point :: struct { normal: rl.Vector3, } +sample_spline :: proc(points: []rl.Vector3, t: f32) -> rl.Vector3 { + points_len := len(points) + if points_len >= 2 { + t_mul_len := math.saturate(t) * f32(len(points)) + i := int(t_mul_len) + frac_t := t_mul_len - f32(i) + + extended_start := points[0] + (points[0] - points[1]) + extended_end := points[points_len - 1] + points[points_len - 1] - points[points_len - 2] + extended_end2 := extended_end + points[points_len - 1] - points[points_len - 2] + + prev := i > 0 ? points[i - 1] : extended_start + current := points[i] + next := i < points_len - 1 ? points[i + 1] : extended_end + next2 := i < points_len - 2 ? points[i + 2] : extended_end2 + + a, b, c, d := catmull_rom_coefs( + prev, + current, + next, + next2, + CATMULL_ROM_ALPHA, + CATMULL_ROM_TENSION, + ) + + return catmull_rom(a, b, c, d, frac_t) + } else if len(points) == 1 { + return points[0] + } + + return {} +} + calculate_spline_interpolated_points :: proc( points: []rl.Vector3, allocator := context.allocator, @@ -47,7 +82,6 @@ calculate_spline_interpolated_points :: proc( ctrl_rotations := calculate_spline_ctrl_rotations(points, context.temp_allocator) - if points_len >= 2 { interpolated_points := make( []Interpolated_Point,