Skip to content
This repository has been archived by the owner on Jun 19, 2021. It is now read-only.

Commit

Permalink
Merge pull request #52 from jonas-schievink/master
Browse files Browse the repository at this point in the history
Temp. plant renderer and treegen fixes/improvements
  • Loading branch information
LukasKalbertodt committed Jul 23, 2016
2 parents 424facb + becdf95 commit 1ad6d54
Show file tree
Hide file tree
Showing 16 changed files with 229 additions and 218 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ cgmath = "0.10.0"
log = "0.3.6"
num-traits = "0.1.33"
rand = "0.3.14"
fnv = "1.0.3"
31 changes: 31 additions & 0 deletions base/src/gen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
//! Functionality about procedurally generating content.
//!

extern crate fnv;

mod plant;
mod world;

pub use self::world::WorldGenerator;
pub use self::plant::PlantGenerator;

use rand::{SeedableRng, XorShiftRng};
use self::fnv::FnvHasher;
use std::hash::{Hash, Hasher};

type Random = XorShiftRng;

/// Creates a seeded RNG for use in world gen.
///
/// This function takes 3 seed parameters which are hashed and mixed together.
///
/// # Parameters
///
/// * `world_seed`: The constant world seed as set in the config
/// * `feat_seed`: Feature-specific constant seed
/// * `loc_seed`: Location-seed, for example, X/Y coordinates of a feature
fn seeded_rng<T: Hash, U: Hash>(world_seed: u64, feat_seed: T, loc_seed: U) -> Random {
// Hash everything, even `world_seed`, since XorShift really doesn't like seeds
// with many 0s in it
let mut fnv = FnvHasher::default();
world_seed.hash(&mut fnv);
feat_seed.hash(&mut fnv);
loc_seed.hash(&mut fnv);
let rng_seed = fnv.finish();
let seed0 = (rng_seed >> 32) as u32;
let seed1 = rng_seed as u32;

XorShiftRng::from_seed([seed0, seed1, seed0, seed1])
}
9 changes: 9 additions & 0 deletions base/src/gen/plant/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod tree;

use self::tree::TreeGen;
use prop::Plant;

use rand::{Rand, Rng};

Expand All @@ -12,6 +13,14 @@ pub enum PlantGenerator {
Tree(TreeGen),
}

impl PlantGenerator {
pub fn generate<R: Rng>(self, rng: &mut R) -> Plant {
match self {
PlantGenerator::Tree(treegen) => Plant::Tree { branches: treegen.generate(rng) },
}
}
}

