Last active
April 9, 2021 15:31
-
-
Save HackerFoo/0bdccff4b07c7e6d55285d2593735b7f to your computer and use it in GitHub Desktop.
main.rs for https://youtu.be/dI3QqMydylM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use bevy::{ | |
prelude::*, | |
render::{ | |
pipeline::PrimitiveTopology, | |
wireframe::{Wireframe, WireframeConfig, WireframePlugin}, | |
mesh::{Mesh, Indices}, | |
}, | |
wgpu::{WgpuFeature, WgpuFeatures, WgpuOptions}, | |
}; | |
use facet::{ | |
shapes, | |
data::{ | |
geometric::{ | |
vec, | |
vector::*, | |
manifold::* | |
}, | |
graph | |
}, | |
algorithm::geometric::convex_hull::* | |
}; | |
use std::time::Instant; | |
use rand::Rng; | |
const FPS: f32 = 30.0; | |
const N: u32 = 64; | |
fn main() { | |
App::build() | |
.insert_resource(Msaa { samples: 4 }) | |
.insert_resource(WgpuOptions { | |
features: WgpuFeatures { | |
// The Wireframe requires NonFillPolygonMode feature | |
features: vec![WgpuFeature::NonFillPolygonMode], | |
}, | |
..Default::default() | |
}) | |
.insert_resource(Timer::from_seconds(1.0 / FPS, true)) | |
.add_plugins(DefaultPlugins) | |
.add_plugin(WireframePlugin) | |
.add_startup_system(setup.system()) | |
.add_system(timer.system()) | |
.add_system(rotator_system.system()) | |
.add_system(remesh_system.system()) | |
.run(); | |
} | |
/// set up a simple 3D scene | |
fn setup( | |
mut commands: Commands, | |
mut wireframe_config: ResMut<WireframeConfig>, | |
mut meshes: ResMut<Assets<Mesh>>, | |
mut materials: ResMut<Assets<StandardMaterial>>, | |
) { | |
// To draw the wireframe on all entities, set this to 'true' | |
wireframe_config.global = false; | |
// cube | |
commands | |
.spawn_bundle(PbrBundle { | |
mesh: meshes.add(hull_mesh(shapes::sphere(N, N))), | |
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), | |
transform: Transform::from_xyz(0.0, 0.5, 0.0), | |
..Default::default() | |
}) | |
// This enables wireframe drawing on this entity | |
.insert(Particles::from_points(shapes::sphere(N, N))) | |
.insert(Wireframe) | |
.insert(Rotates); | |
// light | |
commands.spawn_bundle(LightBundle { | |
transform: Transform::from_xyz(0.0, 4.0, 8.0), | |
light: Light { | |
fov: 120.0, | |
..Default::default() | |
}, | |
..Default::default() | |
}); | |
// camera | |
commands.spawn_bundle(PerspectiveCameraBundle { | |
transform: Transform::from_xyz(-4.0, 4.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y), | |
..Default::default() | |
}); | |
} | |
/// this component indicates what entities should rotate | |
struct Rotates; | |
#[derive(Clone, Debug)] | |
struct Particle { | |
pos: [vec::VecT; 2] | |
} | |
#[derive(Clone, Debug)] | |
struct Particles { | |
ps: Vec<Particle> | |
} | |
fn constrain(v: vec::VecT, l: f32) -> vec::VecT { | |
let d = v.dot(v).sqrt(); | |
if d > l { | |
v.scale(l / d) | |
} else { | |
v | |
} | |
} | |
impl Particles { | |
fn from_points<I>(xs: I) -> Self | |
where I: IntoIterator<Item = vec::VecT> | |
{ | |
Particles { | |
ps: xs.into_iter().map(|x| Particle { pos: [x.clone(), x.clone()] }).collect() | |
} | |
} | |
fn positions(&self) -> Vec<vec::VecT> { | |
self.ps.iter().map(|p| p.pos[0].clone()).collect() | |
} | |
// Simple Verlet integration: http://graphics.cs.cmu.edu/nsp/course/15-869/2006/papers/jakobsen.htm | |
fn update(&mut self, dt: f32) { | |
let mut rng = rand::thread_rng(); | |
let s = 2.0; | |
let g = 2.0; | |
let f = 0.125; | |
for Particle { pos } in &mut self.ps { | |
let prev = pos[0]; | |
let accel = vec::Vec3 { | |
x: rng.gen::<f32>() - 0.5, | |
y: rng.gen::<f32>() - 0.5, | |
z: rng.gen::<f32>() - 0.5 | |
}.scale(s) + prev.scale(g * (1.0 - prev.dot(prev).sqrt()).min(1.0)) + (pos[1] - pos[0]).scale(f); | |
pos[0] = constrain(pos[0] + constrain(pos[0] - pos[1] + accel.scale(dt * dt), s * 4.0), 4.0); | |
pos[1] = prev; | |
} | |
} | |
} | |
fn timer(time: Res<Time>, | |
mut timer: ResMut<Timer>) { | |
timer.tick(time.delta()); | |
} | |
fn rotator_system(timer: Res<Timer>, | |
mut query: Query<&mut Transform, With<Rotates>>) { | |
if timer.just_finished() { | |
for mut transform in query.iter_mut() { | |
*transform = Transform::from_rotation(Quat::from_rotation_y( | |
(0.1 * std::f32::consts::PI) * timer.duration().as_secs_f32(), | |
)) * *transform; | |
} | |
} | |
} | |
fn remesh_system(timer: Res<Timer>, | |
mut meshes: ResMut<Assets<Mesh>>, | |
mut query: Query<(&Handle<Mesh>, &mut Particles)>) { | |
if timer.just_finished() { | |
for (mesh, mut particles) in query.iter_mut() { | |
particles.update(timer.duration().as_secs_f32()); | |
*meshes.get_mut(mesh).unwrap() = hull_mesh(particles.positions()); | |
} | |
} | |
} | |
fn face_to_triangles(f: &Vec<graph::Node>) -> Vec<[u32; 3]> { | |
let n = f.len(); | |
let mut r = Vec::new(); | |
if n >= 3 { | |
for i in 1..n-1 { | |
r.push([f[0] as u32, f[i] as u32, f[i+1] as u32]); | |
} | |
} | |
r | |
} | |
fn hull_mesh(vs: Vec<vec::VecT>) -> Mesh { | |
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); | |
let now = Instant::now(); | |
let h = convex_hull(vs); | |
//println!("convex_hull took {}ms", now.elapsed().as_secs_f64() * 1000.0); | |
//let mut indices: Vec<u32> = Vec::new(); | |
let mut positions: Vec<[f32; 3]> = Vec::new(); | |
let mut normals: Vec<[f32; 3]> = Vec::new(); | |
let mut uvs: Vec<[f32; 2]> = Vec::new(); | |
for f in dff(&h) { | |
let v: Vec<vec::VecT> = f.iter().map(|x| *h.lab(*x)).collect(); | |
let n = f_norm(&v); | |
for t in face_to_triangles(&f) { | |
for i in t.iter() { | |
positions.push(h.lab(*i as usize).clone_array()); | |
normals.push(n.clone_array()); | |
uvs.push([0.0, 0.0]); | |
} | |
} | |
} | |
mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); | |
mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); | |
mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); | |
//mesh.set_indices(Some(Indices::U32(indices))); | |
mesh | |
} | |
#[cfg(test)] | |
#[test] | |
fn cube_test() { | |
hull_mesh(shapes::cube()); | |
} | |
#[cfg(test)] | |
#[test] | |
fn sphere_test() { | |
hull_mesh(shapes::sphere(8, 8)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is to show how I used Bevy while testing my convex hull implementation. This is not an open source project, so
facet
is not publicly available.