transforms.cpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #include "transforms.h"
  2. #include <algorithm>
  3. #include <cmath>
  4. #include <qmatrix4x4.h>
  5. #include <qvectornd.h>
  6. namespace Render::Geom {
  7. namespace {
  8. const QVector3D k_yaxis(0, 1, 0);
  9. const float k_rad_to_deg = 57.2957795131F;
  10. const float k_epsilon = 1e-6F;
  11. const float k_epsilon_sq = k_epsilon * k_epsilon;
  12. constexpr float k_flip_rotation_degrees = 180.0F;
  13. } // namespace
  14. auto cylinderBetween(const QVector3D &a, const QVector3D &b,
  15. float radius) -> QMatrix4x4 {
  16. const float dx = b.x() - a.x();
  17. const float dy = b.y() - a.y();
  18. const float dz = b.z() - a.z();
  19. const float len_sq = dx * dx + dy * dy + dz * dz;
  20. QMatrix4x4 m;
  21. m.translate((a.x() + b.x()) * 0.5F, (a.y() + b.y()) * 0.5F,
  22. (a.z() + b.z()) * 0.5F);
  23. if (len_sq > k_epsilon_sq) {
  24. const float len = std::sqrt(len_sq);
  25. const float inv_len = 1.0F / len;
  26. const float ndx = dx * inv_len;
  27. const float ndy = dy * inv_len;
  28. const float ndz = dz * inv_len;
  29. const float dot = std::clamp(ndy, -1.0F, 1.0F);
  30. const float angle_deg = std::acos(dot) * k_rad_to_deg;
  31. const float axis_x = ndz;
  32. const float axis_z = -ndx;
  33. const float axis_len_sq = axis_x * axis_x + axis_z * axis_z;
  34. if (axis_len_sq < k_epsilon_sq) {
  35. if (dot < 0.0F) {
  36. m.rotate(k_flip_rotation_degrees, 1.0F, 0.0F, 0.0F);
  37. }
  38. } else {
  39. const float axis_inv_len = 1.0F / std::sqrt(axis_len_sq);
  40. m.rotate(angle_deg, axis_x * axis_inv_len, 0.0F, axis_z * axis_inv_len);
  41. }
  42. m.scale(radius, len, radius);
  43. } else {
  44. m.scale(radius, 1.0F, radius);
  45. }
  46. return m;
  47. }
  48. auto sphereAt(const QVector3D &pos, float radius) -> QMatrix4x4 {
  49. QMatrix4x4 m;
  50. m.translate(pos);
  51. m.scale(radius, radius, radius);
  52. return m;
  53. }
  54. auto sphereAt(const QMatrix4x4 &parent, const QVector3D &pos,
  55. float radius) -> QMatrix4x4 {
  56. QMatrix4x4 m = parent;
  57. m.translate(pos);
  58. m.scale(radius, radius, radius);
  59. return m;
  60. }
  61. auto cylinderBetween(const QMatrix4x4 &parent, const QVector3D &a,
  62. const QVector3D &b, float radius) -> QMatrix4x4 {
  63. const float dx = b.x() - a.x();
  64. const float dy = b.y() - a.y();
  65. const float dz = b.z() - a.z();
  66. const float len_sq = dx * dx + dy * dy + dz * dz;
  67. QMatrix4x4 m = parent;
  68. m.translate((a.x() + b.x()) * 0.5F, (a.y() + b.y()) * 0.5F,
  69. (a.z() + b.z()) * 0.5F);
  70. if (len_sq > k_epsilon_sq) {
  71. const float len = std::sqrt(len_sq);
  72. const float inv_len = 1.0F / len;
  73. const float ndx = dx * inv_len;
  74. const float ndy = dy * inv_len;
  75. const float ndz = dz * inv_len;
  76. const float dot = std::clamp(ndy, -1.0F, 1.0F);
  77. const float angle_deg = std::acos(dot) * k_rad_to_deg;
  78. const float axis_x = ndz;
  79. const float axis_z = -ndx;
  80. const float axis_len_sq = axis_x * axis_x + axis_z * axis_z;
  81. if (axis_len_sq < k_epsilon_sq) {
  82. if (dot < 0.0F) {
  83. m.rotate(k_flip_rotation_degrees, 1.0F, 0.0F, 0.0F);
  84. }
  85. } else {
  86. const float axis_inv_len = 1.0F / std::sqrt(axis_len_sq);
  87. m.rotate(angle_deg, axis_x * axis_inv_len, 0.0F, axis_z * axis_inv_len);
  88. }
  89. m.scale(radius, len, radius);
  90. } else {
  91. m.scale(radius, 1.0F, radius);
  92. }
  93. return m;
  94. }
  95. auto coneFromTo(const QVector3D &base_center, const QVector3D &apex,
  96. float base_radius) -> QMatrix4x4 {
  97. return cylinderBetween(base_center, apex, base_radius);
  98. }
  99. auto coneFromTo(const QMatrix4x4 &parent, const QVector3D &base_center,
  100. const QVector3D &apex, float base_radius) -> QMatrix4x4 {
  101. return cylinderBetween(parent, base_center, apex, base_radius);
  102. }
  103. auto capsuleBetween(const QVector3D &a, const QVector3D &b,
  104. float radius) -> QMatrix4x4 {
  105. return cylinderBetween(a, b, radius);
  106. }
  107. auto capsuleBetween(const QMatrix4x4 &parent, const QVector3D &a,
  108. const QVector3D &b, float radius) -> QMatrix4x4 {
  109. return cylinderBetween(parent, a, b, radius);
  110. }
  111. } // namespace Render::Geom