Refactor & almost eliminate tri_mesh; cube_thing_rule working

This commit is contained in:
Chris Hodapp 2020-02-14 14:32:06 -05:00
parent 9e92a469b8
commit 2b6ca70d40
2 changed files with 105 additions and 95 deletions

View File

@ -1,7 +1,7 @@
- `curve_horn_*`: actually *combine* geometry properly. - `curve_horn_*`: actually *combine* geometry properly.
- https://docs.rs/nalgebra/0.19.0/nalgebra/geometry/index.html - try - Why is MeshBuilder so slow? Am I certain I'm not giving it a lot of
some other transformations from here (with to_homogeneous) redundant geometry?
- Look at everything in README.md in automata_scratch. - Look at everything in README.md in automata_scratch.
- Implement some of the tougher examples from the above too, e.g. the - Implement some of the tougher examples from the above too, e.g. the

View File

@ -46,6 +46,20 @@ impl OpenMesh {
} }
} }
fn to_trimesh(&self) -> Result<tm::Mesh, tri_mesh::mesh_builder::Error> {
let mut v: Vec<f64> = vec![0.0; self.verts.len() * 3];
println!("DEBUG: to_trimesh() iterating...");
for (i, vert) in self.verts.iter().enumerate() {
v[3*i] = vert[0].into();
v[3*i+1] = vert[1].into();
v[3*i+2] = vert[2].into();
}
let faces: Vec<u32> = self.faces.iter().map(|f| *f as _).collect();
println!("DEBUG: to_trimesh() calling MeshBuilder. faces.len()={}, v.len()={}...", faces.len(), v.len());
tm::MeshBuilder::new().with_indices(faces).with_positions(v).build()
}
// TODO: Why is this the slow part?
fn connect_single(&self, other: &OpenMesh) -> OpenMesh { fn connect_single(&self, other: &OpenMesh) -> OpenMesh {
// Imagine connecting two pieces of pipe together. We are // Imagine connecting two pieces of pipe together. We are
@ -83,89 +97,89 @@ impl OpenMesh {
} }
} }
fn to_trimesh(&self) -> Result<tm::Mesh, tri_mesh::mesh_builder::Error> {
let mut v: Vec<f64> = vec![0.0; self.verts.len() * 3];
for (i, vert) in self.verts.iter().enumerate() {
v[3*i] = vert[0].into();
v[3*i+1] = vert[1].into();
v[3*i+2] = vert[2].into();
}
let faces: Vec<u32> = self.faces.iter().map(|f| *f as _).collect();
tm::MeshBuilder::new().with_indices(faces).with_positions(v).build()
}
// Just assume this is broken // Just assume this is broken
fn connect(&self, others: &Vec<OpenMesh>) -> OpenMesh { fn connect(&self, others: &Vec<OpenMesh>) -> OpenMesh {
let mut v: Vec<Vertex> = vec![vertex(0.0,0.0,0.0); self.verts.len()]; if others.len() > 1 && self.idxs_exit.len() > 0 {
// Start out by cloning just entrance & body vertices: panic!("connect() is implemented for only one mesh if exit groups are present")
v.copy_from_slice(&self.verts[0..self.idxs_body.1]);
let mut f = self.faces.clone();
// TODO: Don't I need to patch up 'f'? self.faces refers to
// exit vertices which - if others.len() > 1 - need to be
// manually patched up. This patching up should consist
// solely of an offset to all indices in a certain range.
//
// e.g. let idxs_exit be [e0, e1, e2, ... e_(n-1)]
// indices in range [e0, e1-1] are for exit group 0.
// indices in range [e1, e2-1] are for exit group 1.
// indices in range [e2, e3-1] are for exit group 2, etc.
//
// exit group 0 requires no offset (we'll be putting entrance
// group vertices of self.others[0] right over top of them).
//
// exit group 1 requires an offset of the number of entrace &
// body vertices of self.others[0] (because we have appended
// this all)... with some additional adjustment maybe? not
// sure.
//
// exit group 2 requires an offset of the same for
// self.others[0] and self.others[1].
for other in others {
// We are offsetting all vertices in 'other' by everything
// else in 'v', so we need to account for this when we
// copy 'faces' (which has vector indices):
let offset = v.len();
v.extend(other.verts[0..other.idxs_body.1].iter());
f.extend(other.faces.iter().map(|f| *f + offset));
} }
// - Connect up so that each of self's exit groups is an
// entrance group from one of 'other'
return OpenMesh { if false {
verts: v, let mut v: Vec<Vertex> = vec![vertex(0.0,0.0,0.0); self.verts.len()];
faces: f, // Start out by cloning just entrance & body vertices:
idxs_entrance: self.idxs_entrance.clone(), v.copy_from_slice(&self.verts[0..self.idxs_body.1]);
idxs_exit: self.idxs_exit.clone(), // TODO let mut f = self.faces.clone();
idxs_body: self.idxs_body.clone(), // TODO // TODO: Don't I need to patch up 'f'? self.faces refers to
}; // exit vertices which - if others.len() > 1 - need to be
// manually patched up. This patching up should consist
// solely of an offset to all indices in a certain range.
//
// e.g. let idxs_exit be [e0, e1, e2, ... e_(n-1)]
// indices in range [e0, e1-1] are for exit group 0.
// indices in range [e1, e2-1] are for exit group 1.
// indices in range [e2, e3-1] are for exit group 2, etc.
//
// exit group 0 requires no offset (we'll be putting entrance
// group vertices of self.others[0] right over top of them).
//
// exit group 1 requires an offset of the number of entrace &
// body vertices of self.others[0] (because we have appended
// this all)... with some additional adjustment maybe? not
// sure.
//
// exit group 2 requires an offset of the same for
// self.others[0] and self.others[1].
for other in others {
// We are offsetting all vertices in 'other' by everything
// else in 'v', so we need to account for this when we
// copy 'faces' (which has vector indices):
let offset = v.len();
v.extend(other.verts[0..other.idxs_body.1].iter());
f.extend(other.faces.iter().map(|f| *f + offset));
}
// - Connect up so that each of self's exit groups is an
// entrance group from one of 'other'
return OpenMesh {
verts: v,
faces: f,
idxs_entrance: self.idxs_entrance.clone(),
idxs_exit: self.idxs_exit.clone(), // TODO
idxs_body: self.idxs_body.clone(), // TODO
};
}
// This is wrong, but close enough for now;
let mut mesh = self.clone();
for other in others {
mesh = mesh.connect_single(&other);
}
return mesh;
} }
} }
// TODO: Does OpenMesh subsume both 'geom' and 'seeds' in RuleStep?
// TODO: Do I benefit with Rc<Rule> below so Rule can be shared? // TODO: Do I benefit with Rc<Rule> below so Rule can be shared?
enum Rule { enum Rule {
// Produce geometry, and possibly recurse further: // Produce geometry, and possibly recurse further:
Recurse(fn () -> Vec<RuleStep>), Recurse(fn () -> RuleStep),
// Stop recursing here: // Stop recursing here:
EmptyRule, EmptyRule,
} }
// TODO: Rename rules? // TODO: Rename rules?
// TODO: It may be possible to have just a 'static' rule that requires
// no function call.
struct RuleStep { struct RuleStep {
// The geometry generated by this rule on its own - and none of // The geometry generated by this rule on its own (not by any of
// the child rules. // the child rules).
geom: OpenMesh, geom: OpenMesh,
// The next rule to run. If EmptyRule, then stop here (and // Child rules, paired with the transform that will be applied to
// 'xform' is irrelevant). // all of their geometry
rule: Box<Rule>, children: Vec<(Rule, Matrix4<f32>)>,
// The transformation to apply to geometry generated by 'rule' and
// any child rules.
xform: Matrix4<f32>,
} }
// is there a better way to do this? // is there a better way to do this?
@ -361,7 +375,7 @@ fn cube() -> OpenMesh {
}.transform(geometry::Translation3::new(-0.5, -0.5, -0.5).to_homogeneous()) }.transform(geometry::Translation3::new(-0.5, -0.5, -0.5).to_homogeneous())
} }
fn cube_thing_rule() -> Vec<RuleStep> { fn cube_thing_rule() -> RuleStep {
let mesh = cube(); let mesh = cube();
@ -381,18 +395,20 @@ fn cube_thing_rule() -> Vec<RuleStep> {
geometry::Rotation3::from_axis_angle(z, -qtr).to_homogeneous(), geometry::Rotation3::from_axis_angle(z, -qtr).to_homogeneous(),
]; ];
let gen_rulestep = |rot: &Matrix4<f32>| -> RuleStep { let gen_rulestep = |rot: &Matrix4<f32>| -> (Rule, Matrix4<f32>) {
let m: Matrix4<f32> = rot * let m: Matrix4<f32> = rot *
Matrix4::new_scaling(0.5) * Matrix4::new_scaling(0.5) *
geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous(); geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous();
let r = Rule::Recurse(cube_thing_rule); (Rule::Recurse(cube_thing_rule), m)
let m2 = mesh.transform(m);
RuleStep { geom: m2, rule: Box::new(r), xform: m }
}; };
turns.iter().map(gen_rulestep).collect() RuleStep {
geom: mesh,
children: turns.iter().map(gen_rulestep).collect(),
}
} }
// TODO: This doesn't produce a central cube; it's like it's half an
// iteration off.
// Have I any need of this after making OpenMesh? // Have I any need of this after making OpenMesh?
/* /*
@ -473,38 +489,34 @@ impl<'a> Iterator for MeshBound<'a> {
fn rule_to_mesh(rule: &Rule, iters_left: u32) -> (OpenMesh, u32) { fn rule_to_mesh(rule: &Rule, iters_left: u32) -> (OpenMesh, u32) {
let mut mesh = empty_mesh();
let mut nodes: u32 = 1; let mut nodes: u32 = 1;
if iters_left <= 0 { if iters_left <= 0 {
return (mesh, nodes); return (empty_mesh(), nodes);
} }
match rule { match rule {
Rule::Recurse(func) => { Rule::Recurse(f) => {
for step in func() { let rs: RuleStep = f();
let subrule: Rule = *step.rule;
let subxform: Matrix4<f32> = step.xform;
let geom: OpenMesh = step.geom;
mesh = mesh.connect_single(&geom); // Get sub-geometry (from child rules) and transform it:
let subgeom: Vec<(OpenMesh, Matrix4<f32>, u32)> = rs.children.iter().map(|(subrule, subxform)| {
let (mut submesh, subnodes) = rule_to_mesh( let (m,n) = rule_to_mesh(subrule, iters_left - 1);
&subrule, iters_left - 1); (m, *subxform, n)
}).collect();
submesh = submesh.transform(subxform); // Tally up node count:
subgeom.iter().for_each(|(_,_,n)| nodes += n);
nodes += subnodes; let g: Vec<OpenMesh> = subgeom.iter().map(|(m,x,_)| m.transform(*x)).collect();
mesh = mesh.connect_single(&submesh); // Connect geometry from this rule (not child rules):
} return (rs.geom.connect(&g), nodes);
} }
Rule::EmptyRule => { Rule::EmptyRule => {
// do nothing return (empty_mesh(), nodes);
} }
} }
(mesh, nodes)
} }
fn main() { fn main() {
@ -570,9 +582,10 @@ fn main() {
let r = Rule::Recurse(cube_thing_rule); let r = Rule::Recurse(cube_thing_rule);
let max_iters = 2; let max_iters = 4;
println!("Running rules..."); println!("Running rules...");
let (cubemesh_, nodes) = rule_to_mesh(&r, max_iters); let (cubemesh_, nodes) = rule_to_mesh(&r, max_iters);
println!("Converting mesh...");
let cubemesh = cubemesh_.to_trimesh().unwrap(); let cubemesh = cubemesh_.to_trimesh().unwrap();
println!("Collected {} nodes, produced {} faces, {} vertices", println!("Collected {} nodes, produced {} faces, {} vertices",
nodes, cubemesh.no_faces(), cubemesh.no_vertices()); nodes, cubemesh.no_faces(), cubemesh.no_vertices());
@ -591,7 +604,4 @@ fn main() {
s s
}; };
*/ */
// TEMP (while I figure shit out)
println!("DEBUG-------------------------------");
} }