xForm_containers.py 41 KB

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