Meh, kind of over trying to roll my own subdivision crap
This commit is contained in:
parent
701b1df915
commit
545b7c9a60
15
README.md
15
README.md
@ -2,17 +2,10 @@
|
||||
|
||||
## Highest priority:
|
||||
|
||||
- Add better docs and possibly abstraction to `dcel.rs`, *before*
|
||||
it's widely used. Possibly rename too.
|
||||
- `dcel.rs` needs a helper method for subdivision.
|
||||
- Redo `examples::parametric_mesh` along the following lines:
|
||||
- DONE: Make a crappy mesh (as a DCEL) like my 'early' method.
|
||||
- Use my method from around 2020-05-21, and my pile of loose notes,
|
||||
to subdivide - with the help of DCEL.
|
||||
- Document `parametric_mesh` better. It is pretty hairy and could
|
||||
also benefit from some modularity.
|
||||
- parametric_mesh: My err/max_err code seems to sometimes give very high
|
||||
dt values, e.g. if I use just a translation as my transform
|
||||
- Just scrap `parametric_mesh` as much as possible and use existing
|
||||
tools (e.g. OpenSubdiv) because this DCEL method is just painful for
|
||||
what it is and I have some questions on how it can even work
|
||||
theoretically.
|
||||
- Get identical or near-identical meshes to `ramhorn_branch` from
|
||||
Python. (Should just be a matter of tweaking parameters.)
|
||||
- Look at performance.
|
||||
|
||||
68
src/dcel.rs
68
src/dcel.rs
@ -134,10 +134,9 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
}
|
||||
|
||||
for (i,h) in self.halfedges.iter().enumerate() {
|
||||
let twin = if h.has_twin {
|
||||
format!(", twin half-edge {}", h.twin_halfedge)
|
||||
let twin = if h.has_twin { format!(", twin half-edge {}", h.twin_halfedge)
|
||||
} else {
|
||||
String::from("")
|
||||
format!(", no twin")
|
||||
};
|
||||
let v1 = self.verts[h.vert].v;
|
||||
let v2 = self.verts[self.halfedges[h.next_halfedge].vert].v;
|
||||
@ -485,22 +484,43 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
(f_n, [e_n, e_n+1, e_n+2])
|
||||
}
|
||||
|
||||
pub fn split_face(&mut self, face: usize, verts: Vec<V>) {
|
||||
/// Splits a face (assumed to be a triangle) in this mesh by splitting
|
||||
/// its half-edges, in order, at the vertices given, and then creating
|
||||
/// half-edges between each pair of them.
|
||||
///
|
||||
/// This returns; (new face indices, updated face indices). That is,
|
||||
/// the first vector is new face indices that were created, and the
|
||||
/// second is existing face indices that were reused (but the faces
|
||||
/// were updated).
|
||||
///
|
||||
/// This splits the given face into 4 smaller faces, and it splits the
|
||||
/// three *bordering* faces each into 2 faces. That is, this
|
||||
/// increases the number of faces by 6 and the number of vertices
|
||||
/// by 3.
|
||||
///
|
||||
/// Right now, this requires that every halfedge of `face` has a twin.
|
||||
/// If this is not the case, this returns `None` and does not subdivide.
|
||||
/// This behavior will likely be changed in the future.
|
||||
///
|
||||
/// Disclaimer: This code is completely awful; just don't use it.
|
||||
pub fn full_subdiv_face(&mut self, face: usize, verts: Vec<V>) -> Option<(Vec<usize>, Vec<usize>)> {
|
||||
// 'verts' maps 1:1 to vertices for 'face' (i.e. face_to_verts).
|
||||
|
||||
let mut edge_idx = self.faces[face].halfedge;
|
||||
let n = verts.len();
|
||||
|
||||
//
|
||||
let new_edges: Vec<(usize, usize)> = verts.iter().map(|v| {
|
||||
let mut faces_new = vec![];
|
||||
let mut faces_upd = vec![];
|
||||
|
||||
println!("DEBUG: halfedge 3: {:?}", self.halfedges[3]);
|
||||
println!("DEBUG: halfedge {}: {:?}", self.halfedges[3].next_halfedge, self.halfedges[self.halfedges[3].next_halfedge]);
|
||||
let mut fail = false;
|
||||
let new_edges: Vec<(usize, usize)> = verts.iter().map(|v| {
|
||||
|
||||
// As we iterate over every vertex, we walk the half-edges:
|
||||
let mut edge = self.halfedges[edge_idx].clone();
|
||||
if !edge.has_twin {
|
||||
panic!("Halfedge {} has no twin, and split_face (for now) requires twins", edge_idx);
|
||||
println!("Halfedge {} has no twin, and split_face (for now) requires twins", edge_idx);
|
||||
fail = true;
|
||||
return (0,0);
|
||||
}
|
||||
// TODO: Remove the above limitation and just don't try to split
|
||||
// nonexistent twins. I think all logic works the same way.
|
||||
@ -516,10 +536,8 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
// Half of edge_idx is split into j_edge.
|
||||
// Half of twin_idx (its twin) is split into i_edge.
|
||||
|
||||
println!("DEBUG: edge_idx={} next_idx={} twin_idx={} i_edge={} j_edge={}", edge_idx, next_idx, twin_idx, i_edge, j_edge);
|
||||
// This is where the vertex will be inserted:
|
||||
let v_idx = self.num_verts;
|
||||
println!("DEBUG: adding v_idx={}", v_idx);
|
||||
self.verts.push(DCELVertex {
|
||||
v: *v,
|
||||
halfedge: i_edge, // j_edge is also fine
|
||||
@ -531,7 +549,6 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
|
||||
twin.twin_halfedge = j_edge;
|
||||
let i_next = twin.next_halfedge;
|
||||
println!("DEBUG: new twin of {} = {}; new twin of {} = {}", edge_idx, i_edge, twin_idx, j_edge);
|
||||
|
||||
self.halfedges.push(DCELHalfEdge {
|
||||
vert: v_idx,
|
||||
@ -541,8 +558,6 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
next_halfedge: i_next,
|
||||
prev_halfedge: twin_idx,
|
||||
}); // i_edge
|
||||
println!("DEBUG: edge {}: vert {} twin {} next {} prev {} (ends at vertex {})", i_edge, v_idx, edge_idx, i_next, twin_idx, self.halfedges[self.halfedges[twin_idx].next_halfedge].vert);
|
||||
|
||||
self.halfedges.push(DCELHalfEdge {
|
||||
vert: v_idx,
|
||||
face: 0, // This is set properly in the next loop
|
||||
@ -551,7 +566,6 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
next_halfedge: j_next,
|
||||
prev_halfedge: edge_idx,
|
||||
}); // j_edge
|
||||
println!("DEBUG: edge {}: vert {} twin {} next {} prev {} (ends at vertex {})", j_edge, v_idx, twin_idx, j_next, edge_idx, self.halfedges[self.halfedges[edge_idx].next_halfedge].vert);
|
||||
|
||||
self.num_halfedges += 2;
|
||||
|
||||
@ -565,7 +579,9 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
r
|
||||
}).collect();
|
||||
|
||||
println!("DEBUG: {:?}", new_edges);
|
||||
if fail {
|
||||
return None;
|
||||
}
|
||||
|
||||
// We then must connect some edges up 'across' vertices
|
||||
// in order to form the smaller face at each vertex.
|
||||
@ -585,9 +601,6 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
// And the face here:
|
||||
let face_new = self.num_faces;
|
||||
|
||||
println!("DEBUG: i0={} i1={} ep_idx={} en_idx={} e_cross_idx={} e_twin_idx={} face_new={}",
|
||||
i0, i1, ep_idx, en_idx, e_cross_idx, e_twin_idx, face_new);
|
||||
|
||||
// So, the vertex for i0 had two halfedges (one pointing in,
|
||||
// one pointing out). We split both those half-edges earlier.
|
||||
// en_idx & ep_idx are the halves that are nearest the
|
||||
@ -604,7 +617,6 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
next_halfedge: ep_idx,
|
||||
prev_halfedge: en_idx,
|
||||
}); // e_cross_idx
|
||||
println!("DEBUG: edge {}: vert {} twin {} next {} prev {} (ends at vertex {})", e_cross_idx, self.halfedges[en_idx].vert, e_twin_idx, ep_idx, en_idx, self.halfedges[self.halfedges[e_cross_idx].next_halfedge].vert);
|
||||
|
||||
// It also requires a twin half-edge. These all form a single
|
||||
// central face with each edge sharing a boundary with the
|
||||
@ -617,24 +629,21 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
next_halfedge: 0, // TODO
|
||||
prev_halfedge: 0, // TODO
|
||||
}); // e_twin_idx
|
||||
println!("DEBUG: edge {}: vert {} twin {} next/prev incorrect", e_twin_idx, self.halfedges[ep_idx].vert, e_cross_idx);
|
||||
|
||||
self.num_halfedges += 2;
|
||||
|
||||
// en/ep also need directed to 'new' edges and each other
|
||||
self.halfedges[en_idx].prev_halfedge = ep_idx;
|
||||
self.halfedges[en_idx].next_halfedge = e_cross_idx;
|
||||
println!("DEBUG: edge {}: now next {} prev {} ends at vert {}", en_idx, e_cross_idx, ep_idx, self.halfedges[self.halfedges[en_idx].next_halfedge].vert);
|
||||
|
||||
self.halfedges[ep_idx].next_halfedge = en_idx;
|
||||
self.halfedges[ep_idx].prev_halfedge = e_cross_idx;
|
||||
println!("DEBUG: edge {}: now next {} prev {} ends at vert {}", ep_idx, en_idx, e_cross_idx, self.halfedges[self.halfedges[ep_idx].next_halfedge].vert);
|
||||
|
||||
self.halfedges[ep_idx].face = face_new;
|
||||
|
||||
self.faces.push(DCELFace {
|
||||
halfedge: e_cross_idx, // en_idx or ep_idx is fine too
|
||||
});
|
||||
}); // face_new
|
||||
faces_new.push(face_new);
|
||||
self.num_faces += 1;
|
||||
|
||||
// We also need to split the opposite side to make the two
|
||||
@ -674,8 +683,10 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
self.faces.push(DCELFace {
|
||||
halfedge: outer2, // base2 or edge_side2 is fine too
|
||||
});
|
||||
faces_new.push(self.num_faces);
|
||||
self.num_faces += 1;
|
||||
self.faces[face1].halfedge = outer1; // base1 or edge_side1 is fine too
|
||||
faces_upd.push(face1);
|
||||
|
||||
self.halfedges[outer1].next_halfedge = edge_side1;
|
||||
self.halfedges[outer1].prev_halfedge = base1;
|
||||
@ -691,13 +702,9 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
self.halfedges[base2].prev_halfedge = outer2;
|
||||
self.halfedges[base2].next_halfedge = edge_side2;
|
||||
|
||||
println!("DEBUG: base1={} base2={} outer1={} outer2={} face1={} face2={} edge_side1={} edge_side2={}",
|
||||
base1, base2, outer1, outer2, face1, face2, edge_side1, edge_side2);
|
||||
e_twin_idx
|
||||
}).collect();
|
||||
|
||||
println!("DEBUG: cross_edges={:?}", twin_edges);
|
||||
|
||||
for i0 in 0..n {
|
||||
let i1 = (i0 + 1) % n;
|
||||
|
||||
@ -709,6 +716,9 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
||||
// We need something at this index, and the other three already have
|
||||
// indices, so reuse it for the smaller central face:
|
||||
self.faces[face].halfedge = e_twin_idx;
|
||||
faces_upd.push(face);
|
||||
|
||||
return Some((faces_new, faces_upd));
|
||||
}
|
||||
|
||||
pub fn convert_mesh<F>(&self, f: F) -> Mesh
|
||||
|
||||
@ -1268,11 +1268,11 @@ pub fn test_parametric() -> Mesh {
|
||||
vertex( 1.0, 1.0, 0.0),
|
||||
vertex( 1.0, -1.0, 0.0),
|
||||
];
|
||||
let base_verts = util::subdivide_cycle(&base_verts, 4);
|
||||
let base_verts = util::subdivide_cycle(&base_verts, 2);
|
||||
//let base_verts = util::subdivide_cycle(&base_verts, 16);
|
||||
|
||||
let t0 = 0.0;
|
||||
let t1 = 15.0;
|
||||
let t1 = 15;
|
||||
let xform = |t: f32| -> Transform {
|
||||
id().
|
||||
translate(0.0, 0.0, t/5.0).
|
||||
@ -1280,7 +1280,7 @@ pub fn test_parametric() -> Mesh {
|
||||
scale((0.8).powf(t))
|
||||
};
|
||||
|
||||
crate::rule::parametric_mesh(base_verts, xform, t0, t1, 0.001)
|
||||
crate::rule::parametric_mesh(base_verts, xform, t0, t1, 0.01)
|
||||
}
|
||||
|
||||
pub fn test_dcel(fname: &str) {
|
||||
@ -1325,18 +1325,19 @@ pub fn test_dcel(fname: &str) {
|
||||
println!("f3 verts: {:?}", mesh.face_to_verts(f3));
|
||||
println!("f4 verts: {:?}", mesh.face_to_verts(f4));
|
||||
|
||||
println!("DCEL mesh: ");
|
||||
mesh.print();
|
||||
//println!("DCEL mesh: ");
|
||||
//mesh.print();
|
||||
|
||||
mesh.split_face(f1, vec![
|
||||
let faces = mesh.full_subdiv_face(f1, vec![
|
||||
vertex(-0.5, 0.0, 0.0),
|
||||
vertex(0.0, 0.5, 0.0),
|
||||
vertex(0.0, 0.0, 0.0),
|
||||
]);
|
||||
println!("full_subdiv_face returned: {:?}", faces);
|
||||
|
||||
println!("DCEL mesh after subdiv");
|
||||
mesh.check();
|
||||
mesh.print();
|
||||
//println!("DCEL mesh after subdiv");
|
||||
//mesh.check();
|
||||
//mesh.print();
|
||||
|
||||
let mesh_conv = mesh.convert_mesh(|i| i);
|
||||
|
||||
|
||||
81
src/rule.rs
81
src/rule.rs
@ -1,6 +1,7 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::rc::Rc;
|
||||
use std::f32;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::mesh::{Mesh, MeshFunc, VertexUnion};
|
||||
use crate::xform::{Transform, Vertex};
|
||||
@ -658,11 +659,25 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
|
||||
|
||||
// A stack of face indices for faces in 'mesh' that are still
|
||||
// undergoing subdivision
|
||||
let mut stack: Vec<usize> = (0..mesh.num_faces).collect();
|
||||
|
||||
while !stack.is_empty() {
|
||||
let face = stack.pop().unwrap();
|
||||
println!("DEBUG: Examining face: {:?}", face);
|
||||
let mut stack: HashMap<usize, usize> = (0..mesh.num_faces).map(|i| (i, 0)).collect();
|
||||
|
||||
let max_subdiv = 1;
|
||||
|
||||
// This is masked off because it's just horrible:
|
||||
while false && !stack.is_empty() {
|
||||
|
||||
let (face, count) = match stack.iter().next() {
|
||||
None => break,
|
||||
Some((f, c)) => (*f, *c),
|
||||
};
|
||||
stack.remove(&face);
|
||||
|
||||
println!("DEBUG: Examining face: {:?} ({} subdivs)", face, count);
|
||||
|
||||
if count >= max_subdiv {
|
||||
continue;
|
||||
}
|
||||
|
||||
let v_idx = mesh.face_to_verts(face);
|
||||
if v_idx.len() != 3 {
|
||||
@ -703,17 +718,18 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
|
||||
|
||||
let d = p.xyz().dot(&normal);
|
||||
|
||||
if d.is_nan() {
|
||||
println!("DEBUG: p={:?} normal={:?}", p, normal);
|
||||
println!("DEBUG: a={:?} b={:?}", a, b);
|
||||
println!("DEBUG: v0={:?} v1={:?} v2={:?}", v0, v1, v2);
|
||||
//panic!("Distance is NaN?");
|
||||
println!("DEBUG: distance is NaN?");
|
||||
continue;
|
||||
}
|
||||
|
||||
println!("DEBUG: t_mid={} v_mid={},{},{} p={},{},{}", t_mid, v_mid.x, v_mid.y, v_mid.z, p.x, p.y, p.z);
|
||||
println!("DEBUG: d={}", d);
|
||||
|
||||
// DEBUG
|
||||
/*
|
||||
let n = verts.len();
|
||||
verts.push(p);
|
||||
faces.push(face.verts[0]);
|
||||
faces.push(face.verts[1]);
|
||||
faces.push(n);
|
||||
*/
|
||||
if (d <= max_err) {
|
||||
// This triangle is already in the mesh, and already popped
|
||||
// off of the stack. We're done.
|
||||
@ -728,28 +744,35 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
|
||||
|
||||
// This split is done in 'parameter' space:
|
||||
let pairs = [(0,1), (1,2), (0,2)];
|
||||
let mut mids: Vec<Vertex> = pairs.iter().map(|(i,j)| {
|
||||
let mut mids: Vec<VertexTrajectory> = pairs.iter().map(|(i,j)| {
|
||||
let t = (tr[*i].t + tr[*j].t) / 2.0;
|
||||
let v = (tr[*i].frame_vert + tr[*j].frame_vert) / 2.0;
|
||||
f(t).mtx * v
|
||||
VertexTrajectory {
|
||||
vert: f(t).mtx * v,
|
||||
frame_vert: v,
|
||||
t: t,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
// DEBUG
|
||||
//let n = verts.len();
|
||||
// Index n+0 is (0,1), n+1 is (1,2), n+2 is (0,2)
|
||||
/*
|
||||
verts.append(&mut mids);
|
||||
faces[face.face] = n + 0;
|
||||
faces[face.face + 1] = n + 1;
|
||||
faces[face.face + 2] = n + 2;
|
||||
faces.extend_from_slice(&[
|
||||
face.verts[0], n + 0, n + 2,
|
||||
face.verts[1], n + 1, n + 0,
|
||||
face.verts[2], n + 2, n + 1,
|
||||
//n + 0, n + 1, n + 2,
|
||||
]);
|
||||
*/
|
||||
// TODO: Get indices of added faces and put these on the stack too
|
||||
match mesh.full_subdiv_face(face, mids) {
|
||||
None => {},
|
||||
Some((new, upd)) => {
|
||||
|
||||
// The 'updated' faces can remain on the stack if they
|
||||
// already were there. If they were taken off of the stack
|
||||
// because they were too small, they are now even smaller,
|
||||
// so it is fine to leave them off.
|
||||
stack.extend(new.iter().map(|i| (*i, count + 1)));
|
||||
stack.extend(upd.iter().map(|i| (*i, count + 1)));
|
||||
// TODO: Why does the resultant mesh have holes if I do this
|
||||
// and then increase max_subdiv?
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//mesh.print();
|
||||
|
||||
return mesh.convert_mesh(|i| i.vert );
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user