use bevy::window::WindowResolution;
use bevy::{
color::palettes::css::TOMATO,
mesh::{Indices, PrimitiveTopology, VertexAttributeValues},
prelude::*,
};
use bevy_infinite_grid::{InfiniteGridBundle, InfiniteGridPlugin};
use bevy_normal_material::{material::NormalMaterial, plugin::NormalMaterialPlugin};
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
use bevy_points::{plugin::PointsPlugin, prelude::PointsMaterial};
use itertools::Itertools;
use materials::*;
use nalgebra::{Point2, Point3, Point4, Vector2, Vector3};
use curvo::prelude::*;
use rand::RngExt;
use systems::screenshot_on_spacebar;
mod materials;
mod misc;
mod systems;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
resolution: WindowResolution::new(640, 480),
..Default::default()
}),
..Default::default()
}))
.add_plugins(LineMaterialPlugin)
.add_plugins(InfiniteGridPlugin)
.add_plugins(PanOrbitCameraPlugin)
.add_plugins(PointsPlugin)
.add_plugins(NormalMaterialPlugin)
.add_plugins(AppPlugin)
.run();
}
struct AppPlugin;
impl Plugin for AppPlugin {
fn build(&self, app: &mut bevy::prelude::App) {
app.add_systems(Startup, setup)
.add_systems(Update, screenshot_on_spacebar);
}
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut line_materials: ResMut<Assets<LineMaterial>>,
_points_materials: ResMut<Assets<PointsMaterial>>,
mut normal_materials: ResMut<'_, Assets<NormalMaterial>>,
) {
let add_trimmed_surface = |commands: &mut Commands<'_, '_>,
meshes: &mut ResMut<'_, Assets<Mesh>>,
normal_materials: &mut ResMut<'_, Assets<NormalMaterial>>,
surface: &TrimmedSurface<f64>,
transform: Transform| {
let option = AdaptiveTessellationOptions::<_>::default().with_norm_tolerance(1e-2);
let tess = surface.tessellate(Some(option)).unwrap().cast::<f32>();
let vertices = tess.points().iter().map(|pt| (*pt).into()).collect();
let normals = tess.normals().iter().map(|n| (*n).into()).collect();
let uvs = tess.uvs().iter().map(|uv| (*uv).into()).collect();
let indices = tess
.faces()
.iter()
.flat_map(|f| f.iter().map(|i| *i as u32))
.collect();
let mesh = Mesh::new(PrimitiveTopology::TriangleList, default())
.with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::Float32x3(vertices),
)
.with_inserted_attribute(
Mesh::ATTRIBUTE_NORMAL,
VertexAttributeValues::Float32x3(normals),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, VertexAttributeValues::Float32x2(uvs))
.with_inserted_indices(Indices::U32(indices));
commands.spawn((
Mesh3d(meshes.add(mesh)),
MeshMaterial3d(normal_materials.add(NormalMaterial {
cull_mode: None,
..Default::default()
})),
transform,
));
};
let add_surface = |commands: &mut Commands<'_, '_>,
meshes: &mut ResMut<'_, Assets<Mesh>>,
normal_materials: &mut ResMut<'_, Assets<NormalMaterial>>,
surface: &NurbsSurface3D<f64>,
transform: Transform| {
let option = AdaptiveTessellationOptions::<_>::default().with_norm_tolerance(1e-2);
let tess = surface.tessellate(Some(option)).cast::<f32>();
let vertices = tess.points().iter().map(|pt| (*pt).into()).collect();
let normals = tess.normals().iter().map(|n| (*n).into()).collect();
let uvs = tess.uvs().iter().map(|uv| (*uv).into()).collect();
let indices = tess
.faces()
.iter()
.flat_map(|f| f.iter().map(|i| *i as u32))
.collect();
let mesh = Mesh::new(PrimitiveTopology::TriangleList, default())
.with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::Float32x3(vertices),
)
.with_inserted_attribute(
Mesh::ATTRIBUTE_NORMAL,
VertexAttributeValues::Float32x3(normals),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, VertexAttributeValues::Float32x2(uvs))
.with_inserted_indices(Indices::U32(indices));
commands.spawn((
Mesh3d(meshes.add(mesh)),
MeshMaterial3d(normal_materials.add(NormalMaterial {
cull_mode: None,
..Default::default()
})),
transform,
));
};
let add_curve = |commands: &mut Commands<'_, '_>,
meshes: &mut ResMut<'_, Assets<Mesh>>,
line_materials: &mut ResMut<'_, Assets<LineMaterial>>,
curve: &NurbsCurve3D<f64>,
transform: Transform| {
let (min, max) = curve.knots_domain();
let interval = max - min;
let step = interval / 32.;
let mut t = min;
let mut vertices: Vec<Vec3> = vec![];
while t <= max {
let pt = curve.point_at(t).cast::<f32>();
vertices.push(pt.into());
t += step;
}
let samples = curve.tessellate(Some(1e-8));
let mut line = Mesh::new(bevy::mesh::PrimitiveTopology::LineStrip, default());
let line_vertices = samples
.iter()
.map(|p| p.cast::<f32>())
.map(|p| [p.x, p.y, p.z])
.collect();
line.insert_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::Float32x3(line_vertices),
);
commands.spawn((
Mesh3d(meshes.add(line)),
MeshMaterial3d(line_materials.add(LineMaterial {
color: TOMATO.into(),
..Default::default()
})),
transform,
));
};
let plane_surface = NurbsSurface::plane(
Point3::new(2.5, 0., 5.),
Vector3::x() * 2.5,
Vector3::z() * 5.,
);
let trimmed = TrimmedSurface::new(
plane_surface.clone(),
None,
vec![
NurbsCurve2D::try_circle(&Point2::new(0.5, 0.5), &Vector2::x(), &Vector2::y(), 0.25)
.unwrap()
.into(),
],
);
let offset = 5.0;
let trimming_curve = NurbsCurve3D::try_circle(
&Point3::new(2.5, 5.0, 2.5),
&Vector3::x(),
&Vector3::z(),
1.,
)
.unwrap();
let trimmed2 = TrimmedSurface::try_projection(
plane_surface.clone(),
-Vector3::y(),
Some(trimming_curve.clone()),
vec![],
)
.unwrap();
add_trimmed_surface(
&mut commands,
&mut meshes,
&mut normal_materials,
&trimmed,
Transform::default(),
);
let transform = Transform::from_translation(Vec3::new(offset, 0., 0.));
add_curve(
&mut commands,
&mut meshes,
&mut line_materials,
&trimming_curve,
transform,
);
add_trimmed_surface(
&mut commands,
&mut meshes,
&mut normal_materials,
&trimmed2,
transform,
);
let transform = Transform::from_translation(Vec3::new(offset * 2., 0., 0.));
let degree = 3;
let n: usize = 6;
let goal = n + degree + 1;
let knots = KnotVector::uniform(goal - degree * 2, degree);
let _hn = (n - 1) as f64 / 2.;
let mut rng: rand::rngs::StdRng = rand::SeedableRng::from_seed([0; 32]);
let pts = (0..n)
.map(|i| {
(0..n)
.map(|j| {
let x = i as f64;
let z = (rng.random::<f64>() - 0.5) * 2.;
let y = j as f64;
Point4::new(x, z, y, 1.)
})
.collect_vec()
})
.collect_vec();
let wavy_surface = NurbsSurface3D::new(degree, degree, knots.to_vec(), knots.to_vec(), pts);
let trimmed3 = TrimmedSurface::try_projection(
wavy_surface.clone(),
-Vector3::y(),
Some(trimming_curve.clone()),
vec![],
)
.unwrap();
add_curve(
&mut commands,
&mut meshes,
&mut line_materials,
&trimming_curve,
transform,
);
add_surface(
&mut commands,
&mut meshes,
&mut normal_materials,
&wavy_surface,
transform,
);
add_trimmed_surface(
&mut commands,
&mut meshes,
&mut normal_materials,
&trimmed3,
transform * Transform::from_translation(Vec3::new(0., 1.5, 0.)),
);
commands.spawn((
Transform::from_translation(Vec3::new(18., 18., 18.)),
PanOrbitCamera::default(),
));
commands.spawn(InfiniteGridBundle::default());
}