From 9e92a469b878d8d204390d4221b7cc4a9c608d0d Mon Sep 17 00:00:00 2001 From: Chris Hodapp Date: Fri, 14 Feb 2020 11:41:29 -0500 Subject: [PATCH] Code builds with cube_thing_rule. Still has bugs. --- src/main.rs | 281 ++++++++++++++++++++++++---------------------------- 1 file changed, 129 insertions(+), 152 deletions(-) diff --git a/src/main.rs b/src/main.rs index fe27ef4..19e2727 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,9 @@ //use std::io; -use tri_mesh::prelude::*; -//use nalgebra::base::dimension::{U1, U4}; -//use nalgebra::Matrix4; +use tri_mesh::prelude as tm; +use nalgebra::*; /// A type for custom mesh vertices. Initialize with [vertex][self::vertex]. -pub type Vertex = nalgebra::Vector4; +pub type Vertex = Vector4; /// Initializes a vertex for a custom mesh. pub fn vertex(x: f32, y: f32, z: f32) -> Vertex { @@ -37,7 +36,7 @@ struct OpenMesh { impl OpenMesh { - fn transform(&self, xfm: nalgebra::Matrix4) -> OpenMesh { + fn transform(&self, xfm: Matrix4) -> OpenMesh { OpenMesh { verts: self.verts.iter().map(|v| xfm * v).collect(), faces: self.faces.clone(), // TODO: Use Rc? @@ -84,7 +83,7 @@ impl OpenMesh { } } - fn to_trimesh(&self) -> Result { + fn to_trimesh(&self) -> Result { let mut v: Vec = vec![0.0; self.verts.len() * 3]; for (i, vert) in self.verts.iter().enumerate() { v[3*i] = vert[0].into(); @@ -92,7 +91,7 @@ impl OpenMesh { v[3*i+2] = vert[2].into(); } let faces: Vec = self.faces.iter().map(|f| *f as _).collect(); - MeshBuilder::new().with_indices(faces).with_positions(v).build() + tm::MeshBuilder::new().with_indices(faces).with_positions(v).build() } // Just assume this is broken @@ -149,84 +148,89 @@ impl OpenMesh { // TODO: Do I benefit with Rc below so Rule can be shared? enum Rule { - // Recurse further. Input is "seeds" that further geometry should - // *replace*. Generated geometry must have the same outer - // boundary as the seeds, and be in the same coordinate space as - // the input. - Recurse(fn (Vec) -> Vec), + // Produce geometry, and possibly recurse further: + Recurse(fn () -> Vec), // Stop recursing here: EmptyRule, } // TODO: Rename rules? struct RuleStep { - // The 'final' geometry generated at this step. - geom: Mesh, - // The 'seed' geometry from this step. If recursion stops - // (whether because rule is EmptyRule or because recursion depth - // has been hit), this will be transformed with 'xform' and - // appended with 'geom'. If recursion continues, this geometry is - // passed as the input to the next rule. (TODO: rule_to_mesh - // needs to do the 'recursion stops' part.) - // - // This is in the coordinate space that 'rule' should run in - - // thus, if it is transformed with 'xform', it will be in the same - // coordinate space as 'geom'. - seeds: Vec, + // The geometry generated by this rule on its own - and none of + // the child rules. + geom: OpenMesh, + // The next rule to run. If EmptyRule, then stop here (and // 'xform' is irrelevant). rule: Box, - // The transformation which puts 'seeds' and any geometry from - // 'rule' (if applicable) into the same coordinate space as - // 'geom'. - xform: Mat4, + // The transformation to apply to geometry generated by 'rule' and + // any child rules. + xform: Matrix4, } // is there a better way to do this? -fn empty_mesh() -> Mesh { - MeshBuilder::new().with_indices(vec![]).with_positions(vec![]).build().unwrap() +fn empty_mesh() -> OpenMesh { + OpenMesh { + verts: vec![], + faces: vec![], + idxs_entrance: vec![], + idxs_exit: vec![], + idxs_body: (0, 0), + } } -fn curve_horn_start(_v: Vec) -> Vec { +/* +fn curve_horn_start() -> Vec { // Seed is a square in XY, sidelength 1, centered at (0,0,0): let seed = { - let indices: Vec = vec![0, 1, 2, 0, 2, 3]; - let positions: Vec = vec![0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0]; - let mut s = MeshBuilder::new().with_indices(indices).with_positions(positions).build().unwrap(); - s.apply_transformation(Matrix4::from_translation(vec3(-0.5, -0.5, 0.0))); - s + let m = OpenMesh { + verts: vec![ + vertex(0.0, 0.0, 0.0), + vertex(1.0, 0.0, 0.0), + vertex(1.0, 1.0, 0.0), + vertex(0.0, 1.0, 0.0), + ], + faces: vec![ + 0, 1, 2, + 0, 2, 3, + ], + idxs_entrance: vec![0], + idxs_exit: vec![0], + idxs_body: (0, 0), + }; + let xform = nalgebra::geometry::Translation3::new(-0.5, -0.5, 0.0).to_homogeneous(); + m.transform(xform) }; vec![ // Since neither of the other two rules *start* with geometry: RuleStep { geom: seed.clone(), rule: Box::new(Rule::EmptyRule), - xform: Matrix4::identity(), - seeds: vec![] + xform: nalgebra::geometry::Transform3::identity().to_homogeneous(), }, // Recurse in both directions: - RuleStep { geom: empty_mesh(), + RuleStep { geom: seed.clone(), rule: Box::new(Rule::Recurse(curve_horn_thing_rule)), - xform: Matrix4::identity(), - seeds: vec![seed.clone()], + xform: nalgebra::geometry::Transform3::identity().to_homogeneous(), }, - RuleStep { geom: empty_mesh(), + RuleStep { geom: seed.clone(), rule: Box::new(Rule::Recurse(curve_horn_thing_rule)), - xform: Matrix4::from_angle_y(Rad::turn_div_2()), - seeds: vec![seed.clone()], + xform: nalgebra::geometry::Rotation3::from_axis_angle( + &nalgebra::Vector3::y_axis(), + std::f32::consts::FRAC_PI_2).to_homogeneous(), }, ] } //use std::convert::TryFrom; -fn curve_horn_thing_rule(v: Vec) -> Vec { +fn curve_horn_thing_rule() -> Vec { let gen_geom = |seed: &Mesh| -> RuleStep { let mut mesh = seed.clone(); - let m: Mat4 = Matrix4::from_angle_y(Rad(0.1)) * - Matrix4::from_scale(0.95) * - Matrix4::from_translation(vec3(0.0, 0.0, 0.2)); + let m: Mat4 = tm::Matrix4::from_angle_y(Rad(0.1)) * + tm::Matrix4::from_scale(0.95) * + tm::Matrix4::from_translation(vec3(0.0, 0.0, 0.2)); let r = Rule::Recurse(curve_horn_thing_rule); mesh.apply_transformation(m); @@ -279,7 +283,7 @@ fn curve_horn_thing_rule(v: Vec) -> Vec { // that I cannot use MeshBuilder this way and then append // meshes - it just leads to disconnected geometry - let joined = match MeshBuilder::new(). + let joined = match tm::MeshBuilder::new(). with_positions(verts). with_indices(idxs). build() @@ -315,7 +319,7 @@ fn points_to_xform(v0: Point3, v1: Point3, v2: Point3) -> Mat4 { let yn: Vec3 = zn.cross(xn); let s = x.magnitude(); - let _m: Mat4 = Matrix4::from_cols( + let _m: Mat4 = tm::Matrix4::from_cols( (xn*s).extend(0.0), // new X (yn*s).extend(0.0), // new Y (zn*s).extend(0.0), // new Z @@ -323,41 +327,75 @@ fn points_to_xform(v0: Point3, v1: Point3, v2: Point3) -> Mat4 { ); return _m; } +*/ -fn cube_thing_rule(_v: Vec) -> Vec { +fn cube() -> OpenMesh { + OpenMesh { + verts: vec![ + vertex(0.0, 0.0, 0.0), + vertex(1.0, 0.0, 0.0), + vertex(0.0, 1.0, 0.0), + vertex(1.0, 1.0, 0.0), + vertex(0.0, 0.0, 1.0), + vertex(1.0, 0.0, 1.0), + vertex(0.0, 1.0, 1.0), + vertex(1.0, 1.0, 1.0), + ], + faces: vec![ + 0, 3, 1, + 0, 2, 3, + 1, 7, 5, + 1, 3, 7, + 5, 6, 4, + 5, 7, 6, + 4, 2, 0, + 4, 6, 2, + 2, 7, 3, + 2, 6, 7, + 0, 1, 5, + 0, 5, 4, + ], + idxs_entrance: vec![], + idxs_exit: vec![], + idxs_body: (0, 8), + }.transform(geometry::Translation3::new(-0.5, -0.5, -0.5).to_homogeneous()) +} - let mesh = MeshBuilder::new().cube().build().unwrap(); +fn cube_thing_rule() -> Vec { + + let mesh = cube(); // Quarter-turn in radians: - let qtr = Rad::turn_div_4(); + let qtr = std::f32::consts::FRAC_PI_2; + + let y = &Vector3::y_axis(); + let z = &Vector3::z_axis(); // Each element of this turns to a branch for the recursion: - let turns: Vec = vec![ - Matrix4::identity(), - Matrix4::from_angle_y(qtr), - Matrix4::from_angle_y(qtr * 2.0), - Matrix4::from_angle_y(qtr * 3.0), - Matrix4::from_angle_z(qtr), - Matrix4::from_angle_z(-qtr), + let turns: Vec> = vec![ + geometry::Transform3::identity().to_homogeneous(), + geometry::Rotation3::from_axis_angle(y, qtr).to_homogeneous(), + geometry::Rotation3::from_axis_angle(y, qtr * 2.0).to_homogeneous(), + geometry::Rotation3::from_axis_angle(y, qtr * 3.0).to_homogeneous(), + geometry::Rotation3::from_axis_angle(z, qtr).to_homogeneous(), + geometry::Rotation3::from_axis_angle(z, -qtr).to_homogeneous(), ]; - let gen_rulestep = |rot: &Mat4| -> RuleStep { - let m: Mat4 = rot * - Matrix4::from_scale(0.5) * - Matrix4::from_translation(vec3(6.0, 0.0, 0.0)); + let gen_rulestep = |rot: &Matrix4| -> RuleStep { + let m: Matrix4 = rot * + Matrix4::new_scaling(0.5) * + geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous(); let r = Rule::Recurse(cube_thing_rule); - let mut m2 = mesh.clone(); - m2.apply_transformation(m); - RuleStep { geom: m2, rule: Box::new(r), xform: m, seeds: vec![] } + + let m2 = mesh.transform(m); + RuleStep { geom: m2, rule: Box::new(r), xform: m } }; - // TODO: Why is 'mesh' present in each RuleStep? This is just - // duplicate geometry! Either 'm' applies to 'mesh' (and the - // definition of RuleStep changes) - or 'mesh' needs to already be - // transformed. turns.iter().map(gen_rulestep).collect() } +// Have I any need of this after making OpenMesh? +/* struct MeshBound<'a> { m: &'a Mesh, start: HalfEdgeID, @@ -420,6 +458,7 @@ impl<'a> Iterator for MeshBound<'a> { } } +*/ //fn mesh_boundary(m: &Mesh) -> Vec { //} @@ -432,9 +471,9 @@ impl<'a> Iterator for MeshBound<'a> { // rather than repeatedly transforming meshes, it stacks // transformations and then applies them all at once. -fn rule_to_mesh(rule: &Rule, seed: Vec, iters_left: u32) -> (Mesh, u32) { +fn rule_to_mesh(rule: &Rule, iters_left: u32) -> (OpenMesh, u32) { - let mut mesh = MeshBuilder::new().with_indices(vec![]).with_positions(vec![]).build().unwrap(); + let mut mesh = empty_mesh(); let mut nodes: u32 = 1; @@ -444,19 +483,21 @@ fn rule_to_mesh(rule: &Rule, seed: Vec, iters_left: u32) -> (Mesh, u32) { match rule { Rule::Recurse(func) => { - for step in func(seed) { + for step in func() { let subrule: Rule = *step.rule; - let subxform: Mat4 = step.xform; - let geom: Mesh = step.geom; + let subxform: Matrix4 = step.xform; + let geom: OpenMesh = step.geom; - mesh.append(&geom); + mesh = mesh.connect_single(&geom); let (mut submesh, subnodes) = rule_to_mesh( - &subrule, step.seeds, iters_left - 1); - submesh.apply_transformation(subxform); + &subrule, iters_left - 1); + + submesh = submesh.transform(subxform); + nodes += subnodes; - mesh.append(&submesh); + mesh = mesh.connect_single(&submesh); } } Rule::EmptyRule => { @@ -466,17 +507,6 @@ fn rule_to_mesh(rule: &Rule, seed: Vec, iters_left: u32) -> (Mesh, u32) { (mesh, nodes) } -fn print_vector(v: &Vec4) -> String { - return format!("{},{},{},{}", v.x, v.y, v.z, v.w); -} - -fn print_matrix(m: &Mat4) { - let mt = m.transpose(); - println!("[{}]\n[{}]\n[{}]\n[{}]", - print_vector(&mt.x), print_vector(&mt.y), - print_vector(&mt.z), print_vector(&mt.w)); -} - fn main() { println!("DEBUG-------------------------------"); @@ -511,7 +541,7 @@ fn main() { idxs_body: (4, 4), }; - let xform = nalgebra::geometry::Translation3::new(0.0, 0.0, 1.0).to_homogeneous(); + let xform = geometry::Translation3::new(0.0, 0.0, 1.0).to_homogeneous(); let m2 = m.transform(xform); let m3 = m.connect_single(&m2); let m4 = m3.connect_single(&m2.transform(xform)); @@ -537,84 +567,31 @@ fn main() { println!("mesh = {:?}", mesh); try_save(&mesh, "openmesh_cube_several.obj"); } - - // Construct any mesh, this time, we will construct a simple icosahedron - let mesh = MeshBuilder::new().icosahedron().build().unwrap(); - - // Compute the extreme coordinates which defines the axis aligned bounding box.. - let (_min_coordinates, _max_coordinates) = mesh.extreme_coordinates(); - - // .. or construct an actual mesh representing the axis aligned bounding box - let _aabb = mesh.axis_aligned_bounding_box(); - - let xform = points_to_xform( - Point3::new(0.5, 0.5, 0.0), - Point3::new(-0.5, 0.5, 0.0), - Point3::new(2.0, -4.0, 0.0), - ); - println!("points_to_xform:"); - print_matrix(&xform); - - // Export the bounding box to an obj file - std::fs::write("foo.obj", mesh.parse_as_obj()).unwrap(); let r = Rule::Recurse(cube_thing_rule); let max_iters = 2; println!("Running rules..."); - let (cubemesh, nodes) = rule_to_mesh(&r, vec![], max_iters); + let (cubemesh_, nodes) = rule_to_mesh(&r, max_iters); + let cubemesh = cubemesh_.to_trimesh().unwrap(); println!("Collected {} nodes, produced {} faces, {} vertices", nodes, cubemesh.no_faces(), cubemesh.no_vertices()); println!("Writing OBJ..."); std::fs::write("cubemesh.obj", cubemesh.parse_as_obj()).unwrap(); + /* let r2 = Rule::Recurse(curve_horn_start); println!("Running rules..."); // Seed: let seed = { let indices: Vec = vec![0, 1, 2, 2, 1, 3]; let positions: Vec = vec![0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0]; - let mut s = MeshBuilder::new().with_indices(indices).with_positions(positions).build().unwrap(); - s.apply_transformation(Matrix4::from_translation(vec3(-0.5, -0.5, 0.0))); + let mut s = tm::MeshBuilder::new().with_indices(indices).with_positions(positions).build().unwrap(); + s.apply_transformation(tm::Matrix4::from_translation(vec3(-0.5, -0.5, 0.0))); s }; + */ // TEMP (while I figure shit out) - struct VID { val: usize } - fn vertex_id_to_usize(v: VertexID) -> usize { - let v: VID = unsafe { std::mem::transmute(v) }; - v.val - } - println!("DEBUG-------------------------------"); - let mb = MeshBound::new(&seed).unwrap(); - let pos = seed.positions_buffer(); - for bound_edge in mb { - let (v1, v2) = seed.edge_vertices(bound_edge); - let v1idx = vertex_id_to_usize(v1); - let v2idx = vertex_id_to_usize(v2); - - println!("Boundary edge {}, vertices = {},{}, {:?}", - bound_edge, v1, v2, seed.edge_positions(bound_edge)); - println!("v1idx={} pos[...]=[{},{},{}], v2idx={}, pos[...]=[{},{},{}]", - v1idx, pos[3*v1idx], pos[3*v1idx+1], pos[3*v1idx+2], - v2idx, pos[3*v2idx], pos[3*v2idx+1], pos[3*v2idx+2]); - } println!("DEBUG-------------------------------"); - let (mut mesh, nodes) = rule_to_mesh(&r2, vec![seed], 75); - println!("Collected {} nodes, produced {} faces, {} vertices", - nodes, mesh.no_faces(), mesh.no_vertices()); - println!("Trying to merge..."); - match mesh.merge_overlapping_primitives() { - Err(e) => { - println!("Couldn't merge overlapping primitives!"); - println!("Error: {:?}", e); - } - Ok(_) => { - println!("Merged to {} faces, {} vertices", - mesh.no_faces(), mesh.no_vertices()); - } - } - println!("Writing OBJ..."); - std::fs::write("curve_horn_thing.obj", mesh.parse_as_obj()).unwrap(); - // TODO: Can I make the seed geometry part of the rule itself? }