use noise;
use grid::{GridPoint2, GridPoint3};
use super::spec::Spec;
use super::chunk::{Cell, Material};
// TODO: turn this into a component that we can slap onto a Globe
// or other globe-oid (distant point?).
/// Globe content generator. Stores all the state for generating
/// the terrain and any other parts of the globe that are derived
/// from its seed.
///
/// Will eventually do some basic caching, etc., but is pretty dumb
/// right now.
///
/// The plan is for this to eventually be used with multiple
/// implementations of globes, e.g., a full voxmap based globe,
/// a distant blob in the sky, to a shiny dot in the distance.
pub struct Gen {
spec: Spec,
terrain_noise: noise::Fbm<f64>,
}
impl Gen {
pub fn new(spec: Spec) -> Gen {
use noise::Seedable;
use noise::MultiFractal;
assert!(spec.is_valid(), "Invalid globe spec!");
// TODO: get parameters from spec
//
// TODO: store this function... when you figure
// out what's going on with the types.
// ("expected fn pointer, found fn item")
//
// TODO: even more pressing now that the noise API has
// changed to deprecate PermutationTable; is it now stored
// within Fbm? This might be super-slow now...
let terrain_noise = noise::Fbm::<f64>::new()
// TODO: make wavelength etc. part of spec;
// the octaves and wavelength of noise you want
// will probably depend on planet size.
.set_octaves(6)
.set_frequency(1.0 / 700.0)
// TODO: probably allow a bigger seed; what's the smallest usize on any real platform?
.set_seed(spec.seed as usize);
Gen {
spec: spec,
terrain_noise: terrain_noise,
}
}
pub fn land_height(&self, column: GridPoint2) -> f64 {
use noise::NoiseModule;
// Calculate height for this cell from world spec.
// To do this, project the cell onto a sea-level sphere
// and sample 3D simplex noise to get a height value.
//
// Basing on sea-level lets us use similar wavelengths
// to similar effect, regardless of the globe radius.
//
// TODO: split out a proper world generator
// that layers in lots of different kinds of noise etc.
let sea_level_pt3 = self.spec.cell_center_on_unit_sphere(column) * self.spec.ocean_radius;
// Vary a little bit around 1.0.
let delta = self.terrain_noise.get([sea_level_pt3.x, sea_level_pt3.y, sea_level_pt3.z])
* (self.spec.ocean_radius - self.spec.floor_radius)
// TODO: this 0.9 is only to stop the dirt level
// going below bedrock. Need something a bit more sophisticated
// than this eventually.
//
// Also... OpenSimplex, which FBM uses, appears to be totally bonkers?
// https://github.com/brendanzab/noise-rs/issues/149
* 0.45;
self.spec.ocean_radius + delta
}
pub fn cell_at(&self, grid_point: GridPoint3) -> Cell {
let land_height = self.land_height(grid_point.rxy);
let cell_pt3 = self.spec.cell_center_center(grid_point);
// TEMP: ...
let cell_height = cell_pt3.coords.norm();
let material = if cell_height < land_height {
Material::Dirt
} else if cell_height < self.spec.ocean_radius {
Material::Water
} else {
Material::Air
};
Cell {
material: material,
// `Globe` fills this in; it's not really a property
// of the naturally generated world, and it's not
// deterministic from the world seed, so we don't
// want to pollute `Gen` with it.
//
// TODO: probably remove this? We're just using
// temporarily to create some texture across
// cells to make them easy to tell apart and look
// kinda nice, but this probably isn't a great
// long-term solution...
shade: 1.0,
}
}
}