Move Odin renderer into gui/ with per-project Makefiles
CI / Detect changed paths (pull_request) Failing after 1s
CI / Infra unit tests and vet (pull_request) Failing after 1s
CI / Odin unit tests and build (pull_request) Has been skipped
CI / API unit tests and lint (pull_request) Has been skipped

Each sub-project (gui, api, infra) now owns its own Makefile, and the
root Makefile is a lean delegator for the base commands (run, build,
test, clean, fmt). Odin source, deps, and web assets live under gui/
(gui/src, gui/lib, gui/www), with scripts kept under gui/scripts for
build tooling only.
This commit is contained in:
2026-08-31 13:45:58 -06:00
parent 175bb4acad
commit bd8a7da379
16 changed files with 223 additions and 122 deletions
+113
View File
@@ -0,0 +1,113 @@
package main
import "core:math"
import "core:math/rand"
import rl "vendor:raylib"
WIDTH :: 1280
HEIGHT :: 720
// Number of procedurally generated points standing in for the DESI catalog.
// This is replaced by real survey data once ingestion lands.
POINT_COUNT :: 4_000
WORLD_RADIUS :: f32(500.0)
Galaxy :: struct {
position: rl.Vector3,
color: rl.Color,
}
camera: rl.Camera3D
universe: [dynamic]Galaxy
// Deterministic generator so the placeholder sky is stable between runs.
rng: rand.Default_Random_State
main :: proc() {
rng = rand.create(0xDE51_0000)
context.random_generator = rand.default_random_generator(&rng)
rl.SetConfigFlags({.WINDOW_RESIZABLE, .VSYNC_HINT, .MSAA_4X_HINT})
rl.InitWindow(WIDTH, HEIGHT, "DESI Explorer")
defer rl.CloseWindow()
rl.SetTargetFPS(60)
camera = make_camera()
make_universe(&universe, POINT_COUNT)
defer delete(universe)
for !rl.WindowShouldClose() {
update()
draw()
}
}
make_camera :: proc() -> rl.Camera3D {
return {
position = {0, 220, 220},
target = {0, 0, 0},
up = {0, 1, 0},
fovy = 60,
projection = .PERSPECTIVE,
}
}
make_universe :: proc(u: ^[dynamic]Galaxy, count: int) {
clear(u)
reserve(u, count)
for _ in 0 ..< count {
pos := random_sphere_point(WORLD_RADIUS)
append(u, Galaxy{position = pos, color = color_for_position(pos)})
}
}
// Uniformly distributed random point inside the scene's bounding sphere.
random_sphere_point :: proc(radius: f32) -> rl.Vector3 {
for {
x := rand.float32_range(-1, 1)
y := rand.float32_range(-1, 1)
z := rand.float32_range(-1, 1)
len_sq := x * x + y * y + z * z
if len_sq > 0.0001 && len_sq < 1.0 {
return {x * radius, y * radius, z * radius}
}
}
}
// Maps distance from the origin to a blue (near) -> red (far) gradient, a
// stand-in for redshift until real colors are computed from survey metadata.
color_for_position :: proc(p: rl.Vector3) -> rl.Color {
d := math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z)
t := math.clamp(d / WORLD_RADIUS, 0, 1)
return rl.Color{u8(40 + 180 * t), 120, u8(255 - 180 * t), 255}
}
update :: proc() {
// Orbital camera: drag to rotate, scroll to zoom, right-drag / shift to pan.
rl.UpdateCamera(&camera, .ORBITAL)
}
draw :: proc() {
rl.BeginDrawing()
defer rl.EndDrawing()
rl.ClearBackground({5, 5, 15, 255})
rl.BeginMode3D(camera)
defer rl.EndMode3D()
rl.DrawGrid(20, 50.0)
for galaxy in universe {
rl.DrawPoint3D(galaxy.position, galaxy.color)
}
rl.DrawFPS(10, 10)
rl.DrawText(
"DESI Explorer — drag to rotate, scroll to zoom",
10,
34,
18,
rl.RAYWHITE,
)
}