Compare commits
No commits in common. '8603399f19ce73ff7e219137028d2d38faa5edd8' and '3c469c02768dbafd4d6f7a115cc8cc5f1f1f373b' have entirely different histories.
8603399f19
...
3c469c0276
@ -1,107 +0,0 @@
|
|||||||
import math
|
|
||||||
import struct
|
|
||||||
|
|
||||||
MAX_5_BYTE_QUATERNION = 1 / (2 ** 0.5)
|
|
||||||
SCALE_8_BYTE_QUATERNION_COMPRESS = 10000
|
|
||||||
SCALE_8_BYTE_QUATERNION_DECOMPRESS = 1.0 / SCALE_8_BYTE_QUATERNION_COMPRESS
|
|
||||||
SCALE_6_BYTE_VECTOR3_COMPRESS = 32000
|
|
||||||
SCALE_6_BYTE_VECTOR3_DECOMPRESS = 1.0 / SCALE_6_BYTE_VECTOR3_COMPRESS
|
|
||||||
|
|
||||||
def findBiggest(quat):
|
|
||||||
biggest_i = -1
|
|
||||||
biggest_v = 0
|
|
||||||
for i, v in enumerate(quat):
|
|
||||||
if abs(v) > biggest_v:
|
|
||||||
biggest_v = abs(v)
|
|
||||||
biggest_i = i
|
|
||||||
if quat[biggest_i] < 0:
|
|
||||||
return (biggest_i, (-quat[0], -quat[1], -quat[2], -quat[3]))
|
|
||||||
else:
|
|
||||||
return (biggest_i, ( quat[0], quat[1], quat[2], quat[3]))
|
|
||||||
|
|
||||||
def compressQuaternion_5Byte(quat):
|
|
||||||
#Find biggset value and negate the quaternion to make it positive.
|
|
||||||
missing, q = findBiggest(quat)
|
|
||||||
d = []
|
|
||||||
for i in range(4):
|
|
||||||
if i == missing:
|
|
||||||
continue
|
|
||||||
v = int(math.floor(0.5 + q[i] / MAX_5_BYTE_QUATERNION * 2048))
|
|
||||||
if v < -2048:
|
|
||||||
v = -2048
|
|
||||||
elif v > 2047:
|
|
||||||
v = 2047
|
|
||||||
d.append(v + 2048)
|
|
||||||
v = (
|
|
||||||
(missing << 36) |
|
|
||||||
(d[0] << 24) |
|
|
||||||
(d[1] << 12) |
|
|
||||||
d[2]
|
|
||||||
)
|
|
||||||
s = struct.pack("<Q", v)
|
|
||||||
#Return with the top most byte first, but remaining bytes in little endian order.
|
|
||||||
return s[4:5] + s[0:4]
|
|
||||||
|
|
||||||
def decompressQuaternion_5Byte(data):
|
|
||||||
#Source data has the top most byte first, but remaining bytes are little endian.
|
|
||||||
#print("decompressQuaternion_5Byte(data): %s" % ([data],))
|
|
||||||
#Rearrange byte data into a little endian uint64.
|
|
||||||
s = data[1:5] + data[0:1] + b"\x00\x00\x00"
|
|
||||||
(v, ) = struct.unpack("<Q", s)
|
|
||||||
|
|
||||||
#Parse out the data.
|
|
||||||
missing = (v >> 36) & 0x3
|
|
||||||
d = [
|
|
||||||
(v >> 24) & 0xfff,
|
|
||||||
(v >> 12) & 0xfff,
|
|
||||||
v & 0xfff,
|
|
||||||
]
|
|
||||||
|
|
||||||
#Rescale values and summing the squares into x.
|
|
||||||
x = 0
|
|
||||||
#print("%s" % (d, ))
|
|
||||||
for i in range(3):
|
|
||||||
d[i] = (d[i] - 2048) * MAX_5_BYTE_QUATERNION / 2048.0
|
|
||||||
x += d[i] ** 2.0
|
|
||||||
#print("%s -> %s" % (d, x))
|
|
||||||
#Use pythagoras to compute the missing field into x.
|
|
||||||
x = (1 - x) ** 0.5
|
|
||||||
#Rebuild the quaternion.
|
|
||||||
d_i = 0;
|
|
||||||
q = []
|
|
||||||
for i in range(4):
|
|
||||||
if i == missing:
|
|
||||||
q.append(x)
|
|
||||||
else:
|
|
||||||
q.append(d[d_i])
|
|
||||||
d_i += 1
|
|
||||||
return q
|
|
||||||
|
|
||||||
def compressQuaternion_8Byte(quat):
|
|
||||||
return struct.pack("<hhhh",
|
|
||||||
int(math.floor(0.5 + quat[0] * SCALE_8_BYTE_QUATERNION_COMPRESS)),
|
|
||||||
int(math.floor(0.5 + quat[1] * SCALE_8_BYTE_QUATERNION_COMPRESS)),
|
|
||||||
int(math.floor(0.5 + quat[2] * SCALE_8_BYTE_QUATERNION_COMPRESS)),
|
|
||||||
int(math.floor(0.5 + quat[3] * SCALE_8_BYTE_QUATERNION_COMPRESS))
|
|
||||||
)
|
|
||||||
|
|
||||||
def decompressQuaternion_8Byte(data):
|
|
||||||
d = struct.unpack("<hhhh", data[0:8])
|
|
||||||
return [
|
|
||||||
d[0] * SCALE_8_BYTE_QUATERNION_DECOMPRESS,
|
|
||||||
d[1] * SCALE_8_BYTE_QUATERNION_DECOMPRESS,
|
|
||||||
d[2] * SCALE_8_BYTE_QUATERNION_DECOMPRESS,
|
|
||||||
d[3] * SCALE_8_BYTE_QUATERNION_DECOMPRESS
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def compressVector3_6Byte(vec):
|
|
||||||
return struct.pack("<hhh",
|
|
||||||
int(math.floor(0.5 + vec[0] * SCALE_6_BYTE_VECTOR3_COMPRESS)),
|
|
||||||
int(math.floor(0.5 + vec[1] * SCALE_6_BYTE_VECTOR3_COMPRESS)),
|
|
||||||
int(math.floor(0.5 + vec[2] * SCALE_6_BYTE_VECTOR3_COMPRESS))
|
|
||||||
)
|
|
||||||
|
|
||||||
def decompressVector3_6Byte(data):
|
|
||||||
d = struct.unpack("<hhh", data[0:6])
|
|
||||||
return [(x * SCALE_6_BYTE_VECTOR3_DECOMPRESS) for x in d]
|
|
@ -1,212 +0,0 @@
|
|||||||
import bpy.path
|
|
||||||
import bpy
|
|
||||||
import math
|
|
||||||
from mathutils import Vector, Quaternion
|
|
||||||
try:
|
|
||||||
from .anim import *
|
|
||||||
from .bones import *
|
|
||||||
except:
|
|
||||||
from anim import *
|
|
||||||
from bones import *
|
|
||||||
|
|
||||||
def export_fix_coord(v):
|
|
||||||
return Vector((-v[0], v[2], -v[1]))
|
|
||||||
def export_fix_normal(v):
|
|
||||||
return Vector(( v[0], -v[2], v[1]))
|
|
||||||
def export_fix_quaternion(quat):
|
|
||||||
return Quaternion((-quat[1], quat[3], -quat[2], quat[0]))
|
|
||||||
|
|
||||||
|
|
||||||
def getBoneRotation(bone, bone_tracks, index):
|
|
||||||
if bone is None:
|
|
||||||
#rot_p = Quaternion()
|
|
||||||
return Quaternion()
|
|
||||||
else:
|
|
||||||
rot_p = getBoneRotation(bone.parent, bone_tracks, index)
|
|
||||||
if bone.name in bone_tracks:
|
|
||||||
trk = bone_tracks[bone.name]
|
|
||||||
chn = trk["rotation_quaternion"]
|
|
||||||
if index >= len(chn):
|
|
||||||
rot_s = chn[-1].copy()
|
|
||||||
else:
|
|
||||||
rot_s = chn[index].copy()
|
|
||||||
else:
|
|
||||||
rot_s = Quaternion()
|
|
||||||
rot_p.rotate(rot_s)
|
|
||||||
return rot_p
|
|
||||||
#rot_s.rotate(rot_p)
|
|
||||||
#return rot_s
|
|
||||||
|
|
||||||
|
|
||||||
def convert_animation(context, arm_obj, arm_data, nla_track, anim, save_skel):
|
|
||||||
bone_tracks = {}
|
|
||||||
for nla_strip in nla_track.strips:
|
|
||||||
#todo: what's the proper way to handle multiple strips?
|
|
||||||
#Presently later strips will overwrite earlier strips.
|
|
||||||
#>>> [x.data_path for x in bpy.data.objects['fem'].animation_data.nla_tracks['run'].strips[0].action.fcurves]
|
|
||||||
#>>> [x.array_index for x in bpy.data.objects['fem'].animation_data.nla_tracks['run'].strips[0].action.fcurves]
|
|
||||||
for crv in nla_strip.action.fcurves:
|
|
||||||
#>>> [y.co for x in bpy.data.objects['fem'].animation_data.nla_tracks['run'].strips[0].action.fcurves for y in x.sampled_points]
|
|
||||||
dp = crv.data_path
|
|
||||||
idx = crv.array_index
|
|
||||||
print("crv: %s : %s" % (dp, idx))
|
|
||||||
#Naively convert data_path into bone, and transform type
|
|
||||||
parts_a = dp.split('["')
|
|
||||||
parts_b = parts_a[1].split('"].')
|
|
||||||
bone_name = parts_b[0]
|
|
||||||
transform = parts_b[1]
|
|
||||||
if bone_name not in bone_tracks:
|
|
||||||
bone_tracks[bone_name] = {}
|
|
||||||
bone_track = bone_tracks[bone_name]
|
|
||||||
if transform == "location":
|
|
||||||
data_type = Vector
|
|
||||||
elif transform == "rotation_quaternion":
|
|
||||||
data_type = Quaternion
|
|
||||||
else:
|
|
||||||
#todo:
|
|
||||||
raise
|
|
||||||
data_type = None
|
|
||||||
if transform not in bone_track:
|
|
||||||
bone_track[transform] = []
|
|
||||||
bone_track_channel = bone_track[transform]
|
|
||||||
for pnt in crv.sampled_points:
|
|
||||||
k = int(math.floor(pnt.co[0] + 0.5))
|
|
||||||
if k < 0:
|
|
||||||
#ignore samples before 0
|
|
||||||
continue
|
|
||||||
while len(bone_track_channel) <= k:
|
|
||||||
bone_track_channel.append(data_type())
|
|
||||||
bone_track_channel[k][idx] = pnt.co[1]
|
|
||||||
#print("bone_tracks: %s" % bone_tracks)
|
|
||||||
print("bone_tracks['Head']: %s" % bone_tracks.get("Head", None))
|
|
||||||
#todo: convert FCurve data to track positions and rotations
|
|
||||||
|
|
||||||
#todo: Get bones required for export.
|
|
||||||
if save_skel:
|
|
||||||
#If we need to save the skeleton, ensure we have values for the T-pose loaded into bones that haven't been referenced yet.
|
|
||||||
for bn in arm_data.bones.keys():
|
|
||||||
if bn not in bone_tracks:
|
|
||||||
bone_tracks[bn] = {
|
|
||||||
"location": [Vector()],
|
|
||||||
"rotation_quaternion": [Quaternion()],
|
|
||||||
}
|
|
||||||
|
|
||||||
#todo: trim back tracks that have duplicates on their tail.
|
|
||||||
for bn, bt in bone_tracks.items():
|
|
||||||
#ensure missing channels have a T-pose value.
|
|
||||||
if "location" not in bt:
|
|
||||||
bt["location"] = [Vector()]
|
|
||||||
if "rotation_quaternion" not in bt:
|
|
||||||
bt["rotation_quaternion"] = [Quaternion()]
|
|
||||||
#Trim back duplicates at the end of a track.
|
|
||||||
for cn in ("location", "rotation_quaternion"):
|
|
||||||
chn = bt[cn]
|
|
||||||
while len(chn) >= 2:
|
|
||||||
if chn[-1] == chn[-2]:
|
|
||||||
chn.pop()
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
#Convert track positions and rotations into a more convenient form.
|
|
||||||
for bn, bt in bone_tracks.items():
|
|
||||||
bone = arm_data.bones[bn]
|
|
||||||
#Get position of bone, relative to parent (or armature origin for root bones).
|
|
||||||
if bone.parent is None:
|
|
||||||
bone_position = bone.head
|
|
||||||
else:
|
|
||||||
bone_position = bone.head + (bone.parent.tail - bone.parent.head)
|
|
||||||
#print("parent[%s]: %s %s" % (bone.parent.name, bone.parent.head, bone.parent.tail))
|
|
||||||
print("bone_position[%s(%s)]: %s" % (bn, bone.name, bone_position))
|
|
||||||
bt["net_location"] = [bone_position]
|
|
||||||
bt["net_rotation_quaternion"] = []
|
|
||||||
rot_chn = bt["rotation_quaternion"]
|
|
||||||
for i in range(len(rot_chn)):
|
|
||||||
bt["net_rotation_quaternion"].append(getBoneRotation(bone, bone_tracks, i))
|
|
||||||
for i in range(1, len(bt["location"])):
|
|
||||||
if i >= len(bt["net_rotation_quaternion"]):
|
|
||||||
rot = bt["net_rotation_quaternion"][-1]
|
|
||||||
else:
|
|
||||||
rot = bt["net_rotation_quaternion"][i]
|
|
||||||
pos = bone_position + bt["location"][i]
|
|
||||||
print(" pos[%s]: %s" % (i, pos))
|
|
||||||
pos.rotate(rot)
|
|
||||||
bt["net_location"].append(pos)
|
|
||||||
|
|
||||||
#print("bone_tracks: %s" % bone_tracks)
|
|
||||||
|
|
||||||
#Store bone track information in the Anim
|
|
||||||
for bn, bt in bone_tracks.items():
|
|
||||||
bat = BoneAnimTrack()
|
|
||||||
bat.bone_id = BONES_LOOKUP[bn]
|
|
||||||
bat.rotations = [export_fix_quaternion(x) for x in bt["rotation_quaternion"]]
|
|
||||||
bat.positions = [export_fix_coord(x) for x in bt["net_location"]]
|
|
||||||
anim.bone_tracks.append(bat)
|
|
||||||
|
|
||||||
#Save the skeleton (if flagged).
|
|
||||||
if save_skel:
|
|
||||||
anim.skeleton_hierarchy = SkeletonHierarchy()
|
|
||||||
for bn in BONES_LIST:
|
|
||||||
if bn in arm_data.bones:
|
|
||||||
bone = arm_data.bones[bn]
|
|
||||||
if bone.parent is None:
|
|
||||||
parent_id = None
|
|
||||||
else:
|
|
||||||
parent_id = BONES_LOOKUP[bone.parent.name]
|
|
||||||
bone_id = BONES_LOOKUP[bn]
|
|
||||||
anim.skeleton_hierarchy.addBone(parent_id, bone_id)
|
|
||||||
|
|
||||||
anim.dump()
|
|
||||||
pass
|
|
||||||
|
|
||||||
def save(operator, context, scale = 1.0, filepath = "", global_matrix = None, use_mesh_modifiers = True, save_skeleton = False):
|
|
||||||
#todo: prefer armature name that matches import?
|
|
||||||
|
|
||||||
#Choose the first selected armature as the armature to attach to.
|
|
||||||
armature = None
|
|
||||||
for ob in context.selected_objects:
|
|
||||||
if ob.type == "ARMATURE":
|
|
||||||
armature_obj = ob
|
|
||||||
armature = ob.data
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
#If none found try again with all objects.
|
|
||||||
for ob in bpy.data.objects:
|
|
||||||
if ob.type == "ARMATURE":
|
|
||||||
armature_obj = ob
|
|
||||||
armature = ob.data
|
|
||||||
break
|
|
||||||
#todo:
|
|
||||||
track = None
|
|
||||||
skel_track = None
|
|
||||||
#todo: error/warning if multiple tracks are selected.
|
|
||||||
#todo: error/warning if multiple skel_ tracks are found
|
|
||||||
#todo: error if no skel_tracks are found
|
|
||||||
for t in armature_obj.animation_data.nla_tracks:
|
|
||||||
if t.select:
|
|
||||||
track = t
|
|
||||||
if t.name.lower().startswith("skel_"):
|
|
||||||
skel_track = t
|
|
||||||
if save_skeleton:
|
|
||||||
skel_track = track
|
|
||||||
arm_name = armature_obj.name
|
|
||||||
track_name = bpy.path.display_name_from_filepath(filepath)
|
|
||||||
skel_track_name = skel_track.name
|
|
||||||
|
|
||||||
#Get name and base name
|
|
||||||
anim_name = "%s/%s" % (arm_name, track_name)
|
|
||||||
anim_base_name = "%s/%s" % (arm_name, skel_track_name)
|
|
||||||
#todo: warning if anim_name doesn't match file path
|
|
||||||
|
|
||||||
anim = Anim()
|
|
||||||
anim.header_name = anim_name
|
|
||||||
anim.header_base_anim_name = anim_base_name
|
|
||||||
|
|
||||||
save_skel = save_skeleton or anim_name == anim_base_name
|
|
||||||
convert_animation(context, armature_obj, armature, track, anim, save_skel)
|
|
||||||
|
|
||||||
data = anim.saveToData()
|
|
||||||
fh = open(filepath, "wb")
|
|
||||||
fh.write(data)
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
@ -1,12 +0,0 @@
|
|||||||
import bpy.path
|
|
||||||
import bpy
|
|
||||||
|
|
||||||
try:
|
|
||||||
from .export_anim import *
|
|
||||||
except:
|
|
||||||
from export_anim import *
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def save_skel(operator, context, scale = 1.0, filepath = "", global_matrix = None, use_mesh_modifiers = True):
|
|
||||||
return save(operator, context, scale = scale, filepath = filepath, global_matrix = global_matrix, use_mesh_modifiers = use_mesh_modifiers, save_skeleton = True)
|
|
@ -1,191 +0,0 @@
|
|||||||
import bpy.path
|
|
||||||
import bpy
|
|
||||||
from mathutils import Vector, Quaternion
|
|
||||||
try:
|
|
||||||
from .anim import *
|
|
||||||
from .bones import *
|
|
||||||
except:
|
|
||||||
from anim import *
|
|
||||||
from bones import *
|
|
||||||
|
|
||||||
def import_fix_coord(v):
|
|
||||||
return Vector((-v[0], -v[2], v[1]))
|
|
||||||
def import_fix_normal(v):
|
|
||||||
return Vector(( v[0], v[2], -v[1]))
|
|
||||||
def import_fix_quaternion(quat):
|
|
||||||
return Quaternion((quat[3], -quat[0], -quat[2], quat[1]))
|
|
||||||
|
|
||||||
|
|
||||||
def getBoneLength(arm_data, bone_name):
|
|
||||||
bl = arm_data.bones[bone_name]
|
|
||||||
if bl.parent is None:
|
|
||||||
return bl.head
|
|
||||||
|
|
||||||
def getBoneRotation(bone, bone_trk_lookup, trk_rot_list, index):
|
|
||||||
if bone is None:
|
|
||||||
#rot_p = Quaternion()
|
|
||||||
return Quaternion()
|
|
||||||
else:
|
|
||||||
rot_p = getBoneRotation(bone.parent, bone_trk_lookup, trk_rot_list, index)
|
|
||||||
if bone.name in bone_trk_lookup:
|
|
||||||
trk_index = bone_trk_lookup[bone.name]
|
|
||||||
rot_list = trk_rot_list[trk_index]
|
|
||||||
if index >= len(rot_list):
|
|
||||||
rot_s = rot_list[-1].copy()
|
|
||||||
else:
|
|
||||||
rot_s = rot_list[index].copy()
|
|
||||||
else:
|
|
||||||
rot_s = Quaternion()
|
|
||||||
rot_p.rotate(rot_s)
|
|
||||||
return rot_p
|
|
||||||
#rot_s.rotate(rot_p)
|
|
||||||
#return rot_s
|
|
||||||
def convertAnimation(context, arm_obj, arm_data, anim, rescale = True):
|
|
||||||
full_name = anim.header_name.decode("utf-8")
|
|
||||||
anim_name = full_name.split("/")[1]#.lstrip("skel_")
|
|
||||||
#get all bones used in animation, and maximum fram count
|
|
||||||
max_frames = 0
|
|
||||||
bone_ids = []
|
|
||||||
bone_names = []
|
|
||||||
bone_trk_lengths = []
|
|
||||||
bone_arm_lengths = []
|
|
||||||
bone_scales = []
|
|
||||||
bone_trk_lookup = {}
|
|
||||||
for i, bt in enumerate(anim.bone_tracks):
|
|
||||||
#get maximum frame count
|
|
||||||
max_frames = max(max_frames, len(bt.positions), len(bt.rotations))
|
|
||||||
#get IDs and names
|
|
||||||
bone_id = bt.bone_id
|
|
||||||
bone_name = BONES_LIST[bone_id]
|
|
||||||
bone_trk_lookup[bone_name] = i
|
|
||||||
bone_ids.append(bone_id)
|
|
||||||
bone_names.append(bone_name)
|
|
||||||
#get animation's T-pose length
|
|
||||||
bone_trk_len = Vector(bt.positions[0]).length
|
|
||||||
bone_trk_lengths.append(bone_trk_len)
|
|
||||||
#get armature's T-pose length
|
|
||||||
bone_arm_len = getBoneLength(arm_data, bone_name)
|
|
||||||
bone_arm_lengths.append(bone_arm_len)
|
|
||||||
#determine scale
|
|
||||||
bone_scale = rescale and (bone_arm_len / bone_trk_len) or (1.0)
|
|
||||||
bone_scales.append(bone_scale)
|
|
||||||
|
|
||||||
#create animation
|
|
||||||
if arm_obj.animation_data is None:
|
|
||||||
arm_obj.animation_data_create()
|
|
||||||
if anim_name in arm_obj.animation_data.nla_tracks:
|
|
||||||
#todo: properly handle cases where the name already exists
|
|
||||||
nla_track = arm_obj.animation_data.nla_tracks[anim_name]
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
nla_track = arm_obj.animation_data.nla_tracks.new()
|
|
||||||
nla_track.name = anim_name
|
|
||||||
action = bpy.data.actions.new(anim_name)
|
|
||||||
action.use_fake_user = True
|
|
||||||
nla_strip = nla_track.strips.new(anim_name, 0, action)
|
|
||||||
nla_strip.action_frame_start = 0
|
|
||||||
nla_strip.action_frame_end = max_frames
|
|
||||||
|
|
||||||
#Extract all position and rotation track data in blender coordinates.
|
|
||||||
trk_pos_list = []
|
|
||||||
trk_rot_list = []
|
|
||||||
for i, bt in enumerate(anim.bone_tracks):
|
|
||||||
#pos_start = (len(bt.positions) > 1) and 1 or 0
|
|
||||||
pos_start = 0
|
|
||||||
pos_stop = len(bt.positions)
|
|
||||||
#rot_start = (len(bt.rotations) > 1) and 1 or 0
|
|
||||||
rot_start = 0
|
|
||||||
rot_stop = len(bt.rotations)
|
|
||||||
pos_list = []
|
|
||||||
rot_list = []
|
|
||||||
for j in range(pos_start, pos_stop):
|
|
||||||
v = import_fix_coord(bt.positions[j])
|
|
||||||
pos_list.append(v)
|
|
||||||
pass
|
|
||||||
for j in range(rot_start, rot_stop):
|
|
||||||
v = import_fix_quaternion(bt.rotations[j])
|
|
||||||
rot_list.append(v)
|
|
||||||
pass
|
|
||||||
trk_pos_list.append(pos_list)
|
|
||||||
trk_rot_list.append(rot_list)
|
|
||||||
|
|
||||||
|
|
||||||
#Iterate over bone tracks and generate FCurves for each of them.
|
|
||||||
for i, bt in enumerate(anim.bone_tracks):
|
|
||||||
bone_name = bone_names[i]
|
|
||||||
bone = arm_data.bones[bone_name]
|
|
||||||
pose_bone = arm_obj.pose.bones[bone_name]
|
|
||||||
#pos_start = (len(bt.positions) > 1) and 1 or 0
|
|
||||||
pos_start = 0
|
|
||||||
pos_stop = len(bt.positions)
|
|
||||||
#rot_start = (len(bt.rotations) > 1) and 1 or 0
|
|
||||||
rot_start = 0
|
|
||||||
rot_stop = len(bt.rotations)
|
|
||||||
pos_list = trk_pos_list[i]
|
|
||||||
rot_list = trk_rot_list[i]
|
|
||||||
props = [(pose_bone.path_from_id("location"), 3, bone_name), #"Location"),
|
|
||||||
(pose_bone.path_from_id("rotation_quaternion"), 4, bone_name), #"Quaternion Rotation"),
|
|
||||||
#(pose_bone.path_from_id("rotation_axis_angle"), 4, "Axis Angle Rotation"),
|
|
||||||
#(pose_bone,path_from_id("rotatin_euler"), 3, "Euler Rotation"),
|
|
||||||
#(pose_bone.path_from_id("scale"), 3, "Scale"),
|
|
||||||
]
|
|
||||||
curves = [action.fcurves.new(prop, index = cidx, action_group = agrp)
|
|
||||||
for prop, channel_count, agrp in props
|
|
||||||
for cidx in range(channel_count)]
|
|
||||||
pos_curves = curves[0:3]
|
|
||||||
rot_curves = curves[3:7]
|
|
||||||
for j, pos in enumerate(pos_list):
|
|
||||||
#Remove the bone component from the position.
|
|
||||||
if bone.parent is None:
|
|
||||||
#Only compute it for root bones.
|
|
||||||
rot = getBoneRotation(bone.parent, bone_trk_lookup, trk_rot_list, j)
|
|
||||||
#rot.invert()
|
|
||||||
pos0 = pos_list[0].copy()
|
|
||||||
pos0.rotate(rot)
|
|
||||||
l = (pos - pos0).length
|
|
||||||
if l >= 0.001:# and (bone.name in ["Hips", "Waist","Chest"]):
|
|
||||||
print("%s, %s: %s: pos: %s : %s ::: %s : %s" % (bone.name, j, l, pos, pos0, pos_list[0], rot))
|
|
||||||
pos = pos - pos0
|
|
||||||
if l < 0.001:
|
|
||||||
#Distance is close to zero, force position adjustment to zero.
|
|
||||||
pos = Vector()
|
|
||||||
else:
|
|
||||||
#assume 0,0,0 correction in other nodes.
|
|
||||||
pos = Vector()
|
|
||||||
for k, crv in enumerate(pos_curves):
|
|
||||||
crv.keyframe_points.insert(j, pos[k], options={'NEEDED', 'FAST'}).interpolation = 'LINEAR'
|
|
||||||
for j, rot in enumerate(rot_list):
|
|
||||||
for k, crv in enumerate(rot_curves):
|
|
||||||
crv.keyframe_points.insert(j, rot[k], options={'NEEDED', 'FAST'}).interpolation = 'LINEAR'
|
|
||||||
for crv in curves:
|
|
||||||
crv.update()
|
|
||||||
|
|
||||||
|
|
||||||
#todo: delete mid points for simple motions?
|
|
||||||
pass
|
|
||||||
|
|
||||||
def load(operator, context, scale = 1.0, filepath = "", global_matrix = None, use_mesh_modifiers = True, ignore_lod = True):
|
|
||||||
#Load .anim file
|
|
||||||
fh_in = open(filepath, "rb")
|
|
||||||
anim = Anim()
|
|
||||||
anim.loadFromFile(fh_in)
|
|
||||||
fh_in.close()
|
|
||||||
|
|
||||||
#todo: prefer armature name that matches import?
|
|
||||||
#Choose the first selected armature as the armature to attach to.
|
|
||||||
armature = None
|
|
||||||
for ob in context.selected_objects:
|
|
||||||
if ob.type == "ARMATURE":
|
|
||||||
armature_obj = ob
|
|
||||||
armature = ob.data
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
#If none found try again with all objects.
|
|
||||||
for ob in bpy.data.objects:
|
|
||||||
if ob.type == "ARMATURE":
|
|
||||||
armature_obj = ob
|
|
||||||
armature = ob.data
|
|
||||||
break
|
|
||||||
convertAnimation(context, armature_obj, armature, anim, False)
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
@ -1,110 +0,0 @@
|
|||||||
import bpy.path
|
|
||||||
import bpy
|
|
||||||
from mathutils import Vector, Quaternion
|
|
||||||
try:
|
|
||||||
from .anim import *
|
|
||||||
from .bones import *
|
|
||||||
from .import_anim import convertAnimation
|
|
||||||
except:
|
|
||||||
from anim import *
|
|
||||||
from bones import *
|
|
||||||
from import_anim import convertAnimation
|
|
||||||
|
|
||||||
def import_fix_coord(v):
|
|
||||||
return Vector((-v[0], -v[2], v[1]))
|
|
||||||
def import_fix_normal(v):
|
|
||||||
return Vector(( v[0], v[2], -v[1]))
|
|
||||||
def import_fix_quaternion(quat):
|
|
||||||
return Quaternion((quat[3], -quat[0], -quat[2], quat[1]))
|
|
||||||
|
|
||||||
TAIL_LENGTH = 0.05 #0.204377
|
|
||||||
TAIL_VECTOR = Vector((0, 1, 0))
|
|
||||||
|
|
||||||
def getTposeOffset(anim, bone_id):
|
|
||||||
offset = Vector((0, 0, 0))
|
|
||||||
for bt in anim.bone_tracks:
|
|
||||||
if bt.bone_id == bone_id:
|
|
||||||
return import_fix_coord(bt.positions[0])
|
|
||||||
return offset
|
|
||||||
|
|
||||||
def convertBone(anim, arm_obj, arm_data, bone_id, parent, parent_position, tail_nub):
|
|
||||||
bone_link = anim.skeleton_hierarchy.bones[bone_id]
|
|
||||||
#if bone_link.boneid != bone_id:
|
|
||||||
# raise Exception("")
|
|
||||||
bone_name = BONES_LIST[bone_id]
|
|
||||||
|
|
||||||
# Create the (edit)bone.
|
|
||||||
new_bone = arm_data.edit_bones.new(name=bone_name)
|
|
||||||
new_bone.select = True
|
|
||||||
new_bone.name = bone_name
|
|
||||||
|
|
||||||
bone_offset = getTposeOffset(anim, bone_id)
|
|
||||||
bone_position = parent_position + bone_offset
|
|
||||||
if parent is None:
|
|
||||||
if tail_nub:
|
|
||||||
#new_bone.head = bone_offset + TAIL_VECTOR * TAIL_LENGTH
|
|
||||||
#new_bone.tail = bone_offset
|
|
||||||
new_bone.head = bone_position
|
|
||||||
new_bone.tail = bone_position + TAIL_VECTOR * TAIL_LENGTH
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
new_bone.parent = parent
|
|
||||||
if tail_nub:
|
|
||||||
#new_bone.head = bone_offset - TAIL_VECTOR * TAIL_LENGTH
|
|
||||||
#new_bone.tail = bone_offset
|
|
||||||
new_bone.head = bone_position
|
|
||||||
new_bone.tail = bone_position + TAIL_VECTOR * TAIL_LENGTH
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
#visit children
|
|
||||||
if bone_link.next != -1:
|
|
||||||
convertBone(anim, arm_obj, arm_data, bone_link.next, parent, parent_position, tail_nub)
|
|
||||||
if bone_link.child != -1:
|
|
||||||
convertBone(anim, arm_obj, arm_data, bone_link.child, new_bone, bone_position, tail_nub)
|
|
||||||
|
|
||||||
|
|
||||||
def convertSkeleton(context, anim):
|
|
||||||
full_name = anim.header_name.decode("utf-8")
|
|
||||||
skeleton_name = full_name.split("/")[0]
|
|
||||||
|
|
||||||
#create armature
|
|
||||||
arm_data = bpy.data.armatures.new(name=skeleton_name)
|
|
||||||
arm_obj = bpy.data.objects.new(name=skeleton_name, object_data=arm_data)
|
|
||||||
|
|
||||||
# instance in scene
|
|
||||||
context.view_layer.active_layer_collection.collection.objects.link(arm_obj)
|
|
||||||
arm_obj.select_set(True)
|
|
||||||
|
|
||||||
# Switch to Edit mode.
|
|
||||||
context.view_layer.objects.active = arm_obj
|
|
||||||
is_hidden = arm_obj.hide_viewport
|
|
||||||
arm_obj.hide_viewport = False # Can't switch to Edit mode hidden objects...
|
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
|
||||||
|
|
||||||
#Traverse tree, creating bones, and position heads and tails.
|
|
||||||
convertBone(anim, arm_obj, arm_data, anim.skeleton_hierarchy.root, None, Vector(), True)
|
|
||||||
|
|
||||||
#Switch to Object mode.
|
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
|
||||||
arm_obj.hide_viewport = is_hidden
|
|
||||||
|
|
||||||
convertAnimation(context, arm_obj, arm_data, anim, rescale = False)
|
|
||||||
|
|
||||||
def load(operator, context, scale = 1.0, filepath = "", global_matrix = None, use_mesh_modifiers = True, ignore_lod = True):
|
|
||||||
#Load .anim file
|
|
||||||
fh_in = open(filepath, "rb")
|
|
||||||
anim = Anim()
|
|
||||||
anim.loadFromFile(fh_in)
|
|
||||||
fh_in.close()
|
|
||||||
|
|
||||||
|
|
||||||
#Check for a skeleton hierarchy
|
|
||||||
if not anim.checkSkeletonHierarchy():
|
|
||||||
raise Exception("Animation does not have a skeleton. (Or it has errors.)")
|
|
||||||
#todo: check for a T-pose
|
|
||||||
convertSkeleton(context, anim)
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
Loading…
Reference in New Issue