Example #1
0
    def import_bone_bind(self, n_block, n_bind_store, b_armature_data, n_armature, b_parent_bone=None):
        """Adds a bone to the armature in edit mode."""
        # check that n_block is indeed a bone
        if not self.is_bone(n_block):
            return None
        # bone name
        bone_name = block_store.import_name(n_block)
        # create a new bone
        b_edit_bone = b_armature_data.edit_bones.new(bone_name)
        # store nif block for access from object mode
        self.name_to_block[b_edit_bone.name] = n_block
        # get the nif bone's armature space matrix (under the hood all bone space matrixes are multiplied together)
        n_bind = mathutils.Matrix(n_bind_store.get(n_block, NifFormat.Matrix44()).as_list()).transposed()
        # get transformation in blender's coordinate space
        b_bind = math.nif_bind_to_blender_bind(n_bind)

        # the following is a workaround because blender can no longer set matrices to bones directly
        tail, roll = bpy.types.Bone.AxisRollFromMatrix(b_bind.to_3x3())
        b_edit_bone.head = b_bind.to_translation()
        b_edit_bone.tail = tail + b_edit_bone.head
        b_edit_bone.roll = roll
        # link to parent
        if b_parent_bone:
            b_edit_bone.parent = b_parent_bone
        # import and parent bone children
        for n_child in n_block.children:
            self.import_bone_bind(n_child, n_bind_store, b_armature_data, n_armature, b_edit_bone)
    def import_bone_bind(self, n_block, b_armature_data, n_armature, b_parent_bone=None):
        """Adds a bone to the armature in edit mode."""
        # check that n_block is indeed a bone
        if not self.is_bone(n_block):
            return None
        # bone name
        bone_name = block_store.import_name(n_block)
        # create a new bone
        b_edit_bone = b_armature_data.edit_bones.new(bone_name)
        # store nif block for access from object mode
        self.name_to_block[b_edit_bone.name] = n_block
        # get the nif bone's armature space matrix (under the hood all bone space matrixes are multiplied together)
        n_bind = math.nifformat_to_mathutils_matrix(self.bind_store.get(n_block, NifFormat.Matrix44()))
        # get transformation in blender's coordinate space
        b_bind = math.nif_bind_to_blender_bind(n_bind)

        # set the bone matrix - but set the tail first to prevent issues with zero-length bone
        b_edit_bone.tail = mathutils.Vector([0, 0, 1])
        b_edit_bone.matrix = b_bind
        # link to parent
        if b_parent_bone:
            b_edit_bone.parent = b_parent_bone
        # import and parent bone children
        for n_child in n_block.children:
            self.import_bone_bind(n_child, b_armature_data, n_armature, b_edit_bone)
    def import_armature(self, n_armature):
        """Scans an armature hierarchy, and returns a whole armature.
        This is done outside the normal node tree scan to allow for positioning
        of the bones before skins are attached."""

        armature_name = block_store.import_name(n_armature)
        b_armature_data = bpy.data.armatures.new(armature_name)
        b_armature_data.display_type = 'STICK'

        # use heuristics to determine a suitable orientation
        forward, up = self.guess_orientation(n_armature)
        # pass them to the matrix utility
        math.set_bone_orientation(forward, up)
        # store axis orientation for export
        b_armature_data.niftools.axis_forward = forward
        b_armature_data.niftools.axis_up = up
        b_armature_obj = Object.create_b_obj(n_armature, b_armature_data)
        b_armature_obj.show_in_front = True

        armature_space_bind_store, armature_space_pose_store = self.import_pose(
            n_armature)

        # make armature editable and create bones
        bpy.ops.object.mode_set(mode='EDIT', toggle=False)
        for n_child in n_armature.children:
            self.import_bone_bind(n_child, armature_space_bind_store,
                                  b_armature_data, n_armature)
        self.fix_bone_lengths(b_armature_data)
        bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

        # The armature has been created in editmode,
        # now we are ready to set the bone keyframes and store the bones' long names.
        if NifOp.props.animation:
            self.transform_anim.create_action(b_armature_obj,
                                              armature_name + "-Anim")

        for bone_name, b_bone in b_armature_obj.data.bones.items():
            n_block = self.name_to_block[bone_name]
            # the property is only available from object mode!
            block_store.store_longname(b_bone, safe_decode(n_block.name))
            if NifOp.props.animation:
                self.transform_anim.import_transforms(n_block, b_armature_obj,
                                                      bone_name)

        # import pose
        for b_name, n_block in self.name_to_block.items():
            n_pose = armature_space_pose_store[n_block]
            b_pose_bone = b_armature_obj.pose.bones[b_name]
            n_bind = mathutils.Matrix(n_pose.as_list()).transposed()
            b_pose_bone.matrix = math.nif_bind_to_blender_bind(n_bind)
            # force update is required to ensure the transforms are set properly in blender
            bpy.context.view_layer.update()

        return b_armature_obj
Example #4
0
    def set_object_bind(self, b_obj, b_obj_children, b_armature):
        """ Sets up parent-child relationships for b_obj and all its children and corrects space for children of bones"""
        if isinstance(b_obj, bpy.types.Object):
            # simple object parentship, no space correction
            for b_child in b_obj_children:
                b_child.parent = b_obj

        elif isinstance(b_obj, bpy.types.Bone):
            # Mesh attached to bone (may be rigged or static) or a collider, needs bone space correction
            for b_child in b_obj_children:
                b_child.parent = b_armature
                b_child.parent_type = 'BONE'
                b_child.parent_bone = b_obj.name

                # this works even for arbitrary bone orientation
                # note that matrix_parent_inverse is a unity matrix on import, so could be simplified further with a constant
                mpi = math.nif_bind_to_blender_bind(
                    b_child.matrix_parent_inverse).inverted()
                mpi.translation.y -= b_obj.length
                # essentially we mimic a transformed matrix_parent_inverse and delegate its transform
                # nb. matrix local is relative to the armature object, not the bone
                b_child.matrix_local = mpi @ b_child.matrix_basis
        else:
            raise RuntimeError(f"Unexpected object type {b_obj.__class__:s}")