CmCamera.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. /*
  2. -----------------------------------------------------------------------------
  3. This source file is part of OGRE
  4. (Object-oriented Graphics Rendering Engine)
  5. For the latest info, see http://www.ogre3d.org
  6. Copyright (c) 2000-2011 Torus Knot Software Ltd
  7. Permission is hereby granted, free of charge, to any person obtaining a copy
  8. of this software and associated documentation files (the "Software"), to deal
  9. in the Software without restriction, including without limitation the rights
  10. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. copies of the Software, and to permit persons to whom the Software is
  12. furnished to do so, subject to the following conditions:
  13. The above copyright notice and this permission notice shall be included in
  14. all copies or substantial portions of the Software.
  15. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. THE SOFTWARE.
  22. -----------------------------------------------------------------------------
  23. */
  24. #include "CmCamera.h"
  25. #include "CmCameraRTTI.h"
  26. #include "CmMath.h"
  27. #include "CmMatrix3.h"
  28. #include "CmVector2.h"
  29. #include "CmAxisAlignedBox.h"
  30. #include "CmSphere.h"
  31. #include "CmHardwareBufferManager.h"
  32. #include "CmVertexBuffer.h"
  33. #include "CmIndexBuffer.h"
  34. #include "CmException.h"
  35. #include "CmRenderSystem.h"
  36. #include "CmSceneObject.h"
  37. namespace CamelotEngine {
  38. const float Camera::INFINITE_FAR_PLANE_ADJUST = 0.00001f;
  39. //-----------------------------------------------------------------------
  40. Camera::Camera(const HSceneObject& parent)
  41. : Component(parent),
  42. mProjType(PT_PERSPECTIVE),
  43. mFOVy(Radian(Math::PI/4.0f)),
  44. mFarDist(100000.0f),
  45. mNearDist(100.0f),
  46. mAspect(1.33333333333333f),
  47. mOrthoHeight(1000),
  48. mFrustumOffset(Vector2::ZERO),
  49. mFocalLength(1.0f),
  50. mLastParentOrientation(Quaternion::IDENTITY),
  51. mLastParentPosition(Vector3::ZERO),
  52. mRecalcFrustum(true),
  53. mRecalcFrustumPlanes(true),
  54. mRecalcWorldSpaceCorners(true),
  55. mRecalcVertexData(true),
  56. mCustomViewMatrix(false),
  57. mCustomProjMatrix(false),
  58. mFrustumExtentsManuallySet(false)
  59. {
  60. updateView();
  61. updateFrustum();
  62. // Reasonable defaults to camera params
  63. mFOVy = Radian(Math::PI/4.0f);
  64. mNearDist = 100.0f;
  65. mFarDist = 100000.0f;
  66. mAspect = 1.33333333333333f;
  67. mProjType = PT_PERSPECTIVE;
  68. invalidateFrustum();
  69. // Init matrices
  70. mViewMatrix = Matrix4::ZERO;
  71. mProjMatrixRS = Matrix4::ZERO;
  72. }
  73. //-----------------------------------------------------------------------
  74. Camera::~Camera()
  75. {
  76. }
  77. void Camera::init(RenderTargetPtr target, float left, float top, float width, float height, int ZOrder)
  78. {
  79. mViewport = ViewportPtr(CM_NEW(Viewport, PoolAlloc) Viewport(target, left, top, width, height, ZOrder),
  80. &MemAllocDeleter<Viewport, PoolAlloc>::deleter);
  81. }
  82. //-----------------------------------------------------------------------
  83. void Camera::setFOVy(const Radian& fov)
  84. {
  85. mFOVy = fov;
  86. invalidateFrustum();
  87. }
  88. //-----------------------------------------------------------------------
  89. const Radian& Camera::getFOVy(void) const
  90. {
  91. return mFOVy;
  92. }
  93. //-----------------------------------------------------------------------
  94. void Camera::setFarClipDistance(float farPlane)
  95. {
  96. mFarDist = farPlane;
  97. invalidateFrustum();
  98. }
  99. //-----------------------------------------------------------------------
  100. float Camera::getFarClipDistance(void) const
  101. {
  102. return mFarDist;
  103. }
  104. //-----------------------------------------------------------------------
  105. void Camera::setNearClipDistance(float nearPlane)
  106. {
  107. if (nearPlane <= 0)
  108. {
  109. CM_EXCEPT(InvalidParametersException, "Near clip distance must be greater than zero.");
  110. }
  111. mNearDist = nearPlane;
  112. invalidateFrustum();
  113. }
  114. //-----------------------------------------------------------------------
  115. float Camera::getNearClipDistance(void) const
  116. {
  117. return mNearDist;
  118. }
  119. //---------------------------------------------------------------------
  120. void Camera::setFrustumOffset(const Vector2& offset)
  121. {
  122. mFrustumOffset = offset;
  123. invalidateFrustum();
  124. }
  125. //---------------------------------------------------------------------
  126. void Camera::setFrustumOffset(float horizontal, float vertical)
  127. {
  128. setFrustumOffset(Vector2(horizontal, vertical));
  129. }
  130. //---------------------------------------------------------------------
  131. const Vector2& Camera::getFrustumOffset() const
  132. {
  133. return mFrustumOffset;
  134. }
  135. //---------------------------------------------------------------------
  136. void Camera::setFocalLength(float focalLength)
  137. {
  138. if (focalLength <= 0)
  139. {
  140. CM_EXCEPT(InvalidParametersException,
  141. "Focal length must be greater than zero.");
  142. }
  143. mFocalLength = focalLength;
  144. invalidateFrustum();
  145. }
  146. //---------------------------------------------------------------------
  147. float Camera::getFocalLength() const
  148. {
  149. return mFocalLength;
  150. }
  151. //-----------------------------------------------------------------------
  152. const Matrix4& Camera::getProjectionMatrix(void) const
  153. {
  154. updateFrustum();
  155. return mProjMatrix;
  156. }
  157. //-----------------------------------------------------------------------
  158. const Matrix4& Camera::getProjectionMatrixWithRSDepth(void) const
  159. {
  160. updateFrustum();
  161. return mProjMatrixRSDepth;
  162. }
  163. //-----------------------------------------------------------------------
  164. const Matrix4& Camera::getProjectionMatrixRS(void) const
  165. {
  166. updateFrustum();
  167. return mProjMatrixRS;
  168. }
  169. //-----------------------------------------------------------------------
  170. const Matrix4& Camera::getViewMatrix(void) const
  171. {
  172. updateView();
  173. return mViewMatrix;
  174. }
  175. //-----------------------------------------------------------------------
  176. const Plane* Camera::getFrustumPlanes(void) const
  177. {
  178. // Make any pending updates to the calculated frustum planes
  179. updateFrustumPlanes();
  180. return mFrustumPlanes;
  181. }
  182. //-----------------------------------------------------------------------
  183. const Plane& Camera::getFrustumPlane(unsigned short plane) const
  184. {
  185. // Make any pending updates to the calculated frustum planes
  186. updateFrustumPlanes();
  187. return mFrustumPlanes[plane];
  188. }
  189. //-----------------------------------------------------------------------
  190. bool Camera::isVisible(const AxisAlignedBox& bound, FrustumPlane* culledBy) const
  191. {
  192. // Null boxes always invisible
  193. if (bound.isNull()) return false;
  194. // Infinite boxes always visible
  195. if (bound.isInfinite()) return true;
  196. // Make any pending updates to the calculated frustum planes
  197. updateFrustumPlanes();
  198. // Get centre of the box
  199. Vector3 centre = bound.getCenter();
  200. // Get the half-size of the box
  201. Vector3 halfSize = bound.getHalfSize();
  202. // For each plane, see if all points are on the negative side
  203. // If so, object is not visible
  204. for (int plane = 0; plane < 6; ++plane)
  205. {
  206. // Skip far plane if infinite view frustum
  207. if (plane == FRUSTUM_PLANE_FAR && mFarDist == 0)
  208. continue;
  209. Plane::Side side = mFrustumPlanes[plane].getSide(centre, halfSize);
  210. if (side == Plane::NEGATIVE_SIDE)
  211. {
  212. // ALL corners on negative side therefore out of view
  213. if (culledBy)
  214. *culledBy = (FrustumPlane)plane;
  215. return false;
  216. }
  217. }
  218. return true;
  219. }
  220. //-----------------------------------------------------------------------
  221. bool Camera::isVisible(const Vector3& vert, FrustumPlane* culledBy) const
  222. {
  223. // Make any pending updates to the calculated frustum planes
  224. updateFrustumPlanes();
  225. // For each plane, see if all points are on the negative side
  226. // If so, object is not visible
  227. for (int plane = 0; plane < 6; ++plane)
  228. {
  229. // Skip far plane if infinite view frustum
  230. if (plane == FRUSTUM_PLANE_FAR && mFarDist == 0)
  231. continue;
  232. if (mFrustumPlanes[plane].getSide(vert) == Plane::NEGATIVE_SIDE)
  233. {
  234. // ALL corners on negative side therefore out of view
  235. if (culledBy)
  236. *culledBy = (FrustumPlane)plane;
  237. return false;
  238. }
  239. }
  240. return true;
  241. }
  242. //-----------------------------------------------------------------------
  243. bool Camera::isVisible(const Sphere& sphere, FrustumPlane* culledBy) const
  244. {
  245. // Make any pending updates to the calculated frustum planes
  246. updateFrustumPlanes();
  247. // For each plane, see if sphere is on negative side
  248. // If so, object is not visible
  249. for (int plane = 0; plane < 6; ++plane)
  250. {
  251. // Skip far plane if infinite view frustum
  252. if (plane == FRUSTUM_PLANE_FAR && mFarDist == 0)
  253. continue;
  254. // If the distance from sphere center to plane is negative, and 'more negative'
  255. // than the radius of the sphere, sphere is outside frustum
  256. if (mFrustumPlanes[plane].getDistance(sphere.getCenter()) < -sphere.getRadius())
  257. {
  258. // ALL corners on negative side therefore out of view
  259. if (culledBy)
  260. *culledBy = (FrustumPlane)plane;
  261. return false;
  262. }
  263. }
  264. return true;
  265. }
  266. //-----------------------------------------------------------------------
  267. void Camera::calcProjectionParameters(float& left, float& right, float& bottom, float& top) const
  268. {
  269. if (mCustomProjMatrix)
  270. {
  271. // Convert clipspace corners to camera space
  272. Matrix4 invProj = mProjMatrix.inverse();
  273. Vector3 topLeft(-0.5f, 0.5f, 0.0f);
  274. Vector3 bottomRight(0.5f, -0.5f, 0.0f);
  275. topLeft = invProj * topLeft;
  276. bottomRight = invProj * bottomRight;
  277. left = topLeft.x;
  278. top = topLeft.y;
  279. right = bottomRight.x;
  280. bottom = bottomRight.y;
  281. }
  282. else
  283. {
  284. if (mFrustumExtentsManuallySet)
  285. {
  286. left = mLeft;
  287. right = mRight;
  288. top = mTop;
  289. bottom = mBottom;
  290. }
  291. // Calculate general projection parameters
  292. else if (mProjType == PT_PERSPECTIVE)
  293. {
  294. Radian thetaY (mFOVy * 0.5f);
  295. float tanThetaY = Math::Tan(thetaY);
  296. float tanThetaX = tanThetaY * mAspect;
  297. float nearFocal = mNearDist / mFocalLength;
  298. float nearOffsetX = mFrustumOffset.x * nearFocal;
  299. float nearOffsetY = mFrustumOffset.y * nearFocal;
  300. float half_w = tanThetaX * mNearDist;
  301. float half_h = tanThetaY * mNearDist;
  302. left = - half_w + nearOffsetX;
  303. right = + half_w + nearOffsetX;
  304. bottom = - half_h + nearOffsetY;
  305. top = + half_h + nearOffsetY;
  306. mLeft = left;
  307. mRight = right;
  308. mTop = top;
  309. mBottom = bottom;
  310. }
  311. else
  312. {
  313. // Unknown how to apply frustum offset to orthographic camera, just ignore here
  314. float half_w = getOrthoWindowWidth() * 0.5f;
  315. float half_h = getOrthoWindowHeight() * 0.5f;
  316. left = - half_w;
  317. right = + half_w;
  318. bottom = - half_h;
  319. top = + half_h;
  320. mLeft = left;
  321. mRight = right;
  322. mTop = top;
  323. mBottom = bottom;
  324. }
  325. }
  326. }
  327. //-----------------------------------------------------------------------
  328. void Camera::updateFrustumImpl(void) const
  329. {
  330. // Common calcs
  331. float left, right, bottom, top;
  332. calcProjectionParameters(left, right, bottom, top);
  333. if (!mCustomProjMatrix)
  334. {
  335. // The code below will dealing with general projection
  336. // parameters, similar glFrustum and glOrtho.
  337. // Doesn't optimise manually except division operator, so the
  338. // code more self-explaining.
  339. float inv_w = 1 / (right - left);
  340. float inv_h = 1 / (top - bottom);
  341. float inv_d = 1 / (mFarDist - mNearDist);
  342. // Recalc if frustum params changed
  343. if (mProjType == PT_PERSPECTIVE)
  344. {
  345. // Calc matrix elements
  346. float A = 2 * mNearDist * inv_w;
  347. float B = 2 * mNearDist * inv_h;
  348. float C = (right + left) * inv_w;
  349. float D = (top + bottom) * inv_h;
  350. float q, qn;
  351. if (mFarDist == 0)
  352. {
  353. // Infinite far plane
  354. q = Camera::INFINITE_FAR_PLANE_ADJUST - 1;
  355. qn = mNearDist * (Camera::INFINITE_FAR_PLANE_ADJUST - 2);
  356. }
  357. else
  358. {
  359. q = - (mFarDist + mNearDist) * inv_d;
  360. qn = -2 * (mFarDist * mNearDist) * inv_d;
  361. }
  362. // NB: This creates 'uniform' perspective projection matrix,
  363. // which depth range [-1,1], right-handed rules
  364. //
  365. // [ A 0 C 0 ]
  366. // [ 0 B D 0 ]
  367. // [ 0 0 q qn ]
  368. // [ 0 0 -1 0 ]
  369. //
  370. // A = 2 * near / (right - left)
  371. // B = 2 * near / (top - bottom)
  372. // C = (right + left) / (right - left)
  373. // D = (top + bottom) / (top - bottom)
  374. // q = - (far + near) / (far - near)
  375. // qn = - 2 * (far * near) / (far - near)
  376. mProjMatrix = Matrix4::ZERO;
  377. mProjMatrix[0][0] = A;
  378. mProjMatrix[0][2] = C;
  379. mProjMatrix[1][1] = B;
  380. mProjMatrix[1][2] = D;
  381. mProjMatrix[2][2] = q;
  382. mProjMatrix[2][3] = qn;
  383. mProjMatrix[3][2] = -1;
  384. } // perspective
  385. else if (mProjType == PT_ORTHOGRAPHIC)
  386. {
  387. float A = 2 * inv_w;
  388. float B = 2 * inv_h;
  389. float C = - (right + left) * inv_w;
  390. float D = - (top + bottom) * inv_h;
  391. float q, qn;
  392. if (mFarDist == 0)
  393. {
  394. // Can not do infinite far plane here, avoid divided zero only
  395. q = - Camera::INFINITE_FAR_PLANE_ADJUST / mNearDist;
  396. qn = - Camera::INFINITE_FAR_PLANE_ADJUST - 1;
  397. }
  398. else
  399. {
  400. q = - 2 * inv_d;
  401. qn = - (mFarDist + mNearDist) * inv_d;
  402. }
  403. // NB: This creates 'uniform' orthographic projection matrix,
  404. // which depth range [-1,1], right-handed rules
  405. //
  406. // [ A 0 0 C ]
  407. // [ 0 B 0 D ]
  408. // [ 0 0 q qn ]
  409. // [ 0 0 0 1 ]
  410. //
  411. // A = 2 * / (right - left)
  412. // B = 2 * / (top - bottom)
  413. // C = - (right + left) / (right - left)
  414. // D = - (top + bottom) / (top - bottom)
  415. // q = - 2 / (far - near)
  416. // qn = - (far + near) / (far - near)
  417. mProjMatrix = Matrix4::ZERO;
  418. mProjMatrix[0][0] = A;
  419. mProjMatrix[0][3] = C;
  420. mProjMatrix[1][1] = B;
  421. mProjMatrix[1][3] = D;
  422. mProjMatrix[2][2] = q;
  423. mProjMatrix[2][3] = qn;
  424. mProjMatrix[3][3] = 1;
  425. } // ortho
  426. } // !mCustomProjMatrix
  427. RenderSystem* renderSystem = CamelotEngine::RenderSystem::instancePtr();
  428. // API specific
  429. renderSystem->convertProjectionMatrix(mProjMatrix, mProjMatrixRS);
  430. // API specific for Gpu Programs
  431. renderSystem->convertProjectionMatrix(mProjMatrix, mProjMatrixRSDepth, true);
  432. // Calculate bounding box (local)
  433. // Box is from 0, down -Z, max dimensions as determined from far plane
  434. // If infinite view frustum just pick a far value
  435. float farDist = (mFarDist == 0) ? 100000 : mFarDist;
  436. // Near plane bounds
  437. Vector3 min(left, bottom, -farDist);
  438. Vector3 max(right, top, 0);
  439. if (mCustomProjMatrix)
  440. {
  441. // Some custom projection matrices can have unusual inverted settings
  442. // So make sure the AABB is the right way around to start with
  443. Vector3 tmp = min;
  444. min.makeFloor(max);
  445. max.makeCeil(tmp);
  446. }
  447. if (mProjType == PT_PERSPECTIVE)
  448. {
  449. // Merge with far plane bounds
  450. float radio = farDist / mNearDist;
  451. min.makeFloor(Vector3(left * radio, bottom * radio, -farDist));
  452. max.makeCeil(Vector3(right * radio, top * radio, 0));
  453. }
  454. mBoundingBox.setExtents(min, max);
  455. mRecalcFrustum = false;
  456. // Signal to update frustum clipping planes
  457. mRecalcFrustumPlanes = true;
  458. }
  459. //-----------------------------------------------------------------------
  460. void Camera::updateFrustum(void) const
  461. {
  462. if (isFrustumOutOfDate())
  463. {
  464. updateFrustumImpl();
  465. }
  466. }
  467. //-----------------------------------------------------------------------
  468. bool Camera::isFrustumOutOfDate(void) const
  469. {
  470. return mRecalcFrustum;
  471. }
  472. //-----------------------------------------------------------------------
  473. void Camera::updateView(void) const
  474. {
  475. if (!mCustomViewMatrix)
  476. {
  477. Matrix3 rot;
  478. const Quaternion& orientation = sceneObject()->getWorldRotation();
  479. const Vector3& position = sceneObject()->getWorldPosition();
  480. mViewMatrix = Math::makeViewMatrix(position, orientation, 0);
  481. }
  482. }
  483. //-----------------------------------------------------------------------
  484. void Camera::updateFrustumPlanesImpl(void) const
  485. {
  486. // -------------------------
  487. // Update the frustum planes
  488. // -------------------------
  489. Matrix4 combo = mProjMatrix * mViewMatrix;
  490. mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.x = combo[3][0] + combo[0][0];
  491. mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.y = combo[3][1] + combo[0][1];
  492. mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.z = combo[3][2] + combo[0][2];
  493. mFrustumPlanes[FRUSTUM_PLANE_LEFT].d = combo[3][3] + combo[0][3];
  494. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.x = combo[3][0] - combo[0][0];
  495. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.y = combo[3][1] - combo[0][1];
  496. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.z = combo[3][2] - combo[0][2];
  497. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].d = combo[3][3] - combo[0][3];
  498. mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.x = combo[3][0] - combo[1][0];
  499. mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.y = combo[3][1] - combo[1][1];
  500. mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.z = combo[3][2] - combo[1][2];
  501. mFrustumPlanes[FRUSTUM_PLANE_TOP].d = combo[3][3] - combo[1][3];
  502. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.x = combo[3][0] + combo[1][0];
  503. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.y = combo[3][1] + combo[1][1];
  504. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.z = combo[3][2] + combo[1][2];
  505. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].d = combo[3][3] + combo[1][3];
  506. mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.x = combo[3][0] + combo[2][0];
  507. mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.y = combo[3][1] + combo[2][1];
  508. mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.z = combo[3][2] + combo[2][2];
  509. mFrustumPlanes[FRUSTUM_PLANE_NEAR].d = combo[3][3] + combo[2][3];
  510. mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.x = combo[3][0] - combo[2][0];
  511. mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.y = combo[3][1] - combo[2][1];
  512. mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.z = combo[3][2] - combo[2][2];
  513. mFrustumPlanes[FRUSTUM_PLANE_FAR].d = combo[3][3] - combo[2][3];
  514. // Renormalise any normals which were not unit length
  515. for(int i=0; i<6; i++ )
  516. {
  517. float length = mFrustumPlanes[i].normal.normalise();
  518. mFrustumPlanes[i].d /= length;
  519. }
  520. mRecalcFrustumPlanes = false;
  521. }
  522. //-----------------------------------------------------------------------
  523. void Camera::updateFrustumPlanes(void) const
  524. {
  525. updateView();
  526. updateFrustum();
  527. if (mRecalcFrustumPlanes)
  528. {
  529. updateFrustumPlanesImpl();
  530. }
  531. }
  532. //-----------------------------------------------------------------------
  533. void Camera::updateWorldSpaceCornersImpl(void) const
  534. {
  535. Matrix4 eyeToWorld = mViewMatrix.inverseAffine();
  536. // Note: Even though we can dealing with general projection matrix here,
  537. // but because it's incompatibly with infinite far plane, thus, we
  538. // still need to working with projection parameters.
  539. // Calc near plane corners
  540. float nearLeft, nearRight, nearBottom, nearTop;
  541. calcProjectionParameters(nearLeft, nearRight, nearBottom, nearTop);
  542. // Treat infinite fardist as some arbitrary far value
  543. float farDist = (mFarDist == 0) ? 100000 : mFarDist;
  544. // Calc far palne corners
  545. float radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1;
  546. float farLeft = nearLeft * radio;
  547. float farRight = nearRight * radio;
  548. float farBottom = nearBottom * radio;
  549. float farTop = nearTop * radio;
  550. // near
  551. mWorldSpaceCorners[0] = eyeToWorld.transformAffine(Vector3(nearRight, nearTop, -mNearDist));
  552. mWorldSpaceCorners[1] = eyeToWorld.transformAffine(Vector3(nearLeft, nearTop, -mNearDist));
  553. mWorldSpaceCorners[2] = eyeToWorld.transformAffine(Vector3(nearLeft, nearBottom, -mNearDist));
  554. mWorldSpaceCorners[3] = eyeToWorld.transformAffine(Vector3(nearRight, nearBottom, -mNearDist));
  555. // far
  556. mWorldSpaceCorners[4] = eyeToWorld.transformAffine(Vector3(farRight, farTop, -farDist));
  557. mWorldSpaceCorners[5] = eyeToWorld.transformAffine(Vector3(farLeft, farTop, -farDist));
  558. mWorldSpaceCorners[6] = eyeToWorld.transformAffine(Vector3(farLeft, farBottom, -farDist));
  559. mWorldSpaceCorners[7] = eyeToWorld.transformAffine(Vector3(farRight, farBottom, -farDist));
  560. mRecalcWorldSpaceCorners = false;
  561. }
  562. //-----------------------------------------------------------------------
  563. void Camera::updateWorldSpaceCorners(void) const
  564. {
  565. updateView();
  566. if (mRecalcWorldSpaceCorners)
  567. {
  568. updateWorldSpaceCornersImpl();
  569. }
  570. }
  571. //-----------------------------------------------------------------------
  572. float Camera::getAspectRatio(void) const
  573. {
  574. return mAspect;
  575. }
  576. //-----------------------------------------------------------------------
  577. void Camera::setAspectRatio(float r)
  578. {
  579. mAspect = r;
  580. invalidateFrustum();
  581. }
  582. //-----------------------------------------------------------------------
  583. const AxisAlignedBox& Camera::getBoundingBox(void) const
  584. {
  585. return mBoundingBox;
  586. }
  587. // -------------------------------------------------------------------
  588. const Vector3* Camera::getWorldSpaceCorners(void) const
  589. {
  590. updateWorldSpaceCorners();
  591. return mWorldSpaceCorners;
  592. }
  593. //-----------------------------------------------------------------------
  594. void Camera::setProjectionType(ProjectionType pt)
  595. {
  596. mProjType = pt;
  597. invalidateFrustum();
  598. }
  599. //-----------------------------------------------------------------------
  600. ProjectionType Camera::getProjectionType(void) const
  601. {
  602. return mProjType;
  603. }
  604. //---------------------------------------------------------------------
  605. bool Camera::projectSphere(const Sphere& sphere,
  606. float* left, float* top, float* right, float* bottom) const
  607. {
  608. // See http://www.gamasutra.com/features/20021011/lengyel_06.htm
  609. // Transform light position into camera space
  610. updateView();
  611. Vector3 eyeSpacePos = mViewMatrix.transformAffine(sphere.getCenter());
  612. // initialise
  613. *left = *bottom = -1.0f;
  614. *right = *top = 1.0f;
  615. if (eyeSpacePos.z < 0)
  616. {
  617. updateFrustum();
  618. const Matrix4& projMatrix = getProjectionMatrix();
  619. float r = sphere.getRadius();
  620. float rsq = r * r;
  621. // early-exit
  622. if (eyeSpacePos.squaredLength() <= rsq)
  623. return false;
  624. float Lxz = Math::Sqr(eyeSpacePos.x) + Math::Sqr(eyeSpacePos.z);
  625. float Lyz = Math::Sqr(eyeSpacePos.y) + Math::Sqr(eyeSpacePos.z);
  626. // Find the tangent planes to the sphere
  627. // XZ first
  628. // calculate quadratic discriminant: b*b - 4ac
  629. // x = Nx
  630. // a = Lx^2 + Lz^2
  631. // b = -2rLx
  632. // c = r^2 - Lz^2
  633. float a = Lxz;
  634. float b = -2.0f * r * eyeSpacePos.x;
  635. float c = rsq - Math::Sqr(eyeSpacePos.z);
  636. float D = b*b - 4.0f*a*c;
  637. // two roots?
  638. if (D > 0)
  639. {
  640. float sqrootD = Math::Sqrt(D);
  641. // solve the quadratic to get the components of the normal
  642. float Nx0 = (-b + sqrootD) / (2 * a);
  643. float Nx1 = (-b - sqrootD) / (2 * a);
  644. // Derive Z from this
  645. float Nz0 = (r - Nx0 * eyeSpacePos.x) / eyeSpacePos.z;
  646. float Nz1 = (r - Nx1 * eyeSpacePos.x) / eyeSpacePos.z;
  647. // Get the point of tangency
  648. // Only consider points of tangency in front of the camera
  649. float Pz0 = (Lxz - rsq) / (eyeSpacePos.z - ((Nz0 / Nx0) * eyeSpacePos.x));
  650. if (Pz0 < 0)
  651. {
  652. // Project point onto near plane in worldspace
  653. float nearx0 = (Nz0 * mNearDist) / Nx0;
  654. // now we need to map this to viewport coords
  655. // use projection matrix since that will take into account all factors
  656. Vector3 relx0 = projMatrix * Vector3(nearx0, 0, -mNearDist);
  657. // find out whether this is a left side or right side
  658. float Px0 = -(Pz0 * Nz0) / Nx0;
  659. if (Px0 > eyeSpacePos.x)
  660. {
  661. *right = std::min(*right, relx0.x);
  662. }
  663. else
  664. {
  665. *left = std::max(*left, relx0.x);
  666. }
  667. }
  668. float Pz1 = (Lxz - rsq) / (eyeSpacePos.z - ((Nz1 / Nx1) * eyeSpacePos.x));
  669. if (Pz1 < 0)
  670. {
  671. // Project point onto near plane in worldspace
  672. float nearx1 = (Nz1 * mNearDist) / Nx1;
  673. // now we need to map this to viewport coords
  674. // use projection matrix since that will take into account all factors
  675. Vector3 relx1 = projMatrix * Vector3(nearx1, 0, -mNearDist);
  676. // find out whether this is a left side or right side
  677. float Px1 = -(Pz1 * Nz1) / Nx1;
  678. if (Px1 > eyeSpacePos.x)
  679. {
  680. *right = std::min(*right, relx1.x);
  681. }
  682. else
  683. {
  684. *left = std::max(*left, relx1.x);
  685. }
  686. }
  687. }
  688. // Now YZ
  689. // calculate quadratic discriminant: b*b - 4ac
  690. // x = Ny
  691. // a = Ly^2 + Lz^2
  692. // b = -2rLy
  693. // c = r^2 - Lz^2
  694. a = Lyz;
  695. b = -2.0f * r * eyeSpacePos.y;
  696. c = rsq - Math::Sqr(eyeSpacePos.z);
  697. D = b*b - 4.0f*a*c;
  698. // two roots?
  699. if (D > 0)
  700. {
  701. float sqrootD = Math::Sqrt(D);
  702. // solve the quadratic to get the components of the normal
  703. float Ny0 = (-b + sqrootD) / (2 * a);
  704. float Ny1 = (-b - sqrootD) / (2 * a);
  705. // Derive Z from this
  706. float Nz0 = (r - Ny0 * eyeSpacePos.y) / eyeSpacePos.z;
  707. float Nz1 = (r - Ny1 * eyeSpacePos.y) / eyeSpacePos.z;
  708. // Get the point of tangency
  709. // Only consider points of tangency in front of the camera
  710. float Pz0 = (Lyz - rsq) / (eyeSpacePos.z - ((Nz0 / Ny0) * eyeSpacePos.y));
  711. if (Pz0 < 0)
  712. {
  713. // Project point onto near plane in worldspace
  714. float neary0 = (Nz0 * mNearDist) / Ny0;
  715. // now we need to map this to viewport coords
  716. // use projection matriy since that will take into account all factors
  717. Vector3 rely0 = projMatrix * Vector3(0, neary0, -mNearDist);
  718. // find out whether this is a top side or bottom side
  719. float Py0 = -(Pz0 * Nz0) / Ny0;
  720. if (Py0 > eyeSpacePos.y)
  721. {
  722. *top = std::min(*top, rely0.y);
  723. }
  724. else
  725. {
  726. *bottom = std::max(*bottom, rely0.y);
  727. }
  728. }
  729. float Pz1 = (Lyz - rsq) / (eyeSpacePos.z - ((Nz1 / Ny1) * eyeSpacePos.y));
  730. if (Pz1 < 0)
  731. {
  732. // Project point onto near plane in worldspace
  733. float neary1 = (Nz1 * mNearDist) / Ny1;
  734. // now we need to map this to viewport coords
  735. // use projection matriy since that will take into account all factors
  736. Vector3 rely1 = projMatrix * Vector3(0, neary1, -mNearDist);
  737. // find out whether this is a top side or bottom side
  738. float Py1 = -(Pz1 * Nz1) / Ny1;
  739. if (Py1 > eyeSpacePos.y)
  740. {
  741. *top = std::min(*top, rely1.y);
  742. }
  743. else
  744. {
  745. *bottom = std::max(*bottom, rely1.y);
  746. }
  747. }
  748. }
  749. }
  750. return (*left != -1.0f) || (*top != 1.0f) || (*right != 1.0f) || (*bottom != -1.0f);
  751. }
  752. //---------------------------------------------------------------------
  753. void Camera::setCustomViewMatrix(bool enable, const Matrix4& viewMatrix)
  754. {
  755. mCustomViewMatrix = enable;
  756. if (enable)
  757. {
  758. assert(viewMatrix.isAffine());
  759. mViewMatrix = viewMatrix;
  760. }
  761. }
  762. //---------------------------------------------------------------------
  763. void Camera::setCustomProjectionMatrix(bool enable, const Matrix4& projMatrix)
  764. {
  765. mCustomProjMatrix = enable;
  766. if (enable)
  767. {
  768. mProjMatrix = projMatrix;
  769. }
  770. invalidateFrustum();
  771. }
  772. //---------------------------------------------------------------------
  773. void Camera::setOrthoWindow(float w, float h)
  774. {
  775. mOrthoHeight = h;
  776. mAspect = w / h;
  777. invalidateFrustum();
  778. }
  779. //---------------------------------------------------------------------
  780. void Camera::setOrthoWindowHeight(float h)
  781. {
  782. mOrthoHeight = h;
  783. invalidateFrustum();
  784. }
  785. //---------------------------------------------------------------------
  786. void Camera::setOrthoWindowWidth(float w)
  787. {
  788. mOrthoHeight = w / mAspect;
  789. invalidateFrustum();
  790. }
  791. //---------------------------------------------------------------------
  792. float Camera::getOrthoWindowHeight() const
  793. {
  794. return mOrthoHeight;
  795. }
  796. //---------------------------------------------------------------------
  797. float Camera::getOrthoWindowWidth() const
  798. {
  799. return mOrthoHeight * mAspect;
  800. }
  801. //---------------------------------------------------------------------
  802. void Camera::setFrustumExtents(float left, float right, float top, float bottom)
  803. {
  804. mFrustumExtentsManuallySet = true;
  805. mLeft = left;
  806. mRight = right;
  807. mTop = top;
  808. mBottom = bottom;
  809. invalidateFrustum();
  810. }
  811. //---------------------------------------------------------------------
  812. void Camera::resetFrustumExtents()
  813. {
  814. mFrustumExtentsManuallySet = false;
  815. invalidateFrustum();
  816. }
  817. //---------------------------------------------------------------------
  818. void Camera::getFrustumExtents(float& outleft, float& outright, float& outtop, float& outbottom) const
  819. {
  820. updateFrustum();
  821. outleft = mLeft;
  822. outright = mRight;
  823. outtop = mTop;
  824. outbottom = mBottom;
  825. }
  826. // -------------------------------------------------------------------
  827. void Camera::invalidateFrustum(void) const
  828. {
  829. mRecalcFrustumPlanes = true;
  830. mRecalcWorldSpaceCorners = true;
  831. mRecalcVertexData = true;
  832. }
  833. // -------------------------------------------------------------------
  834. float Camera::getBoundingRadius(void) const
  835. {
  836. // return a little bigger than the near distance
  837. // just to keep things just outside
  838. return mNearDist * 1.5f;
  839. }
  840. RTTITypeBase* Camera::getRTTIStatic()
  841. {
  842. return CameraRTTI::instance();
  843. }
  844. RTTITypeBase* Camera::getRTTI() const
  845. {
  846. return Camera::getRTTIStatic();
  847. }
  848. } // namespace CamelotEngine