cyber_rider/src/planet.rs
2022-03-21 16:04:06 -07:00

134 lines
3.7 KiB
Rust

use bevy::{
prelude::{shape::Icosphere, *},
render::mesh::Indices,
};
use bevy_rapier3d::prelude::*;
use hexasphere::shapes::IcoSphere;
use noise::{HybridMulti, NoiseFn, SuperSimplex};
use wgpu::PrimitiveTopology;
use crate::Label;
pub const PLANET_RADIUS: f32 = 6000.0;
#[derive(Component)]
pub struct CyberPlanet;
fn spawn_planet(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let color = Color::rgb(0.2, 0.1, 0.2);
let isphere = Icosphere {
radius: PLANET_RADIUS,
subdivisions: 88,
};
let (mesh, shape) = gen_planet(isphere);
let pbody = RigidBodyBundle {
body_type: RigidBodyType::Static.into(),
ccd: RigidBodyCcd {
ccd_enabled: true,
ccd_thickness: 0.5,
ccd_max_dist: PLANET_RADIUS * 1.05,
..Default::default()
}
.into(),
..Default::default()
};
let pcollide = ColliderBundle {
shape: shape.into(),
material: ColliderMaterial {
friction: 0.05,
restitution: 0.00,
..Default::default()
}
.into(),
..Default::default()
};
commands
.spawn_bundle(PbrBundle {
mesh: meshes.add(mesh),
material: materials.add(StandardMaterial {
base_color: color,
metallic: 0.1,
perceptual_roughness: 0.3,
alpha_mode: AlphaMode::Opaque,
..Default::default()
}),
..Default::default()
})
.insert_bundle(pbody)
.insert_bundle(pcollide)
.insert(CyberPlanet);
}
pub struct CyberPlanetPlugin;
impl Plugin for CyberPlanetPlugin {
fn build(&self, app: &mut App) {
app.add_startup_system(spawn_planet.label(Label::Geometry));
}
}
//---------------------------------------------------------------------
// utils
//---------------------------------------------------------------------
fn gen_planet(sphere: Icosphere) -> (Mesh, ColliderShape) {
// 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]
});
let noise = HybridMulti::<SuperSimplex>::default();
let noisy_points = generated
.raw_points()
.iter()
.map(|&p| {
let disp = noise.get(p.as_dvec3().into()) as f32 * 0.05;
let pt = p + (p.normalize() * disp);
pt.into()
})
.collect::<Vec<[f32; 3]>>();
let points = noisy_points
.iter()
.map(|&p| (Vec3::from_slice(&p) * sphere.radius).into())
.collect::<Vec<[f32; 3]>>();
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 mut idxs = Vec::new();
for idx in indices.chunks_exact(3) {
idxs.push([idx[0], idx[1], idx[2]]);
}
let shape = ColliderShape::trimesh(points.iter().map(|p| Point::from_slice(p)).collect(), idxs);
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_UV_0, uvs);
(mesh, shape)
}