1

I am trying to correctly apply MediaPipe pose data to the bones of a Bevy GLTF model. I have coded it as shown below, and the upper arm seems to move correctly, but the forearm moves strangely.

I’m not entirely sure if both the upper arm and forearm are working properly, but while the upper arm looks plausible, the forearm’s coordinates definitely seem off.

I suspect that when the upper arm rotates, its position changes, and the forearm is using incorrect coordinates because of that. Could you advise me on this? How should I modify my code so that the forearm also operates correctly based on my implementation?

I have implemented the rotation function as follows:

fn update_bone_rotation(
    transform: &mut Transform,
    start: Vec3,  // 부모 또는 기준 랜드마크 (예: 어깨)
    end: Vec3,    // 자식 랜드마크 (예: 팔꿈치)
) -> Quat {
    let smooth_factor = 0.5;
    // 두 점 사이의 방향 벡터 계산 (노멀라이즈)
    let direction = (end - start).normalize();

    // 단순화된 방식으로 목표 회전값 계산 (3D 공간에서는 좀 더 정교한 계산이 필요할 수 있음)
    let z = -direction.x.atan2(direction.y);
    let x = direction.z.atan2((direction.x.powi(2) + direction.y.powi(2)).sqrt());
    let target_rotation = Quat::from_rotation_z(z) * Quat::from_rotation_x(x);

    // 현재 회전과 목표 회전 사이를 Slerp로 보간하여 부드럽게 전환
    transform.rotation.slerp(target_rotation, smooth_factor)
}

When receiving MediaPipe data, I process it for each body part as shown below.

fn message_receive(
    bone_default_transform: Res<BoneDefaultData>,
    mut transforms: Query<(&Name, &mut Transform)>,
) {
    // BEVY_CHANNEL에서 MediaPipe 데이터를 수신
    if let Some((_, rx)) = &mut *BEVY_CHANNEL.lock().unwrap() {
        while let Ok(msg) = rx.try_recv() {
            // 첫 번째 랜드마크 그룹(예: 한 사람)의 데이터를 사용
            if let Some(landmarks) = msg.data.pose_world_landmarks.get(0) {
                // 보간 계수는 상황에 맞게 조절 (0.0: 즉각, 1.0: 변화 없음)
                let mut after_rotate_elbow: Option<Vec3> = None;

                for (name, mut transform) in transforms.iter_mut() {
                    if let Ok(bone_name) = BoneName::str_to(name) {
                        match bone_name {
                            // 상완근(UpperArmL): 어깨 → 팔꿈치, 회전만 업데이트 (부모–자식 관계 유지)
                            BoneName::UpperArmL => {
                                if check_valid_mediapipe_point_data(Box::new([&landmarks[11], &landmarks[13]])) {
                                    let shoulder = get_vec_by_landmark(&landmarks[11]);
                                    let elbow = get_vec_by_landmark(&landmarks[13]);
                                    // 여기서는 회전만 업데이트 – 로컬 오프셋은 기본값 유지
                                    let temp = update_bone_rotation(&mut transform, shoulder, elbow);
                                    transform.rotation = temp;

                                    // BoneDefaultData에서 rest pose 오프셋(어깨→팔꿈치 로컬 벡터) 가져오기
                                    let default = bone_default_transform.0.get(&BoneName::UpperArmL).unwrap();
                                    let offset = default.translation; // Vec3

                                    // 회전과 스케일을 적용해 팔꿈치 월드 좌표 계산
                                    let elbow_pos = transform.translation + transform.rotation * (offset * transform.scale);
                                    after_rotate_elbow = Some(elbow_pos);
                                }
                            }

                            // 전완근(ForearmL): 팔꿈치 → 손목, 회전만 업데이트
                            BoneName::ForearmL => {
                                if check_valid_mediapipe_point_data(Box::new([&landmarks[13], &landmarks[15]])) {
                                    if let Some(elbow) = after_rotate_elbow {
                                        let wrist = get_vec_by_landmark(&landmarks[15]);
                                        transform.rotation = update_bone_rotation(&mut transform, elbow, wrist);
                                    }
                                }
                            }

                            // 오른쪽 팔의 경우 UpperArmR, ForearmR 등도 유사하게 처리
                            BoneName::UpperArmR => {
                                if check_valid_mediapipe_point_data(Box::new([&landmarks[12], &landmarks[14]])) {
                                    let shoulder = get_vec_by_landmark(&landmarks[12]);
                                    let elbow = get_vec_by_landmark(&landmarks[14]);
                                    transform.rotation = update_bone_rotation(&mut transform, shoulder, elbow);
                                }
                            }

                            BoneName::ForearmR => {
                                if check_valid_mediapipe_point_data(Box::new([&landmarks[14], &landmarks[16]])) {
                                    let elbow = get_vec_by_landmark(&landmarks[14]);
                                    let wrist = get_vec_by_landmark(&landmarks[16]);
                                    transform.rotation = update_bone_rotation(&mut transform, elbow, wrist);
                                }
                            }

                            // 기타 본은 필요에 따라 추가...
                            _ => {}
                        }
                    }
                }
            }
        }
    }
}

It’s taking a lot of time to get this working. Do you have any advice or documentation references I could follow?, I would appreciate any advice you can offer.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.