diff --git a/__init__.py b/__init__.py index 5c93d6a..51b16ca 100644 --- a/__init__.py +++ b/__init__.py @@ -27,18 +27,23 @@ ag_path = os.path.join(os.path.dirname(__file__)) # ensure_pip_packages() + def get_python_files(path): return [f[:-3] for f in os.listdir(path) if f.endswith('.py')] + def append_to_sys_path(path): if path not in sys.path: sys.path.append(path) + def create_sam_model_dir(): model_dir = os.path.join(ag_path, "../../models/sam") if not os.path.isdir(model_dir): os.makedirs(model_dir) add_model_folder_path('sam', model_dir) + + create_sam_model_dir() paths = ['blender', 'sam', 'common'] @@ -66,10 +71,19 @@ for file in files: NODE_DISPLAY_NAME_MAPPINGS.update( obj.NODE_DISPLAY_NAME_MAPPINGS()) + if (hasattr(module, 'BLENDER_NODES')): + for node in module.BLENDER_NODES: + # print(node) + # NODE_CLASS_MAPPINGS.update({node: module.BLENDER_NODES[node]}) + # NODE_DISPLAY_NAME_MAPPINGS.update({node: node}) + NODE_CLASS_MAPPINGS.update(node.NODE_CLASS_MAPPINGS()) + NODE_DISPLAY_NAME_MAPPINGS.update( + node.NODE_DISPLAY_NAME_MAPPINGS()) + if hasattr(module, 'NODE_CLASS_MAPPINGS'): NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS) if hasattr(module, 'NODE_DISPLAY_NAME_MAPPINGS'): NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS) WEB_DIRECTORY = "js" -__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] \ No newline at end of file +__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] diff --git a/blender/blender_node.py b/blender/blender_node.py index 3c63118..ed02081 100644 --- a/blender/blender_node.py +++ b/blender/blender_node.py @@ -1,3 +1,5 @@ +import inspect +import re BPY_OBJS = "BPY_OBJS" BPY_OBJ = "BPY_OBJ" @@ -5,7 +7,16 @@ BPY_OBJS_TYPE = { BPY_OBJS: (BPY_OBJS,), } + class ObjectOps: + @classmethod + def get_extra_input_types(cls, bpy): + return cls.EXTRA_INPUT_TYPES + + @classmethod + def get_base_input_types(cls, bpy): + return cls.BASE_INPUT_TYPES + EXTRA_INPUT_TYPES = {} BASE_INPUT_TYPES = { "BPY_OBJ": ("BPY_OBJ",) @@ -13,14 +24,17 @@ class ObjectOps: @classmethod def INPUT_TYPES(cls): + import global_bpy + bpy = global_bpy.get_bpy() # print(cls.BASE_INPUT_TYPES) # print(cls.EXTRA_INPUT_TYPES) result = { "required": { - **cls.BASE_INPUT_TYPES, - **cls.EXTRA_INPUT_TYPES + **cls.get_base_input_types(bpy), + **cls.get_extra_input_types(bpy) } } + # print(result) return result @classmethod @@ -33,7 +47,7 @@ class ObjectOps: def NODE_DISPLAY_NAME_MAPPINGS(cls): import re return { - cls.__name__: re.sub("([a-z])([A-Z])","\g<1> \g<2>",cls.__name__) + cls.__name__: re.sub("([a-z])([A-Z])", "\g<1> \g<2>", cls.__name__) } RETURN_TYPES = ("BPY_OBJ",) @@ -74,3 +88,106 @@ class EditOps(ObjectOps): return (props["BPY_OBJ"], ) return results + + +def map_args(bpy, func): + print(func, type(func)) + + if str(type(func)) == "": + func = func.__init__ + print(func) + print(dir(func)) + + if callable(func): + sig = inspect.signature(func) + print(sig) + args_dict = { + name: param.default + for name, param in sig.parameters.items() + if param.default is not inspect.Parameter.empty + } + print(args_dict) + return args_dict + + + + # if isinstance(func, bpy.types.Function): + # print('is class') + # func = func.__init__ + + rna_type = func.get_rna_type() + args_dict = {} + for prop in rna_type.properties: + if prop.is_readonly: + continue + prop_type = str(prop.type) + prop_dict = {} + if prop_type in ["INT", "FLOAT"]: + prop_dict.update( + {"min": prop.hard_min, "max": prop.hard_max, "default": prop.default}) + elif prop_type == "BOOLEAN": + prop_dict.update({"default": prop.default}) + elif prop_type == "STRING": + prop_dict.update( + {"default": prop.default, "multiline": False}) + elif prop_type == "ENUM": + enum_items = [item.identifier for item in prop.enum_items] + args_dict[prop.identifier] = (enum_items,) + + # print(enum_items) + + if args_dict.get(prop.identifier) is None: + args_dict[prop.identifier] = (prop_type, prop_dict) + + # print(args_dict) + return args_dict + + +def camel_to_snake(name): + name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower() + + +def snake_to_camel(snake_str): + components = snake_str.split('_') + # capitalize the first letter of each component and join them together + camel_str = ''.join(x.title() for x in components) + return camel_str + + +def get_nested_attr(obj, attr_str): + attrs = attr_str.split('.') + for attr in attrs: + obj = getattr(obj, attr) + return obj + + +def print_blender_functions(path): + import sys + import os + + ag_path = os.path.join(os.path.dirname(__file__), '../') + if ag_path not in sys.path: + sys.path.append(ag_path) + + import global_bpy + bpy = global_bpy.get_bpy() + + print(dir(get_nested_attr(bpy, path)), type(get_nested_attr(bpy, path))) + + +def create_ops_class(cls, path, name=None): + node_name = snake_to_camel( + name if name is not None else path.split('.')[-1]) + print(node_name) + return type( + node_name, (cls, object), + { + 'get_extra_input_types': classmethod( + lambda cls, bpy: { + **map_args(bpy, get_nested_attr(bpy, path))} + ), + 'blender_process': lambda self, bpy, BPY_OBJ, **props: + (get_nested_attr(bpy, path)(**props), None)[1] + } + ) diff --git a/blender/ops_create_vertex_group.py b/blender/ops_create_vertex_group.py deleted file mode 100644 index 23518bc..0000000 --- a/blender/ops_create_vertex_group.py +++ /dev/null @@ -1,13 +0,0 @@ -import blender_node - - -class CreateVertexGroupOps(blender_node.EditOps): - EXTRA_INPUT_TYPES = { - "name": ("STRING", { - "multiline": False, - "default": "Group" - }), - } - - def blender_process(self, bpy, BPY_OBJ, name, **props): - bpy.context.object.vertex_groups.new(name=name) \ No newline at end of file diff --git a/blender/ops_edit_delete.py b/blender/ops_edit_delete.py deleted file mode 100644 index 8da6f54..0000000 --- a/blender/ops_edit_delete.py +++ /dev/null @@ -1,10 +0,0 @@ -import blender_node - - -class EditDeleteOps(blender_node.EditOps): - EXTRA_INPUT_TYPES = { - "type": (['VERT', 'EDGE', 'FACE', 'EDGE_FACE', 'ONLY_FACE'],), - } - - def blender_process(self, bpy, BPY_OBJ, **props): - bpy.ops.mesh.delete(**props) \ No newline at end of file diff --git a/blender/ops_mesh.py b/blender/ops_mesh.py new file mode 100644 index 0000000..2f1a8d9 --- /dev/null +++ b/blender/ops_mesh.py @@ -0,0 +1,24 @@ +import blender_node + +mesh_api = ['attribute_set', 'average_normals', 'beautify_fill', 'bevel', 'bisect', 'blend_from_shape', 'bridge_edge_loops', 'colors_reverse', 'colors_rotate', 'convex_hull', 'customdata_bevel_weight_edge_add', 'customdata_bevel_weight_edge_clear', 'customdata_bevel_weight_vertex_add', 'customdata_bevel_weight_vertex_clear', 'customdata_crease_edge_add', 'customdata_crease_edge_clear', 'customdata_crease_vertex_add', 'customdata_crease_vertex_clear', 'customdata_custom_splitnormals_add', 'customdata_custom_splitnormals_clear', 'customdata_mask_clear', 'customdata_skin_add', 'customdata_skin_clear', 'decimate', 'delete', 'delete_edgeloop', 'delete_loose', 'dissolve_degenerate', 'dissolve_edges', 'dissolve_faces', 'dissolve_limited', 'dissolve_mode', 'dissolve_verts', 'dupli_extrude_cursor', 'duplicate', 'duplicate_move', 'edge_collapse', 'edge_face_add', 'edge_rotate', 'edge_split', 'edgering_select', 'edges_select_sharp', 'extrude_context', 'extrude_context_move', 'extrude_edges_indiv', 'extrude_edges_move', 'extrude_faces_indiv', 'extrude_faces_move', 'extrude_manifold', 'extrude_region', 'extrude_region_move', 'extrude_region_shrink_fatten', 'extrude_repeat', 'extrude_vertices_move', 'extrude_verts_indiv', 'face_make_planar', 'face_set_extract', 'face_split_by_edges', 'faces_mirror_uv', 'faces_select_linked_flat', 'faces_shade_flat', 'faces_shade_smooth', 'fill', 'fill_grid', 'fill_holes', 'flip_normals', 'flip_quad_tessellation', 'hide', 'inset', 'intersect', 'intersect_boolean', 'knife_project', 'knife_tool', 'loop_multi_select', 'loop_select', 'loop_to_region', 'loopcut', 'loopcut_slide', 'mark_freestyle_edge', 'mark_freestyle_face', 'mark_seam', 'mark_sharp', 'merge', 'merge_normals', + 'mod_weighted_strength', 'normals_make_consistent', 'normals_tools', 'offset_edge_loops', 'offset_edge_loops_slide', 'paint_mask_extract', 'paint_mask_slice', 'point_normals', 'poke', 'polybuild_delete_at_cursor', 'polybuild_dissolve_at_cursor', 'polybuild_extrude_at_cursor_move', 'polybuild_face_at_cursor', 'polybuild_face_at_cursor_move', 'polybuild_split_at_cursor', 'polybuild_split_at_cursor_move', 'polybuild_transform_at_cursor', 'polybuild_transform_at_cursor_move', 'primitive_circle_add', 'primitive_cone_add', 'primitive_cube_add', 'primitive_cube_add_gizmo', 'primitive_cylinder_add', 'primitive_grid_add', 'primitive_ico_sphere_add', 'primitive_monkey_add', 'primitive_plane_add', 'primitive_torus_add', 'primitive_uv_sphere_add', 'quads_convert_to_tris', 'region_to_loop', 'remove_doubles', 'reveal', 'rip', 'rip_edge', 'rip_edge_move', 'rip_move', 'screw', 'select_all', 'select_axis', 'select_face_by_sides', 'select_interior_faces', 'select_less', 'select_linked', 'select_linked_pick', 'select_loose', 'select_mirror', 'select_mode', 'select_more', 'select_next_item', 'select_non_manifold', 'select_nth', 'select_prev_item', 'select_random', 'select_similar', 'select_similar_region', 'select_ungrouped', 'separate', 'set_normals_from_faces', 'shape_propagate_to_all', 'shortest_path_pick', 'shortest_path_select', 'smooth_normals', 'solidify', 'sort_elements', 'spin', 'split', 'split_normals', 'subdivide', 'subdivide_edgering', 'symmetrize', 'symmetry_snap', 'tris_convert_to_quads', 'unsubdivide', 'uv_texture_add', 'uv_texture_remove', 'uvs_reverse', 'uvs_rotate', 'vert_connect', 'vert_connect_concave', 'vert_connect_nonplanar', 'vert_connect_path', 'vertices_smooth', 'vertices_smooth_laplacian', 'wireframe'] + +object_api = ['add', 'add_named', 'align', 'anim_transforms_to_deltas', 'armature_add', 'assign_property_defaults', 'bake', 'bake_image', 'camera_add', 'clear_override_library', 'collection_add', 'collection_external_asset_drop', 'collection_instance_add', 'collection_link', 'collection_objects_select', 'collection_remove', 'collection_unlink', 'constraint_add', 'constraint_add_with_targets', 'constraints_clear', 'constraints_copy', 'convert', 'correctivesmooth_bind', 'curves_empty_hair_add', 'curves_random_add', 'data_instance_add', 'data_transfer', 'datalayout_transfer', 'delete', 'drop_geometry_nodes', 'drop_named_image', 'drop_named_material', 'duplicate', 'duplicate_move', 'duplicate_move_linked', 'duplicates_make_real', 'editmode_toggle', 'effector_add', 'empty_add', 'explode_refresh', 'face_map_add', 'face_map_assign', 'face_map_deselect', 'face_map_move', 'face_map_remove', 'face_map_remove_from', 'face_map_select', 'forcefield_toggle', 'geometry_node_tree_copy_assign', 'geometry_nodes_input_attribute_toggle', 'geometry_nodes_move_to_nodes', 'gpencil_add', 'gpencil_modifier_add', 'gpencil_modifier_apply', 'gpencil_modifier_copy', 'gpencil_modifier_copy_to_selected', 'gpencil_modifier_move_down', 'gpencil_modifier_move_to_index', 'gpencil_modifier_move_up', 'gpencil_modifier_remove', 'hide_collection', 'hide_render_clear_all', 'hide_view_clear', 'hide_view_set', 'hook_add_newob', 'hook_add_selob', 'hook_assign', 'hook_recenter', 'hook_remove', 'hook_reset', 'hook_select', 'instance_offset_from_cursor', 'instance_offset_from_object', 'instance_offset_to_cursor', 'isolate_type_render', 'join', 'join_shapes', 'join_uvs', 'laplaciandeform_bind', 'light_add', 'lightprobe_add', 'lightprobe_cache_bake', 'lightprobe_cache_free', 'lineart_bake_strokes', 'lineart_bake_strokes_all', 'lineart_clear', 'lineart_clear_all', 'link_to_collection', 'load_background_image', 'load_reference_image', 'location_clear', 'make_dupli_face', 'make_links_data', 'make_links_scene', 'make_local', 'make_override_library', 'make_single_user', 'material_slot_add', 'material_slot_assign', 'material_slot_copy', 'material_slot_deselect', 'material_slot_move', 'material_slot_remove', 'material_slot_remove_unused', 'material_slot_select', 'meshdeform_bind', 'metaball_add', 'mode_set', 'mode_set_with_submode', 'modifier_add', 'modifier_apply', 'modifier_apply_as_shapekey', 'modifier_convert', 'modifier_copy', 'modifier_copy_to_selected', 'modifier_move_down', + 'modifier_move_to_index', 'modifier_move_up', 'modifier_remove', 'modifier_set_active', 'move_to_collection', 'multires_base_apply', 'multires_external_pack', 'multires_external_save', 'multires_higher_levels_delete', 'multires_rebuild_subdiv', 'multires_reshape', 'multires_subdivide', 'multires_unsubdivide', 'ocean_bake', 'origin_clear', 'origin_set', 'parent_clear', 'parent_inverse_apply', 'parent_no_inverse_set', 'parent_set', 'particle_system_add', 'particle_system_remove', 'paths_calculate', 'paths_clear', 'paths_update', 'paths_update_visible', 'pointcloud_add', 'posemode_toggle', 'quadriflow_remesh', 'quick_explode', 'quick_fur', 'quick_liquid', 'quick_smoke', 'randomize_transform', 'reset_override_library', 'rotation_clear', 'scale_clear', 'select_all', 'select_by_type', 'select_camera', 'select_grouped', 'select_hierarchy', 'select_less', 'select_linked', 'select_mirror', 'select_more', 'select_pattern', 'select_random', 'select_same_collection', 'shade_flat', 'shade_smooth', 'shaderfx_add', 'shaderfx_copy', 'shaderfx_move_down', 'shaderfx_move_to_index', 'shaderfx_move_up', 'shaderfx_remove', 'shape_key_add', 'shape_key_clear', 'shape_key_mirror', 'shape_key_move', 'shape_key_remove', 'shape_key_retime', 'shape_key_transfer', 'simulation_nodes_cache_bake', 'simulation_nodes_cache_calculate_to_frame', 'simulation_nodes_cache_delete', 'skin_armature_create', 'skin_loose_mark_clear', 'skin_radii_equalize', 'skin_root_mark', 'speaker_add', 'subdivision_set', 'surfacedeform_bind', 'text_add', 'track_clear', 'track_set', 'transfer_mode', 'transform_apply', 'transform_axis_target', 'transform_to_mouse', 'transforms_to_deltas', 'unlink_data', 'vertex_group_add', 'vertex_group_assign', 'vertex_group_assign_new', 'vertex_group_clean', 'vertex_group_copy', 'vertex_group_copy_to_selected', 'vertex_group_deselect', 'vertex_group_invert', 'vertex_group_levels', 'vertex_group_limit_total', 'vertex_group_lock', 'vertex_group_mirror', 'vertex_group_move', 'vertex_group_normalize', 'vertex_group_normalize_all', 'vertex_group_quantize', 'vertex_group_remove', 'vertex_group_remove_from', 'vertex_group_select', 'vertex_group_set_active', 'vertex_group_smooth', 'vertex_group_sort', 'vertex_parent_set', 'vertex_weight_copy', 'vertex_weight_delete', 'vertex_weight_normalize_active_vertex', 'vertex_weight_paste', 'vertex_weight_set_active', 'visual_transform_apply', 'volume_add', 'volume_import', 'voxel_remesh', 'voxel_size_edit'] + +BLENDER_NODES = [ + blender_node.create_ops_class( + blender_node.EditOps, 'ops.mesh.' + op) for op in mesh_api +] + [ + blender_node.create_ops_class( + blender_node.ObjectOps, 'ops.object.' + op) for op in object_api +] + +# + [ +# blender_node.create_ops_class( +# blender_node.ObjectOps, 'context.object.vertex_groups.new', 'create_new_vertex_group'), +# ] + +# print(blender_node.print_blender_functions('ops','object')) +# print(blender_node.print_blender_functions('context.object')) +# print(blender_node.print_blender_functions('context.object.vertex_groups.new')) diff --git a/blender/ops_mesh_select_all.py b/blender/ops_mesh_select_all.py deleted file mode 100644 index 090ac8a..0000000 --- a/blender/ops_mesh_select_all.py +++ /dev/null @@ -1,10 +0,0 @@ -import blender_node - - -class MeshSelectAllOps(blender_node.EditOps): - EXTRA_INPUT_TYPES = { - "action": (['SELECT', 'TOGGLE', 'DESELECT', 'INVERT'],), - } - - def blender_process(self, bpy, BPY_OBJ, **props): - bpy.ops.mesh.select_all(**props)