Fix anchor interpolation.

This commit is contained in:
yukirij 2024-06-12 20:34:23 -07:00
parent 72f0b71195
commit f2bea2ebbe
6 changed files with 149 additions and 86 deletions

View File

@ -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" }

View File

@ -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(),
}
}
}

View File

@ -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();
}

View File

@ -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
)
}
}

View File

@ -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.));

View File

@ -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)
}