Rename rename mesh slots to trimesh for clarity

This commit is contained in:
kijai 2025-02-07 20:38:33 +02:00
parent 4b3b5f69f3
commit 0c100bb092
2 changed files with 108 additions and 78 deletions

153
nodes.py
View File

@ -380,27 +380,27 @@ class Hy3DMeshUVWrap:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
}, },
} }
RETURN_TYPES = ("HY3DMESH", ) RETURN_TYPES = ("TRIMESH", )
RETURN_NAMES = ("mesh", ) RETURN_NAMES = ("trimesh", )
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def process(self, mesh): def process(self, trimesh):
from .hy3dgen.texgen.utils.uv_warp_utils import mesh_uv_wrap from .hy3dgen.texgen.utils.uv_warp_utils import mesh_uv_wrap
mesh = mesh_uv_wrap(mesh) trimesh = mesh_uv_wrap(trimesh)
return (mesh,) return (trimesh,)
class Hy3DRenderMultiView: class Hy3DRenderMultiView:
@classmethod @classmethod
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"render_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}), "render_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}),
"texture_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}), "texture_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}),
}, },
@ -415,7 +415,7 @@ class Hy3DRenderMultiView:
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def process(self, mesh, render_size, texture_size, camera_config=None, normal_space="world"): def process(self, trimesh, render_size, texture_size, camera_config=None, normal_space="world"):
from .hy3dgen.texgen.differentiable_renderer.mesh_render import MeshRender from .hy3dgen.texgen.differentiable_renderer.mesh_render import MeshRender
@ -436,7 +436,7 @@ class Hy3DRenderMultiView:
camera_distance=camera_distance, camera_distance=camera_distance,
ortho_scale=ortho_scale) ortho_scale=ortho_scale)
self.render.load_mesh(mesh) self.render.load_mesh(trimesh)
if normal_space == "world": if normal_space == "world":
normal_maps, masks = self.render_normal_multiview( normal_maps, masks = self.render_normal_multiview(
@ -497,7 +497,7 @@ class Hy3DRenderSingleView:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"render_type": (["normal", "depth"], {"default": "normal"}), "render_type": (["normal", "depth"], {"default": "normal"}),
"render_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}), "render_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}),
"camera_type": (["orth", "perspective"], {"default": "orth"}), "camera_type": (["orth", "perspective"], {"default": "orth"}),
@ -516,7 +516,7 @@ class Hy3DRenderSingleView:
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def process(self, mesh, render_type, camera_type, ortho_scale, camera_distance, pan_x, pan_y, render_size, azimuth, elevation, bg_color): def process(self, trimesh, render_type, camera_type, ortho_scale, camera_distance, pan_x, pan_y, render_size, azimuth, elevation, bg_color):
from .hy3dgen.texgen.differentiable_renderer.mesh_render import MeshRender from .hy3dgen.texgen.differentiable_renderer.mesh_render import MeshRender
@ -531,7 +531,7 @@ class Hy3DRenderSingleView:
filter_mode='linear' filter_mode='linear'
) )
self.render.load_mesh(mesh) self.render.load_mesh(trimesh)
if render_type == "normal": if render_type == "normal":
normals, mask = self.render.render_normal( normals, mask = self.render.render_normal(
@ -581,7 +581,7 @@ class Hy3DRenderMultiViewDepth:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"render_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}), "render_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}),
"texture_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}), "texture_size": ("INT", {"default": 1024, "min": 64, "max": 4096, "step": 16}),
}, },
@ -595,7 +595,7 @@ class Hy3DRenderMultiViewDepth:
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def process(self, mesh, render_size, texture_size, camera_config=None): def process(self, trimesh, render_size, texture_size, camera_config=None):
mm.unload_all_models() mm.unload_all_models()
mm.soft_empty_cache() mm.soft_empty_cache()
@ -619,7 +619,7 @@ class Hy3DRenderMultiViewDepth:
camera_distance=camera_distance, camera_distance=camera_distance,
ortho_scale=ortho_scale) ortho_scale=ortho_scale)
self.render.load_mesh(mesh) self.render.load_mesh(trimesh)
depth_maps, masks = self.render_depth_multiview( depth_maps, masks = self.render_depth_multiview(
selected_camera_elevs, selected_camera_azims) selected_camera_elevs, selected_camera_azims)
@ -941,8 +941,8 @@ class Hy3DApplyTexture:
}, },
} }
RETURN_TYPES = ("HY3DMESH", ) RETURN_TYPES = ("TRIMESH", )
RETURN_NAMES = ("mesh", ) RETURN_NAMES = ("trimesh", )
FUNCTION = "apply" FUNCTION = "apply"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
@ -963,8 +963,8 @@ class Hy3DLoadMesh:
"glb_path": ("STRING", {"default": "", "tooltip": "The glb path with mesh to load."}), "glb_path": ("STRING", {"default": "", "tooltip": "The glb path with mesh to load."}),
} }
} }
RETURN_TYPES = ("HY3DMESH",) RETURN_TYPES = ("TRIMESH",)
RETURN_NAMES = ("mesh",) RETURN_NAMES = ("trimesh",)
OUTPUT_TOOLTIPS = ("The glb model with mesh to texturize.",) OUTPUT_TOOLTIPS = ("The glb model with mesh to texturize.",)
FUNCTION = "load" FUNCTION = "load"
@ -973,9 +973,9 @@ class Hy3DLoadMesh:
def load(self, glb_path): def load(self, glb_path):
mesh = trimesh.load(glb_path, force="mesh") trimesh = trimesh.load(glb_path, force="mesh")
return (mesh,) return (trimesh,)
class Hy3DUploadMesh: class Hy3DUploadMesh:
@classmethod @classmethod
@ -993,8 +993,8 @@ class Hy3DUploadMesh:
"mesh": (sorted(files),), "mesh": (sorted(files),),
} }
} }
RETURN_TYPES = ("HY3DMESH",) RETURN_TYPES = ("TRIMESH",)
RETURN_NAMES = ("mesh",) RETURN_NAMES = ("trimesh",)
OUTPUT_TOOLTIPS = ("The glb model with mesh to texturize.",) OUTPUT_TOOLTIPS = ("The glb model with mesh to texturize.",)
FUNCTION = "load" FUNCTION = "load"
@ -1089,8 +1089,8 @@ class Hy3DVAEDecode:
}, },
} }
RETURN_TYPES = ("HY3DMESH",) RETURN_TYPES = ("TRIMESH",)
RETURN_NAMES = ("mesh",) RETURN_NAMES = ("trimesh",)
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
@ -1123,7 +1123,7 @@ class Hy3DPostprocessMesh:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"remove_floaters": ("BOOLEAN", {"default": True}), "remove_floaters": ("BOOLEAN", {"default": True}),
"remove_degenerate_faces": ("BOOLEAN", {"default": True}), "remove_degenerate_faces": ("BOOLEAN", {"default": True}),
"reduce_faces": ("BOOLEAN", {"default": True}), "reduce_faces": ("BOOLEAN", {"default": True}),
@ -1132,13 +1132,13 @@ class Hy3DPostprocessMesh:
}, },
} }
RETURN_TYPES = ("HY3DMESH",) RETURN_TYPES = ("TRIMESH",)
RETURN_NAMES = ("mesh",) RETURN_NAMES = ("trimesh",)
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def process(self, mesh, remove_floaters, remove_degenerate_faces, reduce_faces, max_facenum, smooth_normals): def process(self, trimesh, remove_floaters, remove_degenerate_faces, reduce_faces, max_facenum, smooth_normals):
new_mesh = mesh.copy() new_mesh = trimesh.copy()
if remove_floaters: if remove_floaters:
new_mesh = FloaterRemover()(new_mesh) new_mesh = FloaterRemover()(new_mesh)
log.info(f"Removed floaters, resulting in {new_mesh.vertices.shape[0]} vertices and {new_mesh.faces.shape[0]} faces") log.info(f"Removed floaters, resulting in {new_mesh.vertices.shape[0]} vertices and {new_mesh.faces.shape[0]} faces")
@ -1159,7 +1159,7 @@ class Hy3DFastSimplifyMesh:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"target_count": ("INT", {"default": 40000, "min": 1, "max": 100000000, "step": 1, "tooltip": "Target number of triangles"}), "target_count": ("INT", {"default": 40000, "min": 1, "max": 100000000, "step": 1, "tooltip": "Target number of triangles"}),
"aggressiveness": ("INT", {"default": 7, "min": 0, "max": 100, "step": 1, "tooltip": "Parameter controlling the growth rate of the threshold at each iteration when lossless is False."}), "aggressiveness": ("INT", {"default": 7, "min": 0, "max": 100, "step": 1, "tooltip": "Parameter controlling the growth rate of the threshold at each iteration when lossless is False."}),
"max_iterations": ("INT", {"default": 100, "min": 1, "max": 1000, "step": 1, "tooltip": "Maximal number of iterations"}), "max_iterations": ("INT", {"default": 100, "min": 1, "max": 1000, "step": 1, "tooltip": "Maximal number of iterations"}),
@ -1170,21 +1170,21 @@ class Hy3DFastSimplifyMesh:
}, },
} }
RETURN_TYPES = ("HY3DMESH",) RETURN_TYPES = ("TRIMESH",)
RETURN_NAMES = ("mesh",) RETURN_NAMES = ("trimesh",)
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
DESCRIPTION = "Simplifies the mesh using Fast Quadric Mesh Reduction: https://github.com/Kramer84/pyfqmr-Fast-Quadric-Mesh-Reduction" DESCRIPTION = "Simplifies the mesh using Fast Quadric Mesh Reduction: https://github.com/Kramer84/pyfqmr-Fast-Quadric-Mesh-Reduction"
def process(self, mesh, target_count, aggressiveness, preserve_border, max_iterations,lossless, threshold_lossless, update_rate): def process(self, trimesh, target_count, aggressiveness, preserve_border, max_iterations,lossless, threshold_lossless, update_rate):
new_mesh = mesh.copy() new_mesh = trimesh.copy()
try: try:
import pyfqmr import pyfqmr
except ImportError: except ImportError:
raise ImportError("pyfqmr not found. Please install it using 'pip install pyfqmr' https://github.com/Kramer84/pyfqmr-Fast-Quadric-Mesh-Reduction") raise ImportError("pyfqmr not found. Please install it using 'pip install pyfqmr' https://github.com/Kramer84/pyfqmr-Fast-Quadric-Mesh-Reduction")
mesh_simplifier = pyfqmr.Simplify() mesh_simplifier = pyfqmr.Simplify()
mesh_simplifier.setMesh(mesh.vertices, mesh.faces) mesh_simplifier.setMesh(trimesh.vertices, trimesh.faces)
mesh_simplifier.simplify_mesh( mesh_simplifier.simplify_mesh(
target_count=target_count, target_count=target_count,
aggressiveness=aggressiveness, aggressiveness=aggressiveness,
@ -1205,22 +1205,22 @@ class Hy3DMeshInfo:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
}, },
} }
RETURN_TYPES = ("HY3DMESH", "INT", "INT", ) RETURN_TYPES = ("TRIMESH", "INT", "INT", )
RETURN_NAMES = ("mesh", "vertices", "faces",) RETURN_NAMES = ("trimesh", "vertices", "faces",)
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def process(self, mesh): def process(self, trimesh):
vertices_count = mesh.vertices.shape[0] vertices_count = trimesh.vertices.shape[0]
faces_count = mesh.faces.shape[0] faces_count = trimesh.faces.shape[0]
log.info(f"Hy3DMeshInfo: Mesh has {vertices_count} vertices and {mesh.faces.shape[0]} faces") log.info(f"Hy3DMeshInfo: Mesh has {vertices_count} vertices and {trimesh.faces.shape[0]} faces")
return {"ui": { return {"ui": {
"text": [f"{vertices_count:,.0f}x{faces_count:,.0f}"]}, "text": [f"{vertices_count:,.0f}x{faces_count:,.0f}"]},
"result": (mesh, vertices_count, faces_count) "result": (trimesh, vertices_count, faces_count)
} }
class Hy3DIMRemesh: class Hy3DIMRemesh:
@ -1228,7 +1228,7 @@ class Hy3DIMRemesh:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"merge_vertices": ("BOOLEAN", {"default": True}), "merge_vertices": ("BOOLEAN", {"default": True}),
"vertex_count": ("INT", {"default": 10000, "min": 100, "max": 10000000, "step": 1}), "vertex_count": ("INT", {"default": 10000, "min": 100, "max": 10000000, "step": 1}),
"smooth_iter": ("INT", {"default": 8, "min": 0, "max": 100, "step": 1}), "smooth_iter": ("INT", {"default": 8, "min": 0, "max": 100, "step": 1}),
@ -1237,24 +1237,24 @@ class Hy3DIMRemesh:
}, },
} }
RETURN_TYPES = ("HY3DMESH",) RETURN_TYPES = ("TRIMESH",)
RETURN_NAMES = ("mesh",) RETURN_NAMES = ("trimesh",)
FUNCTION = "remesh" FUNCTION = "remesh"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
DESCRIPTION = "Remeshes the mesh using instant-meshes: https://github.com/wjakob/instant-meshes, Note: this will remove all vertex colors and textures." DESCRIPTION = "Remeshes the mesh using instant-meshes: https://github.com/wjakob/instant-meshes, Note: this will remove all vertex colors and textures."
def remesh(self, mesh, merge_vertices, vertex_count, smooth_iter, align_to_boundaries, triangulate_result): def remesh(self, trimesh, merge_vertices, vertex_count, smooth_iter, align_to_boundaries, triangulate_result):
try: try:
import pynanoinstantmeshes as PyNIM import pynanoinstantmeshes as PyNIM
except ImportError: except ImportError:
raise ImportError("pynanoinstantmeshes not found. Please install it using 'pip install pynanoinstantmeshes'") raise ImportError("pynanoinstantmeshes not found. Please install it using 'pip install pynanoinstantmeshes'")
new_mesh = mesh.copy() new_mesh = trimesh.copy()
if merge_vertices: if merge_vertices:
mesh.merge_vertices(new_mesh) trimesh.merge_vertices(new_mesh)
new_verts, new_faces = PyNIM.remesh( new_verts, new_faces = PyNIM.remesh(
np.array(mesh.vertices, dtype=np.float32), np.array(trimesh.vertices, dtype=np.float32),
np.array(mesh.faces, dtype=np.uint32), np.array(trimesh.faces, dtype=np.uint32),
vertex_count, vertex_count,
align_to_boundaries=align_to_boundaries, align_to_boundaries=align_to_boundaries,
smooth_iter=smooth_iter smooth_iter=smooth_iter
@ -1275,7 +1275,7 @@ class Hy3DGetMeshPBRTextures:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"texture" : (["base_color", "emissive", "metallic_roughness", "normal", "occlusion"], ), "texture" : (["base_color", "emissive", "metallic_roughness", "normal", "occlusion"], ),
}, },
} }
@ -1285,7 +1285,7 @@ class Hy3DGetMeshPBRTextures:
FUNCTION = "get_textures" FUNCTION = "get_textures"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def get_textures(self, mesh, texture): def get_textures(self, trimesh, texture):
TEXTURE_MAPPING = { TEXTURE_MAPPING = {
'base_color': ('baseColorTexture', "Base color"), 'base_color': ('baseColorTexture', "Base color"),
@ -1296,7 +1296,7 @@ class Hy3DGetMeshPBRTextures:
} }
texture_attr, texture_name = TEXTURE_MAPPING[texture] texture_attr, texture_name = TEXTURE_MAPPING[texture]
texture_data = getattr(mesh.visual.material, texture_attr) texture_data = getattr(trimesh.visual.material, texture_attr)
if texture_data is None: if texture_data is None:
raise ValueError(f"{texture_name} texture not found") raise ValueError(f"{texture_name} texture not found")
@ -1309,22 +1309,22 @@ class Hy3DSetMeshPBRTextures:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"image": ("IMAGE", ), "image": ("IMAGE", ),
"texture" : (["base_color", "emissive", "metallic_roughness", "normal", "occlusion"], ), "texture" : (["base_color", "emissive", "metallic_roughness", "normal", "occlusion"], ),
}, },
} }
RETURN_TYPES = ("HY3DMESH", ) RETURN_TYPES = ("TRIMESH", )
RETURN_NAMES = ("mesh",) RETURN_NAMES = ("trimesh",)
FUNCTION = "set_textures" FUNCTION = "set_textures"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def set_textures(self, mesh, image, texture): def set_textures(self, trimesh, image, texture):
from trimesh.visual.material import SimpleMaterial from trimesh.visual.material import SimpleMaterial
if isinstance(mesh.visual.material, SimpleMaterial): if isinstance(trimesh.visual.material, SimpleMaterial):
log.info("Found SimpleMaterial, Converting to PBRMaterial") log.info("Found SimpleMaterial, Converting to PBRMaterial")
mesh.visual.material = mesh.visual.material.to_pbr() trimesh.visual.material = trimesh.visual.material.to_pbr()
TEXTURE_MAPPING = { TEXTURE_MAPPING = {
@ -1334,7 +1334,7 @@ class Hy3DSetMeshPBRTextures:
'normal': ('normalTexture', "Normal"), 'normal': ('normalTexture', "Normal"),
'occlusion': ('occlusionTexture', "Occlusion"), 'occlusion': ('occlusionTexture', "Occlusion"),
} }
new_mesh = mesh.copy() new_mesh = trimesh.copy()
texture_attr, texture_name = TEXTURE_MAPPING[texture] texture_attr, texture_name = TEXTURE_MAPPING[texture]
image_np = (image[0].cpu().numpy() * 255).astype(np.uint8) image_np = (image[0].cpu().numpy() * 255).astype(np.uint8)
if image_np.shape[2] == 4: # RGBA if image_np.shape[2] == 4: # RGBA
@ -1351,7 +1351,7 @@ class Hy3DSetMeshPBRAttributes:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"baseColorFactor": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), "baseColorFactor": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"emissiveFactor": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}), "emissiveFactor": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"metallicFactor": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), "metallicFactor": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
@ -1360,14 +1360,14 @@ class Hy3DSetMeshPBRAttributes:
}, },
} }
RETURN_TYPES = ("HY3DMESH", ) RETURN_TYPES = ("TRIMESH", )
RETURN_NAMES = ("mesh",) RETURN_NAMES = ("trimesh",)
FUNCTION = "set_textures" FUNCTION = "set_textures"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def set_textures(self, mesh, baseColorFactor, emissiveFactor, metallicFactor, roughnessFactor, doubleSided): def set_textures(self, trimesh, baseColorFactor, emissiveFactor, metallicFactor, roughnessFactor, doubleSided):
new_mesh = mesh.copy() new_mesh = trimesh.copy()
new_mesh.visual.material.baseColorFactor = [baseColorFactor, baseColorFactor, baseColorFactor, 1.0] new_mesh.visual.material.baseColorFactor = [baseColorFactor, baseColorFactor, baseColorFactor, 1.0]
new_mesh.visual.material.emissiveFactor = [emissiveFactor, emissiveFactor, emissiveFactor] new_mesh.visual.material.emissiveFactor = [emissiveFactor, emissiveFactor, emissiveFactor]
new_mesh.visual.material.metallicFactor = metallicFactor new_mesh.visual.material.metallicFactor = metallicFactor
@ -1381,7 +1381,7 @@ class Hy3DExportMesh:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"filename_prefix": ("STRING", {"default": "3D/Hy3D"}), "filename_prefix": ("STRING", {"default": "3D/Hy3D"}),
"file_format": (["glb", "obj", "ply", "stl", "3mf", "dae"],), "file_format": (["glb", "obj", "ply", "stl", "3mf", "dae"],),
}, },
@ -1395,16 +1395,16 @@ class Hy3DExportMesh:
FUNCTION = "process" FUNCTION = "process"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def process(self, mesh, filename_prefix, file_format, save_file=True): def process(self, trimesh, filename_prefix, file_format, save_file=True):
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, folder_paths.get_output_directory()) full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, folder_paths.get_output_directory())
output_glb_path = Path(full_output_folder, f'{filename}_{counter:05}_.{file_format}') output_glb_path = Path(full_output_folder, f'{filename}_{counter:05}_.{file_format}')
output_glb_path.parent.mkdir(exist_ok=True) output_glb_path.parent.mkdir(exist_ok=True)
if save_file: if save_file:
mesh.export(output_glb_path, file_type=file_format) trimesh.export(output_glb_path, file_type=file_format)
relative_path = Path(subfolder) / f'{filename}_{counter:05}_.{file_format}' relative_path = Path(subfolder) / f'{filename}_{counter:05}_.{file_format}'
else: else:
temp_file = Path(full_output_folder, f'hy3dtemp_.{file_format}') temp_file = Path(full_output_folder, f'hy3dtemp_.{file_format}')
mesh.export(temp_file, file_type=file_format) trimesh.export(temp_file, file_type=file_format)
relative_path = Path(subfolder) / f'hy3dtemp_.{file_format}' relative_path = Path(subfolder) / f'hy3dtemp_.{file_format}'
return (str(relative_path), ) return (str(relative_path), )
@ -1414,7 +1414,7 @@ class Hy3DNvdiffrastRenderer:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return { return {
"required": { "required": {
"mesh": ("HY3DMESH",), "trimesh": ("TRIMESH",),
"render_type": (["textured", "vertex_colors", "normals","depth",],), "render_type": (["textured", "vertex_colors", "normals","depth",],),
"width": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 16, "tooltip": "Width of the rendered image"}), "width": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 16, "tooltip": "Width of the rendered image"}),
"height": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 16, "tooltip": "Height of the rendered image"}), "height": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 16, "tooltip": "Height of the rendered image"}),
@ -1426,6 +1426,8 @@ class Hy3DNvdiffrastRenderer:
"fov": ("FLOAT", {"default": 60.0, "min": 1.0, "max": 179.0, "step": 0.01, "tooltip": "Camera field of view in degrees"}), "fov": ("FLOAT", {"default": 60.0, "min": 1.0, "max": 179.0, "step": 0.01, "tooltip": "Camera field of view in degrees"}),
"near": ("FLOAT", {"default": 0.1, "min": 0.001, "max": 1000.0, "step": 0.01, "tooltip": "Camera near clipping plane"}), "near": ("FLOAT", {"default": 0.1, "min": 0.001, "max": 1000.0, "step": 0.01, "tooltip": "Camera near clipping plane"}),
"far": ("FLOAT", {"default": 1000.0, "min": 1.0, "max": 10000.0, "step": 0.01, "tooltip": "Camera far clipping plane"}), "far": ("FLOAT", {"default": 1000.0, "min": 1.0, "max": 10000.0, "step": 0.01, "tooltip": "Camera far clipping plane"}),
"pan_x": ("FLOAT", {"default": 0.0, "min": -1.0, "max": 1.0, "step": 0.001, "tooltip": "Pan in x direction"}),
"pan_y": ("FLOAT", {"default": 0.0, "min": -1.0, "max": 1.0, "step": 0.001, "tooltip": "Pan in y direction"}),
}, },
} }
@ -1434,7 +1436,7 @@ class Hy3DNvdiffrastRenderer:
FUNCTION = "render" FUNCTION = "render"
CATEGORY = "Hunyuan3DWrapper" CATEGORY = "Hunyuan3DWrapper"
def render(self, mesh, width, height, camera_distance, yaw, pitch, fov, near, far, num_frames, ssaa, render_type): def render(self, trimesh, width, height, camera_distance, yaw, pitch, fov, near, far, num_frames, ssaa, render_type, pan_x, pan_y):
try: try:
import nvdiffrast.torch as dr import nvdiffrast.torch as dr
except ImportError: except ImportError:
@ -1446,11 +1448,12 @@ class Hy3DNvdiffrastRenderer:
# Create GL context # Create GL context
device = mm.get_torch_device() device = mm.get_torch_device()
glctx = dr.RasterizeCudaContext() glctx = dr.RasterizeCudaContext()
mesh_copy = mesh.copy() mesh_copy = trimesh.copy()
mesh_copy = rotate_mesh_matrix(mesh_copy, 90, 'x') mesh_copy = rotate_mesh_matrix(mesh_copy, 90, 'x')
mesh_copy = rotate_mesh_matrix(mesh_copy, 180, 'z') mesh_copy = rotate_mesh_matrix(mesh_copy, 180, 'z')
width, height = width * ssaa, height * ssaa width, height = width * ssaa, height * ssaa
aspect_ratio = width / height
# Get UV coordinates and texture if available # Get UV coordinates and texture if available
if hasattr(mesh_copy.visual, 'uv') and hasattr(mesh_copy.visual, 'material'): if hasattr(mesh_copy.visual, 'uv') and hasattr(mesh_copy.visual, 'material'):
@ -1484,7 +1487,7 @@ class Hy3DNvdiffrastRenderer:
yaws = yaws.tolist() yaws = yaws.tolist()
r = camera_distance r = camera_distance
extrinsics, intrinsics = yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitches, r, fov) extrinsics, intrinsics = yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitches, r, fov, aspect_ratio, pan_x, pan_y)
image_list = [] image_list = []
mask_list = [] mask_list = []

