xForm_nodes.py 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. from .node_container_common import *
  2. from .base_definitions import MantisNode, NodeSocket
  3. from .xForm_socket_templates import *
  4. def TellClasses():
  5. return [
  6. # xForm
  7. xFormArmature,
  8. xFormBone,
  9. xFormGeometryObject,
  10. xFormObjectInstance,
  11. xFormCurvePin,
  12. ]
  13. #*#-------------------------------#++#-------------------------------#*#
  14. # X - F O R M N O D E S
  15. #*#-------------------------------#++#-------------------------------#*#
  16. def reset_object_data(ob):
  17. # moving this to a common function so I can figure out the details later
  18. ob.constraints.clear()
  19. ob.animation_data_clear() # this is a little dangerous. TODO find a better solution since this can wipe animation the user wants to keep
  20. ob.modifiers.clear() # I would also like a way to copy modifiers and their settings, or bake them down. oh well
  21. def get_parent_node(node_container, type = 'XFORM'):
  22. # type variable for selecting whether to get either
  23. # the parent xForm or the inheritance node
  24. node_line, socket = trace_single_line(node_container, "Relationship")
  25. parent_nc = None
  26. for i in range(len(node_line)):
  27. # check each of the possible parent types.
  28. if ( (node_line[ i ].__class__.__name__ == 'LinkInherit') ):
  29. try: # it's the next one
  30. if (type == 'XFORM'):
  31. return node_line[ i + 1 ]
  32. else: # type = 'LINK'
  33. return node_line[ i ]
  34. except IndexError: # if there is no next one...
  35. return None # then there's no parent!
  36. return None
  37. def get_matrix(node):
  38. matrix = node.evaluate_input('Matrix')
  39. if matrix is None:
  40. node_line, socket = trace_single_line(node, "Matrix")
  41. raise RuntimeError(wrapRed(f"No matrix found for Armature {node}"))
  42. return matrix
  43. def set_object_parent(node):
  44. parent_nc = get_parent_node(node, type='LINK')
  45. if (parent_nc):
  46. parent = None
  47. if node.inputs["Relationship"].is_linked:
  48. trace = trace_single_line(node, "Relationship")
  49. for other_node in trace[0]:
  50. if other_node is node: continue # lol
  51. if (other_node.node_type == 'XFORM'):
  52. parent = other_node; break
  53. if parent is None:
  54. prWhite(f"INFO: no parent set for {node}.")
  55. return
  56. prWhite(f"INFO: setting parent of {node} to {other_node}.")
  57. if (parent.bObject) is None:
  58. raise GraphError(f"Could not get parent object from node {parent} for {node}")
  59. if isinstance(parent, xFormBone):
  60. armOb= parent.bGetParentArmature()
  61. node.bObject.parent = armOb
  62. node.bObject.parent_type = 'BONE'
  63. node.bObject.parent_bone = parent.bObject
  64. else:
  65. node.bObject.parent = parent.bGetObject()
  66. class xFormNode(MantisNode):
  67. def __init__(self, signature, base_tree, socket_templates=[]):
  68. super().__init__(signature, base_tree, socket_templates)
  69. self.node_type = 'XFORM'
  70. self.bObject=None
  71. # because new objects are created during prep phase
  72. def reset_execution(self):
  73. super().reset_execution()
  74. self.prepared=False
  75. class xFormArmature(xFormNode):
  76. '''A node representing an armature object'''
  77. def __init__(self, signature, base_tree):
  78. super().__init__(signature, base_tree, xFormArmatureSockets)
  79. self.init_parameters()
  80. self.set_traverse([("Relationship", "xForm Out")])
  81. def bPrepare(self, bContext=None):
  82. self.parameters['Matrix'] = get_matrix(self)
  83. self.prepared = True
  84. def bTransformPass(self, bContext = None,):
  85. # from .utilities import get_node_prototype
  86. import bpy
  87. if (not isinstance(bContext, bpy.types.Context)):
  88. raise RuntimeError("Incorrect context")
  89. name = self.evaluate_input("Name")
  90. matrix = self.parameters['Matrix']
  91. reset_transforms = False
  92. #check if an object by the name exists
  93. if (name) and (ob := bpy.data.objects.get(name)):
  94. if (ob.animation_data):
  95. while (ob.animation_data.drivers):
  96. ob.animation_data.drivers.remove(ob.animation_data.drivers[-1])
  97. for pb in ob.pose.bones:
  98. # clear it, even after deleting the edit bones,
  99. # if we create them again the pose bones will be reused
  100. while (pb.constraints):
  101. pb.constraints.remove(pb.constraints[-1])
  102. if reset_transforms:
  103. pb.location = (0,0,0)
  104. pb.rotation_euler = (0,0,0)
  105. pb.rotation_quaternion = (1.0,0,0,0)
  106. pb.rotation_axis_angle = (0,0,1.0,0)
  107. pb.scale = (1.0,1.0,1.0)
  108. # feels ugly and bad, whatever
  109. collections = []
  110. for bc in ob.data.collections:
  111. collections.append(bc)
  112. for bc in collections:
  113. ob.data.collections.remove(bc)
  114. del collections
  115. # end ugly/bad
  116. else:
  117. # Create the Object
  118. ob = bpy.data.objects.new(name, bpy.data.armatures.new(name)) #create ob
  119. if (ob.name != name):
  120. raise RuntimeError("Could not create xForm object", name)
  121. self.bObject = ob.name
  122. ob.matrix_world = matrix.copy()
  123. ob.data.pose_position = 'REST'
  124. set_object_parent(self)
  125. # Link to Scene:
  126. if (ob.name not in bContext.view_layer.active_layer_collection.collection.objects):
  127. bContext.view_layer.active_layer_collection.collection.objects.link(ob)
  128. print( wrapGreen("Created Armature object: ")+ wrapWhite(ob.name))
  129. # Finalize the action
  130. # oddly, overriding context doesn't seem to work
  131. try:
  132. bpy.ops.object.select_all(action='DESELECT')
  133. except RuntimeError:
  134. pass # we're already in edit mode, should be OK to do this.
  135. bContext.view_layer.objects.active = ob
  136. selected=[]
  137. for other_ob in bpy.data.objects:
  138. if other_ob.mode == "EDIT":
  139. selected.append(other_ob)
  140. selected.append(ob)
  141. context_override = {"active_object":ob, "selected_objects":selected}
  142. print("Changing Armature Mode to " +wrapPurple("EDIT"))
  143. with bContext.temp_override(**context_override):
  144. bpy.ops.object.mode_set(mode='EDIT')
  145. if ob.mode != "EDIT":
  146. prRed("eh?")
  147. # clear it
  148. while (len(ob.data.edit_bones) > 0):
  149. ob.data.edit_bones.remove(ob.data.edit_bones[0])
  150. # bContext.view_layer.objects.active = prevAct
  151. self.executed = True
  152. def bGetObject(self, mode = ''):
  153. import bpy; return bpy.data.objects[self.bObject]
  154. bone_inputs= [
  155. "Name",
  156. "Rotation Order",
  157. "Matrix",
  158. "Relationship",
  159. # IK settings
  160. "IK Stretch",
  161. "Lock IK",
  162. "IK Stiffness",
  163. "Limit IK",
  164. "X Min",
  165. "X Max",
  166. "Y Min",
  167. "Y Max",
  168. "Z Min",
  169. "Z Max",
  170. # Visual stuff
  171. "Bone Collection",
  172. "Hide",
  173. "Custom Object",
  174. "Custom Object xForm Override",
  175. "Custom Object Scale to Bone Length",
  176. "Custom Object Wireframe",
  177. "Custom Object Scale",
  178. "Custom Object Translation",
  179. "Custom Object Rotation",
  180. "Color",
  181. "Inherit Color",
  182. # Deform Stuff
  183. "Deform",
  184. "Envelope Distance",
  185. "Envelope Weight",
  186. "Envelope Multiply",
  187. "Envelope Head Radius",
  188. "Envelope Tail Radius",
  189. # BBone stuff:
  190. "BBone Segments",
  191. "BBone X Size",
  192. "BBone Z Size",
  193. "BBone HQ Deformation",
  194. "BBone X Curve-In",
  195. "BBone Z Curve-In",
  196. "BBone X Curve-Out",
  197. "BBone Z Curve-Out",
  198. "BBone Roll-In",
  199. "BBone Roll-Out",
  200. "BBone Inherit End Roll",
  201. "BBone Scale-In",
  202. "BBone Scale-Out",
  203. "BBone Ease-In",
  204. "BBone Ease-Out",
  205. "BBone Easing",
  206. "BBone Start Handle Type",
  207. "BBone Custom Start Handle",
  208. "BBone Start Handle Scale",
  209. "BBone Start Handle Ease",
  210. "BBone End Handle Type",
  211. "BBone Custom End Handle",
  212. "BBone End Handle Scale",
  213. "BBone End Handle Ease",
  214. # locks
  215. "Lock Location",
  216. "Lock Rotation",
  217. "Lock Scale",
  218. ]
  219. class xFormBone(xFormNode):
  220. '''A node representing a bone in an armature'''
  221. # DO: make a way to identify which armature this belongs to
  222. def __init__(self, signature, base_tree):
  223. super().__init__(signature, base_tree)
  224. outputs = [
  225. "xForm Out",
  226. ]
  227. self.inputs.init_sockets(bone_inputs)
  228. self.outputs.init_sockets(outputs)
  229. self.socket_templates=xFormBoneSockets
  230. # TODO: implement socket templates completely for Bone
  231. # currently it is waiting on BBone and refactoring/cleanup.
  232. self.init_parameters()
  233. self.set_traverse([("Relationship", "xForm Out")])
  234. def bGetParentArmature(self):
  235. if (trace := trace_single_line(self, "Relationship")[0] ) :
  236. for i in range(len(trace)):
  237. # have to look in reverse, actually TODO
  238. if ( isinstance(trace[ i ], xFormArmature ) ):
  239. return trace[ i ].bGetObject()
  240. return None
  241. #should do the trick...
  242. def bSetParent(self, eb):
  243. # print (self.bObject)
  244. from bpy.types import EditBone
  245. parent_nc = get_parent_node(self, type='LINK')
  246. # print (self, parent_nc.inputs['Parent'].from_node)
  247. parent=None
  248. if parent_nc.inputs['Parent'].links[0].from_node.node_type == 'XFORM':
  249. parent = parent_nc.inputs['Parent'].links[0].from_node.bGetObject(mode = 'EDIT')
  250. else:
  251. raise RuntimeError(wrapRed(f"Cannot set parent for node {self}"))
  252. if isinstance(parent, EditBone):
  253. eb.parent = parent
  254. eb.use_connect = parent_nc.evaluate_input("Connected")
  255. eb.use_inherit_rotation = parent_nc.evaluate_input("Inherit Rotation")
  256. eb.inherit_scale = parent_nc.evaluate_input("Inherit Scale")
  257. # otherwise, no need to do anything.
  258. def bPrepare(self, bContext=None):
  259. self.parameters['Matrix'] = get_matrix(self)
  260. self.prepared = True
  261. def bTransformPass(self, bContext = None,): #possibly will need to pass context?
  262. import bpy
  263. from mathutils import Vector
  264. if not (name := self.evaluate_input("Name")):
  265. raise RuntimeError(wrapRed(f"Could not set name for bone in {self}"))
  266. if (not isinstance(bContext, bpy.types.Context)):
  267. raise RuntimeError("Incorrect context")
  268. if not (xF := self.bGetParentArmature()):
  269. raise RuntimeError("Could not create edit bone: ", name, " from node:", self, " Reason: No armature object to add bone to.")
  270. matrix = self.parameters['Matrix']
  271. length = matrix[3][3]
  272. if (xF):
  273. if (xF.mode != "EDIT"):
  274. raise RuntimeError("Armature Object Not in Edit Mode, exiting...")
  275. #
  276. # Create the Object
  277. d = xF.data
  278. eb = d.edit_bones.new(name)
  279. # Bone Collections:
  280. # We treat each separate string as a Bone Collection that this object belongs to
  281. # Bone Collections are fully qualified by their hierarchy.
  282. # Separate Strings with "|" and indicate hierarchy with ">". These are special characters.
  283. # NOTE: if the user names the collections differently at different times, this will take the FIRST definition and go with it
  284. if self.inputs['Bone Collection'].links:
  285. bCol_groups = []
  286. for i, l in enumerate(self.inputs['Bone Collection'].links):
  287. bCol_group = self.evaluate_input("Bone Collection", index=i)
  288. bCol_groups.append(bCol_group)
  289. bCols = '|'.join(bCol_groups)
  290. else:
  291. bCols = self.evaluate_input("Bone Collection")
  292. if bCols: # it is actually possible to add a bone to no collections. odd.
  293. bone_collections = bCols.split("|")
  294. for collection_list in bone_collections:
  295. hierarchy = collection_list.split(">")
  296. col_parent = None
  297. for bCol in hierarchy:
  298. if ( col := d.collections_all.get(bCol) ) is None:
  299. col = d.collections.new(bCol)
  300. col.parent = col_parent
  301. col_parent = col
  302. d.collections_all.get(hierarchy[-1]).assign(eb)
  303. if (eb.name != name):
  304. prRed(f"Expected bone of name: {name}, got {eb.name} instead.")
  305. raise RuntimeError("Could not create bone ", name, "; Perhaps there is a duplicate bone name in the node tree?")
  306. eb.matrix = matrix.copy()
  307. tailoffset = Vector((0,length,0)) #Vector((0,self.tailoffset, 0))
  308. tailoffset = matrix.copy().to_3x3() @ tailoffset
  309. eb.tail = eb.head + tailoffset
  310. if (eb.name != name):
  311. raise RuntimeError("Could not create edit bone: ", name)
  312. assert (eb.name), "Bone must have a name."
  313. self.bObject = eb.name
  314. # The bone should have relationships going in at this point.
  315. self.bSetParent(eb)
  316. if eb.head == eb.tail:
  317. raise RuntimeError(wrapRed(f"Could not create edit bone: {name} because bone head was located in the same place as bone tail."))
  318. # Setup Deform attributes...
  319. eb.use_deform = self.evaluate_input("Deform")
  320. eb.envelope_distance = self.evaluate_input("Envelope Distance")
  321. eb.envelope_weight = self.evaluate_input("Envelope Weight")
  322. eb.use_envelope_multiply = self.evaluate_input("Envelope Multiply")
  323. eb.head_radius = self.evaluate_input("Envelope Head Radius")
  324. eb.tail_radius = self.evaluate_input("Envelope Tail Radius")
  325. print( wrapGreen("Created Bone: ") + wrapOrange(eb.name) + wrapGreen(" in ") + wrapWhite(self.bGetParentArmature().name))
  326. self.executed = True
  327. def set_bone_color(self, b, inherit_color, bContext):
  328. color_values = self.evaluate_input('Color')
  329. if color_values is None:
  330. prOrange(f"Warning: No color information found for {b.name}. This should not happen.")
  331. return
  332. if inherit_color and b.parent:
  333. b.color.palette=b.parent.color.palette
  334. if b.color.palette == 'CUSTOM':
  335. b.color.custom.active=b.parent.color.custom.active
  336. b.color.custom.normal=b.parent.color.custom.normal
  337. b.color.custom.select=b.parent.color.custom.select
  338. return
  339. from mathutils import Color
  340. color_active = Color(color_values[:3])
  341. color_normal = Color(color_values[3:6])
  342. color_select = Color(color_values[6:])
  343. is_theme_colors = False
  344. theme = bContext.preferences.themes[0]
  345. for i, color_set in enumerate(theme.bone_color_sets):
  346. if ((color_active == color_set.active) and
  347. (color_normal == color_set.normal) and
  348. (color_select == color_set.select) ):
  349. is_theme_colors=True; break
  350. if is_theme_colors: # add 1, not 0-indexed
  351. b.color.palette = 'THEME'+str(i+1).zfill(2)
  352. elif ((color_active == theme.view_3d.bone_pose_active) and
  353. (color_normal == theme.view_3d.bone_solid) and
  354. (color_select == theme.view_3d.bone_pose) ):
  355. b.color.palette = 'DEFAULT'
  356. else:
  357. b.color.palette = 'CUSTOM'
  358. b.color.custom.active=color_active
  359. b.color.custom.normal=color_normal
  360. b.color.custom.select=color_select
  361. def bFinalize(self, bContext = None):
  362. b = self.bGetParentArmature().data.bones[self.bObject]
  363. # let's do bone colors first
  364. inherit_color = self.evaluate_input("Inherit Color")
  365. if len(self.inputs['Color'].links) > 0:
  366. inherit_color = False # use the link instead
  367. # try: # just in case, this shouldn't cause a failure
  368. self.set_bone_color(b, inherit_color, bContext)
  369. # except Exception as e:
  370. # prRed("WARNING: failed to set color because of error, see report below:")
  371. # prOrange(e)
  372. #
  373. do_bb=False
  374. b.bbone_x = self.evaluate_input("BBone X Size"); b.bbone_x = max(b.bbone_x, 0.0002)
  375. b.bbone_z = self.evaluate_input("BBone Z Size"); b.bbone_z = max(b.bbone_z, 0.0002)
  376. if (segs := self.evaluate_input("BBone Segments")) > 1:
  377. do_bb=True
  378. b.bbone_segments = segs
  379. b.bbone_x = self.evaluate_input("BBone X Size")
  380. b.bbone_z = self.evaluate_input("BBone Z Size")
  381. if self.evaluate_input("BBone HQ Deformation"):
  382. b.bbone_mapping_mode = "CURVED"
  383. # 'bbone_handle_type_start' : ("BBone Start Handle Type", "AUTO"),
  384. # 'bbone_handle_type_end' : ("BBone End Handle Type", "AUTO"),
  385. # 'bbone_custom_handle_start' : ("BBone Custom Start Handle", "AUTO"),
  386. # 'bbone_custom_handle_end' : ("BBone Custom End Handle", "AUTO"),
  387. if handle_type := self.evaluate_input("BBone Start Handle Type"):
  388. b.bbone_handle_type_start = handle_type
  389. if handle_type := self.evaluate_input("BBone End Handle Type"):
  390. b.bbone_handle_type_end = handle_type
  391. try:
  392. if (custom_handle := self.evaluate_input("BBone Custom Start Handle")):
  393. b.bbone_custom_handle_start = self.bGetParentArmature().data.bones[custom_handle]
  394. # hypothetically we should support xForm inputs.... but we won't do that for now
  395. # elif custom_handle is None:
  396. # b.bbone_custom_handle_start = self.inputs["BBone Custom Start Handle"].links[0].from_node.bGetObject().name
  397. if (custom_handle := self.evaluate_input("BBone Custom End Handle")):
  398. b.bbone_custom_handle_end = self.bGetParentArmature().data.bones[custom_handle]
  399. except KeyError:
  400. prRed("Warning: BBone start or end handle not set because of missing bone in armature.")
  401. bone_props_socket= {
  402. 'bbone_curveinx' : ("BBone X Curve-In", 0.0),
  403. 'bbone_curveinz' : ("BBone Z Curve-In", 0.0),
  404. 'bbone_curveoutx' : ("BBone X Curve-Out", 0.0),
  405. 'bbone_curveoutz' : ("BBone Z Curve-Out", 0.0),
  406. }
  407. evaluate_sockets(self, b, bone_props_socket)
  408. # TODO this section should be done with props-socket thing
  409. b.bbone_handle_use_scale_start = self.evaluate_input("BBone Start Handle Scale")
  410. b.bbone_handle_use_scale_end = self.evaluate_input("BBone End Handle Scale")
  411. import bpy
  412. from .drivers import MantisDriver
  413. # prevAct = bContext.view_layer.objects.active
  414. # bContext.view_layer.objects.active = ob
  415. # bpy.ops.object.mode_set(mode='OBJECT')
  416. # bContext.view_layer.objects.active = prevAct
  417. #
  418. #get relationship
  419. # ensure we have a pose bone...
  420. # set the ik parameters
  421. #
  422. #
  423. # Don't need to bother about whatever that was
  424. pb = self.bGetParentArmature().pose.bones[self.bObject]
  425. rotation_mode = self.evaluate_input("Rotation Order")
  426. if rotation_mode == "AUTO": rotation_mode = "XYZ"
  427. pb.rotation_mode = rotation_mode
  428. pb.id_properties_clear()
  429. # these are kept around unless explicitly deleted.
  430. # from .utilities import get_node_prototype
  431. # np = get_node_prototype(self.signature, self.base_tree)
  432. driver = None
  433. do_prints=False
  434. # detect custom inputs
  435. for i, inp in enumerate(self.inputs.values()):
  436. custom_prop=False
  437. for s_template in self.socket_templates:
  438. if s_template.name == inp.name:
  439. break
  440. else:
  441. custom_prop=True
  442. if custom_prop == False: continue
  443. name = inp.name
  444. try:
  445. value = self.evaluate_input(inp.name)
  446. except KeyError as e:
  447. trace = trace_single_line(self, inp.name)
  448. if do_prints: print(trace[0][-1], trace[1])
  449. if do_prints: print (trace[0][-1].parameters)
  450. raise e
  451. # This may be driven, so let's do this:
  452. if do_prints: print (value)
  453. if (isinstance(value, tuple)):
  454. raise RuntimeError(f"The custom property type is not supported: {self}")
  455. if (isinstance(value, MantisDriver)):
  456. # the value should be the default for its socket...
  457. if do_prints: print (type(self.parameters[inp.name]))
  458. type_val_map = {
  459. str:"",
  460. bool:False,
  461. int:0,
  462. float:0.0,
  463. bpy.types.bpy_prop_array:(0,0,0),
  464. }
  465. driver = value
  466. value = type_val_map[type(self.parameters[inp.name])]
  467. if (value is None):
  468. raise RuntimeError("Could not set value of custom parameter")
  469. # it creates a more confusing error later sometimes, better to catch it here.
  470. # IMPORTANT: Is it possible for more than one driver to
  471. # come through here, and for the variable to be
  472. # overwritten?
  473. #TODO important
  474. #from rna_prop_ui import rna_idprop_ui_create
  475. # use this ^
  476. # add the custom properties to the **Pose Bone**
  477. pb[name] = value
  478. # This is much simpler now.
  479. ui_data = pb.id_properties_ui(name)
  480. description=''
  481. ui_data.update(
  482. description=description,#inp.description,
  483. default=value,)
  484. #if a number
  485. if type(value) == float:
  486. ui_data.update(
  487. min = inp.min,
  488. max = inp.max,
  489. soft_min = inp.soft_min,
  490. soft_max = inp.soft_max,)
  491. elif type(value) == int:
  492. ui_data.update(
  493. min = int(inp.min),
  494. max = int(inp.max),
  495. soft_min = int(inp.soft_min),
  496. soft_max = int(inp.soft_max),)
  497. elif type(value) == bool:
  498. ui_data.update() # TODO I can't figure out what the update function expects because it isn't documented
  499. if (pb.is_in_ik_chain):
  500. # this props_socket thing wasn't really meant to work here but it does, neat
  501. props_sockets = {
  502. 'ik_stretch' : ("IK Stretch", 0),
  503. 'lock_ik_x' : (("Lock IK", 0), False),
  504. 'lock_ik_y' : (("Lock IK", 1), False),
  505. 'lock_ik_z' : (("Lock IK", 2), False),
  506. 'ik_stiffness_x' : (("IK Stiffness", 0), 0.0),
  507. 'ik_stiffness_y' : (("IK Stiffness", 1), 0.0),
  508. 'ik_stiffness_z' : (("IK Stiffness", 2), 0.0),
  509. 'use_ik_limit_x' : (("Limit IK", 0), False),
  510. 'use_ik_limit_y' : (("Limit IK", 1), False),
  511. 'use_ik_limit_z' : (("Limit IK", 2), False),
  512. 'ik_min_x' : ("X Min", 0),
  513. 'ik_max_x' : ("X Max", 0),
  514. 'ik_min_y' : ("Y Min", 0),
  515. 'ik_max_y' : ("Y Max", 0),
  516. 'ik_min_z' : ("Z Min", 0),
  517. 'ik_max_z' : ("Z Max", 0),
  518. }
  519. evaluate_sockets(self, pb, props_sockets)
  520. if do_bb:
  521. props_sockets = {
  522. 'bbone_curveinx' : ("BBone X Curve-In", pb.bone.bbone_curveinx),
  523. 'bbone_curveinz' : ("BBone Z Curve-In", pb.bone.bbone_curveinz),
  524. 'bbone_curveoutx' : ("BBone X Curve-Out", pb.bone.bbone_curveoutx),
  525. 'bbone_curveoutz' : ("BBone Z Curve-Out", pb.bone.bbone_curveoutz),
  526. 'bbone_easein' : ("BBone Ease-In", 0),
  527. 'bbone_easeout' : ("BBone Ease-Out", 0),
  528. 'bbone_rollin' : ("BBone Roll-In", 0),
  529. 'bbone_rollout' : ("BBone Roll-Out", 0),
  530. 'bbone_scalein' : ("BBone Scale-In", (1,1,1)),
  531. 'bbone_scaleout' : ("BBone Scale-Out", (1,1,1)),
  532. }
  533. evaluate_sockets(self, pb, props_sockets)
  534. # we need to clear this stuff since our only real goal was to get some drivers from the above
  535. for attr_name in props_sockets.keys():
  536. try:
  537. setattr(pb, attr_name, 0) # just clear it
  538. except ValueError:
  539. setattr(pb, attr_name, (1.0,1.0,1.0)) # scale needs to be set to 1
  540. # important TODO... all of the drivers and stuff should be handled this way, right?
  541. # time to set up drivers!
  542. # just gonna add this to the end and build off it I guess
  543. props_sockets = {
  544. "lock_location" : ("Lock Location", [False, False, False]),
  545. "lock_rotation" : ("Lock Rotation", [False, False, False]),
  546. "lock_scale" : ("Lock Scale", [False, False, False]),
  547. 'custom_shape_scale_xyz' : ("Custom Object Scale", (0.0,0.0,0.0) ),
  548. 'custom_shape_translation' : ("Custom Object Translation", (0.0,0.0,0.0) ),
  549. 'custom_shape_rotation_euler' : ("Custom Object Rotation", (0.0,0.0,0.0) ),
  550. 'use_custom_shape_bone_size' : ("Custom Object Scale to Bone Length", True,)
  551. }
  552. evaluate_sockets(self, pb, props_sockets)
  553. # this could probably be moved to bTransformPass
  554. props_sockets = {
  555. 'hide' : ("Hide", False),
  556. 'show_wire' : ("Custom Object Wireframe", False),
  557. }
  558. evaluate_sockets(self, pb.bone, props_sockets)
  559. if (driver):
  560. pass
  561. # whatever I was doing there.... was stupid. CLEAN UP TODO
  562. # this is the right thing to do.
  563. finish_drivers(self)
  564. #
  565. # OK, visual settings
  566. #
  567. # Get the override xform's bone:
  568. pb.custom_shape_transform = None
  569. pb.custom_shape = None
  570. if len(self.inputs["Custom Object xForm Override"].links) > 0:
  571. trace = trace_single_line(self, "Custom Object xForm Override")
  572. try:
  573. pb.custom_shape_transform = trace[0][1].bGetObject()
  574. except AttributeError:
  575. pass
  576. if len(self.inputs["Custom Object"].links) > 0:
  577. trace = trace_single_line(self, "Custom Object")
  578. try:
  579. ob = trace[0][1].bGetObject()
  580. except AttributeError:
  581. ob=None
  582. if type(ob) in [bpy.types.Object]:
  583. pb.custom_shape = ob
  584. def bGetObject(self, mode = 'POSE'):
  585. if self.bObject is None: return None
  586. if mode in ["POSE", "OBJECT"] and self.bGetParentArmature().mode == "EDIT":
  587. raise RuntimeError("Cannot get Bone or PoseBone in Edit mode.")
  588. elif mode == "EDIT" and self.bGetParentArmature().mode != "EDIT":
  589. raise RuntimeError("Cannot get EditBone except in Edit mode.")
  590. try:
  591. if (mode == 'EDIT'):
  592. return self.bGetParentArmature().data.edit_bones[self.bObject]
  593. elif (mode == 'OBJECT'):
  594. return self.bGetParentArmature().data.bones[self.bObject]
  595. elif (mode == 'POSE'):
  596. return self.bGetParentArmature().pose.bones[self.bObject]
  597. except Exception as e:
  598. prRed ("Cannot get bone for %s" % self)
  599. raise e
  600. def fill_parameters(self, prototype=None):
  601. # this is the fill_parameters that is run if it isn't a schema
  602. setup_custom_props(self)
  603. super().fill_parameters(prototype)
  604. # otherwise we will do this from the schema
  605. # LEGIBILITY TODO - why? explain this?
  606. class xFormGeometryObject(xFormNode):
  607. '''A node representing an armature object'''
  608. def __init__(self, signature, base_tree):
  609. super().__init__(signature, base_tree, xFormGeometryObjectSockets)
  610. self.init_parameters()
  611. self.set_traverse([("Relationship", "xForm Out")])
  612. self.has_shape_keys = False
  613. def bPrepare(self, bContext = None,):
  614. import bpy
  615. if not self.evaluate_input("Name"):
  616. self.prepared = True
  617. self.executed = True
  618. # and return an error if there are any dependencies:
  619. if self.hierarchy_connections:
  620. raise GraphError(wrapRed(f"Cannot Generate object {self} because the chosen name is empty or invalid."))
  621. return
  622. self.bObject = bpy.data.objects.get(self.evaluate_input("Name"))
  623. trace = trace_single_line(self, "Geometry")
  624. if (not self.bObject):
  625. if trace[-1]:
  626. self.bObject = bpy.data.objects.new(self.evaluate_input("Name"), trace[-1].node.bGetObject())
  627. # handle mismatched data.
  628. data_wrong = False; data = None
  629. if (self.inputs["Geometry"].is_linked and self.bObject.type == "EMPTY"):
  630. data_wrong = True; data = trace[-1].node.bGetObject()
  631. elif (not self.inputs["Geometry"].is_linked and not self.bObject.type == "EMPTY"):
  632. data_wrong = True
  633. # clumsy but functional
  634. if data_wrong:
  635. unlink_me = self.bObject
  636. unlink_me.name = "MANTIS_TRASH.000"
  637. for col in unlink_me.users_collection:
  638. col.objects.unlink(unlink_me)
  639. self.bObject = bpy.data.objects.new(self.evaluate_input("Name"), data)
  640. if self.bObject and (self.inputs["Geometry"].is_linked and self.bObject.type in ["MESH", "CURVE"]):
  641. self.bObject.data = trace[-1].node.bGetObject()
  642. # NOW: find out if we need to duplicate the object data.
  643. dupe_data=False
  644. node_line = trace_single_line(self, "Deformer")[0]
  645. from .deformer_nodes import DeformerHook
  646. for deformer in node_line:
  647. if isinstance(deformer, DeformerHook) and \
  648. deformer.evaluate_input("Affect Curve Radius") == True and \
  649. self.bObject.type == 'CURVE':
  650. print(f"INFO: Duplicating data {self.bObject.data.name} in {self} so it can be used for drivers.")
  651. dupe_data=True; break
  652. if dupe_data:
  653. name = self.bObject.data.name
  654. # it has to be a curve
  655. data = bpy.data.curves.get(self.bObject.data.name+"_MANTIS")
  656. if data: # Delete it and regenerate it if it exists.
  657. data.name+="_TRASH.000" # but we can't actually delete it here
  658. # since previous executions of the graph may be using it.
  659. # instead, we'll rename it and use a new copy. This will probably
  660. # be deleted on its own by Blender's garbage collector.
  661. # if it isn't, then it is still in use and I may NOT touch it.
  662. data=self.bObject.data.copy(); data.animation_data_clear()
  663. data.name = name+"_MANTIS"
  664. self.bObject.data = data
  665. reset_object_data(self.bObject)
  666. matrix= get_matrix(self)
  667. self.parameters['Matrix'] = matrix
  668. try:
  669. set_object_parent(self)
  670. except: # I guess it isn't ready yet. we'll do it later
  671. pass # (This can happen when solving schema.)
  672. self.bObject.matrix_world = matrix
  673. self.prepared = True
  674. def bTransformPass(self, bContext = None,):
  675. try:
  676. bContext.collection.objects.link(self.bObject)
  677. except RuntimeError: #already in; but a dangerous thing to pass.
  678. pass
  679. self.has_shape_keys = False
  680. # putting this in bTransformPass simply prevents it from being run more than once.
  681. # maybe I should do that with the rest of bPrepare, too.
  682. props_sockets = {
  683. 'hide_viewport' : ("Hide in Viewport", False),
  684. 'hide_render' : ("Hide in Render", False),
  685. }
  686. evaluate_sockets(self, self.bObject, props_sockets)
  687. self.executed = True
  688. def bFinalize(self, bContext = None):
  689. matrix= get_matrix(self)
  690. set_object_parent(self)
  691. self.bObject.matrix_world = matrix
  692. for i, (driver_key, driver_item) in enumerate(self.drivers.items()):
  693. print (wrapGreen(i), wrapWhite(self), wrapPurple(driver_key))
  694. prOrange(driver_item)
  695. finish_drivers(self)
  696. def bGetObject(self, mode = 'POSE'):
  697. return self.bObject
  698. class xFormObjectInstance(xFormNode):
  699. """Represents an instance of an existing geometry object."""
  700. def __init__(self, signature, base_tree):
  701. super().__init__(signature, base_tree, xFormGeometryObjectInstanceSockets)
  702. self.init_parameters()
  703. # TODO: I think this field is a leftover from a test or something. see if it can be removed.
  704. self.links = {} # leave this empty for now!
  705. self.set_traverse([("Relationship", "xForm Out")])
  706. self.has_shape_keys = False # Shape Keys will make a dupe so this is OK
  707. def ui_modify_socket(self, ui_socket, socket_name=None):
  708. if ui_socket.name == 'As Instance':
  709. change_handled = True
  710. try:
  711. self.bObject.modifiers[0]['Socket_1'] = ui_socket.default_value
  712. except Exception as e:
  713. print("Failed to update mantis socket because of %s" % e,
  714. "Updating tree instead.")
  715. return change_handled
  716. else:
  717. return super().ui_modify_socket(ui_socket, socket_name)
  718. def bPrepare(self, bContext = None,):
  719. from bpy import data
  720. empty_mesh = data.meshes.get("MANTIS_EMPTY_MESH")
  721. if not empty_mesh:
  722. empty_mesh = data.meshes.new("MANTIS_EMPTY_MESH")
  723. if not self.evaluate_input("Name"):
  724. self.prepared = True
  725. self.executed = True
  726. # and return an error if there are any dependencies:
  727. if self.hierarchy_connections:
  728. raise GraphError(wrapRed(f"Cannot Generate object {self} because the chosen name is empty or invalid."))
  729. return
  730. self.bObject = data.objects.get(self.evaluate_input("Name"))
  731. if (not self.bObject):
  732. self.bObject = data.objects.new(self.evaluate_input("Name"), empty_mesh)
  733. reset_object_data(self.bObject)
  734. matrix= get_matrix(self)
  735. self.parameters['Matrix'] = matrix
  736. set_object_parent(self)
  737. self.bObject.matrix_world = matrix
  738. self.prepared = True
  739. def bTransformPass(self, bContext = None,):
  740. try:
  741. bContext.collection.objects.link(self.bObject)
  742. except RuntimeError: #already in; but a dangerous thing to pass.
  743. pass
  744. self.has_shape_keys = False
  745. # putting this in bTransformPass simply prevents it from being run more than once.
  746. # maybe I should do that with the rest of bPrepare, too.
  747. props_sockets = {
  748. 'hide_viewport' : ("Hide in Viewport", False),
  749. 'hide_render' : ("Hide in Render", False),
  750. }
  751. evaluate_sockets(self, self.bObject, props_sockets)
  752. self.executed = True
  753. def bFinalize(self, bContext = None):
  754. # now we need to set the object instance up.
  755. from bpy import data
  756. trace = trace_single_line(self, "Source Object")
  757. for node in trace[0]:
  758. if node is self: continue # lol
  759. if (node.node_type == 'XFORM'):
  760. source_ob = node.bGetObject(); break
  761. modifier = self.bObject.modifiers.new("Object Instance", type='NODES')
  762. ng = data.node_groups.get("Object Instance")
  763. if ng is None:
  764. from .geometry_node_graphgen import gen_object_instance_node_group
  765. ng = gen_object_instance_node_group()
  766. modifier.node_group = ng
  767. modifier["Socket_0"] = source_ob
  768. modifier["Socket_1"] = self.evaluate_input("As Instance")
  769. for i, (driver_key, driver_item) in enumerate(self.drivers.items()):
  770. print (wrapGreen(i), wrapWhite(self), wrapPurple(driver_key))
  771. prOrange(driver_item)
  772. finish_drivers(self)
  773. def bGetObject(self, mode = 'POSE'):
  774. return self.bObject
  775. class xFormCurvePin(xFormNode):
  776. """An xForm pinned to a specific location on a curve."""
  777. def __init__(self, signature, base_tree):
  778. super().__init__(signature, base_tree,xFormCurvePinSockets)
  779. self.init_parameters(additional_parameters={"Matrix":None})
  780. def prep_driver_values(self, constraint):
  781. from .misc_nodes import UtilityDriver, UtilitySwitch
  782. for socket_name in ["Curve Pin Factor", "Forward Axis","Up Axis",]:
  783. if self.inputs.get(socket_name) is None: continue # in case it has been bypassed
  784. if self.inputs[socket_name].is_linked:
  785. link = self.inputs[socket_name].links[0]
  786. driver = link.from_node
  787. if isinstance(driver, UtilityDriver):
  788. prop_amount = driver.evaluate_input("Property")
  789. elif isinstance(driver, UtilitySwitch):
  790. xf=driver.GetxForm()
  791. prop_amount = xf.evaluate_input(driver.evaluate_input('Parameter'))
  792. else:
  793. return
  794. for template in self.socket_templates:
  795. if template.name == socket_name: break
  796. setattr(constraint, template.blender_property, prop_amount )
  797. def bPrepare(self, bContext = None,):
  798. from bpy import data
  799. if not bContext: # lol
  800. import bpy
  801. bContext = bpy.context
  802. ob = data.objects.get(self.evaluate_input("Name"))
  803. if not ob:
  804. ob = data.objects.new(self.evaluate_input("Name"), None)
  805. ob.lock_location = [True, True, True]
  806. ob.lock_rotation = [True, True, True]
  807. ob.lock_scale = [True, True, True]
  808. ob.lock_rotation_w = True
  809. ob.empty_display_type = 'CONE'
  810. ob.empty_display_size = 0.10
  811. self.bObject = ob
  812. reset_object_data(ob)
  813. node_line = trace_single_line(self, "Parent Curve")[0][1:] # slice excludes self
  814. for other_node in node_line:
  815. if other_node.node_type == 'XFORM':
  816. break
  817. else:
  818. raise GraphError(f"ERROR: {self} is not connected to a parent curve")
  819. if isinstance(other_node, (xFormArmature, xFormBone, xFormObjectInstance,)):
  820. raise GraphError(f"ERROR: {self} must be connected to curve,"
  821. " not {other_node.__class__.__name__}")
  822. curve=other_node.bGetObject()
  823. if curve.type != 'CURVE':
  824. raise GraphError(f"ERROR: {self} must be connected to curve,"
  825. " not {curve.type}")
  826. # we'll limit all the transforms so we can parent it
  827. # because it is annoying to have a cluttered outliner.
  828. #
  829. # always do this so that everything stays consistent.
  830. spline_index = self.evaluate_input("Spline Index")
  831. from .utilities import get_extracted_spline_object
  832. curve = get_extracted_spline_object(curve, spline_index, self.mContext)
  833. # Link to Scene:
  834. for link_me in [ob, curve]:
  835. if (link_me.name not in bContext.view_layer.active_layer_collection.collection.objects):
  836. bContext.view_layer.active_layer_collection.collection.objects.link(link_me)
  837. c = ob.constraints.new("LIMIT_LOCATION")
  838. for max_min in ['max','min']:
  839. for axis in "xyz":
  840. setattr(c, "use_"+max_min+"_"+axis, True)
  841. setattr(c, max_min+"_"+axis, 0.0)
  842. c = ob.constraints.new("LIMIT_ROTATION")
  843. for axis in "xyz":
  844. setattr(c, "use_limit_"+axis, True)
  845. setattr(c, max_min+"_"+axis, 0.0)
  846. c = ob.constraints.new("LIMIT_SCALE")
  847. for max_min in ['max','min']:
  848. for axis in "xyz":
  849. setattr(c, "use_"+max_min+"_"+axis, True)
  850. setattr(c, max_min+"_"+axis, 1.0)
  851. c = ob.constraints.new("FOLLOW_PATH")
  852. c.target = curve
  853. c.use_fixed_location = True
  854. c.use_curve_radius = True
  855. c.use_curve_follow = True
  856. c.name = "Curve Pin"
  857. props_sockets = self.gen_property_socket_map()
  858. constraint_props_sockets = props_sockets.copy()
  859. del constraint_props_sockets['name']; del constraint_props_sockets['empty_display_size']
  860. del props_sockets['offset_factor']; del props_sockets['forward_axis']
  861. del props_sockets['up_axis']
  862. evaluate_sockets(self, c, constraint_props_sockets)
  863. evaluate_sockets(self, self.bObject, props_sockets)
  864. # this isn't usually run on xForm nodes so for now I need to set the
  865. # driver's default values manually if I want a matrix now.
  866. # because the drivers may not have initialized yet.
  867. self.prep_driver_values(c)
  868. # now if all goes well... the matrix will be correct.
  869. dg = bContext.view_layer.depsgraph
  870. dg.update()
  871. # and the matrix should be correct now - copy because it may be modified
  872. self.parameters['Matrix'] = ob.matrix_world.copy()
  873. ob.parent=curve
  874. print( wrapGreen("Created Curve Pin: ") + wrapOrange(self.bObject.name) )
  875. self.prepared = True; self.executed = True
  876. def bFinalize(self, bContext = None):
  877. finish_drivers(self)
  878. def bGetObject(self, mode = 'POSE'):
  879. return self.bObject