Esempio n. 1
0
def calc_relative_rotation(model: PmxModel,
                           links: BoneLinks,
                           motion: VmdMotion,
                           fno: int,
                           limit_links=None):
    add_qs = []

    for link_idx, link_bone_name in enumerate(links.all()):
        link_bone = links.get(link_bone_name)

        if not limit_links or (limit_links
                               and limit_links.get(link_bone_name)):
            # 上限リンクがある場合、ボーンが存在している場合のみ、モーション内のキー情報を取得
            fill_bf = motion.calc_bf(link_bone.name, fno)
        else:
            # 上限リンクでボーンがない場合、ボーンは初期値
            fill_bf = VmdBoneFrame(fno=fno)
            fill_bf.set_name(link_bone_name)

        # 実際の回転量を計算
        rot = deform_rotation(model, motion, fill_bf)

        add_qs.append(rot)

    return add_qs
Esempio n. 2
0
def calc_relative_position(model: PmxModel,
                           links: BoneLinks,
                           motion: VmdMotion,
                           fno: int,
                           limit_links=None):
    trans_vs = []

    for link_idx, link_bone_name in enumerate(links.all()):
        link_bone = links.get(link_bone_name)

        if not limit_links or (limit_links
                               and limit_links.get(link_bone_name)):
            # 上限リンクがある倍、ボーンが存在している場合のみ、モーション内のキー情報を取得
            fill_bf = motion.calc_bf(link_bone.name, fno)
        else:
            # 上限リンクでボーンがない場合、ボーンは初期値
            fill_bf = VmdBoneFrame(fno=fno)
            fill_bf.set_name(link_bone_name)

        # 位置
        if link_idx == 0:
            # 一番親は、グローバル座標を考慮
            trans_vs.append(link_bone.position + fill_bf.position)
        else:
            # 位置:自身から親の位置を引いた相対位置
            trans_vs.append(link_bone.position + fill_bf.position -
                            links.get(link_bone_name, offset=-1).position)

    return trans_vs
    def prepare_split_stance(self, motion: VmdMotion, target_bone_name: str):
        fnos = motion.get_bone_fnos(target_bone_name)

        for fidx, fno in enumerate(fnos):
            if fidx == 0:
                continue

            prev_bf = motion.bones[target_bone_name][fnos[fidx - 1]]
            bf = motion.bones[target_bone_name][fno]
            diff_degree = abs(prev_bf.rotation.toDegree() -
                              bf.rotation.toDegree())

            if diff_degree >= 150:
                # 回転量が約150度以上の場合、半分に分割しておく
                half_fno = prev_bf.fno + round((bf.fno - prev_bf.fno) / 2)

                if prev_bf.fno < half_fno < bf.fno:
                    # キーが追加できる状態であれば、追加
                    half_bf = motion.calc_bf(target_bone_name, half_fno)
                    motion.regist_bf(half_bf, target_bone_name, half_fno)
Esempio n. 4
0
def deform_rotation(model: PmxModel, motion: VmdMotion, bf: VmdBoneFrame):
    if bf.name not in model.bones:
        return MQuaternion()

    bone = model.bones[bf.name]
    rot = bf.rotation.normalized().copy()

    if bone.fixed_axis != MVector3D():
        # 回転角度を求める
        if rot != MQuaternion():
            # 回転補正
            if "右" in bone.name and rot.x() > 0 and bone.fixed_axis.x() <= 0:
                rot.setX(rot.x() * -1)
                rot.setScalar(rot.scalar() * -1)
            elif "左" in bone.name and rot.x() < 0 and bone.fixed_axis.x() >= 0:
                rot.setX(rot.x() * -1)
                rot.setScalar(rot.scalar() * -1)
            # 回転補正(コロン式ミクさん等軸反転パターン)
            elif "右" in bone.name and rot.x() < 0 and bone.fixed_axis.x() > 0:
                rot.setX(rot.x() * -1)
                rot.setScalar(rot.scalar() * -1)
            elif "左" in bone.name and rot.x() > 0 and bone.fixed_axis.x() < 0:
                rot.setX(rot.x() * -1)
                rot.setScalar(rot.scalar() * -1)

            rot.normalize()

        # 軸固定の場合、回転を制限する
        rot = MQuaternion.fromAxisAndAngle(bone.fixed_axis, rot.toDegree())

    if bone.getExternalRotationFlag(
    ) and bone.effect_index in model.bone_indexes:

        effect_parent_bone = bone
        effect_bone = model.bones[model.bone_indexes[bone.effect_index]]
        cnt = 0

        while cnt < 100:
            # 付与親が取得できたら、該当する付与親の回転を取得する
            effect_bf = motion.calc_bf(effect_bone.name, bf.fno)

            # 自身の回転量に付与親の回転量を付与率を加味して付与する
            if effect_parent_bone.effect_factor < 0:
                # マイナス付与の場合、逆回転
                rot = rot * (effect_bf.rotation *
                             abs(effect_parent_bone.effect_factor)).inverted()
            else:
                rot = rot * (effect_bf.rotation *
                             effect_parent_bone.effect_factor)

            if effect_bone.getExternalRotationFlag(
            ) and effect_bone.effect_index in model.bone_indexes:
                # 付与親の親として現在のeffectboneを保持
                effect_parent_bone = effect_bone
                # 付与親置き換え
                effect_bone = model.bones[model.bone_indexes[
                    effect_bone.effect_index]]
            else:
                break

            cnt += 1

    return rot
