camera.cpp 20 KB

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