deformer_containers.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. from .node_container_common import *
  2. from .xForm_containers import xFormGeometryObject
  3. from .misc_nodes import InputExistingGeometryObject
  4. from .base_definitions import MantisNode, MantisSocketTemplate
  5. from .utilities import (prRed, prGreen, prPurple, prWhite, prOrange,
  6. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  7. wrapOrange,)
  8. from bpy.types import NodeTree
  9. def TellClasses():
  10. return [
  11. DeformerArmature,
  12. DeformerHook,
  13. DeformerMorphTarget,
  14. DeformerMorphTargetDeform,
  15. ]
  16. def trace_xForm_back(nc, socket):
  17. from .xForm_containers import xFormGeometryObject
  18. from .misc_nodes import InputExistingGeometryObject
  19. from bpy.types import Object
  20. if (trace := trace_single_line(nc, socket)[0] ) :
  21. for i in range(len(trace)): # have to look in reverse, actually
  22. if ( isinstance(trace[ i ], xFormGeometryObject ) ) or ( isinstance(trace[ i ], InputExistingGeometryObject ) ):
  23. return trace[ i ].bGetObject()
  24. raise GraphError(wrapRed(f"No other object found for {nc}."))
  25. # semi-duplicated from link_containers
  26. def GetxForm(nc):
  27. trace = trace_single_line_up(nc, "Deformer")
  28. for node in trace[0]:
  29. if (node.__class__ in [xFormGeometryObject, InputExistingGeometryObject]):
  30. return node
  31. raise GraphError("%s is not connected to a downstream xForm" % nc)
  32. class MantisDeformerNode(MantisNode):
  33. def __init__(self, signature : tuple,
  34. base_tree : NodeTree,
  35. socket_templates : list[MantisSocketTemplate]=[]):
  36. super().__init__(signature, base_tree, socket_templates)
  37. self.node_type = 'LINK'
  38. self.prepared = True
  39. # we need evaluate_input to have the same behaviour as links.
  40. def evaluate_input(self, input_name, index=0):
  41. if ('Target' in input_name):
  42. socket = self.inputs.get(input_name)
  43. if socket.is_linked:
  44. return socket.links[0].from_node
  45. return None
  46. else:
  47. return super().evaluate_input(input_name, index)
  48. class DeformerArmature(MantisDeformerNode):
  49. '''A node representing an armature deformer'''
  50. def __init__(self, signature, base_tree):
  51. super().__init__(signature, base_tree)
  52. inputs = [
  53. "Input Relationship",
  54. "Armature Object",
  55. "Blend Vertex Group",
  56. "Invert Vertex Group",
  57. "Preserve Volume",
  58. "Use Multi Modifier",
  59. "Use Envelopes",
  60. "Use Vertex Groups",
  61. "Skinning Method",
  62. "Deformer",
  63. "Copy Skin Weights From"
  64. ]
  65. outputs = [
  66. "Deformer"
  67. ]
  68. self.outputs.init_sockets(outputs)
  69. self.inputs.init_sockets(inputs)
  70. self.init_parameters(additional_parameters={"Name":None})
  71. self.set_traverse([("Deformer", "Deformer")])
  72. self.node_type = "LINK"
  73. self.prepared = True
  74. def GetxForm(self, socket="Deformer"):
  75. if socket == "Deformer":
  76. return GetxForm(self)
  77. else:
  78. trace_xForm_back(self, socket)
  79. # DUPLICATED FROM xForm_containers::xFormBone
  80. # DEDUP HACK HACK HACK HACK HACK
  81. def bGetParentArmature(self):
  82. from .xForm_containers import xFormArmature
  83. from .misc_nodes import InputExistingGeometryObject
  84. from bpy.types import Object
  85. if (trace := trace_single_line(self, "Armature Object")[0] ) :
  86. for i in range(len(trace)):
  87. # have to look in reverse, actually
  88. if ( isinstance(trace[ i ], xFormArmature ) ):
  89. return trace[ i ].bGetObject()
  90. elif ( isinstance(trace[i], InputExistingGeometryObject)):
  91. if (ob := trace[i].bGetObject()).type == "ARMATURE":
  92. return ob
  93. raise RuntimeError(f"Cannot find armature for node {self}")
  94. return None
  95. #should do the trick...
  96. def bExecute(self, bContext = None,):
  97. self.executed = True
  98. def initialize_vgroups(self,):
  99. ob = self.GetxForm().bGetObject()
  100. armOb = self.bGetParentArmature()
  101. for b in armOb.data.bones:
  102. if b.use_deform == False:
  103. continue
  104. vg = ob.vertex_groups.get(b.name)
  105. if not vg:
  106. vg = ob.vertex_groups.new(name=b.name)
  107. num_verts = len(ob.data.vertices)
  108. vg.add(range(num_verts), 0, 'REPLACE')
  109. def copy_weights(self):
  110. # we'll use modifiers for this, maybe use GN for it in the future tho
  111. import bpy
  112. ob = self.GetxForm().bGetObject()
  113. try:
  114. copy_from = self.GetxForm(socket="Copy Skin Weights From")
  115. except GraphError:
  116. copy_from = None
  117. prRed(f"No object found for copying weights in {self}, continuing anyway.")
  118. m = ob.modifiers.new(type="DATA_TRANSFER", name="Mantis_temp_data_transfer")
  119. m.object = None; m.use_vert_data = True
  120. m.data_types_verts = {'VGROUP_WEIGHTS'}
  121. m.vert_mapping = 'POLYINTERP_NEAREST'
  122. m.layers_vgroup_select_src = 'ALL'
  123. m.layers_vgroup_select_dst = 'NAME'
  124. m.object = copy_from
  125. # m.use_object_transform = False # testing reveals that this is undesirable - since the objects may not have their transforms applied.
  126. ob.modifiers.move(len(ob.modifiers)-1, 0)
  127. # ob.data = ob.data.copy()
  128. if False: #MAYBE the mouse needs to be in the 3D viewport, no idea how to set this in an override
  129. # TODO: figure out how to apply this, context is incorrect because armature is still in pose mode
  130. original_active = bpy.context.active_object
  131. original_mode = original_active.mode
  132. bpy.ops.object.mode_set(mode='OBJECT')
  133. with bpy.context.temp_override(**{'active_object':ob, 'selected_objects':[ob, copy_from]}):
  134. # bpy.ops.object.datalayout_transfer(modifier=m.name) # note: this operator is used by the modifier or stand-alone in the UI
  135. # the poll for this operator is defined in blender/source/blender/editors/object/object_data_transfer.cc
  136. # and blender/source/blender/editors/object/object_modifier.cc
  137. # bpy.ops.object.modifier_apply(modifier=m.name, single_user=True)
  138. bpy.ops.object.datalayout_transfer(data_type='VGROUP_WEIGHTS')
  139. bpy.ops.object.data_transfer(data_type='VGROUP_WEIGHTS')
  140. bpy.ops.object.mode_set(mode=original_mode)
  141. def bFinalize(self, bContext=None):
  142. prGreen("Executing Armature Deform Node")
  143. mod_name = self.evaluate_input("Name")
  144. d = self.GetxForm().bGetObject().modifiers.new(mod_name, type='ARMATURE')
  145. if d is None:
  146. raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
  147. self.bObject = d
  148. d.object = self.bGetParentArmature()
  149. props_sockets = {
  150. 'vertex_group' : ("Blend Vertex Group", ""),
  151. 'invert_vertex_group' : ("Invert Vertex Group", ""),
  152. 'use_deform_preserve_volume' : ("Preserve Volume", False),
  153. 'use_multi_modifier' : ("Use Multi Modifier", False),
  154. 'use_bone_envelopes' : ("Use Envelopes", False),
  155. 'use_vertex_groups' : ("Use Vertex Groups", False),
  156. }
  157. evaluate_sockets(self, d, props_sockets)
  158. #
  159. if (skin_method := self.evaluate_input("Skinning Method")) == "AUTOMATIC_HEAT":
  160. # This is bad and leads to somewhat unpredictable
  161. # behaviour, e.g. what object will be selected? What mode?
  162. # also bpy.ops is ugly and prone to error when used in
  163. # scripts. I don't intend to use bpy.ops when I can avoid it.
  164. import bpy
  165. self.initialize_vgroups()
  166. bContext.view_layer.depsgraph.update()
  167. ob = self.GetxForm().bGetObject()
  168. armOb = self.bGetParentArmature()
  169. deform_bones = []
  170. for pb in armOb.pose.bones:
  171. if pb.bone.use_deform == True:
  172. deform_bones.append(pb)
  173. context_override = {
  174. 'active_object':ob,
  175. 'selected_objects':[ob, armOb],
  176. 'active_pose_bone':deform_bones[0],
  177. 'selected_pose_bones':deform_bones,}
  178. #
  179. # with bContext.temp_override(**{'active_object':armOb}):
  180. # bpy.ops.object.mode_set(mode='POSE')
  181. # bpy.ops.pose.select_all(action='SELECT')
  182. for b in armOb.data.bones:
  183. b.select = True
  184. with bContext.temp_override(**context_override):
  185. bpy.ops.paint.weight_paint_toggle()
  186. bpy.ops.paint.weight_from_bones(type='AUTOMATIC')
  187. bpy.ops.paint.weight_paint_toggle()
  188. for b in armOb.data.bones:
  189. b.select = False
  190. #
  191. # with bContext.temp_override(**{'active_object':armOb}):
  192. # bpy.ops.object.mode_set(mode='POSE')
  193. # bpy.ops.pose.select_all(action='DESELECT')
  194. # bpy.ops.object.mode_set(mode='OBJECT')
  195. # TODO: modify Blender to make this available as a Python API function.
  196. elif skin_method == "EXISTING_GROUPS":
  197. pass
  198. elif skin_method == "COPY_FROM_OBJECT":
  199. self.initialize_vgroups()
  200. self.copy_weights()
  201. class DeformerHook(MantisDeformerNode):
  202. '''A node representing a hook deformer'''
  203. def __init__(self, signature, base_tree):
  204. super().__init__(signature, base_tree)
  205. inputs = [
  206. "Hook Target",
  207. "Index",
  208. "Deformer",
  209. ]
  210. outputs = [
  211. "Deformer"
  212. ]
  213. # now set up the traverse target...
  214. self.outputs.init_sockets(outputs)
  215. self.inputs.init_sockets(inputs)
  216. self.init_parameters(additional_parameters={"Name":None})
  217. self.set_traverse([("Deformer", "Deformer")])
  218. self.node_type = "LINK"
  219. self.prepared = True
  220. def GetxForm(self, socket="Deformer"):
  221. if socket == "Deformer":
  222. return GetxForm(self)
  223. else:
  224. trace_xForm_back(self, socket)
  225. def bExecute(self, bContext = None,):
  226. self.executed = True
  227. def bFinalize(self, bContext=None):
  228. from bpy.types import Bone, PoseBone, Object
  229. prGreen(f"Executing Hook Deform Node: {self}")
  230. mod_name = self.evaluate_input("Name")
  231. target_node = self.evaluate_input('Hook Target')
  232. target = target_node.bGetObject(); subtarget = ""
  233. if isinstance(target, Bone) or isinstance(target, PoseBone):
  234. subtarget = target.name; target = target.id_data
  235. ob=self.GetxForm().bGetObject()
  236. reuse = False
  237. for m in ob.modifiers:
  238. if m.type == 'HOOK' and m.object == target and m.subtarget == subtarget:
  239. d = m; reuse = True; break
  240. else:
  241. d = ob.modifiers.new(mod_name, type='HOOK')
  242. if d is None:
  243. raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
  244. self.get_target_and_subtarget(d, input_name="Hook Target")
  245. vertices_used=[]
  246. if reuse: # Get the verts in the list... filter out all the unneeded 0's
  247. vertices_used = list(d.vertex_indices)
  248. include_0 = 0 in vertices_used
  249. vertices_used = list(filter(lambda a : a != 0, vertices_used))
  250. if include_0: vertices_used.append(0)
  251. # now we add the selected vertex to the list, too
  252. vertices_used.append(self.evaluate_input("Index"))
  253. d.vertex_indices_set(vertices_used)
  254. # todo: this should be able to take many indices in the future.
  255. # since this only takes a single index, I can always hack together a shape-key solution to tilt...
  256. # GN solution would work too, provided a separate object is made for it
  257. # I like this, it is perhaps a little innefficient but can be improved later on
  258. class DeformerMorphTarget(MantisDeformerNode):
  259. '''A node representing an armature deformer'''
  260. def __init__(self, signature, base_tree):
  261. super().__init__(signature, base_tree)
  262. inputs = [
  263. "Relative to",
  264. "Object",
  265. "Deformer",
  266. "Vertex Group",
  267. ]
  268. outputs = [
  269. "Deformer",
  270. "Morph Target",
  271. ]
  272. # now set up the traverse target...
  273. self.outputs.init_sockets(outputs)
  274. self.inputs.init_sockets(inputs)
  275. self.init_parameters(additional_parameters={"Name":None})
  276. self.set_traverse([("Deformer", "Deformer")])
  277. self.node_type = "LINK"
  278. self.prepared = True
  279. def GetxForm(self, trace_input="Object"):
  280. trace = trace_single_line(self, trace_input)
  281. for node in trace[0]:
  282. if (node.__class__ in [xFormGeometryObject, InputExistingGeometryObject]):
  283. return node
  284. raise GraphError("%s is not connected to an upstream xForm" % self)
  285. def bExecute(self, bContext = None,):
  286. prGreen("Executing Morph Target Node")
  287. ob = None; relative = None
  288. # do NOT check if the object exists here. Just let the next node deal with that.
  289. try:
  290. ob = self.GetxForm().bGetObject().name
  291. except Exception as e: # this will and should throw an error if it fails
  292. ob = self.GetxForm().evaluate_input("Name")
  293. if self.inputs["Relative to"].is_linked:
  294. try:
  295. relative = self.GetxForm("Relative to").bGetObject().name
  296. except Exception as e: # same here
  297. prRed(f"Execution failed at {self}: no relative object found for morph target, despite link existing.")
  298. raise e
  299. vg = self.evaluate_input("Vertex Group") if self.evaluate_input("Vertex Group") else "" # just make sure it is a string
  300. mt={"object":ob, "vertex_group":vg, "relative_shape":relative}
  301. self.parameters["Morph Target"] = mt
  302. self.parameters["Name"] = ob # this is redundant but it's OK since accessing the mt is tedious
  303. self.executed = True
  304. class DeformerMorphTargetDeform(MantisDeformerNode):
  305. '''A node representing an armature deformer'''
  306. def __init__(self, signature, base_tree):
  307. super().__init__(signature, base_tree)
  308. inputs = [
  309. "Deformer",
  310. "Use Shape Key",
  311. "Use Offset",
  312. ]
  313. outputs = [
  314. "Deformer",
  315. ]
  316. self.outputs.init_sockets(outputs)
  317. self.inputs.init_sockets(inputs)
  318. self.init_parameters(additional_parameters={"Name":None})
  319. self.set_traverse([("Deformer", "Deformer")])
  320. self.node_type = "LINK"
  321. self.prepared = True
  322. self.executed = True
  323. self.bObject = None
  324. setup_custom_props(self)
  325. def GetxForm(self):
  326. return GetxForm(self)
  327. # bpy.data.node_groups["Morph Deform.045"].nodes["Named Attribute.020"].data_type = 'FLOAT_VECTOR'
  328. # bpy.context.object.add_rest_position_attribute = True
  329. def gen_morph_target_modifier(self, context):
  330. # first let's see if this is a no-op
  331. targets = []
  332. for k,v in self.inputs.items():
  333. if "Target" in k:
  334. targets.append(v)
  335. if not targets:
  336. return # nothing to do here.
  337. # at this point we make the node tree
  338. from .geometry_node_graphgen import gen_morph_target_nodes
  339. m, props_sockets = gen_morph_target_nodes(
  340. self.evaluate_input("Name"),
  341. self.GetxForm().bGetObject(),
  342. targets,
  343. context,
  344. use_offset=self.evaluate_input("Use Offset"))
  345. self.bObject = m
  346. evaluate_sockets(self, m, props_sockets)
  347. finish_drivers(self)
  348. def gen_shape_key(self, context): # TODO: make this a feature of the node definition that appears only when there are no prior deformers - and shows a warning!
  349. # TODO: the below works well, but it is quite slow. It does not seem to have better performence. Its only advantage is export to FBX.
  350. # there are a number of things I need to fix here
  351. # - reuse shape keys if possible
  352. # - figure out how to make this a lot faster
  353. # - edit the xForm stuff to delete drivers from shape key ID's, since they belong to the Key, not the Object.
  354. # first check if we need to do anythign
  355. targets = []
  356. for k,v in self.inputs.items():
  357. if "Target" in k:
  358. targets.append(v)
  359. if not targets:
  360. return # nothing to do here
  361. from time import time
  362. start_time = time()
  363. from bpy import data
  364. xf = self.GetxForm()
  365. ob = xf.bGetObject()
  366. dg = context.view_layer.depsgraph
  367. dg.update()
  368. if xf.has_shape_keys == False:
  369. m = data.meshes.new_from_object(ob, preserve_all_data_layers=True, depsgraph=dg)
  370. ob.data = m
  371. ob.add_rest_position_attribute = True
  372. ob.shape_key_clear()
  373. ob.shape_key_add(name='Basis', from_mix=False)
  374. else:
  375. m = ob.data
  376. xf.has_shape_keys = True
  377. # using the built-in shapekey feature is actually a lot harder in terms of programming because I need...
  378. # min/max, as it is just not a feature of the GN version
  379. # to carry info from the morph target node regarding relative shapes and vertex groups and all that
  380. # the drivers may be more difficult to apply, too.
  381. # hafta make new geometry for the object and add shape keys and all that
  382. # the benefit to all this being exporting to game engines via .fbx
  383. # first make a basis shape key
  384. keys={}
  385. props_sockets={}
  386. for i, t in enumerate(targets):
  387. mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
  388. if sk_ob is None:
  389. sk_ob = data.objects.new(mt_node.evaluate_input("Name"), data.meshes.new_from_object(ob))
  390. context.collection.objects.link(sk_ob)
  391. prOrange(f"WARN: no object found for f{mt_node}; creating duplicate of current object ")
  392. sk_ob = dg.id_eval_get(sk_ob)
  393. mt_name = sk_ob.name
  394. vg = mt_node.parameters["Morph Target"]["vertex_group"]
  395. if vg: mt_name = mt_name+"."+vg
  396. sk = ob.shape_key_add(name=mt_name, from_mix=False)
  397. # the shapekey data is absolute point data for each vertex, in order, very simple
  398. # SERIOUSLY IMPORTANT:
  399. # use the current position of the vertex AFTER SHAPE KEYS AND DEFORMERS
  400. # easiest way to do it is to eval the depsgraph
  401. # TODO: try and get it without depsgraph update, since that may be (very) slow
  402. sk_m = sk_ob.data#data.meshes.new_from_object(sk_ob, preserve_all_data_layers=True, depsgraph=dg)
  403. for j in range(len(m.vertices)):
  404. sk.data[j].co = sk_m.vertices[j].co # assume they match
  405. # data.meshes.remove(sk_m)
  406. sk.vertex_group = vg
  407. sk.slider_min = -10
  408. sk.slider_max = 10
  409. keys[mt_name]=sk
  410. props_sockets[mt_name]= ("Value."+str(i).zfill(3), 1.0)
  411. for i, t in enumerate(targets):
  412. mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
  413. if sk_ob is None: continue
  414. if rel := mt_node.parameters["Morph Target"]["relative_shape"]:
  415. sk = keys.get(mt_name)
  416. sk.relative_key = keys.get(rel)
  417. self.bObject = sk.id_data
  418. evaluate_sockets(self, sk.id_data, props_sockets)
  419. finish_drivers(self)
  420. prWhite(f"Initializing morph target took {time() -start_time} seconds")
  421. def bFinalize(self, bContext=None):
  422. prGreen(f"Executing Morph Deform node {self}")
  423. # if there is a not a prior deformer then there should be an option to use plain 'ol shape keys
  424. # GN is always desirable as an option though because it can be baked & many other reasons
  425. use_shape_keys = self.evaluate_input("Use Shape Key")
  426. if use_shape_keys: # check and see if we can.
  427. if self.inputs.get("Deformer"): # I guess this isn't available in some node group contexts... bad. FIXME
  428. if (links := self.inputs["Deformer"].links):
  429. if not links[0].from_node.parameters.get("Use Shape Key"):
  430. use_shape_keys = False
  431. elif links[0].from_node.parameters.get("Use Shape Key") == False:
  432. use_shape_keys = False
  433. self.parameters["Use Shape Key"] = use_shape_keys
  434. if use_shape_keys:
  435. self.gen_shape_key(bContext)
  436. else:
  437. self.gen_morph_target_modifier(bContext)