deformer_containers.py 20 KB

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