Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'io_scene_gltf2/blender/imp/gltf2_blender_skin.py')
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_skin.py209
1 files changed, 209 insertions, 0 deletions
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_skin.py b/io_scene_gltf2/blender/imp/gltf2_blender_skin.py
new file mode 100755
index 00000000..db0e50f9
--- /dev/null
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_skin.py
@@ -0,0 +1,209 @@
+# Copyright 2018 The glTF-Blender-IO authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import bpy
+from mathutils import Vector, Matrix
+from ..com.gltf2_blender_conversion import matrix_gltf_to_blender, scale_to_matrix
+from ...io.imp.gltf2_io_binary import BinaryData
+
+
+class BlenderSkin():
+ """Blender Skinning / Armature."""
+ def __new__(cls, *args, **kwargs):
+ raise RuntimeError("%s should not be instantiated" % cls)
+
+ @staticmethod
+ def create_armature(gltf, skin_id, parent):
+ """Armature creation."""
+ pyskin = gltf.data.skins[skin_id]
+
+ if pyskin.name is not None:
+ name = pyskin.name
+ else:
+ name = "Armature_" + str(skin_id)
+
+ armature = bpy.data.armatures.new(name)
+ obj = bpy.data.objects.new(name, armature)
+ bpy.data.scenes[gltf.blender_scene].collection.objects.link(obj)
+ pyskin.blender_armature_name = obj.name
+ if parent is not None:
+ obj.parent = bpy.data.objects[gltf.data.nodes[parent].blender_object]
+
+ @staticmethod
+ def set_bone_transforms(gltf, skin_id, bone, node_id, parent):
+ """Set bone transformations."""
+ pyskin = gltf.data.skins[skin_id]
+ pynode = gltf.data.nodes[node_id]
+
+ obj = bpy.data.objects[pyskin.blender_armature_name]
+
+ # Set bone bind_pose by inverting bindpose matrix
+ if node_id in pyskin.joints:
+ index_in_skel = pyskin.joints.index(node_id)
+ inverse_bind_matrices = BinaryData.get_data_from_accessor(gltf, pyskin.inverse_bind_matrices)
+ # Needed to keep scale in matrix, as bone.matrix seems to drop it
+ if index_in_skel < len(inverse_bind_matrices):
+ pynode.blender_bone_matrix = matrix_gltf_to_blender(
+ inverse_bind_matrices[index_in_skel]
+ ).inverted()
+ bone.matrix = pynode.blender_bone_matrix
+ else:
+ gltf.log.error("Error with inverseBindMatrix for skin " + pyskin)
+ else:
+ print('No invBindMatrix for bone ' + str(node_id))
+ pynode.blender_bone_matrix = Matrix()
+
+ # Parent the bone
+ if parent is not None and hasattr(gltf.data.nodes[parent], "blender_bone_name"):
+ bone.parent = obj.data.edit_bones[gltf.data.nodes[parent].blender_bone_name] # TODO if in another scene
+
+ # Switch to Pose mode
+ bpy.ops.object.mode_set(mode="POSE")
+ obj.data.pose_position = 'POSE'
+
+ # Set posebone location/rotation/scale (in armature space)
+ # location is actual bone location minus it's original (bind) location
+ bind_location = Matrix.Translation(pynode.blender_bone_matrix.to_translation())
+ bind_rotation = pynode.blender_bone_matrix.to_quaternion()
+ bind_scale = scale_to_matrix(pynode.blender_bone_matrix.to_scale())
+
+ location, rotation, scale = matrix_gltf_to_blender(pynode.transform).decompose()
+ if parent is not None and hasattr(gltf.data.nodes[parent], "blender_bone_matrix"):
+ parent_mat = gltf.data.nodes[parent].blender_bone_matrix
+
+ # Get armature space location (bindpose + pose)
+ # Then, remove original bind location from armspace location, and bind rotation
+ final_location = (bind_location.inverted() @ parent_mat @ Matrix.Translation(location)).to_translation()
+ obj.pose.bones[pynode.blender_bone_name].location = \
+ bind_rotation.inverted().to_matrix().to_4x4() @ final_location
+
+ # Do the same for rotation
+ obj.pose.bones[pynode.blender_bone_name].rotation_quaternion = \
+ (bind_rotation.to_matrix().to_4x4().inverted() @ parent_mat @
+ rotation.to_matrix().to_4x4()).to_quaternion()
+ obj.pose.bones[pynode.blender_bone_name].scale = \
+ (bind_scale.inverted() @ parent_mat @ scale_to_matrix(scale)).to_scale()
+
+ else:
+ obj.pose.bones[pynode.blender_bone_name].location = bind_location.inverted() @ location
+ obj.pose.bones[pynode.blender_bone_name].rotation_quaternion = bind_rotation.inverted() @ rotation
+ obj.pose.bones[pynode.blender_bone_name].scale = bind_scale.inverted() @ scale
+
+ @staticmethod
+ def create_bone(gltf, skin_id, node_id, parent):
+ """Bone creation."""
+ pyskin = gltf.data.skins[skin_id]
+ pynode = gltf.data.nodes[node_id]
+
+ scene = bpy.data.scenes[gltf.blender_scene]
+ obj = bpy.data.objects[pyskin.blender_armature_name]
+
+ bpy.context.window.scene = scene
+ bpy.context.view_layer.objects.active = obj
+ bpy.ops.object.mode_set(mode="EDIT")
+
+ if pynode.name:
+ name = pynode.name
+ else:
+ name = "Bone_" + str(node_id)
+
+ bone = obj.data.edit_bones.new(name)
+ pynode.blender_bone_name = bone.name
+ pynode.blender_armature_name = pyskin.blender_armature_name
+ bone.tail = Vector((0.0, 1.0, 0.0)) # Needed to keep bone alive
+
+ # set bind and pose transforms
+ BlenderSkin.set_bone_transforms(gltf, skin_id, bone, node_id, parent)
+ bpy.ops.object.mode_set(mode="OBJECT")
+
+ @staticmethod
+ def create_vertex_groups(gltf, skin_id):
+ """Vertex Group creation."""
+ pyskin = gltf.data.skins[skin_id]
+ for node_id in pyskin.node_ids:
+ obj = bpy.data.objects[gltf.data.nodes[node_id].blender_object]
+ for bone in pyskin.joints:
+ obj.vertex_groups.new(name=gltf.data.nodes[bone].blender_bone_name)
+
+ @staticmethod
+ def assign_vertex_groups(gltf, skin_id):
+ """Assign vertex groups to vertices."""
+ pyskin = gltf.data.skins[skin_id]
+ for node_id in pyskin.node_ids:
+ node = gltf.data.nodes[node_id]
+ obj = bpy.data.objects[node.blender_object]
+
+ offset = 0
+ for prim in gltf.data.meshes[node.mesh].primitives:
+ idx_already_done = {}
+
+ if 'JOINTS_0' in prim.attributes.keys() and 'WEIGHTS_0' in prim.attributes.keys():
+ joint_ = BinaryData.get_data_from_accessor(gltf, prim.attributes['JOINTS_0'])
+ weight_ = BinaryData.get_data_from_accessor(gltf, prim.attributes['WEIGHTS_0'])
+
+ for poly in obj.data.polygons:
+ for loop_idx in range(poly.loop_start, poly.loop_start + poly.loop_total):
+ vert_idx = obj.data.loops[loop_idx].vertex_index
+
+ if vert_idx in idx_already_done.keys():
+ continue
+ idx_already_done[vert_idx] = True
+
+ if vert_idx in range(offset, offset + prim.vertices_length):
+
+ tab_index = vert_idx - offset
+ cpt = 0
+ for joint_idx in joint_[tab_index]:
+ weight_val = weight_[tab_index][cpt]
+ if weight_val != 0.0: # It can be a problem to assign weights of 0
+ # for bone index 0, if there is always 4 indices in joint_
+ # tuple
+ group = obj.vertex_groups[gltf.data.nodes[
+ pyskin.joints[joint_idx]
+ ].blender_bone_name]
+ group.add([vert_idx], weight_val, 'REPLACE')
+ cpt += 1
+ else:
+ gltf.log.error("No Skinning ?????") # TODO
+
+ offset = offset + prim.vertices_length
+
+ @staticmethod
+ def create_armature_modifiers(gltf, skin_id):
+ """Create Armature modifier."""
+ pyskin = gltf.data.skins[skin_id]
+
+ if pyskin.blender_armature_name is None:
+ # TODO seems something is wrong
+ # For example, some joints are in skin 0, and are in another skin too
+ # Not sure this is glTF compliant, will check it
+ return
+
+ for node_id in pyskin.node_ids:
+ node = gltf.data.nodes[node_id]
+ obj = bpy.data.objects[node.blender_object]
+
+ for obj_sel in bpy.context.scene.objects:
+ obj_sel.select_set(False)
+ obj.select_set(True)
+ bpy.context.view_layer.objects.active = obj
+
+ # bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
+ # Reparent skinned mesh to it's armature to avoid breaking
+ # skinning with interleaved transforms
+ obj.parent = bpy.data.objects[pyskin.blender_armature_name]
+ arma = obj.modifiers.new(name="Armature", type="ARMATURE")
+ arma.object = bpy.data.objects[pyskin.blender_armature_name]
+