xForm_nodes.py 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. from .utilities.node_common import *
  2. from .base_definitions import MantisNode, NodeSocket
  3. from .xForm_socket_templates import *
  4. from .mantis_dataclasses import xForm_info
  5. def TellClasses():
  6. return [
  7. # xForm
  8. xFormArmature,
  9. xFormBone,
  10. xFormGeometryObject,
  11. xFormObjectInstance,
  12. xFormCurvePin,
  13. xFormGetBone,
  14. ]
  15. #*#-------------------------------#++#-------------------------------#*#
  16. # X - F O R M N O D E S
  17. #*#-------------------------------#++#-------------------------------#*#
  18. def reset_object_data(ob):
  19. # moving this to a common function so I can figure out the details later
  20. ob.id_properties_clear() # we must remove the custom properties so we can make them again.
  21. ob.constraints.clear()
  22. ob.animation_data_clear() # this is a little dangerous. TODO find a better solution since this can wipe animation the user wants to keep
  23. ob.modifiers.clear() # I would also like a way to copy modifiers and their settings, or bake them down. oh well
  24. def get_parent_node(mantis_node, type = 'XFORM'):
  25. # type variable for selecting whether to get either
  26. # the parent xForm or the inheritance node
  27. node_line, _last_socket = trace_single_line(mantis_node, "Relationship")
  28. for i in range(len(node_line)):
  29. # check each of the possible parent types.
  30. if ( (node_line[ i ].__class__.__name__ == 'LinkInherit') ):
  31. try: # it's the next one
  32. if (type == 'XFORM'):
  33. return node_line[ i + 1 ]
  34. else: # type = 'LINK'
  35. return node_line[ i ]
  36. except IndexError: # if there is no next one...
  37. return None # then there's no parent!
  38. return None
  39. def get_matrix(node):
  40. matrix = node.evaluate_input('Matrix')
  41. if matrix is None:
  42. node_line, socket = trace_single_line(node, "Matrix")
  43. raise RuntimeError(wrapRed(f"No matrix found for Armature {node}"))
  44. return matrix
  45. def set_object_parent(node):
  46. from bpy import data
  47. parent_xForm_info = get_parent_xForm_info(node)
  48. if parent_xForm_info.object_type == '':
  49. return # no parent
  50. elif parent_xForm_info.object_type in ['armature', 'object']:
  51. parent = data.objects.get(parent_xForm_info.self_edit_name)
  52. if parent_xForm_info.parent_edit_name and parent is None:
  53. raise GraphError(f"Could not get parent object for node {node}.")
  54. node.bGetObject().parent = parent
  55. else: # it is a bone
  56. armOb = data.objects.get(parent_xForm_info.root_armature)
  57. node_ob = node.bGetObject()
  58. node_ob.parent = armOb; node_ob.parent_type = 'BONE'
  59. # this one expects a string.
  60. node_ob.parent_bone = parent_xForm_info.self_edit_name
  61. class xFormNode(MantisNode):
  62. __slots__ = ()
  63. node_type = 'XFORM'
  64. def __init__(self, signature, base_tree, socket_templates=[],
  65. additional_parameters={}):
  66. super().__init__(signature, base_tree, socket_templates,
  67. additional_parameters)
  68. self.bObject=None
  69. # because new objects are created during prep phase
  70. def reset_execution(self):
  71. super().reset_execution()
  72. self.prepared=False
  73. def check_drivers(self):
  74. for st in self.socket_templates:
  75. try:
  76. if st.is_animatable == False:
  77. socket = st.name
  78. multi = st.use_multi_input
  79. vector = isinstance(st.default_value, tuple)
  80. # if single property
  81. if not multi and not vector:
  82. has_driver = check_for_driver(self, socket)
  83. elif not multi and vector:
  84. for i in range(len(st.default_value)):
  85. has_driver = check_for_driver(self, socket, i)
  86. if has_driver: break
  87. elif multi:
  88. # TODO: this is a HACK, try and understand this later
  89. if socket == 'Custom Properties':
  90. has_driver = check_for_driver(self, socket,)
  91. else:
  92. for i in range(len(self.inputs[socket].links)):
  93. has_driver = check_for_driver(self, socket, 0, i)
  94. if has_driver: break
  95. if has_driver:
  96. raise GraphError(f"Error: Socket \"{socket}\" is not animatable and cannot be driven.")
  97. except Exception as e:
  98. # This isn't really an error and the user should not see it.
  99. from .__init__ import DOERROR
  100. if DOERROR:
  101. raise e
  102. def setup_custom_props(self, ob, input):
  103. from .mantis_dataclasses import custom_prop_template
  104. # detect custom inputs
  105. for i, l in enumerate(self.inputs[input].links):
  106. # we need to trace back all inputs
  107. custom_prop_data = self.evaluate_input(input, i)
  108. if not isinstance(custom_prop_data, custom_prop_template): continue
  109. from .utilities.drivers import MantisDriver
  110. name = custom_prop_data.name
  111. if name in ob.keys():
  112. raise RuntimeError("Error: cannot create a custom property with "
  113. "the same name as an existing property.")
  114. prop_type = custom_prop_data.prop_type
  115. match prop_type:
  116. case 'BOOL':
  117. value = custom_prop_data.default_value_bool
  118. case 'INT':
  119. value = custom_prop_data.default_value_int
  120. case 'FLOAT':
  121. value = custom_prop_data.default_value_float
  122. case 'VECTOR':
  123. value = custom_prop_data.default_value_vector
  124. case 'STRING':
  125. value = custom_prop_data.default_value_string
  126. if (value is None):
  127. raise RuntimeError("Could not set value of custom parameter")
  128. # it creates a more confusing error later sometimes, better to catch it here.
  129. ob[name] = value
  130. ui_data = ob.id_properties_ui(name)
  131. ui_data.update(
  132. default=value,
  133. description=custom_prop_data.description)
  134. #if a number, set the min/max values. it may overflow on the C side
  135. if type(value) in [float, int]:
  136. for prop_name in ['min', 'max', 'soft_min', 'soft_max', ]:
  137. prop_value = getattr(custom_prop_data, prop_name)
  138. if type(value) == int: prop_value = int(prop_value)
  139. # DO: figure out the right way to prevent an oveflow
  140. try: # we have to do this as a keyword argument like this
  141. ui_data.update( **{prop_name:prop_value} )
  142. except OverflowError: #this occurs when the value is inf
  143. prRed(f"invalid value {prop_value} for custom prop {prop_name}"
  144. f" of type {type(value)} in {self}. It will remain unset.")
  145. ob.property_overridable_library_set(f"[\"{name}\"]", True)
  146. pass
  147. # TODO this should use the socket templates / prop_sockets system!
  148. class xFormArmature(xFormNode):
  149. '''A node representing an armature object'''
  150. __slots__ = ()
  151. socket_templates = xFormArmatureSockets
  152. def __init__(self, signature, base_tree):
  153. super().__init__(signature, base_tree, self.socket_templates)
  154. setup_custom_property_inputs_outputs(self)
  155. def bPrepare(self, bContext=None):
  156. self.parameters['Matrix'] = get_matrix(self)
  157. self.prepared = True
  158. def bTransformPass(self, bContext = None,):
  159. # from .utilities import get_ui_node
  160. import bpy
  161. if (not isinstance(bContext, bpy.types.Context)):
  162. raise RuntimeError("Incorrect context")
  163. name = self.evaluate_input("Name")
  164. matrix = self.parameters['Matrix']
  165. reset_transforms = False
  166. #check if an object by the name exists
  167. if (name) and (ob := bpy.data.objects.get(name)):
  168. # Clear the drivers from BOTH the object/pose and the skeleton
  169. if (ob.animation_data):
  170. while (ob.animation_data.drivers):
  171. ob.animation_data.drivers.remove(ob.animation_data.drivers[-1])
  172. if (ob.data.animation_data):
  173. while (ob.data.animation_data.drivers):
  174. ob.data.animation_data.drivers.remove(ob.data.animation_data.drivers[-1])
  175. for pb in ob.pose.bones:
  176. # clear it, even after deleting the edit bones,
  177. # if we create them again the pose bones will be reused
  178. while (pb.constraints):
  179. pb.constraints.remove(pb.constraints[-1])
  180. if reset_transforms:
  181. pb.location = (0,0,0)
  182. pb.rotation_euler = (0,0,0)
  183. pb.rotation_quaternion = (1.0,0,0,0)
  184. pb.rotation_axis_angle = (0,0,1.0,0)
  185. pb.scale = (1.0,1.0,1.0)
  186. # feels ugly and bad, whatever
  187. collections = []
  188. for bc in ob.data.collections:
  189. collections.append(bc)
  190. for bc in collections:
  191. ob.data.collections.remove(bc)
  192. del collections
  193. # end ugly/bad
  194. else:
  195. # Create the Object
  196. ob = bpy.data.objects.new(name, bpy.data.armatures.new(name)) #create ob
  197. if (ob.name != name):
  198. raise RuntimeError("Could not create xForm object", name)
  199. # it seems this data sticks around even if the object was deleted, so clear it here.
  200. ob.id_properties_clear()
  201. self.bObject = ob.name
  202. self.bRegisterObject('OBJECT', ob)
  203. ob.matrix_world = matrix.copy()
  204. ob.data.pose_position = 'REST'
  205. set_object_parent(self)
  206. # Link to Scene:
  207. if (ob.name not in bContext.view_layer.active_layer_collection.collection.objects):
  208. bContext.view_layer.active_layer_collection.collection.objects.link(ob)
  209. print( wrapGreen("Created Armature object: ")+ wrapWhite(ob.name))
  210. # Finalize the action
  211. # oddly, overriding context doesn't seem to work
  212. try:
  213. bpy.ops.object.select_all(action='DESELECT')
  214. except RuntimeError:
  215. pass # we're already in edit mode, should be OK to do this.
  216. bContext.view_layer.objects.active = ob
  217. selected=[]
  218. for other_ob in bpy.data.objects:
  219. if other_ob.mode == "EDIT":
  220. selected.append(other_ob)
  221. selected.append(ob)
  222. context_override = {"active_object":ob, "selected_objects":selected}
  223. print("Changing Armature Mode to " +wrapPurple("EDIT"))
  224. with bContext.temp_override(**context_override):
  225. bpy.ops.object.mode_set(mode='EDIT')
  226. if ob.mode != "EDIT":
  227. prRed("eh?")
  228. # clear it
  229. while (len(ob.data.edit_bones) > 0):
  230. ob.data.edit_bones.remove(ob.data.edit_bones[0])
  231. # bContext.view_layer.objects.active = prevAct
  232. ob.data.display_type = self.evaluate_input('Display Type')
  233. # This will OVERWRITE the root armature since bones have to trace back to this one.
  234. root_armature = ob.name
  235. parent_xForm_info = get_parent_xForm_info(self)
  236. my_info = xForm_info(
  237. object_type='armature',
  238. root_armature= ob.name,
  239. parent_pose_name=parent_xForm_info.self_pose_name,
  240. parent_edit_name=parent_xForm_info.self_edit_name,
  241. self_pose_name=ob.name,
  242. self_edit_name=ob.name,
  243. )
  244. self.parameters['xForm Out'] = my_info
  245. self.executed = True
  246. def bFinalize(self, bContext = None):
  247. # custom properties
  248. self.setup_custom_props(self.bGetObject(), "Custom Properties")
  249. finish_drivers(self)
  250. def bGetObject(self, mode = ''):
  251. import bpy; return bpy.data.objects[self.parameters['xForm Out'].self_pose_name]
  252. class xFormBone(xFormNode):
  253. '''A node representing a bone in an armature'''
  254. __slots__ = ()
  255. socket_templates = xFormBoneSockets
  256. # DO: make a way to identify which armature this belongs to
  257. def __init__(self, signature, base_tree):
  258. super().__init__(signature, base_tree, xFormBoneSockets)
  259. setup_custom_property_inputs_outputs(self)
  260. def bGetParentArmature(self):
  261. parent_xForm_info = get_parent_xForm_info(self)
  262. from bpy import data
  263. return data.objects.get(parent_xForm_info.root_armature)
  264. def bSetParent(self, eb):
  265. from bpy import data
  266. parent_xForm_info = get_parent_xForm_info(self)
  267. parent_armature = data.objects.get( parent_xForm_info.root_armature)
  268. if parent_xForm_info.self_edit_name != parent_xForm_info.root_armature:
  269. parent_bone = parent_armature.data.edit_bones.get( parent_xForm_info.self_edit_name)
  270. eb.parent = parent_bone
  271. parent_mantis_node = get_parent_node(self, type = 'LINK') # get the link node.
  272. # TODO probably need to send the parenting info or at least the signatures of intervening nodes.
  273. eb.use_connect = parent_mantis_node.evaluate_input("Connected")
  274. eb.use_inherit_rotation = parent_mantis_node.evaluate_input("Inherit Rotation")
  275. eb.inherit_scale = parent_mantis_node.evaluate_input("Inherit Scale")
  276. def bPrepare(self, bContext=None):
  277. self.check_drivers()
  278. self.parameters['Matrix'] = get_matrix(self)
  279. self.prepared = True
  280. def bTransformPass(self, bContext = None,): #possibly will need to pass context?
  281. import bpy
  282. from mathutils import Vector
  283. if not (name := self.evaluate_input("Name")):
  284. raise RuntimeError(wrapRed(f"Could not set name for bone in {self}"))
  285. if (not isinstance(bContext, bpy.types.Context)):
  286. raise RuntimeError("Incorrect context")
  287. if not (xF := self.bGetParentArmature()):
  288. raise RuntimeError("Could not create edit bone: ", name, " from node:", self, " Reason: No armature object to add bone to.")
  289. matrix = self.parameters['Matrix']
  290. length = matrix[3][3]
  291. if (xF):
  292. if (xF.mode != "EDIT"):
  293. raise RuntimeError("Armature Object Not in Edit Mode, exiting...")
  294. #
  295. # Create the Object
  296. d = xF.data
  297. eb = d.edit_bones.new(name)
  298. # Bone Collections:
  299. # We treat each separate string as a Bone Collection that this object belongs to
  300. # Bone Collections are fully qualified by their hierarchy.
  301. # Separate Strings with "|" and indicate hierarchy with ">". These are special characters.
  302. # NOTE: if the user names the collections differently at different times, this will take the FIRST definition and go with it
  303. if self.inputs['Bone Collection'].links:
  304. bCol_groups = []
  305. for i, l in enumerate(self.inputs['Bone Collection'].links):
  306. bCol_group = self.evaluate_input("Bone Collection", index=i)
  307. bCol_groups.append(bCol_group)
  308. bCols = '|'.join(bCol_groups)
  309. else:
  310. bCols = self.evaluate_input("Bone Collection")
  311. if bCols: # it is actually possible to add a bone to no collections. odd.
  312. bone_collections = bCols.split("|")
  313. for collection_list in bone_collections:
  314. hierarchy = collection_list.split(">")
  315. col_parent = None
  316. for bCol in hierarchy:
  317. if bCol is None: continue # I think this
  318. if bCol == '': continue
  319. if ( col := d.collections_all.get(bCol) ) is None:
  320. col = d.collections.new(bCol)
  321. col.parent = col_parent
  322. col_parent = col
  323. if hierarchy[-1]: # will be "" or None if the box is empty
  324. d.collections_all.get(hierarchy[-1]).assign(eb)
  325. if (eb.name != name):
  326. prRed(f"Expected bone of name: {name}, got {eb.name} instead.")
  327. raise RuntimeError("Could not create bone ", name, "; Perhaps there is a duplicate bone name in the node tree?")
  328. eb.matrix = matrix.copy()
  329. tailoffset = Vector((0,length,0)) #Vector((0,self.tailoffset, 0))
  330. tailoffset = matrix.copy().to_3x3() @ tailoffset
  331. eb.tail = eb.head + tailoffset
  332. if (eb.name != name):
  333. raise RuntimeError("Could not create edit bone: ", name)
  334. assert (eb.name), "Bone must have a name."
  335. self.bObject = eb.name
  336. self.bRegisterObject('BONE', xF, eb)
  337. # The bone should have relationships going in at this point.
  338. self.bSetParent(eb)
  339. if eb.head == eb.tail:
  340. raise RuntimeError(wrapRed(f"Could not create edit bone: {name} because bone head was located in the same place as bone tail."))
  341. # Setup Deform attributes...
  342. eb_props_sockets = self.gen_property_socket_map(prop_owner_group="edit_bone")
  343. evaluate_sockets(self, eb, eb_props_sockets)
  344. print( wrapGreen("Created Bone: ") + wrapOrange(eb.name) + wrapGreen(" in ") + wrapWhite(self.bGetParentArmature().name))
  345. parent_xForm_info = get_parent_xForm_info(self)
  346. my_info = xForm_info(
  347. object_type='bone',
  348. root_armature= xF.name,
  349. parent_pose_name=parent_xForm_info.self_pose_name,
  350. parent_edit_name=parent_xForm_info.self_edit_name,
  351. self_pose_name=eb.name,
  352. self_edit_name=eb.name,
  353. )
  354. self.parameters['xForm Out'] = my_info
  355. self.executed = True
  356. def set_bone_color(self, b, inherit_color, bContext):
  357. color_values = self.evaluate_input('Color')
  358. if color_values is None:
  359. prOrange(f"Warning: No color information found for {b.name}. This should not happen.")
  360. return
  361. if inherit_color and b.parent:
  362. b.color.palette=b.parent.color.palette
  363. if b.color.palette == 'CUSTOM':
  364. b.color.custom.active=b.parent.color.custom.active
  365. b.color.custom.normal=b.parent.color.custom.normal
  366. b.color.custom.select=b.parent.color.custom.select
  367. return
  368. from mathutils import Color
  369. color_active = Color(color_values[:3])
  370. color_normal = Color(color_values[3:6])
  371. color_select = Color(color_values[6:])
  372. is_theme_colors = False
  373. theme = bContext.preferences.themes[0]
  374. for i, color_set in enumerate(theme.bone_color_sets):
  375. if ((color_active == color_set.active) and
  376. (color_normal == color_set.normal) and
  377. (color_select == color_set.select) ):
  378. is_theme_colors=True; break
  379. if is_theme_colors: # add 1, not 0-indexed
  380. b.color.palette = 'THEME'+str(i+1).zfill(2)
  381. elif ((color_active == theme.view_3d.bone_pose_active) and
  382. (color_normal == theme.view_3d.bone_solid) and
  383. (color_select == theme.view_3d.bone_pose) ):
  384. b.color.palette = 'DEFAULT'
  385. else:
  386. b.color.palette = 'CUSTOM'
  387. b.color.custom.active=color_active
  388. b.color.custom.normal=color_normal
  389. b.color.custom.select=color_select
  390. def bFinalize(self, bContext = None):
  391. b = self.bGetParentArmature().data.bones[self.bObject]
  392. # let's do bone colors first
  393. inherit_color = self.evaluate_input("Inherit Color")
  394. if len(self.inputs['Color'].links) > 0:
  395. inherit_color = False # use the link instead
  396. self.set_bone_color(b, inherit_color, bContext)
  397. if self.evaluate_input("BBone HQ Deformation"):
  398. b.bbone_mapping_mode = "CURVED"
  399. if handle_type := self.evaluate_input("BBone Start Handle Type"):
  400. b.bbone_handle_type_start = handle_type
  401. if handle_type := self.evaluate_input("BBone End Handle Type"):
  402. b.bbone_handle_type_end = handle_type
  403. try:
  404. if (custom_handle := self.evaluate_input("BBone Custom Start Handle")):
  405. b.bbone_custom_handle_start = self.bGetParentArmature().data.bones[custom_handle]
  406. if (custom_handle := self.evaluate_input("BBone Custom End Handle")):
  407. b.bbone_custom_handle_end = self.bGetParentArmature().data.bones[custom_handle]
  408. except KeyError:
  409. prRed("Warning: BBone start or end handle not set because of missing bone in armature.")
  410. import bpy
  411. pb = self.bGetParentArmature().pose.bones[self.bObject]
  412. if check_for_driver(self, 'Rotation Order'):
  413. # this is stupid and a HACK
  414. from .mantis_dataclasses import MantisSocketFiller
  415. props_sockets = {"rotation_mode": MantisSocketFiller( socket_name="Rotation Order",
  416. property_name="rotation_mode", default_value='XYZ',)}
  417. evaluate_sockets(self, pb, props_sockets)
  418. else: # This is stupid, but I did it to myself and it will be annoying to fix
  419. rotation_mode = self.evaluate_input("Rotation Order")
  420. if rotation_mode == "AUTO": rotation_mode = "XYZ"
  421. pb.rotation_mode = rotation_mode
  422. pb.id_properties_clear()
  423. self.setup_custom_props(pb, "Custom Properties")
  424. pb_props_sockets = self.gen_property_socket_map(prop_owner_group='pose_bone')
  425. evaluate_sockets(self, pb, pb_props_sockets)
  426. bone_props_socket = self.gen_property_socket_map(prop_owner_group='bone')
  427. evaluate_sockets(self, pb.bone, bone_props_socket)
  428. # and in edit mode we need to explicitly set the bbone properties
  429. # I don't like this HACK but there seems to be no other way...
  430. # Better to ask forgiveness than permission. If anyone wants control over this, I'll solve the problem then
  431. # IN the near future, in fact, I think I'll make a BBone Rest Pose matrix thing
  432. # and allow users to set it from bezier curves
  433. pb.bone.bbone_easein = 1; pb.bone.bbone_easeout = 1
  434. finish_drivers(self)
  435. pb.custom_shape_transform = None
  436. pb.custom_shape = None
  437. if len(self.inputs["Custom Object xForm Override"].links) > 0:
  438. trace = trace_single_line(self, "Custom Object xForm Override")
  439. try:
  440. pb.custom_shape_transform = trace[0][1].bGetObject()
  441. except AttributeError:
  442. pass
  443. if len(self.inputs["Custom Object"].links) > 0:
  444. trace = trace_single_line(self, "Custom Object")
  445. try:
  446. ob = trace[0][1].bGetObject()
  447. except AttributeError:
  448. ob=None
  449. if type(ob) in [bpy.types.Object]:
  450. pb.custom_shape = ob
  451. def bGetObject(self, mode = 'POSE'):
  452. if self.bObject is None: return None
  453. if mode in ["POSE", "OBJECT"] and self.bGetParentArmature().mode == "EDIT":
  454. raise RuntimeError("Cannot get Bone or PoseBone in Edit mode.")
  455. elif mode == "EDIT" and self.bGetParentArmature().mode != "EDIT":
  456. raise RuntimeError("Cannot get EditBone except in Edit mode.")
  457. try:
  458. if (mode == 'EDIT'):
  459. return self.bGetParentArmature().data.edit_bones[self.bObject]
  460. elif (mode == 'OBJECT'):
  461. return self.bGetParentArmature().data.bones[self.bObject]
  462. elif (mode == 'POSE'):
  463. return self.bGetParentArmature().pose.bones[self.bObject]
  464. except Exception as e:
  465. prRed ("Cannot get bone for %s" % self)
  466. raise e
  467. class xFormGeometryObject(xFormNode):
  468. '''A node representing an armature object'''
  469. __slots__ = ("has_shape_keys")
  470. socket_templates = xFormGeometryObjectSockets
  471. def __init__(self, signature, base_tree):
  472. super().__init__(signature, base_tree, self.socket_templates)
  473. setup_custom_property_inputs_outputs(self)
  474. def bPrepare(self, bContext = None,):
  475. import bpy
  476. if not self.evaluate_input("Name"):
  477. self.prepared = True
  478. self.executed = True
  479. # and return an error if there are any dependencies:
  480. if self.hierarchy_connections:
  481. raise GraphError(wrapRed(f"Cannot Generate object {self} because the chosen name is empty or invalid."))
  482. return
  483. self.bObject = bpy.data.objects.get(self.evaluate_input("Name"))
  484. trace = trace_single_line(self, "Geometry")
  485. if (not self.bObject):
  486. if trace[-1]:
  487. self.bObject = bpy.data.objects.new(self.evaluate_input("Name"), trace[-1].node.bGetObject())
  488. # handle mismatched data.
  489. data_wrong = False; data = None
  490. if (self.inputs["Geometry"].is_linked and self.bObject.type == "EMPTY"):
  491. data_wrong = True; data = trace[-1].node.bGetObject()
  492. elif (not self.inputs["Geometry"].is_linked and not self.bObject.type == "EMPTY"):
  493. data_wrong = True
  494. # clumsy but functional
  495. if data_wrong:
  496. unlink_me = self.bObject
  497. unlink_me.name = "MANTIS_TRASH.000"
  498. for col in unlink_me.users_collection:
  499. col.objects.unlink(unlink_me)
  500. self.bObject = bpy.data.objects.new(self.evaluate_input("Name"), data)
  501. if self.bObject and (self.inputs["Geometry"].is_linked and self.bObject.type in ["MESH", "CURVE"]):
  502. self.bObject.data = trace[-1].node.bGetObject()
  503. # NOW: find out if we need to duplicate the object data.
  504. dupe_data=False
  505. node_line = trace_single_line(self, "Deformer")[0]
  506. from .deformer_nodes import DeformerHook
  507. for deformer in node_line:
  508. if isinstance(deformer, DeformerHook) and \
  509. deformer.evaluate_input("Affect Curve Radius") == True and \
  510. self.bObject.type == 'CURVE':
  511. print(f"INFO: Duplicating data {self.bObject.data.name} in {self} so it can be used for drivers.")
  512. dupe_data=True; break
  513. if dupe_data:
  514. name = self.bObject.data.name
  515. # it has to be a curve
  516. data = bpy.data.curves.get(self.bObject.data.name+"_MANTIS")
  517. if data: # Delete it and regenerate it if it exists.
  518. data.name+="_TRASH.000" # but we can't actually delete it here
  519. # since previous executions of the graph may be using it.
  520. # instead, we'll rename it and use a new copy. This will probably
  521. # be deleted on its own by Blender's garbage collector.
  522. # if it isn't, then it is still in use and I may NOT touch it.
  523. data=self.bObject.data.copy(); data.animation_data_clear()
  524. data.name = name+"_MANTIS"
  525. self.bObject.data = data
  526. reset_object_data(self.bObject)
  527. matrix= get_matrix(self)
  528. self.parameters['Matrix'] = matrix
  529. try:
  530. set_object_parent(self)
  531. except: # I guess it isn't ready yet. we'll do it later
  532. pass # (This can happen when solving schema.)
  533. self.bObject.matrix_world = matrix
  534. parent_xForm_info = get_parent_xForm_info(self)
  535. root_armature = parent_xForm_info.root_armature
  536. my_info = xForm_info(
  537. object_type='object',
  538. root_armature= root_armature,
  539. parent_pose_name=parent_xForm_info.self_edit_name,
  540. parent_edit_name=parent_xForm_info.self_pose_name,
  541. self_pose_name=self.bObject.name,
  542. self_edit_name=self.bObject.name,
  543. )
  544. self.parameters['xForm Out'] = my_info
  545. self.bRegisterObject('OBJECT', self.bObject)
  546. self.prepared = True
  547. def bTransformPass(self, bContext = None,):
  548. try:
  549. bContext.collection.objects.link(self.bObject)
  550. except RuntimeError: #already in; but a dangerous thing to pass.
  551. pass
  552. self.has_shape_keys = False
  553. # putting this in bTransformPass simply prevents it from being run more than once.
  554. # maybe I should do that with the rest of bPrepare, too.
  555. props_sockets = self.gen_property_socket_map()
  556. evaluate_sockets(self, self.bObject, props_sockets)
  557. self.executed = True
  558. def bFinalize(self, bContext = None):
  559. matrix= get_matrix(self)
  560. set_object_parent(self)
  561. self.bObject.matrix_world = matrix
  562. # custom properties
  563. self.setup_custom_props(self.bGetObject(), "Custom Properties")
  564. for i, (driver_key, driver_item) in enumerate(self.drivers.items()):
  565. print (wrapGreen(i), wrapWhite(self), wrapPurple(driver_key))
  566. prOrange(driver_item)
  567. finish_drivers(self)
  568. def bGetObject(self, mode = 'POSE'):
  569. return self.bObject
  570. class xFormObjectInstance(xFormNode):
  571. """Represents an instance of an existing geometry object."""
  572. __slots__ = ("has_shape_keys")
  573. socket_templates = xFormGeometryObjectInstanceSockets
  574. def __init__(self, signature, base_tree):
  575. super().__init__(signature, base_tree, self.socket_templates)
  576. self.has_shape_keys = False # Shape Keys will make a dupe so this is OK
  577. setup_custom_property_inputs_outputs(self)
  578. def ui_modify_socket(self, ui_socket, socket_name=None):
  579. if ui_socket.name == 'As Instance':
  580. change_handled = True
  581. try:
  582. self.bObject.modifiers[0]['Socket_1'] = ui_socket.default_value
  583. except Exception as e:
  584. print("Failed to update mantis socket because of %s" % e,
  585. "Updating tree instead.")
  586. return change_handled
  587. else:
  588. return super().ui_modify_socket(ui_socket, socket_name)
  589. def bPrepare(self, bContext = None,):
  590. from bpy import data
  591. empty_mesh = data.meshes.get("MANTIS_EMPTY_MESH")
  592. if not empty_mesh:
  593. empty_mesh = data.meshes.new("MANTIS_EMPTY_MESH")
  594. if not self.evaluate_input("Name"):
  595. self.prepared = True
  596. self.executed = True
  597. # and return an error if there are any dependencies:
  598. if self.hierarchy_connections:
  599. raise GraphError(wrapRed(f"Cannot Generate object {self} because the chosen name is empty or invalid."))
  600. return
  601. self.bObject = data.objects.get(self.evaluate_input("Name"))
  602. if (not self.bObject):
  603. self.bObject = data.objects.new(self.evaluate_input("Name"), empty_mesh)
  604. reset_object_data(self.bObject)
  605. matrix= get_matrix(self)
  606. self.parameters['Matrix'] = matrix
  607. set_object_parent(self)
  608. self.bObject.matrix_world = matrix
  609. parent_xForm_info = get_parent_xForm_info(self)
  610. root_armature = parent_xForm_info.root_armature
  611. my_info = xForm_info(
  612. object_type='object',
  613. root_armature= root_armature,
  614. parent_pose_name=parent_xForm_info.self_edit_name,
  615. parent_edit_name=parent_xForm_info.self_pose_name,
  616. self_pose_name=self.bObject.name,
  617. self_edit_name=self.bObject.name,
  618. )
  619. self.parameters['xForm Out'] = my_info
  620. self.bRegisterObject('OBJECT', self.bObject)
  621. self.prepared = True
  622. def bTransformPass(self, bContext = None,):
  623. try:
  624. bContext.collection.objects.link(self.bObject)
  625. except RuntimeError: #already in; but a dangerous thing to pass.
  626. pass
  627. self.has_shape_keys = False
  628. # putting this in bTransformPass simply prevents it from being run more than once.
  629. # maybe I should do that with the rest of bPrepare, too.
  630. props_sockets = {
  631. 'hide_viewport' : ("Hide in Viewport", False),
  632. 'hide_render' : ("Hide in Render", False),
  633. }
  634. evaluate_sockets(self, self.bObject, props_sockets)
  635. self.executed = True
  636. def bFinalize(self, bContext = None):
  637. # now we need to set the object instance up.
  638. from bpy import data
  639. trace = trace_single_line(self, "Source Object")
  640. for node in trace[0]:
  641. if node is self: continue # lol
  642. if (node.node_type == 'XFORM'):
  643. source_ob = node.bGetObject(); break
  644. modifier = self.bObject.modifiers.new("Object Instance", type='NODES')
  645. ng = data.node_groups.get("Object Instance")
  646. if ng is None:
  647. from .utilities.geometry_node_graphgen import gen_object_instance_node_group
  648. ng = gen_object_instance_node_group()
  649. modifier.node_group = ng
  650. modifier["Socket_0"] = source_ob
  651. modifier["Socket_1"] = self.evaluate_input("As Instance")
  652. # custom properties
  653. self.setup_custom_props(self.bGetObject(), "Custom Properties")
  654. for i, (driver_key, driver_item) in enumerate(self.drivers.items()):
  655. print (wrapGreen(i), wrapWhite(self), wrapPurple(driver_key))
  656. prOrange(driver_item)
  657. finish_drivers(self)
  658. def bGetObject(self, mode = 'POSE'):
  659. return self.bObject
  660. class xFormCurvePin(xFormNode):
  661. """An xForm pinned to a specific location on a curve."""
  662. __slots__ = ()
  663. socket_templates = xFormCurvePinSockets
  664. def __init__(self, signature, base_tree):
  665. super().__init__(signature, base_tree, self.socket_templates,
  666. additional_parameters={"Matrix":None})
  667. setup_custom_property_inputs_outputs(self)
  668. def prep_driver_values(self, constraint):
  669. from .misc_nodes import UtilityDriver, UtilitySwitch
  670. for socket_name in ["Curve Pin Factor", "Forward Axis","Up Axis",]:
  671. if self.inputs.get(socket_name) is None: continue # in case it has been bypassed
  672. if self.inputs[socket_name].is_linked:
  673. node_line, _last_socket = trace_single_line(self, socket_name)
  674. driver = None
  675. for other_node in node_line:
  676. if other_node.node_type == 'DRIVER':
  677. driver = other_node; break
  678. if isinstance(driver, UtilityDriver):
  679. prop_amount = driver.evaluate_input("Property")
  680. elif isinstance(driver, UtilitySwitch):
  681. xf=driver.GetxForm()
  682. prop_amount = xf.evaluate_input(driver.evaluate_input('Parameter'))
  683. else:
  684. return
  685. for template in self.socket_templates:
  686. if template.name == socket_name: break
  687. setattr(constraint, template.blender_property, prop_amount )
  688. def bPrepare(self, bContext = None,):
  689. from bpy import data
  690. if not bContext: # lol
  691. import bpy
  692. bContext = bpy.context
  693. ob = data.objects.get(self.evaluate_input("Name"))
  694. if not ob:
  695. ob = data.objects.new(self.evaluate_input("Name"), None)
  696. ob.lock_location = [True, True, True]
  697. ob.lock_rotation = [True, True, True]
  698. ob.lock_scale = [True, True, True]
  699. ob.lock_rotation_w = True
  700. ob.empty_display_type = 'CONE'
  701. ob.empty_display_size = 0.10
  702. self.bObject = ob
  703. self.bRegisterObject('OBJECT', self.bObject)
  704. reset_object_data(ob)
  705. node_line = trace_single_line(self, "Parent Curve")[0][1:] # slice excludes self
  706. for other_node in node_line:
  707. if other_node.node_type == 'XFORM':
  708. break
  709. else:
  710. raise GraphError(f"ERROR: {self} is not connected to a parent curve")
  711. if isinstance(other_node, (xFormArmature, xFormBone, xFormObjectInstance,)):
  712. raise GraphError(f"ERROR: {self} must be connected to curve,"
  713. " not {other_node.__class__.__name__}")
  714. curve=other_node.bGetObject()
  715. if curve.type != 'CURVE':
  716. raise GraphError(f"ERROR: {self} must be connected to curve,"
  717. " not {curve.type}")
  718. # we'll limit all the transforms so we can parent it
  719. # because it is annoying to have a cluttered outliner.
  720. #
  721. # always do this so that everything stays consistent.
  722. spline_index = self.evaluate_input("Spline Index")
  723. from .utilities.utilities import get_extracted_spline_object
  724. curve = get_extracted_spline_object(curve, spline_index, self.mContext)
  725. # Link to Scene:
  726. for link_me in [ob, curve]:
  727. if (link_me.name not in bContext.view_layer.active_layer_collection.collection.objects):
  728. bContext.view_layer.active_layer_collection.collection.objects.link(link_me)
  729. c = ob.constraints.new("LIMIT_LOCATION")
  730. for max_min in ['max','min']:
  731. for axis in "xyz":
  732. setattr(c, "use_"+max_min+"_"+axis, True)
  733. setattr(c, max_min+"_"+axis, 0.0)
  734. c = ob.constraints.new("LIMIT_ROTATION")
  735. for axis in "xyz":
  736. setattr(c, "use_limit_"+axis, True)
  737. setattr(c, max_min+"_"+axis, 0.0)
  738. c = ob.constraints.new("LIMIT_SCALE")
  739. for max_min in ['max','min']:
  740. for axis in "xyz":
  741. setattr(c, "use_"+max_min+"_"+axis, True)
  742. setattr(c, max_min+"_"+axis, 1.0)
  743. c = ob.constraints.new("FOLLOW_PATH")
  744. c.target = curve
  745. c.use_fixed_location = True
  746. c.use_curve_radius = True
  747. c.use_curve_follow = True
  748. c.name = "Curve Pin"
  749. props_sockets = self.gen_property_socket_map()
  750. constraint_props_sockets = props_sockets.copy()
  751. del constraint_props_sockets['name']; del constraint_props_sockets['empty_display_size']
  752. del props_sockets['offset_factor']; del props_sockets['forward_axis']
  753. del props_sockets['up_axis']
  754. evaluate_sockets(self, c, constraint_props_sockets)
  755. evaluate_sockets(self, self.bObject, props_sockets)
  756. # this isn't usually run on xForm nodes so for now I need to set the
  757. # driver's default values manually if I want a matrix now.
  758. # because the drivers may not have initialized yet.
  759. self.prep_driver_values(c)
  760. # now if all goes well... the matrix will be correct.
  761. dg = bContext.view_layer.depsgraph
  762. dg.update()
  763. # and the matrix should be correct now - copy because it may be modified
  764. self.parameters['Matrix'] = ob.matrix_world.copy()
  765. ob.parent=curve
  766. print( wrapGreen("Created Curve Pin: ") + wrapOrange(self.bObject.name) )
  767. parent_xForm_info = get_parent_xForm_info(self)
  768. root_armature = parent_xForm_info.root_armature
  769. my_info = xForm_info(
  770. object_type='object',
  771. root_armature= root_armature,
  772. parent_pose_name=curve.name,
  773. parent_edit_name=curve.name,
  774. self_pose_name=self.bObject.name,
  775. self_edit_name=self.bObject.name,
  776. )
  777. self.parameters['xForm Out'] = my_info
  778. self.prepared = True; self.executed = True
  779. def bFinalize(self, bContext = None):
  780. # custom properties
  781. self.setup_custom_props(self.bGetObject(), "Custom Properties")
  782. finish_drivers(self)
  783. def bGetObject(self, mode = 'POSE'):
  784. return self.bObject
  785. # special thank-you to Natalie Cuthbert, who submitted a patch for this
  786. # it turned out, I already had this patch laying around
  787. # because I was too busy with grad school I didn't succeed in collaborating
  788. # so while I apologize for failing to get your name in the commit history
  789. # now you get a special thank-you in Mantis instead!
  790. class xFormGetBone(xFormNode):
  791. """Represents an instance of an existing geometry object."""
  792. __slots__ = ()
  793. socket_templates = xFormGetBoneSockets
  794. def __init__(self, signature, base_tree):
  795. super().__init__(signature, base_tree, self.socket_templates)
  796. # this is just a getter
  797. self.prepared=True; self.executed=True; self.execution_prepared=True
  798. def bGetParentArmature(self):
  799. from bpy import data
  800. # the below function may return None, but data.objects.get expects a string
  801. return data.objects.get(self.evaluate_input("Parent Armature") or "")
  802. def bGetObject(self, mode = 'POSE'):
  803. bone = None
  804. self.bObject = self.evaluate_input("Bone")
  805. armature = self.bGetParentArmature()
  806. from bpy.types import Object
  807. assert armature is not None, f"{self} requires a parent armature input to operate."
  808. assert isinstance(armature, Object), f"{self}: The parent armature must be an armature object."
  809. if armature:
  810. assert armature.type == 'ARMATURE', f"{self}: The parent armature must be an armature object."
  811. match mode:
  812. case 'EDIT':
  813. bone = armature.data.edit_bones.get(self.evaluate_input("Bone"))
  814. case 'OBJECT':
  815. bone = armature.bones.get(self.evaluate_input("Bone"))
  816. case 'POSE':
  817. bone = armature.pose.bones.get(self.evaluate_input("Bone"))
  818. assert self.bObject is not None, f"{self} failed to get the desired bone. Check if the bone name exists."
  819. self.bRegisterObject('BONE', armature, bone)
  820. return bone