Harbor

branch main
showing the latest snapshot on main
app.odin 8.2 KB · Plain text
gpu/app/app.odin 0644 Raw
package app

import "core:math"
import "core:time"
import gpu ".."
import "window:window"
import platform "../platform"

State :: struct {
	platform: platform.Platform_State,
	bench:    Bench_State,
	defaults: Default_Pipelines_State,
}

Bench_State :: struct {
	max_seconds: f64,
	max_frames:  u64,
	profile_path: string,
	start_time: time.Time,
	screenshot_hold_seconds: f64,
	screenshot_ready_path: string,
}

Key :: window.Key_Code
Mouse_Button :: window.Mouse_Button
Input_Source :: platform.Input_Source
Action :: distinct u32
INVALID_ACTION :: Action(0)

init_window :: proc(width, height: i32, title: cstring) -> (state: State, ok: bool) {
	plat, plat_ok := platform.init_platform(width, height, string(title))
	if !plat_ok {
		return {}, false
	}

	surface := platform.surface_desc(&plat)
	w := u32(width) if width > 0 else gpu.DEFAULT_WINDOW_WIDTH
	h := u32(height) if height > 0 else gpu.DEFAULT_WINDOW_HEIGHT
	if !gpu.init_surface(surface, w, h, title) {
		platform.shutdown_platform(&plat)
		return {}, false
	}

	window.set_gpu_surface(plat.win, true)
	state.platform = plat
	bench_init(&state)
	if !default_pipelines_init(&state) {
		gpu.shutdown()
		platform.shutdown_platform(&state.platform)
		return {}, false
	}
	return state, true
}

shutdown :: proc(state: ^State) {
	bench_shutdown(state)
	default_pipelines_shutdown(state)
	gpu.shutdown()
	platform.shutdown_platform(&state.platform)
}

window_should_close :: proc(state: ^State) -> bool {
	return state == nil || state.platform.should_close || bench_should_close(state)
}

begin_drawing :: proc(state: ^State, clear_color: gpu.Color = gpu.BLACK) {
	platform.poll_events(&state.platform)
	platform.update_time(&state.platform)
	if state.platform.was_resized {
		size := state.platform.new_size
		gpu.resize_surface(size.width, size.height)
		state.platform.was_resized = false
	}
	gpu.begin_drawing(clear_color, platform.get_frame_time(&state.platform))
}

begin_frame :: proc(state: ^State, clear_color: gpu.Color = gpu.BLACK, begin_present: bool = true) -> gpu.Frame {
	platform.poll_events(&state.platform)
	platform.update_time(&state.platform)
	if state.platform.was_resized {
		size := state.platform.new_size
		gpu.resize_surface(size.width, size.height)
		state.platform.was_resized = false
	}

	ctx := gpu.context_from_runtime()
	frame := gpu.begin_frame(ctx)
	if begin_present {
		target_desc := gpu.present_target(ctx)
		target_desc.clear_color = clear_color
		_ = gpu.begin_target(&frame, target_desc)
		_ = gpu.push_layer(&frame, {name = "default", order = .Strict})
	}
	return frame
}

end_drawing :: proc(state: ^State) {
	_ = gpu.end_drawing()
	platform.wait_for_target_fps(&state.platform)
	free_all(context.temp_allocator)
}

submit_frame :: proc(state: ^State, frame: ^gpu.Frame) -> bool {
	ok := gpu.submit(frame)
	gpu.destroy_frame(frame)
	platform.wait_for_target_fps(&state.platform)
	free_all(context.temp_allocator)
	return ok
}

set_target_fps :: proc(state: ^State, fps: i32) {
	platform.set_target_fps(&state.platform, fps)
}

get_frame_time :: proc(state: ^State) -> f32 {
	return platform.get_frame_time(&state.platform)
}

get_time :: proc(state: ^State) -> f64 {
	return platform.get_time(&state.platform)
}

get_fps :: proc(state: ^State) -> i32 {
	return platform.get_fps(&state.platform)
}

is_key_pressed :: proc(state: ^State, key: Key) -> bool {
	return platform.is_key_pressed(&state.platform, key)
}

is_key_down :: proc(state: ^State, key: Key) -> bool {
	return platform.is_key_down(&state.platform, key)
}

is_key_released :: proc(state: ^State, key: Key) -> bool {
	return platform.is_key_released(&state.platform, key)
}

is_mouse_button_pressed :: proc(state: ^State, btn: Mouse_Button) -> bool {
	return platform.is_mouse_button_pressed(&state.platform, btn)
}

is_mouse_button_down :: proc(state: ^State, btn: Mouse_Button) -> bool {
	return platform.is_mouse_button_down(&state.platform, btn)
}

get_mouse_position :: proc(state: ^State) -> gpu.Vec2 {
	pos := platform.get_mouse_position(&state.platform)
	return {pos[0], pos[1]}
}

get_mouse_delta :: proc(state: ^State) -> gpu.Vec2 {
	delta := platform.get_mouse_delta(&state.platform)
	return {delta[0], delta[1]}
}