impl Rand for PlantGenerator {
fn rand<R: Rng>(rng: &mut R) -> Self {
PlantGenerator::Tree(TreeGen::rand(rng))
Expand Down
51 changes: 31 additions & 20 deletions base/src/gen/plant/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::ops::Range;
/// Parameters for the tree generator.
#[derive(Debug)]
struct Preset {
name: &'static str,
/// Diameter of the first branch we create (the trunk).
trunk_diameter: Range<f32>,
/// Trunk height. Note that branches going upward can increase plant height
Expand Down Expand Up @@ -43,6 +44,7 @@ struct Preset {
}

static PRESETS: &'static [Preset] = &[Preset {
name: "'Regular' Tree",
trunk_diameter: 0.3..0.5,
trunk_height: 3.0..6.0,
trunk_diameter_top: 0.2..0.4,
Expand Down Expand Up @@ -147,16 +149,22 @@ impl TreeGen {
points: points,
// FIXME Fixed color for now, we should use a configurable random color (or at least
// make it brown).
color: Vector3f::new(0.0, 0.0, 1.0),
color: Vector3f::new(0.0, 1.0, 0.0),
});
}

/// Given the growing direction of the parent branch, calculates a growing
/// direction to use for a new child branch.
fn gen_branch_direction<R: Rng>(&self, rng: &mut R, parent_dir: Vector3f) -> Vector3f {
let x_angle = range_sample(&self.preset.branch_angle_deg, rng);
let y_angle = range_sample(&self.preset.branch_angle_deg, rng);
// `branch_angle_deg` specifies the angle range in degrees
let mut x_angle = range_sample(&self.preset.branch_angle_deg, rng);
let mut y_angle = range_sample(&self.preset.branch_angle_deg, rng);

// Invert sign with 50%, to mirror the specified range to the other side
x_angle = if rng.gen() { -x_angle } else { x_angle };
y_angle = if rng.gen() { -y_angle } else { y_angle };

// Rotate the growing direction of the parent branch
let rotation = Basis3::from(Euler {
x: Deg::new(x_angle),
y: Deg::new(y_angle),
Expand All @@ -169,17 +177,23 @@ impl TreeGen {
let trunk_diameter = range_sample(&self.preset.trunk_diameter, rng);
let trunk_height = range_sample(&self.preset.trunk_height, rng);
let trunk_diameter_top = range_sample(&self.preset.trunk_diameter_top, rng);
let min_branch_height = range_sample(&self.preset.min_branch_height, rng);
let min_branch_height = range_sample(&self.preset.min_branch_height, rng) * trunk_height;

debug!("trunk diam {} to {}, height {}, branch start at {}",
trunk_diameter,
trunk_diameter_top,
trunk_height,
min_branch_height);

let mut points = Vec::new();

{
let mut add_point = |height, diam| {
let point = Point3f::new(0.0, height, 0.0);
let point = Point3f::new(0.0, 0.0, height);
if height >= min_branch_height {
// FIXME Make branch spawn chance configurable
// 1/5 chance to spawn a branch at any point
if rng.gen_weighted_bool(5) {
// 1/3 chance to spawn a branch at any point
if rng.gen_weighted_bool(3) {
// Build a vector for the branch direction (Z is up)
let dir = self.gen_branch_direction(rng, Vector3f::new(0.0, 0.0, 1.0));
self.create_branch(rng, point, dir, 1, diam);
Expand All @@ -194,19 +208,18 @@ impl TreeGen {

let diam_start = Vector1::new(trunk_diameter);
let diam_end = Vector1::new(trunk_diameter_top);
let mut height = 0.0;
while height < trunk_height {
// Current height as a fraction of the total height
let height_frac = if height == 0.0 { 0.0 } else { trunk_height / height };

// Split trunk in segments
// FIXME Vary the segment direction like we do for normal branches
// FIXME Make segment count depend on the trunk height
const SEGMENT_COUNT: u32 = 10;
for i in 0..SEGMENT_COUNT + 1 {
let height = i as f32 * trunk_height / SEGMENT_COUNT as f32;
let height_frac = height / trunk_height;
let diam = diam_start.lerp(diam_end, height_frac);

add_point(height, diam.x);

let segment_len = segment_dist(diam.x);
height += segment_len;
}

// FIXME Do we need to create another point here?
}

assert!(points.len() >= 2,
Expand All @@ -215,7 +228,7 @@ impl TreeGen {
points: points,
// FIXME Fixed color for now, we should use a configurable random color (or at least
// make it brown).
color: Vector3f::new(0.0, 0.0, 1.0),
color: Vector3f::new(0.0, 1.0, 0.0),
});

debug!("generated tree with {} branches", self.branches.len());
Expand All @@ -225,7 +238,6 @@ impl TreeGen {
///
/// The tree is returned as a list of branches for now.
pub fn generate<R: Rng>(mut self, rng: &mut R) -> Vec<Branch> {
info!("treegen activated!"); // deleteme
// Recursively create the tree and put all branches in a buffer.
self.create_trunk(rng);
self.branches
Expand All @@ -234,8 +246,7 @@ impl TreeGen {

impl Rand for TreeGen {
fn rand<R: Rng>(rng: &mut R) -> Self {
// Create a tree generator with random parameters.
// First, select a random preset:
// Select a random preset that we'll use
let preset = rng.choose(PRESETS).unwrap().clone();

TreeGen {
Expand Down
28 changes: 24 additions & 4 deletions base/src/gen/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
//!

use world::{CHUNK_SIZE, Chunk, ChunkIndex, ChunkProvider, HeightType, HexPillar};
use world::{GroundMaterial, PillarSection, Prop, PropType};
use rand::Rand;
use gen::PlantGenerator;

/// Main type to generate the game world. Implements the `ChunkProvider` trait
/// (TODO, see #8).
Expand All @@ -26,12 +29,29 @@ impl ChunkProvider for WorldGenerator {
let mut pillars = Vec::new();
let q = index.0.q * CHUNK_SIZE as i32;
let r = index.0.r * CHUNK_SIZE as i32;
let mut height;

for i in q..q + CHUNK_SIZE as i32 {
for j in r..r + CHUNK_SIZE as i32 {
height = (((i as f32) * 0.25).sin() * 10.0 +
((j as f32) * 0.25).sin() * 10.0 + 100.0) as u16;
pillars.push(HexPillar::from_height(HeightType(height)));
let height = (((i as f32) * 0.25).sin() * 10.0 + ((j as f32) * 0.25).sin() * 10.0 +
100.0) as u16;

let ground_section =
PillarSection::new(GroundMaterial::Dirt, HeightType(0), HeightType(height));
let mut props = Vec::new();

// Place a test plant every few blocks
const TREE_SPACING: i32 = 8;
if i % TREE_SPACING == 0 && j % TREE_SPACING == 0 {
let mut rng = super::seeded_rng(self.seed, "TREE", (i, j));
let gen = PlantGenerator::rand(&mut rng);

props.push(Prop {
baseline: HeightType(height),
prop: PropType::Plant(gen.generate(&mut rng)),
});
}

pillars.push(HexPillar::new(vec![ground_section], props));
}
}

Expand Down
11 changes: 1 addition & 10 deletions base/src/prop/plant.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
use math::{Point3f, Vector3f};

/// Represents a plant in the game world.
///
/// Currently, the fields are temporary and just for testing
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Plant {
pub height: f32,
pub stem_width: f32,
}

/// Contains all types of plants we can generate.
#[derive(Clone, Debug)]
pub enum NewPlant {
pub enum Plant {
/// A parameterized tree-like structure.
Tree {
/// The list of branches representing this tree.
Expand Down
9 changes: 0 additions & 9 deletions base/src/world/chunk.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use super::{CHUNK_SIZE, HexPillar, PillarIndexComponent};
use std::ops;
use std::iter;
use math::*;

/// Represents one part of the game world.
Expand All @@ -20,14 +19,6 @@ pub struct Chunk {
}

impl Chunk {
/// Creates a dummy chunk for early testing. FIXME: remove
pub fn dummy() -> Self {
let pillars = iter::repeat(HexPillar::dummy())
.take(CHUNK_SIZE.pow(2) as usize)
.collect();
Chunk { pillars: pillars }
}

/// Creates a chunk from a `Vec<HexPillar>`
pub fn from_pillars(pillars: Vec<HexPillar>) -> Self {
assert_eq!(pillars.len() as usize, CHUNK_SIZE.pow(2) as usize);
Expand Down
27 changes: 3 additions & 24 deletions base/src/world/hex_pillar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,10 @@ pub struct HexPillar {
}

impl HexPillar {
/// Creates a dummy pillar for early testing. FIXME: remove
pub fn dummy() -> Self {
pub fn new(sections: Vec<PillarSection>, props: Vec<Prop>) -> Self {
HexPillar {
sections: vec![PillarSection::new(GroundMaterial::Dirt, HeightType(0), HeightType(50))],
props: vec![Prop {
baseline: HeightType(50),
prop: PropType::Plant(prop::Plant {
height: 5.0,
stem_width: 0.5,
}),
}],
}
}

/// Creates the dummy pillar but with the given height.
pub fn from_height(height: HeightType) -> Self {
HexPillar {
sections: vec![PillarSection::new(GroundMaterial::Dirt, HeightType(0), height)],
props: vec![Prop {
baseline: HeightType(50),
prop: PropType::Plant(prop::Plant {
height: 5.0,
stem_width: 0.5,
}),
}],
sections: sections,
props: props,
}
}

Expand Down
8 changes: 0 additions & 8 deletions base/src/world/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@ impl World {
World { chunks: HashMap::new() }
}

/// Returns a dummy world with one dummy chunk for early testing.
/// FIXME: remove
pub fn dummy() -> Self {
let mut chunks = HashMap::new();
chunks.insert(ChunkIndex(AxialPoint::new(0, 0)), Chunk::dummy());
World { chunks: chunks }
}

pub fn replace_chunk(&mut self, index: ChunkIndex, provider: &ChunkProvider) {
match provider.load_chunk(index) {
Some(x) => {
Expand Down
16 changes: 16 additions & 0 deletions client/src/render/to_arr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,19 @@ impl<T: BaseFloat> ToArr for Vector2<T> {
(*self).into()
}
}

impl<T: BaseNum> ToArr for Point2<T> {
type Output = [T; 2];

fn to_arr(&self) -> Self::Output {
(*self).into()
}
}

impl<T: BaseNum> ToArr for Point3<T> {
type Output = [T; 3];

fn to_arr(&self) -> Self::Output {
(*self).into()
}
}

0 comments on commit 1ad6d54

Please sign in to comment.