55 Hunyuan3DViewImage ,
66 InputGenerateType ,
77 ResultFile3D ,
8+ SmartTopologyRequest ,
9+ TaskFile3DInput ,
810 TextureEditTaskRequest ,
11+ To3DPartTaskRequest ,
912 To3DProTaskCreateResponse ,
1013 To3DProTaskQueryRequest ,
1114 To3DProTaskRequest ,
1215 To3DProTaskResultResponse ,
13- To3DUVFileInput ,
1416 To3DUVTaskRequest ,
1517)
1618from comfy_api_nodes .util import (
1719 ApiEndpoint ,
1820 download_url_to_file_3d ,
19- download_url_to_image_tensor ,
2021 downscale_image_tensor_by_max_side ,
2122 poll_op ,
2223 sync_op ,
@@ -344,7 +345,6 @@ def define_schema(cls):
344345 outputs = [
345346 IO .File3DOBJ .Output (display_name = "OBJ" ),
346347 IO .File3DFBX .Output (display_name = "FBX" ),
347- IO .Image .Output (),
348348 ],
349349 hidden = [
350350 IO .Hidden .auth_token_comfy_org ,
@@ -375,7 +375,7 @@ async def execute(
375375 ApiEndpoint (path = "/proxy/tencent/hunyuan/3d-uv" , method = "POST" ),
376376 response_model = To3DProTaskCreateResponse ,
377377 data = To3DUVTaskRequest (
378- File = To3DUVFileInput (
378+ File = TaskFile3DInput (
379379 Type = file_format .upper (),
380380 Url = await upload_3d_model_to_comfyapi (cls , model_3d , file_format ),
381381 )
@@ -394,7 +394,6 @@ async def execute(
394394 return IO .NodeOutput (
395395 await download_url_to_file_3d (get_file_from_response (result .ResultFile3Ds , "obj" ).Url , "obj" ),
396396 await download_url_to_file_3d (get_file_from_response (result .ResultFile3Ds , "fbx" ).Url , "fbx" ),
397- await download_url_to_image_tensor (get_file_from_response (result .ResultFile3Ds , "image" ).Url ),
398397 )
399398
400399
@@ -463,7 +462,7 @@ async def execute(
463462 ApiEndpoint (path = "/proxy/tencent/hunyuan/3d-texture-edit" , method = "POST" ),
464463 response_model = To3DProTaskCreateResponse ,
465464 data = TextureEditTaskRequest (
466- File3D = To3DUVFileInput (Type = file_format .upper (), Url = model_url ),
465+ File3D = TaskFile3DInput (Type = file_format .upper (), Url = model_url ),
467466 Prompt = prompt ,
468467 EnablePBR = True ,
469468 ),
@@ -538,8 +537,8 @@ async def execute(
538537 cls ,
539538 ApiEndpoint (path = "/proxy/tencent/hunyuan/3d-part" , method = "POST" ),
540539 response_model = To3DProTaskCreateResponse ,
541- data = To3DUVTaskRequest (
542- File = To3DUVFileInput (Type = file_format .upper (), Url = model_url ),
540+ data = To3DPartTaskRequest (
541+ File = TaskFile3DInput (Type = file_format .upper (), Url = model_url ),
543542 ),
544543 is_rate_limited = _is_tencent_rate_limited ,
545544 )
@@ -557,15 +556,107 @@ async def execute(
557556 )
558557
559558
559+ class TencentSmartTopologyNode (IO .ComfyNode ):
560+
561+ @classmethod
562+ def define_schema (cls ):
563+ return IO .Schema (
564+ node_id = "TencentSmartTopologyNode" ,
565+ display_name = "Hunyuan3D: Smart Topology" ,
566+ category = "api node/3d/Tencent" ,
567+ description = "Perform smart retopology on a 3D model. "
568+ "Supports GLB/OBJ formats; max 200MB; recommended for high-poly models." ,
569+ inputs = [
570+ IO .MultiType .Input (
571+ "model_3d" ,
572+ types = [IO .File3DGLB , IO .File3DOBJ , IO .File3DAny ],
573+ tooltip = "Input 3D model (GLB or OBJ)" ,
574+ ),
575+ IO .Combo .Input (
576+ "polygon_type" ,
577+ options = ["triangle" , "quadrilateral" ],
578+ tooltip = "Surface composition type." ,
579+ ),
580+ IO .Combo .Input (
581+ "face_level" ,
582+ options = ["medium" , "high" , "low" ],
583+ tooltip = "Polygon reduction level." ,
584+ ),
585+ IO .Int .Input (
586+ "seed" ,
587+ default = 0 ,
588+ min = 0 ,
589+ max = 2147483647 ,
590+ display_mode = IO .NumberDisplay .number ,
591+ control_after_generate = True ,
592+ tooltip = "Seed controls whether the node should re-run; "
593+ "results are non-deterministic regardless of seed." ,
594+ ),
595+ ],
596+ outputs = [
597+ IO .File3DOBJ .Output (display_name = "OBJ" ),
598+ ],
599+ hidden = [
600+ IO .Hidden .auth_token_comfy_org ,
601+ IO .Hidden .api_key_comfy_org ,
602+ IO .Hidden .unique_id ,
603+ ],
604+ is_api_node = True ,
605+ price_badge = IO .PriceBadge (expr = '{"type":"usd","usd":1.0}' ),
606+ )
607+
608+ SUPPORTED_FORMATS = {"glb" , "obj" }
609+
610+ @classmethod
611+ async def execute (
612+ cls ,
613+ model_3d : Types .File3D ,
614+ polygon_type : str ,
615+ face_level : str ,
616+ seed : int ,
617+ ) -> IO .NodeOutput :
618+ _ = seed
619+ file_format = model_3d .format .lower ()
620+ if file_format not in cls .SUPPORTED_FORMATS :
621+ raise ValueError (
622+ f"Unsupported file format: '{ file_format } '. " f"Supported: { ', ' .join (sorted (cls .SUPPORTED_FORMATS ))} ."
623+ )
624+ model_url = await upload_3d_model_to_comfyapi (cls , model_3d , file_format )
625+ response = await sync_op (
626+ cls ,
627+ ApiEndpoint (path = "/proxy/tencent/hunyuan/3d-smart-topology" , method = "POST" ),
628+ response_model = To3DProTaskCreateResponse ,
629+ data = SmartTopologyRequest (
630+ File3D = TaskFile3DInput (Type = file_format .upper (), Url = model_url ),
631+ PolygonType = polygon_type ,
632+ FaceLevel = face_level ,
633+ ),
634+ is_rate_limited = _is_tencent_rate_limited ,
635+ )
636+ if response .Error :
637+ raise ValueError (f"Task creation failed: [{ response .Error .Code } ] { response .Error .Message } " )
638+ result = await poll_op (
639+ cls ,
640+ ApiEndpoint (path = "/proxy/tencent/hunyuan/3d-smart-topology/query" , method = "POST" ),
641+ data = To3DProTaskQueryRequest (JobId = response .JobId ),
642+ response_model = To3DProTaskResultResponse ,
643+ status_extractor = lambda r : r .Status ,
644+ )
645+ return IO .NodeOutput (
646+ await download_url_to_file_3d (get_file_from_response (result .ResultFile3Ds , "obj" ).Url , "obj" ),
647+ )
648+
649+
560650class TencentHunyuan3DExtension (ComfyExtension ):
561651 @override
562652 async def get_node_list (self ) -> list [type [IO .ComfyNode ]]:
563653 return [
564654 TencentTextToModelNode ,
565655 TencentImageToModelNode ,
566- # TencentModelTo3DUVNode,
656+ TencentModelTo3DUVNode ,
567657 # Tencent3DTextureEditNode,
568658 Tencent3DPartNode ,
659+ TencentSmartTopologyNode ,
569660 ]
570661
571662
0 commit comments