get_mouse_wheel_move :: proc(state: ^State) -> f32 {
	return platform.get_mouse_wheel_move(&state.platform)
}

register_action :: proc(state: ^State, name: string) -> Action {
	id := platform.register_action(&state.platform.actions, name)
	if id == 0 {
		return INVALID_ACTION
	}
	return Action(id)
}

bind_action_key :: proc(state: ^State, action: Action, key: Key) {
	platform.bind_action_key(&state.platform.actions, u32(action), key)
}

bind_action_mouse :: proc(state: ^State, action: Action, btn: Mouse_Button) {
	platform.bind_action_mouse(&state.platform.actions, u32(action), btn)
}

unbind_action :: proc(state: ^State, action: Action) {
	platform.unbind_action(&state.platform.actions, u32(action))
}

is_action_pressed :: proc(state: ^State, action: Action) -> bool {
	return platform.is_action_pressed(&state.platform, u32(action))
}

is_action_down :: proc(state: ^State, action: Action) -> bool {
	return platform.is_action_down(&state.platform, u32(action))
}

is_action_released :: proc(state: ^State, action: Action) -> bool {
	return platform.is_action_released(&state.platform, u32(action))
}

get_action_name :: proc(state: ^State, action: Action) -> string {
	return platform.get_action_name(&state.platform.actions, u32(action))
}

get_action_bindings :: proc(state: ^State, action: Action) -> []Input_Source {
	return platform.get_action_bindings(&state.platform.actions, u32(action))
}

vec3_length :: proc(v: gpu.Vec3) -> f32 {
	return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
}

vec3_normalize :: proc(v: gpu.Vec3) -> gpu.Vec3 {
	len := vec3_length(v)
	if len < 1e-8 {
		return {}
	}
	return v / len
}

vec3_cross :: proc(a, b: gpu.Vec3) -> gpu.Vec3 {
	return {
		a.y * b.z - a.z * b.y,
		a.z * b.x - a.x * b.z,
		a.x * b.y - a.y * b.x,
	}
}

update_camera :: proc(state: ^State, camera: ^gpu.Camera3D, mode: gpu.Camera_Mode = .FREE) {
	if state == nil || camera == nil {
		return
	}

	dt := get_frame_time(state)
	mouse_delta := get_mouse_delta(state)

	#partial switch mode {
	case .FREE:
		sensitivity: f32 = 0.003
		yaw := -mouse_delta.x * sensitivity
		pitch := -mouse_delta.y * sensitivity

		forward := vec3_normalize(camera.target - camera.position)
		right := vec3_normalize(vec3_cross(forward, camera.up))

		cos_y := math.cos(yaw)
		sin_y := math.sin(yaw)
		forward = {
			forward.x * cos_y + forward.z * sin_y,
			forward.y,
			-forward.x * sin_y + forward.z * cos_y,
		}

		cos_p := math.cos(pitch)
		sin_p := math.sin(pitch)
		new_forward := vec3_normalize(gpu.Vec3{
			forward.x * cos_p,
			forward.y * cos_p + sin_p,
			forward.z * cos_p,
		})
		if math.abs(new_forward.y) < 0.99 {
			forward = new_forward
		}

		camera.target = camera.position + forward

		speed: f32 = 5.0 * dt
		if is_key_down(state, .Shift_Left) {
			speed *= 2.5
		}

		right = vec3_normalize(vec3_cross(forward, camera.up))

		if is_key_down(state, .W) {
			camera.position += forward * speed
			camera.target += forward * speed
		}
		if is_key_down(state, .S) {
			camera.position -= forward * speed
			camera.target -= forward * speed
		}
		if is_key_down(state, .D) {
			camera.position += right * speed
			camera.target += right * speed
		}
		if is_key_down(state, .A) {
			camera.position -= right * speed
			camera.target -= right * speed
		}
		if is_key_down(state, .E) || is_key_down(state, .Space) {
			camera.position.y += speed
			camera.target.y += speed
		}
		if is_key_down(state, .Q) || is_key_down(state, .Ctrl_Left) {
			camera.position.y -= speed
			camera.target.y -= speed
		}
	case .ORBITAL:
		sensitivity: f32 = 0.005
		dist := vec3_length(camera.position - camera.target)

		offset := camera.position - camera.target
		theta := math.atan2(offset.x, offset.z)
		phi := math.acos(clamp(offset.y / dist, -1, 1))

		if is_mouse_button_down(state, .Right) {
			theta -= mouse_delta.x * sensitivity
			phi -= mouse_delta.y * sensitivity
			phi = clamp(phi, 0.05, math.PI - 0.05)
		}

		scroll := get_mouse_wheel_move(state)
		dist -= scroll * dist * 0.1
		dist = max(dist, 0.1)

		camera.position = camera.target + gpu.Vec3{
			dist * math.sin(phi) * math.sin(theta),
			dist * math.cos(phi),
			dist * math.sin(phi) * math.cos(theta),
		}
	}
}