Fix anchor interpolation.
This commit is contained in:
parent
72f0b71195
commit
f2bea2ebbe
@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
glam = "0.27.0"
|
||||
rand = "0.8.5"
|
||||
noise = "0.9.0"
|
||||
|
||||
pool = { git = "https://git.tsukiyo.org/Utility/pool" }
|
||||
|
@ -18,24 +18,56 @@ impl Neighbor {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Anchor {
|
||||
pub generator:noise::Perlin,
|
||||
pub struct Octave {
|
||||
pub scale:f64,
|
||||
pub weight:f64,
|
||||
}
|
||||
impl Octave {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
scale:1.,
|
||||
weight:1.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Terrain {
|
||||
pub elevation:f64,
|
||||
pub octaves:[Octave; 4],
|
||||
}
|
||||
impl Terrain {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
elevation:0.,
|
||||
octaves:[Octave::new(); 4],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Anchor {
|
||||
pub position:DVec3,
|
||||
pub centroid:DVec3,
|
||||
pub basis:DMat3,
|
||||
pub ibasis:DMat3,
|
||||
pub neighbors:[Neighbor; 3],
|
||||
|
||||
pub terrain:Terrain,
|
||||
}
|
||||
impl Anchor {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
generator:noise::Perlin::new(1),
|
||||
elevation:0.,
|
||||
position:DVec3::ZERO,
|
||||
centroid:DVec3::ZERO,
|
||||
basis:DMat3::IDENTITY,
|
||||
ibasis:DMat3::IDENTITY,
|
||||
neighbors:[Neighbor::new(); 3],
|
||||
|
||||
terrain:Terrain::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
103
src/bin/world.rs
103
src/bin/world.rs
@ -1,4 +1,5 @@
|
||||
use glam::{DMat3, DVec3, Vec3};
|
||||
use rand::prelude::*;
|
||||
use sparse::Sparse;
|
||||
use terrain::{
|
||||
anchor::Anchor, generator::Generator, icosphere::Icosphere, mesh::Mesh, terrain::Terrain
|
||||
@ -43,22 +44,25 @@ fn get_neighbors(ico:&Icosphere<Anchor>, origin:usize, distance:usize) -> Vec<(u
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let radius_km = 100.;
|
||||
let radius_km = 1.;
|
||||
let terrain_radius = 1;
|
||||
|
||||
let mut ico = Icosphere::<Anchor>::new();
|
||||
for _ in 0..get_subdivisions(radius_km) { ico.subdivide(); }
|
||||
|
||||
let world_scale = radius_km * 1000.; // * 1000.
|
||||
let world_relief = 1.0;
|
||||
let world_scale = radius_km * 1000.;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
|
||||
// Set anchor properties.
|
||||
for id in ico.cells.list() {
|
||||
let mut cell = ico.cells.get_mut(id).unwrap().clone();
|
||||
|
||||
let vertices = [
|
||||
*ico.vertices.get(cell.vertices[0]).unwrap() * world_scale,
|
||||
*ico.vertices.get(cell.vertices[1]).unwrap() * world_scale,
|
||||
*ico.vertices.get(cell.vertices[2]).unwrap() * world_scale,
|
||||
*ico.vertices.get(cell.vertices[0]).unwrap(),
|
||||
*ico.vertices.get(cell.vertices[1]).unwrap(),
|
||||
*ico.vertices.get(cell.vertices[2]).unwrap(),
|
||||
];
|
||||
|
||||
let centroid = (vertices[0] + vertices[1] + vertices[2]) / 3.;
|
||||
@ -73,10 +77,21 @@ fn main() {
|
||||
cell.data = Some({
|
||||
let mut anchor = Anchor::new();
|
||||
|
||||
anchor.generator = noise::Perlin::new(id as u32);
|
||||
anchor.elevation = 0.;
|
||||
anchor.terrain.elevation = (rng.gen::<f64>() * 50.) - 10.;
|
||||
|
||||
anchor.position = centroid;
|
||||
let mut scale = (rng.gen::<f64>() / 32.) + (1. / 256.);
|
||||
let mut weight = (rng.gen::<f64>() * 2.) + 0.25;
|
||||
let octaves = (rng.gen::<f64>() * anchor.terrain.octaves.len() as f64) as usize + 1;
|
||||
for oct in 0..octaves {
|
||||
anchor.terrain.octaves[oct].scale = scale;
|
||||
anchor.terrain.octaves[oct].weight = weight;
|
||||
|
||||
scale *= (rng.gen::<f64>() / 2.) + (1. / 32.);
|
||||
weight *= (rng.gen::<f64>() * 16.) + 1.;
|
||||
}
|
||||
|
||||
anchor.position = centroid.normalize() * world_scale;
|
||||
anchor.centroid = centroid * world_scale;
|
||||
anchor.basis = basis;
|
||||
anchor.ibasis = basis_inverse;
|
||||
|
||||
@ -86,6 +101,7 @@ fn main() {
|
||||
ico.cells.update(id, cell).ok();
|
||||
}
|
||||
|
||||
|
||||
// Set anchor neighbor properties.
|
||||
for id in ico.cells.list() {
|
||||
let mut cell = ico.cells.get(id).unwrap().clone();
|
||||
@ -111,58 +127,67 @@ fn main() {
|
||||
ico.cells.update(id, cell).ok();
|
||||
}
|
||||
|
||||
|
||||
// Generate terrain.
|
||||
let mut gen = Generator::new();
|
||||
|
||||
let mut terrain_mesh: Mesh = Mesh::new();
|
||||
let use_sphere = true;
|
||||
|
||||
let mut mesh_count = 0;
|
||||
if let Some(origin_cell) = ico.cells.get(1) {
|
||||
let origin_anchor = &origin_cell.data.unwrap();
|
||||
let origin = origin_anchor.basis * origin_anchor.position;
|
||||
let world_center = DVec3::NEG_Y * world_scale;
|
||||
let world_origin = -(origin_anchor.basis * origin_anchor.position);
|
||||
|
||||
// Generate zone meshes.
|
||||
let tiles = get_neighbors(&ico, 1, 8);
|
||||
let tiles = get_neighbors(&ico, 1, terrain_radius);
|
||||
let total_tiles = tiles.len();
|
||||
for (cell_id, depth) in tiles {
|
||||
for (tile_id, depth) in tiles {
|
||||
mesh_count += 1;
|
||||
println!("Generating {} [{} / {}]", cell_id, mesh_count, total_tiles);
|
||||
println!("Generating {} [{} / {}]", tile_id, mesh_count, total_tiles);
|
||||
|
||||
if let Some(cell) = ico.cells.get(cell_id) {
|
||||
let cell_anchor = &cell.data.unwrap();
|
||||
if let Some(zone) = ico.cells.get(tile_id) {
|
||||
|
||||
for n in 0..cell.neighbors.len() {
|
||||
let nid = cell.neighbors[n];
|
||||
if let Some(neighbor) = ico.cells.get(nid) {
|
||||
if let Some(neighbor_anchor) = neighbor.data {
|
||||
gen.anchors[n] = neighbor_anchor;
|
||||
gen.anchors[n].position = origin_anchor.basis * gen.anchors[n].position;
|
||||
let mut zone_vertices = [DVec3::ZERO; 3];
|
||||
|
||||
// Prepare generator with parameters of neighboring tiles.
|
||||
let nearby = get_neighbors(&ico, tile_id, 3);
|
||||
for vindex in 0..zone.vertices.len() {
|
||||
let mut influence_count = 0;
|
||||
|
||||
let extent = zone.vertices[vindex];
|
||||
gen.sources[vindex] = terrain::generator::Source::new();
|
||||
zone_vertices[vindex] = (*ico.vertices.get(extent).unwrap()) * world_scale;
|
||||
gen.sources[vindex].position = zone_vertices[vindex];
|
||||
|
||||
for n in 0..nearby.len() {
|
||||
let (nearby_id, _depth) = nearby[n];
|
||||
|
||||
if let Some(tile) = ico.cells.get(nearby_id) {
|
||||
if let Some(tile_anchor) = &tile.data {
|
||||
for nvid in tile.vertices {
|
||||
if nvid == extent {
|
||||
gen.sources[vindex].elevation += tile_anchor.terrain.elevation;
|
||||
gen.sources[vindex].octaves.append(&mut tile_anchor.terrain.octaves.to_vec());
|
||||
influence_count += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gen.sources[vindex].elevation /= influence_count as f64;
|
||||
}
|
||||
gen.anchors[3] = *cell_anchor;
|
||||
|
||||
let cell_vertices: [DVec3; 3] = [
|
||||
(origin_anchor.basis * ((*ico.vertices.get(cell.vertices[0]).unwrap()) * world_scale)) - origin,
|
||||
(origin_anchor.basis * ((*ico.vertices.get(cell.vertices[1]).unwrap()) * world_scale)) - origin,
|
||||
(origin_anchor.basis * ((*ico.vertices.get(cell.vertices[2]).unwrap()) * world_scale)) - origin,
|
||||
];
|
||||
let _centroid = (cell_vertices[0] + cell_vertices[1] + cell_vertices[2]) / 3.;
|
||||
let cell_position = origin_anchor.basis * cell_anchor.position;
|
||||
let _cell_normal = cell_position.normalize();
|
||||
|
||||
let mut terrain = Terrain::triangle((cell_vertices[0], cell_vertices[1], cell_vertices[2]), gen.clone());
|
||||
let mut terrain = Terrain::triangle((zone_vertices[0], zone_vertices[1], zone_vertices[2]), gen.clone());
|
||||
for _ in 0..(4 - depth.min(3)) { terrain.subdivide(); }
|
||||
terrain.tessellate();
|
||||
|
||||
for vid in terrain.vertices.list() {
|
||||
if let Some(vertex) = terrain.vertices.get_mut(vid) {
|
||||
if use_sphere {
|
||||
vertex.position = ((vertex.position - world_center).normalize() * (world_scale + (world_relief * vertex.height))) + world_center;
|
||||
vertex.height = 0.;
|
||||
}
|
||||
let sv = vertex.position.normalize() * (world_scale + vertex.height);
|
||||
vertex.position = (origin_anchor.basis * sv) + world_origin;
|
||||
vertex.height = 0.;
|
||||
}
|
||||
}
|
||||
terrain_mesh.merge(&terrain.to_mesh(), Vec3::ZERO);
|
||||
@ -170,6 +195,6 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
//terrain::obj::save("icosphere.obj", &ico.to_mesh()).ok();
|
||||
terrain::obj::save("icosphere.obj", &ico.to_mesh(), 50.).ok();
|
||||
terrain::obj::save("test.obj", &terrain_mesh, 0.1).ok();
|
||||
}
|
||||
|
@ -1,52 +1,56 @@
|
||||
use glam::DVec3;
|
||||
use noise::NoiseFn;
|
||||
use glam::{DVec3, DVec4};
|
||||
use crate::anchor::Anchor;
|
||||
use crate::anchor::Octave;
|
||||
use crate::utility::barycentric;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone)]
|
||||
pub struct Source {
|
||||
pub position:DVec3,
|
||||
pub elevation:f64,
|
||||
pub octaves:Vec<Octave>,
|
||||
}
|
||||
impl Source {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
position:DVec3::ZERO,
|
||||
elevation:0.,
|
||||
octaves:Vec::<Octave>::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Generator {
|
||||
generator:noise::Perlin,
|
||||
pub anchors:[Anchor; 4],
|
||||
pub generator:noise::Perlin,
|
||||
pub sources:[Source; 3],
|
||||
}
|
||||
impl Generator {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
generator:noise::Perlin::new(120),
|
||||
anchors:[Anchor::new(); 4],
|
||||
generator:noise::Perlin::new(12),
|
||||
sources:[Source::new(), Source::new(), Source::new()],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seed(&mut self, seed:u32)
|
||||
{
|
||||
self.generator = noise::Perlin::new(seed);
|
||||
}
|
||||
|
||||
pub fn generate(&mut self, position:DVec3) -> f64
|
||||
{
|
||||
let h = DVec4::new(
|
||||
self.anchors[0].elevation,
|
||||
self.anchors[1].elevation,
|
||||
self.anchors[2].elevation,
|
||||
self.anchors[3].elevation,
|
||||
);
|
||||
let mut w = DVec4::new(
|
||||
(position.dot(self.anchors[3].position - self.anchors[3].position) > 0.) as i32 as f64 * (position - self.anchors[0].position).length_squared(),
|
||||
(position.dot(self.anchors[3].position - self.anchors[3].position) > 0.) as i32 as f64 * (position - self.anchors[1].position).length_squared(),
|
||||
(position.dot(self.anchors[3].position - self.anchors[3].position) > 0.) as i32 as f64 * (position - self.anchors[2].position).length_squared(),
|
||||
(position - self.anchors[3].position).length_squared(),
|
||||
);
|
||||
let sw = w.x + w.y + w.z + w.w;
|
||||
w /= sw;
|
||||
let mut heights = vec![0.; 3];
|
||||
|
||||
let mut scale = 1. / 2048.;
|
||||
let mut weight = 64.;
|
||||
let mut value :f64 = 0. * h.dot(w);
|
||||
for src in 0..heights.len() {
|
||||
heights[src] = self.sources[src].elevation;
|
||||
|
||||
for _ in 0..6 {
|
||||
value += weight * self.generator.get([scale * position.x, scale * position.y, scale * position.z]);
|
||||
scale *= 8.;
|
||||
weight *= 1. / 8.;
|
||||
for octave in &self.sources[src].octaves {
|
||||
heights[src] += octave.weight * self.generator.get([octave.scale * position.x, octave.scale * position.y, octave.scale * position.z]);
|
||||
}
|
||||
heights[src] /= self.sources[src].octaves.len().max(1) as f64;
|
||||
}
|
||||
value
|
||||
|
||||
barycentric(
|
||||
(self.sources[0].position, self.sources[1].position, self.sources[2].position),
|
||||
(heights[0], heights[1], heights[2]),
|
||||
position
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ impl Terrain {
|
||||
let mut grid = Self::new(generator);
|
||||
let (a, b, c) = basis;
|
||||
|
||||
let cross = (b - a).cross(c - a);
|
||||
let cross = (c - a).cross(b - a);
|
||||
grid.normal = cross.normalize();
|
||||
grid.scale = ((b - a).length() + (c - a).length()) / (1000. * f64::sqrt(3.));
|
||||
|
||||
|
@ -5,23 +5,24 @@ pub fn f64_sign(value:f64) -> f64
|
||||
((value > 0.) as i32 - (value < 0.) as i32) as f64
|
||||
}
|
||||
|
||||
pub fn barycenter_coordinates(a:DVec3, b:DVec3, c:DVec3, p:DVec3) -> DVec3
|
||||
pub fn barycentric(positions:(DVec3, DVec3, DVec3), values:(f64, f64, f64), point:DVec3) -> f64
|
||||
{
|
||||
let (a, b, c) = positions;
|
||||
let (va, vb, vc) = values;
|
||||
|
||||
let v0 = b - a;
|
||||
let v1 = c - a;
|
||||
let v2 = p - a;
|
||||
let v2 = point - a;
|
||||
|
||||
let d00 = v0.dot(v0);
|
||||
let d01 = v0.dot(v1);
|
||||
let d11 = v1.dot(v1);
|
||||
let d20 = v2.dot(v0);
|
||||
let d21 = v2.dot(v1);
|
||||
|
||||
let denom = (d00 * d11) - (d01 * d01);
|
||||
let b0 = ((d11 * d20) - (d01 * d21)) / denom;
|
||||
let b1 = ((d00 * d21) - (d01 * d20)) / denom;
|
||||
let b2 = 1. - (b0 + b1);
|
||||
|
||||
let v = ((d11 * d20) - (d01 * d21)) / denom;
|
||||
let w = ((d00 * d21) - (d01 * d20)) / denom;
|
||||
let u = 1. - v - w;
|
||||
|
||||
DVec3::new(u, v, w)
|
||||
(va * b2) + (vb * b0) + (vc * b1)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user