glTF importer: code cleanup. Better distinction between edit / pose / bind pose

This commit is contained in:
Julien Duroure 2020-02-23 11:46:59 +01:00
parent ffd8a68724
commit b4fc9fbcf4
5 changed files with 64 additions and 35 deletions

View File

@ -15,7 +15,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
"version": (1, 2, 26),
"version": (1, 2, 27),
'blender': (2, 82, 7),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',

View File

@ -20,8 +20,8 @@ from ...io.imp.gltf2_io_binary import BinaryData
from .gltf2_blender_animation_utils import simulate_stash, make_fcurve
# The glTF curves store the value of the final transform, but in Blender
# curves animate a pose bone that is relative to the edit bone
# In Blender we animate a pose bone. The final TRS of the bone depends on
# both the edit bone and pose bone
#
# Final = EditBone * PoseBone
# where
@ -29,7 +29,7 @@ from .gltf2_blender_animation_utils import simulate_stash, make_fcurve
# EditBone = Trans[et] Rot[er] (edit bones have no scale)
# PoseBone = Trans[pt] Rot[pr] Scale[ps]
#
# Solving for the PoseBone gives the change we need to apply to the curves
# Given Final we can solve for the PoseBone we need to use with
#
# pt = Rot[er^{-1}] (ft - et)
# pr = er^{-1} fr
@ -58,18 +58,18 @@ class BlenderBoneAnim():
else:
translation_keyframes = (gltf.loc_gltf_to_blender(vals) for vals in values)
bind_trans, bind_rot, _ = vnode.trs
bind_rot_inv = bind_rot.conjugated()
final_translations = [
bind_rot_inv @ (trans - bind_trans)
# Calculate pose bone trans from final bone trans
edit_trans, edit_rot = vnode.editbone_trans, vnode.editbone_rot
edit_rot_inv = edit_rot.conjugated()
pose_translations = [
edit_rot_inv @ (trans - edit_trans)
for trans in translation_keyframes
]
BlenderBoneAnim.fill_fcurves(
obj.animation_data.action,
keys,
final_translations,
pose_translations,
group_name,
blender_path,
animation.samplers[channel.sampler].interpolation
@ -93,24 +93,23 @@ class BlenderBoneAnim():
else:
quat_keyframes = [gltf.quaternion_gltf_to_blender(vals) for vals in values]
_, bind_rot, _ = vnode.trs
bind_rot_inv = bind_rot.conjugated()
final_rots = [
bind_rot_inv @ rot
# Calculate pose bone rotation from final bone rotation
edit_rot = vnode.editbone_rot
edit_rot_inv = edit_rot.conjugated()
pose_rots = [
edit_rot_inv @ rot
for rot in quat_keyframes
]
# Manage antipodal quaternions
for i in range(1, len(final_rots)):
if final_rots[i].dot(final_rots[i-1]) < 0:
final_rots[i] = -final_rots[i]
for i in range(1, len(pose_rots)):
if pose_rots[i].dot(pose_rots[i-1]) < 0:
pose_rots[i] = -pose_rots[i]
BlenderBoneAnim.fill_fcurves(
obj.animation_data.action,
keys,
final_rots,
pose_rots,
group_name,
blender_path,
animation.samplers[channel.sampler].interpolation
@ -127,17 +126,17 @@ class BlenderBoneAnim():
if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
# TODO manage tangent?
final_scales = [
scale_keyframes = [
gltf.scale_gltf_to_blender(values[idx * 3 + 1])
for idx in range(0, len(keys))
]
else:
final_scales = [gltf.scale_gltf_to_blender(vals) for vals in values]
scale_keyframes = [gltf.scale_gltf_to_blender(vals) for vals in values]
BlenderBoneAnim.fill_fcurves(
obj.animation_data.action,
keys,
final_scales,
scale_keyframes,
group_name,
blender_path,
animation.samplers[channel.sampler].interpolation

View File

@ -134,7 +134,7 @@ class BlenderNode():
editbone.use_connect = False # TODO?
# Give the position of the bone in armature space
arma_mat = vnode.bone_arma_mat
arma_mat = vnode.editbone_arma_mat
editbone.head = arma_mat @ Vector((0, 0, 0))
editbone.tail = arma_mat @ Vector((0, 1, 0))
editbone.align_roll(arma_mat @ Vector((0, 0, 1)) - editbone.head)
@ -159,8 +159,13 @@ class BlenderNode():
vnode = gltf.vnodes[id]
pose_bone = blender_arma.pose.bones[vnode.blender_bone_name]
# Put scale on pose bone (edit bones have no scale)
_, _, s = vnode.trs
# BoneTRS = EditBone * PoseBone
# Set PoseBone to make BoneTRS = vnode.trs.
t, r, s = vnode.trs
et, er = vnode.editbone_trans, vnode.editbone_rot
pose_bone.location = er.conjugated() @ (t - et)
pose_bone.rotation_mode = 'QUATERNION'
pose_bone.rotation_quaternion = er.conjugated() @ r
pose_bone.scale = s
if isinstance(id, int):

View File

@ -78,8 +78,8 @@ class BlenderPrimitive():
inv_binds = [gltf.matrix_gltf_to_blender(m) for m in inv_binds]
else:
inv_binds = [Matrix.Identity(4) for i in range(len(pyskin.joints))]
arma_mats = [gltf.vnodes[joint].bone_arma_mat for joint in pyskin.joints]
joint_mats = [arma_mat @ inv_bind for arma_mat, inv_bind in zip(arma_mats, inv_binds)]
bind_mats = [gltf.vnodes[joint].bind_arma_mat for joint in pyskin.joints]
joint_mats = [bind_mat @ inv_bind for bind_mat, inv_bind in zip(bind_mats, inv_binds)]
def skin_vert(pos, pidx):
out = Vector((0, 0, 0))

View File

@ -25,6 +25,7 @@ def compute_vnodes(gltf):
move_skinned_meshes(gltf)
fixup_multitype_nodes(gltf)
correct_cameras_and_lights(gltf)
pick_bind_pose(gltf)
calc_bone_matrices(gltf)
@ -181,7 +182,7 @@ def move_skinned_meshes(gltf):
* Move a skinned mesh to become a child of the armature that skins it.
Have to ensure the mesh and arma have the same world transform.
* When we do mesh creation, we will also need to put all the verts in
their rest pose (ie. the pose the edit bones are in)
the bind pose in arma space.
"""
ids = list(gltf.vnodes.keys())
for id in ids:
@ -334,22 +335,46 @@ def correct_cameras_and_lights(gltf):
vnode.light_node_idx = None
def pick_bind_pose(gltf):
"""
Pick the bind pose for all bones. Skinned meshes will be retargeted onto
this bind pose during mesh creation.
"""
for vnode_id in gltf.vnodes:
vnode = gltf.vnodes[vnode_id]
if vnode.type == VNode.Bone:
# For now, use the node TR for bind pose.
# TODO: try calculating from inverseBindMatices?
vnode.bind_trans = Vector(vnode.trs[0])
vnode.bind_rot = Quaternion(vnode.trs[1])
# Initialize editbones to match bind pose
vnode.editbone_trans = Vector(vnode.bind_trans)
vnode.editbone_rot = Quaternion(vnode.bind_rot)
def calc_bone_matrices(gltf):
"""
Calculate bone_arma_mat, the transformation from bone space to armature
space for the edit bone, for each bone.
Calculate the transformations from bone space to arma space in the bind
pose and in the edit bone pose.
"""
def visit(vnode_id): # Depth-first walk
vnode = gltf.vnodes[vnode_id]
if vnode.type == VNode.Bone:
if gltf.vnodes[vnode.parent].type == VNode.Bone:
parent_arma_mat = gltf.vnodes[vnode.parent].bone_arma_mat
parent_bind_mat = gltf.vnodes[vnode.parent].bind_arma_mat
parent_editbone_mat = gltf.vnodes[vnode.parent].editbone_arma_mat
else:
parent_arma_mat = Matrix.Identity(4)
parent_bind_mat = Matrix.Identity(4)
parent_editbone_mat = Matrix.Identity(4)
t, r, _ = vnode.trs
t, r = vnode.bind_trans, vnode.bind_rot
local_to_parent = Matrix.Translation(t) @ Quaternion(r).to_matrix().to_4x4()
vnode.bone_arma_mat = parent_arma_mat @ local_to_parent
vnode.bind_arma_mat = parent_bind_mat @ local_to_parent
t, r = vnode.editbone_trans, vnode.editbone_rot
local_to_parent = Matrix.Translation(t) @ Quaternion(r).to_matrix().to_4x4()
vnode.editbone_arma_mat = parent_editbone_mat @ local_to_parent
for child in vnode.children:
visit(child)