Compare commits
5 Commits
783d4999a7
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 85b79a86ed | |||
| 78222ad55a | |||
| 6dc8e2a47f | |||
| a0007956f1 | |||
| cf44341c22 |
11
Cargo.toml
11
Cargo.toml
@@ -12,8 +12,11 @@ readme = "README.md"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = { version = "0.12.1", default-features = true}
|
bevy = { version = "0.12.1", default-features = true}
|
||||||
|
|
||||||
fj-math = "0.47.0"
|
|
||||||
fj-interop = "0.47.0"
|
|
||||||
fj-core = "0.47.0"
|
|
||||||
fj-export = "0.47.0"
|
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
||||||
|
fj-math = "0.48.0"
|
||||||
|
fj-interop = "0.48.0"
|
||||||
|
fj-core = "0.48.0"
|
||||||
|
fj-export = "0.48.0"
|
||||||
|
|
||||||
|
|||||||
BIN
fonts/FiraSans-Bold.ttf
Normal file
BIN
fonts/FiraSans-Bold.ttf
Normal file
Binary file not shown.
60
src/interop.rs
Normal file
60
src/interop.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
use bevy::prelude::{Component, Reflect, Resource};
|
||||||
|
use fj_core::objects::{Sketch, Solid};
|
||||||
|
use fj_math::{Point, Vector};
|
||||||
|
use fj_interop::mesh::Mesh as FjMesh;
|
||||||
|
use fj_core::storage::Handle as FjHandle;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct FjSolidWrapper {
|
||||||
|
handle: fj_core::storage::Handle<Solid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct FjMeshWrapper {
|
||||||
|
pub mesh: FjMesh<Point<3>>,
|
||||||
|
pub handle: FjHandle<Solid>,
|
||||||
|
pub sketch: Sketch,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait ToPoint3 {
|
||||||
|
fn to_point3(&self) -> Point<3>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ToPoint3 for bevy::prelude::Vec3 {
|
||||||
|
fn to_point3(&self) -> fj_math::Point<3> {
|
||||||
|
fj_math::Point { coords: Vector { components: [fj_math::Scalar::from_u64(0),fj_math::Scalar::from_u64(0),fj_math::Scalar::from_u64(0)] } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait ToVec3 {
|
||||||
|
fn to_vec3(&self) -> bevy::prelude::Vec3;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToVec3 for fj_math::Point<3> {
|
||||||
|
fn to_vec3(&self) -> bevy::prelude::Vec3 {
|
||||||
|
bevy::prelude::Vec3::new(self.x.into_f32(), self.y.into_f32(), self.z.into_f32())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToVec3 for fj_math::Vector<3> {
|
||||||
|
fn to_vec3(&self) -> bevy::prelude::Vec3 {
|
||||||
|
bevy::prelude::Vec3::new(self.x.into_f32(), self.y.into_f32(), self.z.into_f32())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait ToVec2 {
|
||||||
|
fn to_vec2(&self) -> bevy::prelude::Vec2;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::interop::ToVec2 for fj_math::Point<2> {
|
||||||
|
fn to_vec2(&self) -> bevy::prelude::Vec2 {
|
||||||
|
bevy::prelude::Vec2::new(self.u.into_f32(), self.v.into_f32())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::interop::ToVec2 for fj_math::Vector<2> {
|
||||||
|
fn to_vec2(&self) -> bevy::prelude::Vec2 {
|
||||||
|
bevy::prelude::Vec2::new(self.u.into_f32(), self.v.into_f32())
|
||||||
|
}
|
||||||
|
}
|
||||||
420
src/lib.rs
420
src/lib.rs
@@ -1,12 +1,17 @@
|
|||||||
|
pub mod mesh;
|
||||||
|
pub mod interop;
|
||||||
|
|
||||||
// Importing Bevy ECS components and systems
|
// Importing Bevy ECS components and systems
|
||||||
|
use crate::mesh::create_rectangle_mesh;
|
||||||
use bevy::ecs::component::Component;
|
use bevy::ecs::component::Component;
|
||||||
use bevy::ecs::system::{Commands, Query};
|
use bevy::ecs::system::{Commands, Query};
|
||||||
use bevy::ecs::entity::Entity;
|
use bevy::ecs::entity::Entity;
|
||||||
use bevy::ecs::query::Without;
|
use bevy::ecs::query::Without;
|
||||||
use bevy::ecs::system::ResMut;
|
use bevy::ecs::system::ResMut;
|
||||||
|
use crate::interop::{ToPoint3, ToVec3};
|
||||||
|
|
||||||
// Importing Bevy assets and rendering related components
|
// Importing Bevy assets and rendering related components
|
||||||
use bevy::prelude::{Mesh, shape};
|
use bevy::prelude::*;
|
||||||
use bevy::render::mesh::{Indices, PrimitiveTopology};
|
use bevy::render::mesh::{Indices, PrimitiveTopology};
|
||||||
use bevy::asset::Assets;
|
use bevy::asset::Assets;
|
||||||
use bevy::pbr::{PbrBundle, StandardMaterial};
|
use bevy::pbr::{PbrBundle, StandardMaterial};
|
||||||
@@ -18,51 +23,29 @@ use bevy::utils::default;
|
|||||||
use bevy::app::{App, Plugin, Update};
|
use bevy::app::{App, Plugin, Update};
|
||||||
|
|
||||||
// Importing Fj-core functionalities
|
// Importing Fj-core functionalities
|
||||||
use fj_core::algorithms::approx::Tolerance;
|
|
||||||
use fj_core::algorithms::bounding_volume::BoundingVolume;
|
|
||||||
use fj_core::algorithms::sweep::Sweep;
|
|
||||||
use fj_core::algorithms::triangulate::Triangulate;
|
use fj_core::algorithms::triangulate::Triangulate;
|
||||||
use fj_core::objects::{Cycle, Region, Shell, Sketch, Solid};
|
use fj_core::objects::{Sketch, Solid};
|
||||||
use fj_core::operations::{BuildCycle, BuildRegion, BuildSketch, Insert, Reverse, UpdateRegion, UpdateSketch};
|
|
||||||
use fj_core::services::Services;
|
|
||||||
use fj_core::storage::Handle as FjHandle;
|
use fj_core::storage::Handle as FjHandle;
|
||||||
|
|
||||||
// Importing Fj-interop mesh
|
// Importing Fj-interop mesh
|
||||||
use fj_interop::mesh::Mesh as FjMesh;
|
use fj_interop::mesh::Mesh as FjMesh;
|
||||||
|
|
||||||
// Importing Fj-math and other standard functionalities
|
// Importing Fj-math and other standard functionalities
|
||||||
use fj_math::{Aabb, Point, Scalar, Vector};
|
|
||||||
|
use fj_math::{Aabb, Line, Point, Scalar, Vector};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use bevy::a11y::accesskit::Size;
|
||||||
use bevy::math::Vec3;
|
use bevy::math::Vec3;
|
||||||
use fj_core::geometry::curve::GlobalPath;
|
use bevy::ui::ZIndex::Global;
|
||||||
|
use fj_core::geometry::{GlobalPath, SurfacePath};
|
||||||
|
use fj_core::objects::Object::Surface;
|
||||||
|
use fj_core::operations::holes::AddHole;
|
||||||
|
use fj_core::operations::split::{SplitEdge, SplitFace, SplitHalfEdge};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use crate::interop::FjMeshWrapper;
|
||||||
|
use crate::mesh::{convert_mesh, LineList};
|
||||||
|
|
||||||
trait ToVec3 {
|
|
||||||
fn to_vec3(&self) -> bevy::prelude::Vec3;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToVec3 for fj_math::Point<3> {
|
|
||||||
fn to_vec3(&self) -> bevy::prelude::Vec3 {
|
|
||||||
bevy::prelude::Vec3::new(self.x.into_f32(), self.y.into_f32(), self.z.into_f32())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToVec3 for fj_math::Vector<3> {
|
|
||||||
fn to_vec3(&self) -> bevy::prelude::Vec3 {
|
|
||||||
bevy::prelude::Vec3::new(self.x.into_f32(), self.y.into_f32(), self.z.into_f32())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
struct FjSolidWrapper{
|
|
||||||
handle: fj_core::storage::Handle<Solid>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
|
||||||
pub struct FjMeshWrapper {
|
|
||||||
pub mesh: FjMesh<Point<3>>,
|
|
||||||
pub handle: FjHandle<Solid>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct FjConvertedFlag;
|
struct FjConvertedFlag;
|
||||||
@@ -75,196 +58,261 @@ impl Plugin for FjRenderPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn generate_uv_mapping(fj_mesh: &FjMesh<Point<3>>) -> Vec<[f32; 2]> {
|
|
||||||
let mut uvs = Vec::new();
|
|
||||||
|
|
||||||
for vertex in fj_mesh.vertices() {
|
|
||||||
let x = vertex.coords.x.into_f32();
|
|
||||||
let y = vertex.coords.y.into_f32();
|
|
||||||
// Here we're using x and y coordinates as u and v
|
|
||||||
uvs.push([x, y]);
|
|
||||||
uvs.push([x, y]);
|
|
||||||
uvs.push([x, y]);
|
|
||||||
uvs.push([x, y]);
|
|
||||||
uvs.push([x, y]);
|
|
||||||
uvs.push([x, y]);
|
|
||||||
}
|
|
||||||
uvs
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_normals(fj_mesh: &FjMesh<Point<3>>) -> Vec<[f32; 3]> {
|
|
||||||
let mut normals = Vec::new();
|
|
||||||
for triangle in fj_mesh.triangles() {
|
|
||||||
let normal = triangle.inner.normal();
|
|
||||||
normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]);
|
|
||||||
normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]);
|
|
||||||
normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]);
|
|
||||||
}
|
|
||||||
normals
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_positions(fj_mesh: &FjMesh<Point<3>>) -> Vec<[f32; 3]> {
|
|
||||||
let mut positions = Vec::new();
|
|
||||||
|
|
||||||
// Iterate through each triangle
|
|
||||||
for triangle in fj_mesh.triangles() {
|
|
||||||
// For each vertex index in the triangle
|
|
||||||
for vertex_index in triangle.inner.points() {
|
|
||||||
positions.push([vertex_index.x.into_f32(), vertex_index.y.into_f32(), vertex_index.z.into_f32()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
positions
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_indices(fj_mesh: &FjMesh<Point<3>>) -> Vec<u32> {
|
|
||||||
let mut num_positions = 0;
|
|
||||||
|
|
||||||
// Count the total number of positions (3 per triangle)
|
|
||||||
for triangle in fj_mesh.triangles() {
|
|
||||||
num_positions += 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the indices [0, 1, 2, ..., num_positions-1]
|
|
||||||
(0..num_positions as u32).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to take a solid, and return all the data needed to create a Bevy Mesh from scratch
|
|
||||||
pub fn convert_mesh(fj_mesh: &FjMesh<Point<3>>) -> Mesh {
|
|
||||||
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
|
||||||
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, generate_positions(&fj_mesh));
|
|
||||||
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, generate_normals(&fj_mesh));
|
|
||||||
// mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, generate_uv_mapping(&fj_mesh));
|
|
||||||
mesh.set_indices(Some(Indices::U32(generate_indices(&fj_mesh))));
|
|
||||||
mesh
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_fj_model_system(
|
fn update_fj_model_system(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
query: Query<(Entity, &FjMeshWrapper), Without<FjConvertedFlag>>,
|
query: Query<(Entity, &FjMeshWrapper), Without<FjConvertedFlag>>,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
asset_server: Res<AssetServer>
|
||||||
) {
|
) {
|
||||||
|
|
||||||
for (entity, solid) in &query {
|
for (entity, solid) in &query {
|
||||||
|
|
||||||
let bevy_mesh = convert_mesh(&solid.mesh);
|
let bevy_mesh = convert_mesh(&solid.mesh);
|
||||||
commands.entity(entity).insert(
|
commands.entity(entity).insert(
|
||||||
(
|
(
|
||||||
PbrBundle {
|
PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(bevy_mesh.clone())),
|
mesh: meshes.add(Mesh::from(bevy_mesh.clone())),
|
||||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
||||||
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
FjConvertedFlag
|
FjConvertedFlag
|
||||||
|
));
|
||||||
));
|
|
||||||
add_debug_info_to_entity(&mut commands, solid, &mut meshes, &mut materials);
|
add_debug_info_to_entity(&mut commands, solid, &mut meshes, &mut materials);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn add_debug_info_to_entity(mut commands: &mut Commands,
|
fn add_debug_info_to_entity(mut commands: &mut Commands,
|
||||||
fj_model: &FjMeshWrapper,
|
fj_model: &FjMeshWrapper,
|
||||||
mut meshes: &mut ResMut<Assets<Mesh>>,
|
mut meshes: &mut ResMut<Assets<Mesh>>,
|
||||||
mut materials: &mut ResMut<Assets<StandardMaterial>>
|
mut materials: &mut ResMut<Assets<StandardMaterial>>,
|
||||||
) {
|
) {
|
||||||
|
// for region in fj_model.sketch.regions() {
|
||||||
|
// for cycle in region.all_cycles() {
|
||||||
|
// for half_edge in cycle.half_edges() {
|
||||||
|
// match half_edge.path() {
|
||||||
|
// SurfacePath::Circle(x) => {}
|
||||||
|
// SurfacePath::Line(x) => {
|
||||||
|
// render_line(commands, fj_model, meshes, materials, x)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // 1d point
|
||||||
|
// half_edge.boundary().inner[0].coords;
|
||||||
|
// half_edge.boundary().inner[1].coords;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
for shell in fj_model.handle.shells(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for shell in fj_model.handle.shells() {
|
for shell in fj_model.handle.shells() {
|
||||||
println!("{:?}. shell", shell);
|
println!("{:?}. shell", shell);
|
||||||
for face in shell.faces() {
|
for face in shell.faces() {
|
||||||
|
|
||||||
println!("{:?}. face", face);
|
println!("{:?}. face", face);
|
||||||
let surface = face.surface();
|
let surface = face.surface();
|
||||||
let geom = surface.geometry();
|
let geom = surface.geometry();
|
||||||
|
|
||||||
let geom_sweep_vector = geom.v;
|
let geom_sweep_vector = geom.v;
|
||||||
|
|
||||||
|
for edge in face.region().exterior().half_edges(){
|
||||||
|
match edge.path() {
|
||||||
|
SurfacePath::Circle(_) => {}
|
||||||
|
SurfacePath::Line(x) => {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let color = Color::rgb(1.0, 0.,0.).into();
|
||||||
|
|
||||||
|
let mut origin = geom.point_from_surface_coords(x.origin()).to_xyz();
|
||||||
|
let direction = geom.vector_from_surface_coords(x.direction()).to_xyz();
|
||||||
|
let opposite_corner = Point{ coords: Vector{
|
||||||
|
components: [
|
||||||
|
Scalar::from(origin.x.into_f32()) + direction.x,
|
||||||
|
Scalar::from(origin.y.into_f32()) + direction.y,
|
||||||
|
Scalar::from(origin.z.into_f32()) + direction.z,
|
||||||
|
]
|
||||||
|
}};
|
||||||
|
let line = GlobalPath::line_from_points([origin, opposite_corner]);
|
||||||
|
match line.0 {
|
||||||
|
GlobalPath::Circle(_) => {}
|
||||||
|
GlobalPath::Line(x) => {
|
||||||
|
render_line(commands, fj_model, meshes, materials, x, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match geom.u {
|
match geom.u {
|
||||||
GlobalPath::Circle(x) => {
|
GlobalPath::Circle(x) => {
|
||||||
x.aabb();
|
x.aabb();
|
||||||
}
|
}
|
||||||
GlobalPath::Line(x) => {
|
GlobalPath::Line(x) => {
|
||||||
let origin = x.origin();
|
|
||||||
let direction = x.direction();
|
|
||||||
let opposite_corner = Vec3::new(
|
|
||||||
origin.x.into_f32() + direction.x.into_f32() + 0.01,
|
|
||||||
origin.y.into_f32() + direction.y.into_f32() + 0.01,
|
|
||||||
origin.z.into_f32() + direction.z.into_f32() + 0.01,
|
|
||||||
);
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let red: f32 = rng.gen_range(0.0..1.0);
|
let red: f32 = rng.gen_range(0.0..1.0);
|
||||||
let green: f32 = rng.gen_range(0.0..1.0);
|
let green: f32 = rng.gen_range(0.0..1.0);
|
||||||
let blue: f32 = rng.gen_range(0.0..1.0);
|
let blue: f32 = rng.gen_range(0.0..1.0);
|
||||||
commands.spawn(( // line on base of sweep
|
// let color = Color::rgb(red, green, blue).into();
|
||||||
PbrBundle {
|
// render_line(commands, fj_model, meshes, materials, x, color);
|
||||||
mesh: meshes.add(Mesh::from(shape::Box::from_corners(opposite_corner.into(), origin.to_vec3()))),
|
|
||||||
material: materials.add(Color::rgb(red, green, blue).into()),
|
|
||||||
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
// RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
|
||||||
// PickableBundle::default() // <- This one too
|
|
||||||
));
|
|
||||||
commands.spawn(( // line on top of sweep, just offset by the sweep vector
|
|
||||||
PbrBundle {
|
|
||||||
mesh: meshes.add(Mesh::from(shape::Box::from_corners(opposite_corner.into(), origin.to_vec3()))),
|
|
||||||
material: materials.add(Color::rgb(red, green, blue).into()),
|
|
||||||
transform: Transform::from_xyz(geom_sweep_vector.x.into_f32(), geom_sweep_vector.y.into_f32(), geom_sweep_vector.z.into_f32()),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
// RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
|
||||||
// PickableBundle::default() // <- This one too
|
|
||||||
));
|
|
||||||
commands.spawn(( // line following the sweep
|
|
||||||
PbrBundle {
|
|
||||||
mesh: meshes.add(Mesh::from(shape::Box::from_corners(origin.to_vec3() + geom_sweep_vector.to_vec3(), origin.to_vec3() + 0.01))),
|
|
||||||
material: materials.add(Color::rgb(red, green, blue).into()),
|
|
||||||
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
// RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
|
||||||
// PickableBundle::default() // <- This one too
|
|
||||||
));
|
|
||||||
commands.spawn(( // vertex
|
|
||||||
PbrBundle {
|
|
||||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 0.025 })),
|
|
||||||
material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
|
|
||||||
transform: Transform::from_xyz(origin.to_xyz().x.into_f32(), origin.to_xyz().y.into_f32(), origin.to_xyz().z.into_f32()),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
// RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
|
||||||
// PickableBundle::default() // <- This one too
|
|
||||||
));
|
|
||||||
commands.spawn(( // swept vertex
|
|
||||||
PbrBundle {
|
|
||||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 0.025 })),
|
|
||||||
material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
|
|
||||||
transform: Transform::from_xyz(
|
|
||||||
origin.to_xyz().x.into_f32() + geom_sweep_vector.to_vec3().x,
|
|
||||||
origin.to_xyz().y.into_f32() + geom_sweep_vector.to_vec3().y,
|
|
||||||
origin.to_xyz().z.into_f32() + geom_sweep_vector.to_vec3().z,
|
|
||||||
),
|
|
||||||
// transform: Transform::from_scale(origin.to_vec3() + geom_sweep_vector.to_vec3()),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
// RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
|
||||||
// PickableBundle::default(), // <- This one too
|
|
||||||
|
|
||||||
// I want to insert a component when dragging starts, that contains
|
// let origin = x.origin();
|
||||||
// data about the exact ray hit location.
|
// let direction = x.direction();
|
||||||
// On::<Pointer<DragStart>>::run(run),
|
// let opposite_corner = Vec3::new(
|
||||||
// On::<Pointer<DragStart>>::target_insert(DragCaster::default()),
|
// origin.x.into_f32() + direction.x.into_f32(),
|
||||||
// On::<Pointer<Drag>>::target_component_mut::<DragCaster>(|drag, mut caster| {
|
// origin.y.into_f32() + direction.y.into_f32(),
|
||||||
// caster.hit_location = drag.hit.position.unwrap();
|
// origin.z.into_f32() + direction.z.into_f32(),
|
||||||
//
|
// );
|
||||||
// // (*transform).translation = transform.translation + Vec3::new(-drag.delta.x / 100.0, 0.0, drag.delta.y / 100.0);
|
//
|
||||||
// }),
|
// commands.spawn(( // line on top of sweep, just offset by the sweep vector
|
||||||
// On::<Pointer<Drag>>::target_component_mut::<DragCaster>(|drag, mut caster| {
|
// PbrBundle {
|
||||||
// drag.distance
|
// // mesh: meshes.add(Mesh::from(LineList {
|
||||||
// // (*transform).translation = transform.translation + Vec3::new(-drag.delta.x / 100.0, 0.0, drag.delta.y / 100.0);
|
// // lines: vec![
|
||||||
// }),
|
// // (opposite_corner.into(), origin.to_vec3()),
|
||||||
));
|
// // ],
|
||||||
|
// // })),
|
||||||
|
// mesh: meshes.add(create_rectangle_mesh(opposite_corner, origin.to_vec3(), 0.01, 0.01)),
|
||||||
|
// material: materials.add(Color::rgb(red, green, blue).into()),
|
||||||
|
// transform: Transform::from_xyz(geom_sweep_vector.x.into_f32(), geom_sweep_vector.y.into_f32(), geom_sweep_vector.z.into_f32()),
|
||||||
|
// ..default()
|
||||||
|
// },
|
||||||
|
// // RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
||||||
|
// // PickableBundle::default() // <- This one too
|
||||||
|
// ));
|
||||||
|
// commands.spawn(( // line following the sweep
|
||||||
|
// PbrBundle {
|
||||||
|
// mesh: meshes.add(create_rectangle_mesh(origin.to_vec3() + geom_sweep_vector.to_vec3(), origin.to_vec3(), 0.01, 0.01)),
|
||||||
|
// material: materials.add(Color::rgb(red, green, blue).into()),
|
||||||
|
// transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
|
// ..default()
|
||||||
|
// },
|
||||||
|
// // RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
||||||
|
// // PickableBundle::default() // <- This one too
|
||||||
|
// ));
|
||||||
|
// commands.spawn(( // swept vertex
|
||||||
|
// PbrBundle {
|
||||||
|
// mesh: meshes.add(Mesh::from(shape::Cube { size: 0.025 })),
|
||||||
|
// material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
|
||||||
|
// transform: Transform::from_xyz(
|
||||||
|
// origin.to_xyz().x.into_f32() + geom_sweep_vector.to_vec3().x,
|
||||||
|
// origin.to_xyz().y.into_f32() + geom_sweep_vector.to_vec3().y,
|
||||||
|
// origin.to_xyz().z.into_f32() + geom_sweep_vector.to_vec3().z,
|
||||||
|
// ),
|
||||||
|
// // transform: Transform::from_scale(origin.to_vec3() + geom_sweep_vector.to_vec3()),
|
||||||
|
// ..default()
|
||||||
|
// },
|
||||||
|
// // RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
||||||
|
// // PickableBundle::default(), // <- This one too
|
||||||
|
//
|
||||||
|
// // I want to insert a component when dragging starts, that contains
|
||||||
|
// // data about the exact ray hit location.
|
||||||
|
// // On::<Pointer<DragStart>>::run(run),
|
||||||
|
// // On::<Pointer<DragStart>>::target_insert(DragCaster::default()),
|
||||||
|
// // On::<Pointer<Drag>>::target_component_mut::<DragCaster>(|drag, mut caster| {
|
||||||
|
// // caster.hit_location = drag.hit.position.unwrap();
|
||||||
|
// //
|
||||||
|
// // // (*transform).translation = transform.translation + Vec3::new(-drag.delta.x / 100.0, 0.0, drag.delta.y / 100.0);
|
||||||
|
// // }),
|
||||||
|
// // On::<Pointer<Drag>>::target_component_mut::<DragCaster>(|drag, mut caster| {
|
||||||
|
// // drag.distance
|
||||||
|
// // // (*transform).translation = transform.translation + Vec3::new(-drag.delta.x / 100.0, 0.0, drag.delta.y / 100.0);
|
||||||
|
// // }),
|
||||||
|
// ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_line<const T: usize>(mut commands: &mut Commands,
|
||||||
|
fj_model: &FjMeshWrapper,
|
||||||
|
mut meshes: &mut ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: &mut ResMut<Assets<StandardMaterial>>,
|
||||||
|
line: Line<T>,
|
||||||
|
color: Color) {
|
||||||
|
let origin = line.origin().to_xyz();
|
||||||
|
let direction = line.direction().to_xyz();
|
||||||
|
let opposite_corner = Vec3::new(
|
||||||
|
origin.x.into_f32() + direction.x.into_f32(),
|
||||||
|
origin.y.into_f32() + direction.y.into_f32(),
|
||||||
|
origin.z.into_f32() + direction.z.into_f32(),
|
||||||
|
);
|
||||||
|
|
||||||
|
commands.spawn(( // line on base of sweep
|
||||||
|
PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(create_rectangle_mesh(opposite_corner, origin.to_vec3(), 0.01, 0.01))),
|
||||||
|
// mesh: meshes.add(Mesh::from(shape::Box::from_corners(opposite_corner.into(), origin.to_vec3()))),
|
||||||
|
material: materials.add(StandardMaterial::from(color)),
|
||||||
|
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
// RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
||||||
|
// PickableBundle::default() // <- This one too
|
||||||
|
));
|
||||||
|
commands.spawn(( // vertex
|
||||||
|
PbrBundle {
|
||||||
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 0.025 })),
|
||||||
|
material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
|
||||||
|
transform: Transform::from_xyz(origin.to_xyz().x.into_f32(), origin.to_xyz().y.into_f32(), origin.to_xyz().z.into_f32()),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
// RaycastPickTarget::default(), // <- Needed for the raycast backend.
|
||||||
|
// PickableBundle::default() // <- This one too
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
// Root node
|
||||||
|
commands.spawn(NodeBundle {
|
||||||
|
style: Style {
|
||||||
|
flex_direction: FlexDirection::Column,
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}).with_children(|parent| {
|
||||||
|
// Text node
|
||||||
|
parent.spawn(TextBundle {
|
||||||
|
text: Text::from_section(
|
||||||
|
"This is some text",
|
||||||
|
TextStyle {
|
||||||
|
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
|
||||||
|
font_size: 40.0,
|
||||||
|
color: Color::WHITE,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Checkbox (custom implementation)
|
||||||
|
parent.spawn(NodeBundle {
|
||||||
|
style: Style {
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}).with_children(|parent| {
|
||||||
|
parent.spawn(TextBundle {
|
||||||
|
text: Text::from_section(
|
||||||
|
"X", // Simulating a checked state
|
||||||
|
TextStyle {
|
||||||
|
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||||
|
font_size: 20.0,
|
||||||
|
color: Color::BLACK,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
*/
|
||||||
162
src/mesh.rs
Normal file
162
src/mesh.rs
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
use bevy::math::Vec3;
|
||||||
|
use bevy::prelude::Mesh;
|
||||||
|
use bevy::render::mesh::{Indices, PrimitiveTopology};
|
||||||
|
use fj_math::Point;
|
||||||
|
use fj_interop::mesh::Mesh as FjMesh;
|
||||||
|
use fj_core::storage::Handle as FjHandle;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LineList {
|
||||||
|
pub lines: Vec<(Vec3, Vec3)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LineList> for Mesh {
|
||||||
|
fn from(line: LineList) -> Self {
|
||||||
|
let vertices: Vec<_> = line.lines.into_iter().flat_map(|(a, b)| [a, b]).collect();
|
||||||
|
|
||||||
|
// This tells wgpu that the positions are list of lines
|
||||||
|
// where every pair is a start and end point
|
||||||
|
Mesh::new(PrimitiveTopology::LineList)
|
||||||
|
// Add the vertices positions as an attribute
|
||||||
|
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_uv_mapping(fj_mesh: &FjMesh<Point<3>>) -> Vec<[f32; 2]> {
|
||||||
|
let mut uvs = Vec::new();
|
||||||
|
|
||||||
|
for vertex in fj_mesh.vertices() {
|
||||||
|
let x = vertex.coords.x.into_f32();
|
||||||
|
let y = vertex.coords.y.into_f32();
|
||||||
|
// Here we're using x and y coordinates as u and v
|
||||||
|
uvs.push([x, y]);
|
||||||
|
uvs.push([x, y]);
|
||||||
|
uvs.push([x, y]);
|
||||||
|
uvs.push([x, y]);
|
||||||
|
uvs.push([x, y]);
|
||||||
|
uvs.push([x, y]);
|
||||||
|
}
|
||||||
|
uvs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_normals(fj_mesh: &FjMesh<Point<3>>) -> Vec<[f32; 3]> {
|
||||||
|
let mut normals = Vec::new();
|
||||||
|
for triangle in fj_mesh.triangles() {
|
||||||
|
let normal = triangle.inner.normal();
|
||||||
|
normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]);
|
||||||
|
normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]);
|
||||||
|
normals.push([normal.x.into_f32(), normal.y.into_f32(), normal.z.into_f32()]);
|
||||||
|
}
|
||||||
|
normals
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_positions(fj_mesh: &FjMesh<Point<3>>) -> Vec<[f32; 3]> {
|
||||||
|
let mut positions = Vec::new();
|
||||||
|
|
||||||
|
// Iterate through each triangle
|
||||||
|
for triangle in fj_mesh.triangles() {
|
||||||
|
// For each vertex index in the triangle
|
||||||
|
for vertex_index in triangle.inner.points() {
|
||||||
|
positions.push([vertex_index.x.into_f32(), vertex_index.y.into_f32(), vertex_index.z.into_f32()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
positions
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_indices(fj_mesh: &FjMesh<Point<3>>) -> Vec<u32> {
|
||||||
|
let mut num_positions = 0;
|
||||||
|
|
||||||
|
// Count the total number of positions (3 per triangle)
|
||||||
|
for triangle in fj_mesh.triangles() {
|
||||||
|
num_positions += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the indices [0, 1, 2, ..., num_positions-1]
|
||||||
|
(0..num_positions as u32).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to take a solid, and return all the data needed to create a Bevy Mesh from scratch
|
||||||
|
pub fn convert_mesh(fj_mesh: &FjMesh<Point<3>>) -> Mesh {
|
||||||
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
||||||
|
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, generate_positions(&fj_mesh));
|
||||||
|
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, generate_normals(&fj_mesh));
|
||||||
|
// mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, generate_uv_mapping(&fj_mesh));
|
||||||
|
mesh.set_indices(Some(Indices::U32(generate_indices(&fj_mesh))));
|
||||||
|
mesh
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_rectangle_mesh(p1: Vec3, p2: Vec3, width: f32, height: f32) -> Mesh {
|
||||||
|
// Calculate direction vector from p1 to p2
|
||||||
|
let direction = (p2 - p1).normalize();
|
||||||
|
// Create arbitrary up vector
|
||||||
|
let up = Vec3::Y;
|
||||||
|
// Right vector for the rectangle width
|
||||||
|
let right = direction.cross(up).normalize() * width * 0.5;
|
||||||
|
// Up vector for rectangle height, ensuring it's perpendicular
|
||||||
|
let up_perpendicular = direction.cross(right).normalize() * height * 0.5;
|
||||||
|
|
||||||
|
let vertices = [
|
||||||
|
p1 - right - up_perpendicular, // Bottom-left of p1 face
|
||||||
|
p1 + right - up_perpendicular, // Bottom-right of p1 face
|
||||||
|
p1 + right + up_perpendicular, // Top-right of p1 face
|
||||||
|
p1 - right + up_perpendicular, // Top-left of p1 face
|
||||||
|
p2 - right - up_perpendicular, // Bottom-left of p2 face
|
||||||
|
p2 + right - up_perpendicular, // Bottom-right of p2 face
|
||||||
|
p2 + right + up_perpendicular, // Top-right of p2 face
|
||||||
|
p2 - right + up_perpendicular, // Top-left of p2 face
|
||||||
|
];
|
||||||
|
|
||||||
|
// Calculate face normals
|
||||||
|
let front_normal = (vertices[1] - vertices[0]).cross(vertices[3] - vertices[0]).normalize();
|
||||||
|
let back_normal = (vertices[5] - vertices[4]).cross(vertices[7] - vertices[4]).normalize();
|
||||||
|
let right_normal = (vertices[5] - vertices[1]).cross(vertices[2] - vertices[1]).normalize();
|
||||||
|
let left_normal = (vertices[0] - vertices[4]).cross(vertices[7] - vertices[4]).normalize();
|
||||||
|
let top_normal = (vertices[2] - vertices[3]).cross(vertices[7] - vertices[3]).normalize();
|
||||||
|
let bottom_normal = (vertices[1] - vertices[0]).cross(vertices[4] - vertices[0]).normalize();
|
||||||
|
|
||||||
|
let normals = vec![
|
||||||
|
bottom_normal, bottom_normal, top_normal, top_normal, // Bottom and top for first quad
|
||||||
|
bottom_normal, bottom_normal, top_normal, top_normal, // Bottom and top for second quad
|
||||||
|
front_normal, front_normal, front_normal, front_normal, // Front face normals
|
||||||
|
back_normal, back_normal, back_normal, back_normal, // Back face normals
|
||||||
|
right_normal, right_normal, right_normal, right_normal, // Right face normals
|
||||||
|
left_normal, left_normal, left_normal, left_normal, // Left face normals
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// Convert positions and normals to the format expected by Bevy
|
||||||
|
|
||||||
|
// Creating a mesh
|
||||||
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
||||||
|
|
||||||
|
// Insert positions
|
||||||
|
let positions: Vec<[f32; 3]> = vertices.iter().map(|v| [v.x, v.y, v.z]).collect();
|
||||||
|
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
|
||||||
|
|
||||||
|
// Insert normals (optional here, assuming all outwards for simplicity)
|
||||||
|
let normals: Vec<[f32; 3]> = normals.iter().map(|n| [n.x, n.y, n.z]).collect();
|
||||||
|
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
|
||||||
|
|
||||||
|
// Define two triangles for each face of the rectangle mesh
|
||||||
|
|
||||||
|
let indices = Indices::U32(vec![
|
||||||
|
// Front face
|
||||||
|
0, 2, 1, 0, 3, 2,
|
||||||
|
// Back face
|
||||||
|
4, 6, 5, 4, 7, 6,
|
||||||
|
// Top face
|
||||||
|
2, 3, 6, 6, 3, 7,
|
||||||
|
// Bottom face
|
||||||
|
0, 1, 5, 0, 5, 4,
|
||||||
|
// Right face
|
||||||
|
1, 2, 6, 1, 6, 5,
|
||||||
|
// Left face
|
||||||
|
0, 4, 7, 0, 7, 3,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
mesh.set_indices(Some(indices));
|
||||||
|
|
||||||
|
mesh
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user