@@ -317,11 +317,74 @@ def execute(cls, model_3d: Types.File3D, viewport_state, width: int, height: int
317317 )
318318
319319
320+ MESH_EXTENSIONS = {'.gltf' , '.glb' , '.obj' , '.fbx' , '.stl' }
321+
322+
323+ class Load3DAdvanced (IO .ComfyNode ):
324+ @classmethod
325+ def define_schema (cls ):
326+ input_dir = os .path .join (folder_paths .get_input_directory (), "3d" )
327+ os .makedirs (input_dir , exist_ok = True )
328+
329+ input_path = Path (input_dir )
330+ base_path = Path (folder_paths .get_input_directory ())
331+
332+ files = [
333+ normalize_path (str (file_path .relative_to (base_path )))
334+ for file_path in input_path .rglob ("*" )
335+ if file_path .suffix .lower () in MESH_EXTENSIONS
336+ ]
337+ return IO .Schema (
338+ node_id = "Load3DAdvanced" ,
339+ display_name = "Load 3D (Advanced)" ,
340+ category = "3d" ,
341+ search_aliases = [
342+ "load mesh" ,
343+ "load gltf" ,
344+ "load glb" ,
345+ "load obj" ,
346+ "load fbx" ,
347+ "load stl" ,
348+ ],
349+ is_experimental = True ,
350+ inputs = [
351+ IO .Combo .Input ("model_file" , options = ["none" ] + sorted (files ), upload = IO .UploadType .model ),
352+ IO .Load3D .Input ("viewport_state" ),
353+ IO .Int .Input ("width" , default = 1024 , min = 1 , max = 4096 , step = 1 ),
354+ IO .Int .Input ("height" , default = 1024 , min = 1 , max = 4096 , step = 1 ),
355+ ],
356+ outputs = [
357+ IO .File3DAny .Output (display_name = "model_3d" ),
358+ IO .Load3DModelInfo .Output (display_name = "model_3d_info" ),
359+ IO .Load3DCamera .Output (display_name = "camera_info" ),
360+ IO .Int .Output (display_name = "width" ),
361+ IO .Int .Output (display_name = "height" ),
362+ ],
363+ )
364+
365+ @classmethod
366+ def validate_inputs (cls , model_file , ** kwargs ) -> bool | str :
367+ if not model_file or model_file == "none" :
368+ return True
369+ if not folder_paths .exists_annotated_filepath (model_file ):
370+ return f"Invalid 3D model file: { model_file } "
371+ return True
372+
373+ @classmethod
374+ def execute (cls , model_file , viewport_state , width : int , height : int , ** kwargs ) -> IO .NodeOutput :
375+ file_3d = None
376+ if model_file and model_file != "none" :
377+ file_3d = Types .File3D (folder_paths .get_annotated_filepath (model_file ))
378+ model_3d_info = viewport_state .get ('model_3d_info' , [])
379+ return IO .NodeOutput (file_3d , model_3d_info , viewport_state ['camera_info' ], width , height )
380+
381+
320382class Load3DExtension (ComfyExtension ):
321383 @override
322384 async def get_node_list (self ) -> list [type [IO .ComfyNode ]]:
323385 return [
324386 Load3D ,
387+ Load3DAdvanced ,
325388 Preview3D ,
326389 Preview3DAdvanced ,
327390 PreviewGaussianSplat ,
0 commit comments