glTF importer: code cleanup. Better distinction between edit / pose / bind pose
This commit is contained in:
parent
ffd8a68724
commit
b4fc9fbcf4
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue