From ad2d175b10da363a2eb13735bd291be90e830766 Mon Sep 17 00:00:00 2001 From: sergeypdev Date: Sat, 4 Jan 2025 03:42:04 +0400 Subject: [PATCH] Kansei dorifto (no idea why car rotates like that lol) --- game/game.odin | 45 +++++++++++----- game/track.odin | 135 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 162 insertions(+), 18 deletions(-) diff --git a/game/game.odin b/game/game.odin index 49ebf38..3cfcae9 100644 --- a/game/game.odin +++ b/game/game.odin @@ -370,18 +370,35 @@ draw :: proc() { camera := game_camera_3d() + points := &get_world().track.points + + interpolated_points := calculate_spline_interpolated_points(points[:], context.temp_allocator) + + collision, segment_idx := raycast_spline_tube( + interpolated_points, + rl.GetScreenToWorldRay(rl.GetMousePosition(), camera), + ) + { rl.BeginMode3D(camera) defer rl.EndMode3D() - rl.DrawModel( - assets.get_model(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb"), - rl.Vector3{0, 0, 0}, - 1, - rl.WHITE, - ) + if collision.hit { + tangent, bitangent := get_point_frame(interpolated_points, segment_idx) - points := &get_world().track.points + rot_matrix: linalg.Matrix3f32 + rot_matrix[0] = bitangent + rot_matrix[1] = interpolated_points[segment_idx].normal + rot_matrix[2] = -tangent + + angle, axis := linalg.angle_axis_from_quaternion( + linalg.quaternion_from_matrix3(rot_matrix), + ) + car_model := assets.get_model(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb") + car_model.transform = rl.MatrixRotate(axis, angle) + + rl.DrawModel(car_model, collision.point, 1, rl.WHITE) + } // road: rl.Mesh // defer rl.UnloadMesh(road) @@ -394,11 +411,6 @@ 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 @@ -439,6 +451,10 @@ draw :: proc() { } } } + + if collision.hit { + rl.DrawSphereWires(collision.point, 1, 8, 8, rl.RED) + } } { @@ -448,6 +464,10 @@ draw :: proc() { if g_mem.editor { rl.DrawText("Editor", 5, 5, 8, rl.ORANGE) + if collision.hit { + rl.DrawText(fmt.ctprintf("Segment: %v", segment_idx), 5, 32, 8, rl.ORANGE) + } + switch g_mem.es.track_edit_state { case .Select: case .Move: @@ -465,7 +485,6 @@ draw :: proc() { if g_mem.editor { es := &g_mem.es - points := &get_world().track.points points_len := len(points) if points_len > 0 { diff --git a/game/track.odin b/game/track.odin index 041b594..2c446be 100644 --- a/game/track.odin +++ b/game/track.odin @@ -74,17 +74,142 @@ sample_spline :: proc(points: []rl.Vector3, t: f32) -> rl.Vector3 { return {} } +get_point_frame :: #force_inline proc( + ps: #soa[]Interpolated_Point, + i: int, +) -> ( + tangent: rl.Vector3, + bitangent: rl.Vector3, +) { + if len(ps) >= 2 { + tangent = + ps[i + 1].pos - ps[i].pos if (i < len(ps) - 1) else ps[len(ps) - 1].pos - ps[len(ps) - 2].pos + normal := ps[i].normal + bitangent = lg.normalize0(lg.cross(tangent, normal)) + } + + return +} + +point_to_quad_sdf :: proc(p, a, b, c, d: rl.Vector3) -> f32 { + ba := b - a + pa := p - a + cb := c - b + pb := p - b + dc := d - c + pc := p - c + ad := a - d + pd := p - d + nor := lg.cross(ba, ad) + + sqrt :: math.sqrt + dot :: lg.dot + cross :: lg.cross + length2 :: lg.length2 + min :: math.min + sign :: math.sign + clamp :: math.clamp + + return sqrt( + (sign(dot(cross(ba, nor), pa)) + sign(dot(cross(cb, nor), pb)) + sign(dot(cross(dc, nor), pc)) + sign(dot(cross(ad, nor), pd)) < 3.0) ? (min(min(min(length2(ba * clamp(dot(ba, pa) / length2(ba), 0.0, 1.0) - pa), length2(cb * clamp(dot(cb, pb) / length2(cb), 0.0, 1.0) - pb)), length2(dc * clamp(dot(dc, pc) / length2(dc), 0.0, 1.0) - pc)), length2(ad * clamp(dot(ad, pd) / length2(ad), 0.0, 1.0) - pd))) : (dot(nor, pa) * dot(nor, pa) / length2(nor)), + ) +} + +// point_to_segmented_line_distance :: proc( +// ps: #soa[]Interpolated_Point, +// p: rl.Vector3, +// ) -> ( +// min_distance: f32, +// segment_idx: int, +// ok: bool, // is point within spline +// ) { +// min_distance = math.F32_MAX +// segment_idx = -1 +// for i in 0 ..< len(ps) - 1 { +// cur, next := ps[i].pos, ps[i + 1].pos +// +// tangent, bitangent := get_point_frame(ps, i) +// next_tangent, next_bitangent := get_point_frame(ps, i + 1) +// normal, next_normal := ps[i].normal, ps[i + 1].normal +// +// tangent_len := lg.length(tangent) +// tangent_norm := tangent / tangent_len if tangent_len > 0 else 0 +// +// translated_p := p - cur +// +// // point_to_quad_sdf(p, cur - bitangent * -ROAD_WIDTH, cur) +// +// dot_v := lg.dot(tangent_norm, translated_p) +// // dot_u := lg.dot(bitangent) +// +// distance: f32 +// if dot_v < 0 { +// distance = lg.length(translated_p) +// ok = false +// } else if dot_v > tangent_len { +// distance = lg.distance(tangent, translated_p) +// ok = false +// } else { +// projected_p := tangent_norm * dot_v +// distance = lg.distance(projected_p, translated_p) +// ok = true +// } +// +// if distance < min_distance { +// segment_idx = i +// min_distance = distance +// } +// } +// +// return min_distance, segment_idx, ok +// } + +/// Find collision with the closest +raycast_spline_tube :: proc( + ps: #soa[]Interpolated_Point, + ray: rl.Ray, +) -> ( + collision: rl.RayCollision, + segment_idx: int, +) { + for i in 0 ..< len(ps) - 1 { + cur, next := ps[i].pos, ps[i + 1].pos + + _, bitangent := get_point_frame(ps, i) + _, next_bitangent := get_point_frame(ps, i + 1) + // normal, next_normal := ps[i].normal, ps[i + 1].normal + + p1 := cur + bitangent * -ROAD_WIDTH + p2 := cur + bitangent * ROAD_WIDTH + p3 := next + next_bitangent * -ROAD_WIDTH + p4 := next + next_bitangent * ROAD_WIDTH + + segment_idx = i + + collision = rl.GetRayCollisionTriangle(ray, p1, p2, p3) + if collision.hit { + break + } + collision = rl.GetRayCollisionTriangle(ray, p2, p4, p3) + if collision.hit { + break + } + } + + return +} + calculate_spline_interpolated_points :: proc( points: []rl.Vector3, allocator := context.allocator, -) -> []Interpolated_Point { +) -> #soa[]Interpolated_Point { points_len := len(points) ctrl_rotations := calculate_spline_ctrl_rotations(points, context.temp_allocator) if points_len >= 2 { - interpolated_points := make( - []Interpolated_Point, + interpolated_points := make_soa( + #soa[]Interpolated_Point, (points_len - 1) * SPLINE_SUBDIVS_V + 1, allocator, ) @@ -132,7 +257,7 @@ calculate_spline_interpolated_points :: proc( return nil } -debug_draw_spline :: proc(interpolated_points: []Interpolated_Point) { +debug_draw_spline :: proc(interpolated_points: #soa[]Interpolated_Point) { rlgl.Begin(rlgl.LINES) defer rlgl.End() for i in 0 ..< len(interpolated_points) - 1 { @@ -160,7 +285,7 @@ debug_draw_spline :: proc(interpolated_points: []Interpolated_Point) { } } -debug_draw_spline_mesh :: proc(interpolated_points: []Interpolated_Point) { +debug_draw_spline_mesh :: proc(interpolated_points: #soa[]Interpolated_Point) { rlgl.Begin(rlgl.TRIANGLES) defer rlgl.End()