Esempio n. 5
0
def calc_IK(model: PmxModel,
            links: BoneLinks,
            motion: VmdMotion,
            fno: int,
            target_pos: MVector3D,
            ik_links: BoneLinks,
            max_count=10):
    for bone_name in list(ik_links.all().keys())[1:]:
        # bfをモーションに登録
        bf = motion.calc_bf(bone_name, fno)
        motion.regist_bf(bf, bone_name, fno)

    local_effector_pos = MVector3D()
    local_target_pos = MVector3D()

    for cnt in range(max_count):
        # 規定回数ループ
        for ik_idx, joint_name in enumerate(list(ik_links.all().keys())[1:]):
            # 処理対象IKボーン
            ik_bone = ik_links.get(joint_name)

            # 現在のボーングローバル位置と行列を取得
            global_3ds_dic, total_mats = calc_global_pos(model,
                                                         links,
                                                         motion,
                                                         fno,
                                                         return_matrix=True)

            # エフェクタ(末端)
            global_effector_pos = global_3ds_dic[ik_links.first_name()]

            # 注目ノード(実際に動かすボーン)
            joint_mat = total_mats[joint_name]

            # ワールド座標系から注目ノードの局所座標系への変換
            inv_coord = joint_mat.inverted()

            # 注目ノードを起点とした、エフェクタのローカル位置
            local_effector_pos = inv_coord * global_effector_pos
            local_target_pos = inv_coord * target_pos

            #  (1) 基準関節→エフェクタ位置への方向ベクトル
            basis2_effector = local_effector_pos.normalized()
            #  (2) 基準関節→目標位置への方向ベクトル
            basis2_target = local_target_pos.normalized()

            # ベクトル (1) を (2) に一致させるための最短回転量(Axis-Angle)
            # 回転角
            rotation_dot = MVector3D.dotProduct(basis2_effector, basis2_target)
            # 回転角度
            rotation_radian = math.acos(max(-1, min(1, rotation_dot)))

            if abs(rotation_radian) > 0.0001:
                # 一定角度以上の場合

                # 回転軸
                rotation_axis = MVector3D.crossProduct(
                    basis2_effector, basis2_target).normalized()
                # 回転角度
                rotation_degree = math.degrees(rotation_radian)

                # 関節回転量の補正(最大変位量を制限する)
                correct_qq = MQuaternion.fromAxisAndAngle(
                    rotation_axis, min(rotation_degree, ik_bone.degree_limit))

                # ジョイントに補正をかける
                bf = motion.calc_bf(joint_name, fno)
                new_ik_qq = correct_qq * bf.rotation

                # IK軸制限がある場合、上限下限をチェック
                if ik_bone.ik_limit_min != MVector3D(
                ) and ik_bone.ik_limit_max != MVector3D():
                    x_qq, y_qq, z_qq, yz_qq = separate_local_qq(
                        fno, bone_name, new_ik_qq,
                        model.get_local_x_axis(ik_bone.name))

                    logger.test("new_ik_qq: %s, x_qq: %s, y_qq: %s, z_qq: %s",
                                new_ik_qq.toEulerAngles(),
                                x_qq.toEulerAngles(), y_qq.toEulerAngles(),
                                z_qq.toEulerAngles())
                    logger.test("new_ik_qq: %s, x_qq: %s, y_qq: %s, z_qq: %s",
                                new_ik_qq.toDegree(), x_qq.toDegree(),
                                y_qq.toDegree(), z_qq.toDegree())

                    euler_x = min(
                        ik_bone.ik_limit_max.x(),
                        max(ik_bone.ik_limit_min.x(), x_qq.toDegree()))
                    euler_y = min(
                        ik_bone.ik_limit_max.y(),
                        max(ik_bone.ik_limit_min.y(), y_qq.toDegree()))
                    euler_z = min(
                        ik_bone.ik_limit_max.z(),
                        max(ik_bone.ik_limit_min.z(), z_qq.toDegree()))

                    logger.test(
                        "limit_qq: %s -> %s", new_ik_qq.toEulerAngles(),
                        MQuaternion.fromEulerAngles(euler_x, euler_y,
                                                    euler_z).toEulerAngles())

                    new_ik_qq = MQuaternion.fromEulerAngles(
                        euler_x, euler_y, euler_z)

                bf.rotation = new_ik_qq

        # 位置の差がほとんどない場合、終了
        if (local_effector_pos - local_target_pos).lengthSquared() < 0.0001:
            return

    return