diff --git a/Cargo.lock b/Cargo.lock index a984c8c..b7b4592 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1066,8 +1066,10 @@ dependencies = [ "bevy", "bevy_polyline", "bevy_rapier3d", + "hexasphere", "noise", "rand 0.8.5", + "wgpu", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0223384..4be56ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" rand = "0.8" bevy_polyline = "0.1" noise = { git = "https://github.com/Razaekel/noise-rs" } +hexasphere = "6.0.0" +wgpu = "0.12.0" [dependencies.bevy] version = "0.6" diff --git a/src/geometry.rs b/src/geometry.rs index 5ef4c58..a9ed7d8 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,4 +1,10 @@ -use bevy::prelude::*; +use bevy::{ + prelude::{shape::Icosphere, *}, + render::mesh::Indices, +}; +use hexasphere::shapes::IcoSphere; +use noise::{HybridMulti, NoiseFn, Perlin}; +use wgpu::PrimitiveTopology; use crate::Label; @@ -17,13 +23,18 @@ fn spawn_giant_sphere( mut materials: ResMut>, ) { let mut color = Color::DARK_GRAY; - color.set_a(0.8); + color.set_a(0.92); + + let isphere = shape::Icosphere { + radius: PLANET_RADIUS, + subdivisions: 26, + }; + + let pmesh = gen_planet(isphere); + commands .spawn_bundle(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Icosphere { - radius: PLANET_RADIUS, - subdivisions: 24, - })), + mesh: meshes.add(pmesh), material: materials.add(StandardMaterial { base_color: color, metallic: 0.65, @@ -60,3 +71,60 @@ impl Plugin for CyberGeomPlugin { .add_startup_system(spawn_cyberbike.label(Label::Geometry)); } } + +//--------------------------------------------------------------------- +// utils +//--------------------------------------------------------------------- + +fn gen_planet(sphere: Icosphere) -> Mesh { + // straight-up stolen from Bevy's impl of Mesh from Icosphere, so I can do the + // displacement before normals are calculated. + let generated = IcoSphere::new(sphere.subdivisions, |point| { + let inclination = point.y.acos(); + let azimuth = point.z.atan2(point.x); + + let norm_inclination = inclination / std::f32::consts::PI; + let norm_azimuth = 0.5 - (azimuth / std::f32::consts::TAU); + + [norm_azimuth, norm_inclination] + }); + + // TODO: use displaced points for normals by replacing raw_points with + // noise-displaced points. + let noise = HybridMulti::::default(); + let raw_points = generated.raw_points(); + + let points = raw_points + .iter() + .map(|&p| { + let pt = p * sphere.radius; + let disp = noise.get(pt.as_dvec3().into()) as f32; + let disp = p * disp * 20.0; + let pt = pt + disp; + pt.into() + }) + .collect::>(); + + let normals = raw_points + .iter() + .copied() + .map(Into::into) + .collect::>(); + + let uvs = generated.raw_data().to_owned(); + + let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20); + + for i in 0..20 { + generated.get_indices(i, &mut indices); + } + + let indices = Indices::U32(indices); + + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + mesh.set_indices(Some(indices)); + mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, points); + mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + mesh +}