Skip to main content

celestialsim/
surface.rs

1//! Neutral CPU-baked surface type + the provider trait that supplies it.
2//!
3//! The chunked planet bakes each chunk's surface into three per-slot GPU
4//! buffers — colour (rgba8), height (`f32`, in the provider's own vertical unit)
5//! and normal (rgba8-packed) — and the realize/bake shaders read them when the
6//! surface is enabled. TWO paths fill those buffers:
7//!
8//! * **GPU procedural noise** — a compute shader computes them in place (the
9//!   default; no provider, `surface_enabled == 0`).
10//! * **CPU async** — a worker pool ([`crate::bake_pool::BakePool`]) calls a
11//!   [`CpuSurfaceProvider`] off the main thread to produce a [`ChunkSurface`],
12//!   which the planet uploads into buffers 4/5/6 and the shaders read when the
13//!   surface is enabled. The built-in provider is [`crate::noise_provider`]
14//!   (CPU fBm terrain), used by a [`crate::builder::CesBuilder`] on the
15//!   [`crate::builder::BuilderRoute::CpuNoise`] route.
16//!
17//! This module holds ONLY the neutral data type and the trait — no concrete
18//! surface source, no Godot rendering.
19
20use std::collections::HashSet;
21
22use godot::builtin::Vector3;
23
24use celestial_algo::clipmap::FaceFrame;
25use celestial_algo::quadtree::{Chunk, ChunkId};
26
27/// A resampled per-chunk surface: `color` is `tile_res·tile_res·4` RGBA8,
28/// `height` is `tile_res·tile_res` elevations (the provider's vertical unit,
29/// scaled by [`CpuSurfaceProvider::height_scale`] on the GPU), and `normal` is
30/// `tile_res·tile_res·4` rgba8-packed world normals — all row-major. The chunk's
31/// real footprint is the lower-left triangle, but the WHOLE square holds valid
32/// data (texels past the diagonal replicate the nearest diagonal sample; see
33/// [`crate::surface_tiles::build_patch`]).
34#[derive(Clone, Debug)]
35pub struct ChunkSurface {
36    pub color: Vec<u8>,
37    pub height: Vec<f32>,
38    pub normal: Vec<u8>,
39}
40
41/// A source of CPU-baked chunk surfaces (colour/height/normal). Implementors run
42/// on the bake worker pool ([`bake`](Self::bake) is called off the main thread,
43/// so it must be `Send + Sync` and use interior mutability for any shared state).
44/// The other hooks run on the main thread.
45pub trait CpuSurfaceProvider: Send + Sync {
46    /// Resample this chunk's surface at `tile_res × tile_res`. Called on a bake
47    /// worker thread. A streaming provider may also enqueue any data it still
48    /// needs (a side effect) so [`poll_refresh`](Self::poll_refresh) can later
49    /// report the chunk for a re-bake once that data arrives.
50    fn bake(&self, frame: &FaceFrame, chunk: &Chunk, tile_res: u32) -> ChunkSurface;
51
52    /// Surface displacement at `dir`, in the height buffer's unit (multiply by
53    /// [`height_scale`](Self::height_scale) · radius for world units). Steers LOD
54    /// toward the displaced surface. `None` ⇒ no data (treated as sea level).
55    fn sample_height(&self, _dir: Vector3) -> Option<f32> {
56        None
57    }
58
59    /// Chunks whose awaited data has arrived and should be re-baked (streaming).
60    /// Called each frame on the main thread; a non-streaming provider returns
61    /// empty. Returns whole [`Chunk`]s (not just ids) so the caller can re-queue
62    /// a bake directly.
63    fn poll_refresh(&self) -> Vec<Chunk> {
64        Vec::new()
65    }
66
67    /// Camera-driven cancellation hint: the set of chunk ids still in view.
68    /// Streaming providers drop bookkeeping / downloads for anything else.
69    fn set_wanted(&self, _ids: &HashSet<ChunkId>) {}
70
71    /// Is enough data present for the surface to be shown (the GPU
72    /// `surface_enabled` gate)? A streaming provider returns `false` until its base
73    /// map lands; a self-contained provider (noise) is always ready.
74    fn base_ready(&self) -> bool {
75        true
76    }
77
78    /// Per-height-unit displaced-radius factor for the GPU: the surface radius
79    /// becomes `radius · (1 + height · height_scale)`.
80    fn height_scale(&self) -> f32 {
81        0.0
82    }
83
84    /// External resource fetches in flight (streaming providers; for the HUD).
85    fn resources_in_flight(&self) -> usize {
86        0
87    }
88
89    /// Absolute path of any on-disk cache this provider uses (for the HUD).
90    fn cache_dir(&self) -> Option<String> {
91        None
92    }
93}
94