use crate::{ column::{ColumnGen, ColumnSample}, util::{RandomField, Sampler, SmallCache}, IndexRef, }; use common::terrain::{ structure::{self, StructureBlock}, Block, BlockKind, SpriteKind, }; use core::ops::{Div, Mul, Range}; use serde::Deserialize; use vek::*; #[derive(Deserialize)] pub struct Colors { // TODO(@Sharp): After the merge, construct enough infrastructure to make it convenient to // define mapping functions over the input; i.e. we should be able to interpret some fields as // defining App, Arg>, where Fun : (Context, Arg) → (S, Type). pub structure_blocks: structure::structure_block::PureCases>>, } pub struct BlockGen<'a> { pub column_gen: ColumnGen<'a>, } impl<'a> BlockGen<'a> { pub fn new(column_gen: ColumnGen<'a>) -> Self { Self { column_gen } } pub fn sample_column<'b>( column_gen: &ColumnGen<'a>, cache: &'b mut SmallCache>>, wpos: Vec2, index: IndexRef<'a>, ) -> Option<&'b ColumnSample<'a>> { cache .get(wpos, |wpos| column_gen.get((wpos, index))) .as_ref() } pub fn get_z_cache(&mut self, wpos: Vec2, index: IndexRef<'a>) -> Option> { let BlockGen { column_gen } = self; // Main sample let sample = column_gen.get((wpos, index))?; Some(ZCache { sample }) } pub fn get_with_z_cache(&mut self, wpos: Vec3, z_cache: Option<&ZCache>) -> Option { let BlockGen { column_gen } = self; let world = column_gen.sim; let z_cache = z_cache?; let sample = &z_cache.sample; let &ColumnSample { alt, basement, chaos, water_level, warp_factor, surface_color, sub_surface_color, //tree_density, //forest_kind, //close_structures, // marble, // marble_small, rock, // temp, // humidity, stone_col, snow_cover, .. } = sample; let wposf = wpos.map(|e| e as f64); let (_definitely_underground, height, basement_height, water_height) = if (wposf.z as f32) < alt - 64.0 * chaos { // Shortcut warping (true, alt, basement, water_level) } else { // Apply warping let warp = world .gen_ctx .warp_nz .get(wposf.div(24.0)) .mul((chaos - 0.1).max(0.0).min(1.0).powi(2)) .mul(16.0); let warp = Lerp::lerp(0.0, warp, warp_factor); let height = alt + warp; ( false, height, basement + height - alt, (if water_level <= alt { water_level + warp } else { water_level }), ) }; // Sample blocks let water = Block::new(BlockKind::Water, Rgb::zero()); let grass_depth = (1.5 + 2.0 * chaos).min(height - basement_height); if (wposf.z as f32) < height - grass_depth { let stone_factor = (height - grass_depth - wposf.z as f32) * 0.15; let col = Lerp::lerp( sub_surface_color, stone_col.map(|e| e as f32 / 255.0), stone_factor, ) .map(|e| (e * 255.0) as u8); if stone_factor >= 0.5 { Some(Block::new(BlockKind::Rock, col)) } else { Some(Block::new(BlockKind::Earth, col)) } } else if (wposf.z as f32) < height { let grass_factor = (wposf.z as f32 - (height - grass_depth)) .div(grass_depth) .sqrt(); let col = Lerp::lerp(sub_surface_color, surface_color, grass_factor); // Surface Some(Block::new( if snow_cover { //if temp < CONFIG.snow_temp + 0.031 { BlockKind::Snow } else if grass_factor > 0.7 { BlockKind::Grass } else { BlockKind::Earth }, col.map(|e| (e * 255.0) as u8), )) } else { None } .or_else(|| { // Rocks if (height + 2.5 - wposf.z as f32).div(7.5).abs().powi(2) < rock { #[allow(clippy::identity_op)] let field0 = RandomField::new(world.seed + 0); let field1 = RandomField::new(world.seed + 1); let field2 = RandomField::new(world.seed + 2); Some(Block::new( BlockKind::WeakRock, stone_col.map2( Rgb::new( field0.get(wpos) as u8 % 16, field1.get(wpos) as u8 % 16, field2.get(wpos) as u8 % 16, ), |stone, x| stone.saturating_sub(x), ), )) } else { None } }) .or_else(|| { // Water if (wposf.z as f32) < water_height { // Ocean Some(water) } else { None } }) } } pub struct ZCache<'a> { pub sample: ColumnSample<'a>, } impl<'a> ZCache<'a> { pub fn get_z_limits(&self) -> (f32, f32) { let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0); let min = min - 4.0; let rocks = if self.sample.rock > 0.0 { 12.0 } else { 0.0 }; let warp = self.sample.chaos * 32.0; let ground_max = self.sample.alt + warp + rocks + 2.0; let max = ground_max.max(self.sample.water_level + 2.0); (min, max) } } pub fn block_from_structure( index: IndexRef, sblock: StructureBlock, pos: Vec3, structure_pos: Vec2, structure_seed: u32, sample: &ColumnSample, mut with_sprite: impl FnMut(SpriteKind) -> Block, ) -> Option { let field = RandomField::new(structure_seed); let lerp = ((field.get(Vec3::from(structure_pos)).rem_euclid(256)) as f32 / 255.0) * 0.8 + ((field.get(pos + std::i32::MAX / 2).rem_euclid(256)) as f32 / 255.0) * 0.2; match sblock { StructureBlock::None => None, StructureBlock::Hollow => Some(with_sprite(SpriteKind::Empty)), StructureBlock::Grass => Some(Block::new( BlockKind::Grass, sample.surface_color.map(|e| (e * 255.0) as u8), )), StructureBlock::Normal(color) => Some(Block::new(BlockKind::Misc, color)), StructureBlock::Block(kind, color) => Some(Block::new(kind, color)), StructureBlock::Water => Some(Block::water(SpriteKind::Empty)), // TODO: If/when liquid supports other colors again, revisit this. StructureBlock::GreenSludge => Some(Block::water(SpriteKind::Empty)), // None of these BlockKinds has an orientation, so we just use zero for the other color // bits. StructureBlock::Liana => Some(with_sprite(SpriteKind::Liana)), StructureBlock::Fruit => { if field.get(pos + structure_pos) % 24 == 0 { Some(with_sprite(SpriteKind::Beehive)) } else if field.get(pos + structure_pos + 1) % 3 == 0 { Some(with_sprite(SpriteKind::Apple)) } else { None } }, StructureBlock::Coconut => { if field.get(pos + structure_pos) % 3 > 0 { None } else { Some(with_sprite(SpriteKind::Coconut)) } }, StructureBlock::Chest => { if structure_seed % 10 < 7 { Some(Block::empty()) } else { Some(with_sprite(SpriteKind::Chest)) } }, StructureBlock::Log => Some(Block::new(BlockKind::Wood, Rgb::new(60, 30, 0))), // We interpolate all these BlockKinds as needed. StructureBlock::TemperateLeaves | StructureBlock::PineLeaves | StructureBlock::PalmLeavesInner | StructureBlock::PalmLeavesOuter | StructureBlock::Acacia | StructureBlock::Mangrove => sblock .elim_case_pure(&index.colors.block.structure_blocks) .as_ref() .map(|range| { Block::new( BlockKind::Leaves, Rgb::::lerp( Rgb::::from(range.start).map(f32::from), Rgb::::from(range.end).map(f32::from), lerp, ) .map(|e| e as u8), ) }), } }