698 lines
17 KiB
Odin
698 lines
17 KiB
Odin
// This file is compiled as part of the `odin.dll` file. It contains the
|
|
// procs that `game_hot_reload.exe` will call, such as:
|
|
//
|
|
// game_init: Sets up the game state
|
|
// game_update: Run once per frame
|
|
// game_shutdown: Shuts down game and frees memory
|
|
// game_memory: Run just before a hot reload, so game.exe has a pointer to the
|
|
// game's memory.
|
|
// game_hot_reloaded: Run after a hot reload so that the `g_mem` global variable
|
|
// can be set to whatever pointer it was in the old DLL.
|
|
//
|
|
// Note: When compiled as part of the release executable this whole package is imported as a normal
|
|
// odin package instead of a DLL.
|
|
|
|
package game
|
|
|
|
import "assets"
|
|
import "core:math"
|
|
import "core:math/linalg"
|
|
import rl "vendor:raylib"
|
|
import "vendor:raylib/rlgl"
|
|
|
|
PIXEL_WINDOW_HEIGHT :: 360
|
|
|
|
Track :: struct {
|
|
points: [dynamic]rl.Vector3,
|
|
}
|
|
Game_Memory :: struct {
|
|
assetman: assets.Asset_Manager,
|
|
player_pos: rl.Vector3,
|
|
camera_yaw_pitch: rl.Vector2,
|
|
camera_speed: f32,
|
|
track: Track,
|
|
es: Editor_State,
|
|
editor: bool,
|
|
}
|
|
|
|
Track_Edit_State :: enum {
|
|
// Point selection
|
|
Select,
|
|
// Moving points
|
|
Move,
|
|
}
|
|
|
|
Move_Axis :: enum {
|
|
None,
|
|
X,
|
|
Y,
|
|
Z,
|
|
XZ,
|
|
XY,
|
|
YZ,
|
|
}
|
|
|
|
Editor_State :: struct {
|
|
mouse_captured: bool,
|
|
selected_track_point: int,
|
|
track_edit_state: Track_Edit_State,
|
|
move_axis: Move_Axis,
|
|
initial_point_pos: rl.Vector3,
|
|
}
|
|
|
|
g_mem: ^Game_Memory
|
|
|
|
game_camera :: proc() -> rl.Camera2D {
|
|
w := f32(rl.GetScreenWidth())
|
|
h := f32(rl.GetScreenHeight())
|
|
|
|
return {zoom = h / PIXEL_WINDOW_HEIGHT, target = g_mem.player_pos.xy, offset = {w / 2, h / 2}}
|
|
}
|
|
|
|
camera_rotation_matrix :: proc() -> matrix[3, 3]f32 {
|
|
return linalg.matrix3_from_euler_angles_xy(g_mem.camera_yaw_pitch.x, g_mem.camera_yaw_pitch.y)
|
|
}
|
|
|
|
camera_forward_vec :: proc() -> rl.Vector3 {
|
|
rotation_matrix := camera_rotation_matrix()
|
|
return rotation_matrix * rl.Vector3{0, 0, 1}
|
|
}
|
|
|
|
game_camera_3d :: proc() -> rl.Camera3D {
|
|
return {
|
|
position = g_mem.player_pos,
|
|
up = {0, 1, 0},
|
|
fovy = 60,
|
|
target = g_mem.player_pos + camera_forward_vec(),
|
|
projection = .PERSPECTIVE,
|
|
}
|
|
}
|
|
|
|
ui_camera :: proc() -> rl.Camera2D {
|
|
return {zoom = f32(rl.GetScreenHeight()) / PIXEL_WINDOW_HEIGHT}
|
|
}
|
|
|
|
update_free_look_camera :: proc() {
|
|
es := &g_mem.es
|
|
|
|
input: rl.Vector2
|
|
|
|
if rl.IsKeyDown(.UP) || rl.IsKeyDown(.W) {
|
|
input.y -= 1
|
|
}
|
|
if rl.IsKeyDown(.DOWN) || rl.IsKeyDown(.S) {
|
|
input.y += 1
|
|
}
|
|
if rl.IsKeyDown(.LEFT) || rl.IsKeyDown(.A) {
|
|
input.x -= 1
|
|
}
|
|
if rl.IsKeyDown(.RIGHT) || rl.IsKeyDown(.D) {
|
|
input.x += 1
|
|
}
|
|
|
|
should_capture_mouse := rl.IsMouseButtonDown(.RIGHT)
|
|
if es.mouse_captured != should_capture_mouse {
|
|
if should_capture_mouse {
|
|
rl.DisableCursor()
|
|
} else {
|
|
rl.EnableCursor()
|
|
}
|
|
}
|
|
es.mouse_captured = should_capture_mouse
|
|
|
|
if es.mouse_captured {
|
|
g_mem.camera_yaw_pitch += rl.GetMouseDelta().yx * -1 * 0.001
|
|
}
|
|
|
|
g_mem.camera_speed += rl.GetMouseWheelMove() * 0.01
|
|
g_mem.camera_speed = linalg.clamp(g_mem.camera_speed, 0.01, 10)
|
|
|
|
rotation_matrix := camera_rotation_matrix()
|
|
forward := -rotation_matrix[2]
|
|
right := linalg.cross(rl.Vector3{0, 1, 0}, forward)
|
|
|
|
input = linalg.normalize0(input)
|
|
g_mem.player_pos += (input.x * right + input.y * forward) * g_mem.camera_speed
|
|
}
|
|
|
|
add_track_spline_point :: proc() {
|
|
forward := camera_rotation_matrix()[2]
|
|
|
|
append(&g_mem.track.points, g_mem.player_pos + forward)
|
|
g_mem.es.selected_track_point = len(g_mem.track.points) - 1
|
|
}
|
|
|
|
get_movement_axes :: proc(
|
|
axis: Move_Axis,
|
|
out_axes: ^[2]rl.Vector3,
|
|
out_colors: ^[2]rl.Color,
|
|
) -> (
|
|
axes: []rl.Vector3,
|
|
colors: []rl.Color,
|
|
) {
|
|
switch axis {
|
|
case .None:
|
|
return out_axes[0:0], {}
|
|
case .X:
|
|
out_axes[0] = {1, 0, 0}
|
|
out_colors[0] = rl.RED
|
|
return out_axes[0:1], out_colors[0:1]
|
|
case .Y:
|
|
out_axes[0] = {0, 1, 0}
|
|
out_colors[0] = rl.GREEN
|
|
return out_axes[0:1], out_colors[0:1]
|
|
case .Z:
|
|
out_axes[0] = {0, 0, 1}
|
|
out_colors[0] = rl.BLUE
|
|
return out_axes[0:1], out_colors[0:1]
|
|
case .XZ:
|
|
out_axes[0] = {1, 0, 0}
|
|
out_axes[1] = {0, 0, 1}
|
|
out_colors[0] = rl.RED
|
|
out_colors[1] = rl.BLUE
|
|
return out_axes[:], out_colors[:]
|
|
case .XY:
|
|
out_axes[0] = {1, 0, 0}
|
|
out_axes[1] = {0, 1, 0}
|
|
out_colors[0] = rl.RED
|
|
out_colors[1] = rl.GREEN
|
|
return out_axes[:], out_colors[:]
|
|
case .YZ:
|
|
out_axes[0] = {0, 1, 0}
|
|
out_axes[1] = {0, 0, 1}
|
|
out_colors[0] = rl.GREEN
|
|
out_colors[1] = rl.BLUE
|
|
return out_axes[:], out_colors[:]
|
|
}
|
|
|
|
return out_axes[0:0], out_colors[0:0]
|
|
}
|
|
|
|
update_editor :: proc() {
|
|
es := &g_mem.es
|
|
|
|
switch es.track_edit_state {
|
|
case .Select:
|
|
{
|
|
if rl.IsKeyPressed(.F) {
|
|
add_track_spline_point()
|
|
}
|
|
|
|
if rl.IsKeyPressed(.G) && es.selected_track_point >= 0 {
|
|
es.track_edit_state = .Move
|
|
es.move_axis = .None
|
|
es.initial_point_pos = g_mem.track.points[es.selected_track_point]
|
|
}
|
|
}
|
|
case .Move:
|
|
{
|
|
if rl.IsKeyPressed(.ESCAPE) {
|
|
es.track_edit_state = .Select
|
|
g_mem.track.points[es.selected_track_point] = es.initial_point_pos
|
|
break
|
|
}
|
|
|
|
if (rl.IsMouseButtonPressed(.LEFT)) {
|
|
es.track_edit_state = .Select
|
|
break
|
|
}
|
|
|
|
if !es.mouse_captured {
|
|
// Blender style movement
|
|
if rl.IsKeyDown(.LEFT_SHIFT) {
|
|
if rl.IsKeyPressed(.X) {
|
|
es.move_axis = .YZ
|
|
}
|
|
if rl.IsKeyPressed(.Y) {
|
|
es.move_axis = .XZ
|
|
}
|
|
if rl.IsKeyPressed(.Z) {
|
|
es.move_axis = .XY
|
|
}
|
|
} else {
|
|
if rl.IsKeyPressed(.X) {
|
|
es.move_axis = .X
|
|
}
|
|
if rl.IsKeyPressed(.Y) {
|
|
es.move_axis = .Y
|
|
}
|
|
if rl.IsKeyPressed(.Z) {
|
|
es.move_axis = .Z
|
|
}
|
|
}
|
|
|
|
// log.debugf("Move axis %v", es.move_axis)
|
|
|
|
camera := game_camera_3d()
|
|
|
|
mouse_delta := rl.GetMouseDelta() * 0.05
|
|
|
|
view_rotation := linalg.transpose(rl.GetCameraMatrix(camera))
|
|
view_rotation[3].xyz = 0
|
|
view_proj := view_rotation * rl.MatrixOrtho(-1, 1, 1, -1, -1, 1)
|
|
|
|
axes_buf: [2]rl.Vector3
|
|
colors_buf: [2]rl.Color
|
|
axes, _ := get_movement_axes(es.move_axis, &axes_buf, &colors_buf)
|
|
|
|
movement_world: rl.Vector3
|
|
for axis in axes {
|
|
axis_screen := (rl.Vector4{axis.x, axis.y, axis.z, 1} * view_proj).xy
|
|
axis_screen = linalg.normalize0(axis_screen)
|
|
|
|
movement_screen := linalg.dot(axis_screen, mouse_delta) * axis_screen
|
|
movement_world +=
|
|
(rl.Vector4{movement_screen.x, movement_screen.y, 0, 1} * rl.MatrixInvert(view_proj)).xyz
|
|
}
|
|
|
|
g_mem.track.points[es.selected_track_point] += movement_world
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
update :: proc() {
|
|
if rl.IsKeyPressed(.TAB) {
|
|
g_mem.editor = !g_mem.editor
|
|
|
|
if g_mem.editor {
|
|
rl.EnableCursor()
|
|
} else {
|
|
rl.DisableCursor()
|
|
}
|
|
}
|
|
|
|
if g_mem.editor {
|
|
update_free_look_camera()
|
|
|
|
update_editor()
|
|
}
|
|
}
|
|
|
|
catmull_rom_coefs :: proc(
|
|
v0, v1, v2, v3: rl.Vector3,
|
|
alpha, tension: f32,
|
|
) -> (
|
|
a, b, c, d: rl.Vector3,
|
|
) {
|
|
t01 := math.pow(linalg.distance(v0, v1), alpha)
|
|
t12 := math.pow(linalg.distance(v1, v2), alpha)
|
|
t23 := math.pow(linalg.distance(v2, v3), alpha)
|
|
|
|
m1 := (1.0 - tension) * (v2 - v1 + t12 * ((v1 - v0) / t01 - (v2 - v0) / (t01 + t12)))
|
|
m2 := (1.0 - tension) * (v2 - v1 + t12 * ((v3 - v2) / t23 - (v3 - v1) / (t12 + t23)))
|
|
|
|
a = 2.0 * (v1 - v2) + m1 + m2
|
|
b = -3.0 * (v1 - v2) - m1 - m1 - m2
|
|
c = m1
|
|
d = v1
|
|
|
|
return
|
|
}
|
|
|
|
catmull_rom :: proc(a, b, c, d: rl.Vector3, t: f32) -> rl.Vector3 {
|
|
t2 := t * t
|
|
t3 := t2 * t
|
|
|
|
return a * t3 + b * t2 + c * t + d
|
|
}
|
|
|
|
draw :: proc() {
|
|
rl.BeginDrawing()
|
|
defer rl.EndDrawing()
|
|
rl.ClearBackground(rl.BLACK)
|
|
|
|
camera := game_camera_3d()
|
|
|
|
{
|
|
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,
|
|
)
|
|
|
|
points := &g_mem.track.points
|
|
points_len := len(points)
|
|
|
|
// road: rl.Mesh
|
|
// defer rl.UnloadMesh(road)
|
|
// road_vertices: [dynamic]f32
|
|
// road_normals: [dynamic]f32
|
|
// road_uvs: [dynamic]f32
|
|
// road_indices: [dynamic]u16
|
|
// road_vertices.allocator = context.temp_allocator
|
|
// road_normals.allocator = context.temp_allocator
|
|
// road_uvs.allocator = context.temp_allocator
|
|
// road_indices.allocator = context.temp_allocator
|
|
|
|
// spline_segment_count := max(len(g_mem.track.points) - 4, 0)
|
|
|
|
|
|
SPLINE_SUBDIVS_U :: 4
|
|
SPLINE_SUBDIVS_V :: 16
|
|
ROAD_WIDTH :: 2.0
|
|
CATMULL_ROM_ALPHA :: 1.0
|
|
CATMULL_ROM_TENSION :: 0.0
|
|
|
|
{
|
|
// Debug draw spline road
|
|
{
|
|
rlgl.EnableWireMode()
|
|
defer rlgl.DisableWireMode()
|
|
|
|
rlgl.Color3f(1, 0, 0)
|
|
|
|
|
|
Frame :: struct {
|
|
tangent, normal: rl.Vector3,
|
|
rotation: linalg.Quaternionf32,
|
|
}
|
|
|
|
ctrl_frames := make_soa(#soa[]Frame, points_len, context.temp_allocator)
|
|
|
|
// Normals for control points
|
|
for i in 0 ..< points_len - 1 {
|
|
pos := points[i]
|
|
tangent := linalg.normalize0(points[i + 1] - pos)
|
|
bitangent := linalg.normalize0(linalg.cross(tangent, rl.Vector3{0, 1, 0}))
|
|
normal := linalg.normalize0(linalg.cross(bitangent, tangent))
|
|
rotation_matrix: linalg.Matrix3f32
|
|
rotation_matrix[0], rotation_matrix[1], rotation_matrix[2] =
|
|
bitangent, normal, -tangent
|
|
ctrl_frames[i].tangent = tangent
|
|
ctrl_frames[i].normal = normal
|
|
ctrl_frames[i].rotation = linalg.quaternion_from_matrix3(rotation_matrix)
|
|
}
|
|
ctrl_frames[points_len - 1] = ctrl_frames[points_len - 2]
|
|
|
|
interpolated_points := make(
|
|
[]rl.Vector3,
|
|
(points_len - 1) * SPLINE_SUBDIVS_V + 1,
|
|
context.temp_allocator,
|
|
)
|
|
|
|
if points_len >= 2 {
|
|
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]
|
|
|
|
for i in 0 ..< points_len - 1 {
|
|
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,
|
|
)
|
|
|
|
v_dt := 1.0 / f32(SPLINE_SUBDIVS_V)
|
|
for v_index in 0 ..< SPLINE_SUBDIVS_V {
|
|
v_t := f32(v_index) * v_dt
|
|
|
|
interpolated_pos := catmull_rom(a, b, c, d, v_t)
|
|
interpolated_points[i * SPLINE_SUBDIVS_V + v_index] = interpolated_pos
|
|
}
|
|
}
|
|
|
|
interpolated_points[len(interpolated_points) - 1] = points[points_len - 1]
|
|
|
|
frames := make_soa(
|
|
#soa[]Frame,
|
|
len(interpolated_points),
|
|
context.temp_allocator,
|
|
)
|
|
|
|
{
|
|
for i in 0 ..< len(frames) - 1 {
|
|
ctrl_index := i / SPLINE_SUBDIVS_V
|
|
v_t := f32(i % SPLINE_SUBDIVS_V) / (SPLINE_SUBDIVS_V)
|
|
|
|
pos := interpolated_points[i]
|
|
cur_frame := ctrl_frames[ctrl_index]
|
|
next_frame := ctrl_frames[ctrl_index + 1]
|
|
|
|
frames[i].rotation = linalg.quaternion_slerp(
|
|
cur_frame.rotation,
|
|
next_frame.rotation,
|
|
v_t,
|
|
)
|
|
normal := linalg.matrix3_from_quaternion(frames[i].rotation)[1]
|
|
tangent := linalg.normalize0(interpolated_points[i + 1] - pos)
|
|
|
|
frames[i].tangent = tangent
|
|
frames[i].normal = normal
|
|
}
|
|
|
|
frames[len(frames) - 1] = frames[len(frames) - 2]
|
|
}
|
|
|
|
// Parallel frame algorithm (doesn't work because it rotates the road too much, but looks pretty)
|
|
if false {
|
|
// Calculate reference normal
|
|
{
|
|
tangent := linalg.normalize0(
|
|
interpolated_points[1] - interpolated_points[0],
|
|
)
|
|
frames[0].tangent = tangent
|
|
bitangent := linalg.normalize0(
|
|
linalg.cross(tangent, rl.Vector3{0, 1, 0}),
|
|
)
|
|
frames[0].normal = linalg.normalize0(linalg.cross(bitangent, tangent))
|
|
}
|
|
|
|
for i in 1 ..< len(frames) {
|
|
cur, next := interpolated_points[i], interpolated_points[i + 1]
|
|
|
|
tangent := linalg.normalize0(next - cur)
|
|
bitangent := linalg.cross(frames[i - 1].tangent, tangent)
|
|
bitangent_len := linalg.length(bitangent)
|
|
normal: rl.Vector3
|
|
if bitangent_len < math.F32_EPSILON {
|
|
normal = frames[i - 1].normal
|
|
} else {
|
|
bitangent /= bitangent_len
|
|
angle := math.acos(linalg.dot(frames[i - 1].tangent, tangent))
|
|
normal =
|
|
linalg.matrix3_rotate(angle, bitangent) * frames[i - 1].normal
|
|
}
|
|
|
|
frames[i].tangent = tangent
|
|
frames[i].normal = normal
|
|
}
|
|
}
|
|
|
|
{
|
|
rlgl.Begin(rlgl.LINES)
|
|
defer rlgl.End()
|
|
|
|
for i in 0 ..< len(interpolated_points) - 1 {
|
|
cur, next := interpolated_points[i], interpolated_points[i + 1]
|
|
|
|
// rotation := linalg.matrix3_look_at(cur, next, rl.Vector3{0, 1, 0})
|
|
tangent := frames[i].tangent
|
|
normal := frames[i].normal
|
|
bitangent := linalg.cross(tangent, normal)
|
|
|
|
rlgl.Color4ub(255, 255, 255, 255)
|
|
rlgl.Vertex3f(cur.x, cur.y, cur.z)
|
|
rlgl.Vertex3f(next.x, next.y, next.z)
|
|
|
|
rlgl.Color4ub(255, 0, 0, 255)
|
|
rlgl.Vertex3f(cur.x, cur.y, cur.z)
|
|
rlgl.Vertex3f(cur.x + tangent.x, cur.y + tangent.y, cur.z + tangent.z)
|
|
|
|
rlgl.Color4ub(0, 255, 0, 255)
|
|
rlgl.Vertex3f(cur.x, cur.y, cur.z)
|
|
rlgl.Vertex3f(
|
|
cur.x + bitangent.x,
|
|
cur.y + bitangent.y,
|
|
cur.z + bitangent.z,
|
|
)
|
|
|
|
rlgl.Color4ub(0, 0, 255, 255)
|
|
rlgl.Vertex3f(cur.x, cur.y, cur.z)
|
|
rlgl.Vertex3f(cur.x + normal.x, cur.y + normal.y, cur.z + normal.z)
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if g_mem.editor {
|
|
es := &g_mem.es
|
|
|
|
switch es.track_edit_state {
|
|
case .Select:
|
|
case .Move:
|
|
rlgl.Begin(rlgl.LINES)
|
|
defer rlgl.End()
|
|
axes_buf: [2]rl.Vector3
|
|
colors_buf: [2]rl.Color
|
|
axes, colors := get_movement_axes(es.move_axis, &axes_buf, &colors_buf)
|
|
|
|
|
|
for v in soa_zip(axis = axes, color = colors) {
|
|
rlgl.Color4ub(v.color.r, v.color.g, v.color.b, v.color.a)
|
|
start, end :=
|
|
es.initial_point_pos -
|
|
v.axis * 100000,
|
|
es.initial_point_pos +
|
|
v.axis * 100000
|
|
|
|
rlgl.Vertex3f(start.x, start.y, start.z)
|
|
rlgl.Vertex3f(end.x, end.y, end.z)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
rl.BeginMode2D(ui_camera())
|
|
defer rl.EndMode2D()
|
|
|
|
if g_mem.editor {
|
|
rl.DrawText("Editor", 5, 5, 8, rl.ORANGE)
|
|
}
|
|
}
|
|
|
|
if g_mem.editor {
|
|
es := &g_mem.es
|
|
|
|
points := &g_mem.track.points
|
|
points_len := len(points)
|
|
|
|
for i in 0 ..< points_len {
|
|
if spline_handle(g_mem.track.points[i], camera, es.selected_track_point == i) {
|
|
g_mem.es.selected_track_point = i
|
|
}
|
|
}
|
|
}
|
|
|
|
// axis lines
|
|
if g_mem.editor {
|
|
size := f32(100)
|
|
pos := rl.Vector2{20, f32(rl.GetScreenHeight()) - 20 - size}
|
|
view_rotation := linalg.transpose(rl.GetCameraMatrix(camera))
|
|
view_rotation[3].xyz = 0
|
|
view_proj := view_rotation * rl.MatrixOrtho(-1, 1, 1, -1, -1, 1)
|
|
|
|
center := (rl.Vector4{0, 0, 0, 1} * view_proj).xy * 0.5 + 0.5
|
|
x_axis := (rl.Vector4{1, 0, 0, 1} * view_proj).xy * 0.5 + 0.5
|
|
y_axis := (rl.Vector4{0, 1, 0, 1} * view_proj).xy * 0.5 + 0.5
|
|
z_axis := (rl.Vector4{0, 0, 1, 1} * view_proj).xy * 0.5 + 0.5
|
|
|
|
old_width := rlgl.GetLineWidth()
|
|
rlgl.SetLineWidth(4)
|
|
defer rlgl.SetLineWidth(old_width)
|
|
rl.DrawLineV(pos + center * size, pos + x_axis * size, rl.RED)
|
|
rl.DrawLineV(pos + center * size, pos + y_axis * size, rl.GREEN)
|
|
rl.DrawLineV(pos + center * size, pos + z_axis * size, rl.BLUE)
|
|
}
|
|
|
|
}
|
|
|
|
spline_handle :: proc(
|
|
world_pos: rl.Vector3,
|
|
camera: rl.Camera,
|
|
selected: bool,
|
|
) -> (
|
|
clicked: bool,
|
|
) {
|
|
if linalg.dot(camera.target - camera.position, world_pos - camera.position) < 0 {
|
|
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
|
|
|
|
rl.DrawRectangleV(pos, size, selected ? rl.BLUE : (is_hover ? rl.ORANGE : rl.WHITE))
|
|
|
|
return rl.IsMouseButtonPressed(.LEFT) && is_hover
|
|
}
|
|
|
|
@(export)
|
|
game_update :: proc() -> bool {
|
|
update()
|
|
draw()
|
|
return !rl.WindowShouldClose()
|
|
}
|
|
|
|
@(export)
|
|
game_init_window :: proc() {
|
|
rl.SetConfigFlags({.WINDOW_RESIZABLE, .VSYNC_HINT})
|
|
rl.InitWindow(1280, 720, "Odin + Raylib + Hot Reload template!")
|
|
rl.SetExitKey(.KEY_NULL)
|
|
rl.SetWindowPosition(200, 200)
|
|
rl.SetTargetFPS(500)
|
|
}
|
|
|
|
@(export)
|
|
game_init :: proc() {
|
|
g_mem = new(Game_Memory)
|
|
|
|
g_mem^ = Game_Memory{}
|
|
|
|
g_mem.es.selected_track_point = -1
|
|
|
|
game_hot_reloaded(g_mem)
|
|
}
|
|
|
|
@(export)
|
|
game_shutdown :: proc() {
|
|
assets.shutdown(&g_mem.assetman)
|
|
delete(g_mem.track.points)
|
|
free(g_mem)
|
|
}
|
|
|
|
@(export)
|
|
game_shutdown_window :: proc() {
|
|
rl.CloseWindow()
|
|
}
|
|
|
|
@(export)
|
|
game_memory :: proc() -> rawptr {
|
|
return g_mem
|
|
}
|
|
|
|
@(export)
|
|
game_memory_size :: proc() -> int {
|
|
return size_of(Game_Memory)
|
|
}
|
|
|
|
@(export)
|
|
game_hot_reloaded :: proc(mem: rawptr) {
|
|
g_mem = (^Game_Memory)(mem)
|
|
}
|
|
|
|
@(export)
|
|
game_force_reload :: proc() -> bool {
|
|
return rl.IsKeyPressed(.F5)
|
|
}
|
|
|
|
@(export)
|
|
game_force_restart :: proc() -> bool {
|
|
return rl.IsKeyPressed(.F6)
|
|
}
|