Blender插件如何对自定义创建的节点树进行分组?

时间:2016-08-16 08:45:41

标签: python-3.x blender

我正在尝试编写可以创建自定义节点的blender插件,我们可以将它们作为普通的blender构建节点来操作,特别是将它们组合在一起,在我的选项中它是一个非常导入的函数,但现在代码可以添加自定义节点,但分组仍然不起作用,所以任何提示?

#
# architect.py -- the blender addon
#

import bpy
import nodeitems_utils
from nodeitems_utils import NodeCategory, NodeItem, NodeItemCustom
from bpy.types import NodeTree, ShaderNodeTree, Node, NodeGroup, NodeCustomGroup, NodeSocket


bl_info = {
    "name": "Architect",
    "author": "Lei Liu",
    "category": "Node"}


class ArchitectEngine(bpy.types.RenderEngine):
    bl_idname = 'ARCHITECT_RENDER'
    bl_label = "Architect"
    bl_use_preview = False
    bl_use_shading_nodes = False
    bl_use_exclude_layers = False
    bl_use_save_buffers = False

    draw_callbacks = {}

    def __init__(self):
        self.session = None
        pass


    def __del__(self):
        pass


    # main scene render
    def update(self, data, scene):
        pass


    def render(self, scene):
        pass


class ArchitectNodeTree(ShaderNodeTree):
    bl_idname = 'ArchitectNodeTree'
    bl_label = 'Architect Node Tree'
    bl_icon = 'NODETREE'
    nodetypes = {}
    pass

    @classmethod
    def poll(cls, context):
        return context.scene.render.engine == 'ARCHITECT_RENDER'


class ArchitectNodeGroup(NodeCustomGroup):
    bl_idname = 'ArchitectNodeGroup'
    bl_label = 'Architect Node Group'
    node_tree = ArchitectNodeTree

    @classmethod
    def poll(cls, context):
        return context.scene.render.engine == 'ARCHITECT_RENDER'


# Custom socket type
class ArchitectSocket(NodeSocket):
    # Description string
    '''Architect node socket type'''
    # Optional identifier string. If not explicitly defined, the python class name is used.
    bl_idname = 'ArchitectSocketType'
    # Label for nice name display
    bl_label = 'Architect Node Socket'

    # Enum items list
    my_items = [
        ("DOWN", "Down", "Where your feet are"),
        ("UP", "Up", "Where your head should be"),
        ("LEFT", "Left", "Not right"),
        ("RIGHT", "Right", "Not left")
    ]

    myEnumProperty = bpy.props.EnumProperty(name="Direction", description="Just an example", items=my_items, default='UP')

    # Optional function for drawing the socket input value
    def draw(self, context, layout, node, text):
        if self.is_output or self.is_linked:
            layout.label(text)
        else:
            layout.prop(self, "myEnumProperty", text=text)

    # Socket color
    def draw_color(self, context, node):
        return (1.0, 0.4, 0.216, 0.5)


class ArchitectTreeNode:
    @classmethod
    def poll(cls, ntree):
        return ntree.bl_idname == 'ArchitectNodeTree'


class DemoNode(Node, ArchitectTreeNode):
    bl_idname = 'DemoNodeType'
    bl_label = 'Demo Node'
    bl_icon = 'SOUND'
    typename = 'DemoNodeType'
    # === Custom Properties ===
    # These work just like custom properties in ID data blocks
    # Extensive information can be found under
    # http://wiki.blender.org/index.php/Doc:2.6/Manual/Extensions/Python/Properties
    myStringProperty = bpy.props.StringProperty()
    myFloatProperty = bpy.props.FloatProperty(default=3.1415926)

    # === Optional Functions ===
    # Initialization function, called when a new node is created.
    # This is the most common place to create the sockets for a node, as shown below.
    # NOTE: this is not the same as the standard __init__ function in Python, which is
    #       a purely internal Python method and unknown to the node system!
    def init(self, context):
        self.inputs.new('ArchitectSocketType', "Hello")
        self.inputs.new('NodeSocketFloat', "World")
        self.inputs.new('NodeSocketVector', "!")

        self.outputs.new('NodeSocketColor', "How")
        self.outputs.new('NodeSocketColor', "are")
        self.outputs.new('NodeSocketFloat', "you")

    # Copy function to initialize a copied node from an existing one.
    def copy(self, node):
        print("Copying from node ", node)

    # Free function to clean up on removal.
    def free(self):
        print("Removing node ", self, ", Goodbye!")

    # Additional buttons displayed on the node.
    def draw_buttons(self, context, layout):
        layout.label("Node settings")
        layout.prop(self, "myFloatProperty")

    # Detail buttons in the sidebar.
    # If this function is not defined, the draw_buttons function is used instead
    def draw_buttons_ext(self, context, layout):
        layout.prop(self, "myFloatProperty")
        # myStringProperty button will only be visible in the sidebar
        layout.prop(self, "myStringProperty")

    # Optional: custom label
    # Explicit user label overrides this, but here we can define a label dynamically
    def draw_label(self):
        return "I am a custom node"


class ArchitectNodeCategory(NodeCategory):
    @classmethod
    def poll(cls, context):
        return (context.space_data.tree_type == 'ArchitectNodeTree')


# menu entry for node group tools
def group_tools_draw(self, layout, context):
    layout.operator("node.group_make")
    layout.operator("node.group_ungroup")
    layout.separator()

# maps node tree type to group node type
node_tree_group_type = {
    'CompositorNodeTree': 'CompositorNodeGroup',
    'ShaderNodeTree': 'ShaderNodeGroup',
    'TextureNodeTree': 'TextureNodeGroup',
    'ArchitectNodeTree': 'ArchitectNodeGroup',
    }


# generic node group items generator for shader, compositor and texture node groups
def node_group_items(context):
    if context is None:
        return
    space = context.space_data
    if not space:
        return
    ntree = space.edit_tree
    if not ntree:
        return

    yield NodeItemCustom(draw=group_tools_draw)

    def contains_group(nodetree, group):
        if nodetree == group:
            return True
        else:
            for node in nodetree.nodes:
                if node.bl_idname in node_tree_group_type.values() and node.node_tree is not None:
                    if contains_group(node.node_tree, group):
                        return True
        return False

    for group in context.blend_data.node_groups:
        if group.bl_idname != ntree.bl_idname:
            continue
        # filter out recursive groups
        if contains_group(group, ntree):
            continue

        yield NodeItem(node_tree_group_type[group.bl_idname],
                       group.name,
                       {"node_tree": "bpy.data.node_groups[%r]" % group.name})


# only show input/output nodes inside node groups
def group_input_output_item_poll(context):
    return False


architect_node_categories = [
        ArchitectNodeCategory("ARCH_DEMO", "Demo", items=[
            NodeItem("DemoNodeType"),
            ]),
        ArchitectNodeCategory("ARCH_INPUT", "Input", items=[
            NodeItem("TextureNodeCurveTime"),
            NodeItem("TextureNodeCoordinates"),
            NodeItem("TextureNodeTexture"),
            NodeItem("TextureNodeImage"),
            NodeItem("NodeGroupInput", poll=group_input_output_item_poll),
            ]),
        ArchitectNodeCategory("ARCH_OUTPUT", "Output", items=[
            NodeItem("NodeGroupOutput", poll=group_input_output_item_poll),
            ]),
        ArchitectNodeCategory("ARCH_GROUP", "Group", items=node_group_items),
        ArchitectNodeCategory("ARCH_LAYOUT", "Layout", items=[
            NodeItem("NodeFrame"),
            NodeItem("NodeReroute"),
            ]),
    ]


def register():
    bpy.utils.register_class(ArchitectNodeTree)
    bpy.utils.register_class(ArchitectNodeGroup)
    bpy.utils.register_class(DemoNode)
    nodeitems_utils.register_node_categories('ARCHITECT', architect_node_categories)
    bpy.utils.register_module(__name__)
    pass


def unregister():
    nodeitems_utils.unregister_node_categories('ARCHITECT')
    bpy.utils.unregister_class(ArchitectNodeGroup)
    bpy.utils.unregister_class(ArchitectNodeTree)
    bpy.utils.unregister_class(DemoNode)
    bpy.utils.unregister_module(__name__)
    pass


if __name__ == "__main__":
    register()

0 个答案:

没有答案