pose_controller.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. #include "pose_controller.h"
  2. #include "humanoid_math.h"
  3. #include <QVector3D>
  4. #include <algorithm>
  5. #include <cmath>
  6. #include <numbers>
  7. namespace Render::GL {
  8. HumanoidPoseController::HumanoidPoseController(
  9. HumanoidPose &pose, const HumanoidAnimationContext &anim_ctx)
  10. : m_pose(pose), m_anim_ctx(anim_ctx) {}
  11. void HumanoidPoseController::standIdle() {}
  12. void HumanoidPoseController::kneel(float depth) {
  13. using HP = HumanProportions;
  14. depth = std::clamp(depth, 0.0F, 1.0F);
  15. float const kneel_offset = depth * 0.40F;
  16. float const pelvis_y = HP::WAIST_Y - kneel_offset;
  17. m_pose.pelvisPos.setY(pelvis_y);
  18. float const stance_narrow = 0.11F;
  19. float const left_knee_y = HP::GROUND_Y + 0.07F * depth;
  20. float const left_knee_z = -0.06F * depth;
  21. m_pose.knee_l = QVector3D(-stance_narrow, left_knee_y, left_knee_z);
  22. m_pose.footL = QVector3D(-stance_narrow - 0.025F, HP::GROUND_Y,
  23. left_knee_z - HP::LOWER_LEG_LEN * 0.93F * depth);
  24. float const right_knee_y = pelvis_y - 0.12F;
  25. float const right_foot_z = 0.28F * depth;
  26. m_pose.knee_r = QVector3D(stance_narrow, right_knee_y, right_foot_z - 0.05F);
  27. m_pose.foot_r =
  28. QVector3D(stance_narrow, HP::GROUND_Y + m_pose.footYOffset, right_foot_z);
  29. float const upper_body_drop = kneel_offset;
  30. m_pose.shoulderL.setY(m_pose.shoulderL.y() - upper_body_drop);
  31. m_pose.shoulderR.setY(m_pose.shoulderR.y() - upper_body_drop);
  32. m_pose.neck_base.setY(m_pose.neck_base.y() - upper_body_drop);
  33. m_pose.headPos.setY(m_pose.headPos.y() - upper_body_drop);
  34. }
  35. void HumanoidPoseController::lean(const QVector3D &direction, float amount) {
  36. amount = std::clamp(amount, 0.0F, 1.0F);
  37. QVector3D dir = direction;
  38. if (dir.lengthSquared() > 1e-6F) {
  39. dir.normalize();
  40. } else {
  41. dir = QVector3D(0.0F, 0.0F, 1.0F);
  42. }
  43. float const lean_magnitude = 0.12F * amount;
  44. QVector3D const lean_offset = dir * lean_magnitude;
  45. m_pose.shoulderL += lean_offset;
  46. m_pose.shoulderR += lean_offset;
  47. m_pose.neck_base += lean_offset * 0.85F;
  48. m_pose.headPos += lean_offset * 0.75F;
  49. }
  50. void HumanoidPoseController::placeHandAt(bool is_left,
  51. const QVector3D &target_position) {
  52. getHand(is_left) = target_position;
  53. const QVector3D &shoulder = getShoulder(is_left);
  54. const QVector3D outward_dir = computeOutwardDir(is_left);
  55. float const along_frac = is_left ? 0.45F : 0.48F;
  56. float const lateral_offset = is_left ? 0.15F : 0.12F;
  57. float const y_bias = is_left ? -0.08F : 0.02F;
  58. float const outward_sign = 1.0F;
  59. getElbow(is_left) =
  60. solveElbowIK(is_left, shoulder, target_position, outward_dir, along_frac,
  61. lateral_offset, y_bias, outward_sign);
  62. }
  63. auto HumanoidPoseController::solveElbowIK(
  64. bool, const QVector3D &shoulder, const QVector3D &hand,
  65. const QVector3D &outward_dir, float along_frac, float lateral_offset,
  66. float y_bias, float outward_sign) const -> QVector3D {
  67. return elbowBendTorso(shoulder, hand, outward_dir, along_frac, lateral_offset,
  68. y_bias, outward_sign);
  69. }
  70. auto HumanoidPoseController::solveKneeIK(
  71. bool is_left, const QVector3D &hip, const QVector3D &foot,
  72. float height_scale) const -> QVector3D {
  73. using HP = HumanProportions;
  74. QVector3D hip_to_foot = foot - hip;
  75. float const distance = hip_to_foot.length();
  76. if (distance < 1e-5F) {
  77. return hip;
  78. }
  79. float const upper_len = HP::UPPER_LEG_LEN * height_scale;
  80. float const lower_len = HP::LOWER_LEG_LEN * height_scale;
  81. float const reach = upper_len + lower_len;
  82. float const min_reach =
  83. std::max(std::abs(upper_len - lower_len) + 1e-4F, 1e-3F);
  84. float const max_reach = std::max(reach - 1e-4F, min_reach + 1e-4F);
  85. float const clamped_dist = std::clamp(distance, min_reach, max_reach);
  86. QVector3D const dir = hip_to_foot / distance;
  87. float cos_theta = (upper_len * upper_len + clamped_dist * clamped_dist -
  88. lower_len * lower_len) /
  89. (2.0F * upper_len * clamped_dist);
  90. cos_theta = std::clamp(cos_theta, -1.0F, 1.0F);
  91. float const sin_theta =
  92. std::sqrt(std::max(0.0F, 1.0F - cos_theta * cos_theta));
  93. QVector3D bend_pref =
  94. is_left ? QVector3D(-0.24F, 0.0F, 0.95F) : QVector3D(0.24F, 0.0F, 0.95F);
  95. bend_pref.normalize();
  96. QVector3D bend_axis = bend_pref - dir * QVector3D::dotProduct(dir, bend_pref);
  97. if (bend_axis.lengthSquared() < 1e-6F) {
  98. bend_axis = QVector3D::crossProduct(dir, QVector3D(0.0F, 1.0F, 0.0F));
  99. if (bend_axis.lengthSquared() < 1e-6F) {
  100. bend_axis = QVector3D::crossProduct(dir, QVector3D(1.0F, 0.0F, 0.0F));
  101. }
  102. }
  103. bend_axis.normalize();
  104. QVector3D const knee =
  105. hip + dir * (cos_theta * upper_len) + bend_axis * (sin_theta * upper_len);
  106. float const knee_floor = HP::GROUND_Y + m_pose.footYOffset * 0.5F;
  107. if (knee.y() < knee_floor) {
  108. QVector3D adjusted = knee;
  109. adjusted.setY(knee_floor);
  110. return adjusted;
  111. }
  112. return knee;
  113. }
  114. auto HumanoidPoseController::getShoulder(bool is_left) const
  115. -> const QVector3D & {
  116. return is_left ? m_pose.shoulderL : m_pose.shoulderR;
  117. }
  118. auto HumanoidPoseController::getHand(bool is_left) -> QVector3D & {
  119. return is_left ? m_pose.handL : m_pose.hand_r;
  120. }
  121. auto HumanoidPoseController::getHand(bool is_left) const -> const QVector3D & {
  122. return is_left ? m_pose.handL : m_pose.hand_r;
  123. }
  124. auto HumanoidPoseController::getElbow(bool is_left) -> QVector3D & {
  125. return is_left ? m_pose.elbowL : m_pose.elbowR;
  126. }
  127. auto HumanoidPoseController::computeRightAxis() const -> QVector3D {
  128. QVector3D right_axis = m_pose.shoulderR - m_pose.shoulderL;
  129. right_axis.setY(0.0F);
  130. if (right_axis.lengthSquared() < 1e-8F) {
  131. right_axis = QVector3D(1.0F, 0.0F, 0.0F);
  132. }
  133. right_axis.normalize();
  134. return right_axis;
  135. }
  136. auto HumanoidPoseController::computeOutwardDir(bool is_left) const
  137. -> QVector3D {
  138. QVector3D const right_axis = computeRightAxis();
  139. return is_left ? -right_axis : right_axis;
  140. }
  141. } // namespace Render::GL