Last active
January 2, 2020 16:02
-
-
Save dpogue/dc69d4f799afacd18e62d41d781767e7 to your computer and use it in GitHub Desktop.
Put these in a "avimport" folder in your Blender addons folder
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
bl_info = { | |
'name': 'Avatar Importer', | |
'author': 'dpogue & Lontahv & Jrius', | |
'version': (0, 0, 1), | |
'blender': (2, 7, 9), | |
'location': 'File > Import > Avatar Import', | |
'description': 'Avatar and ATC Anim Importer', | |
'category': 'Import-Export' | |
} | |
import bpy | |
import avimport.importer | |
def register(): | |
importer.register() | |
def unregister(): | |
importer.unregister() | |
if __name__ == "__main__": | |
register() |
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
import bpy | |
import mathutils | |
import os | |
import types | |
from PyHSPlasma import * | |
from bpy.props import * | |
def menu_func_import(self, context): | |
self.layout.operator(AvImport.bl_idname, text=AvImport.bl_label) | |
self.layout.operator(AnimImport.bl_idname, text=AnimImport.bl_label) | |
class AnimImport(bpy.types.Operator): | |
bl_idname = "import.animimport" | |
bl_label = "ATC Anim Import" | |
filepath = StringProperty(subtype='FILE_PATH') | |
def invoke(self, context, event): | |
context.window_manager.fileselect_add(self) | |
return {'RUNNING_MODAL'} | |
def execute(self, context): | |
filePath = self.filepath | |
armature = None | |
if '_female' in filePath.lower(): | |
armature = 'Female_Armature' | |
elif '_male' in filePath.lower(): | |
armature = 'Male_Armature' | |
else: | |
raise RuntimeError("Not an avatar animation") | |
armObj = None | |
try: | |
armObj = bpy.data.objects[armature] | |
except: | |
raise RuntimeError("Could not locate the armature") | |
self.rm = plResManager() | |
page = self.rm.ReadPage(filePath) | |
atc_anim = self.extractAnim(page) | |
animdata = armObj.animation_data_create() | |
action = bpy.data.actions.new(atc_anim.key.name) | |
action.use_fake_user = True | |
animdata.action = action | |
for applicator in atc_anim.applicators: | |
if not isinstance(applicator, plMatrixChannelApplicator): | |
raise RuntimeError('Got unexpected applicator type') | |
channel = applicator.channel | |
ctrl = channel.controller | |
name = applicator.channelName | |
if name == 'handle': # Hack for PotS | |
name = 'Handle' | |
try: | |
bone = armObj.pose.bones[name] | |
except: | |
raise RuntimeError('Could not find bone %s' % name) | |
# I guess we should do something with the affine parts before we | |
# apply keyframe transforms... or adjust everything by the inverse? | |
ap = channel.affine | |
if isinstance(ctrl, plTMController): | |
pos, rot, scale = ctrl.pos, ctrl.rot, ctrl.scale | |
elif isinstance(ctrl, plCompoundController): | |
pos, rot, scale = ctrl.X, ctrl.Y, ctrl.Z | |
else: | |
raise RuntimeError('Unknown controller type %s for %s' % (ctrl.ClassName(), bone.name)) | |
if pos: | |
if isinstance(pos, plLeafController): | |
self.processSimplePosController(action, bone, pos, ap) | |
elif isinstance(pos, plSimplePosController): | |
self.processSimplePosController(action, bone, pos.position, ap) | |
elif isinstance(pos, (plCompoundPosController, plCompoundController)): | |
self.processCompoundPosController(action, bone, pos, ap) | |
else: | |
raise RuntimeError('Unexpected position controller type %s for %s' % (pos.ClassName(), bone.name)) | |
if rot: | |
if isinstance(rot, plLeafController): | |
self.processSimpleRotController(action, bone, rot, ap) | |
elif isinstance(rot, plSimpleRotController): | |
self.processSimpleRotController(action, bone, rot.rot, ap) | |
elif isinstance(rot, (plCompoundRotController, plCompoundController)): | |
self.processCompoundRotController(action, bone, rot, ap) | |
else: | |
raise RuntimeError('Unexpected rotation controller type %s for %s' % (rot.ClassName(), bone.name)) | |
return {'FINISHED'} | |
def extractAnim(self, page): | |
sn = self.rm.getSceneNode(page.location) | |
animname = sn.key.name.split('_')[-1] | |
for key in sn.poolObjects: | |
if key.name == animname and isinstance(key.object, plATCAnim): | |
return key.object | |
return None | |
def processSimplePosController(self, action, bone, ctrl, ap): | |
fcurveX = action.fcurves.new(bone.path_from_id('location'), 0, action_group=bone.name) | |
fcurveY = action.fcurves.new(bone.path_from_id('location'), 1, action_group=bone.name) | |
fcurveZ = action.fcurves.new(bone.path_from_id('location'), 2, action_group=bone.name) | |
for key in ctrl.keys[0]: | |
kx = fcurveX.keyframe_points.insert(key.frame, key.value.X - ap.T.X) | |
ky = fcurveY.keyframe_points.insert(key.frame, key.value.Y - ap.T.Y) | |
kz = fcurveZ.keyframe_points.insert(key.frame, key.value.Z - ap.T.Z) | |
def processCompoundPosController(self, action, bone, ctrl, ap): | |
axes = [ctrl.X, ctrl.Y, ctrl.Z] | |
afns = [ap.T.X, ap.T.Y, ap.T.Z] | |
i = 0 | |
for axis in axes: | |
if not axis: | |
bone.location[i] = afns[i] | |
i += 1 | |
continue | |
fcurve = action.fcurves.new(bone.path_from_id('location'), i, action_group=bone.name) | |
for key in axis.keys[0]: | |
kf = fcurve.keyframe_points.insert(key.frame, key.value - afns[i]) | |
i += 1 | |
def processSimpleRotController(self, action, bone, ctrl, ap): | |
bone.rotation_mode = 'QUATERNION' | |
q = mathutils.Quaternion((ap.Q.W, ap.Q.X, ap.Q.Y, ap.Q.Z)).inverted() | |
fcurveW = action.fcurves.new(bone.path_from_id('rotation_quaternion'), 0, action_group=bone.name) | |
fcurveX = action.fcurves.new(bone.path_from_id('rotation_quaternion'), 1, action_group=bone.name) | |
fcurveY = action.fcurves.new(bone.path_from_id('rotation_quaternion'), 2, action_group=bone.name) | |
fcurveZ = action.fcurves.new(bone.path_from_id('rotation_quaternion'), 3, action_group=bone.name) | |
for key in ctrl.keys[0]: | |
v = q * mathutils.Quaternion((key.value.W, key.value.X, key.value.Y, key.value.Z)) | |
kw = fcurveW.keyframe_points.insert(key.frame, v.w) | |
kx = fcurveX.keyframe_points.insert(key.frame, v.x) | |
ky = fcurveY.keyframe_points.insert(key.frame, v.y) | |
kz = fcurveZ.keyframe_points.insert(key.frame, v.z) | |
def processCompoundRotController(self, action, bone, ctrl, ap): | |
print('CompoundRotController %s' % bone.name) | |
bone.rotation_mode = 'XYZ' | |
q = mathutils.Quaternion((ap.Q.W, ap.Q.X, ap.Q.Y, ap.Q.Z)).to_euler('XYZ') | |
axes = [ctrl.X, ctrl.Y, ctrl.Z] | |
afns = [q.x, q.y, q.z] | |
i = 0 | |
for axis in axes: | |
if not axis: | |
bone.rotation_euler[i] = afns[i] | |
i += 1 | |
continue | |
fcurve = action.fcurves.new(bone.path_from_id('rotation_euler'), i, action_group=bone.name) | |
for key in axis.keys[0]: | |
print('Axis %d; value %f, afn %f' % (i, key.value, afns[i])) | |
kf = fcurve.keyframe_points.insert(key.frame, key.value - afns[i]) | |
i += 1 | |
class AvImport(bpy.types.Operator): | |
bl_idname = "import.avimport" | |
bl_label = "Avatar Import" | |
filepath = StringProperty(subtype='FILE_PATH') | |
def invoke(self, context, event): | |
context.window_manager.fileselect_add(self) | |
return {'RUNNING_MODAL'} | |
def execute(self, context): | |
filePath = self.filepath | |
if not filePath.lower().endswith('male.prp') and not filePath.lower().endswith('female.prp'): | |
raise RuntimeError("Not an avatar") | |
name, boneMap = self.importAvatar(filePath) | |
self.constructArmature(name, boneMap) | |
return {'FINISHED'} | |
def isBone(self, name): | |
return name.lower().startswith('bone') or name.lower() in ['male', 'female', 'handle', 'convergence', 'bookhandle'] | |
def importAvatar(self, filePath): | |
rm = plResManager() | |
page = rm.ReadPage(filePath) | |
sn = rm.getSceneNode(page.location) | |
boneMap = {} | |
for key in sn.sceneObjects: | |
if not self.isBone(key.name): | |
continue | |
so = key.object | |
if so.coord.object: | |
ci = so.coord.object | |
if key.name not in boneMap.keys(): | |
boneMap[key.name] = types.SimpleNamespace(parent=None) | |
boneMap[key.name].matrix = ci.localToWorld.glMat | |
boneMap[key.name].inverse = ci.worldToLocal.glMat | |
boneMap[key.name].l2p = ci.localToParent.glMat | |
boneMap[key.name].p2l = ci.parentToLocal.glMat | |
for child in ci.children: | |
if not self.isBone(child.name): | |
continue | |
if child.name not in boneMap.keys(): | |
boneMap[child.name] = types.SimpleNamespace() | |
boneMap[child.name].parent = key.name | |
return sn.sceneObjects[0].name, boneMap | |
def constructArmature(self, name, bones): | |
armature = bpy.data.armatures.new(name) | |
armObj = bpy.data.objects.new(name + '_Armature', armature) | |
bpy.context.scene.objects.link(armObj) | |
bpy.context.scene.objects.active = armObj | |
bpy.ops.object.mode_set(mode='EDIT') | |
if name in bones: | |
glMat = bones[name].matrix | |
armObj.matrix_basis = [glMat[:4], glMat[4:8], glMat[8:12], glMat[12:]] | |
bone = armature.edit_bones.new('Handle') | |
bone.head = (0, 0, 0) | |
bone.tail = (0, 0, 0.1) | |
del bones[name] | |
for boneName in bones.keys(): | |
armature.edit_bones.new(boneName) | |
for boneName in bones.keys(): | |
boneObj = bones[boneName] | |
bone = armature.edit_bones[boneName] | |
bone.head = (0, 0, 0) | |
bone.tail = (0, 0, 0.1) | |
glMat = boneObj.matrix | |
bone.matrix = [glMat[:4], glMat[4:8], glMat[8:12], glMat[12:]] | |
if boneObj.parent: | |
if boneObj.parent != name: | |
bone.parent = armature.edit_bones[boneObj.parent] | |
for boneName in bones.keys(): | |
bone = armature.edit_bones[boneName] | |
x,y,z = [0, 0, 0] | |
for child in bone.children: | |
x += child.head[0] | |
y += child.head[1] | |
z += child.head[2] | |
if len(bone.children) and boneName != 'Bone_Root': #hack for root | |
bone.tail = (x/len(bone.children), y/len(bone.children), z/len(bone.children)) | |
else: | |
bone.tail = (bone.head[0], bone.head[1] - 0.1, bone.head[2]) | |
bpy.ops.object.mode_set(mode='OBJECT') | |
""" | |
for boneName in bones.keys(): | |
if not boneName in armObj.pose.bones: | |
continue | |
bone = armObj.pose.bones[boneName] | |
if boneName in bpy.data.objects: | |
obj = bpy.data.objects[boneName] | |
im = bones[boneName].p2l | |
obj.data.transform([im[:4], im[4:8], im[8:12], im[12:]]) | |
glMat = bones[boneName].l2p | |
obj.matrix_local = [glMat[:4], glMat[4:8], glMat[8:12], glMat[12:]] | |
#curmat = mathutils.Matrix(obj.matrix_basis) | |
##invmat = mathutils.Matrix([im[:4], im[4:8], im[8:12], im[12:]]) | |
#invmat = mathutils.Matrix(bone.matrix) | |
##resmat = curmat * invmat | |
#resmat = invmat.inverted_safe() | |
#obj.matrix_basis = resmat | |
#bone.custom_shape = obj | |
bone.use_custom_shape_bone_size = False | |
""" | |
def register(): | |
bpy.utils.register_class(AvImport) | |
bpy.utils.register_class(AnimImport) | |
bpy.types.INFO_MT_file_import.append(menu_func_import) | |
def unregister(): | |
bpy.types.INFO_MT_file_import.remove(menu_func_import) | |
bpy.utils.unregister_class(AvImport) | |
bpy.utils.unregister_class(AnimImport) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment