cyber_rider/src/geometry.rs

132 lines
3.7 KiB
Rust
Raw Normal View History

use bevy::{
prelude::{shape::Icosphere, *},
render::mesh::Indices,
};
use hexasphere::shapes::IcoSphere;
2022-02-25 07:20:29 +00:00
use noise::{HybridMulti, NoiseFn, Simplex};
use wgpu::PrimitiveTopology;
2022-02-08 21:27:57 +00:00
use crate::Label;
2022-01-14 06:05:51 +00:00
pub const PLANET_RADIUS: f32 = 5000.0;
2022-02-25 07:20:29 +00:00
pub(crate) const SPAWN_ALTITUDE: f32 = PLANET_RADIUS * 1.015;
#[derive(Component, Debug)]
pub struct CyberBike;
2022-01-14 00:14:08 +00:00
#[derive(Component)]
pub struct CyberSphere;
fn spawn_giant_sphere(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
2022-02-21 21:08:49 +00:00
let color = Color::DARK_GRAY;
let isphere = shape::Icosphere {
radius: PLANET_RADIUS,
2022-02-25 07:20:29 +00:00
subdivisions: 64,
};
let pmesh = gen_planet(isphere);
commands
.spawn_bundle(PbrBundle {
mesh: meshes.add(pmesh),
material: materials.add(StandardMaterial {
base_color: color,
2022-02-21 21:08:49 +00:00
metallic: 0.1,
2022-02-09 06:27:41 +00:00
perceptual_roughness: 0.3,
alpha_mode: AlphaMode::Opaque,
..Default::default()
}),
..Default::default()
})
.insert(CyberSphere);
}
2022-01-14 00:14:08 +00:00
fn spawn_cyberbike(mut commands: Commands, asset_server: Res<AssetServer>) {
commands
.spawn_bundle((
Transform {
2022-01-14 00:14:08 +00:00
translation: Vec3::new(SPAWN_ALTITUDE, 0.0, 0.0),
..Default::default()
}
.looking_at(Vec3::new(PLANET_RADIUS, 1000.0, 0.0), Vec3::X),
GlobalTransform::identity(),
))
.with_children(|rider| {
rider.spawn_scene(asset_server.load("cyber-bike_no_y_up.glb#Scene0"));
})
2022-02-08 21:27:57 +00:00
.insert(CyberBike);
2022-01-14 00:14:08 +00:00
}
pub struct CyberGeomPlugin;
impl Plugin for CyberGeomPlugin {
fn build(&self, app: &mut App) {
2022-01-14 06:05:51 +00:00
app.add_startup_system(spawn_giant_sphere.label(Label::Geometry))
.add_startup_system(spawn_cyberbike.label(Label::Geometry));
2022-01-14 00:14:08 +00:00
}
}
//---------------------------------------------------------------------
// 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.
2022-02-25 07:20:29 +00:00
let noise = HybridMulti::<Simplex>::default();
2022-02-21 21:08:49 +00:00
let raw_points = generated
.raw_points()
.iter()
.map(|&p| {
let disp = noise.get(p.as_dvec3().into()) as f32 * 0.05;
2022-02-21 21:08:49 +00:00
let pt = p + disp;
pt.into()
})
.collect::<Vec<[f32; 3]>>();
2022-02-21 21:08:49 +00:00
let points = raw_points
.iter()
.map(|&p| (Vec3::from_slice(&p) * sphere.radius).into())
.collect::<Vec<[f32; 3]>>();
let normals = raw_points
.iter()
.copied()
.map(Into::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 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
}