camera.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. #include "camera.h"
  2. #include "../../game/map/visibility_service.h"
  3. #include "render_constants.h"
  4. #include <QtMath>
  5. #include <algorithm>
  6. #include <cmath>
  7. #include <qglobal.h>
  8. #include <qmath.h>
  9. #include <qmatrix4x4.h>
  10. #include <qnumeric.h>
  11. #include <qpoint.h>
  12. #include <qvectornd.h>
  13. namespace Render::GL {
  14. using namespace Render::GL::FrustumPlane;
  15. namespace {
  16. constexpr float k_eps = 1e-6F;
  17. constexpr float k_tiny = 1e-4F;
  18. constexpr float k_min_dist = 1.0F;
  19. constexpr float k_max_dist = 200.0F;
  20. constexpr float k_min_fov = 1.0F;
  21. constexpr float k_max_fov = 89.0F;
  22. constexpr float k_min_margin_percent = 0.03F;
  23. constexpr float k_max_margin_percent = 0.10F;
  24. constexpr float k_boundary_smoothness = 0.3F;
  25. constexpr float k_reference_height = 50.0F;
  26. constexpr float k_height_factor_min = 0.5F;
  27. constexpr float k_height_factor_max = 2.0F;
  28. constexpr float k_max_pitch_angle = 90.0F;
  29. constexpr float k_pitch_factor_min = 0.5F;
  30. constexpr float k_pitch_factor_max = 1.5F;
  31. constexpr float k_max_ortho_scale = 20.0F;
  32. constexpr float k_min_ortho_scale = 0.05F;
  33. constexpr float k_zoom_delta_multiplier = 0.1F;
  34. constexpr float k_zoom_distance_delta = 0.15F;
  35. constexpr float k_zoom_factor_min = 0.1F;
  36. constexpr float k_zoom_factor_max = 10.0F;
  37. constexpr double k_ndc_scale = 2.0;
  38. constexpr double k_ndc_offset = 1.0;
  39. constexpr double k_ndc_half = 0.5;
  40. constexpr float k_boundary_panning_smoothness = 0.7F;
  41. inline auto finite(const QVector3D &v) -> bool {
  42. return qIsFinite(v.x()) && qIsFinite(v.y()) && qIsFinite(v.z());
  43. }
  44. inline auto finite(float v) -> bool { return qIsFinite(v); }
  45. inline auto safeNormalize(const QVector3D &v, const QVector3D &fallback,
  46. float eps = k_eps) -> QVector3D {
  47. if (!finite(v)) {
  48. return fallback;
  49. }
  50. float const len2 = v.lengthSquared();
  51. if (len2 < eps) {
  52. return fallback;
  53. }
  54. return v / std::sqrt(len2);
  55. }
  56. inline void orthonormalize(const QVector3D &frontIn, QVector3D &frontOut,
  57. QVector3D &rightOut, QVector3D &upOut) {
  58. QVector3D const world_up(0.F, 1.F, 0.F);
  59. QVector3D const f = safeNormalize(frontIn, QVector3D(0, 0, -1));
  60. QVector3D u = (std::abs(QVector3D::dotProduct(f, world_up)) > 1.F - 1e-3F)
  61. ? QVector3D(0, 0, 1)
  62. : world_up;
  63. QVector3D r = QVector3D::crossProduct(f, u);
  64. if (r.lengthSquared() < k_eps) {
  65. r = QVector3D(1, 0, 0);
  66. }
  67. r = r.normalized();
  68. u = QVector3D::crossProduct(r, f).normalized();
  69. frontOut = f;
  70. rightOut = r;
  71. upOut = u;
  72. }
  73. inline void clampOrthoBox(float &left, float &right, float &bottom,
  74. float &top) {
  75. if (left == right) {
  76. left -= 0.5F;
  77. right += 0.5F;
  78. } else if (left > right) {
  79. std::swap(left, right);
  80. }
  81. if (bottom == top) {
  82. bottom -= 0.5F;
  83. top += 0.5F;
  84. } else if (bottom > top) {
  85. std::swap(bottom, top);
  86. }
  87. }
  88. inline auto calculateDynamicMargin(float baseMargin, float camera_height,
  89. float pitch_deg) -> float {
  90. float const height_factor =
  91. std::clamp(camera_height / k_reference_height, k_height_factor_min,
  92. k_height_factor_max);
  93. float const pitch_factor =
  94. std::clamp(1.0F - std::abs(pitch_deg) / k_max_pitch_angle,
  95. k_pitch_factor_min, k_pitch_factor_max);
  96. return baseMargin * height_factor * pitch_factor;
  97. }
  98. inline auto smoothApproach(float current, float target,
  99. float smoothness) -> float {
  100. if (std::abs(current - target) < k_tiny) {
  101. return target;
  102. }
  103. return current +
  104. (target - current) * std::clamp(1.0F - smoothness, 0.01F, 0.99F);
  105. }
  106. } // namespace
  107. Camera::Camera() { update_vectors(); }
  108. void Camera::setPosition(const QVector3D &position) {
  109. if (!finite(position)) {
  110. return;
  111. }
  112. m_position = position;
  113. apply_soft_boundaries();
  114. QVector3D const new_front = (m_target - m_position);
  115. orthonormalize(new_front, m_front, m_right, m_up);
  116. }
  117. void Camera::setTarget(const QVector3D &target) {
  118. if (!finite(target)) {
  119. return;
  120. }
  121. m_target = target;
  122. apply_soft_boundaries();
  123. QVector3D dir = (m_target - m_position);
  124. if (dir.lengthSquared() < k_eps) {
  125. m_target =
  126. m_position +
  127. (m_front.lengthSquared() < k_eps ? QVector3D(0, 0, -1) : m_front);
  128. dir = (m_target - m_position);
  129. }
  130. orthonormalize(dir, m_front, m_right, m_up);
  131. }
  132. void Camera::setUp(const QVector3D &up) {
  133. if (!finite(up)) {
  134. return;
  135. }
  136. QVector3D up_n = up;
  137. if (up_n.lengthSquared() < k_eps) {
  138. up_n = QVector3D(0, 1, 0);
  139. }
  140. orthonormalize(m_target - m_position, m_front, m_right, m_up);
  141. }
  142. void Camera::lookAt(const QVector3D &position, const QVector3D &target,
  143. const QVector3D &up) {
  144. if (!finite(position) || !finite(target) || !finite(up)) {
  145. return;
  146. }
  147. m_position = position;
  148. m_target = (position == target) ? position + QVector3D(0, 0, -1) : target;
  149. apply_soft_boundaries();
  150. QVector3D const f = (m_target - m_position);
  151. m_up = up.lengthSquared() < k_eps ? QVector3D(0, 1, 0) : up.normalized();
  152. orthonormalize(f, m_front, m_right, m_up);
  153. }
  154. void Camera::set_perspective(float fov, float aspect, float near_plane,
  155. float far_plane) {
  156. if (!finite(fov) || !finite(aspect) || !finite(near_plane) ||
  157. !finite(far_plane)) {
  158. return;
  159. }
  160. m_isPerspective = true;
  161. m_fov = std::clamp(fov, k_min_fov, k_max_fov);
  162. m_aspect = std::max(aspect, 1e-6F);
  163. m_near_plane = std::max(near_plane, 1e-4F);
  164. m_far_plane = std::max(far_plane, m_near_plane + 1e-3F);
  165. }
  166. void Camera::set_orthographic(float left, float right, float bottom, float top,
  167. float near_plane, float far_plane) {
  168. if (!finite(left) || !finite(right) || !finite(bottom) || !finite(top) ||
  169. !finite(near_plane) || !finite(far_plane)) {
  170. return;
  171. }
  172. m_isPerspective = false;
  173. clampOrthoBox(left, right, bottom, top);
  174. m_orthoLeft = left;
  175. m_orthoRight = right;
  176. m_orthoBottom = bottom;
  177. m_orthoTop = top;
  178. m_near_plane = std::min(near_plane, far_plane - 1e-3F);
  179. m_far_plane = std::max(far_plane, m_near_plane + 1e-3F);
  180. }
  181. void Camera::move_forward(float distance) {
  182. if (!finite(distance)) {
  183. return;
  184. }
  185. m_position += m_front * distance;
  186. m_target = m_position + m_front;
  187. apply_soft_boundaries();
  188. }
  189. void Camera::move_right(float distance) {
  190. if (!finite(distance)) {
  191. return;
  192. }
  193. m_position += m_right * distance;
  194. m_target = m_position + m_front;
  195. apply_soft_boundaries();
  196. }
  197. void Camera::move_up(float distance) {
  198. if (!finite(distance)) {
  199. return;
  200. }
  201. m_position += QVector3D(0, 1, 0) * distance;
  202. m_target = m_position + m_front;
  203. apply_soft_boundaries();
  204. }
  205. void Camera::zoom(float delta) {
  206. if (!finite(delta)) {
  207. return;
  208. }
  209. if (m_isPerspective) {
  210. m_fov = qBound(k_min_fov, m_fov - delta, k_max_fov);
  211. } else {
  212. float scale = 1.0F + delta * k_zoom_delta_multiplier;
  213. if (!finite(scale) || scale <= k_min_ortho_scale) {
  214. scale = k_min_ortho_scale;
  215. }
  216. if (scale > k_max_ortho_scale) {
  217. scale = k_max_ortho_scale;
  218. }
  219. m_orthoLeft *= scale;
  220. m_orthoRight *= scale;
  221. m_orthoBottom *= scale;
  222. m_orthoTop *= scale;
  223. clampOrthoBox(m_orthoLeft, m_orthoRight, m_orthoBottom, m_orthoTop);
  224. }
  225. }
  226. void Camera::zoom_distance(float delta) {
  227. if (!finite(delta)) {
  228. return;
  229. }
  230. QVector3D const offset = m_position - m_target;
  231. float r = offset.length();
  232. if (r < k_tiny) {
  233. r = k_tiny;
  234. }
  235. float factor = 1.0F - delta * k_zoom_distance_delta;
  236. if (!finite(factor)) {
  237. factor = 1.0F;
  238. }
  239. factor = std::clamp(factor, k_zoom_factor_min, k_zoom_factor_max);
  240. float const new_r = std::clamp(r * factor, k_min_dist, k_max_dist);
  241. QVector3D const dir = safeNormalize(offset, QVector3D(0, 0, 1));
  242. QVector3D const new_pos = m_target + dir * new_r;
  243. m_position = new_pos;
  244. apply_soft_boundaries();
  245. QVector3D const f = (m_target - m_position);
  246. orthonormalize(f, m_front, m_right, m_up);
  247. }
  248. void Camera::rotate(float yaw, float pitch) { orbit(yaw, pitch); }
  249. void Camera::pan(float right_dist, float forwardDist) {
  250. if (!finite(right_dist) || !finite(forwardDist)) {
  251. return;
  252. }
  253. QVector3D const right = m_right;
  254. QVector3D front = m_front;
  255. front.setY(0.0F);
  256. if (front.lengthSquared() > 0) {
  257. front.normalize();
  258. }
  259. QVector3D const delta = right * right_dist + front * forwardDist;
  260. if (!finite(delta)) {
  261. return;
  262. }
  263. m_position += delta;
  264. m_target += delta;
  265. apply_soft_boundaries(true);
  266. }
  267. void Camera::elevate(float dy) {
  268. if (!finite(dy)) {
  269. return;
  270. }
  271. m_position.setY(m_position.y() + dy);
  272. apply_soft_boundaries();
  273. }
  274. void Camera::yaw(float degrees) {
  275. if (!finite(degrees)) {
  276. return;
  277. }
  278. orbit(degrees, 0.0F);
  279. }
  280. void Camera::orbit(float yaw_deg, float pitch_deg) {
  281. if (!finite(yaw_deg) || !finite(pitch_deg)) {
  282. return;
  283. }
  284. QVector3D const offset = m_position - m_target;
  285. float cur_yaw = 0.F;
  286. float cur_pitch = 0.F;
  287. computeYawPitchFromOffset(offset, cur_yaw, cur_pitch);
  288. m_orbitStartYaw = cur_yaw;
  289. m_orbitStartPitch = cur_pitch;
  290. m_orbitTargetYaw = cur_yaw + yaw_deg;
  291. m_orbitTargetPitch =
  292. qBound(m_pitchMinDeg, cur_pitch + pitch_deg, m_pitchMaxDeg);
  293. m_orbitTime = 0.0F;
  294. m_orbitPending = true;
  295. }
  296. void Camera::update(float dt) {
  297. if (!m_orbitPending) {
  298. return;
  299. }
  300. if (!finite(dt)) {
  301. return;
  302. }
  303. m_orbitTime += std::max(0.0F, dt);
  304. float const t = (m_orbitDuration <= 0.0F)
  305. ? 1.0F
  306. : std::clamp(m_orbitTime / m_orbitDuration, 0.0F, 1.0F);
  307. float const s = t * t * (3.0F - 2.0F * t);
  308. float const new_yaw =
  309. m_orbitStartYaw + (m_orbitTargetYaw - m_orbitStartYaw) * s;
  310. float const new_pitch =
  311. m_orbitStartPitch + (m_orbitTargetPitch - m_orbitStartPitch) * s;
  312. QVector3D const offset = m_position - m_target;
  313. float r = offset.length();
  314. if (r < k_tiny) {
  315. r = k_tiny;
  316. }
  317. float const yaw_rad = qDegreesToRadians(new_yaw);
  318. float const pitch_rad = qDegreesToRadians(new_pitch);
  319. QVector3D const new_dir(std::sin(yaw_rad) * std::cos(pitch_rad),
  320. std::sin(pitch_rad),
  321. std::cos(yaw_rad) * std::cos(pitch_rad));
  322. QVector3D const fwd = safeNormalize(new_dir, m_front);
  323. m_position = m_target - fwd * r;
  324. apply_soft_boundaries();
  325. orthonormalize((m_target - m_position), m_front, m_right, m_up);
  326. if (t >= 1.0F) {
  327. m_orbitPending = false;
  328. }
  329. }
  330. auto Camera::screen_to_ground(qreal sx, qreal sy, qreal screenW, qreal screenH,
  331. QVector3D &outWorld) const -> bool {
  332. if (screenW <= 0 || screenH <= 0) {
  333. return false;
  334. }
  335. if (!qIsFinite(sx) || !qIsFinite(sy)) {
  336. return false;
  337. }
  338. double const x = (k_ndc_scale * sx / screenW) - k_ndc_offset;
  339. double const y = k_ndc_offset - (k_ndc_scale * sy / screenH);
  340. bool ok = false;
  341. QMatrix4x4 const inv_vp =
  342. (get_projection_matrix() * get_view_matrix()).inverted(&ok);
  343. if (!ok) {
  344. return false;
  345. }
  346. QVector4D const near_clip(float(x), float(y), 0.0F, 1.0F);
  347. QVector4D const far_clip(float(x), float(y), 1.0F, 1.0F);
  348. QVector4D const near_world4 = inv_vp * near_clip;
  349. QVector4D const far_world4 = inv_vp * far_clip;
  350. if (std::abs(near_world4.w()) < k_eps || std::abs(far_world4.w()) < k_eps) {
  351. return false;
  352. }
  353. QVector3D const ray_origin = (near_world4 / near_world4.w()).toVector3D();
  354. QVector3D const ray_end = (far_world4 / far_world4.w()).toVector3D();
  355. if (!finite(ray_origin) || !finite(ray_end)) {
  356. return false;
  357. }
  358. QVector3D const ray_dir =
  359. safeNormalize(ray_end - ray_origin, QVector3D(0, -1, 0));
  360. if (std::abs(ray_dir.y()) < k_eps) {
  361. return false;
  362. }
  363. float const t = (m_ground_y - ray_origin.y()) / ray_dir.y();
  364. if (!finite(t) || t < 0.0F) {
  365. return false;
  366. }
  367. outWorld = ray_origin + ray_dir * t;
  368. return finite(outWorld);
  369. }
  370. auto Camera::world_to_screen(const QVector3D &world, qreal screenW,
  371. qreal screenH, QPointF &outScreen) const -> bool {
  372. if (screenW <= 0 || screenH <= 0) {
  373. return false;
  374. }
  375. if (!finite(world)) {
  376. return false;
  377. }
  378. QVector4D const clip =
  379. get_projection_matrix() * get_view_matrix() * QVector4D(world, 1.0F);
  380. if (std::abs(clip.w()) < k_eps) {
  381. return false;
  382. }
  383. QVector3D const ndc = (clip / clip.w()).toVector3D();
  384. if (!qIsFinite(ndc.x()) || !qIsFinite(ndc.y()) || !qIsFinite(ndc.z())) {
  385. return false;
  386. }
  387. if (ndc.z() < -1.0F || ndc.z() > 1.0F) {
  388. return false;
  389. }
  390. qreal const sx = (ndc.x() * k_ndc_half + k_ndc_half) * screenW;
  391. qreal const sy =
  392. (k_ndc_offset - (ndc.y() * k_ndc_half + k_ndc_half)) * screenH;
  393. outScreen = QPointF(sx, sy);
  394. return qIsFinite(sx) && qIsFinite(sy);
  395. }
  396. void Camera::update_follow(const QVector3D &targetCenter) {
  397. if (!m_followEnabled) {
  398. return;
  399. }
  400. if (!finite(targetCenter)) {
  401. return;
  402. }
  403. if (m_followOffset.lengthSquared() < 1e-5F) {
  404. m_followOffset = m_position - m_target;
  405. }
  406. QVector3D const desired_pos = targetCenter + m_followOffset;
  407. QVector3D const new_pos =
  408. (m_followLerp >= 0.999F)
  409. ? desired_pos
  410. : (m_position +
  411. (desired_pos - m_position) * std::clamp(m_followLerp, 0.0F, 1.0F));
  412. if (!finite(new_pos)) {
  413. return;
  414. }
  415. m_target = targetCenter;
  416. m_position = new_pos;
  417. apply_soft_boundaries();
  418. orthonormalize((m_target - m_position), m_front, m_right, m_up);
  419. }
  420. void Camera::set_rts_view(const QVector3D &center, float distance, float angle,
  421. float yaw_deg) {
  422. if (!finite(center) || !finite(distance) || !finite(angle) ||
  423. !finite(yaw_deg)) {
  424. return;
  425. }
  426. m_target = center;
  427. distance = std::max(distance, 0.01F);
  428. float const pitch_rad = qDegreesToRadians(angle);
  429. float const yaw_rad = qDegreesToRadians(yaw_deg);
  430. float const y = distance * qSin(pitch_rad);
  431. float const horiz = distance * qCos(pitch_rad);
  432. float const x = std::sin(yaw_rad) * horiz;
  433. float const z = std::cos(yaw_rad) * horiz;
  434. m_position = center + QVector3D(x, y, z);
  435. QVector3D const f = (m_target - m_position);
  436. orthonormalize(f, m_front, m_right, m_up);
  437. apply_soft_boundaries();
  438. }
  439. void Camera::set_top_down_view(const QVector3D &center, float distance) {
  440. if (!finite(center) || !finite(distance)) {
  441. return;
  442. }
  443. m_target = center;
  444. m_position = center + QVector3D(0, std::max(distance, 0.01F), 0);
  445. m_up = QVector3D(0, 0, -1);
  446. m_front = safeNormalize((m_target - m_position), QVector3D(0, 0, 1));
  447. update_vectors();
  448. apply_soft_boundaries();
  449. }
  450. auto Camera::get_view_matrix() const -> QMatrix4x4 {
  451. QMatrix4x4 view;
  452. view.lookAt(m_position, m_target, m_up);
  453. return view;
  454. }
  455. auto Camera::get_projection_matrix() const -> QMatrix4x4 {
  456. QMatrix4x4 projection;
  457. if (m_isPerspective) {
  458. projection.perspective(m_fov, m_aspect, m_near_plane, m_far_plane);
  459. } else {
  460. float left = m_orthoLeft;
  461. float right = m_orthoRight;
  462. float bottom = m_orthoBottom;
  463. float top = m_orthoTop;
  464. clampOrthoBox(left, right, bottom, top);
  465. projection.ortho(left, right, bottom, top, m_near_plane, m_far_plane);
  466. }
  467. return projection;
  468. }
  469. auto Camera::get_view_projection_matrix() const -> QMatrix4x4 {
  470. return get_projection_matrix() * get_view_matrix();
  471. }
  472. auto Camera::get_distance() const -> float {
  473. return (m_position - m_target).length();
  474. }
  475. auto Camera::get_pitch_deg() const -> float {
  476. QVector3D const off = m_position - m_target;
  477. QVector3D const dir = -off;
  478. if (dir.lengthSquared() < 1e-6F) {
  479. return 0.0F;
  480. }
  481. float const len_xz = std::sqrt(dir.x() * dir.x() + dir.z() * dir.z());
  482. float const pitch_rad = std::atan2(dir.y(), len_xz);
  483. return qRadiansToDegrees(pitch_rad);
  484. }
  485. void Camera::update_vectors() {
  486. QVector3D const f = (m_target - m_position);
  487. orthonormalize(f, m_front, m_right, m_up);
  488. }
  489. void Camera::apply_soft_boundaries(bool isPanning) {
  490. if (!qIsFinite(m_position.y())) {
  491. return;
  492. }
  493. if (m_position.y() < m_ground_y + m_min_height) {
  494. m_position.setY(m_ground_y + m_min_height);
  495. }
  496. auto &vis = Game::Map::VisibilityService::instance();
  497. if (!vis.is_initialized()) {
  498. return;
  499. }
  500. const float tile = vis.getTileSize();
  501. const float half_w = vis.getWidth() * 0.5F - 0.5F;
  502. const float half_h = vis.getHeight() * 0.5F - 0.5F;
  503. if (tile <= 0.0F || half_w < 0.0F || half_h < 0.0F) {
  504. return;
  505. }
  506. const float map_min_x = -half_w * tile;
  507. const float map_max_x = half_w * tile;
  508. const float map_min_z = -half_h * tile;
  509. const float map_max_z = half_h * tile;
  510. float const camera_height = m_position.y() - m_ground_y;
  511. float const pitch_deg = get_pitch_deg();
  512. float const map_width = map_max_x - map_min_x;
  513. float const map_depth = map_max_z - map_min_z;
  514. float const base_margin_x =
  515. map_width * std::lerp(k_min_margin_percent, k_max_margin_percent,
  516. std::min(camera_height / k_reference_height, 1.0F));
  517. float const base_margin_z =
  518. map_depth * std::lerp(k_min_margin_percent, k_max_margin_percent,
  519. std::min(camera_height / k_reference_height, 1.0F));
  520. float const margin_x =
  521. calculateDynamicMargin(base_margin_x, camera_height, pitch_deg);
  522. float const margin_z =
  523. calculateDynamicMargin(base_margin_z, camera_height, pitch_deg);
  524. float const ext_min_x = map_min_x - margin_x;
  525. float const ext_max_x = map_max_x + margin_x;
  526. float const ext_min_z = map_min_z - margin_z;
  527. float const ext_max_z = map_max_z + margin_z;
  528. QVector3D const target_to_pos = m_position - m_target;
  529. float const target_to_pos_dist = target_to_pos.length();
  530. QVector3D position_adjustment(0, 0, 0);
  531. QVector3D target_adjustment(0, 0, 0);
  532. if (m_position.x() < ext_min_x) {
  533. position_adjustment.setX(ext_min_x - m_position.x());
  534. } else if (m_position.x() > ext_max_x) {
  535. position_adjustment.setX(ext_max_x - m_position.x());
  536. }
  537. if (m_position.z() < ext_min_z) {
  538. position_adjustment.setZ(ext_min_z - m_position.z());
  539. } else if (m_position.z() > ext_max_z) {
  540. position_adjustment.setZ(ext_max_z - m_position.z());
  541. }
  542. if (m_target.x() < map_min_x) {
  543. target_adjustment.setX(map_min_x - m_target.x());
  544. } else if (m_target.x() > map_max_x) {
  545. target_adjustment.setX(map_max_x - m_target.x());
  546. }
  547. if (m_target.z() < map_min_z) {
  548. target_adjustment.setZ(map_min_z - m_target.z());
  549. } else if (m_target.z() > map_max_z) {
  550. target_adjustment.setZ(map_max_z - m_target.z());
  551. }
  552. if (isPanning) {
  553. if ((position_adjustment.x() > 0 && m_lastPosition.x() < m_position.x()) ||
  554. (position_adjustment.x() < 0 && m_lastPosition.x() > m_position.x())) {
  555. position_adjustment.setX(0);
  556. }
  557. if ((position_adjustment.z() > 0 && m_lastPosition.z() < m_position.z()) ||
  558. (position_adjustment.z() < 0 && m_lastPosition.z() > m_position.z())) {
  559. position_adjustment.setZ(0);
  560. }
  561. }
  562. if (!position_adjustment.isNull()) {
  563. m_position +=
  564. position_adjustment *
  565. (isPanning ? k_boundary_panning_smoothness : k_boundary_smoothness);
  566. }
  567. if (!target_adjustment.isNull()) {
  568. m_target += target_adjustment * (isPanning ? k_boundary_panning_smoothness
  569. : k_boundary_smoothness);
  570. if (target_to_pos_dist > k_tiny) {
  571. QVector3D const dir = target_to_pos.normalized();
  572. m_position = m_target + dir * target_to_pos_dist;
  573. }
  574. }
  575. m_lastPosition = m_position;
  576. }
  577. void Camera::clamp_above_ground() {
  578. if (!qIsFinite(m_position.y())) {
  579. return;
  580. }
  581. if (m_position.y() < m_ground_y + m_min_height) {
  582. m_position.setY(m_ground_y + m_min_height);
  583. }
  584. }
  585. void Camera::computeYawPitchFromOffset(const QVector3D &off, float &yaw_deg,
  586. float &pitch_deg) {
  587. QVector3D const dir = -off;
  588. if (dir.lengthSquared() < 1e-6F) {
  589. yaw_deg = 0.F;
  590. pitch_deg = 0.F;
  591. return;
  592. }
  593. float const yaw = qRadiansToDegrees(std::atan2(dir.x(), dir.z()));
  594. float const len_xz = std::sqrt(dir.x() * dir.x() + dir.z() * dir.z());
  595. float const pitch = qRadiansToDegrees(std::atan2(dir.y(), len_xz));
  596. yaw_deg = yaw;
  597. pitch_deg = pitch;
  598. }
  599. auto Camera::is_in_frustum(const QVector3D &center,
  600. float radius) const -> bool {
  601. QMatrix4x4 const vp = get_view_projection_matrix();
  602. float m[MatrixSize];
  603. const float *data = vp.constData();
  604. for (int i = 0; i < MatrixSize; ++i) {
  605. m[i] = data[i];
  606. }
  607. QVector3D const left_n(m[Index3] + m[Index0], m[Index7] + m[Index4],
  608. m[Index11] + m[Index8]);
  609. float const left_d = m[15] + m[12];
  610. QVector3D const right_n(m[Index3] - m[Index0], m[Index7] - m[Index4],
  611. m[Index11] - m[Index8]);
  612. float const right_d = m[15] - m[12];
  613. QVector3D const bottom_n(m[Index3] + m[Index1], m[Index7] + m[Index5],
  614. m[Index11] + m[Index9]);
  615. float const bottom_d = m[15] + m[13];
  616. QVector3D const top_n(m[Index3] - m[Index1], m[Index7] - m[Index5],
  617. m[Index11] - m[Index9]);
  618. float const top_d = m[15] - m[13];
  619. QVector3D const near_n(m[Index3] + m[Index2], m[Index7] + m[Index6],
  620. m[Index11] + m[Index10]);
  621. float const near_d = m[15] + m[14];
  622. QVector3D const far_n(m[Index3] - m[Index2], m[Index7] - m[Index6],
  623. m[Index11] - m[Index10]);
  624. float const far_d = m[15] - m[14];
  625. auto test_plane = [&center, radius](const QVector3D &n, float d) -> bool {
  626. float const len = n.length();
  627. if (len < 1e-6F) {
  628. return true;
  629. }
  630. float const dist = QVector3D::dotProduct(center, n) + d;
  631. return dist >= -radius * len;
  632. };
  633. return test_plane(left_n, left_d) && test_plane(right_n, right_d) &&
  634. test_plane(bottom_n, bottom_d) && test_plane(top_n, top_d) &&
  635. test_plane(near_n, near_d) && test_plane(far_n, far_d);
  636. }
  637. } // namespace Render::GL