Skip to content

Instantly share code, notes, and snippets.

@mxgrey
Created March 6, 2023 14:52
Show Gist options
  • Save mxgrey/9f60f7e7319204f3a22a17606ad4bf55 to your computer and use it in GitHub Desktop.
Save mxgrey/9f60f7e7319204f3a22a17606ad4bf55 to your computer and use it in GitHub Desktop.
#![feature(array_windows)]
use std::f32::consts::PI;
use bevy::{
prelude::*,
render::mesh::{PrimitiveTopology, Indices},
window::close_on_esc,
};
use bevy_mod_outline::*;
#[bevy_main]
fn main() {
App::new()
.insert_resource(Msaa { samples: 4 })
.insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins)
.add_plugin(OutlinePlugin)
.add_startup_system(setup)
.add_system(oscillate)
.add_system(close_on_esc)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let mut flat_mesh = Mesh::new(PrimitiveTopology::TriangleList);
let positions: Vec<_> = [
[-1.0, 0.0, 0.0],
[-1.0, 1.0, 0.0],
[1.0, 1.0, 0.0],
[2.5, 0.0, 0.0],
].into_iter().collect();
let normals: Vec<_> = [[0.0, 0.0, 1.0]].into_iter().cycle().take(4).collect();
let indices: Vec<_> = [0, 2, 1, 0, 3, 2].into_iter().collect();
let (positions, normals, indices) = outline_sharp_edges(positions, normals, indices);
dbg!(&indices);
flat_mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
flat_mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
flat_mesh.set_indices(Some(Indices::U32(indices)));
commands
.spawn(PbrBundle {
mesh: meshes.add(flat_mesh),
material: materials.add(Color::CYAN.into()),
..default()
})
.insert(OutlineBundle {
outline: OutlineVolume {
visible: true,
width: 5.0,
colour: Color::RED,
},
..default()
})
.insert(SetOutlineDepth::Flat { model_origin: Vec3::ZERO });
commands
.spawn(PointLightBundle {
point_light: PointLight {
intensity: 1500.0,
..default()
},
transform: Transform::from_xyz(0.0, 0.0, 3.0),
..default()
});
commands
.spawn(SpatialBundle::default())
.insert(Oscillate {
lower: -PI * 30.0 / 180.0,
upper: PI * 30.0 / 180.0,
rate: 3.0 / (2.0 * PI),
// lower: -1.1 * PI,
// upper: 1.1 * PI,
// rate: 1.0 / (2.0 * PI),
axis: Vec3::X,
})
.with_children(|p| {
p.spawn(Camera3dBundle {
// transform: Transform::from_xyz(0.0, -1.0, 10.0).looking_at(Vec3::ZERO, Vec3::Z),
transform: Transform::from_xyz(0.0, 0.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
// transform: Transform::from_xyz(15.0, -1.0, 10.0).looking_at(Vec3::new(15.0, -1.0, 0.0), Vec3::Y),
..default()
});
});
}
fn outline_sharp_edges(
mut initial_positions: Vec<[f32; 3]>,
initial_normals: Vec<[f32; 3]>,
initial_indices: Vec<u32>,
) -> (Vec<[f32; 3]>, Vec<[f32; 3]>, Vec<u32>) {
let initial_index = initial_positions.len() as u32;
let mut positions = initial_positions.clone();
let mut normals = initial_normals;
let mut indices = initial_indices;
// Close the loop by repeating the first and last values at the end and start, respectively
if let Some(pf) = initial_positions.last() {
let pf = *pf;
if let Some(pi) = initial_positions.first() {
let pi = *pi;
initial_positions.push(pi);
}
initial_positions.insert(0, pf);
}
for (i, [p0, p1, p2]) in initial_positions.array_windows::<3>().enumerate() {
let i = i as u32;
let p = *p1;
let p0 = Vec3::new(p0[0], p0[1], 0.0);
let p1 = Vec3::new(p1[0], p1[1], 0.0);
let p2 = Vec3::new(p2[0], p2[1], 0.0);
let v0 = match (p1 - p0).try_normalize() {
Some(v) => v,
None => continue,
};
let v1 = match (p2 - p1).try_normalize() {
Some(v) => v,
None => continue,
};
// n: normal
let n = Vec3::Z;
let u = n.cross(v0).normalize();
let w = n.cross(v1).normalize();
// b: bisector
let b = match (u + w).try_normalize() {
Some(b) => b,
None => {
// This means that u and w are pointing in opposite directions,
// so the next vertex is in a perfect 180 back towards the
// previous vertex. We can simply use v0 as the bisecting
// vector.
v0
}
};
positions.extend([p, p, p, p, p, p, p, p]);
normals.extend([u, -u, w, -w, b, -b, n, -n].map(Into::<[f32; 3]>::into));
let u0 = 0;
let u1 = 1;
let w0 = 2;
let w1 = 3;
let b0 = 4;
let b1 = 5;
let n0 = 6;
let n1 = 7;
let i_delta = 8;
// Current base index
let c = initial_index + i_delta*i;
// Next base index
let f = if i == initial_index-1 {
// We have reached the last element of the original vertex set
initial_index
} else {
initial_index + i_delta*(i+1)
};
dbg!(c, f);
if w.cross(b).dot(n) < 0.0 {
// left turn
indices.extend([
c+u1, c+b1, c+n0, c+b1, c+w1, c+n0,
c+u1, c+n1, c+b1, c+b1, c+n1, c+w1,
]);
} else {
// right turn
indices.extend([
c+u0, c+n0, c+b0, c+b0, c+n0, c+w0,
c+u0, c+b0, c+n1, c+b0, c+w0, c+n1,
]);
}
indices.extend([
c+w0, c+n0, f+n0, c+w0, f+n0, f+u0,
c+w1, f+n0, c+n0, c+w1, f+u1, f+n0,
c+w0, f+u0, f+n1, c+w0, f+n1, c+n1,
c+w1, f+n1, f+u1, c+w1, c+n1, f+n1,
]);
}
(positions, normals, indices)
}
#[derive(Component)]
struct Oscillate {
lower: f32,
upper: f32,
rate: f32,
axis: Vec3,
}
fn oscillate(
mut oscillating: Query<(&mut Transform, &Oscillate)>,
time: Res<Time>,
) {
for (mut tf, osc) in &mut oscillating {
let angle = (osc.upper - osc.lower) * (osc.rate * time.elapsed_seconds()).cos() + osc.lower;
tf.rotation = Quat::from_axis_angle(osc.axis, angle);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment