Harbor

branch main
showing the latest snapshot on main
main.odin 4.9 KB · Plain text
examples/torches/main.odin 0644 Raw
package main

import "core:fmt"
import "core:math"
import gpu "../.."
import app "../../app"
import particles "../../particles"

MAX_TORCHES :: 4
MAX_COLUMNS :: 6
SHADOW_Y :: f32(0.006)

torch_positions := [MAX_TORCHES]gpu.Vec3{
	{-3, 5.0, -3},
	{ 3, 5.0, -3},
	{-3, 5.0,  3},
	{ 3, 5.0,  3},
}

column_positions := [MAX_COLUMNS]gpu.Vec3{
	{-3, 0, -3},
	{ 3, 0, -3},
	{-3, 0,  3},
	{ 3, 0,  3},
	{-1, 0,  0},
	{ 1, 0,  0},
}

frame_material :: proc(frame: ^gpu.Frame, color: gpu.Color, roughness: f32 = 0.7) -> gpu.Material_Handle {
	return gpu.add_mesh_material(frame, {
		color = color,
		roughness = roughness,
		reflectance = 0.35,
	})
}

clamp01 :: proc(v: f32) -> f32 {
	return max(0, min(1, v))
}

draw_column_shadow :: proc(frame: ^gpu.Frame, column: gpu.Vec3, flickers: [MAX_TORCHES]f32, sort_key: u64) {
	best_index := -1
	best_power: f32
	for light, i in torch_positions {
		dx := column.x - light.x
		dz := column.z - light.z
		dist := math.sqrt(dx * dx + dz * dz)
		if dist < 0.001 {
			continue
		}
		reach := clamp01(1 - dist / 8.0)
		power := reach * flickers[i]
		if power > best_power {
			best_power = power
			best_index = i
		}
	}
	if best_index < 0 {
		return
	}

	light := torch_positions[best_index]
	dx := column.x - light.x
	dz := column.z - light.z
	dist := math.sqrt(dx * dx + dz * dz)
	if dist < 0.001 {
		return
	}
	dir_x := dx / dist
	dir_z := dz / dist
	right_x := -dir_z
	right_z := dir_x
	strength := clamp01(best_power / 2.4)
	near_len: f32 = 0.18
	far_len := 1.0 + 2.4 * strength
	near_width := 0.34 + 0.22 * strength
	far_width := 0.52 + 0.42 * strength
	alpha := 0.08 + 0.18 * strength
	start_x := column.x + dir_x * near_len
	start_z := column.z + dir_z * near_len
	end_x := column.x + dir_x * far_len
	end_z := column.z + dir_z * far_len

	_ = gpu.draw_quad_3d_frame(frame, {
		corners = {
			{start_x - right_x * near_width, SHADOW_Y, start_z - right_z * near_width},
			{start_x + right_x * near_width, SHADOW_Y, start_z + right_z * near_width},
			{end_x   + right_x * far_width,  SHADOW_Y, end_z   + right_z * far_width},
			{end_x   - right_x * far_width,  SHADOW_Y, end_z   - right_z * far_width},
		},
		normal = {0, 1, 0},
		color = {0, 0, 0, alpha},
		sort_key = sort_key,
		order = .Sortable,
	})
}

main :: proc() {
	state, ok := app.init_window(1024, 768, "GPU - Torches & Point Lights")
	if !ok {
		return
	}
	defer app.shutdown(&state)

	camera := gpu.default_camera_3d()
	camera.position = {6, 5, 8}
	camera.target = {0, 1, 0}

	fire_config := particles.default_config_3d()
	fire_config.max_particles = 200
	fire_config.emission_rate = 60
	fire_config.direction = {0, 1, 0}
	fire_config.gravity = {0, 2.0, 0}
	fire_config.speed = {0.8, 2.0}
	fire_config.spread = 25
	fire_config.start_color = {1.0, 0.7, 0.1, 0.9}
	fire_config.end_color = {0.8, 0.1, 0.0, 0.0}
	fire_config.start_size = 0.35
	fire_config.end_size = 0.05
	fire_config.lifetime = {0.4, 1.0}

	fire_systems: [MAX_TORCHES]particles.System_3D
	for i in 0..<MAX_TORCHES {
		system, system_ok := particles.init_3d(fire_config, u32(2463534242 + i * 101))
		if !system_ok {
			return
		}
		fire_systems[i] = system
	}
	defer for i in 0..<MAX_TORCHES {
		particles.destroy_3d(&fire_systems[i])
	}

	time: f32 = 0
	flickers: [MAX_TORCHES]f32

	for !app.window_should_close(&state) {
		dt := app.get_frame_time(&state)
		time += dt
		app.update_camera(&state, &camera, .ORBITAL)

		for i in 0..<MAX_TORCHES {
			flickers[i] = 1.5 + 0.6 * math.sin(time * 8.0 + f32(i) * 1.7) + 0.25 * math.sin(time * 13.0 + f32(i) * 3.1)
			particles.update_3d(&fire_systems[i], torch_positions[i], dt)
		}

		frame := app.begin_frame(&state, {0.02, 0.02, 0.04, 1.0})
		gpu.set_camera_3d(&frame, camera)
		gpu.set_ambient_light_frame(&frame, {0.035, 0.028, 0.03, 1})

		for i in 0..<MAX_TORCHES {
			_ = gpu.add_light_frame(&frame, gpu.create_point_light(torch_positions[i], {1.0, 0.48, 0.12, 1}, flickers[i], 8.0))
		}

		ground_mat := frame_material(&frame, {0.42, 0.34, 0.28, 1.0}, 0.85)
		column_mat := frame_material(&frame, {0.48, 0.43, 0.40, 1.0}, 0.75)
		grid_mat := frame_material(&frame, {0.22, 0.20, 0.18, 1.0}, 0.9)

		_ = gpu.draw_plane_frame(&frame, {center = {0, -0.01, 0}, size = {20, 20}, material = ground_mat})
		for pos in column_positions {
			_ = gpu.draw_cube_frame(&frame, {center = {pos.x, 2.5, pos.z}, size = {0.4, 5.0, 0.4}, material = column_mat})
		}
		_ = gpu.draw_grid_frame(&frame, {slices = 20, spacing = 1, material = grid_mat})

		for pos, i in column_positions {
			draw_column_shadow(&frame, pos, flickers, 100 + u64(i))
		}

		for i in 0..<MAX_TORCHES {
			particles.draw_3d(&frame, &fire_systems[i])
		}

		fps_text := fmt.tprintf("FPS: %d", app.get_fps(&state))
		_ = gpu.draw_rect(&frame, {x = 5, y = 5, width = 80, height = 20, color = {0, 0, 0, 0.5}, sort_key = 10_000})
		gpu.draw_text_frame(&frame, {text = fps_text, x = 10, y = 9, color = gpu.WHITE, scale = 2, sort_key = 10_001})

		_ = app.submit_frame(&state, &frame)

		if app.is_key_pressed(&state, .Escape) {
			break
		}
	}
}