This workspace is a first vertical slice for an Esri-like geospatial platform in Odin.
- A shared Odin core for coordinates, layers, features, and routes.
- A native Odin entry point that prints the demo scene summary.
- A native Vulkan client rendering the globe with streamed imagery, feature points with click selection, billboarded name labels, and animated great-circle route arcs.
- A browser/WebGL client that renders a 3D globe with imagery, feature points, and route overlays.
main.odin- native entry point.geo_app/- native app loop, input wiring, scene bootstrap.geo_core/- camera, mesh generation, math/types.geo_layers/- scene model and imagery tile addressing.geo_ingest/- edge cache and bundle ingest helpers.geo_sync/- fetch queue and network worker helpers.geo_cvulkan/- Vulkan device/swapchain/pipeline/frame/texture code.geo_render/- shared render-facing structs (push constants, commands).geo_style/,geo_catalog/,geo_webgl/- style/catalog/web backend stubs.web/- browser MVP.
Native Odin demo:
odin run .Native compile check:
odin check .Web demo:
Open web/index.html in a browser.
- Left mouse drag: orbit (rotate around the globe).
- Left click (no drag): select the feature under the cursor; clicking empty space clears the selection. The picked feature pulses and its details print to the console.
- Right mouse drag: tilt (change elevation angle only).
- Mouse wheel: zoom in/out.
1-9: toggle feature layer visibility (demo scene:1Ops Sites,2Sensor Net). Hiding a layer also hides its labels and clears selection.R: toggle route arcs.Esc: close window.
- Orbit drag sensitivity is intentionally reduced to avoid over-rotation.
- Zoom speed scales with distance so far-away zoom is faster and close-in zoom is precise.
Midgaard uses edge-first imagery lookup:
- Cache (
./.cache/imagery/base/...) - Offline bundle (
./edge_bundles/imagery/base/...) - Remote fetch (ArcGIS export/tile endpoints)
At startup, the app warms a small seed tile set and may fetch missing tiles. During runtime it:
- Prefetches around camera focus.
- Deduplicates fetch queue entries.
- Throttles fetch work to reduce interaction lag.
- Swaps world imagery LOD conservatively (cooldowns + capped max texture size).
- Depth testing is enabled in Vulkan so back-side features do not render through the globe.
- Streaming work is deferred during active zoom/drag interaction to prioritize smooth camera motion.
- If interaction still feels heavy on your machine, reduce source imagery resolution or increase streaming cadence intervals in
geo_app/app.odin.
- Runtime cache:
./.cache/imagery/base/ - Optional offline bundle root:
./edge_bundles/imagery/base/
Safe cleanup for cached imagery:
rm -rf .cache/imagery/baselibvulkan.so.1 not found: install Vulkan loader packages for your distro.- Black/fallback globe texture: verify network access to ArcGIS imagery export endpoint and check cache write permissions.
- Slow first run: expected while warming cache and downloading first imagery LODs.
This repository is organized as small Odin packages with clear boundaries. Use this section as the default guide when adding features.
geo_app: app composition layer. Owns lifecycle (app_run, init/destroy), input callbacks, and frame-to-frame orchestration.geo_core: foundational math and camera behavior. Keep it independent from rendering backends.geo_layers: domain scene model (layers/features/routes) and tile coordinate logic. No GPU code here.geo_ingest: prepares local imagery data (cache roots, bundle binding) before runtime rendering.geo_sync: async-like fetch orchestration primitives (queue/tasks/batch worker execution).geo_cvulkan: native GPU backend; Vulkan-only concerns live here.geo_render: backend-agnostic render structs shared by backends.geo_style,geo_catalog,geo_webgl: extension points for styling, catalog metadata, and WebGL bridge.
Keep dependencies flowing inward to avoid circular coupling:
geo_app-> may import all feature/backend packages.geo_cvulkan-> may importgeo_core,geo_layers,geo_render.geo_layers-> may importgeo_core.geo_coreandgeo_render-> should remain near-leaf/foundation packages.
Rule of thumb: if a package starts importing "up" into app orchestration, move that code to geo_app.
Add camera/input behavior:
- Implement math/state behavior in
geo_core. - Bind input event handling in
geo_app/window.odin. - Trigger app-level cooldown/throttling in
geo_app/app.odinif interaction affects frame pacing.
Add new scene/layer data:
- Extend structs and helpers in
geo_layers. - Seed demo/test data in
geo_app/demo.odin. - Convert to GPU inputs in
geo_appupload path (and backend package if needed).
Feature layers:
- Features live in
FeatureLayers (Scene.feature_layers), not a flat list. scene_visible_featuresflattens visible layers in stable order; that order IS the GPU buffer order, so it doubles as the pick/highlight index.- Toggling visibility rebuilds the feature/label buffers
(
app_toggle_feature_layerwaits for device idle first) and clears the selection, since flat indices shift.
Coordinate convention:
- World space is Y-up with
geo_core.lat_lon_to_xyznegating z so longitude increases eastward (screen right) when viewed from outside with north up. - The globe imagery UV mapping in
shaders/globe.fragmatches this frame (u_tex = 0.5 - u). Keep the two in sync: features, routes, picking, and imagery tile prefetch all assume the same longitudes.
Routes:
- Routes render as great-circle line-list arcs (
geo_layers/routes.odin) slightly above the surface, depth-tested against the globe, with a flow pulse animated inshaders/route.frag.
Picking and labels:
- Picking is CPU-side screen-space projection (
geo_app/picking.odin+geo_core.camera_world_to_screen), with a hemisphere test so back-side features cannot be selected. Runodin test geo_appto validate. - Labels are billboarded text quads built once from feature names
(
geo_layers/labels.odin, embedded public-domain 8x8 font) and offset in screen space byshaders/label.vert; they fade past the globe horizon. - The selected feature index rides in the shared push constants
(
geo_render.Push_Constants.selected_index) and drives the pulse highlight inshaders/feature.vert.
Add imagery behavior:
- Tile addressing/probing changes go in
geo_layers/imagery.odin. - Queue/fetch policy changes go in
geo_sync. - Frame budget/cadence decisions stay in
geo_app/app.odin.
Add native rendering features:
- Backend resources/pipeline changes in
geo_cvulkan. - Shared constants or command structs in
geo_render. - Keep scene semantics in
geo_layers, not Vulkan files.
- Compile check:
odin check . - Run app:
odin run . - Rebuild changed GLSL:
glslc shaders/<name>.vert -o shaders/<name>.vert.spvand/orglslc shaders/<name>.frag -o shaders/<name>.frag.spv
- Avoid disk reads in per-frame loops; prefer probe/metadata paths.
- Gate expensive streaming/fetch work by interaction cooldowns and cadence intervals.
- Treat large texture swaps as expensive; throttle LOD switching.
- Prefer adding coarse-grained toggles in
geo_app/app.odinfor quick runtime tuning.
The MVP is intentionally small:
- 3D globe first.
- Imagery as a first-class surface layer.
- Feature layers as semantic points and routes.
- Shared model for later native and web renderers.