Skip to content

Instantly share code, notes, and snippets.

@HackerFoo
Last active April 9, 2021 15:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HackerFoo/0bdccff4b07c7e6d55285d2593735b7f to your computer and use it in GitHub Desktop.
Save HackerFoo/0bdccff4b07c7e6d55285d2593735b7f to your computer and use it in GitHub Desktop.
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));
}
@HackerFoo
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment