versioning.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #Versioning Tasks
  2. # this will be the new versioning system, and will deprecate the old SOCKETS_ADDED and such
  3. from bpy.types import Node, NodeSocket
  4. from .utilities import prRed, prGreen, prPurple
  5. def version_upgrade_very_old(*args, **kwargs):
  6. node = kwargs['node']
  7. current_major_version = node.id_data.mantis_version[0]
  8. current_minor_version = node.id_data.mantis_version[1]
  9. if current_major_version > 0: return# major version must be 0
  10. if current_minor_version > 11: return# minor version must be 12 or less
  11. # this is the old node update, very inneficient and strange and badly organized
  12. NODES_REMOVED=["xFormRootNode"]
  13. # Node bl_idname, # Socket Name
  14. SOCKETS_REMOVED=[("UtilityDriverVariable", "Transform Channel"),
  15. ("xFormRootNode","World Out"),
  16. ("UtilitySwitch","xForm"),
  17. ("LinkDrivenParameter", "Enable")]
  18. # Node Class #Prior bl_idname # prior name # new bl_idname # new name, # Multi
  19. SOCKETS_RENAMED=[ ("LinkDrivenParameter", "DriverSocket", "Driver", "FloatSocket", "Value", False),
  20. ("DeformerHook", "IntSocket", "Index", "UnsignedIntSocket", "Point Index", False),
  21. ("SchemaConstOutput", "IntSocket", "Expose when N==", "UnsignedIntSocket", "Expose at Index", False),]
  22. # NODE CLASS NAME IN_OUT SOCKET TYPE SOCKET NAME INDEX MULTI DEFAULT
  23. SOCKETS_ADDED=[("DeformerMorphTargetDeform", 'INPUT', 'BooleanSocket', "Use Shape Key", 1, False, False),
  24. ("DeformerMorphTargetDeform", 'INPUT', 'BooleanSocket', "Use Offset", 2, False, True),
  25. ("UtilityFCurve", 'INPUT', "eFCrvExtrapolationMode", "Extrapolation Mode", 0, False, 'CONSTANT'),
  26. ("LinkCopyScale", 'INPUT', "BooleanSocket", "Additive", 3, False, False),
  27. ("DeformerHook", 'INPUT', "FloatFactorSocket", "Influence",3, False, 1.0),
  28. ("DeformerHook", 'INPUT', "UnsignedIntSocket", "Spline Index", 2, False, 0),
  29. ("DeformerHook", 'INPUT', "BooleanSocket", "Auto-Bezier", 5, False, True),
  30. ("UtilityCompare", 'INPUT', "EnumCompareOperation", "Comparison", 0, False, 'EQUAL'),
  31. ("UtilityMatrixFromCurve", 'INPUT', "UnsignedIntSocket", "Spline Index", 1, False, 0),
  32. ("UtilityMatricesFromCurve", 'INPUT', "UnsignedIntSocket", "Spline Index", 1, False, 0),
  33. ("UtilityPointFromCurve", 'INPUT', "UnsignedIntSocket", "Spline Index", 1, False, 0),
  34. ("LinkCopyScale", 'INPUT', "FloatFactorSocket", "Power", 5, False, 1.0),
  35. ]
  36. rename_jobs = []
  37. node_tree = kwargs['node_tree']
  38. try:
  39. if node.bl_idname in NODES_REMOVED:
  40. print(f"INFO: removing node {node.name} of type {node.bl_idname} because it has been deprecated.")
  41. node.inputs.remove(socket)
  42. return
  43. for i, socket in enumerate(node.inputs.values()):
  44. if (node.bl_idname, socket.identifier) in SOCKETS_REMOVED:
  45. print(f"INFO: removing socket {socket.identifier} of node {node.name} of type {node.bl_idname} because it has been deprecated.")
  46. node.inputs.remove(socket)
  47. for old_class, old_bl_idname, old_name, new_bl_idname, new_name, multi in SOCKETS_RENAMED:
  48. if (node.bl_idname == old_class and socket.bl_idname == old_bl_idname and socket.name == old_name):
  49. rename_jobs.append((socket, i, new_bl_idname, new_name, multi))
  50. for i, socket in enumerate(node.outputs.values()):
  51. if (node.bl_idname, socket.identifier) in SOCKETS_REMOVED:
  52. print(f"INFO: removing socket {socket.identifier} of node {node.name} of type {node.bl_idname} because it has been deprecated.")
  53. node.outputs.remove(socket)
  54. for old_class, old_bl_idname, old_name, new_bl_idname, new_name, multi in SOCKETS_RENAMED:
  55. if (node.bl_idname == old_class and socket.bl_idname == old_bl_idname and socket.name == old_name):
  56. rename_jobs.append((socket, i, new_bl_idname, new_name, multi))
  57. for bl_idname, in_out, socket_type, socket_name, index, use_multi_input, default_val in SOCKETS_ADDED:
  58. if node.bl_idname != bl_idname:
  59. continue
  60. if in_out == 'INPUT' and node.inputs.get(socket_name) is None:
  61. print(f"INFO: adding socket \"{socket_name}\" of type {socket_type} to node {node.name} of type {node.bl_idname}.")
  62. s = node.inputs.new(socket_type, socket_name, use_multi_input=use_multi_input)
  63. try:
  64. s.default_value = default_val
  65. except AttributeError:
  66. pass # the socket is read-only
  67. node.inputs.move(len(node.inputs)-1, index)
  68. socket_map = None
  69. if rename_jobs:
  70. from .utilities import get_socket_maps
  71. socket_maps = get_socket_maps(node)
  72. for socket, socket_index, new_bl_idname, new_name, multi in rename_jobs:
  73. old_id = socket.identifier
  74. print (f"Renaming socket {socket.identifier} to {new_name} in node {node.name}")
  75. from .utilities import do_relink
  76. if socket.is_output:
  77. index = 1
  78. in_out = "OUTPUT"
  79. node.outputs.remove(socket)
  80. s = node.outputs.new(new_bl_idname, new_name, identifier=new_name, use_multi_input=multi)
  81. node.outputs.move(len(node.outputs)-1, socket_index)
  82. socket_map = socket_maps[1]
  83. else:
  84. index = 0
  85. in_out = "INPUT"
  86. node.inputs.remove(socket)
  87. s = node.inputs.new(new_bl_idname, new_name, identifier=new_name, use_multi_input=multi)
  88. node.inputs.move(len(node.inputs)-1, socket_index)
  89. socket_map = socket_maps[0]
  90. socket_map[new_name] = socket_map[old_id]
  91. if new_name != old_id: del socket_map[old_id] # sometimes rename just changes the socket type or multi
  92. do_relink(node, s, socket_map)
  93. except Exception as e:
  94. prRed(f"Error updating version in node: {node.id_data.name}::{node.name}; see error:")
  95. print(e)
  96. def version_upgrade_bone_0_12_0_from_older(*args, **kwargs):
  97. # we need to check if it has an array collection input and a color input
  98. # then we need to solve each task
  99. node = kwargs['node']
  100. current_major_version = node.id_data.mantis_version[0]
  101. current_minor_version = node.id_data.mantis_version[1]
  102. if current_major_version > 0: # major version must be 0
  103. return
  104. if current_minor_version >= 12: # minor version must be 11 or less
  105. return
  106. # sub version doesn't matter since any subversion of 11 should trigger this task
  107. try:
  108. collection_input_is_array = node.inputs['Bone Collection'].is_multi_input
  109. if not collection_input_is_array: # it must be made into an array!
  110. prPurple(f"Updating \"Bone Collection\" Socket in {node.name}")
  111. from .utilities import get_socket_maps
  112. socket_maps = get_socket_maps(node)
  113. socket_map = socket_maps[0]
  114. for i, socket in enumerate(node.inputs):
  115. if socket.name == 'Bone Collection': break
  116. old_id = socket.identifier
  117. # it is an input
  118. node.inputs.remove(socket)
  119. s = node.inputs.new('BoneCollectionSocket', 'Bone Collection',
  120. identifier='Bone Collection', use_multi_input=True)
  121. node.inputs.move(len(node.inputs)-1, i)
  122. socket_map_from_old_socket = socket_map[old_id]
  123. # there seems to be an error in do_relink
  124. # gonna do it directly instead
  125. if isinstance(socket_map_from_old_socket, list):
  126. for map_info in socket_map_from_old_socket:
  127. if isinstance(map_info, Node ):
  128. l = node.id_data.links.new(input=map_info.outputs[0], output=s)
  129. elif isinstance(map_info, NodeSocket):
  130. l = node.id_data.links.new(input=map_info, output=s)
  131. else:
  132. s.default_value = socket_map_from_old_socket
  133. if node.inputs.get('Color') is None:
  134. prPurple(f"Adding \"Color\" Socket to {node.name}")
  135. s = node.inputs.new('ColorSetSocket', 'Color',)
  136. node.inputs.move(len(node.inputs)-1, 22)
  137. except Exception as e:
  138. prRed(f"Error updating version in node: {node.id_data.name}::{node.name}; see error:")
  139. print(e)
  140. def up_0_12_1_add_inherit_color(*args, **kwargs):
  141. # add an inherit color input.
  142. node = kwargs['node']
  143. current_major_version = node.id_data.mantis_version[0]
  144. current_minor_version = node.id_data.mantis_version[1]
  145. current_sub_version = node.id_data.mantis_version[2]
  146. if current_major_version > 0: return# major version must be 0
  147. if current_minor_version > 12: return# minor version must be 12 or less
  148. if current_minor_version == 12 and current_sub_version > 9: return # sub version must be 8 or less
  149. # I am having it do 8 or less because there was a bug in this function prior to 9
  150. # sub version doesn't matter since any subversion of 11 should trigger this task
  151. prPurple(f"Adding \"Inherit Color\" socket to {node.name}")
  152. try:
  153. inh_color = node.inputs.get('Inherit Color')
  154. if inh_color and inh_color.bl_idname != 'BooleanSocket':
  155. node.inputs.remove(inh_color)
  156. inh_color = None
  157. if inh_color is None:
  158. s = node.inputs.new('BooleanSocket', 'Inherit Color',)
  159. node.inputs.move(len(node.inputs)-1, 23)
  160. s.default_value=True
  161. except Exception as e:
  162. prRed(f"Error updating version in node: {node.id_data.name}::{node.name}; see error:")
  163. print(e)
  164. versioning_tasks = [
  165. # node bl_idname task required keyword arguments
  166. (['ALL'], version_upgrade_very_old, ['node_tree', 'node'],),
  167. (['xFormBoneNode'], version_upgrade_bone_0_12_0_from_older, ['node'],),
  168. (['xFormBoneNode'], up_0_12_1_add_inherit_color, ['node'],),
  169. ]
  170. # WORKAROUNDS for bugs should go here:
  171. # 4.5.0 LTS valid socket types bug:
  172. def workaround_4_5_0_interface_update(tree, name, in_out, sock_type, parent_name, do_parent=False):
  173. # TODO: dedupe this code from i_o.py
  174. prRed("There is a bug in Blender 4.5.0 regarding node-group interface sockets. Working around it.")
  175. sock = tree.interface.new_socket(name=name, in_out=in_out, socket_type="NodeSocketGeometry")
  176. import json
  177. interface_helper = {} # initialize it if it is empty
  178. if tree.interface_helper: # may be empty, check here
  179. interface_helper = json.loads(tree.interface_helper)
  180. error_message= 'There is a bug in Blender 4.5.0 LTS, that is why these sockets are blue.'\
  181. ' This will be fixed in future Blender versions.'
  182. interface_helper[sock.identifier] = {
  183. 'name' : sock.name,
  184. 'identifier' : sock.identifier,
  185. 'in_out' : sock.in_out,
  186. 'socket_type' : sock_type,
  187. 'bl_socket_idname' : sock_type,
  188. 'mantis_socket_category' : parent_name,
  189. 'description' : error_message,
  190. }
  191. sock.description = error_message # this tells the user why the socket looks weird.
  192. tree.interface_helper = json.dumps(interface_helper)
  193. if do_parent and (parent := tree.interface.items_tree.get(parent_name)):
  194. prRed(parent.name)
  195. tree.interface.move_to_parent(
  196. sock,
  197. parent,
  198. 0, # what to do here?
  199. )
  200. return sock
  201. def socket_add_workaround_for_4_5_0_LTS(item, socket_collection, multi):
  202. import json
  203. tree = item.id_data
  204. interface_helper = json.loads(tree.interface_helper)
  205. socket_info = interface_helper.get(item.identifier)
  206. if not socket_info: raise RuntimeError(f"There has been an error adding the socket {item.name}")
  207. s = socket_collection.new(
  208. type=socket_info['bl_socket_idname'],
  209. name=item.name, # in case the user has changed it
  210. identifier=item.identifier,
  211. use_multi_input=multi, )
  212. return s