Reorganize terrain; add orbital system.
This commit is contained in:
parent
9623bbd3e4
commit
5273e55c0b
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||
glam = "0.27.0"
|
||||
rand = "0.8.5"
|
||||
noise = "0.9.0"
|
||||
gltf = "1.4.1"
|
||||
|
||||
pool = { git = "https://git.tsukiyo.org/Utility/pool" }
|
||||
sparse = { path = "../../Utility/sparse" }
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Project: Kirisame Project
|
||||
Organization: Atelier Kirisame
|
||||
Licensor: Yukiri Corporation
|
||||
|
||||
License available at:
|
||||
https://ykr.info/license/
|
|
@ -0,0 +1,66 @@
|
|||
Quaternion { x :decimal, y :decimal, z :decimal, w :decimal }
|
||||
|
||||
StarSystem {
|
||||
Orbit {
|
||||
Orientation :Quaternion
|
||||
Periapsis :decimal
|
||||
Eccentricity :decimal
|
||||
}
|
||||
|
||||
Entity {
|
||||
Data :record
|
||||
Parent :natural
|
||||
Children :list<{ Orbit :Orbit, Entity :natural }>
|
||||
}
|
||||
|
||||
Entities :list<Entity>
|
||||
}
|
||||
|
||||
Octave {
|
||||
Frequency :decimal
|
||||
Amplitude :decimal
|
||||
}
|
||||
|
||||
Planet {
|
||||
Standard {
|
||||
Radius :decimal # km
|
||||
Gravity :decimal # m/s2
|
||||
}
|
||||
|
||||
Terrain :option<{
|
||||
|
||||
|
||||
Anchors :list<{
|
||||
Geography :{
|
||||
Elevation :decimal
|
||||
Octaves :Octave
|
||||
}
|
||||
|
||||
Biome :Biome
|
||||
}>
|
||||
}>
|
||||
|
||||
Ocean :option<{
|
||||
|
||||
|
||||
Anchors :list<{
|
||||
|
||||
}>
|
||||
}>
|
||||
|
||||
Atmosphere :option<{
|
||||
|
||||
|
||||
Anchors :list<{
|
||||
|
||||
}>
|
||||
}>
|
||||
|
||||
Climate :option<{
|
||||
|
||||
|
||||
Anchors :list<{
|
||||
|
||||
}>
|
||||
}>
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use glam::{DMat3, DVec3};
|
||||
use crate::{
|
||||
terrain::Terrain,
|
||||
generator::Generator,
|
||||
generator::{Generator, Octave},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -21,21 +21,6 @@ impl Neighbor {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Octave {
|
||||
pub frequency:f64,
|
||||
pub amplitude:f64,
|
||||
}
|
||||
impl Octave {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
frequency:1.,
|
||||
amplitude:1.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Geography {
|
||||
pub elevation:f64,
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
use glam::{DVec3, DQuat};
|
||||
use terrain::{
|
||||
icosphere::Icosphere,
|
||||
system::{StarSystem, Orbit},
|
||||
export::export_obj,
|
||||
};
|
||||
|
||||
fn main()
|
||||
{
|
||||
let ico = Icosphere::new();
|
||||
|
||||
let systems = [
|
||||
5.,
|
||||
1.,
|
||||
0.5,
|
||||
1.5,
|
||||
];
|
||||
|
||||
let sys = StarSystem::singular(0, 10.)
|
||||
.append(
|
||||
StarSystem::singular(1, 1.)
|
||||
.append(
|
||||
StarSystem::singular(2, 0.5),
|
||||
Orbit::with(DQuat::IDENTITY, 2., 0.1, 5., 0.),
|
||||
),
|
||||
Orbit::with(DQuat::IDENTITY, 20., 0.5, 10., 0.),
|
||||
)
|
||||
.append(
|
||||
StarSystem::singular(3, 1.),
|
||||
Orbit::with(DQuat::from_axis_angle(-DVec3::Z, f64::to_radians(30.)), 50., 0.1, 15., 0.),
|
||||
);
|
||||
|
||||
let mut mesh = ico.to_mesh().scale(systems[0]);
|
||||
for frame in 0..20 {
|
||||
let time = frame as f64 * 0.5;
|
||||
for (id, position) in sys.resolve(time) {
|
||||
if id != 0 {
|
||||
mesh = mesh.merge(&ico.to_mesh().scale(systems[id]), position.as_vec3());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export_obj("system.obj", &mesh, 10.).ok();
|
||||
}
|
319
src/bin/world.rs
319
src/bin/world.rs
|
@ -3,7 +3,12 @@ use noise::NoiseFn;
|
|||
use rand::prelude::*;
|
||||
use sparse::Sparse;
|
||||
use terrain::{
|
||||
anchor::Anchor, generator::Generator, icosphere::Icosphere, mesh::Mesh, terrain::Terrain
|
||||
anchor::Anchor,
|
||||
generator::Generator,
|
||||
icosphere::Icosphere,
|
||||
mesh::Mesh,
|
||||
terrain::Terrain,
|
||||
utility::rgb,
|
||||
};
|
||||
|
||||
fn get_subdivisions(radius_km:f64) -> usize
|
||||
|
@ -52,8 +57,9 @@ fn get_neighbors(ico:&Icosphere, origin:usize, distance:usize) -> Vec<(usize, us
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let radius_km = 100.;
|
||||
let terrain_radius = 3;
|
||||
let radius_km = 10.;
|
||||
let terrain_radius = 6;
|
||||
let max_terrain_detail = 14;
|
||||
|
||||
let mut ico = Icosphere::new();
|
||||
let mut anchors = Sparse::<Anchor>::new();
|
||||
|
@ -61,7 +67,10 @@ fn main() {
|
|||
for _ in 0..subdivisions { ico.subdivide(); }
|
||||
println!("Subdivisions: {}", subdivisions);
|
||||
|
||||
let world_scale = radius_km * 1000.;
|
||||
let world_radius = radius_km * 1000.;
|
||||
let world_relief = world_radius / 150.; // typically 100:1 - 300:1
|
||||
|
||||
println!("radius: {} / relief: {}", world_radius, world_relief);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let perlin = noise::Perlin::new(12);
|
||||
|
@ -95,6 +104,7 @@ fn main() {
|
|||
let basis = basis_inverse.inverse();
|
||||
|
||||
// Define anchor properties.
|
||||
//
|
||||
anchors.set(id as isize, {
|
||||
let mut anchor = Anchor::new();
|
||||
|
||||
|
@ -103,19 +113,25 @@ fn main() {
|
|||
anchor.basis = basis;
|
||||
anchor.ibasis = basis_inverse;
|
||||
|
||||
let mut elevation_frequency = 10000000. / 1.;// / world_scale;
|
||||
let mut elevation_frequency = 1. / 4.;
|
||||
for _ in 0..4 {
|
||||
anchor.geography.elevation += (100. * rng.gen::<f64>() / elevation_frequency) * perlin.get([elevation_frequency * anchor.position.x, elevation_frequency * anchor.position.y, elevation_frequency * anchor.position.z]);
|
||||
elevation_frequency *= 25.;
|
||||
let frequency = world_radius * elevation_frequency;
|
||||
anchor.geography.elevation += (world_relief * elevation_frequency) * perlin.get([frequency * anchor.position.x, frequency * anchor.position.y, frequency * anchor.position.z]);
|
||||
elevation_frequency /= 10.;
|
||||
}
|
||||
|
||||
let mut frequency = 32.;
|
||||
let _octaves = (rng.gen::<f64>() * anchor.geography.octaves.len() as f64) as usize + 1;
|
||||
for oct in 0..1 {
|
||||
anchor.geography.octaves[oct].frequency = frequency;
|
||||
anchor.geography.octaves[oct].amplitude = 100.; //rng.gen::<f64>();
|
||||
|
||||
frequency *= 1. / 4.;
|
||||
let octaves = (rng.gen::<f64>() * anchor.geography.octaves.len() as f64) as usize + 1;
|
||||
let mut octave_modifier = 500.;
|
||||
for oct in 0..octaves {
|
||||
let wavelength = (octave_modifier * world_relief * rng.gen::<f64>()) + 10.;
|
||||
anchor.geography.octaves[oct].frequency = world_radius / wavelength;
|
||||
anchor.geography.octaves[oct].amplitude = f64::sqrt(0.25 * rng.gen::<f64>() * wavelength);
|
||||
anchor.geography.octaves[oct].offset = world_radius * DVec3::new(
|
||||
(rng.gen::<f64>() * 2.) - 1.,
|
||||
(rng.gen::<f64>() * 2.) - 1.,
|
||||
(rng.gen::<f64>() * 2.) - 1.,
|
||||
);
|
||||
octave_modifier /= (rng.gen::<f64>() * 100.) + 2.;
|
||||
}
|
||||
|
||||
anchor
|
||||
|
@ -134,8 +150,8 @@ fn main() {
|
|||
let neighbor_id = cell.neighbors[n];
|
||||
let neighbor_anchor = anchors.get(neighbor_id as isize).unwrap();
|
||||
|
||||
anchor.neighbors[n].distance = anchor.position.angle_between(neighbor_anchor.position) * world_scale;
|
||||
anchor.neighbors[n].position = (anchor.basis * (neighbor_anchor.position - anchor.position)) * world_scale;
|
||||
anchor.neighbors[n].distance = anchor.position.angle_between(neighbor_anchor.position) * world_radius;
|
||||
anchor.neighbors[n].position = (anchor.basis * (neighbor_anchor.position - anchor.position)) * world_radius;
|
||||
anchor.neighbors[n].transform = anchor.ibasis * neighbor_anchor.basis;
|
||||
}
|
||||
|
||||
|
@ -150,7 +166,7 @@ fn main() {
|
|||
let origin_id = 1;
|
||||
|
||||
let origin_anchor = anchors.get(origin_id as isize).unwrap().clone();
|
||||
let world_origin = DVec3::NEG_Y * world_scale;
|
||||
let world_origin = DVec3::NEG_Y * world_radius;
|
||||
|
||||
// Generate zone meshes.
|
||||
let tiles = get_neighbors(&ico, 1, terrain_radius);
|
||||
|
@ -193,9 +209,11 @@ fn main() {
|
|||
}
|
||||
|
||||
let mut terrain = Terrain::triangle((gen.sources[0].position, gen.sources[1].position, gen.sources[2].position), gen);
|
||||
for _ in 0..(4 - (*depth).min(3)) { terrain.subdivide(); }
|
||||
terrain.tessellate();
|
||||
terrain.config.origin = origin_anchor.position;
|
||||
terrain.config.scale = world_radius;
|
||||
|
||||
for _ in 0..(4 - (*depth).min(3)) { terrain.subdivide(); }
|
||||
terrain.tessellate(max_terrain_detail);
|
||||
terrain
|
||||
} else { break; };
|
||||
|
||||
|
@ -209,64 +227,224 @@ fn main() {
|
|||
|
||||
let mut tile_count = 0;
|
||||
for (tile_id, _depth) in &tiles {
|
||||
let cell = ico.cells.get(*tile_id).unwrap().clone();
|
||||
let anchor = anchors.get(*tile_id as isize).unwrap().clone();
|
||||
let tile = ico.cells.get(*tile_id).unwrap().clone();
|
||||
let mut cterrain = anchors.get(*tile_id as isize).unwrap().terrain.clone();
|
||||
|
||||
tile_count += 1;
|
||||
println!("Stitching {} [{} / {}]", *tile_id, tile_count, tiles.len());
|
||||
|
||||
for side_id in 0..cell.neighbors.len() {
|
||||
let neighbor_id = cell.neighbors[side_id];
|
||||
let neighbor_cell = ico.cells.get(neighbor_id).unwrap();
|
||||
let neighbor_anchor = anchors.get_mut(neighbor_id as isize).unwrap();
|
||||
// Apply stitching to each side of the triangle mesh.
|
||||
for tile_side_index in 0..tile.neighbors.len() {
|
||||
let neighbor_id = tile.neighbors[tile_side_index];
|
||||
let neighbor_tile = ico.cells.get(neighbor_id).unwrap();
|
||||
let nterrain = &mut anchors.get_mut(neighbor_id as isize).unwrap().terrain;
|
||||
|
||||
// Determine rotation of neighboring tile.
|
||||
let side_vertices = [(side_id + 1) % 3, (side_id + 2) % 3];
|
||||
let mut neighbor_vertex_side = [0, 0];
|
||||
for s in 0..side_vertices.len() {
|
||||
let vid = cell.vertices[side_vertices[s]];
|
||||
if nterrain.extent == [0; 3] {
|
||||
// Ignore uninitialized (out of scope) terrain.
|
||||
continue;
|
||||
}
|
||||
|
||||
for ns in 0..neighbor_cell.vertices.len() {
|
||||
if neighbor_cell.vertices[ns] == vid {
|
||||
neighbor_vertex_side[s] = ns;
|
||||
// Find indices of shared vertices.
|
||||
let mut shared_vertex_indices = [0, 0];
|
||||
let mut nshared_vertex_indices = [0, 0];
|
||||
let mut shared_vertices = [0, 0];
|
||||
let mut nshared_vertices = [0, 0];
|
||||
let mut shared_index = 0;
|
||||
for vertex_index in 0..tile.vertices.len() {
|
||||
for nvertex_index in 0..neighbor_tile.vertices.len() {
|
||||
if shared_index < 2 && tile.vertices[vertex_index] == neighbor_tile.vertices[nvertex_index] {
|
||||
shared_vertex_indices[shared_index] = vertex_index;
|
||||
nshared_vertex_indices[shared_index] = nvertex_index;
|
||||
|
||||
shared_vertices[shared_index] = tile.vertices[vertex_index];
|
||||
nshared_vertices[shared_index] = neighbor_tile.vertices[nvertex_index];
|
||||
|
||||
shared_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let neighbor_direction = 3 - (nshared_vertex_indices[0] + nshared_vertex_indices[1]);
|
||||
|
||||
/*println!("shared c({} : {}={}/{}, {}={}/{}) n({} : {}={}/{}, {}={}/{})",
|
||||
tile_side_index,
|
||||
shared_vertex_indices[0], shared_vertices[0], cterrain.extent[shared_vertex_indices[0]],
|
||||
shared_vertex_indices[1], shared_vertices[1], cterrain.extent[shared_vertex_indices[1]],
|
||||
neighbor_direction,
|
||||
nshared_vertex_indices[0], nshared_vertices[0], nterrain.extent[nshared_vertex_indices[0]],
|
||||
nshared_vertex_indices[1], nshared_vertices[1], nterrain.extent[nshared_vertex_indices[1]],
|
||||
);*/
|
||||
|
||||
// Get ids of corner cells.
|
||||
let mut current_face_id = {
|
||||
let mut find = 0;
|
||||
for face_id in cterrain.cells.list() {
|
||||
let face = cterrain.cells.get(face_id).unwrap();
|
||||
let search = cterrain.extent[shared_vertex_indices[0]];
|
||||
if face.vertices[0] == search || face.vertices[1] == search || face.vertices[2] == search {
|
||||
find = face_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let neighbor_side = 3 - (neighbor_vertex_side[0] + neighbor_vertex_side[1]);
|
||||
|
||||
// Get maximum depth on edge of current cell.
|
||||
let mut max_depth = 0;
|
||||
for cell_id in anchor.terrain.cells.list() {
|
||||
let tcell = anchor.terrain.cells.get(cell_id).unwrap();
|
||||
|
||||
let side_counts = (tcell.neighbors[0] == 0) as usize
|
||||
+ (tcell.neighbors[1] == 0) as usize
|
||||
+ (tcell.neighbors[2] == 0) as usize;
|
||||
|
||||
if side_counts == 1 && tcell.neighbors[side_id] == 0 {
|
||||
max_depth = max_depth.max(tcell.depth);
|
||||
}
|
||||
}
|
||||
|
||||
// Subdivide neighbor edges.
|
||||
let mut subdivide_count = 1;
|
||||
while subdivide_count > 0 {
|
||||
subdivide_count = 0;
|
||||
|
||||
let cell_list = neighbor_anchor.terrain.cells.list();
|
||||
for cell_id in cell_list {
|
||||
let tcell = neighbor_anchor.terrain.cells.get(cell_id).unwrap().clone();
|
||||
|
||||
if tcell.neighbors[neighbor_side] == 0 {
|
||||
if tcell.depth < max_depth {
|
||||
neighbor_anchor.terrain.subdivide_cell(cell_id);
|
||||
subdivide_count += 1;
|
||||
}
|
||||
find
|
||||
};
|
||||
let mut neighbor_face_id = {
|
||||
let mut find = 0;
|
||||
for face_id in nterrain.cells.list() {
|
||||
let face = nterrain.cells.get(face_id).unwrap();
|
||||
let search = nterrain.extent[nshared_vertex_indices[0]];
|
||||
if face.vertices[0] == search || face.vertices[1] == search || face.vertices[2] == search {
|
||||
find = face_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
find
|
||||
};
|
||||
|
||||
if current_face_id == 0 || neighbor_face_id == 0 {
|
||||
//println!("! Failed to find faces: {}, {}", current_face_id, neighbor_face_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
let current_direction_down = shared_vertex_indices[0];
|
||||
let current_direction_edge = tile_side_index;
|
||||
let current_direction_away = 3 - (current_direction_down + current_direction_edge);
|
||||
let neighbor_direction_down = nshared_vertex_indices[0];
|
||||
let neighbor_direction_edge = neighbor_direction;
|
||||
let neighbor_direction_away = 3 - (neighbor_direction_down + neighbor_direction_edge);
|
||||
|
||||
/*println!(" DIR D0 {} E0 {} A0 {} D1 {} E1 {} A1 {}",
|
||||
current_direction_down,
|
||||
current_direction_edge,
|
||||
current_direction_away,
|
||||
neighbor_direction_down,
|
||||
neighbor_direction_edge,
|
||||
neighbor_direction_away,
|
||||
);*/
|
||||
|
||||
let mut end_current = false;
|
||||
let mut end_neighbor = false;
|
||||
|
||||
// Check if each edge face requires subdivision.
|
||||
//
|
||||
loop {
|
||||
let tile_cell = cterrain.cells.get(current_face_id).unwrap().clone();
|
||||
let neighbor_cell = nterrain.cells.get(neighbor_face_id).unwrap().clone();
|
||||
|
||||
/*println!("instep t {} (d {} r {}) n {} (d {} r {})",
|
||||
current_face_id, tile_cell.depth, tile_cell.direction,
|
||||
neighbor_face_id, neighbor_cell.depth, neighbor_cell.direction,
|
||||
);*/
|
||||
|
||||
// Work is done once both cells have no neighbor in donward directions.
|
||||
if end_current && end_neighbor {
|
||||
//println!("done");
|
||||
break;
|
||||
}
|
||||
|
||||
//println!("step t({}) = {} n({}) = {}", current_face_id, tile_cell.depth, neighbor_face_id, neighbor_cell.depth);
|
||||
|
||||
if tile_cell.depth & !1 > neighbor_cell.depth & !1 {
|
||||
// Subdivide neighbor tile's terrain and traverse backward.
|
||||
nterrain.subdivide_cell(neighbor_face_id);
|
||||
|
||||
let new_face = nterrain.cells.get(neighbor_face_id).unwrap();
|
||||
if new_face.sibling != 3 {
|
||||
neighbor_face_id = new_face.neighbors[new_face.sibling as usize];
|
||||
}
|
||||
|
||||
neighbor_face_id = nterrain.cells.get(neighbor_face_id).unwrap().neighbors[neighbor_direction_down];
|
||||
apply_count += 1;
|
||||
|
||||
} else if tile_cell.depth & !1 < neighbor_cell.depth & !1 {
|
||||
// Subdivide current tile's terrain and traverse backward.
|
||||
cterrain.subdivide_cell(current_face_id);
|
||||
|
||||
let new_face = cterrain.cells.get(current_face_id).unwrap();
|
||||
if new_face.sibling != 3 {
|
||||
current_face_id = new_face.neighbors[new_face.sibling as usize];
|
||||
}
|
||||
current_face_id = cterrain.cells.get(current_face_id).unwrap().neighbors[current_direction_down];
|
||||
apply_count += 1;
|
||||
|
||||
} else {
|
||||
// Traverse to next tile on edge.
|
||||
let mut move_last = current_face_id;
|
||||
let mut move_direction = current_direction_down;
|
||||
let mut move_count = 0;
|
||||
while {
|
||||
let face = cterrain.cells.get(current_face_id).unwrap();
|
||||
|
||||
let neighbor_id = face.neighbors[move_direction];
|
||||
/*println!("mv ({}) current {} / {} (d {} s {} r {}) -> {}",
|
||||
move_direction,
|
||||
move_last, neighbor_face_id,
|
||||
face.depth, face.sibling, face.direction,
|
||||
neighbor_id
|
||||
);*/
|
||||
if neighbor_id != 0 {
|
||||
move_count += 1;
|
||||
|
||||
if move_last != neighbor_id {
|
||||
move_last = current_face_id;
|
||||
current_face_id = neighbor_id;
|
||||
} else {
|
||||
if move_direction != current_direction_away {
|
||||
move_direction = current_direction_away;
|
||||
} else {
|
||||
move_direction = current_direction_edge;
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
if move_count == 0 {
|
||||
end_current = true;
|
||||
}
|
||||
false
|
||||
}
|
||||
} { }
|
||||
|
||||
// Traverse neighbor tile to next edge face.
|
||||
move_last = neighbor_face_id;
|
||||
move_direction = neighbor_direction_down;
|
||||
move_count = 0;
|
||||
while {
|
||||
let face = nterrain.cells.get(neighbor_face_id).unwrap();
|
||||
|
||||
let neighbor_id = face.neighbors[move_direction];
|
||||
/*println!("mv ({}) neighbor {} / {} (d {} s {} r {}) -> {}",
|
||||
move_direction,
|
||||
move_last, neighbor_face_id,
|
||||
face.depth, face.sibling, face.direction,
|
||||
neighbor_id
|
||||
);*/
|
||||
if neighbor_id != 0 {
|
||||
move_count += 1;
|
||||
|
||||
if move_last != neighbor_id {
|
||||
move_last = neighbor_face_id;
|
||||
neighbor_face_id = neighbor_id;
|
||||
} else {
|
||||
if move_direction != neighbor_direction_away {
|
||||
move_direction = neighbor_direction_away;
|
||||
} else {
|
||||
move_direction = neighbor_direction_edge;
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
if move_count == 0 {
|
||||
end_neighbor = true;
|
||||
}
|
||||
false
|
||||
}
|
||||
} { }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors.get_mut(*tile_id as isize).unwrap().terrain = cterrain;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,15 +454,20 @@ fn main() {
|
|||
|
||||
for vid in anchor.terrain.vertices.list() {
|
||||
if let Some(vertex) = anchor.terrain.vertices.get_mut(vid) {
|
||||
let norm = vertex.position.normalize() * (world_scale + vertex.height);
|
||||
let norm = vertex.position.normalize() * (world_radius + vertex.height);
|
||||
vertex.position = (origin_anchor.basis * norm) + world_origin;
|
||||
|
||||
if vertex.height > 0. {
|
||||
vertex.color = rgb(192, 128, 32);
|
||||
} else {
|
||||
vertex.color = rgb(32, 96, 192);
|
||||
}
|
||||
vertex.height = 0.;
|
||||
}
|
||||
}
|
||||
|
||||
terrain_mesh.merge(&anchor.terrain.to_mesh(), Vec3::ZERO);
|
||||
terrain_mesh = terrain_mesh.merge(&anchor.terrain.to_mesh(), Vec3::ZERO);
|
||||
}
|
||||
|
||||
//terrain::obj::save("icosphere.obj", &ico.to_mesh(), 20.).ok();
|
||||
terrain::obj::save("test.obj", &terrain_mesh, 0.01).ok();
|
||||
terrain::export::export_obj("test.obj", &terrain_mesh, 0.1).ok();
|
||||
}
|
||||
|
|
|
@ -2,19 +2,22 @@ use std::io::Write;
|
|||
use glam::Vec3;
|
||||
use crate::mesh::Mesh;
|
||||
|
||||
pub fn save(path:&str, mesh:&Mesh, scale:f32) -> Result<(),()>
|
||||
pub fn export_obj(path:&str, mesh:&Mesh, scale:f32) -> Result<(),()>
|
||||
{
|
||||
match std::fs::File::create(path) {
|
||||
Ok(mut file) => {
|
||||
let mut min = Vec3::ZERO;
|
||||
let mut max = Vec3::ZERO;
|
||||
|
||||
for v in &mesh.vertices {
|
||||
let v = *v * scale;
|
||||
for vertex in &mesh.vertices {
|
||||
let v = vertex.position * scale;
|
||||
if v.x < min.x { min.x = v.x } else if v.x > max.x { max.x = v.x };
|
||||
if v.z < min.z { min.z = v.z } else if v.z > max.z { max.z = v.z };
|
||||
|
||||
file.write_all(format!("v {:.6} {:.6} {:.6}\n", v.x, v.y, v.z).as_bytes()).ok();
|
||||
file.write_all(format!("v {:.6} {:.6} {:.6} {:3} {:3} {:3}\n",
|
||||
v.x, v.y, v.z,
|
||||
vertex.color.x, vertex.color.y, vertex.color.z,
|
||||
).as_bytes()).ok();
|
||||
}
|
||||
|
||||
file.write_all("\n".as_bytes()).ok();
|
||||
|
@ -22,8 +25,8 @@ pub fn save(path:&str, mesh:&Mesh, scale:f32) -> Result<(),()>
|
|||
let rx = max.x - min.x;
|
||||
let rz = max.z - min.z;
|
||||
|
||||
for v in &mesh.vertices {
|
||||
let v = *v * scale;
|
||||
for vertex in &mesh.vertices {
|
||||
let v = vertex.position * scale;
|
||||
let ux = ((v.x / rx) + 0.5).clamp(0., 1.);
|
||||
let uz = ((v.z / rz) + 0.5).clamp(0., 1.);
|
||||
|
|
@ -1,7 +1,23 @@
|
|||
use glam::DVec3;
|
||||
use noise::NoiseFn;
|
||||
use crate::anchor::Octave;
|
||||
use crate::utility::barycentric;
|
||||
use crate::utility::barycentric_value;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Octave {
|
||||
pub frequency:f64,
|
||||
pub amplitude:f64,
|
||||
pub offset:DVec3,
|
||||
}
|
||||
impl Octave {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
frequency:1.,
|
||||
amplitude:1.,
|
||||
offset:DVec3::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Source {
|
||||
|
@ -42,12 +58,16 @@ impl Generator {
|
|||
heights[src] = self.sources[src].elevation;
|
||||
|
||||
for octave in &self.sources[src].octaves {
|
||||
heights[src] += 10. * octave.amplitude * self.generator.get([octave.frequency * position.x, octave.frequency * position.y, octave.frequency * position.z]);
|
||||
heights[src] += octave.amplitude * self.generator.get([
|
||||
octave.frequency * (position.x + octave.offset.x),
|
||||
octave.frequency * (position.y + octave.offset.y),
|
||||
octave.frequency * (position.z + octave.offset.z),
|
||||
]);
|
||||
}
|
||||
heights[src] /= self.sources[src].octaves.len().max(1) as f64;
|
||||
//heights[src] /= self.sources[src].octaves.len().max(1) as f64;
|
||||
}
|
||||
|
||||
barycentric(
|
||||
barycentric_value(
|
||||
(self.sources[0].position, self.sources[1].position, self.sources[2].position),
|
||||
(heights[0], heights[1], heights[2]),
|
||||
position
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::f64::consts::PI;
|
||||
use glam::{IVec3, DVec3};
|
||||
use glam::{IVec3, DVec3, Vec3};
|
||||
use pool::Pool;
|
||||
use sparse::Sparse;
|
||||
|
||||
|
@ -335,7 +335,10 @@ impl Icosphere {
|
|||
for index in self.vertices.list() {
|
||||
if let Some(vertex) = self.vertices.get(index) {
|
||||
conversion.set(index as isize, mesh.vertices.len());
|
||||
mesh.vertices.push(vertex.as_vec3() * MESH_SCALE);
|
||||
mesh.vertices.push(crate::mesh::Vertex::new(
|
||||
vertex.as_vec3() * MESH_SCALE,
|
||||
Vec3::new(1., 1., 1.),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
pub mod utility;
|
||||
pub mod mesh;
|
||||
pub mod obj;
|
||||
pub mod export;
|
||||
pub mod anchor;
|
||||
pub mod generator;
|
||||
pub mod terrain;
|
||||
pub mod icosphere;
|
||||
pub mod planet;
|
||||
pub mod system;
|
||||
|
|
31
src/mesh.rs
31
src/mesh.rs
|
@ -1,23 +1,37 @@
|
|||
use glam::{Vec3, IVec3};
|
||||
|
||||
pub struct Vertex {
|
||||
pub position:Vec3,
|
||||
pub color:Vec3,
|
||||
}
|
||||
impl Vertex{
|
||||
pub fn new(position:Vec3, color:Vec3) -> Self
|
||||
{
|
||||
Self {
|
||||
position:position,
|
||||
color:color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Mesh {
|
||||
pub vertices:Vec<Vec3>,
|
||||
pub vertices:Vec<Vertex>,
|
||||
pub faces:Vec<IVec3>,
|
||||
}
|
||||
impl Mesh {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
vertices:Vec::<Vec3>::new(),
|
||||
vertices:Vec::<Vertex>::new(),
|
||||
faces:Vec::<IVec3>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, mesh:&Mesh, translation:Vec3)
|
||||
pub fn merge(mut self, mesh:&Mesh, translation:Vec3) -> Self
|
||||
{
|
||||
let start = self.vertices.len() as i32;
|
||||
for vertex in &mesh.vertices {
|
||||
self.vertices.push(*vertex + translation);
|
||||
self.vertices.push(Vertex::new(vertex.position + translation, vertex.color));
|
||||
}
|
||||
|
||||
for face in &mesh.faces {
|
||||
|
@ -27,5 +41,14 @@ impl Mesh {
|
|||
face.z + start,
|
||||
));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn scale(mut self, scale:f32) -> Self
|
||||
{
|
||||
for vertex in &mut self.vertices {
|
||||
vertex.position *= scale;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
use super::Planet;
|
||||
|
||||
pub struct Builder {
|
||||
data:Planet,
|
||||
}
|
||||
impl Builder {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Builder {
|
||||
data:Planet::new(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
mod planet; pub use planet::Planet;
|
||||
mod builder; pub use builder::Builder;
|
|
@ -0,0 +1,20 @@
|
|||
pub struct Planet {
|
||||
// Icosphere
|
||||
// Anchors
|
||||
// Geography
|
||||
// Ocean
|
||||
// Atmosphere
|
||||
// Weather
|
||||
// Climate
|
||||
}
|
||||
impl Planet {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self { }
|
||||
}
|
||||
|
||||
pub fn builder() -> super::Builder
|
||||
{
|
||||
super::Builder::new()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
use glam::DVec3;
|
||||
|
||||
mod orbit; pub use orbit::Orbit;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Edge {
|
||||
pub id:usize,
|
||||
pub orbit:Orbit,
|
||||
}
|
||||
impl Edge {
|
||||
pub fn new(id:usize, orbit:Orbit) -> Self
|
||||
{
|
||||
Self {
|
||||
id:id,
|
||||
orbit:orbit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum NodeData {
|
||||
Single(usize),
|
||||
Binary(Edge, Edge),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Node {
|
||||
pub data:NodeData,
|
||||
pub mass:f64,
|
||||
pub children:Vec<Edge>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StarSystem {
|
||||
nodes:Vec<Node>,
|
||||
}
|
||||
impl StarSystem {
|
||||
pub fn singular(id:usize, mass:f64) -> Self
|
||||
{
|
||||
Self {
|
||||
nodes:vec![Node {
|
||||
data:NodeData::Single(id),
|
||||
mass:mass,
|
||||
children:vec![],
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn binary(a:Self, b:Self, distance:f64, orbit:Orbit) -> Self
|
||||
{
|
||||
let offset_a = 1;
|
||||
let offset_b = a.nodes.len() + 1;
|
||||
|
||||
let mass_a = a.nodes[0].mass;
|
||||
let mass_b = b.nodes[0].mass;
|
||||
let total_mass = mass_a + mass_b;
|
||||
|
||||
let mut orbit_a = orbit;
|
||||
orbit_a.periapsis = mass_b * distance / total_mass;
|
||||
|
||||
let mut orbit_b = orbit;
|
||||
orbit_b.periapsis = mass_a * distance / total_mass;
|
||||
|
||||
Self {
|
||||
nodes:[
|
||||
vec![
|
||||
Node {
|
||||
data:NodeData::Binary(
|
||||
Edge::new(offset_a, orbit_a),
|
||||
Edge::new(offset_b, orbit_b),
|
||||
),
|
||||
mass:total_mass,
|
||||
children:vec![],
|
||||
}
|
||||
],
|
||||
a.nodes,
|
||||
b.nodes,
|
||||
].concat(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(mut self, mut system:Self, orbit:Orbit) -> Self
|
||||
{
|
||||
let id = self.nodes.len();
|
||||
println!("append at {}", id);
|
||||
|
||||
for node in &mut system.nodes {
|
||||
for child in &mut node.children {
|
||||
child.id += id;
|
||||
}
|
||||
}
|
||||
|
||||
self.nodes[0].children.push(Edge::new(id, orbit));
|
||||
self.nodes.append(&mut system.nodes.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn resolve(&self, time:f64) -> Vec<(usize, DVec3)>
|
||||
// Returns the absolute-space positions of all bodies.
|
||||
//
|
||||
{
|
||||
let mut result = Vec::<(usize, DVec3)>::new();
|
||||
let mut stack = Vec::<(usize, DVec3)>::new();
|
||||
stack.push((0, DVec3::ZERO));
|
||||
|
||||
while let Some(params) = stack.pop() {
|
||||
let (id, position) = params;
|
||||
println!("resolving {} at {}", id, position);
|
||||
|
||||
match &self.nodes[id].data {
|
||||
NodeData::Single(eid) => {
|
||||
result.push((*eid, position));
|
||||
}
|
||||
NodeData::Binary(edge1, edge2) => {
|
||||
stack.push((edge1.id, position + edge1.orbit.position(time)));
|
||||
stack.push((edge2.id, position - edge2.orbit.position(time)));
|
||||
}
|
||||
}
|
||||
|
||||
for edge in &self.nodes[id].children {
|
||||
stack.push((edge.id, position + edge.orbit.position(time)));
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
use std::f64::consts::PI;
|
||||
use glam::{DVec3, DQuat};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Orbit {
|
||||
pub orientation:DQuat, // orientation of the orbital system
|
||||
pub periapsis:f64, // focal distance at periapsis
|
||||
pub eccentricity:f64, // eccentricity of orbit
|
||||
pub period:f64, // period of orbit
|
||||
pub basis:f64, // starting position in multiples of period
|
||||
}
|
||||
impl Orbit {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
orientation:DQuat::IDENTITY,
|
||||
periapsis:1.,
|
||||
eccentricity:0.,
|
||||
period:1.,
|
||||
basis:0.,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with(orientation:DQuat, periapsis:f64, eccentricity:f64, period:f64, basis:f64) -> Self
|
||||
{
|
||||
Self {
|
||||
orientation:orientation,
|
||||
periapsis:periapsis,
|
||||
eccentricity:eccentricity,
|
||||
period:period,
|
||||
basis:basis,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn orientation(mut self, orientation:DQuat) -> Self
|
||||
{
|
||||
self.orientation = orientation;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn periapsis(mut self, periapsis:f64) -> Self
|
||||
{
|
||||
self.periapsis = periapsis;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn eccentricity(mut self, eccentricity:f64) -> Self
|
||||
{
|
||||
self.eccentricity = eccentricity;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn period(mut self, period:f64) -> Self
|
||||
{
|
||||
self.period = period;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn basis(mut self, basis:f64) -> Self
|
||||
{
|
||||
self.basis = basis;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn position(&self, time:f64) -> DVec3
|
||||
{
|
||||
let semimajor_axis = self.periapsis / (1. - self.eccentricity);
|
||||
let time = time + (self.basis * self.period);
|
||||
|
||||
let n = 2. * PI / self.period;
|
||||
let mean_anomaly = n * time;
|
||||
|
||||
let mut eccentric_anomaly = mean_anomaly + (self.eccentricity * ((mean_anomaly < PI) as i32 as f64 - 0.5));
|
||||
{
|
||||
let threshold = 1e-6;
|
||||
let mut delta: f64 = 1.;
|
||||
while delta.abs() > threshold {
|
||||
delta = (eccentric_anomaly - (self.eccentricity * eccentric_anomaly.sin()) - mean_anomaly) / (1. - self.eccentricity * eccentric_anomaly.cos());
|
||||
eccentric_anomaly = eccentric_anomaly - delta;
|
||||
}
|
||||
}
|
||||
|
||||
let theta = 2. * ((1. + self.eccentricity).sqrt() * (eccentric_anomaly / 2.).tan()).atan2((1. - self.eccentricity).sqrt());
|
||||
let radius = semimajor_axis * (1. - (self.eccentricity * eccentric_anomaly.cos()));
|
||||
|
||||
self.orientation * DVec3::new(
|
||||
radius * theta.cos(),
|
||||
0.,
|
||||
-radius * theta.sin(),
|
||||
)
|
||||
}
|
||||
}
|
105
src/terrain.rs
105
src/terrain.rs
|
@ -1,7 +1,7 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::f64::consts::PI;
|
||||
use glam::{IVec3, DVec3};
|
||||
use glam::{IVec3, DVec3, Vec3};
|
||||
use pool::Pool;
|
||||
use sparse::Sparse;
|
||||
|
||||
|
@ -11,10 +11,8 @@ const DEBUG_PRINTS :bool = false;
|
|||
|
||||
const DEGREES :f64 = PI / 180.;
|
||||
|
||||
const DELTA_THRESHOLD :f64 = (1. / 48.) * DEGREES;
|
||||
const MAX_SUBDIVISIONS :u16 = 4;
|
||||
const MAX_DEPTH :u16 = 2 * MAX_SUBDIVISIONS;
|
||||
const RADIAL_FALLOFF :f64 = 2.;
|
||||
const DELTA_THRESHOLD :f64 = (1. / 16.) * DEGREES;
|
||||
const RADIAL_FALLOFF :f64 = 1. / 6.;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Action {
|
||||
|
@ -23,10 +21,28 @@ enum Action {
|
|||
Split(/*Id*/usize, /*Caller*/usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TessellateConfig {
|
||||
pub origin:DVec3,
|
||||
pub scale:f64,
|
||||
pub max_depth:u16,
|
||||
}
|
||||
impl TessellateConfig {
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
origin:DVec3::ZERO,
|
||||
scale:1.,
|
||||
max_depth:1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Vertex {
|
||||
pub position:DVec3,
|
||||
pub height:f64,
|
||||
pub color:Vec3,
|
||||
}
|
||||
impl Vertex {
|
||||
pub fn new() -> Self
|
||||
|
@ -34,6 +50,7 @@ impl Vertex {
|
|||
Self {
|
||||
position:DVec3::ZERO,
|
||||
height:0.,
|
||||
color:Vec3::ONE,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +59,7 @@ impl Vertex {
|
|||
Self {
|
||||
position:position,
|
||||
height:height,
|
||||
color:Vec3::ONE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,9 +89,11 @@ impl Cell {
|
|||
pub struct Terrain {
|
||||
pub vertices:Pool<Vertex>,
|
||||
pub cells:Pool<Cell>,
|
||||
pub extent:[DVec3; 3],
|
||||
pub extent:[usize; 3],
|
||||
pub normal:DVec3,
|
||||
generator:Generator,
|
||||
|
||||
pub config:TessellateConfig,
|
||||
}
|
||||
impl Terrain {
|
||||
pub fn new(generator:Generator) -> Self
|
||||
|
@ -81,9 +101,11 @@ impl Terrain {
|
|||
Self {
|
||||
vertices:Pool::<Vertex>::new(),
|
||||
cells:Pool::<Cell>::new(),
|
||||
extent:[DVec3::ZERO; 3],
|
||||
extent:[0; 3],
|
||||
normal:DVec3::Y,
|
||||
generator:generator,
|
||||
|
||||
config:TessellateConfig::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,15 +117,15 @@ impl Terrain {
|
|||
let cross = (c - a).cross(b - a);
|
||||
grid.normal = cross.normalize();
|
||||
|
||||
grid.extent = [a, b, c];
|
||||
|
||||
grid.add_vertex(a);
|
||||
grid.add_vertex(b);
|
||||
grid.add_vertex(c);
|
||||
grid.extent = [
|
||||
grid.add_vertex(a),
|
||||
grid.add_vertex(b),
|
||||
grid.add_vertex(c),
|
||||
];
|
||||
|
||||
grid.cells.add({
|
||||
let mut cell = Cell::new();
|
||||
cell.vertices = [1, 2, 3];
|
||||
cell.vertices = grid.extent;
|
||||
cell
|
||||
});
|
||||
|
||||
|
@ -122,18 +144,21 @@ impl Terrain {
|
|||
self.node_update(&vec![Action::Subdivide(id, 0)]);
|
||||
}
|
||||
|
||||
pub fn tessellate(&mut self)
|
||||
pub fn tessellate(&mut self, max_depth:u16)
|
||||
{
|
||||
let mut data = self.cells.list();
|
||||
let mut data_len = data.len();
|
||||
|
||||
let mut config = self.config.clone();
|
||||
config.max_depth = max_depth;
|
||||
|
||||
let mut passes = 1;
|
||||
while data.len() > 0 {
|
||||
let pass = data.clone();
|
||||
data.clear();
|
||||
|
||||
for id in pass {
|
||||
if self.node_prepare(id) {
|
||||
if self.node_prepare(id, &config) {
|
||||
data.push(id);
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +177,7 @@ impl Terrain {
|
|||
}
|
||||
}
|
||||
|
||||
fn node_prepare(&mut self, id:usize) -> bool
|
||||
fn node_prepare(&mut self, id:usize, config:&TessellateConfig) -> bool
|
||||
// Test the height change of the node to determine if it should be subdivided.
|
||||
//
|
||||
{
|
||||
|
@ -178,9 +203,7 @@ impl Terrain {
|
|||
];
|
||||
let normal = (v[1] - v[0]).cross(v[2] - v[0]).normalize();
|
||||
|
||||
let mut neighbor_normal = DVec3::ZERO;
|
||||
let mut count = 0.;
|
||||
|
||||
let mut delta: f64 = 0.;
|
||||
for n in 0..3 {
|
||||
let neighbor_id = cell.neighbors[n];
|
||||
if neighbor_id != 0 {
|
||||
|
@ -196,29 +219,33 @@ impl Terrain {
|
|||
vt[2].position + (self.normal * vt[2].height),
|
||||
];
|
||||
|
||||
neighbor_normal += (v[1] - v[0]).cross(v[2] - v[0]).normalize();
|
||||
count += 1.;
|
||||
delta = delta.max(normal.angle_between((v[1] - v[0]).cross(v[2] - v[0]).normalize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
neighbor_normal /= count;
|
||||
let centroid = (vp[0] + vp[1] + vp[2]) / 3.;
|
||||
let min_dist = centroid.length_squared().max(1.);
|
||||
|
||||
let delta = normal.angle_between(neighbor_normal);
|
||||
let max_depth = MAX_DEPTH - 2 * (f64::log2(min_dist) / f64::log2(RADIAL_FALLOFF)).min((MAX_SUBDIVISIONS - 1) as f64) as u16;
|
||||
let threshold = DELTA_THRESHOLD * f64::log2(cell.depth as f64 + 16.) * 4.;
|
||||
if delta > threshold && cell.depth < max_depth {
|
||||
let cvp = [
|
||||
vp[0] - config.origin,
|
||||
vp[1] - config.origin,
|
||||
vp[2] - config.origin,
|
||||
];
|
||||
let centroid = (cvp[0] + cvp[1] + cvp[2]) / 3.;
|
||||
let min_dist = centroid.length_squared().min(cvp[0].length_squared()).min(cvp[1].length_squared()).min(cvp[2].length_squared()).sqrt() * config.scale;
|
||||
|
||||
let depth_threshold = 0.125 * cell.depth as f64;
|
||||
let distance_threshold = min_dist.log2();
|
||||
|
||||
//println!("dt {}", distance_threshold);
|
||||
|
||||
let threshold_modifier = (depth_threshold + distance_threshold) * RADIAL_FALLOFF;
|
||||
let threshold: f64 = DELTA_THRESHOLD * threshold_modifier * threshold_modifier;
|
||||
if delta > threshold && delta > threshold_modifier && cell.depth < (config.max_depth * 2) {
|
||||
self.node_update(&vec![Action::Subdivide(id, 0)]);
|
||||
|
||||
let new_cell = if let Some(data) = self.cells.get(id) {
|
||||
*data
|
||||
} else { return false; };
|
||||
|
||||
new_cell.depth != cell.depth
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if let Some(data) = self.cells.get(id) {
|
||||
data.depth != cell.depth
|
||||
} else { false }
|
||||
} else { false }
|
||||
}
|
||||
|
||||
fn node_update(&mut self, actions:&Vec<Action>)
|
||||
|
@ -298,7 +325,6 @@ impl Terrain {
|
|||
let mut cell = if let Some(data) = self.cells.get(id) {
|
||||
*data
|
||||
} else { return vec![]; };
|
||||
if cell.depth >= MAX_DEPTH { return vec![]; }
|
||||
|
||||
let mut actions = Vec::<Action>::new();
|
||||
|
||||
|
@ -658,7 +684,10 @@ impl Terrain {
|
|||
for index in self.vertices.list() {
|
||||
if let Some(vertex) = self.vertices.get(index) {
|
||||
conversion.set(index as isize, mesh.vertices.len());
|
||||
mesh.vertices.push((vertex.position + (self.normal * vertex.height)).as_vec3());
|
||||
mesh.vertices.push(crate::mesh::Vertex::new(
|
||||
(vertex.position + (self.normal * vertex.height)).as_vec3(),
|
||||
vertex.color,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
use glam::DVec3;
|
||||
use glam::{Vec3, DVec3};
|
||||
|
||||
pub fn f64_sign(value:f64) -> f64
|
||||
{
|
||||
((value > 0.) as i32 - (value < 0.) as i32) as f64
|
||||
}
|
||||
|
||||
pub fn barycentric(positions:(DVec3, DVec3, DVec3), values:(f64, f64, f64), point:DVec3) -> f64
|
||||
pub fn barycentric_coordinates(positions:(DVec3, DVec3, DVec3), point:DVec3) -> DVec3
|
||||
{
|
||||
let (a, b, c) = positions;
|
||||
let (va, vb, vc) = values;
|
||||
|
||||
let v0 = b - a;
|
||||
let v1 = c - a;
|
||||
|
@ -24,5 +23,23 @@ pub fn barycentric(positions:(DVec3, DVec3, DVec3), values:(f64, f64, f64), poin
|
|||
let b1 = ((d00 * d21) - (d01 * d20)) / denom;
|
||||
let b2 = 1. - (b0 + b1);
|
||||
|
||||
(va * b2) + (vb * b0) + (vc * b1)
|
||||
DVec3::new(b2, b0, b1)
|
||||
}
|
||||
|
||||
pub fn barycentric_inside(positions:(DVec3, DVec3, DVec3), point:DVec3) -> bool
|
||||
{
|
||||
let coords = barycentric_coordinates(positions, point);
|
||||
coords.x >= 0. && coords.x <= 1. && coords.y >= 0. && coords.y <= 1. && coords.z >= 0. && coords.z <= 1.
|
||||
}
|
||||
|
||||
pub fn barycentric_value(positions:(DVec3, DVec3, DVec3), values:(f64, f64, f64), point:DVec3) -> f64
|
||||
{
|
||||
let coords = barycentric_coordinates(positions, point);
|
||||
let (va, vb, vc) = values;
|
||||
(va * coords.x) + (vb * coords.y) + (vc * coords.z)
|
||||
}
|
||||
|
||||
pub fn rgb(r:u8, g:u8, b:u8) -> Vec3
|
||||
{
|
||||
Vec3::new(r as f32 / 255., g as f32 / 255., b as f32 / 255.)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue