Skip to content

Commit

Permalink
Merge pull request #42 from mattatz/constrained-surface-tessellation
Browse files Browse the repository at this point in the history
0.1.43
  • Loading branch information
mattatz authored Jan 9, 2025
2 parents 9a02904 + 1909e3f commit 10eedbf
Show file tree
Hide file tree
Showing 28 changed files with 1,891 additions and 694 deletions.
398 changes: 184 additions & 214 deletions Cargo.lock

Large diffs are not rendered by default.

33 changes: 18 additions & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "curvo"
version = "0.1.42"
version = "0.1.43"
authors = ["Masatatsu Nakamura <masatatsu.nakamura@gmail.com"]
edition = "2021"
keywords = ["nurbs", "modeling", "graphics", "3d"]
Expand All @@ -12,7 +12,7 @@ license = "MIT"
readme = "README.md"

[dependencies]
anyhow = "1.0.94"
anyhow = "1.0.95"
nalgebra = { version = "0.33.2", features = [
"serde-serialize"
] }
Expand All @@ -23,25 +23,24 @@ robust = { version = "1.1.0" }
easer = { version = "0.3.0", optional = true }
simba = { version = "0.9.0", default-features = false }
spade = { version = "2.12.1" }
gauss-quad = "0.2.1"
bevy = { version = "0.15.0", optional = true }
bevy_egui = { version = "0.31.1", optional = true }
bevy-inspector-egui = { version = "0.28.0", optional = true }
gauss-quad = "0.2.2"
bevy = { version = "0.15.1", optional = true }
bevy_egui = { version = "0.32.0", optional = true }
bevy_infinite_grid = { version = "0.14.0", optional = true }
bevy_normal_material = { version = "0.7.1", optional = true }
bevy_panorbit_camera = { version = "0.21.1", optional = true }
bevy_panorbit_camera = { version = "0.21.2", optional = true }
bevy_points = { version = "0.7.0", optional = true }
argmin = "0.10.0"
itertools = "0.13.0"
itertools = "0.14.0"
log = { version = "0.4.22", optional = true }
serde = { version = "1.0.216", optional = true }
serde = { version = "1.0.217", optional = true }

[target.wasm32-unknown-unknown.dependencies]
argmin = { version = "0.10.0", features = ["wasm-bindgen"] }

[dev-dependencies]
approx = { version = "0.5", default-features = false }
serde_json = "1.0.133"
serde_json = "1.0.135"

[features]
default = []
Expand All @@ -51,7 +50,6 @@ bevy = [
"dep:rand_distr",
"dep:bevy",
"dep:bevy_egui",
"dep:bevy-inspector-egui",
"dep:bevy_infinite_grid",
"dep:bevy_normal_material",
"dep:bevy_panorbit_camera",
Expand Down Expand Up @@ -106,10 +104,10 @@ name = "revolve_surface"
path = "examples/revolve_surface.rs"
required-features = ["bevy"]

# [[example]]
# name = "trimmed_surface"
# path = "examples/trimmed_surface.rs"
# required-features = ["bevy"]
[[example]]
name = "trimmed_surface"
path = "examples/trimmed_surface.rs"
required-features = ["bevy"]

[[example]]
name = "frenet_frame"
Expand Down Expand Up @@ -171,3 +169,8 @@ name = "split_surface"
path = "examples/split_surface.rs"
required-features = ["bevy"]

[[example]]
name = "constrained_surface_tessellation"
path = "examples/constrained_surface_tessellation.rs"
required-features = ["bevy"]

2 changes: 0 additions & 2 deletions examples/boolean_curves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use bevy::{
};
use bevy_infinite_grid::InfiniteGridPlugin;

use bevy_inspector_egui::quick::WorldInspectorPlugin;
use bevy_normal_material::{material::NormalMaterial, plugin::NormalMaterialPlugin};
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
use bevy_points::{mesh::PointsMesh, plugin::PointsPlugin, prelude::PointsMaterial};
Expand Down Expand Up @@ -37,7 +36,6 @@ fn main() {
.add_plugins(PanOrbitCameraPlugin)
.add_plugins(PointsPlugin)
.add_plugins(NormalMaterialPlugin)
.add_plugins(WorldInspectorPlugin::new())
.add_plugins(AppPlugin)
.run();
}
Expand Down
205 changes: 205 additions & 0 deletions examples/constrained_surface_tessellation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
use std::f64::consts::FRAC_PI_2;

use bevy::{
color::palettes::css::{RED, TOMATO},
prelude::*,
render::{
camera::ScalingMode,
mesh::{Indices, PrimitiveTopology, VertexAttributeValues},
},
};
use bevy_infinite_grid::InfiniteGridPlugin;

use bevy_normal_material::{material::NormalMaterial, plugin::NormalMaterialPlugin};
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
use bevy_points::{
material::PointsShaderSettings, mesh::PointsMesh, plugin::PointsPlugin, prelude::PointsMaterial,
};
use itertools::Itertools;
use materials::*;
use misc::{add_surface, add_surface_normals};
use nalgebra::{Point3, Rotation3, Translation3, Vector3};

use curvo::prelude::*;
mod materials;
mod misc;

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.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);
}
}

fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut line_materials: ResMut<Assets<LineMaterial>>,
mut points_materials: ResMut<Assets<PointsMaterial>>,
mut normal_materials: ResMut<'_, Assets<NormalMaterial>>,
) {
let interpolation_target = vec![
Point3::new(-1.0, -1.0, 0.),
Point3::new(1.0, -1.0, 0.),
Point3::new(1.0, 1.0, 0.),
Point3::new(-1.0, 1.0, 0.),
Point3::new(-1.0, 2.0, 0.),
Point3::new(1.0, 2.5, 0.),
];
let interpolated = NurbsCurve3D::<f64>::try_interpolate(&interpolation_target, 3).unwrap();

let rotation = Rotation3::from_axis_angle(&Vector3::z_axis(), FRAC_PI_2);
let translation = Translation3::new(0., 0., 1.5);
let m = translation * rotation;
let front = interpolated.transformed(&(translation.inverse()).into());
let back = interpolated.transformed(&m.into());

let (umin, umax) = front.knots_domain();

let surface = NurbsSurface::try_loft(&[front, back], Some(3)).unwrap();

let u_parameters = vec![
umin,
(umin + umax) * 0.25,
(umin + umax) * 0.5,
(umin + umax) * 0.75,
umax,
];
let vmin = surface.v_knots_domain().0;

let boundary = BoundaryConstraints::default().with_u_parameters_at_v_min(u_parameters.clone());

commands.spawn((
Mesh3d(
meshes.add(PointsMesh {
vertices: u_parameters
.iter()
.map(|u| surface.point_at(*u, vmin).cast::<f32>().into())
.collect_vec(),
..Default::default()
}),
),
MeshMaterial3d(points_materials.add(PointsMaterial {
settings: PointsShaderSettings {
point_size: 0.05,
color: TOMATO.into(),
..Default::default()
},
circle: true,
..Default::default()
})),
));

// let tess = surface.tessellate(Some(Default::default()));
let option = AdaptiveTessellationOptions {
..Default::default()
};
let tess = surface.constrained_tessellate(boundary, Some(option));
let tess = tess.cast::<f32>();

let vertices = tess.points().iter().map(|pt| (*pt).into()).collect_vec();
commands.spawn((
Mesh3d(meshes.add(PointsMesh {
vertices: vertices.clone(),
..Default::default()
})),
MeshMaterial3d(points_materials.add(PointsMaterial {
settings: PointsShaderSettings {
point_size: 0.02,
color: Color::WHITE.into(),
..Default::default()
},
circle: true,
..Default::default()
})),
));

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_vec();

let mesh = Mesh::new(PrimitiveTopology::LineList, default()).with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
// triangle edges
VertexAttributeValues::Float32x3(
indices
.chunks(3)
.flat_map(|idx| {
[
vertices[idx[0] as usize].into(),
vertices[idx[1] as usize].into(),
vertices[idx[1] as usize].into(),
vertices[idx[2] as usize].into(),
vertices[idx[2] as usize].into(),
vertices[idx[0] as usize].into(),
]
})
.collect_vec(),
),
);
commands.spawn((
Mesh3d(meshes.add(mesh)),
MeshMaterial3d(line_materials.add(LineMaterial {
color: Color::WHITE.into(),
..Default::default()
})),
));

let mesh = Mesh::new(PrimitiveTopology::TriangleList, default())
.with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::Float32x3(vertices.into_iter().map(|v| v.into()).collect()),
)
.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()
})),
));

let (umin, umax) = surface.u_knots_domain();
let (vmin, vmax) = surface.v_knots_domain();
let center = surface
.point_at((umax + umin) * 0.5, (vmax + vmin) * 0.5)
.cast::<f32>()
.into();

let scale = 5.;
commands.spawn((
Projection::Orthographic(OrthographicProjection {
scale,
near: 1e-1,
far: 1e4,
scaling_mode: ScalingMode::FixedVertical {
viewport_height: 2.,
},
..OrthographicProjection::default_3d()
}),
Transform::from_translation(center + Vec3::new(0., 0., 3.)).looking_at(center, Vec3::Y),
PanOrbitCamera::default(),
));
}
1 change: 0 additions & 1 deletion examples/intersect_curves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ fn main() {
.add_plugins(PanOrbitCameraPlugin)
.add_plugins(PointsPlugin)
.add_plugins(AppPlugin)
// .add_plugins(WorldInspectorPlugin::new())
.run();
}
struct AppPlugin;
Expand Down
21 changes: 0 additions & 21 deletions examples/misc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,27 +121,6 @@ pub fn add_surface(
let tess = surface.tessellate(Some(option));
let tess = tess.cast::<f32>();

let mut line_list = Mesh::new(bevy::render::mesh::PrimitiveTopology::LineList, default());
let normal_length = 0.15;
let normals = tess.normals();

let vertices = tess
.points()
.iter()
.enumerate()
.flat_map(|(i, p)| {
let pt: Vec3 = (*p).into();
let normal: Vec3 = normals[i].normalize().into();
[pt, pt + normal * normal_length]
})
.map(|p| p.to_array())
.collect();

line_list.insert_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::Float32x3(vertices),
);

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();
Expand Down
Loading

0 comments on commit 10eedbf

Please sign in to comment.