mirror of
https://git.datalinker.icu/kijai/ComfyUI-Hunyuan3DWrapper.git
synced 2025-12-10 05:14:29 +08:00
195 lines
5.9 KiB
Python
195 lines
5.9 KiB
Python
"""Mesh data utilities."""
|
|
import random
|
|
import networkx as nx
|
|
import numpy as np
|
|
# import pyrr
|
|
from six.moves import range
|
|
import trimesh
|
|
from scipy.spatial.transform import Rotation
|
|
|
|
|
|
def to_mesh(vertices, faces, transpose=True, post_process=False):
|
|
if transpose:
|
|
vertices = vertices[:, [1, 2, 0]]
|
|
|
|
if faces.min() == 1:
|
|
faces = (np.array(faces) - 1).tolist()
|
|
mesh = trimesh.Trimesh(vertices=vertices, faces=faces, process=False)
|
|
|
|
if post_process:
|
|
mesh.merge_vertices()
|
|
mesh.update_faces(mesh.unique_faces())
|
|
mesh.fix_normals()
|
|
return mesh
|
|
|
|
|
|
def center_vertices(vertices):
|
|
"""Translate the vertices so that bounding box is centered at zero."""
|
|
vert_min = vertices.min(axis=0)
|
|
vert_max = vertices.max(axis=0)
|
|
vert_center = 0.5 * (vert_min + vert_max)
|
|
# vert_center = np.mean(vertices, axis=0)
|
|
return vertices - vert_center
|
|
|
|
|
|
def face_to_cycles(face):
|
|
"""Find cycles in face."""
|
|
g = nx.Graph()
|
|
for v in range(len(face) - 1):
|
|
g.add_edge(face[v], face[v + 1])
|
|
g.add_edge(face[-1], face[0])
|
|
return list(nx.cycle_basis(g))
|
|
|
|
|
|
def block_index(vertex, block_size=32):
|
|
return (vertex[2] // block_size, vertex[1] // block_size, vertex[0] // block_size)
|
|
|
|
def block_id(block_index, num_blocks=4):
|
|
return block_index[0] * num_blocks**2 + block_index[1] * num_blocks + block_index[2]
|
|
|
|
|
|
def normalize_vertices_scale(vertices, scale=0.95):
|
|
"""Scale the vertices so that the long axis of the bounding box is one."""
|
|
vert_min = vertices.min(axis=0)
|
|
vert_max = vertices.max(axis=0)
|
|
extents = (vert_max - vert_min).max()
|
|
return 2.0 * scale * vertices / (extents + 1e-6)
|
|
|
|
|
|
def quantize_process_mesh(vertices, faces, quantization_bits=8, block_first_order=True, block_size=32, num_blocks=4):
|
|
"""Quantize vertices, remove resulting duplicates and reindex faces."""
|
|
vertices = discretize(vertices, num_discrete=2**quantization_bits)
|
|
vertices, inv = np.unique(vertices, axis=0, return_inverse=True)
|
|
|
|
if block_first_order:
|
|
block_indices = np.array([block_index(v, block_size) for v in vertices])
|
|
block_ids = np.array([block_id(b, num_blocks) for b in block_indices])
|
|
sort_inds = np.lexsort((vertices[:, 0], vertices[:, 1], vertices[:, 2], block_ids))
|
|
else:
|
|
# Sort vertices by z then y then x.
|
|
sort_inds = np.lexsort(vertices.T)
|
|
|
|
vertices = vertices[sort_inds]
|
|
faces = [np.argsort(sort_inds)[inv[f]] for f in faces]
|
|
|
|
sub_faces = []
|
|
for f in faces:
|
|
cliques = face_to_cycles(f)
|
|
for c in cliques:
|
|
c_length = len(c)
|
|
if c_length > 2:
|
|
d = np.argmin(f)
|
|
sub_faces.append([f[(d + i) % c_length] for i in range(c_length)])
|
|
|
|
faces = sub_faces
|
|
|
|
# Sort faces by lowest vertex indices. If two faces have the same lowest
|
|
# index then sort by next lowest and so on.
|
|
faces.sort(key=lambda f: tuple(sorted(f)))
|
|
num_verts = vertices.shape[0]
|
|
vert_connected = np.equal(
|
|
np.arange(num_verts)[:, None], np.hstack(faces)[None]
|
|
).any(axis=-1)
|
|
vertices = vertices[vert_connected]
|
|
|
|
# Re-index faces to re-ordered vertices.
|
|
vert_indices = np.arange(num_verts) - np.cumsum(1 - vert_connected.astype("int"))
|
|
faces = [vert_indices[f].tolist() for f in faces]
|
|
|
|
return vertices, faces
|
|
|
|
|
|
def process_mesh(vertices, faces, quantization_bits=8, augment=True, augment_dict=None):
|
|
"""Process mesh vertices and faces."""
|
|
|
|
# Transpose so that z-axis is vertical.
|
|
vertices = vertices[:, [2, 0, 1]]
|
|
|
|
# Translate the vertices so that bounding box is centered at zero.
|
|
vertices = center_vertices(vertices)
|
|
|
|
if augment:
|
|
vertices = augment_mesh(vertices, **augment_dict)
|
|
|
|
# Scale the vertices so that the long diagonal of the bounding box is equal
|
|
# to one.
|
|
vertices = normalize_vertices_scale(vertices)
|
|
|
|
# Quantize and sort vertices, remove resulting duplicates, sort and reindex
|
|
# faces.
|
|
vertices, faces = quantize_process_mesh(
|
|
vertices, faces, quantization_bits=quantization_bits
|
|
)
|
|
vertices = undiscretize(vertices, num_discrete=2**quantization_bits)
|
|
|
|
|
|
# Discard degenerate meshes without faces.
|
|
return {
|
|
"vertices": vertices,
|
|
"faces": faces,
|
|
}
|
|
|
|
|
|
def load_process_mesh(mesh_obj_path, quantization_bits=8, augment=False, augment_dict=None):
|
|
"""Load obj file and process."""
|
|
# Load mesh
|
|
mesh = trimesh.load(mesh_obj_path, force='mesh', process=False)
|
|
return process_mesh(mesh.vertices, mesh.faces, quantization_bits, augment=augment, augment_dict=augment_dict)
|
|
|
|
|
|
def augment_mesh(vertices, scale_min=0.95, scale_max=1.05, rotation=0., jitter_strength=0.):
|
|
'''scale vertices by a factor in [0.75, 1.25]'''
|
|
|
|
# vertices [nv, 3]
|
|
for i in range(3):
|
|
# Generate a random scale factor
|
|
scale = random.uniform(scale_min, scale_max)
|
|
|
|
# independently applied scaling across each axis of vertices
|
|
vertices[:, i] *= scale
|
|
|
|
if rotation != 0.:
|
|
axis = [random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1)]
|
|
radian = np.pi / 180 * rotation
|
|
rotation = Rotation.from_rotvec(radian * np.array(axis))
|
|
vertices =rotation.apply(vertices)
|
|
|
|
|
|
if jitter_strength != 0.:
|
|
jitter_amount = np.random.uniform(-jitter_strength, jitter_strength)
|
|
vertices += jitter_amount
|
|
|
|
|
|
return vertices
|
|
|
|
|
|
def discretize(
|
|
t,
|
|
continuous_range = (-1, 1),
|
|
num_discrete: int = 128
|
|
):
|
|
lo, hi = continuous_range
|
|
assert hi > lo
|
|
|
|
t = (t - lo) / (hi - lo)
|
|
t *= num_discrete
|
|
t -= 0.5
|
|
|
|
return t.round().astype(np.int32).clip(min = 0, max = num_discrete - 1)
|
|
|
|
|
|
def undiscretize(
|
|
t,
|
|
continuous_range = (-1, 1),
|
|
num_discrete: int = 128
|
|
):
|
|
lo, hi = continuous_range
|
|
assert hi > lo
|
|
|
|
t = t.astype(np.float32)
|
|
|
|
t += 0.5
|
|
t /= num_discrete
|
|
return t * (hi - lo) + lo
|
|
|