Created
January 29, 2020 04:45
-
-
Save nathanielanozie/593b5d999a362534afcc145a5508cd4d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#naPropRigAddOn.py | |
#modify use at your own risk | |
bl_info = { | |
"name":"prop rigging tool", | |
"description":"tool to build a simple prop rig in blender", | |
"category": "Object", | |
"author":"Nathaniel Anozie", | |
"blender":(2,79,0) | |
} | |
import bpy | |
from bpy.types import( | |
Operator, | |
Panel | |
) | |
class propRigOperator(Operator): | |
"""first select single mesh for prop in object mode. | |
""" | |
bl_idname = "obj.buildproprig" #needs to be all lowercase | |
bl_label = "build propRig" | |
bl_options = {"REGISTER"} | |
def execute(self, context): | |
naBuildRig(context) | |
return {'FINISHED'} | |
class propRigPanel(Panel): | |
bl_label = "propRig Panel" | |
bl_space_type = "PROPERTIES" | |
bl_region_type = "WINDOW" | |
def draw(self, context): | |
layout = self.layout | |
layout.label(text = "first select single mesh geo of prop") | |
layout.operator( "obj.buildproprig") | |
def register(): | |
bpy.utils.register_class(propRigOperator) | |
bpy.utils.register_class(propRigPanel) | |
def unregister(): | |
bpy.utils.unregister_class(propRigOperator) | |
bpy.utils.unregister_class(propRigPanel) | |
if __name__ == "__main__": | |
register() | |
##actual procs going here | |
#for widgets | |
squareShapeVertsEdges = ([(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (-1.0, 1.0, 0.0), (1.0, 1.0, 0.0)], [(2, 0), (0, 1), (1, 3), (3, 2)]) | |
cubeShapeVertsEdges = ([(-1.0, -1.0, -1.0), (-1.0, -1.0, 1.0), (-1.0, 1.0, -1.0), (-1.0, 1.0, 1.0), (1.0, -1.0, -1.0), (1.0, -1.0, 1.0), (1.0, 1.0, -1.0), (1.0, 1.0, 1.0)], [(2, 0), (0, 1), (1, 3), (3, 2), (6, 2), (3, 7), (7, 6), (4, 6), (7, 5), (5, 4), (0, 4), (5, 1)]) | |
circleShapeVertsEdges = ([(0.0, 1.0, 0.0), (-0.1951, 0.9808, 0.0), (-0.3827, 0.9239, 0.0), (-0.5556, 0.8315, 0.0), (-0.7071, 0.7071, 0.0), (-0.8315, 0.5556, 0.0), (-0.9239, 0.3827, 0.0), (-0.9808, 0.1951, 0.0), (-1.0, 0.0, 0.0), (-0.9808, -0.1951, 0.0), (-0.9239, -0.3827, 0.0), (-0.8315, -0.5556, 0.0), (-0.7071, -0.7071, 0.0), (-0.5556, -0.8315, 0.0), (-0.3827, -0.9239, 0.0), (-0.1951, -0.9808, 0.0), (0.0, -1.0, 0.0), (0.1951, -0.9808, 0.0), (0.3827, -0.9239, 0.0), (0.5556, -0.8315, 0.0), (0.7071, -0.7071, 0.0), (0.8315, -0.5556, 0.0), (0.9239, -0.3827, 0.0), (0.9808, -0.1951, 0.0), (1.0, 0.0, 0.0), (0.9808, 0.1951, 0.0), (0.9239, 0.3827, 0.0), (0.8315, 0.5556, 0.0), (0.7071, 0.7071, 0.0), (0.5556, 0.8315, 0.0), (0.3827, 0.9239, 0.0), (0.1951, 0.9808, 0.0)], [(1, 0), (2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 6), (8, 7), (9, 8), (10, 9), (11, 10), (12, 11), (13, 12), (14, 13), (15, 14), (16, 15), (17, 16), (18, 17), (19, 18), (20, 19), (21, 20), (22, 21), (23, 22), (24, 23), (25, 24), (26, 25), (27, 26), (28, 27), (29, 28), (30, 29), (31, 30), (0, 31)]) | |
def naBuildRig(context): | |
"""build simple prop rig with 3 animator controls. | |
requires single mesh selected in object mode | |
""" | |
#if armature doesnt exist make it | |
print('makeArmature') | |
sel = context.selected_objects #context.object doesnt work for addon | |
if not sel: | |
print("requires a mesh selected in object mode to prop") | |
return | |
propObj = sel[0] | |
if (propObj is None) or (propObj.type != "MESH"): | |
print("requires a mesh selected in object mode to prop") | |
return | |
propName = propObj.name#"Cube" #Change all the hard coded names later | |
name = "%s_armature" %(propName) | |
###Making armature | |
if name in bpy.data.objects: | |
print('armature already created %s skipping' %(name)) | |
return bpy.data.objects[name] | |
arm_dat = None | |
if name in bpy.data.armatures: | |
print('armatures with name already created %s removing it' %(name)) | |
armature = bpy.data.armatures[name] | |
bpy.data.armatures.remove(armature) | |
arm_dat = bpy.data.armatures.new(name) | |
arm_obj = bpy.data.objects.new(name,arm_dat) | |
arm_obj.data = arm_dat | |
scene = context.scene | |
scene.objects.link(arm_obj) | |
scene.objects.active = arm_obj #need to select armature to put it in edit mode | |
scene.update() | |
#### | |
###Make bones in armature | |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) | |
rootBoneName = "%s_root_anim" %(propName) | |
rootBone = bpy.data.armatures[name].edit_bones.new(rootBoneName) | |
rootBone.head = (0,0,0) | |
rootBone.tail = (0,1,0) | |
midPosZ = propObj.dimensions.z/2 #midPosZ = 5 #get from dimensions of prop geo | |
midABoneName = "%s_mida_anim" %(propName) | |
midABone = bpy.data.armatures[name].edit_bones.new(midABoneName) | |
midABone.head = (0,0,midPosZ) | |
midABone.tail = (0,1,midPosZ) | |
midBBoneName = "%s_midb_anim" %(propName) | |
midBBone = bpy.data.armatures[name].edit_bones.new(midBBoneName) | |
midBBone.head = (0,0,midPosZ) | |
midBBone.tail = (0,1,midPosZ) | |
##handle parenting of bones | |
print("doing parenting") | |
context.scene.objects.active = arm_obj | |
bpy.ops.object.mode_set(mode='EDIT', toggle=False) | |
if arm_obj == context.active_object and context.mode == "EDIT_ARMATURE": | |
print("parenting bone >>") | |
arm_obj.data.edit_bones[midBBoneName].parent = arm_obj.data.edit_bones[midABoneName] | |
arm_obj.data.edit_bones[midABoneName].parent = arm_obj.data.edit_bones[rootBoneName] | |
print("finished parenting bone") | |
bpy.ops.object.mode_set(mode='OBJECT') | |
##make widgets for bones | |
rootWidgetName = "%s_root_wdgt" %("naProp") #so all prop rigs use same widgets | |
midAWidgetName = "%s_mida_wdgt" %("naProp") | |
midBWidgetName = "%s_midb_wdgt" %("naProp") | |
if not rootWidgetName in bpy.data.objects: | |
rootWidgetShape = drawShape( context, name=rootWidgetName, shapeVertsEdges=cubeShapeVertsEdges) | |
else: | |
rootWidgetShape = bpy.data.objects[rootWidgetName] | |
#z scaling of root widget | |
rootWidgetShape.scale.z = 0.1 | |
rootWidgetShape.select = True | |
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) | |
if not midAWidgetName in bpy.data.objects: | |
midAWidgetShape = drawShape( context, name=midAWidgetName, shapeVertsEdges=squareShapeVertsEdges) | |
else: | |
midAWidgetShape = bpy.data.objects[midAWidgetName] | |
if not midBWidgetName in bpy.data.objects: | |
midBWidgetShape = drawShape( context, name=midBWidgetName, shapeVertsEdges=circleShapeVertsEdges) | |
else: | |
midBWidgetShape = bpy.data.objects[midBWidgetName] | |
#put widgets in last layer so they are not seen | |
rootWidgetShape.layers = [False]*19+[True] | |
midAWidgetShape.layers = [False]*19+[True] | |
midBWidgetShape.layers = [False]*19+[True] | |
##add widgets to bones | |
bpy.ops.object.mode_set(mode='OBJECT') | |
arm_obj.pose.bones[rootBoneName].custom_shape = rootWidgetShape | |
arm_obj.data.bones[rootBoneName].show_wire=True | |
arm_obj.pose.bones[midABoneName].custom_shape = midAWidgetShape | |
arm_obj.data.bones[midABoneName].show_wire=True | |
arm_obj.pose.bones[midBBoneName].custom_shape = midBWidgetShape | |
arm_obj.data.bones[midBBoneName].show_wire=True | |
##set rotation mode to bones | |
arm_obj.pose.bones[rootBoneName].rotation_mode = 'XYZ' | |
arm_obj.pose.bones[midABoneName].rotation_mode = 'XYZ' | |
arm_obj.pose.bones[midBBoneName].rotation_mode = 'XYZ' | |
##resize widgets using prop geo | |
propScale = 1.5*propObj.dimensions.x/2 | |
for boneName in [rootBoneName,midABoneName,midBBoneName]: | |
arm_obj.pose.bones[boneName].custom_shape_scale = propScale | |
#make prop geo follow armature | |
#using constraint (possibly later change to parenting) | |
cnt = propObj.constraints.new('CHILD_OF') | |
cnt.target = arm_obj | |
cnt.subtarget = midBBoneName | |
poseDrivingBone = arm_obj.pose.bones[midBBoneName] | |
##prevent offset because driving bone not at origin | |
cnt.inverse_matrix = (arm_obj.matrix_world*poseDrivingBone.matrix).inverted() | |
#end make prop geo follow armature | |
return arm_obj | |
###helper for widgets for bones | |
def drawShape(context, name = None, shapeVertsEdges = None): | |
""" draw mesh no faces. if it exists does nothing. it takes name of shape and tuple with lists for verts and edges""" | |
if not name: | |
print("requires a name >> exiting") | |
return | |
scene = context.scene | |
#if data object already created return the object | |
if name in bpy.data.objects: | |
print("%s is already created doing nothing" %name) | |
return bpy.data.objects[name] | |
#create the object in data | |
mesh = bpy.data.meshes.new(name) | |
obj = bpy.data.objects.new(name,mesh) | |
scene.objects.link(obj) | |
if obj is None: | |
print("issue drawing shape") | |
return None | |
#use for from_pydata | |
verts,edges = shapeVertsEdges | |
meshData = obj.data | |
meshData.from_pydata(verts,edges,[]) | |
meshData.update() | |
return obj | |
def getVertsEdgesFromSelected(context): | |
"""help for from_pydata creation of widgets. returns tuple verts and edges of selected | |
""" | |
obj = context.selected_objects[0] | |
verts = [] | |
edges = [] | |
for i in range(0,len(obj.data.vertices)): | |
pos = context.object.data.vertices[i].co | |
verts.append( tuple( (round(pos[0],4),round(pos[1],4),round(pos[2],4)) ) ) | |
for i in range(0,len(obj.data.edges)): | |
edges.append( (context.object.data.edges[i].vertices[0],context.object.data.edges[i].vertices[1]) ) | |
return verts,edges | |
##inspired by | |
#Nathan Vegdahl's rigify | |
#https://blenderartists.org/t/how-to-update-bpy-data-meshes-bpy-data-armatures/592370 | |
#https://blender.stackexchange.com/questions/18562/child-ofs-set-inverse-without-using-bpy-ops | |
#https://blender.stackexchange.com/questions/112776/parent-object-to-a-bone-and-object-becomes-offsets-how-do-i-prevent-object-from ##parenting mesh to bone |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment