base_definitions.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. #Mantis Nodes Base
  2. import bpy
  3. from bpy.props import BoolProperty, StringProperty, EnumProperty, CollectionProperty, IntProperty
  4. from . import ops_nodegroup
  5. from bpy.types import NodeTree, Node, PropertyGroup, Operator, UIList, Panel
  6. from mantis.utilities import (prRed, prGreen, prPurple, prWhite,
  7. prOrange,
  8. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  9. wrapOrange,)
  10. from bpy.app.handlers import persistent
  11. def TellClasses():
  12. #Why use a function to do this? Because I don't need every class to register.
  13. return [MantisNodeGroup, MantisTree, ]
  14. class MantisTree(NodeTree):
  15. '''A custom node tree type that will show up in the editor type list'''
  16. bl_idname = 'MantisTree'
  17. bl_label = "Rigging Nodes"
  18. bl_icon = 'OUTLINER_OB_ARMATURE'
  19. tree_valid:BoolProperty(default=False)
  20. do_live_update:BoolProperty(default=True)
  21. # use this to disable updates for e.g. scripts
  22. locked:BoolProperty(default=True)
  23. num_links:IntProperty(default=-1)
  24. parsed_tree={}
  25. def interface_update(self, context):
  26. # no idea what this does
  27. print ("Update Interface function in MantisTree class")
  28. def interface_update(self, context):
  29. prGreen("interface_update")
  30. if bpy.app.version >= (3, 2): # in 3.1 this can lead to a crash
  31. @classmethod
  32. def valid_socket_type(cls, socket_type: str):
  33. # https://docs.blender.org/api/master/bpy.types.NodeTree.html#bpy.types.NodeTree.valid_socket_type
  34. from mantis.socket_definitions import Tell_bl_idnames
  35. return socket_type in Tell_bl_idnames()
  36. # thank you, Sverchok
  37. def update_tree(self, context):
  38. if self.do_live_update == False:
  39. return
  40. from mantis import readtree
  41. prGreen("Validating Tree: %s" % self.name)
  42. parsed_tree = readtree.parse_tree(self)
  43. self.parsed_tree=parsed_tree
  44. current_tree = bpy.context.space_data.path[-1].node_tree
  45. self.tree_valid = True
  46. prWhite("Number of Nodes: %s" % (len(self.parsed_tree)))
  47. self.display_update(context)
  48. def display_update(self, context):
  49. if self.do_live_update == False:
  50. return
  51. current_tree = bpy.context.space_data.path[-1].node_tree
  52. for node in current_tree.nodes:
  53. if hasattr(node, "display_update"):
  54. try:
  55. node.display_update(self.parsed_tree, context)
  56. except Exception as e:
  57. print("Node \"%s\" failed to update display with error: %s" %(wrapGreen(node.name), wrapRed(e)))
  58. # raise e
  59. def execute_tree(self,context):
  60. prGreen("Executing Tree: %s" % self.name)
  61. from mantis import readtree
  62. readtree.execute_tree(self.parsed_tree, self, context)
  63. def update_handler(scene):
  64. context=bpy.context
  65. if context.space_data:
  66. node_tree = context.space_data.path[0].node_tree
  67. if node_tree.do_live_update:
  68. prev_links = node_tree.num_links
  69. node_tree.num_links = len(node_tree.links)
  70. if (prev_links == -1):
  71. return
  72. if prev_links != node_tree.num_links:
  73. node_tree.tree_valid = False
  74. if node_tree.tree_valid == False:
  75. from mantis import readtree
  76. node_tree.update_tree(context)
  77. def execute_handler(scene):
  78. context = bpy.context
  79. if context.space_data:
  80. node_tree = context.space_data.path[0].node_tree
  81. if node_tree.tree_valid and node_tree.do_live_update:
  82. node_tree.execute_tree(context)
  83. self.tree_valid = False
  84. # bpy.app.handlers.load_post.append(set_tree_invalid)
  85. bpy.app.handlers.depsgraph_update_pre.append(update_handler)
  86. bpy.app.handlers.depsgraph_update_post.append(execute_handler)
  87. class MantisNode:
  88. num_links:IntProperty(default=-1)
  89. # do_display_update:BoolProperty(default=False)
  90. @classmethod
  91. def poll(cls, ntree):
  92. return (ntree.bl_idname == 'MantisTree')
  93. def insert_link(self, link):
  94. context = bpy.context
  95. if context.space_data:
  96. node_tree = context.space_data.path[0].node_tree
  97. from mantis import readtree
  98. prOrange("Updating from insert_link callback")
  99. node_tree.update_tree(context)
  100. if (link.to_socket.is_linked == False):
  101. node_tree.num_links+=1
  102. elif (link.to_socket.is_multi_input and
  103. link.to_socket.links < link.to_socket.link_limit ):
  104. node_tree.num_links+=1
  105. class LinkNode(MantisNode):
  106. useTarget : BoolProperty(default=False)
  107. @classmethod
  108. def poll(cls, ntree):
  109. return (ntree.bl_idname == 'MantisTree')
  110. class xFormNode(MantisNode):
  111. @classmethod
  112. def poll(cls, ntree):
  113. return (ntree.bl_idname == 'MantisTree')
  114. class DeformerNode(MantisNode):
  115. @classmethod
  116. def poll(cls, ntree):
  117. return (ntree.bl_idname == 'MantisTree')
  118. from bpy.types import NodeCustomGroup
  119. # TODO: make this one's traverse() function actually work
  120. class MantisNodeGroup(NodeCustomGroup, MantisNode):
  121. bl_idname = "MantisNodeGroup"
  122. bl_label = "Node Group"
  123. # def poll_node_tree(self, object):
  124. # if object.bl_idname not in "MantisTree":
  125. # return False
  126. # context=bpy.context
  127. # context = bpy.context
  128. # if context.space_data:
  129. # used_trees = [ pathitem.node_tree for pathitem in context.space_data.path]
  130. # if object in used_trees:
  131. # return False
  132. # node_tree:bpy.props.PointerProperty(type=bpy.types.NodeTree, poll=poll_node_tree)
  133. def init(self, context):
  134. pass
  135. def draw_buttons(self, context, layout):
  136. row = layout.row(align=True)
  137. row.prop(self, "node_tree", text="")
  138. row.operator("mantis.edit_group", text="", icon='NODETREE', emboss=True)
  139. # I don't remember why I need this?
  140. class GroupOutputDummySocket:
  141. # a dummy class for acting like a socket that is coming from every
  142. # group output node
  143. def __init__(self, tree, identifier, is_input=True):
  144. #
  145. # So, we need to go through the node tree and find all
  146. # the Group Input sockets that match this
  147. # socket's identifier
  148. #
  149. sockets = []
  150. s = None
  151. for node in tree.nodes:
  152. if (is_input):
  153. if node.bl_idname == 'NodeGroupInput':
  154. # Group Inputs have outputs... confusing.
  155. for s in node.outputs:
  156. if (s.identifier == identifier):
  157. sockets.append(s)
  158. else:
  159. if node.bl_idname == 'NodeGroupOutput':
  160. for s in node.inputs:
  161. if (s.identifier == identifier):
  162. sockets.append(s)
  163. sock = sockets[-1]
  164. # whatever the last socket is should be OK for most of this stuff
  165. self.bl_idname=sock.bl_idname
  166. self.identifier = identifier
  167. self.name = sock.name
  168. is_linked = False
  169. for s in sockets:
  170. if s.is_linked:
  171. is_linked = True; break
  172. self.is_linked = is_linked
  173. self.is_output = not is_input
  174. # hopefully this doesn't matter, since it is a group node...
  175. self.node = sock.node
  176. self.links = []
  177. for s in sockets:
  178. self.links.extend(s.links)
  179. # seems to werk
  180. class CircularDependencyError(Exception):
  181. pass
  182. class GraphError(Exception):
  183. pass
  184. def get_signature_from_edited_tree(self, context):
  185. sig_path=[None,]
  186. for item in context.space_data.path[:-1]:
  187. sig_path.append(item.node_tree.nodes.active.name)
  188. return tuple(sig_path+[self.name])