xForm_containers.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. from .node_container_common import *
  2. from .base_definitions import MantisNode, NodeSocket
  3. def TellClasses():
  4. return [
  5. # xForm
  6. xFormArmature,
  7. xFormBone,
  8. xFormGeometryObject,
  9. xFormObjectInstance,
  10. ]
  11. #*#-------------------------------#++#-------------------------------#*#
  12. # X - F O R M N O D E S
  13. #*#-------------------------------#++#-------------------------------#*#
  14. def reset_object_data(ob):
  15. # moving this to a common function so I can figure out the details later
  16. ob.constraints.clear()
  17. ob.animation_data_clear() # this is a little dangerous. TODO find a better solution since this can wipe animation the user wants to keep
  18. ob.modifiers.clear() # I would also like a way to copy modifiers and their settings, or bake them down. oh well
  19. def get_parent(node_container, type = 'XFORM'):
  20. # type variable for selecting whether to get either
  21. # the parent xForm or the inheritance node
  22. node_line, socket = trace_single_line(node_container, "Relationship")
  23. parent_nc = None
  24. for i in range(len(node_line)):
  25. # check each of the possible parent types.
  26. if ( (node_line[ i ].__class__.__name__ == 'LinkInherit') ):
  27. try: # it's the next one
  28. if (type == 'XFORM'):
  29. return node_line[ i + 1 ]
  30. else: # type = 'LINK'
  31. return node_line[ i ]
  32. except IndexError: # if there is no next one...
  33. return None # then there's no parent!
  34. return None
  35. class xFormArmature(MantisNode):
  36. '''A node representing an armature object'''
  37. bObject = None
  38. def __init__(self, signature, base_tree):
  39. super().__init__(signature, base_tree)
  40. inputs = [
  41. "Name" ,
  42. "Rotation Order" ,
  43. "Matrix" ,
  44. "Relationship" ,
  45. ]
  46. outputs = [
  47. "xForm Out",
  48. ]
  49. self.inputs.init_sockets(inputs)
  50. self.outputs.init_sockets(outputs)
  51. self.init_parameters()
  52. self.set_traverse([("Relationship", "xForm Out")])
  53. self.node_type = 'XFORM'
  54. def bPrepare(self, bContext=None):
  55. if not ( matrix := self.evaluate_input('Matrix')):
  56. raise RuntimeError(wrapRed(f"No matrix found for Armature {self}"))
  57. self.parameters['Matrix'] = matrix
  58. self.prepared = True
  59. def bExecute(self, bContext = None,):
  60. # from .utilities import get_node_prototype
  61. import bpy
  62. if (not isinstance(bContext, bpy.types.Context)):
  63. raise RuntimeError("Incorrect context")
  64. name = self.evaluate_input("Name")
  65. matrix = self.parameters['Matrix']
  66. reset_transforms = False
  67. #check if an object by the name exists
  68. if (name) and (ob := bpy.data.objects.get(name)):
  69. if (ob.animation_data):
  70. while (ob.animation_data.drivers):
  71. ob.animation_data.drivers.remove(ob.animation_data.drivers[-1])
  72. for pb in ob.pose.bones:
  73. # clear it, even after deleting the edit bones,
  74. # if we create them again the pose bones will be reused
  75. while (pb.constraints):
  76. pb.constraints.remove(pb.constraints[-1])
  77. if reset_transforms:
  78. pb.location = (0,0,0)
  79. pb.rotation_euler = (0,0,0)
  80. pb.rotation_quaternion = (1.0,0,0,0)
  81. pb.rotation_axis_angle = (0,0,1.0,0)
  82. pb.scale = (1.0,1.0,1.0)
  83. # feels ugly and bad, whatever
  84. collections = []
  85. for bc in ob.data.collections:
  86. collections.append(bc)
  87. for bc in collections:
  88. ob.data.collections.remove(bc)
  89. del collections
  90. # end ugly/bad
  91. else:
  92. # Create the Object
  93. ob = bpy.data.objects.new(name, bpy.data.armatures.new(name)) #create ob
  94. if (ob.name != name):
  95. raise RuntimeError("Could not create xForm object", name)
  96. self.bObject = ob.name
  97. ob.matrix_world = matrix.copy()
  98. ob.data.pose_position = 'REST'
  99. if True:
  100. from bpy.types import EditBone
  101. parent_nc = get_parent(self, type='LINK')
  102. if parent_nc:
  103. parent = parent_nc.inputs['Parent'].links[0].from_node.bGetObject(mode = 'OBJECT')
  104. ob.parent = parent
  105. # Link to Scene:
  106. if (ob.name not in bContext.view_layer.active_layer_collection.collection.objects):
  107. bContext.view_layer.active_layer_collection.collection.objects.link(ob)
  108. #self.bParent(bContext)
  109. print( wrapGreen("Created Armature object: ")+ wrapWhite(ob.name))
  110. # Finalize the action
  111. # oddly, overriding context doesn't seem to work
  112. try:
  113. bpy.ops.object.select_all(action='DESELECT')
  114. except RuntimeError:
  115. pass # we're already in edit mode, should be OK to do this.
  116. bContext.view_layer.objects.active = ob
  117. selected=[]
  118. for other_ob in bpy.data.objects:
  119. if other_ob.mode == "EDIT":
  120. selected.append(other_ob)
  121. selected.append(ob)
  122. context_override = {"active_object":ob, "selected_objects":selected}
  123. print("Changing Armature Mode to " +wrapPurple("EDIT"))
  124. with bContext.temp_override(**context_override):
  125. bpy.ops.object.mode_set(mode='EDIT')
  126. if ob.mode != "EDIT":
  127. prRed("eh?")
  128. # clear it
  129. while (len(ob.data.edit_bones) > 0):
  130. ob.data.edit_bones.remove(ob.data.edit_bones[0])
  131. # bContext.view_layer.objects.active = prevAct
  132. self.executed = True
  133. def bGetObject(self, mode = ''):
  134. import bpy; return bpy.data.objects[self.bObject]
  135. bone_inputs= [
  136. "Name",
  137. "Rotation Order",
  138. "Matrix",
  139. "Relationship",
  140. # IK settings
  141. "IK Stretch",
  142. "Lock IK",
  143. "IK Stiffness",
  144. "Limit IK",
  145. "X Min",
  146. "X Max",
  147. "Y Min",
  148. "Y Max",
  149. "Z Min",
  150. "Z Max",
  151. # Visual stuff
  152. "Bone Collection",
  153. "Hide",
  154. "Custom Object",
  155. "Custom Object xForm Override",
  156. "Custom Object Scale to Bone Length",
  157. "Custom Object Wireframe",
  158. "Custom Object Scale",
  159. "Custom Object Translation",
  160. "Custom Object Rotation",
  161. # Deform Stuff
  162. "Deform",
  163. "Envelope Distance",
  164. "Envelope Weight",
  165. "Envelope Multiply",
  166. "Envelope Head Radius",
  167. "Envelope Tail Radius",
  168. # BBone stuff:
  169. "BBone Segments",
  170. "BBone X Size",
  171. "BBone Z Size",
  172. "BBone HQ Deformation",
  173. "BBone X Curve-In",
  174. "BBone Z Curve-In",
  175. "BBone X Curve-Out",
  176. "BBone Z Curve-Out",
  177. "BBone Roll-In",
  178. "BBone Roll-Out",
  179. "BBone Inherit End Roll",
  180. "BBone Scale-In",
  181. "BBone Scale-Out",
  182. "BBone Ease-In",
  183. "BBone Ease-Out",
  184. "BBone Easing",
  185. "BBone Start Handle Type",
  186. "BBone Custom Start Handle",
  187. "BBone Start Handle Scale",
  188. "BBone Start Handle Ease",
  189. "BBone End Handle Type",
  190. "BBone Custom End Handle",
  191. "BBone End Handle Scale",
  192. "BBone End Handle Ease",
  193. # locks
  194. "Lock Location",
  195. "Lock Rotation",
  196. "Lock Scale",
  197. ]
  198. class xFormBone(MantisNode):
  199. '''A node representing a bone in an armature'''
  200. # DO: make a way to identify which armature this belongs to
  201. def __init__(self, signature, base_tree):
  202. super().__init__(signature, base_tree)
  203. outputs = [
  204. "xForm Out",
  205. ]
  206. self.inputs.init_sockets(bone_inputs)
  207. self.outputs.init_sockets(outputs)
  208. self.init_parameters()
  209. self.set_traverse([("Relationship", "xForm Out")])
  210. self.node_type = 'XFORM'
  211. self.prepared = True
  212. self.bObject=None
  213. def bGetParentArmature(self):
  214. finished = False
  215. if (trace := trace_single_line(self, "Relationship")[0] ) :
  216. for i in range(len(trace)):
  217. # have to look in reverse, actually TODO
  218. if ( isinstance(trace[ i ], xFormArmature ) ):
  219. return trace[ i ].bGetObject()
  220. return None
  221. #should do the trick...
  222. def bSetParent(self, eb):
  223. # print (self.bObject)
  224. from bpy.types import EditBone
  225. parent_nc = get_parent(self, type='LINK')
  226. # print (self, parent_nc.inputs['Parent'].from_node)
  227. parent=None
  228. if parent_nc.inputs['Parent'].links[0].from_node.node_type == 'XFORM':
  229. parent = parent_nc.inputs['Parent'].links[0].from_node.bGetObject(mode = 'EDIT')
  230. else:
  231. raise RuntimeError(wrapRed(f"Cannot set parent for node {self}"))
  232. if isinstance(parent, EditBone):
  233. eb.parent = parent
  234. #DUMMY
  235. # I NEED TO GET THE LINK NC
  236. # IDIOT
  237. eb.use_connect = parent_nc.evaluate_input("Connected")
  238. eb.use_inherit_rotation = parent_nc.evaluate_input("Inherit Rotation")
  239. eb.inherit_scale = parent_nc.evaluate_input("Inherit Scale")
  240. # otherwise, no need to do anything.
  241. def bPrepare(self, bContext=None):
  242. if not ( matrix := self.evaluate_input('Matrix')):
  243. raise RuntimeError(wrapRed(f"No matrix found for Bone {self}"))
  244. self.parameters['Matrix'] = matrix
  245. self.prepared = True
  246. def bExecute(self, bContext = None,): #possibly will need to pass context?
  247. import bpy
  248. from mathutils import Vector
  249. if not (name := self.evaluate_input("Name")):
  250. raise RuntimeError(wrapRed(f"Could not set name for bone in {self}"))
  251. if (not isinstance(bContext, bpy.types.Context)):
  252. raise RuntimeError("Incorrect context")
  253. if not (xF := self.bGetParentArmature()):
  254. raise RuntimeError("Could not create edit bone: ", name, " from node:", self.signature, " Reason: No armature object to add bone to.")
  255. matrix = self.parameters['Matrix']
  256. length = matrix[3][3]
  257. if (xF):
  258. if (xF.mode != "EDIT"):
  259. raise RuntimeError("Armature Object Not in Edit Mode, exiting...")
  260. #
  261. # Create the Object
  262. d = xF.data
  263. eb = d.edit_bones.new(name)
  264. # Bone Collections:
  265. # We treat each separate string as a Bone Collection that this object belongs to
  266. # Bone Collections are fully qualified by their hierarchy.
  267. # Separate Strings with "|" and indicate hierarchy with ">". These are special characters.
  268. # NOTE: if the user names the collections differently at different times, this will take the FIRST definition and go with it
  269. sCols = self.evaluate_input("Bone Collection")
  270. bone_collections = sCols.split("|")
  271. for collection_list in bone_collections:
  272. hierarchy = collection_list.split(">")
  273. col_parent = None
  274. for sCol in hierarchy:
  275. if ( col := d.collections.get(sCol) ) is None:
  276. col = d.collections.new(sCol)
  277. col.parent = col_parent
  278. col_parent = col
  279. col.assign(eb)
  280. if (eb.name != name):
  281. prRed(f"Expected bone of name: {name}, got {eb.name} instead.")
  282. raise RuntimeError("Could not create bone ", name, "; Perhaps there is a duplicate bone name in the node tree?")
  283. eb.matrix = matrix.copy()
  284. tailoffset = Vector((0,length,0)) #Vector((0,self.tailoffset, 0))
  285. tailoffset = matrix.copy().to_3x3() @ tailoffset
  286. eb.tail = eb.head + tailoffset
  287. if (eb.name != name):
  288. raise RuntimeError("Could not create edit bone: ", name)
  289. assert (eb.name), "Bone must have a name."
  290. self.bObject = eb.name
  291. # The bone should have relationships going in at this point.
  292. self.bSetParent(eb)
  293. if eb.head == eb.tail:
  294. raise RuntimeError(wrapRed(f"Could not create edit bone: {name} because bone head was located in the same place as bone tail."))
  295. # Setup Deform attributes...
  296. eb.use_deform = self.evaluate_input("Deform")
  297. eb.envelope_distance = self.evaluate_input("Envelope Distance")
  298. eb.envelope_weight = self.evaluate_input("Envelope Weight")
  299. eb.use_envelope_multiply = self.evaluate_input("Envelope Multiply")
  300. eb.head_radius = self.evaluate_input("Envelope Head Radius")
  301. eb.tail_radius = self.evaluate_input("Envelope Tail Radius")
  302. print( wrapGreen("Created Bone: ") + wrapOrange(eb.name) + wrapGreen(" in ") + wrapWhite(self.bGetParentArmature().name))
  303. self.executed = True
  304. def bFinalize(self, bContext = None):
  305. do_bb=False
  306. b = self.bGetParentArmature().data.bones[self.bObject]
  307. b.bbone_x = self.evaluate_input("BBone X Size"); b.bbone_x = max(b.bbone_x, 0.0002)
  308. b.bbone_z = self.evaluate_input("BBone Z Size"); b.bbone_z = max(b.bbone_z, 0.0002)
  309. if (segs := self.evaluate_input("BBone Segments")) > 1:
  310. do_bb=True
  311. b.bbone_segments = segs
  312. b.bbone_x = self.evaluate_input("BBone X Size")
  313. b.bbone_z = self.evaluate_input("BBone Z Size")
  314. if self.evaluate_input("BBone HQ Deformation"):
  315. b.bbone_mapping_mode = "CURVED"
  316. # 'bbone_handle_type_start' : ("BBone Start Handle Type", "AUTO"),
  317. # 'bbone_handle_type_end' : ("BBone End Handle Type", "AUTO"),
  318. # 'bbone_custom_handle_start' : ("BBone Custom Start Handle", "AUTO"),
  319. # 'bbone_custom_handle_end' : ("BBone Custom End Handle", "AUTO"),
  320. if handle_type := self.evaluate_input("BBone Start Handle Type"):
  321. b.bbone_handle_type_start = handle_type
  322. if handle_type := self.evaluate_input("BBone End Handle Type"):
  323. b.bbone_handle_type_end = handle_type
  324. try:
  325. if (custom_handle := self.evaluate_input("BBone Custom Start Handle")):
  326. b.bbone_custom_handle_start = self.bGetParentArmature().data.bones[custom_handle]
  327. # hypothetically we should support xForm inputs.... but we won't do that for now
  328. # elif custom_handle is None:
  329. # b.bbone_custom_handle_start = self.inputs["BBone Custom Start Handle"].links[0].from_node.bGetObject().name
  330. if (custom_handle := self.evaluate_input("BBone Custom End Handle")):
  331. b.bbone_custom_handle_end = self.bGetParentArmature().data.bones[custom_handle]
  332. except KeyError:
  333. prRed("Warning: BBone start or end handle not set because of missing bone in armature.")
  334. b.bbone_curveinx = self.evaluate_input("BBone X Curve-In")
  335. b.bbone_curveinz = self.evaluate_input("BBone Z Curve-In")
  336. b.bbone_curveoutx = self.evaluate_input("BBone X Curve-Out")
  337. b.bbone_curveoutz = self.evaluate_input("BBone Z Curve-Out")
  338. # 'bbone_curveinx' : ("BBone X Curve-In", pb.bone.bbone_curveinx),
  339. # 'bbone_curveinz' : ("BBone Z Curve-In", pb.bone.bbone_curveinz),
  340. # 'bbone_curveoutx' : ("BBone X Curve-Out", pb.bone.bbone_curveoutx),
  341. # 'bbone_curveoutz' : ("BBone Z Curve-Out", pb.bone.bbone_curveoutz),
  342. # TODO this section should be done with props-socket thing
  343. b.bbone_handle_use_scale_start = self.evaluate_input("BBone Start Handle Scale")
  344. b.bbone_handle_use_scale_end = self.evaluate_input("BBone End Handle Scale")
  345. import bpy
  346. from .drivers import MantisDriver
  347. # prevAct = bContext.view_layer.objects.active
  348. # bContext.view_layer.objects.active = ob
  349. # bpy.ops.object.mode_set(mode='OBJECT')
  350. # bContext.view_layer.objects.active = prevAct
  351. #
  352. #get relationship
  353. # ensure we have a pose bone...
  354. # set the ik parameters
  355. #
  356. #
  357. # Don't need to bother about whatever that was
  358. pb = self.bGetParentArmature().pose.bones[self.bObject]
  359. rotation_mode = self.evaluate_input("Rotation Order")
  360. if rotation_mode == "AUTO": rotation_mode = "XYZ"
  361. pb.rotation_mode = rotation_mode
  362. pb.id_properties_clear()
  363. # these are kept around unless explicitly deleted.
  364. # from .utilities import get_node_prototype
  365. # np = get_node_prototype(self.signature, self.base_tree)
  366. driver = None
  367. do_prints=False
  368. # detect custom inputs
  369. for i, inp in enumerate(self.inputs.values()):
  370. if inp.name in bone_inputs:
  371. continue
  372. name = inp.name
  373. try:
  374. value = self.evaluate_input(inp.name)
  375. except KeyError as e:
  376. trace = trace_single_line(self, inp.name)
  377. if do_prints: print(trace[0][-1], trace[1])
  378. if do_prints: print (trace[0][-1].parameters)
  379. raise e
  380. # This may be driven, so let's do this:
  381. if do_prints: print (value)
  382. if (isinstance(value, tuple)):
  383. # it's either a CombineThreeBool or a CombineVector.
  384. prRed("COMITTING SUICIDE NOW!!")
  385. bpy.ops.wm.quit_blender()
  386. if (isinstance(value, MantisDriver)):
  387. # the value should be the default for its socket...
  388. if do_prints: print (type(self.parameters[inp.name]))
  389. type_val_map = {
  390. str:"",
  391. bool:False,
  392. int:0,
  393. float:0.0,
  394. bpy.types.bpy_prop_array:(0,0,0),
  395. }
  396. driver = value
  397. value = type_val_map[type(self.parameters[inp.name])]
  398. if (value is None):
  399. prRed("This is probably not supposed to happen")
  400. value = 0
  401. raise RuntimeError("Could not set value of custom parameter")
  402. # it creates a more confusing error later sometimes, better to catch it here.
  403. # IMPORTANT: Is it possible for more than one driver to
  404. # come through here, and for the variable to be
  405. # overwritten?
  406. #TODO important
  407. #from rna_prop_ui import rna_idprop_ui_create
  408. # use this ^
  409. # add the custom properties to the **Pose Bone**
  410. pb[name] = value
  411. # This is much simpler now.
  412. ui_data = pb.id_properties_ui(name)
  413. description=''
  414. ui_data.update(
  415. description=description,#inp.description,
  416. default=value,)
  417. #if a number
  418. if type(value) == float:
  419. ui_data.update(
  420. min = inp.min,
  421. max = inp.max,
  422. soft_min = inp.soft_min,
  423. soft_max = inp.soft_max,)
  424. elif type(value) == int:
  425. ui_data.update(
  426. min = int(inp.min),
  427. max = int(inp.max),
  428. soft_min = int(inp.soft_min),
  429. soft_max = int(inp.soft_max),)
  430. elif type(value) == bool:
  431. ui_data.update() # TODO I can't figure out what the update function expects because it isn't documented
  432. if (pb.is_in_ik_chain):
  433. # this props_socket thing wasn't really meant to work here but it does, neat
  434. props_sockets = {
  435. 'ik_stretch' : ("IK Stretch", 0),
  436. 'lock_ik_x' : (("Lock IK", 0), False),
  437. 'lock_ik_y' : (("Lock IK", 1), False),
  438. 'lock_ik_z' : (("Lock IK", 2), False),
  439. 'ik_stiffness_x' : (("IK Stiffness", 0), 0.0),
  440. 'ik_stiffness_y' : (("IK Stiffness", 1), 0.0),
  441. 'ik_stiffness_z' : (("IK Stiffness", 2), 0.0),
  442. 'use_ik_limit_x' : (("Limit IK", 0), False),
  443. 'use_ik_limit_y' : (("Limit IK", 1), False),
  444. 'use_ik_limit_z' : (("Limit IK", 2), False),
  445. 'ik_min_x' : ("X Min", 0),
  446. 'ik_max_x' : ("X Max", 0),
  447. 'ik_min_y' : ("Y Min", 0),
  448. 'ik_max_y' : ("Y Max", 0),
  449. 'ik_min_z' : ("Z Min", 0),
  450. 'ik_max_z' : ("Z Max", 0),
  451. }
  452. evaluate_sockets(self, pb, props_sockets)
  453. if do_bb:
  454. props_sockets = {
  455. 'bbone_curveinx' : ("BBone X Curve-In", pb.bone.bbone_curveinx),
  456. 'bbone_curveinz' : ("BBone Z Curve-In", pb.bone.bbone_curveinz),
  457. 'bbone_curveoutx' : ("BBone X Curve-Out", pb.bone.bbone_curveoutx),
  458. 'bbone_curveoutz' : ("BBone Z Curve-Out", pb.bone.bbone_curveoutz),
  459. 'bbone_easein' : ("BBone Ease-In", 0),
  460. 'bbone_easeout' : ("BBone Ease-Out", 0),
  461. 'bbone_rollin' : ("BBone Roll-In", 0),
  462. 'bbone_rollout' : ("BBone Roll-Out", 0),
  463. 'bbone_scalein' : ("BBone Scale-In", (1,1,1)),
  464. 'bbone_scaleout' : ("BBone Scale-Out", (1,1,1)),
  465. }
  466. prRed("BBone Implementation is not complete, expect errors and missing features for now")
  467. evaluate_sockets(self, pb, props_sockets)
  468. # we need to clear this stuff since our only real goal was to get some drivers from the above
  469. for attr_name in props_sockets.keys():
  470. try:
  471. setattr(pb, attr_name, 0) # just clear it
  472. except ValueError:
  473. setattr(pb, attr_name, (1.0,1.0,1.0)) # scale needs to be set to 1
  474. # important TODO... all of the drivers and stuff should be handled this way, right?
  475. # time to set up drivers!
  476. # just gonna add this to the end and build off it I guess
  477. props_sockets = {
  478. "lock_location" : ("Lock Location", [False, False, False]),
  479. "lock_rotation" : ("Lock Rotation", [False, False, False]),
  480. "lock_scale" : ("Lock Scale", [False, False, False]),
  481. 'custom_shape_scale_xyz' : ("Custom Object Scale", (0.0,0.0,0.0) ),
  482. 'custom_shape_translation' : ("Custom Object Translation", (0.0,0.0,0.0) ),
  483. 'custom_shape_rotation_euler' : ("Custom Object Rotation", (0.0,0.0,0.0) ),
  484. 'use_custom_shape_bone_size' : ("Custom Object Scale to Bone Length", True,)
  485. }
  486. evaluate_sockets(self, pb, props_sockets)
  487. # this could probably be moved to bExecute
  488. props_sockets = {
  489. 'hide' : ("Hide", False),
  490. 'show_wire' : ("Custom Object Wireframe", False),
  491. }
  492. evaluate_sockets(self, pb.bone, props_sockets)
  493. if (driver):
  494. pass
  495. # whatever I was doing there.... was stupid. CLEAN UP TODO
  496. # this is the right thing to do.
  497. finish_drivers(self)
  498. #
  499. # OK, visual settings
  500. #
  501. # Get the override xform's bone:
  502. pb.custom_shape_transform = None
  503. pb.custom_shape = None
  504. if len(self.inputs["Custom Object xForm Override"].links) > 0:
  505. trace = trace_single_line(self, "Custom Object xForm Override")
  506. try:
  507. pb.custom_shape_transform = trace[0][1].bGetObject()
  508. except AttributeError:
  509. pass
  510. if len(self.inputs["Custom Object"].links) > 0:
  511. trace = trace_single_line(self, "Custom Object")
  512. try:
  513. ob = trace[0][1].bGetObject()
  514. except AttributeError:
  515. ob=None
  516. if type(ob) in [bpy.types.Object]:
  517. pb.custom_shape = ob
  518. def bGetObject(self, mode = 'POSE'):
  519. if self.bObject is None: return None
  520. if mode in ["POSE", "OBJECT"] and self.bGetParentArmature().mode == "EDIT":
  521. raise RuntimeError("Cannot get Bone or PoseBone in Edit mode.")
  522. elif mode == "EDIT" and self.bGetParentArmature().mode != "EDIT":
  523. raise RuntimeError("Cannot get EditBone except in Edit mode.")
  524. try:
  525. if (mode == 'EDIT'):
  526. return self.bGetParentArmature().data.edit_bones[self.bObject]
  527. elif (mode == 'OBJECT'):
  528. return self.bGetParentArmature().data.bones[self.bObject]
  529. elif (mode == 'POSE'):
  530. return self.bGetParentArmature().pose.bones[self.bObject]
  531. except Exception as e:
  532. prRed ("Cannot get bone for %s" % self)
  533. raise e
  534. def fill_parameters(self, prototype=None):
  535. # this is the fill_parameters that is run if it isn't a schema
  536. setup_custom_props(self)
  537. super().fill_parameters(prototype)
  538. # otherwise we will do this from the schema
  539. # LEGIBILITY TODO - why? explain this?
  540. class xFormGeometryObject(MantisNode):
  541. '''A node representing an armature object'''
  542. def __init__(self, signature, base_tree):
  543. super().__init__(signature, base_tree)
  544. inputs = [
  545. "Name" ,
  546. "Geometry" ,
  547. "Matrix" ,
  548. "Relationship" ,
  549. "Deformer" ,
  550. "Hide in Viewport" ,
  551. "Hide in Render" ,
  552. ]
  553. outputs = [
  554. "xForm Out",
  555. ]
  556. self.inputs.init_sockets(inputs)
  557. self.outputs.init_sockets(outputs)
  558. self.init_parameters()
  559. self.set_traverse([("Relationship", "xForm Out")])
  560. self.node_type = "XFORM"
  561. self.bObject = None
  562. self.has_shape_keys = False
  563. def bSetParent(self):
  564. from bpy.types import Object
  565. parent_nc = get_parent(self, type='LINK')
  566. if (parent_nc):
  567. parent = None
  568. if self.inputs["Relationship"].is_linked:
  569. trace = trace_single_line(self, "Relationship")
  570. for node in trace[0]:
  571. if node is self: continue # lol
  572. if (node.node_type == 'XFORM'):
  573. parent = node; break
  574. if parent is None:
  575. prWhite(f"INFO: no parent set for {self}.")
  576. return
  577. if (parent_object := parent.bGetObject()) is None:
  578. raise GraphError(f"Could not get parent object from node {parent} for {self}")
  579. if isinstance(parent, xFormBone):
  580. armOb= parent.bGetParentArmature()
  581. self.bObject.parent = armOb
  582. self.bObject.parent_type = 'BONE'
  583. self.bObject.parent_bone = parent.bObject
  584. # self.bObject.matrix_parent_inverse = parent.parameters["Matrix"].inverted()
  585. elif isinstance(parent_object, Object):
  586. self.bObject.parent = parent.bGetObject()
  587. def bPrepare(self, bContext = None,):
  588. import bpy
  589. if not self.evaluate_input("Name"):
  590. self.prepared = True
  591. self.executed = True
  592. # and return an error if there are any dependencies:
  593. if self.hierarchy_connections:
  594. raise GraphError(wrapRed(f"Cannot Generate object {self} because the chosen name is empty or invalid."))
  595. return
  596. self.bObject = bpy.data.objects.get(self.evaluate_input("Name"))
  597. trace = trace_single_line(self, "Geometry")
  598. if (not self.bObject):
  599. if trace[-1]:
  600. self.bObject = bpy.data.objects.new(self.evaluate_input("Name"), trace[-1].node.bGetObject())
  601. # handle mismatched data.
  602. data_wrong = False; data = None
  603. if (self.inputs["Geometry"].is_linked and self.bObject.type == "EMPTY"):
  604. data_wrong = True; data = trace[-1].node.bGetObject()
  605. elif (not self.inputs["Geometry"].is_linked and not self.bObject.type == "EMPTY"):
  606. data_wrong = True
  607. # clumsy but functional
  608. if data_wrong:
  609. unlink_me = self.bObject
  610. unlink_me.name = "MANTIS_TRASH.000"
  611. for col in unlink_me.users_collection:
  612. col.objects.unlink(unlink_me)
  613. self.bObject = bpy.data.objects.new(self.evaluate_input("Name"), data)
  614. if self.bObject and (self.inputs["Geometry"].is_linked and self.bObject.type in ["MESH", "CURVE"]):
  615. self.bObject.data = trace[-1].node.bGetObject()
  616. reset_object_data(self.bObject)
  617. if not ( matrix := self.evaluate_input('Matrix')):
  618. raise RuntimeError(wrapRed(f"No matrix found for Bone {self}"))
  619. self.parameters['Matrix'] = matrix
  620. self.matrix_world = matrix
  621. self.prepared = True
  622. def bExecute(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 bExecute 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. self.bSetParent()
  638. matrix = self.evaluate_input("Matrix")
  639. self.parameters['Matrix'] = matrix
  640. self.bObject.matrix_world = matrix
  641. for i, (driver_key, driver_item) in enumerate(self.drivers.items()):
  642. print (wrapGreen(i), wrapWhite(self), wrapPurple(driver_key))
  643. prOrange(driver_item)
  644. finish_drivers(self)
  645. def bGetObject(self, mode = 'POSE'):
  646. return self.bObject
  647. class xFormObjectInstance(MantisNode):
  648. """Represents an instance of an existing geometry object."""
  649. def __init__(self, signature, base_tree):
  650. super().__init__(signature, base_tree)
  651. inputs = [
  652. "Name" ,
  653. "Source Object" ,
  654. "As Instance" ,
  655. "Matrix" ,
  656. "Relationship" ,
  657. "Deformer" ,
  658. "Hide in Viewport" ,
  659. "Hide in Render" ,
  660. ]
  661. outputs = [
  662. "xForm Out",
  663. ]
  664. self.inputs.init_sockets(inputs)
  665. self.outputs.init_sockets(outputs)
  666. self.init_parameters()
  667. self.links = {} # leave this empty for now!
  668. # now set up the traverse target...
  669. self.set_traverse([("Relationship", "xForm Out")])
  670. self.node_type = "XFORM"
  671. self.bObject = None
  672. self.has_shape_keys = False # Shape Keys will make a dupe so this is OK
  673. def bSetParent(self):
  674. from bpy.types import Object
  675. parent_nc = get_parent(self, type='LINK')
  676. if (parent_nc):
  677. parent = None
  678. if self.inputs["Relationship"].is_linked:
  679. trace = trace_single_line(self, "Relationship")
  680. for node in trace[0]:
  681. if node is self: continue # lol
  682. if (node.node_type == 'XFORM'):
  683. parent = node; break
  684. if parent is None:
  685. prWhite(f"INFO: no parent set for {self}.")
  686. return
  687. if (parent_object := parent.bGetObject()) is None:
  688. raise GraphError(f"Could not get parent object from node {parent} for {self}")
  689. if isinstance(parent, xFormBone):
  690. armOb= parent.bGetParentArmature()
  691. self.bObject.parent = armOb
  692. self.bObject.parent_type = 'BONE'
  693. self.bObject.parent_bone = parent.bObject
  694. # self.bObject.matrix_parent_inverse = parent.parameters["Matrix"].inverted()
  695. elif isinstance(parent_object, Object):
  696. self.bObject.parent = parent.bGetObject()
  697. def bPrepare(self, bContext = None,):
  698. from bpy import data
  699. empty_mesh = data.meshes.get("MANTIS_EMPTY_MESH")
  700. if not empty_mesh:
  701. empty_mesh = data.meshes.new("MANTIS_EMPTY_MESH")
  702. if not self.evaluate_input("Name"):
  703. self.prepared = True
  704. self.executed = True
  705. # and return an error if there are any dependencies:
  706. if self.hierarchy_connections:
  707. raise GraphError(wrapRed(f"Cannot Generate object {self} because the chosen name is empty or invalid."))
  708. return
  709. self.bObject = data.objects.get(self.evaluate_input("Name"))
  710. if (not self.bObject):
  711. self.bObject = data.objects.new(self.evaluate_input("Name"), empty_mesh)
  712. reset_object_data(self.bObject)
  713. if not ( matrix := self.evaluate_input('Matrix')):
  714. raise RuntimeError(wrapRed(f"No matrix found for Bone {self}"))
  715. self.parameters['Matrix'] = matrix
  716. self.matrix_world = matrix
  717. self.prepared = True
  718. def bExecute(self, bContext = None,):
  719. try:
  720. bContext.collection.objects.link(self.bObject)
  721. except RuntimeError: #already in; but a dangerous thing to pass.
  722. pass
  723. self.has_shape_keys = False
  724. # putting this in bExecute simply prevents it from being run more than once.
  725. # maybe I should do that with the rest of bPrepare, too.
  726. props_sockets = {
  727. 'hide_viewport' : ("Hide in Viewport", False),
  728. 'hide_render' : ("Hide in Render", False),
  729. }
  730. evaluate_sockets(self, self.bObject, props_sockets)
  731. self.executed = True
  732. def bFinalize(self, bContext = None):
  733. # now we need to set the object instance up.
  734. from bpy import data
  735. trace = trace_single_line(self, "Source Object")
  736. for node in trace[0]:
  737. if node is self: continue # lol
  738. if (node.node_type == 'XFORM'):
  739. source_ob = node.bGetObject(); break
  740. modifier = self.bObject.modifiers.new("Object Instance", type='NODES')
  741. ng = data.node_groups.get("Object Instance")
  742. if ng is None:
  743. from .geometry_node_graphgen import gen_object_instance_node_group
  744. ng = gen_object_instance_node_group()
  745. modifier.node_group = ng
  746. modifier["Socket_0"] = source_ob
  747. modifier["Socket_1"] = self.evaluate_input("As Instance")
  748. self.bSetParent()
  749. matrix = self.evaluate_input("Matrix") # has to be done after parenting
  750. self.parameters['Matrix'] = matrix
  751. self.bObject.matrix_world = matrix
  752. for i, (driver_key, driver_item) in enumerate(self.drivers.items()):
  753. print (wrapGreen(i), wrapWhite(self), wrapPurple(driver_key))
  754. prOrange(driver_item)
  755. finish_drivers(self)
  756. def bGetObject(self, mode = 'POSE'):
  757. return self.bObject