base_definitions.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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 .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 .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 . 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 . 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 . 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 . 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. def poll_node_tree(self, tree):
  121. return True #TODO: prevent circular group ofc
  122. class MantisNodeGroup(NodeCustomGroup, MantisNode):
  123. bl_idname = "MantisNodeGroup"
  124. bl_label = "Node Group"
  125. node_tree_updater : bpy.props.PointerProperty(type=bpy.types.NodeTree, poll=poll_node_tree)
  126. # def poll_node_tree(self, object):
  127. # if object.bl_idname not in "MantisTree":
  128. # return False
  129. # context=bpy.context
  130. # context = bpy.context
  131. # if context.space_data:
  132. # used_trees = [ pathitem.node_tree for pathitem in context.space_data.path]
  133. # if object in used_trees:
  134. # return False
  135. # node_tree:bpy.props.PointerProperty(type=bpy.types.NodeTree, poll=poll_node_tree)
  136. # def init(self, context):
  137. # pass
  138. def socket_value_update(self, context):
  139. prGreen("updating...")
  140. # this is a total HACK
  141. def update(self):
  142. if self.node_tree_updater is not self.node_tree:
  143. self.update_node_tree()
  144. self.node_tree_updater = self.node_tree
  145. def update_node_tree(self):
  146. self.inputs.clear()
  147. self.outputs.clear()
  148. for item in self.node_tree.interface.items_tree:
  149. if item.item_type != "SOCKET":
  150. continue
  151. s = None
  152. if item.in_out == 'OUTPUT':
  153. s = self.outputs.new(type=item.socket_type, name=item.name, identifier=item.identifier)
  154. else:
  155. s = self.inputs.new(type=item.socket_type, name=item.name, identifier=item.identifier)
  156. def draw_buttons(self, context, layout):
  157. row = layout.row(align=True)
  158. row.prop(self, "node_tree", text="")
  159. row.operator("mantis.edit_group", text="", icon='NODETREE', emboss=True)
  160. # # I don't remember why I need this?
  161. # class GroupOutputDummySocket:
  162. # # a dummy class for acting like a socket that is coming from every
  163. # # group output node
  164. # def __init__(self, tree, identifier, is_input=True):
  165. # #
  166. # # So, we need to go through the node tree and find all
  167. # # the Group Input sockets that match this
  168. # # socket's identifier
  169. # #
  170. # sockets = []
  171. # s = None
  172. # for node in tree.nodes:
  173. # if (is_input):
  174. # if node.bl_idname == 'NodeGroupInput':
  175. # # Group Inputs have outputs... confusing.
  176. # for s in node.outputs:
  177. # if (s.identifier == identifier):
  178. # sockets.append(s)
  179. # else:
  180. # if node.bl_idname == 'NodeGroupOutput':
  181. # for s in node.inputs:
  182. # if (s.identifier == identifier):
  183. # sockets.append(s)
  184. # sock = sockets[-1]
  185. # # whatever the last socket is should be OK for most of this stuff
  186. # self.bl_idname=sock.bl_idname
  187. # self.identifier = identifier
  188. # self.name = sock.name
  189. # is_linked = False
  190. # for s in sockets:
  191. # if s.is_linked:
  192. # is_linked = True; break
  193. # self.is_linked = is_linked
  194. # self.is_output = not is_input
  195. # # hopefully this doesn't matter, since it is a group node...
  196. # self.node = sock.node
  197. # self.links = []
  198. # for s in sockets:
  199. # self.links.extend(s.links)
  200. # # seems to werk
  201. class CircularDependencyError(Exception):
  202. pass
  203. class GraphError(Exception):
  204. pass
  205. def get_signature_from_edited_tree(self, context):
  206. sig_path=[None,]
  207. for item in context.space_data.path[:-1]:
  208. sig_path.append(item.node_tree.nodes.active.name)
  209. return tuple(sig_path+[self.name])