269 lines
11 KiB
Python
269 lines
11 KiB
Python
class Preview_3DMesh:
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"mesh_file_path": ("STRING", {"default": '', "multiline": False}),
|
|
},
|
|
}
|
|
|
|
OUTPUT_NODE = True
|
|
RETURN_TYPES = ()
|
|
FUNCTION = "preview_mesh"
|
|
CATEGORY = "Comfy3D/Visualize"
|
|
|
|
def preview_mesh(self, mesh_file_path):
|
|
|
|
mesh_folder_path, filename = os.path.split(mesh_file_path)
|
|
|
|
if not os.path.isabs(mesh_file_path):
|
|
mesh_file_path = os.path.join(comfy_paths.output_directory, mesh_folder_path, filename)
|
|
|
|
if not filename.lower().endswith(SUPPORTED_3D_EXTENSIONS):
|
|
cstr(f"[{self.__class__.__name__}] File name {filename} does not end with supported 3D file extensions: {SUPPORTED_3D_EXTENSIONS}").error.print()
|
|
mesh_file_path = ""
|
|
|
|
print(f"[Preview_3DMesh] Final mesh path: {mesh_file_path}")
|
|
print(f"[Preview_3DMesh] File exists: {os.path.exists(mesh_file_path) if mesh_file_path else False}")
|
|
|
|
previews = [
|
|
{
|
|
"filepath": mesh_file_path,
|
|
}
|
|
]
|
|
return {"ui": {"previews": previews}, "result": ()}
|
|
|
|
class Load_3D_Mesh:
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"mesh_file_path": ("STRING", {"default": '', "multiline": False}),
|
|
"use_fastmesh": ("BOOLEAN", {"default": True, "tooltip": "Использовать FastMesh для GLB/GLTF файлов"}),
|
|
"resize": ("BOOLEAN", {"default": False},),
|
|
"renormal": ("BOOLEAN", {"default": True},),
|
|
"retex": ("BOOLEAN", {"default": False},),
|
|
"optimizable": ("BOOLEAN", {"default": False},),
|
|
"clean": ("BOOLEAN", {"default": False},),
|
|
"resize_bound": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1000.0, "step": 0.001}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = (
|
|
"MESH",
|
|
)
|
|
RETURN_NAMES = (
|
|
"mesh",
|
|
)
|
|
FUNCTION = "load_mesh"
|
|
CATEGORY = "Comfy3D/Import|Export"
|
|
|
|
def load_mesh(self, mesh_file_path, use_fastmesh, resize, renormal, retex, optimizable, clean, resize_bound):
|
|
import time
|
|
mesh = None
|
|
|
|
if not os.path.isabs(mesh_file_path):
|
|
mesh_file_path = os.path.join(comfy_paths.input_directory, mesh_file_path)
|
|
|
|
if os.path.exists(mesh_file_path):
|
|
folder, filename = os.path.split(mesh_file_path)
|
|
if filename.lower().endswith(SUPPORTED_3D_EXTENSIONS):
|
|
with torch.inference_mode(not optimizable):
|
|
start_time = time.time()
|
|
if use_fastmesh and mesh_file_path.endswith((".glb", ".gltf")):
|
|
mesh = FastMesh.load(mesh_file_path, resize, renormal, retex, clean, resize_bound)
|
|
print(f"[FastMesh] Loading completed in {time.time() - start_time:.3f}s")
|
|
else:
|
|
mesh = Mesh.load(mesh_file_path, resize, renormal, retex, clean, resize_bound)
|
|
print(f"[Mesh] Loading completed in {time.time() - start_time:.3f}s")
|
|
else:
|
|
cstr(f"[{self.__class__.__name__}] File name {filename} does not end with supported 3D file extensions: {SUPPORTED_3D_EXTENSIONS}").error.print()
|
|
else:
|
|
cstr(f"[{self.__class__.__name__}] File {mesh_file_path} does not exist").error.print()
|
|
return (mesh, )
|
|
|
|
class Save_3D_Mesh:
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"mesh": ("MESH",),
|
|
"save_path": ("STRING", {"default": 'Mesh_%Y-%m-%d-%M-%S-%f.glb', "multiline": False}),
|
|
"use_fastmesh": ("BOOLEAN", {"default": True, "tooltip": "Использовать FastMesh для быстрого сохранения GLB/PLY/OBJ"}),
|
|
},
|
|
}
|
|
|
|
OUTPUT_NODE = True
|
|
RETURN_TYPES = (
|
|
"STRING",
|
|
)
|
|
RETURN_NAMES = (
|
|
"save_path",
|
|
)
|
|
FUNCTION = "save_mesh"
|
|
CATEGORY = "Comfy3D/Import|Export"
|
|
|
|
def save_mesh(self, mesh, save_path, use_fastmesh):
|
|
import time
|
|
save_path = parse_save_filename(save_path, comfy_paths.output_directory, SUPPORTED_3D_EXTENSIONS, self.__class__.__name__)
|
|
|
|
if save_path is not None:
|
|
start_time = time.time()
|
|
|
|
# Используем FastMesh методы для быстрого сохранения
|
|
if use_fastmesh and save_path.endswith((".glb", ".ply", ".obj")):
|
|
# Если это уже FastMesh - используем напрямую
|
|
print(f"[Save_3D_Mesh] FastMesh detected")
|
|
if hasattr(mesh, '_write_glb_fast'):
|
|
mesh.write(save_path)
|
|
save_time = time.time() - start_time
|
|
print(f"[FastMesh] Saving completed in {save_time:.3f}s")
|
|
else:
|
|
# Если обычный Mesh - создаем временный FastMesh для быстрых методов
|
|
print(f"[Save_3D_Mesh] REGULAR Mesh detected")
|
|
fast_mesh = FastMesh(
|
|
v=mesh.v, f=mesh.f, vn=mesh.vn, fn=mesh.fn,
|
|
vt=mesh.vt, ft=mesh.ft, vc=mesh.vc,
|
|
albedo=mesh.albedo, metallicRoughness=getattr(mesh, 'metallicRoughness', None),
|
|
device=mesh.device
|
|
)
|
|
fast_mesh.ori_center = getattr(mesh, 'ori_center', 0)
|
|
fast_mesh.ori_scale = getattr(mesh, 'ori_scale', 1)
|
|
fast_mesh.write(save_path)
|
|
save_time = time.time() - start_time
|
|
print(f"[FastMesh] Saving completed in {save_time:.3f}s")
|
|
else:
|
|
# Обычное сохранение через стандартный метод
|
|
mesh.write(save_path)
|
|
save_time = time.time() - start_time
|
|
print(f"[Mesh] Saving completed in {save_time:.3f}s")
|
|
|
|
return (save_path, )
|
|
|
|
|
|
class Preview_3D_FastMesh:
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"mesh_file_path": ("STRING", {"default": '', "multiline": False}),
|
|
},
|
|
}
|
|
|
|
OUTPUT_NODE = True
|
|
RETURN_TYPES = ()
|
|
FUNCTION = "preview_mesh"
|
|
CATEGORY = "Comfy3D/Visualize"
|
|
|
|
def preview_mesh(self, mesh_file_path):
|
|
|
|
mesh_folder_path, filename = os.path.split(mesh_file_path)
|
|
|
|
if not os.path.isabs(mesh_file_path):
|
|
mesh_file_path = os.path.join(comfy_paths.output_directory, mesh_folder_path, filename)
|
|
|
|
if not filename.lower().endswith(SUPPORTED_3D_EXTENSIONS):
|
|
cstr(f"[{self.__class__.__name__}] File name {filename} does not end with supported 3D file extensions: {SUPPORTED_3D_EXTENSIONS}").error.print()
|
|
mesh_file_path = ""
|
|
|
|
print(f"[Preview_3D_FastMesh] Final mesh path: {mesh_file_path}")
|
|
print(f"[Preview_3D_FastMesh] File exists: {os.path.exists(mesh_file_path) if mesh_file_path else False}")
|
|
|
|
previews = [
|
|
{
|
|
"filepath": mesh_file_path,
|
|
}
|
|
]
|
|
return {"ui": {"previews": previews}, "result": ()}
|
|
|
|
|
|
class Load_3D_FastMesh:
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"mesh_file_path": ("STRING", {"default": '', "multiline": False}),
|
|
"use_fastmesh": ("BOOLEAN", {"default": True, "tooltip": "Использовать FastMesh загрузчик"}),
|
|
"resize": ("BOOLEAN", {"default": False},),
|
|
"renormal": ("BOOLEAN", {"default": True},),
|
|
"retex": ("BOOLEAN", {"default": False},),
|
|
"optimizable": ("BOOLEAN", {"default": False},),
|
|
"clean": ("BOOLEAN", {"default": False},),
|
|
"resize_bound": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1000.0, "step": 0.001}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = (
|
|
"MESH",
|
|
)
|
|
RETURN_NAMES = (
|
|
"mesh",
|
|
)
|
|
FUNCTION = "load_mesh"
|
|
CATEGORY = "Comfy3D/Import|Export"
|
|
|
|
def load_mesh(self, mesh_file_path, use_fastmesh, resize, renormal, retex, optimizable, clean, resize_bound):
|
|
import time
|
|
mesh = None
|
|
|
|
if not os.path.isabs(mesh_file_path):
|
|
mesh_file_path = os.path.join(comfy_paths.input_directory, mesh_file_path)
|
|
|
|
if os.path.exists(mesh_file_path):
|
|
folder, filename = os.path.split(mesh_file_path)
|
|
if filename.lower().endswith(SUPPORTED_3D_EXTENSIONS):
|
|
with torch.inference_mode(not optimizable):
|
|
start_time = time.time()
|
|
if use_fastmesh and mesh_file_path.endswith((".glb", ".gltf")):
|
|
from mesh_processor.mesh import FastMesh
|
|
mesh = FastMesh.load(mesh_file_path, resize, renormal, retex, clean, resize_bound)
|
|
print(f"[FastMesh] Loading completed in {time.time() - start_time:.3f}s")
|
|
else:
|
|
mesh = Mesh.load(mesh_file_path, resize, renormal, retex, clean, resize_bound)
|
|
mesh_type = "FastMesh" if use_fastmesh else "Mesh"
|
|
print(f"[{mesh_type}] Loading completed in {time.time() - start_time:.3f}s")
|
|
else:
|
|
cstr(f"[{self.__class__.__name__}] File name {filename} does not end with supported 3D file extensions: {SUPPORTED_3D_EXTENSIONS}").error.print()
|
|
else:
|
|
cstr(f"[{self.__class__.__name__}] File {mesh_file_path} does not exist").error.print()
|
|
return (mesh, )
|
|
|
|
|
|
class Save_3D_FastMesh:
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"mesh": ("MESH",),
|
|
"save_path": ("STRING", {"default": 'FastMesh_%Y-%m-%d-%M-%S-%f.glb', "multiline": False}),
|
|
},
|
|
}
|
|
|
|
OUTPUT_NODE = True
|
|
RETURN_TYPES = (
|
|
"STRING",
|
|
)
|
|
RETURN_NAMES = (
|
|
"save_path",
|
|
)
|
|
FUNCTION = "save_mesh"
|
|
CATEGORY = "Comfy3D/Import|Export"
|
|
|
|
def save_mesh(self, mesh, save_path):
|
|
import time
|
|
save_path = parse_save_filename(save_path, comfy_paths.output_directory, SUPPORTED_3D_EXTENSIONS, self.__class__.__name__)
|
|
|
|
if save_path is not None:
|
|
start_time = time.time()
|
|
mesh.write(save_path)
|
|
save_time = time.time() - start_time
|
|
mesh_type = "FastMesh" if hasattr(mesh, '_create_empty_albedo') else "Mesh"
|
|
print(f"[{mesh_type}] Saving completed in {save_time:.3f}s")
|
|
|
|
return (save_path, ) |