deformer_containers.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. from .node_container_common import *
  2. from .xForm_containers import xFormGeometryObject, xFormObjectInstance
  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 .deformer_socket_templates import *
  9. from bpy.types import NodeTree
  10. def TellClasses():
  11. return [
  12. DeformerArmature,
  13. DeformerHook,
  14. DeformerMorphTarget,
  15. DeformerMorphTargetDeform,
  16. DeformerSurfaceDeform,
  17. DeformerMeshDeform,
  18. DeformerLatticeDeform,
  19. ]
  20. # object instance probably can't use the deformer but it doesn't hurt to try.
  21. deformable_types= (xFormGeometryObject, InputExistingGeometryObject, xFormObjectInstance)
  22. def trace_xForm_back(nc, socket):
  23. if (trace := trace_single_line(nc, socket)[0] ) :
  24. for i in range(len(trace)): # have to look in reverse, actually
  25. if ( isinstance(trace[ i ], deformable_types ) ):
  26. return trace[ i ].bGetObject()
  27. raise GraphError(wrapRed(f"No other object found for {nc}."))
  28. class MantisDeformerNode(MantisNode):
  29. def __init__(self, signature : tuple,
  30. base_tree : NodeTree,
  31. socket_templates : list[MantisSocketTemplate]=[]):
  32. super().__init__(signature, base_tree, socket_templates)
  33. self.node_type = 'LINK'
  34. self.prepared = True
  35. self.bObject=[]
  36. # we need evaluate_input to have the same behaviour as links.
  37. def evaluate_input(self, input_name, index=0):
  38. if (input_name in ['Target', 'Object']):
  39. socket = self.inputs.get(input_name)
  40. if socket.is_linked:
  41. return socket.links[0].from_node
  42. return None
  43. else:
  44. return super().evaluate_input(input_name, index)
  45. def GetxForm(nc, output_name="Deformer"):
  46. break_condition= lambda node : node.__class__ in deformable_types
  47. xforms = trace_line_up_branching(nc, output_name, break_condition)
  48. return_me=[]
  49. for xf in xforms:
  50. if xf.node_type != 'XFORM':
  51. continue
  52. if xf in return_me:
  53. continue
  54. return_me.append(xf)
  55. return return_me
  56. def reset_execution(self):
  57. super().reset_execution()
  58. self.bObject=[]; self.prepared=True
  59. class DeformerArmature(MantisDeformerNode):
  60. '''A node representing an armature deformer'''
  61. def __init__(self, signature, base_tree):
  62. super().__init__(signature, base_tree)
  63. inputs = [
  64. "Input Relationship",
  65. "Armature Object",
  66. "Blend Vertex Group",
  67. "Invert Vertex Group",
  68. "Preserve Volume",
  69. "Use Multi Modifier",
  70. "Use Envelopes",
  71. "Use Vertex Groups",
  72. "Skinning Method",
  73. "Deformer",
  74. "Copy Skin Weights From"
  75. ]
  76. outputs = [
  77. "Deformer"
  78. ]
  79. self.outputs.init_sockets(outputs)
  80. self.inputs.init_sockets(inputs)
  81. self.init_parameters(additional_parameters={"Name":None})
  82. self.set_traverse([("Deformer", "Deformer")])
  83. self.node_type = "LINK"
  84. self.prepared = True
  85. def GetxForm(self, socket="Deformer"):
  86. if socket == "Deformer":
  87. return super().GetxForm()
  88. else:
  89. trace_xForm_back(self, socket)
  90. # DUPLICATED FROM xForm_containers::xFormBone
  91. # DEDUP HACK HACK HACK HACK HACK
  92. def bGetParentArmature(self):
  93. from .xForm_containers import xFormArmature
  94. from .misc_nodes import InputExistingGeometryObject
  95. from bpy.types import Object
  96. if (trace := trace_single_line(self, "Armature Object")[0] ) :
  97. for i in range(len(trace)):
  98. # have to look in reverse, actually
  99. if ( isinstance(trace[ i ], xFormArmature ) ):
  100. return trace[ i ].bGetObject()
  101. elif ( isinstance(trace[i], InputExistingGeometryObject)):
  102. if (ob := trace[i].bGetObject()).type == "ARMATURE":
  103. return ob
  104. raise RuntimeError(f"Cannot find armature for node {self}")
  105. return None
  106. #should do the trick...
  107. def bExecute(self, bContext = None,):
  108. self.executed = True
  109. def initialize_vgroups(self, xf):
  110. ob = xf.bGetObject()
  111. armOb = self.bGetParentArmature()
  112. for b in armOb.data.bones:
  113. if b.use_deform == False:
  114. continue
  115. vg = ob.vertex_groups.get(b.name)
  116. if not vg:
  117. vg = ob.vertex_groups.new(name=b.name)
  118. num_verts = len(ob.data.vertices)
  119. vg.add(range(num_verts), 0, 'REPLACE')
  120. def copy_weights(self, xf):
  121. # we'll use modifiers for this, maybe use GN for it in the future tho
  122. import bpy
  123. ob = xf.bGetObject()
  124. try:
  125. copy_from = self.GetxForm(socket="Copy Skin Weights From")
  126. except GraphError:
  127. copy_from = None
  128. prRed(f"No object found for copying weights in {self}, continuing anyway.")
  129. m = ob.modifiers.new(type="DATA_TRANSFER", name="Mantis_temp_data_transfer")
  130. m.object = None; m.use_vert_data = True
  131. m.data_types_verts = {'VGROUP_WEIGHTS'}
  132. m.vert_mapping = 'POLYINTERP_NEAREST'
  133. m.layers_vgroup_select_src = 'ALL'
  134. m.layers_vgroup_select_dst = 'NAME'
  135. m.object = copy_from
  136. # m.use_object_transform = False # testing reveals that this is undesirable - since the objects may not have their transforms applied.
  137. ob.modifiers.move(len(ob.modifiers)-1, 0)
  138. # ob.data = ob.data.copy()
  139. if False: #MAYBE the mouse needs to be in the 3D viewport, no idea how to set this in an override
  140. # TODO: figure out how to apply this, context is incorrect because armature is still in pose mode
  141. original_active = bpy.context.active_object
  142. original_mode = original_active.mode
  143. bpy.ops.object.mode_set(mode='OBJECT')
  144. with bpy.context.temp_override(**{'active_object':ob, 'selected_objects':[ob, copy_from]}):
  145. # bpy.ops.object.datalayout_transfer(modifier=m.name) # note: this operator is used by the modifier or stand-alone in the UI
  146. # the poll for this operator is defined in blender/source/blender/editors/object/object_data_transfer.cc
  147. # and blender/source/blender/editors/object/object_modifier.cc
  148. # bpy.ops.object.modifier_apply(modifier=m.name, single_user=True)
  149. bpy.ops.object.datalayout_transfer(data_type='VGROUP_WEIGHTS')
  150. bpy.ops.object.data_transfer(data_type='VGROUP_WEIGHTS')
  151. bpy.ops.object.mode_set(mode=original_mode)
  152. def bFinalize(self, bContext=None):
  153. prGreen("Executing Armature Deform Node")
  154. mod_name = self.evaluate_input("Name")
  155. for xf in self.GetxForm():
  156. ob = xf.bGetObject()
  157. d = ob.modifiers.new(mod_name, type='ARMATURE')
  158. if d is None:
  159. raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
  160. self.bObject.append(d)
  161. d.object = self.bGetParentArmature()
  162. props_sockets = {
  163. 'vertex_group' : ("Blend Vertex Group", ""),
  164. 'invert_vertex_group' : ("Invert Vertex Group", ""),
  165. 'use_deform_preserve_volume' : ("Preserve Volume", False),
  166. 'use_multi_modifier' : ("Use Multi Modifier", False),
  167. 'use_bone_envelopes' : ("Use Envelopes", False),
  168. 'use_vertex_groups' : ("Use Vertex Groups", False),
  169. }
  170. evaluate_sockets(self, d, props_sockets)
  171. #
  172. if (skin_method := self.evaluate_input("Skinning Method")) == "AUTOMATIC_HEAT":
  173. # This is bad and leads to somewhat unpredictable
  174. # behaviour, e.g. what object will be selected? What mode?
  175. # also bpy.ops is ugly and prone to error when used in
  176. # scripts. I don't intend to use bpy.ops when I can avoid it.
  177. import bpy
  178. self.initialize_vgroups(xf)
  179. bContext.view_layer.depsgraph.update()
  180. armOb = self.bGetParentArmature()
  181. deform_bones = []
  182. for pb in armOb.pose.bones:
  183. if pb.bone.use_deform == True:
  184. deform_bones.append(pb)
  185. context_override = {
  186. 'active_object':ob,
  187. 'selected_objects':[ob, armOb],
  188. 'active_pose_bone':deform_bones[0],
  189. 'selected_pose_bones':deform_bones,}
  190. for b in armOb.data.bones:
  191. b.select = True
  192. with bContext.temp_override(**context_override):
  193. bpy.ops.paint.weight_paint_toggle()
  194. bpy.ops.paint.weight_from_bones(type='AUTOMATIC')
  195. bpy.ops.paint.weight_paint_toggle()
  196. for b in armOb.data.bones:
  197. b.select = False
  198. # TODO: modify Blender to make this available as a Python API function.
  199. elif skin_method == "COPY_FROM_OBJECT":
  200. self.initialize_vgroups(xf)
  201. self.copy_weights(xf)
  202. # elif skin_method == "EXISTING_GROUPS":
  203. # pass
  204. class DeformerHook(MantisDeformerNode):
  205. '''A node representing a hook deformer'''
  206. def __init__(self, signature, base_tree):
  207. super().__init__(signature, base_tree, HookSockets)
  208. # now set up the traverse target...
  209. self.init_parameters(additional_parameters={"Name":None})
  210. self.set_traverse([("Deformer", "Deformer")])
  211. self.prepared = True
  212. def driver_for_radius(self, object, hook, index, influence, bezier=True):
  213. """ Creates a driver to control the radius of a curve point with the hook."""
  214. from bpy.types import Bone, PoseBone
  215. var_template = {"owner":hook,
  216. "name":"a",
  217. "type":"TRANSFORMS",
  218. "space":'WORLD_SPACE',
  219. "channel":'SCALE_X',}
  220. var1_template = {"owner":hook.id_data,
  221. "name":"b",
  222. "type":"TRANSFORMS",
  223. "space":'WORLD_SPACE',
  224. "channel":'SCALE_X',}
  225. keys_template = [{"co":(0,0),
  226. "interpolation": "LINEAR",
  227. "type":"KEYFRAME",},
  228. {"co":(1,influence),
  229. "interpolation": "LINEAR",
  230. "type":"KEYFRAME",},]
  231. if bezier:
  232. owner=object.data.splines[0].bezier_points
  233. else:
  234. owner=object.data.splines[0].points
  235. driver = {
  236. "owner":owner[index],
  237. "prop":"radius",
  238. "ind":-1,
  239. "extrapolation":"LINEAR",
  240. "type":"AVERAGE",
  241. "vars":[],
  242. "keys":keys_template,
  243. }
  244. if isinstance(hook, (Bone, PoseBone)):
  245. driver['type']='SCRIPTED'
  246. driver['expression']="(((1/b)*a)+((1/b_001)*a_001)+((1/b_002)*a_002))/3"
  247. from .drivers import CreateDrivers
  248. axes='XYZ'
  249. for i in range(3):
  250. var = var_template.copy()
  251. var["channel"]="SCALE_"+axes[i]
  252. driver["vars"].append(var)
  253. if isinstance(hook, (Bone, PoseBone)):
  254. var1=var1_template.copy()
  255. var1['channel']="SCALE_"+axes[i]
  256. driver['vars'].append(var1)
  257. CreateDrivers([driver])
  258. def bExecute(self, bContext = None,):
  259. self.executed = True
  260. def bFinalize(self, bContext=None):
  261. from bpy.types import Bone, PoseBone, Object
  262. prGreen(f"Executing Hook Deform Node: {self}")
  263. mod_name = self.evaluate_input("Name")
  264. affect_radius = self.evaluate_input("Affect Curve Radius")
  265. auto_bezier = self.evaluate_input("Auto-Bezier")
  266. target_node = self.evaluate_input('Hook Target')
  267. target = target_node.bGetObject(); subtarget = ""
  268. props_sockets = self.gen_property_socket_map()
  269. if isinstance(target, Bone) or isinstance(target, PoseBone):
  270. subtarget = target.name; target = target.id_data
  271. for xf in self.GetxForm():
  272. ob=xf.bGetObject()
  273. if ob.type == 'CURVE':
  274. spline_index = self.evaluate_input("Spline Index")
  275. from .utilities import get_extracted_spline_object
  276. ob = get_extracted_spline_object(ob, spline_index, self.mContext)
  277. reuse = False
  278. for m in ob.modifiers:
  279. if m.type == 'HOOK' and m.object == target and m.subtarget == subtarget:
  280. if self.evaluate_input("Influence") != m.strength:
  281. continue # make a new modifier so they can have different strengths
  282. if ob.animation_data: # this can be None
  283. drivers = ob.animation_data.drivers
  284. for k in props_sockets.keys():
  285. if driver := drivers.find(k):
  286. # TODO: I should check to see if the drivers are the same...
  287. break # continue searching for an equivalent modifier
  288. else: # There was no driver - use this one.
  289. d = m; reuse = True; break
  290. else: # use this one, there can't be drivers without animation_data.
  291. d = m; reuse = True; break
  292. else:
  293. d = ob.modifiers.new(mod_name, type='HOOK')
  294. if d is None:
  295. raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
  296. self.bObject.append(d)
  297. self.get_target_and_subtarget(d, input_name="Hook Target")
  298. vertices_used=[]
  299. if reuse: # Get the verts in the list... filter out all the unneeded 0's
  300. vertices_used = list(d.vertex_indices)
  301. include_0 = 0 in vertices_used
  302. vertices_used = list(filter(lambda a : a != 0, vertices_used))
  303. if include_0: vertices_used.append(0)
  304. # now we add the selected vertex to the list, too
  305. vertex = self.evaluate_input("Point Index")
  306. if ob.type == 'CURVE' and ob.data.splines[0].type == 'BEZIER' and auto_bezier:
  307. if affect_radius:
  308. self.driver_for_radius(ob, target_node.bGetObject(), vertex, d.strength)
  309. vertex*=3
  310. vertices_used.extend([vertex, vertex+1, vertex+2])
  311. else:
  312. vertices_used.append(vertex)
  313. # if we have a curve and it is NOT using auto-bezier for the verts..
  314. if ob.type == 'CURVE' and ob.data.splines[0].type == 'BEZIER' and affect_radius and not auto_bezier:
  315. print (f"WARN: {self}: \"Affect Radius\" may not behave as expected"
  316. " when used on Bezier curves without Auto-Bezier")
  317. #bezier point starts at 1, and then every third vert, so 4, 7, 10...
  318. if vertex%3==1:
  319. self.driver_for_radius(ob, target_node.bGetObject(), vertex, d.strength)
  320. if ob.type == 'CURVE' and ob.data.splines[0].type != 'BEZIER' and \
  321. affect_radius:
  322. self.driver_for_radius(ob, target_node.bGetObject(), vertex, d.strength, bezier=False)
  323. d.vertex_indices_set(vertices_used)
  324. evaluate_sockets(self, d, props_sockets)
  325. finish_drivers(self)
  326. # todo: this node should be able to take many indices in the future.
  327. # Also: I have a Geometry Nodes implementation of this I can use... maybe...
  328. class DeformerMorphTarget(MantisDeformerNode):
  329. '''A node representing an armature deformer'''
  330. def __init__(self, signature, base_tree):
  331. super().__init__(signature, base_tree)
  332. inputs = [
  333. "Relative to",
  334. "Object",
  335. "Deformer",
  336. "Vertex Group",
  337. ]
  338. outputs = [
  339. "Deformer",
  340. "Morph Target",
  341. ]
  342. # now set up the traverse target...
  343. self.outputs.init_sockets(outputs)
  344. self.inputs.init_sockets(inputs)
  345. self.init_parameters(additional_parameters={"Name":None})
  346. self.set_traverse([("Deformer", "Deformer")])
  347. self.node_type = "LINK"
  348. self.prepared = True
  349. def GetxForm(self, trace_input="Object"):
  350. trace = trace_single_line(self, trace_input)
  351. for node in trace[0]:
  352. if (isinstance(node, deformable_types)):
  353. return node
  354. raise GraphError("%s is not connected to an upstream xForm" % self)
  355. def bExecute(self, bContext = None,):
  356. prGreen("Executing Morph Target Node")
  357. ob = None; relative = None
  358. # do NOT check if the object exists here. Just let the next node deal with that.
  359. try:
  360. ob = self.GetxForm().bGetObject().name
  361. except Exception as e: # this will and should throw an error if it fails
  362. ob = self.GetxForm().evaluate_input("Name")
  363. if self.inputs["Relative to"].is_linked:
  364. try:
  365. relative = self.GetxForm("Relative to").bGetObject().name
  366. except Exception as e: # same here
  367. prRed(f"Execution failed at {self}: no relative object found for morph target, despite link existing.")
  368. raise e
  369. vg = self.evaluate_input("Vertex Group") if self.evaluate_input("Vertex Group") else "" # just make sure it is a string
  370. mt={"object":ob, "vertex_group":vg, "relative_shape":relative}
  371. self.parameters["Morph Target"] = mt
  372. self.parameters["Name"] = ob # this is redundant but it's OK since accessing the mt is tedious
  373. self.executed = True
  374. class DeformerMorphTargetDeform(MantisDeformerNode):
  375. '''A node representing an armature deformer'''
  376. def __init__(self, signature, base_tree):
  377. super().__init__(signature, base_tree)
  378. inputs = [
  379. "Deformer",
  380. "Use Shape Key",
  381. "Use Offset",
  382. ]
  383. outputs = [
  384. "Deformer",
  385. ]
  386. self.outputs.init_sockets(outputs)
  387. self.inputs.init_sockets(inputs)
  388. self.init_parameters(additional_parameters={"Name":None})
  389. self.set_traverse([("Deformer", "Deformer")])
  390. self.node_type = "LINK"
  391. self.prepared = True
  392. self.executed = True
  393. setup_custom_props(self)
  394. # bpy.data.node_groups["Morph Deform.045"].nodes["Named Attribute.020"].data_type = 'FLOAT_VECTOR'
  395. # bpy.context.object.add_rest_position_attribute = True
  396. def reset_execution(self):
  397. return super().reset_execution()
  398. self.executed=True
  399. def gen_morph_target_modifier(self, xf, context):
  400. # first let's see if this is a no-op
  401. targets = []
  402. for k,v in self.inputs.items():
  403. if "Target" in k:
  404. targets.append(v)
  405. if not targets:
  406. return # nothing to do here.
  407. # at this point we make the node tree
  408. from .geometry_node_graphgen import gen_morph_target_nodes
  409. m, props_sockets = gen_morph_target_nodes(
  410. self.evaluate_input("Name"),
  411. xf.bGetObject(),
  412. targets,
  413. context,
  414. use_offset=self.evaluate_input("Use Offset"))
  415. self.bObject.append(m)
  416. evaluate_sockets(self, m, props_sockets)
  417. finish_drivers(self)
  418. def gen_shape_key(self, xf, context): # TODO: make this a feature of the node definition that appears only when there are no prior deformers - and shows a warning!
  419. # 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.
  420. # there are a number of things I need to fix here
  421. # - reuse shape keys if possible
  422. # - figure out how to make this a lot faster
  423. # - edit the xForm stuff to delete drivers from shape key ID's, since they belong to the Key, not the Object.
  424. # first check if we need to do anythign
  425. targets = []
  426. for k,v in self.inputs.items():
  427. if "Target" in k:
  428. targets.append(v)
  429. if not targets:
  430. return # nothing to do here
  431. from time import time
  432. start_time = time()
  433. from bpy import data
  434. ob = xf.bGetObject()
  435. dg = context.view_layer.depsgraph
  436. dg.update()
  437. if xf.has_shape_keys == False:
  438. m = data.meshes.new_from_object(ob, preserve_all_data_layers=True, depsgraph=dg)
  439. ob.data = m
  440. ob.add_rest_position_attribute = True
  441. ob.shape_key_clear()
  442. ob.shape_key_add(name='Basis', from_mix=False)
  443. else:
  444. m = ob.data
  445. xf.has_shape_keys = True
  446. # using the built-in shapekey feature is actually a lot harder in terms of programming because I need...
  447. # min/max, as it is just not a feature of the GN version
  448. # to carry info from the morph target node regarding relative shapes and vertex groups and all that
  449. # the drivers may be more difficult to apply, too.
  450. # hafta make new geometry for the object and add shape keys and all that
  451. # the benefit to all this being exporting to game engines via .fbx
  452. # first make a basis shape key
  453. keys={}
  454. props_sockets={}
  455. for i, t in enumerate(targets):
  456. mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
  457. if sk_ob is None:
  458. sk_ob = data.objects.new(mt_node.evaluate_input("Name"), data.meshes.new_from_object(ob))
  459. context.collection.objects.link(sk_ob)
  460. prOrange(f"WARN: no object found for f{mt_node}; creating duplicate of current object ")
  461. sk_ob = dg.id_eval_get(sk_ob)
  462. mt_name = sk_ob.name
  463. vg = mt_node.parameters["Morph Target"]["vertex_group"]
  464. if vg: mt_name = mt_name+"."+vg
  465. sk = ob.shape_key_add(name=mt_name, from_mix=False)
  466. # the shapekey data is absolute point data for each vertex, in order, very simple
  467. # SERIOUSLY IMPORTANT:
  468. # use the current position of the vertex AFTER SHAPE KEYS AND DEFORMERS
  469. # easiest way to do it is to eval the depsgraph
  470. # TODO: try and get it without depsgraph update, since that may be (very) slow
  471. sk_m = sk_ob.data#data.meshes.new_from_object(sk_ob, preserve_all_data_layers=True, depsgraph=dg)
  472. for j in range(len(m.vertices)):
  473. sk.data[j].co = sk_m.vertices[j].co # assume they match
  474. # data.meshes.remove(sk_m)
  475. sk.vertex_group = vg
  476. sk.slider_min = -10
  477. sk.slider_max = 10
  478. keys[mt_name]=sk
  479. props_sockets[mt_name]= ("Value."+str(i).zfill(3), 1.0)
  480. for i, t in enumerate(targets):
  481. mt_node = t.links[0].from_node; sk_ob = mt_node.GetxForm().bGetObject()
  482. if sk_ob is None: continue
  483. if rel := mt_node.parameters["Morph Target"]["relative_shape"]:
  484. sk = keys.get(mt_name)
  485. sk.relative_key = keys.get(rel)
  486. self.bObject.append(sk.id_data)
  487. evaluate_sockets(self, sk.id_data, props_sockets)
  488. finish_drivers(self)
  489. prWhite(f"Initializing morph target took {time() -start_time} seconds")
  490. def bFinalize(self, bContext=None):
  491. prGreen(f"Executing Morph Deform node {self}")
  492. use_shape_keys = self.evaluate_input("Use Shape Key")
  493. # if there is a not a prior deformer then there should be an option to use plain 'ol shape keys
  494. # GN is always desirable as an option though because it can be baked & many other reasons
  495. if use_shape_keys: # check and see if we can.
  496. if self.inputs.get("Deformer"): # I guess this isn't available in some node group contexts... bad. FIXME
  497. if (links := self.inputs["Deformer"].links):
  498. if not links[0].from_node.parameters.get("Use Shape Key"):
  499. use_shape_keys = False
  500. elif links[0].from_node.parameters.get("Use Shape Key") == False:
  501. use_shape_keys = False
  502. self.parameters["Use Shape Key"] = use_shape_keys
  503. for xf in self.GetxForm():
  504. if use_shape_keys:
  505. self.gen_shape_key(xf, bContext)
  506. else:
  507. self.gen_morph_target_modifier(xf, bContext)
  508. class DeformerSurfaceDeform(MantisDeformerNode):
  509. '''A node representing an surface deform modifier'''
  510. def __init__(self, signature, base_tree):
  511. super().__init__(signature, base_tree, SurfaceDeformSockets)
  512. # now set up the traverse target...
  513. self.init_parameters(additional_parameters={"Name":None})
  514. self.set_traverse([("Deformer", "Deformer")])
  515. self.prepared = True
  516. def GetxForm(self, socket="Deformer"):
  517. if socket == "Deformer":
  518. return super().GetxForm()
  519. else:
  520. trace_xForm_back(self, socket)
  521. def bExecute(self, bContext = None,):
  522. self.executed = True
  523. def bFinalize(self, bContext=None):
  524. prGreen("Executing Surface Deform Node")
  525. mod_name = self.evaluate_input("Name")
  526. for xf in self.GetxForm():
  527. ob = xf.bGetObject()
  528. d = ob.modifiers.new(mod_name, type='SURFACE_DEFORM')
  529. if d is None:
  530. raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
  531. self.bObject.append(d)
  532. self.get_target_and_subtarget(d, input_name="Target")
  533. props_sockets = self.gen_property_socket_map()
  534. evaluate_sockets(self, d, props_sockets)
  535. # now we have to bind it
  536. import bpy
  537. target = d.target
  538. with bpy.context.temp_override(**{'active_object':ob, 'selected_objects':[ob, target]}):
  539. bpy.ops.object.surfacedeform_bind(modifier=d.name)
  540. class DeformerMeshDeform(MantisDeformerNode):
  541. '''A node representing a mesh deform modifier'''
  542. def __init__(self, signature, base_tree):
  543. super().__init__(signature, base_tree, MeshDeformSockets)
  544. # now set up the traverse target...
  545. self.init_parameters(additional_parameters={"Name":None})
  546. self.set_traverse([("Deformer", "Deformer")])
  547. self.prepared = True
  548. def GetxForm(self, socket="Deformer"):
  549. if socket == "Deformer":
  550. return super().GetxForm()
  551. else:
  552. trace_xForm_back(self, socket)
  553. def bExecute(self, bContext = None,):
  554. self.executed = True
  555. def bFinalize(self, bContext=None):
  556. prGreen("Executing Mesh Deform Node")
  557. mod_name = self.evaluate_input("Name")
  558. for xf in self.GetxForm():
  559. ob = xf.bGetObject()
  560. d = ob.modifiers.new(mod_name, type='MESH_DEFORM')
  561. if d is None:
  562. raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
  563. self.bObject.append(d)
  564. self.get_target_and_subtarget(d, input_name="Object")
  565. props_sockets = self.gen_property_socket_map()
  566. evaluate_sockets(self, d, props_sockets)
  567. # now we have to bind it
  568. import bpy
  569. target = d.object
  570. with bpy.context.temp_override(**{'active_object':ob, 'selected_objects':[ob, target]}):
  571. bpy.ops.object.meshdeform_bind(modifier=d.name)
  572. # todo: add influence parameter and set it up with vertex group and geometry nodes
  573. # todo: make cage object display as wireframe if it is not being used for something else
  574. # or add the option in the Geometry Object node
  575. class DeformerLatticeDeform(MantisDeformerNode):
  576. '''A node representing a lattice deform modifier'''
  577. def __init__(self, signature, base_tree):
  578. super().__init__(signature, base_tree, LatticeDeformSockets)
  579. # now set up the traverse target...
  580. self.init_parameters(additional_parameters={"Name":None})
  581. self.set_traverse([("Deformer", "Deformer")])
  582. self.prepared = True
  583. def GetxForm(self, socket="Deformer"):
  584. if socket == "Deformer":
  585. return super().GetxForm()
  586. else:
  587. trace_xForm_back(self, socket)
  588. def bExecute(self, bContext = None,):
  589. self.executed = True
  590. def bFinalize(self, bContext=None):
  591. prGreen("Executing Mesh Deform Node")
  592. mod_name = self.evaluate_input("Name")
  593. for xf in self.GetxForm():
  594. ob = xf.bGetObject()
  595. d = ob.modifiers.new(mod_name, type='LATTICE')
  596. if d is None:
  597. raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
  598. self.bObject.append(d)
  599. self.get_target_and_subtarget(d, input_name="Object")
  600. props_sockets = self.gen_property_socket_map()
  601. evaluate_sockets(self, d, props_sockets)