CmFrustum.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111
  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 "CmFrustum.h"
  25. #include "CmMath.h"
  26. #include "CmMatrix3.h"
  27. #include "CmSphere.h"
  28. #include "CmException.h"
  29. #include "CmHardwareBufferManager.h"
  30. #include "CmHardwareVertexBuffer.h"
  31. #include "CmHardwareIndexBuffer.h"
  32. #include "CmRenderSystem.h"
  33. #include "CmRenderSystemManager.h"
  34. namespace CamelotEngine {
  35. const float Frustum::INFINITE_FAR_PLANE_ADJUST = 0.00001f;
  36. //-----------------------------------------------------------------------
  37. Frustum::Frustum(const String& name) :
  38. mProjType(PT_PERSPECTIVE),
  39. mFOVy(Radian(Math::PI/4.0f)),
  40. mFarDist(100000.0f),
  41. mNearDist(100.0f),
  42. mAspect(1.33333333333333f),
  43. mOrthoHeight(1000),
  44. mFrustumOffset(Vector2::ZERO),
  45. mFocalLength(1.0f),
  46. mLastParentOrientation(Quaternion::IDENTITY),
  47. mLastParentPosition(Vector3::ZERO),
  48. mRecalcFrustum(true),
  49. mRecalcView(true),
  50. mRecalcFrustumPlanes(true),
  51. mRecalcWorldSpaceCorners(true),
  52. mRecalcVertexData(true),
  53. mCustomViewMatrix(false),
  54. mCustomProjMatrix(false),
  55. mFrustumExtentsManuallySet(false)
  56. {
  57. updateView();
  58. updateFrustum();
  59. }
  60. //-----------------------------------------------------------------------
  61. Frustum::~Frustum()
  62. {
  63. // Do nothing
  64. }
  65. //-----------------------------------------------------------------------
  66. void Frustum::setFOVy(const Radian& fov)
  67. {
  68. mFOVy = fov;
  69. invalidateFrustum();
  70. }
  71. //-----------------------------------------------------------------------
  72. const Radian& Frustum::getFOVy(void) const
  73. {
  74. return mFOVy;
  75. }
  76. //-----------------------------------------------------------------------
  77. void Frustum::setFarClipDistance(float farPlane)
  78. {
  79. mFarDist = farPlane;
  80. invalidateFrustum();
  81. }
  82. //-----------------------------------------------------------------------
  83. float Frustum::getFarClipDistance(void) const
  84. {
  85. return mFarDist;
  86. }
  87. //-----------------------------------------------------------------------
  88. void Frustum::setNearClipDistance(float nearPlane)
  89. {
  90. if (nearPlane <= 0)
  91. {
  92. CM_EXCEPT(InvalidParametersException, "Near clip distance must be greater than zero.");
  93. }
  94. mNearDist = nearPlane;
  95. invalidateFrustum();
  96. }
  97. //-----------------------------------------------------------------------
  98. float Frustum::getNearClipDistance(void) const
  99. {
  100. return mNearDist;
  101. }
  102. //---------------------------------------------------------------------
  103. void Frustum::setFrustumOffset(const Vector2& offset)
  104. {
  105. mFrustumOffset = offset;
  106. invalidateFrustum();
  107. }
  108. //---------------------------------------------------------------------
  109. void Frustum::setFrustumOffset(float horizontal, float vertical)
  110. {
  111. setFrustumOffset(Vector2(horizontal, vertical));
  112. }
  113. //---------------------------------------------------------------------
  114. const Vector2& Frustum::getFrustumOffset() const
  115. {
  116. return mFrustumOffset;
  117. }
  118. //---------------------------------------------------------------------
  119. void Frustum::setFocalLength(float focalLength)
  120. {
  121. if (focalLength <= 0)
  122. {
  123. CM_EXCEPT(InvalidParametersException,
  124. "Focal length must be greater than zero.");
  125. }
  126. mFocalLength = focalLength;
  127. invalidateFrustum();
  128. }
  129. //---------------------------------------------------------------------
  130. float Frustum::getFocalLength() const
  131. {
  132. return mFocalLength;
  133. }
  134. //-----------------------------------------------------------------------
  135. const Matrix4& Frustum::getProjectionMatrix(void) const
  136. {
  137. updateFrustum();
  138. return mProjMatrix;
  139. }
  140. //-----------------------------------------------------------------------
  141. const Matrix4& Frustum::getProjectionMatrixWithRSDepth(void) const
  142. {
  143. updateFrustum();
  144. return mProjMatrixRSDepth;
  145. }
  146. //-----------------------------------------------------------------------
  147. const Matrix4& Frustum::getProjectionMatrixRS(void) const
  148. {
  149. updateFrustum();
  150. return mProjMatrixRS;
  151. }
  152. //-----------------------------------------------------------------------
  153. const Matrix4& Frustum::getViewMatrix(void) const
  154. {
  155. updateView();
  156. return mViewMatrix;
  157. }
  158. //-----------------------------------------------------------------------
  159. const Plane* Frustum::getFrustumPlanes(void) const
  160. {
  161. // Make any pending updates to the calculated frustum planes
  162. updateFrustumPlanes();
  163. return mFrustumPlanes;
  164. }
  165. //-----------------------------------------------------------------------
  166. const Plane& Frustum::getFrustumPlane(unsigned short plane) const
  167. {
  168. // Make any pending updates to the calculated frustum planes
  169. updateFrustumPlanes();
  170. return mFrustumPlanes[plane];
  171. }
  172. //-----------------------------------------------------------------------
  173. bool Frustum::isVisible(const AxisAlignedBox& bound, FrustumPlane* culledBy) const
  174. {
  175. // Null boxes always invisible
  176. if (bound.isNull()) return false;
  177. // Infinite boxes always visible
  178. if (bound.isInfinite()) return true;
  179. // Make any pending updates to the calculated frustum planes
  180. updateFrustumPlanes();
  181. // Get centre of the box
  182. Vector3 centre = bound.getCenter();
  183. // Get the half-size of the box
  184. Vector3 halfSize = bound.getHalfSize();
  185. // For each plane, see if all points are on the negative side
  186. // If so, object is not visible
  187. for (int plane = 0; plane < 6; ++plane)
  188. {
  189. // Skip far plane if infinite view frustum
  190. if (plane == FRUSTUM_PLANE_FAR && mFarDist == 0)
  191. continue;
  192. Plane::Side side = mFrustumPlanes[plane].getSide(centre, halfSize);
  193. if (side == Plane::NEGATIVE_SIDE)
  194. {
  195. // ALL corners on negative side therefore out of view
  196. if (culledBy)
  197. *culledBy = (FrustumPlane)plane;
  198. return false;
  199. }
  200. }
  201. return true;
  202. }
  203. //-----------------------------------------------------------------------
  204. bool Frustum::isVisible(const Vector3& vert, FrustumPlane* culledBy) const
  205. {
  206. // Make any pending updates to the calculated frustum planes
  207. updateFrustumPlanes();
  208. // For each plane, see if all points are on the negative side
  209. // If so, object is not visible
  210. for (int plane = 0; plane < 6; ++plane)
  211. {
  212. // Skip far plane if infinite view frustum
  213. if (plane == FRUSTUM_PLANE_FAR && mFarDist == 0)
  214. continue;
  215. if (mFrustumPlanes[plane].getSide(vert) == Plane::NEGATIVE_SIDE)
  216. {
  217. // ALL corners on negative side therefore out of view
  218. if (culledBy)
  219. *culledBy = (FrustumPlane)plane;
  220. return false;
  221. }
  222. }
  223. return true;
  224. }
  225. //-----------------------------------------------------------------------
  226. bool Frustum::isVisible(const Sphere& sphere, FrustumPlane* culledBy) const
  227. {
  228. // Make any pending updates to the calculated frustum planes
  229. updateFrustumPlanes();
  230. // For each plane, see if sphere is on negative side
  231. // If so, object is not visible
  232. for (int plane = 0; plane < 6; ++plane)
  233. {
  234. // Skip far plane if infinite view frustum
  235. if (plane == FRUSTUM_PLANE_FAR && mFarDist == 0)
  236. continue;
  237. // If the distance from sphere center to plane is negative, and 'more negative'
  238. // than the radius of the sphere, sphere is outside frustum
  239. if (mFrustumPlanes[plane].getDistance(sphere.getCenter()) < -sphere.getRadius())
  240. {
  241. // ALL corners on negative side therefore out of view
  242. if (culledBy)
  243. *culledBy = (FrustumPlane)plane;
  244. return false;
  245. }
  246. }
  247. return true;
  248. }
  249. //-----------------------------------------------------------------------
  250. void Frustum::calcProjectionParameters(float& left, float& right, float& bottom, float& top) const
  251. {
  252. if (mCustomProjMatrix)
  253. {
  254. // Convert clipspace corners to camera space
  255. Matrix4 invProj = mProjMatrix.inverse();
  256. Vector3 topLeft(-0.5f, 0.5f, 0.0f);
  257. Vector3 bottomRight(0.5f, -0.5f, 0.0f);
  258. topLeft = invProj * topLeft;
  259. bottomRight = invProj * bottomRight;
  260. left = topLeft.x;
  261. top = topLeft.y;
  262. right = bottomRight.x;
  263. bottom = bottomRight.y;
  264. }
  265. else
  266. {
  267. if (mFrustumExtentsManuallySet)
  268. {
  269. left = mLeft;
  270. right = mRight;
  271. top = mTop;
  272. bottom = mBottom;
  273. }
  274. // Calculate general projection parameters
  275. else if (mProjType == PT_PERSPECTIVE)
  276. {
  277. Radian thetaY (mFOVy * 0.5f);
  278. float tanThetaY = Math::Tan(thetaY);
  279. float tanThetaX = tanThetaY * mAspect;
  280. float nearFocal = mNearDist / mFocalLength;
  281. float nearOffsetX = mFrustumOffset.x * nearFocal;
  282. float nearOffsetY = mFrustumOffset.y * nearFocal;
  283. float half_w = tanThetaX * mNearDist;
  284. float half_h = tanThetaY * mNearDist;
  285. left = - half_w + nearOffsetX;
  286. right = + half_w + nearOffsetX;
  287. bottom = - half_h + nearOffsetY;
  288. top = + half_h + nearOffsetY;
  289. mLeft = left;
  290. mRight = right;
  291. mTop = top;
  292. mBottom = bottom;
  293. }
  294. else
  295. {
  296. // Unknown how to apply frustum offset to orthographic camera, just ignore here
  297. float half_w = getOrthoWindowWidth() * 0.5f;
  298. float half_h = getOrthoWindowHeight() * 0.5f;
  299. left = - half_w;
  300. right = + half_w;
  301. bottom = - half_h;
  302. top = + half_h;
  303. mLeft = left;
  304. mRight = right;
  305. mTop = top;
  306. mBottom = bottom;
  307. }
  308. }
  309. }
  310. //-----------------------------------------------------------------------
  311. void Frustum::updateFrustumImpl(void) const
  312. {
  313. // Common calcs
  314. float left, right, bottom, top;
  315. calcProjectionParameters(left, right, bottom, top);
  316. if (!mCustomProjMatrix)
  317. {
  318. // The code below will dealing with general projection
  319. // parameters, similar glFrustum and glOrtho.
  320. // Doesn't optimise manually except division operator, so the
  321. // code more self-explaining.
  322. float inv_w = 1 / (right - left);
  323. float inv_h = 1 / (top - bottom);
  324. float inv_d = 1 / (mFarDist - mNearDist);
  325. // Recalc if frustum params changed
  326. if (mProjType == PT_PERSPECTIVE)
  327. {
  328. // Calc matrix elements
  329. float A = 2 * mNearDist * inv_w;
  330. float B = 2 * mNearDist * inv_h;
  331. float C = (right + left) * inv_w;
  332. float D = (top + bottom) * inv_h;
  333. float q, qn;
  334. if (mFarDist == 0)
  335. {
  336. // Infinite far plane
  337. q = Frustum::INFINITE_FAR_PLANE_ADJUST - 1;
  338. qn = mNearDist * (Frustum::INFINITE_FAR_PLANE_ADJUST - 2);
  339. }
  340. else
  341. {
  342. q = - (mFarDist + mNearDist) * inv_d;
  343. qn = -2 * (mFarDist * mNearDist) * inv_d;
  344. }
  345. // NB: This creates 'uniform' perspective projection matrix,
  346. // which depth range [-1,1], right-handed rules
  347. //
  348. // [ A 0 C 0 ]
  349. // [ 0 B D 0 ]
  350. // [ 0 0 q qn ]
  351. // [ 0 0 -1 0 ]
  352. //
  353. // A = 2 * near / (right - left)
  354. // B = 2 * near / (top - bottom)
  355. // C = (right + left) / (right - left)
  356. // D = (top + bottom) / (top - bottom)
  357. // q = - (far + near) / (far - near)
  358. // qn = - 2 * (far * near) / (far - near)
  359. mProjMatrix = Matrix4::ZERO;
  360. mProjMatrix[0][0] = A;
  361. mProjMatrix[0][2] = C;
  362. mProjMatrix[1][1] = B;
  363. mProjMatrix[1][2] = D;
  364. mProjMatrix[2][2] = q;
  365. mProjMatrix[2][3] = qn;
  366. mProjMatrix[3][2] = -1;
  367. } // perspective
  368. else if (mProjType == PT_ORTHOGRAPHIC)
  369. {
  370. float A = 2 * inv_w;
  371. float B = 2 * inv_h;
  372. float C = - (right + left) * inv_w;
  373. float D = - (top + bottom) * inv_h;
  374. float q, qn;
  375. if (mFarDist == 0)
  376. {
  377. // Can not do infinite far plane here, avoid divided zero only
  378. q = - Frustum::INFINITE_FAR_PLANE_ADJUST / mNearDist;
  379. qn = - Frustum::INFINITE_FAR_PLANE_ADJUST - 1;
  380. }
  381. else
  382. {
  383. q = - 2 * inv_d;
  384. qn = - (mFarDist + mNearDist) * inv_d;
  385. }
  386. // NB: This creates 'uniform' orthographic projection matrix,
  387. // which depth range [-1,1], right-handed rules
  388. //
  389. // [ A 0 0 C ]
  390. // [ 0 B 0 D ]
  391. // [ 0 0 q qn ]
  392. // [ 0 0 0 1 ]
  393. //
  394. // A = 2 * / (right - left)
  395. // B = 2 * / (top - bottom)
  396. // C = - (right + left) / (right - left)
  397. // D = - (top + bottom) / (top - bottom)
  398. // q = - 2 / (far - near)
  399. // qn = - (far + near) / (far - near)
  400. mProjMatrix = Matrix4::ZERO;
  401. mProjMatrix[0][0] = A;
  402. mProjMatrix[0][3] = C;
  403. mProjMatrix[1][1] = B;
  404. mProjMatrix[1][3] = D;
  405. mProjMatrix[2][2] = q;
  406. mProjMatrix[2][3] = qn;
  407. mProjMatrix[3][3] = 1;
  408. } // ortho
  409. } // !mCustomProjMatrix
  410. RenderSystem* renderSystem = CamelotEngine::RenderSystemManager::getActive();
  411. // API specific
  412. renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRS);
  413. // API specific for Gpu Programs
  414. renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRSDepth, true);
  415. // Calculate bounding box (local)
  416. // Box is from 0, down -Z, max dimensions as determined from far plane
  417. // If infinite view frustum just pick a far value
  418. float farDist = (mFarDist == 0) ? 100000 : mFarDist;
  419. // Near plane bounds
  420. Vector3 min(left, bottom, -farDist);
  421. Vector3 max(right, top, 0);
  422. if (mCustomProjMatrix)
  423. {
  424. // Some custom projection matrices can have unusual inverted settings
  425. // So make sure the AABB is the right way around to start with
  426. Vector3 tmp = min;
  427. min.makeFloor(max);
  428. max.makeCeil(tmp);
  429. }
  430. if (mProjType == PT_PERSPECTIVE)
  431. {
  432. // Merge with far plane bounds
  433. float radio = farDist / mNearDist;
  434. min.makeFloor(Vector3(left * radio, bottom * radio, -farDist));
  435. max.makeCeil(Vector3(right * radio, top * radio, 0));
  436. }
  437. mBoundingBox.setExtents(min, max);
  438. mRecalcFrustum = false;
  439. // Signal to update frustum clipping planes
  440. mRecalcFrustumPlanes = true;
  441. }
  442. //-----------------------------------------------------------------------
  443. void Frustum::updateFrustum(void) const
  444. {
  445. if (isFrustumOutOfDate())
  446. {
  447. updateFrustumImpl();
  448. }
  449. }
  450. //-----------------------------------------------------------------------
  451. void Frustum::updateVertexData(void) const
  452. {
  453. if (mRecalcVertexData)
  454. {
  455. if (mVertexData.vertexBufferBinding->getBufferCount() <= 0)
  456. {
  457. // Initialise vertex & index data
  458. mVertexData.vertexDeclaration->addElement(0, 0, VET_FLOAT3, VES_POSITION);
  459. mVertexData.vertexCount = 32;
  460. mVertexData.vertexStart = 0;
  461. mVertexData.vertexBufferBinding->setBinding( 0,
  462. HardwareBufferManager::getSingleton().createVertexBuffer(
  463. sizeof(float)*3, 32, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY) );
  464. }
  465. // Note: Even though we can dealing with general projection matrix here,
  466. // but because it's incompatibly with infinite far plane, thus, we
  467. // still need to working with projection parameters.
  468. // Calc near plane corners
  469. float vpLeft, vpRight, vpBottom, vpTop;
  470. calcProjectionParameters(vpLeft, vpRight, vpBottom, vpTop);
  471. // Treat infinite fardist as some arbitrary far value
  472. float farDist = (mFarDist == 0) ? 100000 : mFarDist;
  473. // Calc far plane corners
  474. float radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1;
  475. float farLeft = vpLeft * radio;
  476. float farRight = vpRight * radio;
  477. float farBottom = vpBottom * radio;
  478. float farTop = vpTop * radio;
  479. // Calculate vertex positions (local)
  480. // 0 is the origin
  481. // 1, 2, 3, 4 are the points on the near plane, top left first, clockwise
  482. // 5, 6, 7, 8 are the points on the far plane, top left first, clockwise
  483. HardwareVertexBufferPtr vbuf = mVertexData.vertexBufferBinding->getBuffer(0);
  484. float* pFloat = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
  485. // near plane (remember frustum is going in -Z direction)
  486. *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  487. *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  488. *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  489. *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  490. *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  491. *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  492. *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  493. *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  494. // far plane (remember frustum is going in -Z direction)
  495. *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist;
  496. *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist;
  497. *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist;
  498. *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
  499. *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
  500. *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist;
  501. *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist;
  502. *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist;
  503. // Sides of the pyramid
  504. *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f;
  505. *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  506. *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f;
  507. *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  508. *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f;
  509. *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  510. *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f;
  511. *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  512. // Sides of the box
  513. *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  514. *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist;
  515. *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  516. *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist;
  517. *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  518. *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
  519. *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  520. *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist;
  521. vbuf->unlock();
  522. mRecalcVertexData = false;
  523. }
  524. }
  525. //-----------------------------------------------------------------------
  526. bool Frustum::isViewOutOfDate(void) const
  527. {
  528. // Attached to node?
  529. // TODO PORT - Not attached to node because I'll be handling this differently
  530. //if (mParentNode)
  531. //{
  532. // if (mRecalcView ||
  533. // mParentNode->_getDerivedOrientation() != mLastParentOrientation ||
  534. // mParentNode->_getDerivedPosition() != mLastParentPosition)
  535. // {
  536. // // Ok, we're out of date with SceneNode we're attached to
  537. // mLastParentOrientation = mParentNode->_getDerivedOrientation();
  538. // mLastParentPosition = mParentNode->_getDerivedPosition();
  539. // mRecalcView = true;
  540. // }
  541. //}
  542. return mRecalcView;
  543. }
  544. //-----------------------------------------------------------------------
  545. bool Frustum::isFrustumOutOfDate(void) const
  546. {
  547. return mRecalcFrustum;
  548. }
  549. //-----------------------------------------------------------------------
  550. void Frustum::updateViewImpl(void) const
  551. {
  552. // ----------------------
  553. // Update the view matrix
  554. // ----------------------
  555. // Get orientation from quaternion
  556. if (!mCustomViewMatrix)
  557. {
  558. Matrix3 rot;
  559. const Quaternion& orientation = getOrientationForViewUpdate();
  560. const Vector3& position = getPositionForViewUpdate();
  561. mViewMatrix = Math::makeViewMatrix(position, orientation, 0);
  562. }
  563. mRecalcView = false;
  564. // Signal to update frustum clipping planes
  565. mRecalcFrustumPlanes = true;
  566. // Signal to update world space corners
  567. mRecalcWorldSpaceCorners = true;
  568. }
  569. //---------------------------------------------------------------------
  570. void Frustum::calcViewMatrixRelative(const Vector3& relPos, Matrix4& matToUpdate) const
  571. {
  572. Matrix4 matTrans = Matrix4::IDENTITY;
  573. matTrans.setTrans(relPos);
  574. matToUpdate = getViewMatrix() * matTrans;
  575. }
  576. //-----------------------------------------------------------------------
  577. void Frustum::updateView(void) const
  578. {
  579. if (isViewOutOfDate())
  580. {
  581. updateViewImpl();
  582. }
  583. }
  584. //-----------------------------------------------------------------------
  585. void Frustum::updateFrustumPlanesImpl(void) const
  586. {
  587. // -------------------------
  588. // Update the frustum planes
  589. // -------------------------
  590. Matrix4 combo = mProjMatrix * mViewMatrix;
  591. mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.x = combo[3][0] + combo[0][0];
  592. mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.y = combo[3][1] + combo[0][1];
  593. mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.z = combo[3][2] + combo[0][2];
  594. mFrustumPlanes[FRUSTUM_PLANE_LEFT].d = combo[3][3] + combo[0][3];
  595. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.x = combo[3][0] - combo[0][0];
  596. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.y = combo[3][1] - combo[0][1];
  597. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.z = combo[3][2] - combo[0][2];
  598. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].d = combo[3][3] - combo[0][3];
  599. mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.x = combo[3][0] - combo[1][0];
  600. mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.y = combo[3][1] - combo[1][1];
  601. mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.z = combo[3][2] - combo[1][2];
  602. mFrustumPlanes[FRUSTUM_PLANE_TOP].d = combo[3][3] - combo[1][3];
  603. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.x = combo[3][0] + combo[1][0];
  604. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.y = combo[3][1] + combo[1][1];
  605. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.z = combo[3][2] + combo[1][2];
  606. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].d = combo[3][3] + combo[1][3];
  607. mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.x = combo[3][0] + combo[2][0];
  608. mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.y = combo[3][1] + combo[2][1];
  609. mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.z = combo[3][2] + combo[2][2];
  610. mFrustumPlanes[FRUSTUM_PLANE_NEAR].d = combo[3][3] + combo[2][3];
  611. mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.x = combo[3][0] - combo[2][0];
  612. mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.y = combo[3][1] - combo[2][1];
  613. mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.z = combo[3][2] - combo[2][2];
  614. mFrustumPlanes[FRUSTUM_PLANE_FAR].d = combo[3][3] - combo[2][3];
  615. // Renormalise any normals which were not unit length
  616. for(int i=0; i<6; i++ )
  617. {
  618. float length = mFrustumPlanes[i].normal.normalise();
  619. mFrustumPlanes[i].d /= length;
  620. }
  621. mRecalcFrustumPlanes = false;
  622. }
  623. //-----------------------------------------------------------------------
  624. void Frustum::updateFrustumPlanes(void) const
  625. {
  626. updateView();
  627. updateFrustum();
  628. if (mRecalcFrustumPlanes)
  629. {
  630. updateFrustumPlanesImpl();
  631. }
  632. }
  633. //-----------------------------------------------------------------------
  634. void Frustum::updateWorldSpaceCornersImpl(void) const
  635. {
  636. Matrix4 eyeToWorld = mViewMatrix.inverseAffine();
  637. // Note: Even though we can dealing with general projection matrix here,
  638. // but because it's incompatibly with infinite far plane, thus, we
  639. // still need to working with projection parameters.
  640. // Calc near plane corners
  641. float nearLeft, nearRight, nearBottom, nearTop;
  642. calcProjectionParameters(nearLeft, nearRight, nearBottom, nearTop);
  643. // Treat infinite fardist as some arbitrary far value
  644. float farDist = (mFarDist == 0) ? 100000 : mFarDist;
  645. // Calc far palne corners
  646. float radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1;
  647. float farLeft = nearLeft * radio;
  648. float farRight = nearRight * radio;
  649. float farBottom = nearBottom * radio;
  650. float farTop = nearTop * radio;
  651. // near
  652. mWorldSpaceCorners[0] = eyeToWorld.transformAffine(Vector3(nearRight, nearTop, -mNearDist));
  653. mWorldSpaceCorners[1] = eyeToWorld.transformAffine(Vector3(nearLeft, nearTop, -mNearDist));
  654. mWorldSpaceCorners[2] = eyeToWorld.transformAffine(Vector3(nearLeft, nearBottom, -mNearDist));
  655. mWorldSpaceCorners[3] = eyeToWorld.transformAffine(Vector3(nearRight, nearBottom, -mNearDist));
  656. // far
  657. mWorldSpaceCorners[4] = eyeToWorld.transformAffine(Vector3(farRight, farTop, -farDist));
  658. mWorldSpaceCorners[5] = eyeToWorld.transformAffine(Vector3(farLeft, farTop, -farDist));
  659. mWorldSpaceCorners[6] = eyeToWorld.transformAffine(Vector3(farLeft, farBottom, -farDist));
  660. mWorldSpaceCorners[7] = eyeToWorld.transformAffine(Vector3(farRight, farBottom, -farDist));
  661. mRecalcWorldSpaceCorners = false;
  662. }
  663. //-----------------------------------------------------------------------
  664. void Frustum::updateWorldSpaceCorners(void) const
  665. {
  666. updateView();
  667. if (mRecalcWorldSpaceCorners)
  668. {
  669. updateWorldSpaceCornersImpl();
  670. }
  671. }
  672. //-----------------------------------------------------------------------
  673. float Frustum::getAspectRatio(void) const
  674. {
  675. return mAspect;
  676. }
  677. //-----------------------------------------------------------------------
  678. void Frustum::setAspectRatio(float r)
  679. {
  680. mAspect = r;
  681. invalidateFrustum();
  682. }
  683. //-----------------------------------------------------------------------
  684. const AxisAlignedBox& Frustum::getBoundingBox(void) const
  685. {
  686. return mBoundingBox;
  687. }
  688. //-----------------------------------------------------------------------
  689. float Frustum::getBoundingRadius(void) const
  690. {
  691. return (mFarDist == 0)? 100000 : mFarDist;
  692. }
  693. // -------------------------------------------------------------------
  694. void Frustum::invalidateFrustum() const
  695. {
  696. mRecalcFrustum = true;
  697. mRecalcFrustumPlanes = true;
  698. mRecalcWorldSpaceCorners = true;
  699. mRecalcVertexData = true;
  700. }
  701. // -------------------------------------------------------------------
  702. void Frustum::invalidateView() const
  703. {
  704. mRecalcView = true;
  705. mRecalcFrustumPlanes = true;
  706. mRecalcWorldSpaceCorners = true;
  707. }
  708. // -------------------------------------------------------------------
  709. const Vector3* Frustum::getWorldSpaceCorners(void) const
  710. {
  711. updateWorldSpaceCorners();
  712. return mWorldSpaceCorners;
  713. }
  714. //-----------------------------------------------------------------------
  715. void Frustum::setProjectionType(ProjectionType pt)
  716. {
  717. mProjType = pt;
  718. invalidateFrustum();
  719. }
  720. //-----------------------------------------------------------------------
  721. ProjectionType Frustum::getProjectionType(void) const
  722. {
  723. return mProjType;
  724. }
  725. //-----------------------------------------------------------------------
  726. const Vector3& Frustum::getPositionForViewUpdate(void) const
  727. {
  728. return mLastParentPosition;
  729. }
  730. //-----------------------------------------------------------------------
  731. const Quaternion& Frustum::getOrientationForViewUpdate(void) const
  732. {
  733. return mLastParentOrientation;
  734. }
  735. //---------------------------------------------------------------------
  736. bool Frustum::projectSphere(const Sphere& sphere,
  737. float* left, float* top, float* right, float* bottom) const
  738. {
  739. // See http://www.gamasutra.com/features/20021011/lengyel_06.htm
  740. // Transform light position into camera space
  741. updateView();
  742. Vector3 eyeSpacePos = mViewMatrix.transformAffine(sphere.getCenter());
  743. // initialise
  744. *left = *bottom = -1.0f;
  745. *right = *top = 1.0f;
  746. if (eyeSpacePos.z < 0)
  747. {
  748. updateFrustum();
  749. const Matrix4& projMatrix = getProjectionMatrix();
  750. float r = sphere.getRadius();
  751. float rsq = r * r;
  752. // early-exit
  753. if (eyeSpacePos.squaredLength() <= rsq)
  754. return false;
  755. float Lxz = Math::Sqr(eyeSpacePos.x) + Math::Sqr(eyeSpacePos.z);
  756. float Lyz = Math::Sqr(eyeSpacePos.y) + Math::Sqr(eyeSpacePos.z);
  757. // Find the tangent planes to the sphere
  758. // XZ first
  759. // calculate quadratic discriminant: b*b - 4ac
  760. // x = Nx
  761. // a = Lx^2 + Lz^2
  762. // b = -2rLx
  763. // c = r^2 - Lz^2
  764. float a = Lxz;
  765. float b = -2.0f * r * eyeSpacePos.x;
  766. float c = rsq - Math::Sqr(eyeSpacePos.z);
  767. float D = b*b - 4.0f*a*c;
  768. // two roots?
  769. if (D > 0)
  770. {
  771. float sqrootD = Math::Sqrt(D);
  772. // solve the quadratic to get the components of the normal
  773. float Nx0 = (-b + sqrootD) / (2 * a);
  774. float Nx1 = (-b - sqrootD) / (2 * a);
  775. // Derive Z from this
  776. float Nz0 = (r - Nx0 * eyeSpacePos.x) / eyeSpacePos.z;
  777. float Nz1 = (r - Nx1 * eyeSpacePos.x) / eyeSpacePos.z;
  778. // Get the point of tangency
  779. // Only consider points of tangency in front of the camera
  780. float Pz0 = (Lxz - rsq) / (eyeSpacePos.z - ((Nz0 / Nx0) * eyeSpacePos.x));
  781. if (Pz0 < 0)
  782. {
  783. // Project point onto near plane in worldspace
  784. float nearx0 = (Nz0 * mNearDist) / Nx0;
  785. // now we need to map this to viewport coords
  786. // use projection matrix since that will take into account all factors
  787. Vector3 relx0 = projMatrix * Vector3(nearx0, 0, -mNearDist);
  788. // find out whether this is a left side or right side
  789. float Px0 = -(Pz0 * Nz0) / Nx0;
  790. if (Px0 > eyeSpacePos.x)
  791. {
  792. *right = std::min(*right, relx0.x);
  793. }
  794. else
  795. {
  796. *left = std::max(*left, relx0.x);
  797. }
  798. }
  799. float Pz1 = (Lxz - rsq) / (eyeSpacePos.z - ((Nz1 / Nx1) * eyeSpacePos.x));
  800. if (Pz1 < 0)
  801. {
  802. // Project point onto near plane in worldspace
  803. float nearx1 = (Nz1 * mNearDist) / Nx1;
  804. // now we need to map this to viewport coords
  805. // use projection matrix since that will take into account all factors
  806. Vector3 relx1 = projMatrix * Vector3(nearx1, 0, -mNearDist);
  807. // find out whether this is a left side or right side
  808. float Px1 = -(Pz1 * Nz1) / Nx1;
  809. if (Px1 > eyeSpacePos.x)
  810. {
  811. *right = std::min(*right, relx1.x);
  812. }
  813. else
  814. {
  815. *left = std::max(*left, relx1.x);
  816. }
  817. }
  818. }
  819. // Now YZ
  820. // calculate quadratic discriminant: b*b - 4ac
  821. // x = Ny
  822. // a = Ly^2 + Lz^2
  823. // b = -2rLy
  824. // c = r^2 - Lz^2
  825. a = Lyz;
  826. b = -2.0f * r * eyeSpacePos.y;
  827. c = rsq - Math::Sqr(eyeSpacePos.z);
  828. D = b*b - 4.0f*a*c;
  829. // two roots?
  830. if (D > 0)
  831. {
  832. float sqrootD = Math::Sqrt(D);
  833. // solve the quadratic to get the components of the normal
  834. float Ny0 = (-b + sqrootD) / (2 * a);
  835. float Ny1 = (-b - sqrootD) / (2 * a);
  836. // Derive Z from this
  837. float Nz0 = (r - Ny0 * eyeSpacePos.y) / eyeSpacePos.z;
  838. float Nz1 = (r - Ny1 * eyeSpacePos.y) / eyeSpacePos.z;
  839. // Get the point of tangency
  840. // Only consider points of tangency in front of the camera
  841. float Pz0 = (Lyz - rsq) / (eyeSpacePos.z - ((Nz0 / Ny0) * eyeSpacePos.y));
  842. if (Pz0 < 0)
  843. {
  844. // Project point onto near plane in worldspace
  845. float neary0 = (Nz0 * mNearDist) / Ny0;
  846. // now we need to map this to viewport coords
  847. // use projection matriy since that will take into account all factors
  848. Vector3 rely0 = projMatrix * Vector3(0, neary0, -mNearDist);
  849. // find out whether this is a top side or bottom side
  850. float Py0 = -(Pz0 * Nz0) / Ny0;
  851. if (Py0 > eyeSpacePos.y)
  852. {
  853. *top = std::min(*top, rely0.y);
  854. }
  855. else
  856. {
  857. *bottom = std::max(*bottom, rely0.y);
  858. }
  859. }
  860. float Pz1 = (Lyz - rsq) / (eyeSpacePos.z - ((Nz1 / Ny1) * eyeSpacePos.y));
  861. if (Pz1 < 0)
  862. {
  863. // Project point onto near plane in worldspace
  864. float neary1 = (Nz1 * mNearDist) / Ny1;
  865. // now we need to map this to viewport coords
  866. // use projection matriy since that will take into account all factors
  867. Vector3 rely1 = projMatrix * Vector3(0, neary1, -mNearDist);
  868. // find out whether this is a top side or bottom side
  869. float Py1 = -(Pz1 * Nz1) / Ny1;
  870. if (Py1 > eyeSpacePos.y)
  871. {
  872. *top = std::min(*top, rely1.y);
  873. }
  874. else
  875. {
  876. *bottom = std::max(*bottom, rely1.y);
  877. }
  878. }
  879. }
  880. }
  881. return (*left != -1.0f) || (*top != 1.0f) || (*right != 1.0f) || (*bottom != -1.0f);
  882. }
  883. //---------------------------------------------------------------------
  884. void Frustum::setCustomViewMatrix(bool enable, const Matrix4& viewMatrix)
  885. {
  886. mCustomViewMatrix = enable;
  887. if (enable)
  888. {
  889. assert(viewMatrix.isAffine());
  890. mViewMatrix = viewMatrix;
  891. }
  892. invalidateView();
  893. }
  894. //---------------------------------------------------------------------
  895. void Frustum::setCustomProjectionMatrix(bool enable, const Matrix4& projMatrix)
  896. {
  897. mCustomProjMatrix = enable;
  898. if (enable)
  899. {
  900. mProjMatrix = projMatrix;
  901. }
  902. invalidateFrustum();
  903. }
  904. //---------------------------------------------------------------------
  905. void Frustum::setOrthoWindow(float w, float h)
  906. {
  907. mOrthoHeight = h;
  908. mAspect = w / h;
  909. invalidateFrustum();
  910. }
  911. //---------------------------------------------------------------------
  912. void Frustum::setOrthoWindowHeight(float h)
  913. {
  914. mOrthoHeight = h;
  915. invalidateFrustum();
  916. }
  917. //---------------------------------------------------------------------
  918. void Frustum::setOrthoWindowWidth(float w)
  919. {
  920. mOrthoHeight = w / mAspect;
  921. invalidateFrustum();
  922. }
  923. //---------------------------------------------------------------------
  924. float Frustum::getOrthoWindowHeight() const
  925. {
  926. return mOrthoHeight;
  927. }
  928. //---------------------------------------------------------------------
  929. float Frustum::getOrthoWindowWidth() const
  930. {
  931. return mOrthoHeight * mAspect;
  932. }
  933. //---------------------------------------------------------------------
  934. void Frustum::setFrustumExtents(float left, float right, float top, float bottom)
  935. {
  936. mFrustumExtentsManuallySet = true;
  937. mLeft = left;
  938. mRight = right;
  939. mTop = top;
  940. mBottom = bottom;
  941. invalidateFrustum();
  942. }
  943. //---------------------------------------------------------------------
  944. void Frustum::resetFrustumExtents()
  945. {
  946. mFrustumExtentsManuallySet = false;
  947. invalidateFrustum();
  948. }
  949. //---------------------------------------------------------------------
  950. void Frustum::getFrustumExtents(float& outleft, float& outright, float& outtop, float& outbottom) const
  951. {
  952. updateFrustum();
  953. outleft = mLeft;
  954. outright = mRight;
  955. outtop = mTop;
  956. outbottom = mBottom;
  957. }
  958. } // namespace CamelotEngine