|
|
|
|
@@ -0,0 +1,270 @@
|
|
|
|
|
// Importing Bevy ECS components and systems
|
|
|
|
|
use bevy::ecs::component::Component;
|
|
|
|
|
use bevy::ecs::system::{Commands, Query};
|
|
|
|
|
use bevy::ecs::entity::Entity;
|
|
|
|
|
use bevy::ecs::query::Without;
|
|
|
|
|
use bevy::ecs::system::ResMut;
|
|
|
|
|
|
|
|
|
|
// Importing Bevy assets and rendering related components
|
|
|
|
|
use bevy::prelude::{Mesh, shape};
|
|
|
|
|
use bevy::render::mesh::{Indices, PrimitiveTopology};
|
|
|
|
|
use bevy::asset::Assets;
|
|
|
|
|
use bevy::pbr::{PbrBundle, StandardMaterial};
|
|
|
|
|
use bevy::render::color::Color;
|
|
|
|
|
use bevy::transform::components::Transform;
|
|
|
|
|
use bevy::utils::default;
|
|
|
|
|
|
|
|
|
|
// Importing Bevy application related components
|
|
|
|
|
use bevy::app::{App, Plugin, Update};
|
|
|
|
|
|
|
|
|
|
// 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::objects::{Cycle, Region, Shell, 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;
|
|
|
|
|
|
|
|
|
|
// Importing Fj-interop mesh
|
|
|
|
|
use fj_interop::mesh::Mesh as FjMesh;
|
|
|
|
|
|
|
|
|
|
// Importing Fj-math and other standard functionalities
|
|
|
|
|
use fj_math::{Aabb, Point, Scalar, Vector};
|
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
use bevy::math::Vec3;
|
|
|
|
|
use fj_core::geometry::curve::GlobalPath;
|
|
|
|
|
use rand::Rng;
|
|
|
|
|
|
|
|
|
|
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)]
|
|
|
|
|
struct FjConvertedFlag;
|
|
|
|
|
|
|
|
|
|
pub struct FjRenderPlugin;
|
|
|
|
|
|
|
|
|
|
impl Plugin for FjRenderPlugin {
|
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
|
app.add_systems(Update, update_fj_model_system);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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(
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
query: Query<(Entity, &FjMeshWrapper), Without<FjConvertedFlag>>,
|
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
|
|
|
) {
|
|
|
|
|
for (entity, solid) in &query {
|
|
|
|
|
let bevy_mesh = convert_mesh(&solid.mesh);
|
|
|
|
|
commands.entity(entity).insert(
|
|
|
|
|
(
|
|
|
|
|
PbrBundle {
|
|
|
|
|
mesh: meshes.add(Mesh::from(bevy_mesh.clone())),
|
|
|
|
|
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
|
|
|
|
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
FjConvertedFlag
|
|
|
|
|
|
|
|
|
|
));
|
|
|
|
|
add_debug_info_to_entity(&mut commands, solid, &mut meshes, &mut materials);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_debug_info_to_entity(mut commands: &mut Commands,
|
|
|
|
|
fj_model: &FjMeshWrapper,
|
|
|
|
|
mut meshes: &mut ResMut<Assets<Mesh>>,
|
|
|
|
|
mut materials: &mut ResMut<Assets<StandardMaterial>>
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
for shell in fj_model.handle.shells() {
|
|
|
|
|
println!("{:?}. shell", shell);
|
|
|
|
|
for face in shell.faces() {
|
|
|
|
|
println!("{:?}. face", face);
|
|
|
|
|
let surface = face.surface();
|
|
|
|
|
let geom = surface.geometry();
|
|
|
|
|
let geom_sweep_vector = geom.v;
|
|
|
|
|
match geom.u {
|
|
|
|
|
GlobalPath::Circle(x) => {
|
|
|
|
|
x.aabb();
|
|
|
|
|
}
|
|
|
|
|
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 red: 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);
|
|
|
|
|
commands.spawn(( // line on base of sweep
|
|
|
|
|
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(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
|
|
|
|
|
// 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);
|
|
|
|
|
// }),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|