-
-
Notifications
You must be signed in to change notification settings - Fork 5
Configuration
raytiles is configured through four plain structs that you pass to the streamer at construction:
- World Configuration — world topology and geometry.
- Streaming Configuration — which tiles are kept resident and when the working set is refreshed.
- Rendering Configuration — shader uniforms (lighting, fog, terrain relief).
- Pool Configuration — download workers, cache paths, and tile providers.
All four structs ship with sensible defaults; you only need to override the fields that matter to your scene.
World topology and geometry parameters. Everything in this struct is effectively immutable once a streamer exists: changing any field requires rebuilding meshes, re-uploading textures, or re-anchoring the world. Set it once at construction.
World-space anchor expressed in tile coordinates at base_zoom. The streamer translates tile XY to world XZ relative to this anchor, so the world origin ends up wherever you want it (e.g. at your runway).
raytiles uses raylib's coordinate system, where the XZ plane is the world surface.
int anchor_x_tile = 306;
int anchor_z_tile = 207;The following services can help you pick an anchor. Select an area at the right zoom level (9 is the default) and copy the values into the configuration as shown above:
The (X, Y, Z) format reported by those tools maps to:
// default zoom is 9 (Z)
int anchor_x_tile = X;
int anchor_z_tile = Y;Lowest level-of-detail zoom that will ever be loaded. Tiles outside the camera's near radius are kept at this zoom to bound the working set.
int base_zoom = 9;Changing this value also requires updating streaming_config::thresholds and base_zoom_tile_size.
Note: the library has never been tested with a
base_zoomlower than 9.
Highest level-of-detail zoom available. Tiles directly under the camera are subdivided up to this zoom.
int max_zoom = 15;Changing this value also requires updating streaming_config::thresholds. 15 is the maximum zoom currently supported.
World size, in meters, of one tile at base_zoom. Tiles at higher zooms are scaled by 1 / (1 << (zoom - base_zoom)).
float base_zoom_tile_size = 66400.0f;raytiles consumes XYZ (slippy-map) tiles, which are produced in the Web Mercator projection. In Web Mercator, the ground distance covered by a single tile changes with latitude: tiles near the equator cover much more ground than tiles near the poles, even at the same zoom level. There is no single "correct" tile size for the whole planet — you pick the size that matches the latitude band your scene lives in.
The width (and height) of one tile, in meters on the ground, at zoom
where:
-
$R = 6{,}378{,}137\ \text{m}$ is the Earth's equatorial radius (WGS-84). -
$\varphi$ is the latitude of your anchor, in radians. -
$z$ is the zoom level.
The default 66400.0f corresponds to zoom 9 at roughly 30°–32° latitude (e.g. North Africa, the southern US, parts of the Middle East). If your scene sits at a very different latitude, you should recompute this value or your terrain will be horizontally stretched or squashed.
Examples at zoom 9:
| Latitude | base_zoom_tile_size |
|---|---|
| 0° | ~78,272 m |
| 30° | ~67,786 m |
| 45° | ~55,346 m |
| 60° | ~39,136 m |
Note: this is also why changing
base_zoomrequires updatingbase_zoom_tile_size— the meters-per-tile term scales by$2^{\Delta z}$ .
Per-zoom skirt overlap factors used to hide cracks between neighboring tiles at different LODs. The factor scales the geometric skirt baked into the tile mesh at that zoom — a value of 1.0 keeps the default overlap, larger values widen the skirt (more reliable crack-hiding, more fill rate), and 0.0 disables it for that zoom. Must cover every zoom in [base_zoom, max_zoom]; missing entries cause the streamer to throw at construction. Baked into generated meshes.
std::unordered_map<Zoom, float> skirt_overlap = {
{9, 1.0f},
{10, 1.0f},
{11, 1.0f},
{12, 1.0f},
{13, 1.0f},
{14, 1.0f},
{15, 1.0f},
};Note: the vertical drop of the skirt geometry is configured separately via
rendering_config::skirt_drop— it's a shader uniform, not baked into the mesh.
Generate trilinear / anisotropic mipmaps for the albedo texture on upload. Strongly recommended — it avoids shimmering at distance.
bool use_mipmap = true;Whether to emit log lines from the streamer. Logs from the main thread / process are routed through raylib's TraceLog.
bool use_logger = false;Tile-streaming parameters. Governs which tiles are kept resident and how aggressively the working set is updated. Safe to tweak at runtime (no mesh / texture rebuild needed), but most users set it once.
Radius, expressed in world_config::base_zoom tiles, of the disc of tiles loaded around the camera. Larger values mean more tiles in flight, and therefore more memory and bandwidth.
int rendering_radius = 6;To translate to meters on the ground, multiply by base_zoom_tile_size. With the defaults (6 tiles × 66400 m), the streamer keeps tiles resident out to roughly 400 km from the camera.
Per-zoom distance thresholds covering world_config::base_zoom through world_config::max_zoom. For each zoom level, the map gives the maximum camera distance (in meters) at which tiles of that zoom may be used. The streamer picks the highest zoom whose threshold still includes the tile, so closer tiles get more detail and far tiles fall back to base_zoom.
std::unordered_map<Zoom, Meters> thresholds = {
{9, 100000.0f},
{10, 80000.0f},
{11, 40000.0f},
{12, 20000.0f},
{13, 10000.0f},
{14, 5000.0f},
{15, 2500.0f},
};The defaults are tuned for performance and to keep the resident tile count under 600. If the zoom range changes, this map must be updated to match.
Squared XZ distance the camera must travel before the desired-tile set is recomputed. The value is squared so the streamer can compare against dx*dx + dz*dz without ever calling sqrt. The default of 1000.0f * 1000.0f means recompute after moving 1 km. Keep this large enough that small movements don't churn the working set.
MetersSq update_distance_sq = 1000.0f * 1000.0f;Altitude delta, in meters, that triggers a desired-set recomputation independent of update_distance_sq. Lets you stream new LODs as you climb or descend without horizontal motion.
Meters update_height = 500.0f;Wall-clock budget, in seconds, per frame for promoting downloaded tiles into GPU resources. Caps the cost of a single bursty frame.
double upload_budget_sec = 0.002;Hard cap on tile promotions per frame, applied on top of upload_budget_sec. Whichever limit is hit first stops the loop.
int max_uploads_per_frame = 8;Rendering and shader-uniform parameters. Every field here is genuinely runtime-mutable; most have matching streamer::set_* setters that push new values to the shader on the next update().
Near clip plane, in meters, used by the displacement shader for fog and depth-precision tuning. Match this to your camera setup.
MetersD near_plane = 1;Far clip plane, in meters, used by the displacement shader for fog and depth-precision tuning. Match this to your camera setup.
MetersD far_plane = 400000;Distance, in meters, at which atmospheric fog starts fading tiles toward fog_color.
Meters fog_start = 100000.0f;Distance, in meters, at which fog reaches full coverage.
Meters fog_end = 150000.0f;Vertical drop, in meters, of the skirt geometry below each tile's edge. Larger values hide cracks more reliably between neighboring tiles at different LODs, but cost more fill rate. Applied as a shader uniform, so it can be tweaked at runtime. Set to 0.0f to disable the vertical drop entirely.
Meters skirt_drop = 0.0f;Fog color (RGBA, 0..1). Match this to your sky color for a seamless horizon.
float fog_color[4] = {0.0f, 0.0f, 1.0f, 1.0f};World ambient color (RGBA, 0..1). Drives day / night / weather lighting changes.
float ambient_light[4] = {1.0f, 1.0f, 1.0f, 1.0f};Sun direction vector. The shader normalizes it internally, so the magnitude is irrelevant.
float sun_direction[3] = {0.1f, 1.0f, 0.1f};Sun lighting intensity. Controls the contrast between lit and shaded areas.
float sun_scale = 1.0f;Scales the heightmap to exaggerate or flatten the terrain relief (drama factor).
float height_scale = 1.0f;Scales the normals to increase or reduce lighting contrast. Higher values make the terrain look bumpier, but can cause lighting artifacts if the normals become too steep.
float normals_scale = 1.0f;Background tile-fetching parameters: download workers, on-disk cache layout, and the tile provider URLs.
Number of background download workers. Downloads are I/O-bound, so it is safe to use more threads than CPU cores; 2 is a reasonable starting point for HTTP keep-alive against a single host.
int download_threads = 4;Skip TLS certificate verification for tile downloads. Only useful for local proxies — never enable this against a real server.
bool allow_insecure_tls = false;Whether the pool's worker threads emit log lines.
bool use_logger = false;On-disk cache path templates, formatted with {zoom}/{x}/{z} via std::vformat. Parent directories are created on demand.
std::string texture_cache_path = "assets/texture/{}/{}/{}.png";
std::string heightmap_cache_path = "assets/heightmap/{}/{}/{}.png";
std::string normals_cache_path = "assets/normals/{}/{}/{}.png";URL templates for the three tile streams: satellite imagery (texture_url), heightmaps (heightmap_url), and surface normals (normals_url). The placeholders :zoom:, :x:, and :y: are substituted with the tile coordinates at request time (plus any optional token baked into the template). Any provider that follows the XYZ (slippy-map) convention works, as long as the heightmap provider returns RGB-encoded heightmaps.
The defaults point at public providers — Esri World Imagery for textures and AWS Terrain Tiles for heightmaps and normals — so an unconfigured pool_config already produces a working world:
// the order zoom/y/x is not a mistake — that's how Esri encodes their URLs
std::string texture_url = "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/:zoom:/:y:/:x:";
std::string heightmap_url = "https://s3.amazonaws.com/elevation-tiles-prod/terrarium/:zoom:/:x:/:y:.png";
std::string normals_url = "https://s3.amazonaws.com/elevation-tiles-prod/normal/:zoom:/:x:/:y:.png";At compile time the defaults can be overridden via the RAYTILES_TEXTURE_URL, RAYTILES_HEIGHTMAP_URL, and RAYTILES_NORMALS_URL preprocessor macros.
Note:
pool_configalso exposestexture_host/texture_url_path(and the matchingheightmap_*/normals_*pairs). You don't set these yourself — the pool splits each*_urlinto host and path at construction so the HTTP client can keep one connection per host alive. Set the*_urlfields and leave the rest empty.
See Working with Mapbox for a concrete provider setup.
Raytiles