View File

@ -64,7 +64,7 @@ def intrinsics_to_projection(
ret[3, 2] = 1. ret[3, 2] = 1.
return ret return ret
def yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitchs, rs, fovs): def yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitchs, rs, fovs, aspect_ratio=1.0, pan_x=0.0, pan_y=0.0):
import utils3d import utils3d
is_list = isinstance(yaws, list) is_list = isinstance(yaws, list)
if not is_list: if not is_list:
@ -74,10 +74,17 @@ def yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitchs, rs, fovs):
rs = [rs] * len(yaws) rs = [rs] * len(yaws)
if not isinstance(fovs, list): if not isinstance(fovs, list):
fovs = [fovs] * len(yaws) fovs = [fovs] * len(yaws)
MIN_DISTANCE = 1e-6
rs = [max(r, MIN_DISTANCE) for r in rs]
extrinsics = [] extrinsics = []
intrinsics = [] intrinsics = []
for yaw, pitch, r, fov in zip(yaws, pitchs, rs, fovs): for yaw, pitch, r, fov in zip(yaws, pitchs, rs, fovs):
fov = torch.deg2rad(torch.tensor(float(fov))).cuda() fov = torch.deg2rad(torch.tensor(float(fov))).cuda()
fov_y = fov
fov_x = 2.0 * torch.atan(torch.tan(fov_y * 0.5) * aspect_ratio)
yaw = torch.tensor(float(yaw)).cuda() yaw = torch.tensor(float(yaw)).cuda()
pitch = torch.tensor(float(pitch)).cuda() pitch = torch.tensor(float(pitch)).cuda()
orig = torch.tensor([ orig = torch.tensor([
@ -85,8 +92,28 @@ def yaw_pitch_r_fov_to_extrinsics_intrinsics(yaws, pitchs, rs, fovs):
torch.cos(yaw) * torch.cos(pitch), torch.cos(yaw) * torch.cos(pitch),
torch.sin(pitch), torch.sin(pitch),
]).cuda() * r ]).cuda() * r
extr = utils3d.torch.extrinsics_look_at(orig, torch.tensor([0, 0, 0]).float().cuda(), torch.tensor([0, 0, 1]).float().cuda())
intr = utils3d.torch.intrinsics_from_fov_xy(fov, fov) # Calculate camera right vector
right = torch.tensor([
torch.cos(yaw),
-torch.sin(yaw),
0.0
]).cuda()
# Calculate camera up vector after pitch
up = torch.tensor([
torch.sin(yaw) * torch.sin(pitch),
torch.cos(yaw) * torch.sin(pitch),
-torch.cos(pitch)
]).cuda()
# Apply panning in camera space
target = torch.tensor([0.0, 0.0, 0.0]).float().cuda()
target = target + right * pan_x + up * pan_y
up_vector = torch.tensor([0, 0, 1]).float().cuda()
extr = utils3d.torch.extrinsics_look_at(orig, target, up_vector)
intr = utils3d.torch.intrinsics_from_fov_xy(fov_x, fov_y)
extrinsics.append(extr) extrinsics.append(extr)
intrinsics.append(intr) intrinsics.append(intr)
if not is_list: if not is_list: