base_definitions.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. prWhite("Updating.")
  39. if self.do_live_update == False:
  40. return
  41. from mantis import readtree
  42. prGreen("Validating Tree: %s" % self.name)
  43. try:
  44. parsed_tree = readtree.parse_tree(self)
  45. except RecursionError:
  46. parsed_tree = {}
  47. self.parsed_tree=parsed_tree
  48. current_tree = bpy.context.space_data.path[-1].node_tree
  49. self.tree_valid = True
  50. prWhite("Number of Nodes: %s" % (len(self.parsed_tree)))
  51. self.display_update(context)
  52. def display_update(self, context):
  53. if self.do_live_update == False:
  54. return
  55. current_tree = bpy.context.space_data.path[-1].node_tree
  56. for node in current_tree.nodes:
  57. if hasattr(node, "display_update"):
  58. # try:
  59. node.display_update(self.parsed_tree, context)
  60. # except Exception as e:
  61. # print("Node \"%s\" failed to update display with error: %s" %(wrapGreen(node.name), wrapRed(e)))
  62. # # raise e
  63. def execute_tree(self,context):
  64. prGreen("Executing Tree: %s" % self.name)
  65. from mantis import readtree
  66. readtree.execute_tree(self.parsed_tree, self, context)
  67. @persistent
  68. def update_handler(scene):
  69. prGreen("Updating from depsgraph-pre handler!")
  70. context=bpy.context
  71. if context.space_data:
  72. node_tree = context.space_data.path[0].node_tree
  73. if node_tree.do_live_update:
  74. prev_links = node_tree.num_links
  75. node_tree.num_links = len(node_tree.links)
  76. if (prev_links == -1):
  77. return
  78. if prev_links != node_tree.num_links:
  79. node_tree.tree_valid = False
  80. if node_tree.tree_valid == False:
  81. from mantis import readtree
  82. node_tree.update_tree(context)
  83. # if node_tree.tree_valid and node_tree.do_live_update:
  84. # try:
  85. # node_tree.execute_tree(context)
  86. # except (RecursionError, RuntimeError):
  87. # pass
  88. # node_tree.tree_valid = False
  89. # @persistent
  90. # def execute_handler(scene):
  91. # prGreen("Executing from depsgraph-post handler!")
  92. # context = bpy.context
  93. # if context.space_data:
  94. # node_tree = context.space_data.path[0].node_tree
  95. # bpy.app.handlers.load_post.append(set_tree_invalid)
  96. # bpy.app.handlers.depsgraph_update_pre.append(update_handler)
  97. # bpy.app.handlers.depsgraph_update_post.append(execute_handler)
  98. class MantisNode:
  99. num_links:IntProperty(default=-1)
  100. # do_display_update:BoolProperty(default=False)
  101. @classmethod
  102. def poll(cls, ntree):
  103. return (ntree.bl_idname == 'MantisTree')
  104. def init(self, context):
  105. context = bpy.context
  106. if context.space_data:
  107. node_tree = context.space_data.path[0].node_tree
  108. from mantis import readtree
  109. prOrange("Updating from init callback")
  110. node_tree.update_tree(context)
  111. # def insert_link(self, link):
  112. # context = bpy.context
  113. # if context.space_data:
  114. # node_tree = context.space_data.path[0].node_tree
  115. # from mantis import readtree
  116. # prOrange("Updating from insert_link callback")
  117. # node_tree.update_tree(context)
  118. # if (link.to_socket.is_linked == False):
  119. # node_tree.num_links+=1
  120. # elif (link.to_socket.is_multi_input and
  121. # link.to_socket.links < link.to_socket.link_limit ):
  122. # node_tree.num_links+=1
  123. def free(self):
  124. context = bpy.context
  125. if context.space_data:
  126. node_tree = context.space_data.path[0].node_tree
  127. from mantis import readtree
  128. prOrange("Updating from free callback")
  129. node_tree.update_tree(context)
  130. sig = get_signature_from_edited_tree(self, context)
  131. if node_tree.parsed_tree.get(sig):
  132. del node_tree.parsed_tree[sig]
  133. node_tree.display_update(context)
  134. def copy(self, original):
  135. context = bpy.context
  136. if context.space_data:
  137. node_tree = context.space_data.path[0].node_tree
  138. from mantis import readtree
  139. prOrange("Updating from copy callback")
  140. node_tree.update_tree(context)
  141. class LinkNode(MantisNode):
  142. useTarget : BoolProperty(default=False)
  143. @classmethod
  144. def poll(cls, ntree):
  145. return (ntree.bl_idname == 'MantisTree')
  146. class xFormNode(MantisNode):
  147. @classmethod
  148. def poll(cls, ntree):
  149. return (ntree.bl_idname == 'MantisTree')
  150. class DeformerNode(MantisNode):
  151. @classmethod
  152. def poll(cls, ntree):
  153. return (ntree.bl_idname == 'MantisTree')
  154. from bpy.types import NodeCustomGroup
  155. # TODO: make this one's traverse() function actually work
  156. class MantisNodeGroup(NodeCustomGroup, MantisNode):
  157. bl_idname = "MantisNodeGroup"
  158. bl_label = "Node Group"
  159. # def poll_node_tree(self, object):
  160. # if object.bl_idname not in "MantisTree":
  161. # return False
  162. # context=bpy.context
  163. # context = bpy.context
  164. # if context.space_data:
  165. # used_trees = [ pathitem.node_tree for pathitem in context.space_data.path]
  166. # if object in used_trees:
  167. # return False
  168. # node_tree:bpy.props.PointerProperty(type=bpy.types.NodeTree, poll=poll_node_tree)
  169. def init(self, context):
  170. pass
  171. def draw_buttons(self, context, layout):
  172. row = layout.row(align=True)
  173. row.prop(self, "node_tree", text="")
  174. row.operator("mantis.edit_group", text="", icon='NODETREE', emboss=True)
  175. # I don't remember why I need this?
  176. class GroupOutputDummySocket:
  177. # a dummy class for acting like a socket that is coming from every
  178. # group output node
  179. def __init__(self, tree, identifier, is_input=True):
  180. #
  181. # So, we need to go through the node tree and find all
  182. # the Group Input sockets that match this
  183. # socket's identifier
  184. #
  185. sockets = []
  186. s = None
  187. for node in tree.nodes:
  188. if (is_input):
  189. if node.bl_idname == 'NodeGroupInput':
  190. # Group Inputs have outputs... confusing.
  191. for s in node.outputs:
  192. if (s.identifier == identifier):
  193. sockets.append(s)
  194. else:
  195. if node.bl_idname == 'NodeGroupOutput':
  196. for s in node.inputs:
  197. if (s.identifier == identifier):
  198. sockets.append(s)
  199. sock = sockets[-1]
  200. # whatever the last socket is should be OK for most of this stuff
  201. self.bl_idname=sock.bl_idname
  202. self.identifier = identifier
  203. self.name = sock.name
  204. is_linked = False
  205. for s in sockets:
  206. if s.is_linked:
  207. is_linked = True; break
  208. self.is_linked = is_linked
  209. self.is_output = not is_input
  210. # hopefully this doesn't matter, since it is a group node...
  211. self.node = sock.node
  212. self.links = []
  213. for s in sockets:
  214. self.links.extend(s.links)
  215. # seems to werk
  216. class CircularDependencyError(Exception):
  217. pass
  218. class GraphError(Exception):
  219. pass
  220. def get_signature_from_edited_tree(self, context):
  221. sig_path=[None,]
  222. for item in context.space_data.path[:-1]:
  223. sig_path.append(item.node_tree.nodes.active.name)
  224. return tuple(sig_path+[self.name])
  225. def get_is_name_unique(nc, tree, namespace='BONE'):
  226. if namespace == 'BONE':
  227. name = nc.evaluate_input("Name")
  228. for sig, nc_other in tree.items():
  229. if nc.__class__.__name__ in ["xFormBone"]:
  230. if nc_other == nc:
  231. continue
  232. if name == nc_other.evaluate_input("Name"):
  233. return False
  234. return True
  235. # def get_unique_name(name, tree, namespace='BONE'):
  236. # if namespace == 'BONE':
  237. # stub_index = {}
  238. # for sig, nc in tree.items():
  239. # if nc.__class__.__name__ in ["xFormBone"]:
  240. # name = sig[-1]
  241. # split_name = name.split('.')
  242. # prWhite(".".join(split_name[:-1]))
  243. # try:
  244. # i = int(split_name[-1])
  245. # prGreen(i)
  246. # except ValueError:
  247. # i=-1
  248. # if i > 0:
  249. # prev_i = stub_index
  250. # stub_index[".".join(split_name[:-1])]=i
  251. # print (stub_index)