Added autotune_cache file for Triton + Fixed "Mesh Texturing" + "PreProcess Image" node

This commit is contained in:
Bruno Fargnoli
2025-12-28 23:29:57 +01:00
parent 03884e1e89
commit e933ce06a4
3 changed files with 48831 additions and 21 deletions
+48757
View File
File diff suppressed because it is too large Load Diff
+65 -18
View File
@@ -18,6 +18,7 @@ import copy
import pymeshlab
import cumesh as CuMesh
import o_voxel
import meshlib.mrmeshnumpy as mrmeshnumpy
import meshlib.mrmeshpy as mrmeshpy
@@ -164,6 +165,8 @@ class Trellis2LoadModel:
def process(self, modelname, backend, device, low_vram, keep_models_loaded):
os.environ['OPENCV_IO_ENABLE_OPENEXR'] = '1'
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True" # Can save GPU memory
os.environ["FLEX_GEMM_AUTOTUNE_CACHE_PATH"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'autotune_cache.json')
os.environ["FLEX_GEMM_AUTOTUNER_VERBOSE"] = '1'
os.environ['ATTN_BACKEND'] = backend
torch.backends.cudnn.benchmark = False
@@ -1042,6 +1045,7 @@ class Trellis2Remesh:
"fill_holes": ("BOOLEAN", {"default":True}),
"fill_holes_max_perimeter": ("FLOAT",{"default":0.03,"min":0.001,"max":99.999,"step":0.001}),
"dual_contouring_resolution": (["Auto","128","256","512","1024","2048"],{"default":"Auto"}),
"remove_floaters": ("BOOLEAN",{"default":True}),
},
}
@@ -1051,9 +1055,12 @@ class Trellis2Remesh:
CATEGORY = "Trellis2Wrapper"
OUTPUT_NODE = True
def process(self, mesh, remesh_band, remesh_project, fill_holes, fill_holes_max_perimeter, dual_contouring_resolution):
def process(self, mesh, remesh_band, remesh_project, fill_holes, fill_holes_max_perimeter, dual_contouring_resolution, remove_floaters):
mesh_copy = copy.deepcopy(mesh)
if remove_floaters:
mesh_copy = remove_floater(mesh_copy)
aabb = [[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]]
vertices = mesh_copy.vertices
@@ -1334,9 +1341,10 @@ class Trellis2PostProcess2:
"required": {
"mesh": ("MESHWITHVOXEL",),
"fill_holes": ("BOOLEAN", {"default":True}),
"fill_holes_max_perimeter": ("FLOAT",{"default":0.03,"min":0.001,"max":99.999,"step":0.001}),
"fix_normals": ("BOOLEAN", {"default":False}),
"fix_face_orientation": ("BOOLEAN", {"default":True}),
"remove_duplicate_faces": ("BOOLEAN",{"default":True}),
"remove_duplicate_faces": ("BOOLEAN",{"default":True}),
"remove_infinite_values": ("BOOLEAN",{"default":True}),
},
}
@@ -1346,7 +1354,7 @@ class Trellis2PostProcess2:
CATEGORY = "Trellis2Wrapper"
OUTPUT_NODE = True
def process(self, mesh, fill_holes, fill_holes_max_perimeter, fix_face_orientation, remove_duplicate_faces):
def process(self, mesh, fill_holes, fix_normals, fix_face_orientation, remove_duplicate_faces, remove_infinite_values):
mesh_copy = copy.deepcopy(mesh)
vertices_np = mesh_copy.vertices.cpu().numpy()
@@ -1357,26 +1365,24 @@ class Trellis2PostProcess2:
print(f"Initial mesh: {len(trimesh.faces)} faces")
print(f"Is winding consistent? {trimesh.is_winding_consistent}")
if fix_face_orientation:
# 2. Fix the normals
# This aligns the winding of all faces to be consistent
if fix_normals:
print('Fixing normals ...')
trimesh.fix_normals()
# 3. Handle inverted orientations
# If the mesh is "inside out" (normals pointing inward),
# this attempts to flip them to point outward based on the mesh volume.
trimesh.fix_normals()
if fix_face_orientation:
if trimesh.is_watertight:
print('Mesh is watertight, fixing inversion ...')
Trimesh.repair.fix_inversion(trimesh)
Trimesh.repair.fix_inversion(trimesh)
else:
print('Mesh is not watertight, cannot fix inversion')
# 4. Optional: General Cleanup
# AI meshes often have duplicate vertices or degenerate faces
if remove_duplicate_faces:
print('Removing duplicate faces ...')
trimesh.remove_duplicate_faces()
trimesh.remove_infinite_values()
if remove_infinite_values:
print('Removing infinite values ...')
trimesh.remove_infinite_values()
if fill_holes:
print('Filling holes ...')
@@ -1391,7 +1397,46 @@ class Trellis2PostProcess2:
del trimesh
gc.collect()
return (mesh_copy,)
return (mesh_copy,)
class Trellis2OvoxelExportToGLB:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"mesh": ("MESHWITHVOXEL",),
"resolution": ([512,1024],{"default":1024}),
"texture_size": ([512,1024,2048,4096],{"default":2048}),
"target_face_num": ("INT",{"default":2000000,"min":500,"max":16000000}),
},
}
RETURN_TYPES = ("TRIMESH",)
RETURN_NAMES = ("trimesh",)
FUNCTION = "process"
CATEGORY = "Trellis2Wrapper"
OUTPUT_NODE = True
def process(self, mesh, resolution, texture_size, target_face_num):
mesh_copy = copy.deepcopy(mesh)
glb = o_voxel.postprocess.to_glb(
vertices=mesh_copy.vertices,
faces=mesh_copy.faces,
attr_volume=mesh_copy.attrs,
coords=mesh_copy.coords,
attr_layout=mesh_copy.layout,
grid_size=resolution,
aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
decimation_target=target_face_num,
texture_size=texture_size,
remesh=True,
remesh_band=1,
remesh_project=0,
use_tqdm=True,
)
return (glb,)
NODE_CLASS_MAPPINGS = {
"Trellis2LoadModel": Trellis2LoadModel,
@@ -1410,6 +1455,7 @@ NODE_CLASS_MAPPINGS = {
"Trellis2PreProcessImage": Trellis2PreProcessImage,
"Trellis2MeshRefiner": Trellis2MeshRefiner,
"Trellis2PostProcess2": Trellis2PostProcess2,
"Trellis2OvoxelExportToGLB": Trellis2OvoxelExportToGLB,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -1429,4 +1475,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Trellis2PreProcessImage": "Trellis2 - PreProcess Image",
"Trellis2MeshRefiner": "Trellis2 - Mesh Refiner",
"Trellis2PostProcess2": "Trellis2 - PostProcess Mesh 2",
"Trellis2OvoxelExportToGLB": "Trellis2 - Ovoxel Export to GLB",
}
+9 -3
View File
@@ -1001,7 +1001,9 @@ class Trellis2ImageTo3DPipeline(Pipeline):
"""
Preprocess the input mesh.
"""
vertices = mesh.vertices
mesh = mesh.copy()
vertices = mesh.vertices.copy()
vertices_min = vertices.min(axis=0)
vertices_max = vertices.max(axis=0)
center = (vertices_min + vertices_max) / 2
@@ -1011,7 +1013,9 @@ class Trellis2ImageTo3DPipeline(Pipeline):
vertices[:, 1] = -vertices[:, 2]
vertices[:, 2] = tmp
assert np.all(vertices >= -0.5) and np.all(vertices <= 0.5), 'vertices out of range'
return trimesh.Trimesh(vertices=vertices, faces=mesh.faces, process=False)
mesh.vertices = vertices
return mesh
def encode_shape_slat(
self,
@@ -1069,9 +1073,11 @@ class Trellis2ImageTo3DPipeline(Pipeline):
texture_alpha_mode = 'OPAQUE',
double_side_material = True
):
vertices = mesh.vertices
faces = mesh.faces
normals = mesh.vertex_normals
normals = np.asarray(mesh.vertex_normals).copy()
vertices_torch = torch.from_numpy(vertices).float().cuda()
faces_torch = torch.from_numpy(faces).int().cuda()
if hasattr(mesh, 'visual') and hasattr(mesh.visual, 'uv') and mesh.visual.uv is not None: