Open Xin-gif opened 4 years ago
Same problem! Mid-point subdivision will produce four new faces for the original one. So a long-narrow face will be subdivided to four more long-narrow faces.
Here is my modified version of original trimesh.remesh
. It only subdivides the longest edge of each face and thus produces only two new faces. It works fine in my project.
# coding: utf8
"""
modified from trimesh.remesh, only subdivide the longest edge
"""
import numpy as np
from trimesh import grouping
from trimesh import util
def subdivide(vertices,
faces,
face_index=None):
"""
Subdivide a mesh into smaller triangles.
Note that if `face_index` is passed, only those
faces will be subdivided and their neighbors won't
be modified making the mesh no longer "watertight."
Parameters
------------
vertices : (n, 3) float
Vertices in space
faces : (m, 3) int
Indexes of vertices which make up triangular faces
face_index : faces to subdivide.
if None: all faces of mesh will be subdivided
if (n,) int array of indices: only specified faces
Returns
----------
new_vertices : (q, 3) float
Vertices in space
new_faces : (p, 3) int
Remeshed faces
"""
if face_index is None:
face_index = np.arange(len(faces))
else:
face_index = np.asanyarray(face_index)
# the (c, 3) int array of vertex indices
faces_subset = faces[face_index] # (F,3)
# find max edge of each face
face_edges = faces_subset[:, [0, 1, 1, 2, 2, 0]].reshape((-1, 3, 2)) # (F,3,2)
face_edges_length = ((np.diff(vertices[face_edges], axis=2) ** 2).sum(axis=3) ** 0.5).reshape((-1, 3)) # (F,3)
face_edges_argmax = np.argmax(face_edges_length, axis=1) # (F,)
face_max_edge = face_edges[np.arange(len(face_edges_argmax)), face_edges_argmax] # (F,2)
# subdivide max_edge
mid = vertices[face_max_edge].mean(axis=1)
mid_idx = np.arange(len(mid)) + len(vertices)
# find another vertex of triangle out of max edge
vertex_in_edge = np.full_like(faces_subset, fill_value=False)
for i in range(faces_subset.shape[1]):
for j in range(face_max_edge.shape[1]):
vertex_in_edge[:, i] = np.logical_or(vertex_in_edge[:, i], faces_subset[:, i] == face_max_edge[:, j])
another_vertices = faces_subset[np.logical_not(vertex_in_edge)]
# the new faces_subset with correct winding
f = np.column_stack([another_vertices,
face_max_edge[:, 0],
mid_idx,
mid_idx,
face_max_edge[:, 1],
another_vertices,
]).reshape((-1, 3))
# add new faces_subset per old face
new_faces = np.vstack((faces, f[len(face_index):]))
# replace the old face with a smaller face
new_faces[face_index] = f[:len(face_index)]
new_vertices = np.vstack((vertices, mid))
return new_vertices, new_faces
def subdivide_to_size(vertices,
faces,
max_edge,
max_iter=10,
return_index=False):
"""
Subdivide a mesh until every edge is shorter than a
specified length.
Will return a triangle soup, not a nicely structured mesh.
Parameters
------------
vertices : (n, 3) float
Vertices in space
faces : (m, 3) int
Indices of vertices which make up triangles
max_edge : float
Maximum length of any edge in the result
max_iter : int
The maximum number of times to run subdivision
return_index : bool
If True, return index of original face for new faces
Returns
------------
vertices : (j, 3) float
Vertices in space
faces : (q, 3) int
Indices of vertices
index : (q, 3) int
Only returned if `return_index`, index of
original face for each new face.
"""
# store completed
done_face = []
done_vert = []
done_idx = []
# copy inputs and make sure dtype is correct
current_faces = np.array(
faces, dtype=np.int64, copy=True)
current_vertices = np.array(
vertices, dtype=np.float64, copy=True)
current_index = np.arange(len(faces))
# loop through iteration cap
for i in range(max_iter + 1):
# compute the length of every triangle edge
edge_length = (np.diff(
current_vertices[current_faces[:, [0, 1, 2, 0]], :3],
axis=1) ** 2).sum(axis=2) ** 0.5
# check edge length against maximum
too_long = (edge_length > max_edge).any(axis=1)
# faces that are OK
face_ok = ~too_long
# clean up the faces a little bit so we don't
# store a ton of unused vertices
unique, inverse = grouping.unique_bincount(
current_faces[face_ok].flatten(),
return_inverse=True)
# store vertices and faces meeting criteria
done_vert.append(current_vertices[unique])
done_face.append(inverse.reshape((-1, 3)))
done_idx.append(current_index[face_ok])
# met our goals so exit
if not too_long.any():
break
current_index = np.tile(current_index[too_long], (2, 1)).T.ravel()
# run subdivision again
(current_vertices,
current_faces) = subdivide(current_vertices,
current_faces[too_long])
if i >= max_iter:
util.log.warning(
'subdivide_to_size reached maximum iterations before exit criteria!')
# stack sequence into nice (n, 3) arrays
final_vertices, final_faces = util.append_faces(
done_vert, done_face)
if len(final_vertices) == 0:
# max_iter is too small to generate short-enough edges, thus reserve current result
final_vertices, final_faces = current_vertices, current_faces
if return_index:
final_index = np.concatenate(done_idx)
assert len(final_index) == len(final_faces)
return final_vertices, final_faces, final_index
return final_vertices, final_faces
BTW, @mikedh would you like to involve this new subdivision into trimesh? Maybe another remesh, like remesh2?
Since trimesh.remesh
is used in Trimesh.subdivide_to_size(..)
, some coding work is still required to integrate this version. I'd like to do this job if you like.
Hi,
I'm now trying to refine and decimate the meshes in order to get them ready for further process. I used mesh.subdivide() function to subdivide the shapes that don't have enough vertices and faces. But this function uses mid-point subdivision. It works well but only if initially shapes have polygons having roughly the same size. Otherwise, some (already) small faces would get extremely finely subdivided. I'm wondering is there any more advanced remeshing functions that i can use to fix this? Also i couldn't find any function to do the decimation, could you please let me know if there's a function for that?
Looking forward to hearing from you. Thank you