xForm_nodes.py 43 KB

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