From b5af2a769ccfe74e583003d57e2bb4d0e6c35b79 Mon Sep 17 00:00:00 2001 From: William Silversmith Date: Tue, 12 Aug 2025 17:31:38 -0400 Subject: [PATCH 1/7] wip: try to use new zmesh.chunk_mesh --- igneous/tasks/mesh/multires.py | 69 ++++++++++++++-------------------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/igneous/tasks/mesh/multires.py b/igneous/tasks/mesh/multires.py index ee458f46..ccdcf4c9 100644 --- a/igneous/tasks/mesh/multires.py +++ b/igneous/tasks/mesh/multires.py @@ -109,7 +109,11 @@ def process_mesh( return (None, None) lods = [ - create_octree_level_from_mesh(lods[lod], chunk_shape, lod, len(lods), grid_origin, mesh_shape) + create_octree_level_from_mesh( + lods[lod], chunk_shape, + lod, len(lods), + grid_origin, mesh_shape + ) for lod in range(len(lods)) ] fragment_positions = [ nodes for submeshes, nodes in lods ] @@ -132,6 +136,9 @@ def process_mesh( mesh_binaries = [] for lod, submeshes in enumerate(lods): for frag_no, submesh in enumerate(submeshes): + if submesh.empty(): + continue + submesh.vertices = to_stored_model_space( submesh.vertices, manifest, lod=lod, @@ -222,7 +229,8 @@ def MultiResShardedMeshMergeTask( # important to iterate this way to avoid # creating a copy of meshes vs. { ... for in } for label in labels: - meshes[label] = Mesh.concatenate(*meshes[label]) + mesh = Mesh.concatenate(*meshes[label], segid=label) + meshes[label] = mesh.consolidate() del labels fname, shard = create_mesh_shard( @@ -324,7 +332,7 @@ def generate_lods( ) lods.append( - Mesh(*simplifier.getMesh()) + Mesh(*simplifier.getMesh(), segid=label) ) return lods @@ -493,7 +501,7 @@ def less_msb(x: int, y: int) -> bool: return lhs[msd] - rhs[msd] def determine_mesh_shape_from_lods( - lods: list[trimesh.Trimesh], + lods: list[Mesh], ) -> Tuple[npt.NDArray[np.float32], npt.NDArray[np.int_]]: mesh_starts = [np.min(lod.vertices, axis=0) for lod in lods] mesh_ends = [np.max(lod.vertices, axis=0) for lod in lods] @@ -504,35 +512,17 @@ def determine_mesh_shape_from_lods( return grid_origin, mesh_shape def generate_gridded_submeshes( - mesh: trimesh.Trimesh, + mesh: Mesh, offset: np.ndarray, - grid_size: Vec, scale: Vec, -) -> Iterator[Tuple[trimesh.Trimesh, Tuple[int, int, int]]]: - nx, ny, nz = np.eye(3) - ox, oy, oz = offset * np.eye(3) - - for x in range(0, grid_size.x): - # list(...) required b/c it doesn't like Vec classes - mesh_x = trimesh.intersections.slice_mesh_plane(mesh, plane_normal=nx, plane_origin=list(nx*x*scale.x+ox)) - mesh_x = trimesh.intersections.slice_mesh_plane(mesh_x, plane_normal=-nx, plane_origin=list(nx*(x+1)*scale.x+ox)) - for y in range(0, grid_size.y): - mesh_y = trimesh.intersections.slice_mesh_plane(mesh_x, plane_normal=ny, plane_origin=list(ny*y*scale.y+oy)) - mesh_y = trimesh.intersections.slice_mesh_plane(mesh_y, plane_normal=-ny, plane_origin=list(ny*(y+1)*scale.y+oy)) - for z in range(0, grid_size.z): - mesh_z = trimesh.intersections.slice_mesh_plane(mesh_y, plane_normal=nz, plane_origin=list(nz*z*scale.z+oz)) - mesh_z = trimesh.intersections.slice_mesh_plane(mesh_z, plane_normal=-nz, plane_origin=list(nz*(z+1)*scale.z+oz)) - - if len(mesh_z.vertices) == 0: - continue - - # test for totally degenerate meshes by checking if - # all of two axes match, meaning the mesh must be a - # point or a line. - if np.sum([ np.all(mesh_z.vertices[:,i] == mesh_z.vertices[0,i]) for i in range(3) ]) >= 2: - continue - - yield mesh_z, (x, y, z) +) -> Iterator[tuple[Mesh, tuple[int, int, int]]]: + + chunked_meshes = zmesh.chunk_mesh( + mesh, + chunk_size=scale, + grid_origin=offset, + ) + yield from chunked_meshes.items() def retriangulate_mesh( mesh: trimesh.Trimesh, @@ -543,12 +533,12 @@ def retriangulate_mesh( """ Retriangulate the input mesh to avoid any cases where the boundaries of a triangle are split across the boundaries of the submeshes """ - new_mesh = trimesh.Trimesh() + new_mesh = Mesh() - for submesh, _ in generate_gridded_submeshes( - mesh, offset, grid_size, scale + for _, submesh in generate_gridded_submeshes( + mesh, offset, scale ): - new_mesh = trimesh.util.concatenate(new_mesh, submesh) + new_mesh = zmesh.Mesh.concatenate(new_mesh, submesh, id=mesh.id) return new_mesh @@ -559,7 +549,6 @@ def create_octree_level_from_mesh(mesh, chunk_shape, lod, num_lods, offset, grid This creates (2^lod)^3 submeshes. """ - mesh = trimesh.Trimesh(vertices=mesh.vertices, faces=mesh.faces) scale = Vec(*(np.array(chunk_shape) * (2**lod))) grid_size = Vec(*(np.ceil(grid_length / scale)), dtype=int) @@ -576,8 +565,8 @@ def create_octree_level_from_mesh(mesh, chunk_shape, lod, num_lods, offset, grid submeshes = [] nodes = [] - for submesh, node in generate_gridded_submeshes( - mesh, offset, grid_size, scale + for node, submesh in generate_gridded_submeshes( + mesh, offset, scale ): submeshes.append(submesh) nodes.append(node) @@ -587,7 +576,7 @@ def create_octree_level_from_mesh(mesh, chunk_shape, lod, num_lods, offset, grid *sorted(zip(submeshes, nodes), key=functools.cmp_to_key(lambda x, y: cmp_zorder(x[1], y[1]))) ) - # convert back from trimesh to CV Mesh class - submeshes = [ Mesh(m.vertices, m.faces) for m in submeshes ] + # convert back from zmesh.Mesh to CV Mesh class + submeshes = [ Mesh(m.vertices, m.faces, segid=mesh.id) for m in submeshes ] return (submeshes, nodes) From d3e086f9600b47c8f3c99d654ebf20ec3a1b0288 Mon Sep 17 00:00:00 2001 From: William Silversmith Date: Tue, 12 Aug 2025 18:40:30 -0400 Subject: [PATCH 2/7] wip: meshes appearing, but of low quality --- igneous/tasks/mesh/multires.py | 51 +++++++++++++--------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/igneous/tasks/mesh/multires.py b/igneous/tasks/mesh/multires.py index ccdcf4c9..aba37bb9 100644 --- a/igneous/tasks/mesh/multires.py +++ b/igneous/tasks/mesh/multires.py @@ -511,36 +511,25 @@ def determine_mesh_shape_from_lods( return grid_origin, mesh_shape -def generate_gridded_submeshes( - mesh: Mesh, - offset: np.ndarray, - scale: Vec, -) -> Iterator[tuple[Mesh, tuple[int, int, int]]]: - - chunked_meshes = zmesh.chunk_mesh( - mesh, - chunk_size=scale, - grid_origin=offset, - ) - yield from chunked_meshes.items() - def retriangulate_mesh( - mesh: trimesh.Trimesh, + mesh: Mesh, offset: np.ndarray, grid_size: Vec, scale: Vec, - ) -> trimesh.Trimesh: + ) -> Mesh: """ Retriangulate the input mesh to avoid any cases where the boundaries of a triangle are split across the boundaries of the submeshes """ new_mesh = Mesh() - for _, submesh in generate_gridded_submeshes( - mesh, offset, scale - ): - new_mesh = zmesh.Mesh.concatenate(new_mesh, submesh, id=mesh.id) + chunks = zmesh.chunk_mesh( + mesh, + chunk_size=scale, + grid_origin=offset, + ) - return new_mesh + new_mesh = zmesh.Mesh.concatenate(*chunks.values(), id=mesh.segid) + return new_mesh.merge_close_vertices(radius=1e-3) def create_octree_level_from_mesh(mesh, chunk_shape, lod, num_lods, offset, grid_length): """ @@ -563,20 +552,20 @@ def create_octree_level_from_mesh(mesh, chunk_shape, lod, num_lods, offset, grid if lod == num_lods - 1: return ([Mesh(mesh.vertices, mesh.faces)], ((0, 0, 0),)) - submeshes = [] - nodes = [] - for node, submesh in generate_gridded_submeshes( - mesh, offset, scale - ): - submeshes.append(submesh) - nodes.append(node) + grid = zmesh.chunk_mesh( + mesh, + chunk_size=scale, + grid_origin=offset, + ) # Sort in Z-curve order - submeshes, nodes = zip( - *sorted(zip(submeshes, nodes), - key=functools.cmp_to_key(lambda x, y: cmp_zorder(x[1], y[1]))) + nodes, submeshes = zip( + *sorted( + grid.items(), + key=functools.cmp_to_key(lambda x, y: cmp_zorder(x[0], y[0])) + ) ) # convert back from zmesh.Mesh to CV Mesh class - submeshes = [ Mesh(m.vertices, m.faces, segid=mesh.id) for m in submeshes ] + submeshes = [ Mesh(m.vertices, m.faces, segid=mesh.segid) for m in submeshes ] return (submeshes, nodes) From 7ad7a7b5c5f56d6a29c200efc5623a25496aa4a0 Mon Sep 17 00:00:00 2001 From: William Silversmith Date: Tue, 12 Aug 2025 18:44:57 -0400 Subject: [PATCH 3/7] fix: use a more reasonable radius for merging close vertices --- igneous/tasks/mesh/multires.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/igneous/tasks/mesh/multires.py b/igneous/tasks/mesh/multires.py index aba37bb9..e5ee654f 100644 --- a/igneous/tasks/mesh/multires.py +++ b/igneous/tasks/mesh/multires.py @@ -529,7 +529,7 @@ def retriangulate_mesh( ) new_mesh = zmesh.Mesh.concatenate(*chunks.values(), id=mesh.segid) - return new_mesh.merge_close_vertices(radius=1e-3) + return new_mesh.merge_close_vertices(radius=1e-5) def create_octree_level_from_mesh(mesh, chunk_shape, lod, num_lods, offset, grid_length): """ From 2e9bcdd3095cb4dab2baacf558bb818c3007d0c1 Mon Sep 17 00:00:00 2001 From: William Silversmith Date: Thu, 14 Aug 2025 00:52:01 -0400 Subject: [PATCH 4/7] fix: grid_origin was incorrect for these meshes, use minpt instead --- igneous/tasks/mesh/multires.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/igneous/tasks/mesh/multires.py b/igneous/tasks/mesh/multires.py index e5ee654f..ede8ac50 100644 --- a/igneous/tasks/mesh/multires.py +++ b/igneous/tasks/mesh/multires.py @@ -520,14 +520,7 @@ def retriangulate_mesh( """ Retriangulate the input mesh to avoid any cases where the boundaries of a triangle are split across the boundaries of the submeshes """ - new_mesh = Mesh() - - chunks = zmesh.chunk_mesh( - mesh, - chunk_size=scale, - grid_origin=offset, - ) - + chunks = zmesh.chunk_mesh(mesh, scale) new_mesh = zmesh.Mesh.concatenate(*chunks.values(), id=mesh.segid) return new_mesh.merge_close_vertices(radius=1e-5) @@ -552,11 +545,7 @@ def create_octree_level_from_mesh(mesh, chunk_shape, lod, num_lods, offset, grid if lod == num_lods - 1: return ([Mesh(mesh.vertices, mesh.faces)], ((0, 0, 0),)) - grid = zmesh.chunk_mesh( - mesh, - chunk_size=scale, - grid_origin=offset, - ) + grid = zmesh.chunk_mesh(mesh, scale) # Sort in Z-curve order nodes, submeshes = zip( From 3f4c017a9d75b6b8d081e212d192eb6674a83d77 Mon Sep 17 00:00:00 2001 From: William Silversmith Date: Thu, 14 Aug 2025 14:15:45 -0400 Subject: [PATCH 5/7] fix: use grid offsets --- igneous/tasks/mesh/multires.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/igneous/tasks/mesh/multires.py b/igneous/tasks/mesh/multires.py index ede8ac50..0583908c 100644 --- a/igneous/tasks/mesh/multires.py +++ b/igneous/tasks/mesh/multires.py @@ -520,7 +520,7 @@ def retriangulate_mesh( """ Retriangulate the input mesh to avoid any cases where the boundaries of a triangle are split across the boundaries of the submeshes """ - chunks = zmesh.chunk_mesh(mesh, scale) + chunks = zmesh.chunk_mesh(mesh, scale, offset) new_mesh = zmesh.Mesh.concatenate(*chunks.values(), id=mesh.segid) return new_mesh.merge_close_vertices(radius=1e-5) @@ -545,7 +545,7 @@ def create_octree_level_from_mesh(mesh, chunk_shape, lod, num_lods, offset, grid if lod == num_lods - 1: return ([Mesh(mesh.vertices, mesh.faces)], ((0, 0, 0),)) - grid = zmesh.chunk_mesh(mesh, scale) + grid = zmesh.chunk_mesh(mesh, scale, offset) # Sort in Z-curve order nodes, submeshes = zip( From 36aed006917b37b17d9a3d887266e95a23e03e38 Mon Sep 17 00:00:00 2001 From: William Silversmith Date: Mon, 18 Aug 2025 14:16:12 -0400 Subject: [PATCH 6/7] fix: remove unused parameter --- igneous/tasks/mesh/multires.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/igneous/tasks/mesh/multires.py b/igneous/tasks/mesh/multires.py index 0583908c..4997eb82 100644 --- a/igneous/tasks/mesh/multires.py +++ b/igneous/tasks/mesh/multires.py @@ -514,7 +514,6 @@ def determine_mesh_shape_from_lods( def retriangulate_mesh( mesh: Mesh, offset: np.ndarray, - grid_size: Vec, scale: Vec, ) -> Mesh: """ @@ -539,8 +538,7 @@ def create_octree_level_from_mesh(mesh, chunk_shape, lod, num_lods, offset, grid # at the higher level of the octree if lod > 0: upper_grid_scale = Vec(*(np.array(chunk_shape) * (2 ** (lod - 1)))) - upper_grid_shape = Vec(*np.ceil(grid_length / upper_grid_scale), dtype=int) - mesh = retriangulate_mesh(mesh, offset, upper_grid_shape, upper_grid_scale) + mesh = retriangulate_mesh(mesh, offset, upper_grid_scale) if lod == num_lods - 1: return ([Mesh(mesh.vertices, mesh.faces)], ((0, 0, 0),)) From ed11b5ad95819b09f614db4a77204939c2a54eb7 Mon Sep 17 00:00:00 2001 From: William Silversmith Date: Tue, 7 Oct 2025 18:22:12 -0400 Subject: [PATCH 7/7] install: set minimum zmesh version as 1.9.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 040d6395..9c4bb953 100755 --- a/requirements.txt +++ b/requirements.txt @@ -25,4 +25,4 @@ task-queue>=2.4.0 tqdm trimesh[easy] xs3d>=1.11.0 -zmesh>=1.4,<2.0 \ No newline at end of file +zmesh>=1.9.0,<2.0 \ No newline at end of file