use types::*;
use grid::{GridCoord, GridPoint2, GridPoint3};
// Contains the specifications (dimensions, seed, etc.)
// needed to deterministically generate a `Globe`.
//
// Provides helper functions that don't need to know anything
// beyond these values.
//
// TODO: accessors for all the fields, and make them private.
//
// TODO: split out parameters that are applicable to all
// kinds of globes, and those specific to individual kinds
// of globes.
#[derive(Clone, Copy)]
pub struct Spec {
pub seed: u32,
pub floor_radius: f64,
// NOTE: Don't let ocean radius be a neat multiple of block
// height above floor radius, or we'll end up with
// z-fighting in evaluating what blocks are water/air.
pub ocean_radius: f64,
pub block_height: f64,
// These are the full width/height/depth of a given root quad or chunk's voxmap;
// i.e. not an exponent. Only chunks specify a depth resolution because the
// world can have unbounded total depth.
pub root_resolution: [GridCoord; 2],
pub chunk_resolution: [GridCoord; 3],
}
impl Spec {
pub fn new_earth_scale_example() -> Spec {
let ocean_radius = 6_371_000.0;
// TODO: actually more like 60_000 when we know how to:
// - Unload chunks properly
// - Start with a guess about the z-position of the player
// so we don't have to start at bedrock and search up.
let crust_depth = 60.0;
let floor_radius = ocean_radius - crust_depth;
Spec {
// TODO: This only coincidentally puts you on land.
// Implement deterministic (PRNG) land finding so that the seed does not matter.
seed: 14,
floor_radius: floor_radius,
ocean_radius: ocean_radius,
block_height: 0.65,
root_resolution: [8388608, 16777216],
// Chunks should probably be taller, but short chunks are a bit
// better for now in exposing bugs visually.
chunk_resolution: [16, 16, 4],
}
}
pub fn is_valid(&self) -> bool {
// Chunk resolution needs to divide perfectly into root resolution.
let cprs = self.chunks_per_root_side();
let calculated_root_resolution = [
cprs[0] * self.chunk_resolution[0],
cprs[1] * self.chunk_resolution[1],
];
if calculated_root_resolution != self.root_resolution {
return false;
}
// Root resolution needs to be exactly twice in the y-direction
// that it is in the x-direction. I can't think of any serious
// use cases for anything else, and it's extremely unclear how
// a lot of scenarios should work otherwise.
if self.root_resolution[1] != self.root_resolution[0] * 2 {
return false;
}
true
}
pub fn chunks_per_root_side(&self) -> [GridCoord; 2] {
// Assume chunk resolution divides perfectly into root resolution.
[
self.root_resolution[0] / self.chunk_resolution[0],
self.root_resolution[1] / self.chunk_resolution[1],
]
}
// Ignore the z-coordinate; just project to a unit sphere.
// This is useful for, e.g., sampling noise to determine elevation
// at a particular point on the surface, or other places where you're
// really just talking about longitude/latitude.
pub fn cell_center_on_unit_sphere(&self, column: GridPoint2) -> Pt3 {
let res_x = self.root_resolution[0] as f64;
let res_y = self.root_resolution[1] as f64;
let pt_in_root_quad = Pt2::new(column.x as f64 / res_x, column.y as f64 / res_y);
super::project(column.root, pt_in_root_quad)
}
pub fn cell_center_center(&self, grid_point: GridPoint3) -> Pt3 {
let radius = self.floor_radius + self.block_height * (grid_point.z as f64 + 0.5);
radius * self.cell_center_on_unit_sphere(grid_point.rxy)
}
pub fn cell_bottom_center(&self, grid_point: GridPoint3) -> Pt3 {
let radius = self.floor_radius + self.block_height * (grid_point.z as f64);
radius * self.cell_center_on_unit_sphere(grid_point.rxy)
}
// TODO: describe meaning of offsets, where to get it from, etc.?
pub fn cell_vertex_on_unit_sphere(&self, grid_point: GridPoint3, offset: [i64; 2]) -> Pt3 {
let res_x = (self.root_resolution[0] * 6) as f64;
let res_y = (self.root_resolution[1] * 6) as f64;
let pt_in_root_quad = Pt2::new(
(grid_point.x as i64 * 6 + offset[0]) as f64 / res_x,
(grid_point.y as i64 * 6 + offset[1]) as f64 / res_y,
);
super::project(grid_point.root, pt_in_root_quad)
}
pub fn cell_bottom_vertex(&self, grid_point: GridPoint3, offset: [i64; 2]) -> Pt3 {
let radius = self.floor_radius + self.block_height * grid_point.z as f64;
radius * self.cell_vertex_on_unit_sphere(grid_point, offset)
}
pub fn cell_top_vertex(&self, mut grid_point: GridPoint3, offset: [i64; 2]) -> Pt3 {
// The top of one cell is the bottom of the next.
grid_point.z += 1;
self.cell_bottom_vertex(grid_point, offset)
}
// TODO: test me.
pub fn approx_cell_z_from_radius(&self, radius: f64) -> GridCoord {
((radius - self.floor_radius) / self.block_height) as GridCoord
}
}