deformer_containers.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. from .node_container_common import *
  2. from .xForm_containers import xFormGeometryObject
  3. from .misc_containers import InputExistingGeometryObject
  4. from bpy.types import Node
  5. from .base_definitions import MantisNode
  6. from .utilities import (prRed, prGreen, prPurple, prWhite, prOrange,
  7. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  8. wrapOrange,)
  9. def TellClasses():
  10. return [
  11. DeformerArmature,
  12. DeformerMorphTarget,
  13. DeformerMorphTargetDeform,
  14. ]
  15. def default_evaluate_input(nc, input_name):
  16. # duped from link_containers... should be common?
  17. # should catch 'Target', 'Pole Target' and ArmatureConstraint targets, too
  18. if ('Target' in input_name) and input_name != "Target Space":
  19. socket = nc.inputs.get(input_name)
  20. if socket.is_linked:
  21. return socket.links[0].from_node
  22. return None
  23. else:
  24. return evaluate_input(nc, input_name)
  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 DeformerArmature:
  33. '''A node representing an armature deformer'''
  34. def __init__(self, signature, base_tree):
  35. self.base_tree=base_tree
  36. self.signature = signature
  37. self.inputs = {
  38. "Input Relationship" : NodeSocket(is_input = True, name = "Input Relationship", node = self,),
  39. "Armature Object" : NodeSocket(is_input = True, name = "Armature Object", node = self,),
  40. "Blend Vertex Group" : NodeSocket(is_input = True, name = "Blend Vertex Group", node = self),
  41. "Invert Vertex Group" : NodeSocket(is_input = True, name = "Invert Vertex Group", node = self),
  42. "Preserve Volume" : NodeSocket(is_input = True, name = "Preserve Volume", node = self),
  43. "Use Multi Modifier" : NodeSocket(is_input = True, name = "Use Multi Modifier", node = self),
  44. "Use Envelopes" : NodeSocket(is_input = True, name = "Use Envelopes", node = self),
  45. "Use Vertex Groups" : NodeSocket(is_input = True, name = "Use Vertex Groups", node = self),
  46. "Skinning Method" : NodeSocket(is_input = True, name = "Skinning Method", node = self),
  47. "Deformer" : NodeSocket(is_input = True, name = "Deformer", node = self),
  48. "Copy Skin Weights From" : NodeSocket(is_input = True, name = "Copy Skin Weights From", node = self),
  49. }
  50. self.outputs = {
  51. "Deformer" : NodeSocket(is_input = False, name = "Deformer", node=self), }
  52. self.parameters = {
  53. "Name" : None,
  54. "Armature Object" : None,
  55. "Blend Vertex Group" : None,
  56. "Invert Vertex Group" : None,
  57. "Preserve Volume" : None,
  58. "Use Multi Modifier" : None,
  59. "Use Envelopes" : None,
  60. "Use Vertex Groups" : None,
  61. "Skinning Method" : None,
  62. "Deformer" : None,
  63. "Copy Skin Weights From" : None,
  64. }
  65. # now set up the traverse target...
  66. self.inputs["Deformer"].set_traverse_target(self.outputs["Deformer"])
  67. self.outputs["Deformer"].set_traverse_target(self.inputs["Deformer"])
  68. self.node_type = "LINK"
  69. self.hierarchy_connections, self.connections = [], []
  70. self.hierarchy_dependencies, self.dependencies = [], []
  71. self.prepared = True
  72. self.executed = False
  73. def evaluate_input(self, input_name):
  74. return default_evaluate_input(self, input_name)
  75. def GetxForm(self, socket="Deformer"):
  76. if socket == "Deformer":
  77. return GetxForm(self)
  78. else:
  79. from .xForm_containers import xFormGeometryObject
  80. from .misc_containers import InputExistingGeometryObject
  81. from bpy.types import Object
  82. if (trace := trace_single_line(self, socket)[0] ) :
  83. for i in range(len(trace)): # have to look in reverse, actually
  84. if ( isinstance(trace[ i ], xFormGeometryObject ) ) or ( isinstance(trace[ i ], InputExistingGeometryObject ) ):
  85. return trace[ i ].bGetObject()
  86. raise GraphError(wrapRed(f"No other object found for {self}."))
  87. # DUPLICATED FROM xForm_containers::xFormBone
  88. # DEDUP HACK HACK HACK HACK HACK
  89. def bGetParentArmature(self):
  90. from .xForm_containers import xFormArmature
  91. from .misc_containers import InputExistingGeometryObject
  92. from bpy.types import Object
  93. if (trace := trace_single_line(self, "Armature Object")[0] ) :
  94. for i in range(len(trace)):
  95. # have to look in reverse, actually
  96. if ( isinstance(trace[ i ], xFormArmature ) ):
  97. return trace[ i ].bGetObject()
  98. elif ( isinstance(trace[i], InputExistingGeometryObject)):
  99. if (ob := trace[i].bGetObject()).type == "ARMATURE":
  100. return ob
  101. raise RuntimeError(f"Cannot find armature for node {self}")
  102. return None
  103. #should do the trick...
  104. def bExecute(self, bContext = None,):
  105. self.executed = True
  106. def initialize_vgroups(self,):
  107. ob = self.GetxForm().bGetObject()
  108. armOb = self.bGetParentArmature()
  109. for b in armOb.data.bones:
  110. if b.use_deform == False:
  111. continue
  112. vg = ob.vertex_groups.get(b.name)
  113. if not vg:
  114. vg = ob.vertex_groups.new(name=b.name)
  115. num_verts = len(ob.data.vertices)
  116. vg.add(range(num_verts), 0, 'REPLACE')
  117. def copy_weights(self):
  118. # we'll use modifiers for this, maybe use GN for it in the future tho
  119. import bpy
  120. ob = self.GetxForm().bGetObject()
  121. copy_from = self.GetxForm(socket="Copy Skin Weights From")
  122. m = ob.modifiers.new(type="DATA_TRANSFER", name="Mantis_temp_data_transfer")
  123. m.object = None; m.use_vert_data = True
  124. m.data_types_verts = {'VGROUP_WEIGHTS'}
  125. m.vert_mapping = 'POLYINTERP_NEAREST'
  126. m.layers_vgroup_select_src = 'ALL'
  127. m.layers_vgroup_select_dst = 'NAME'
  128. m.object = copy_from
  129. ob.modifiers.move(len(ob.modifiers)-1, 0)
  130. # ob.data = ob.data.copy()
  131. if False: #MAYBE the mouse needs to be in the 3D viewport, no idea how to set this in an override
  132. # TODO: figure out how to apply this, context is incorrect because armature is still in pose mode
  133. original_active = bpy.context.active_object
  134. original_mode = original_active.mode
  135. bpy.ops.object.mode_set(mode='OBJECT')
  136. with bpy.context.temp_override(**{'active_object':ob, 'selected_objects':[ob, copy_from]}):
  137. # bpy.ops.object.datalayout_transfer(modifier=m.name) # note: this operator is used by the modifier or stand-alone in the UI
  138. # the poll for this operator is defined in blender/source/blender/editors/object/object_data_transfer.cc
  139. # and blender/source/blender/editors/object/object_modifier.cc
  140. # bpy.ops.object.modifier_apply(modifier=m.name, single_user=True)
  141. bpy.ops.object.datalayout_transfer(data_type='VGROUP_WEIGHTS')
  142. bpy.ops.object.data_transfer(data_type='VGROUP_WEIGHTS')
  143. bpy.ops.object.mode_set(mode=original_mode)
  144. def bFinalize(self, bContext=None):
  145. prGreen("Executing Armature Deform Node")
  146. mod_name = self.evaluate_input("Name")
  147. try:
  148. d = self.GetxForm().bGetObject().modifiers[mod_name]
  149. except KeyError:
  150. d = self.GetxForm().bGetObject().modifiers.new(mod_name, type='ARMATURE')
  151. if d is None:
  152. raise RuntimeError(f"Modifier was not created in node {self} -- the object is invalid.")
  153. self.bObject = d
  154. d.object = self.bGetParentArmature()
  155. props_sockets = {
  156. 'vertex_group' : ("Blend Vertex Group", ""),
  157. 'invert_vertex_group' : ("Invert Vertex Group", ""),
  158. 'use_deform_preserve_volume' : ("Preserve Volume", False),
  159. 'use_multi_modifier' : ("Use Multi Modifier", False),
  160. 'use_bone_envelopes' : ("Use Envelopes", False),
  161. 'use_vertex_groups' : ("Use Vertex Groups", False),
  162. }
  163. evaluate_sockets(self, d, props_sockets)
  164. #
  165. if (skin_method := self.evaluate_input("Skinning Method")) == "AUTOMATIC_HEAT":
  166. # This is reatarded and leads to somewhat unpredictable
  167. # behaviour, e.g. what object will be selected? What mode?
  168. # also bpy.ops is ugly and prone to error when used in
  169. # scripts. I don't intend to use bpy.ops when I can avoid it.
  170. import bpy
  171. self.initialize_vgroups()
  172. bContext.view_layer.depsgraph.update()
  173. ob = self.GetxForm().bGetObject()
  174. armOb = self.bGetParentArmature()
  175. deform_bones = []
  176. for pb in armOb.pose.bones:
  177. if pb.bone.use_deform == True:
  178. deform_bones.append(pb)
  179. context_override = {
  180. 'active_object':ob,
  181. 'selected_objects':[ob, armOb],
  182. 'active_pose_bone':deform_bones[0],
  183. 'selected_pose_bones':deform_bones,}
  184. #
  185. with bContext.temp_override(**{'active_object':armOb}):
  186. bpy.ops.object.mode_set(mode='POSE')
  187. bpy.ops.pose.select_all(action='SELECT')
  188. with bContext.temp_override(**context_override):
  189. bpy.ops.paint.weight_paint_toggle()
  190. bpy.ops.paint.weight_from_bones(type='AUTOMATIC')
  191. bpy.ops.paint.weight_paint_toggle()
  192. #
  193. with bContext.temp_override(**{'active_object':armOb}):
  194. bpy.ops.object.mode_set(mode='POSE')
  195. bpy.ops.pose.select_all(action='DESELECT')
  196. bpy.ops.object.mode_set(mode='OBJECT')
  197. # TODO: modify Blender to make this available as a Python API function.
  198. elif skin_method == "EXISTING_GROUPS":
  199. pass
  200. elif skin_method == "COPY_FROM_OBJECT":
  201. self.copy_weights()
  202. # self.initialize_vgroups(use_existing = True)
  203. class DeformerMorphTarget:
  204. '''A node representing an armature deformer'''
  205. def __init__(self, signature, base_tree):
  206. self.base_tree=base_tree
  207. self.signature = signature
  208. self.inputs = {
  209. "Relative to" : NodeSocket(is_input = True, name = "Relative To", node = self,),
  210. "Object" : NodeSocket(is_input = True, name = "Object", node = self,),
  211. "Deformer" : NodeSocket(is_input = True, name = "Deformer", node = self),
  212. "Vertex Group" : NodeSocket(is_input = True, name = "Vertex Group", node = self),
  213. }
  214. self.outputs = {
  215. "Deformer" : NodeSocket(is_input = False, name = "Deformer", node=self),
  216. "Morph Target" : NodeSocket(is_input = False, name = "Morph Target", node=self), }
  217. self.parameters = {
  218. "Name" : None,
  219. "Relative to" : None,
  220. "Object" : None,
  221. "Morph Target" : None,
  222. "Deformer" : None,
  223. "Vertex Group" : None,
  224. }
  225. # now set up the traverse target...
  226. self.inputs["Deformer"].set_traverse_target(self.outputs["Deformer"])
  227. self.outputs["Deformer"].set_traverse_target(self.inputs["Deformer"])
  228. self.node_type = "LINK"
  229. self.hierarchy_connections, self.connections = [], []
  230. self.hierarchy_dependencies, self.dependencies = [], []
  231. self.prepared = True
  232. self.executed = False
  233. def GetxForm(self, trace_input="Object"):
  234. trace = trace_single_line(self, trace_input)
  235. for node in trace[0]:
  236. if (node.__class__ in [xFormGeometryObject, InputExistingGeometryObject]):
  237. return node
  238. raise GraphError("%s is not connected to an upstream xForm" % self)
  239. def bExecute(self, bContext = None,):
  240. prGreen("Executing Morph Target Node")
  241. name = ''
  242. ob = None; relative = None
  243. try:
  244. ob = self.GetxForm().bGetObject().name
  245. except Exception as e: # this will and should throw an error if it fails
  246. prRed(f"Execution failed at {self}: no object found for morph target.")
  247. raise e
  248. if self.inputs["Relative to"].is_linked:
  249. try:
  250. relative = self.GetxForm("Relative to").bGetObject().name
  251. except Exception as e: # same here
  252. prRed(f"Execution failed at {self}: no relative object found for morph target, despite link existing.")
  253. raise e
  254. vg = self.evaluate_input("Vertex Group") if self.evaluate_input("Vertex Group") else "" # just make sure it is a string
  255. mt={"object":ob, "vertex_group":vg, "relative_shape":relative}
  256. self.parameters["Morph Target"] = mt
  257. self.executed = True
  258. class DeformerMorphTargetDeform:
  259. '''A node representing an armature deformer'''
  260. def __init__(self, signature, base_tree):
  261. self.base_tree=base_tree
  262. self.signature = signature
  263. self.inputs = {
  264. "Deformer" : NodeSocket(is_input = True, name = "Deformer", node = self),
  265. }
  266. self.outputs = {
  267. "Deformer" : NodeSocket(is_input = False, name = "Deformer", node=self), }
  268. self.parameters = {
  269. "Name" : None,
  270. "Deformer" : None,}
  271. # now set up the traverse target...
  272. self.inputs["Deformer"].set_traverse_target(self.outputs["Deformer"])
  273. self.outputs["Deformer"].set_traverse_target(self.inputs["Deformer"])
  274. self.node_type = "LINK"
  275. self.hierarchy_connections, self.connections = [], []
  276. self.hierarchy_dependencies, self.dependencies = [], []
  277. self.prepared = True
  278. self.executed = True
  279. self.bObject = None
  280. setup_custom_props(self)
  281. def GetxForm(self):
  282. return GetxForm(self)
  283. def gen_morph_target_modifier(self):
  284. mod_name = self.evaluate_input("Name")
  285. # self.GetxForm().bGetObject().add_rest_position_attribute = True # this ended up being unnecessary
  286. try:
  287. m = self.GetxForm().bGetObject().modifiers[mod_name]
  288. except KeyError:
  289. m = self.GetxForm().bGetObject().modifiers.new(mod_name, type='NODES')
  290. self.bObject = m
  291. # at this point we make the node tre
  292. from bpy import data
  293. ng = data.node_groups.new(mod_name, "GeometryNodeTree")
  294. m.node_group = ng
  295. ng.interface.new_socket("Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")
  296. ng.interface.new_socket("Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")
  297. inp = ng.nodes.new("NodeGroupInput")
  298. out = ng.nodes.new("NodeGroupOutput")
  299. # TODO CLEANUP here
  300. if (position := ng.nodes.get("Position")) is None: position = ng.nodes.new("GeometryNodeInputPosition")
  301. if (index := ng.nodes.get("Index")) is None: index = ng.nodes.new("GeometryNodeInputIndex")
  302. # if (rest_position := ng.nodes.get("Rest Position")) is None: rest_position = ng.nodes.new("GeometryNodeInputNamedAttribute"); rest_position.name = "Rest Position"
  303. # rest_position.data_type = "FLOAT_VECTOR"; rest_position.inputs["Name"].default_value = "rest_position"
  304. rest_position = position
  305. add_these = []
  306. props_sockets={}
  307. object_map = {}
  308. targets = []
  309. for k,v in self.inputs.items():
  310. if "Target" in k:
  311. targets.append(v)
  312. for i, t in enumerate(targets):
  313. mt_node = t.links[0].from_node
  314. # mt_name = "Morph Target."+str(i).zfill(3)
  315. mt_name = mt_node.GetxForm().bGetObject().name
  316. vg = mt_node.parameters["Morph Target"]["vertex_group"]
  317. if vg: mt_name = mt_name+"."+vg
  318. try:
  319. ob_relative = t.links[0].from_node.inputs["Relative to"].links[0].from_node.bGetObject()
  320. except IndexError:
  321. ob_relative = None
  322. ng.interface.new_socket(mt_name, in_out = "INPUT", socket_type="NodeSocketObject")
  323. ng.interface.new_socket(mt_name+" Value", in_out = "INPUT", socket_type="NodeSocketFloat")
  324. ob_node = ng.nodes.new("GeometryNodeObjectInfo")
  325. sample_index = ng.nodes.new("GeometryNodeSampleIndex"); sample_index.data_type = 'FLOAT_VECTOR'
  326. # if (rest_position := ng.nodes.get("Rest Position")) is None: rest_position = ng.nodes.new("GeometryNodeInputNamedAttribute"); rest_position.name = "Rest Position"
  327. # rest_position.data_type = "FLOAT_VECTOR"; rest_position.inputs["Name"].default_value = "rest_position"
  328. subtract = ng.nodes.new("ShaderNodeVectorMath"); subtract.operation="SUBTRACT"
  329. scale1 = ng.nodes.new("ShaderNodeVectorMath"); scale1.operation="SCALE"
  330. ng.links.new(input=inp.outputs[mt_name], output=ob_node.inputs["Object"])
  331. ng.links.new(input=index.outputs["Index"], output=sample_index.inputs["Index"])
  332. ng.links.new(input=position.outputs["Position"], output=sample_index.inputs["Value"])
  333. ng.links.new(input=sample_index.outputs["Value"], output=subtract.inputs[0])
  334. ng.links.new(input=ob_node.outputs["Geometry"], output=sample_index.inputs["Geometry"])
  335. if ob_relative: # TODO: this should also be exposed as an input
  336. ob_node1 = ng.nodes.new("GeometryNodeObjectInfo"); ob_node1.inputs["Object"].default_value = ob_relative
  337. sample_index1 = ng.nodes.new("GeometryNodeSampleIndex"); sample_index1.data_type = 'FLOAT_VECTOR'
  338. ng.links.new(input=index.outputs["Index"], output=sample_index1.inputs["Index"])
  339. ng.links.new(input=position.outputs["Position"], output=sample_index1.inputs["Value"])
  340. ng.links.new(input=ob_node1.outputs["Geometry"], output=sample_index1.inputs["Geometry"])
  341. ng.links.new(input=sample_index1.outputs["Value"], output=subtract.inputs[1])
  342. else:
  343. # ng.links.new(input=rest_position.outputs["Attribute"], output=subtract.inputs[1])
  344. ng.links.new(input=rest_position.outputs["Position"], output=subtract.inputs[1])
  345. # IMPORTANT TODO (?):
  346. # relative objects can be recursive! Need to go back and back and back as long as we have relative objects!
  347. # in reality I am not sure haha
  348. ng.links.new(input=subtract.outputs["Vector"], output=scale1.inputs[0])
  349. # TODO: this should be exposed as a node tree input
  350. if vg:= mt_node.evaluate_input("Vertex Group"): # works
  351. vg_att = ng.nodes.new("GeometryNodeInputNamedAttribute"); vg_att.inputs["Name"].default_value=vg
  352. multiply = ng.nodes.new("ShaderNodeMath"); multiply.operation = "MULTIPLY"
  353. ng.links.new(input=vg_att.outputs["Attribute"], output=multiply.inputs[1])
  354. ng.links.new(input=inp.outputs[mt_name+" Value"], output=multiply.inputs[0])
  355. ng.links.new(input=multiply.outputs[0], output=scale1.inputs["Scale"])
  356. else:
  357. ng.links.new(input=inp.outputs[mt_name+" Value"], output=scale1.inputs["Scale"])
  358. add_these.append(scale1)
  359. object_map["Socket_"+str((i+1)*2)]=mt_node.GetxForm().bGetObject()
  360. props_sockets["Socket_"+str((i+1)*2+1)]= ("Value."+str(i).zfill(3), 1.0)
  361. set_position = ng.nodes.new("GeometryNodeSetPosition")
  362. ng.links.new(inp.outputs["Geometry"], output=set_position.inputs["Geometry"])
  363. ng.links.new(set_position.outputs["Geometry"], output=out.inputs["Geometry"])
  364. prev_node = rest_position
  365. for i, node in enumerate(add_these):
  366. add = ng.nodes.new("ShaderNodeVectorMath"); add.operation="ADD"
  367. ng.links.new(prev_node.outputs[0], output=add.inputs[0])
  368. ng.links.new(node.outputs[0], output=add.inputs[1])
  369. prev_node = add
  370. ng.links.new(add.outputs[0], output=set_position.inputs["Position"])
  371. from .utilities import SugiyamaGraph
  372. SugiyamaGraph(ng, 12)
  373. # for k,v in props_sockets.items():
  374. # print(wrapWhite(k), wrapOrange(v))
  375. evaluate_sockets(self, m, props_sockets)
  376. for socket, ob in object_map.items():
  377. m[socket]=ob
  378. finish_drivers(self)
  379. def gen_shape_key(self): # TODO: make this a feature of the node definition that appears only when there are no prior deformers - and shows a warning!
  380. self.gen_morph_target_modifier()
  381. return
  382. # 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.
  383. # there are a number of things I need to fix here
  384. # - reuse shape keys if possible
  385. # - figure out how to make this a lot faster
  386. # - edit the xForm stuff to delete drivers from shape key ID's, since they belong to the Key, not the Object.
  387. from time import time
  388. start_time = time()
  389. from bpy import data
  390. ob = self.GetxForm().bGetObject()
  391. m = data.meshes.new_from_object(ob, preserve_all_data_layers=True)
  392. ob.data = m
  393. ob.add_rest_position_attribute = True
  394. ob.shape_key_clear()
  395. targets = []
  396. for k,v in self.inputs.items():
  397. if "Target" in k:
  398. targets.append(v)
  399. for i, t in enumerate(targets):
  400. mt_node = t.links[0].from_node
  401. mt_name = "Morph Target."+str(i).zfill(3)
  402. # using the built-in shapekey feature is actually a lot harder in terms of programming because I need...
  403. # min/max, as it is just not a feature of the GN version
  404. # to carry info from the morph target node regarding relative shapes and vertex groups and all that
  405. # the drivers may be more difficult to apply, too.
  406. # hafta make new geometry for the object and add shape keys and all that
  407. # the benefit to all this being maybe better performence and exporting to game engines via .fbx
  408. #
  409. # first make a basis shape key
  410. ob.shape_key_add(name='Basis', from_mix=False)
  411. keys={}
  412. props_sockets={}
  413. for i, t in enumerate(targets):
  414. mt_node = t.links[0].from_node
  415. # mt_name = "Morph Target."+str(i).zfill(3)
  416. sk_ob = mt_node.GetxForm().bGetObject()
  417. mt_name = sk_ob.name
  418. vg = mt_node.parameters["Morph Target"]["vertex_group"]
  419. if vg: mt_name = mt_name+"."+vg
  420. sk = ob.shape_key_add(name=mt_name, from_mix=False)
  421. # the shapekey data is absolute point data for each vertex, in order, very simple
  422. for j in range(len(m.vertices)):
  423. sk.data[j].co = sk_ob.data.vertices[j].co # assume they match
  424. sk.vertex_group = vg
  425. sk.slider_min = -10
  426. sk.slider_max = 10
  427. keys[mt_name]=sk
  428. props_sockets[mt_name]= ("Value."+str(i).zfill(3), 1.0)
  429. for i, t in enumerate(targets):
  430. mt_node = t.links[0].from_node
  431. # mt_name = "Morph Target."+str(i).zfill(3)
  432. sk_ob = mt_node.GetxForm().bGetObject()
  433. mt_name = sk_ob.name
  434. vg = mt_node.parameters["Morph Target"]["vertex_group"]
  435. if vg: mt_name = mt_name+"."+vg
  436. if rel := mt_node.parameters["Morph Target"]["relative_shape"]:
  437. sk = keys.get(mt_name)
  438. sk.relative_key = keys.get(rel)
  439. # for k,v in props_sockets.items():
  440. # print(wrapWhite(k), wrapOrange(v), wrapRed(self.evaluate_input(v)))
  441. self.bObject = sk.id_data
  442. evaluate_sockets(self, sk.id_data, props_sockets)
  443. finish_drivers(self)
  444. prWhite(f"Initializing morph target took {time() -start_time} seconds")
  445. # then we need to get all the data from the morph targets, pull all the relative shapes first and add them, vertex groups and properties
  446. # next we add all the shape keys that are left, and their vertex groups
  447. # set the slider ranges to -10 and 10
  448. # then set up the drivers
  449. def bFinalize(self, bContext=None):
  450. # let's find out if there is a prior deformer.
  451. # if not, then there should be an option to use plain 'ol shape keys
  452. # GN is always desirable as an option though because it can be baked.
  453. if self.inputs["Deformer"].is_linked:
  454. self.gen_morph_target_modifier()
  455. else:
  456. # for now we'll just do it this way.
  457. self.gen_shape_key()
  458. for c in TellClasses():
  459. setup_container(c)