OgreFrustum.cpp 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121
  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 "OgreFrustum.h"
  25. #include "OgreMath.h"
  26. #include "OgreMatrix3.h"
  27. #include "OgreSphere.h"
  28. #include "OgreException.h"
  29. #include "OgreHardwareBufferManager.h"
  30. #include "OgreHardwareVertexBuffer.h"
  31. #include "OgreHardwareIndexBuffer.h"
  32. #include "OgreRenderSystem.h"
  33. #include "CmRenderSystemManager.h"
  34. namespace Ogre {
  35. const Real 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(Real farPlane)
  78. {
  79. mFarDist = farPlane;
  80. invalidateFrustum();
  81. }
  82. //-----------------------------------------------------------------------
  83. Real Frustum::getFarClipDistance(void) const
  84. {
  85. return mFarDist;
  86. }
  87. //-----------------------------------------------------------------------
  88. void Frustum::setNearClipDistance(Real nearPlane)
  89. {
  90. if (nearPlane <= 0)
  91. OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Near clip distance must be greater than zero.",
  92. "Frustum::setNearClipDistance");
  93. mNearDist = nearPlane;
  94. invalidateFrustum();
  95. }
  96. //-----------------------------------------------------------------------
  97. Real Frustum::getNearClipDistance(void) const
  98. {
  99. return mNearDist;
  100. }
  101. //---------------------------------------------------------------------
  102. void Frustum::setFrustumOffset(const Vector2& offset)
  103. {
  104. mFrustumOffset = offset;
  105. invalidateFrustum();
  106. }
  107. //---------------------------------------------------------------------
  108. void Frustum::setFrustumOffset(Real horizontal, Real vertical)
  109. {
  110. setFrustumOffset(Vector2(horizontal, vertical));
  111. }
  112. //---------------------------------------------------------------------
  113. const Vector2& Frustum::getFrustumOffset() const
  114. {
  115. return mFrustumOffset;
  116. }
  117. //---------------------------------------------------------------------
  118. void Frustum::setFocalLength(Real focalLength)
  119. {
  120. if (focalLength <= 0)
  121. {
  122. OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
  123. "Focal length must be greater than zero.",
  124. "Frustum::setFocalLength");
  125. }
  126. mFocalLength = focalLength;
  127. invalidateFrustum();
  128. }
  129. //---------------------------------------------------------------------
  130. Real 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(Real& left, Real& right, Real& bottom, Real& 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. Real tanThetaY = Math::Tan(thetaY);
  279. Real tanThetaX = tanThetaY * mAspect;
  280. Real nearFocal = mNearDist / mFocalLength;
  281. Real nearOffsetX = mFrustumOffset.x * nearFocal;
  282. Real nearOffsetY = mFrustumOffset.y * nearFocal;
  283. Real half_w = tanThetaX * mNearDist;
  284. Real 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. Real half_w = getOrthoWindowWidth() * 0.5f;
  298. Real 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. Real left, right, bottom, top;
  315. #if OGRE_NO_VIEWPORT_ORIENTATIONMODE == 0
  316. if (mOrientationMode != OR_PORTRAIT)
  317. calcProjectionParameters(bottom, top, left, right);
  318. else
  319. #endif
  320. calcProjectionParameters(left, right, bottom, top);
  321. if (!mCustomProjMatrix)
  322. {
  323. // The code below will dealing with general projection
  324. // parameters, similar glFrustum and glOrtho.
  325. // Doesn't optimise manually except division operator, so the
  326. // code more self-explaining.
  327. Real inv_w = 1 / (right - left);
  328. Real inv_h = 1 / (top - bottom);
  329. Real inv_d = 1 / (mFarDist - mNearDist);
  330. // Recalc if frustum params changed
  331. if (mProjType == PT_PERSPECTIVE)
  332. {
  333. // Calc matrix elements
  334. Real A = 2 * mNearDist * inv_w;
  335. Real B = 2 * mNearDist * inv_h;
  336. Real C = (right + left) * inv_w;
  337. Real D = (top + bottom) * inv_h;
  338. Real q, qn;
  339. if (mFarDist == 0)
  340. {
  341. // Infinite far plane
  342. q = Frustum::INFINITE_FAR_PLANE_ADJUST - 1;
  343. qn = mNearDist * (Frustum::INFINITE_FAR_PLANE_ADJUST - 2);
  344. }
  345. else
  346. {
  347. q = - (mFarDist + mNearDist) * inv_d;
  348. qn = -2 * (mFarDist * mNearDist) * inv_d;
  349. }
  350. // NB: This creates 'uniform' perspective projection matrix,
  351. // which depth range [-1,1], right-handed rules
  352. //
  353. // [ A 0 C 0 ]
  354. // [ 0 B D 0 ]
  355. // [ 0 0 q qn ]
  356. // [ 0 0 -1 0 ]
  357. //
  358. // A = 2 * near / (right - left)
  359. // B = 2 * near / (top - bottom)
  360. // C = (right + left) / (right - left)
  361. // D = (top + bottom) / (top - bottom)
  362. // q = - (far + near) / (far - near)
  363. // qn = - 2 * (far * near) / (far - near)
  364. mProjMatrix = Matrix4::ZERO;
  365. mProjMatrix[0][0] = A;
  366. mProjMatrix[0][2] = C;
  367. mProjMatrix[1][1] = B;
  368. mProjMatrix[1][2] = D;
  369. mProjMatrix[2][2] = q;
  370. mProjMatrix[2][3] = qn;
  371. mProjMatrix[3][2] = -1;
  372. } // perspective
  373. else if (mProjType == PT_ORTHOGRAPHIC)
  374. {
  375. Real A = 2 * inv_w;
  376. Real B = 2 * inv_h;
  377. Real C = - (right + left) * inv_w;
  378. Real D = - (top + bottom) * inv_h;
  379. Real q, qn;
  380. if (mFarDist == 0)
  381. {
  382. // Can not do infinite far plane here, avoid divided zero only
  383. q = - Frustum::INFINITE_FAR_PLANE_ADJUST / mNearDist;
  384. qn = - Frustum::INFINITE_FAR_PLANE_ADJUST - 1;
  385. }
  386. else
  387. {
  388. q = - 2 * inv_d;
  389. qn = - (mFarDist + mNearDist) * inv_d;
  390. }
  391. // NB: This creates 'uniform' orthographic projection matrix,
  392. // which depth range [-1,1], right-handed rules
  393. //
  394. // [ A 0 0 C ]
  395. // [ 0 B 0 D ]
  396. // [ 0 0 q qn ]
  397. // [ 0 0 0 1 ]
  398. //
  399. // A = 2 * / (right - left)
  400. // B = 2 * / (top - bottom)
  401. // C = - (right + left) / (right - left)
  402. // D = - (top + bottom) / (top - bottom)
  403. // q = - 2 / (far - near)
  404. // qn = - (far + near) / (far - near)
  405. mProjMatrix = Matrix4::ZERO;
  406. mProjMatrix[0][0] = A;
  407. mProjMatrix[0][3] = C;
  408. mProjMatrix[1][1] = B;
  409. mProjMatrix[1][3] = D;
  410. mProjMatrix[2][2] = q;
  411. mProjMatrix[2][3] = qn;
  412. mProjMatrix[3][3] = 1;
  413. } // ortho
  414. } // !mCustomProjMatrix
  415. #if OGRE_NO_VIEWPORT_ORIENTATIONMODE == 0
  416. // Deal with orientation mode
  417. mProjMatrix = mProjMatrix * Quaternion(Degree(mOrientationMode * 90.f), Vector3::UNIT_Z);
  418. #endif
  419. RenderSystem* renderSystem = CamelotEngine::RenderSystemManager::getActive();
  420. // API specific
  421. renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRS);
  422. // API specific for Gpu Programs
  423. renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRSDepth, true);
  424. // Calculate bounding box (local)
  425. // Box is from 0, down -Z, max dimensions as determined from far plane
  426. // If infinite view frustum just pick a far value
  427. Real farDist = (mFarDist == 0) ? 100000 : mFarDist;
  428. // Near plane bounds
  429. Vector3 min(left, bottom, -farDist);
  430. Vector3 max(right, top, 0);
  431. if (mCustomProjMatrix)
  432. {
  433. // Some custom projection matrices can have unusual inverted settings
  434. // So make sure the AABB is the right way around to start with
  435. Vector3 tmp = min;
  436. min.makeFloor(max);
  437. max.makeCeil(tmp);
  438. }
  439. if (mProjType == PT_PERSPECTIVE)
  440. {
  441. // Merge with far plane bounds
  442. Real radio = farDist / mNearDist;
  443. min.makeFloor(Vector3(left * radio, bottom * radio, -farDist));
  444. max.makeCeil(Vector3(right * radio, top * radio, 0));
  445. }
  446. mBoundingBox.setExtents(min, max);
  447. mRecalcFrustum = false;
  448. // Signal to update frustum clipping planes
  449. mRecalcFrustumPlanes = true;
  450. }
  451. //-----------------------------------------------------------------------
  452. void Frustum::updateFrustum(void) const
  453. {
  454. if (isFrustumOutOfDate())
  455. {
  456. updateFrustumImpl();
  457. }
  458. }
  459. //-----------------------------------------------------------------------
  460. void Frustum::updateVertexData(void) const
  461. {
  462. if (mRecalcVertexData)
  463. {
  464. if (mVertexData.vertexBufferBinding->getBufferCount() <= 0)
  465. {
  466. // Initialise vertex & index data
  467. mVertexData.vertexDeclaration->addElement(0, 0, VET_FLOAT3, VES_POSITION);
  468. mVertexData.vertexCount = 32;
  469. mVertexData.vertexStart = 0;
  470. mVertexData.vertexBufferBinding->setBinding( 0,
  471. HardwareBufferManager::getSingleton().createVertexBuffer(
  472. sizeof(float)*3, 32, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY) );
  473. }
  474. // Note: Even though we can dealing with general projection matrix here,
  475. // but because it's incompatibly with infinite far plane, thus, we
  476. // still need to working with projection parameters.
  477. // Calc near plane corners
  478. Real vpLeft, vpRight, vpBottom, vpTop;
  479. calcProjectionParameters(vpLeft, vpRight, vpBottom, vpTop);
  480. // Treat infinite fardist as some arbitrary far value
  481. Real farDist = (mFarDist == 0) ? 100000 : mFarDist;
  482. // Calc far plane corners
  483. Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1;
  484. Real farLeft = vpLeft * radio;
  485. Real farRight = vpRight * radio;
  486. Real farBottom = vpBottom * radio;
  487. Real farTop = vpTop * radio;
  488. // Calculate vertex positions (local)
  489. // 0 is the origin
  490. // 1, 2, 3, 4 are the points on the near plane, top left first, clockwise
  491. // 5, 6, 7, 8 are the points on the far plane, top left first, clockwise
  492. HardwareVertexBufferPtr vbuf = mVertexData.vertexBufferBinding->getBuffer(0);
  493. float* pFloat = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
  494. // near plane (remember frustum is going in -Z direction)
  495. *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  496. *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  497. *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  498. *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  499. *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  500. *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  501. *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  502. *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  503. // far plane (remember frustum is going in -Z direction)
  504. *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist;
  505. *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist;
  506. *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist;
  507. *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
  508. *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
  509. *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist;
  510. *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist;
  511. *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist;
  512. // Sides of the pyramid
  513. *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f;
  514. *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  515. *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f;
  516. *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  517. *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f;
  518. *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  519. *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f;
  520. *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  521. // Sides of the box
  522. *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  523. *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist;
  524. *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist;
  525. *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist;
  526. *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  527. *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
  528. *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
  529. *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist;
  530. vbuf->unlock();
  531. mRecalcVertexData = false;
  532. }
  533. }
  534. //-----------------------------------------------------------------------
  535. bool Frustum::isViewOutOfDate(void) const
  536. {
  537. // Attached to node?
  538. // TODO PORT - Not attached to node because I'll be handling this differently
  539. //if (mParentNode)
  540. //{
  541. // if (mRecalcView ||
  542. // mParentNode->_getDerivedOrientation() != mLastParentOrientation ||
  543. // mParentNode->_getDerivedPosition() != mLastParentPosition)
  544. // {
  545. // // Ok, we're out of date with SceneNode we're attached to
  546. // mLastParentOrientation = mParentNode->_getDerivedOrientation();
  547. // mLastParentPosition = mParentNode->_getDerivedPosition();
  548. // mRecalcView = true;
  549. // }
  550. //}
  551. return mRecalcView;
  552. }
  553. //-----------------------------------------------------------------------
  554. bool Frustum::isFrustumOutOfDate(void) const
  555. {
  556. return mRecalcFrustum;
  557. }
  558. //-----------------------------------------------------------------------
  559. void Frustum::updateViewImpl(void) const
  560. {
  561. // ----------------------
  562. // Update the view matrix
  563. // ----------------------
  564. // Get orientation from quaternion
  565. if (!mCustomViewMatrix)
  566. {
  567. Matrix3 rot;
  568. const Quaternion& orientation = getOrientationForViewUpdate();
  569. const Vector3& position = getPositionForViewUpdate();
  570. mViewMatrix = Math::makeViewMatrix(position, orientation, 0);
  571. }
  572. mRecalcView = false;
  573. // Signal to update frustum clipping planes
  574. mRecalcFrustumPlanes = true;
  575. // Signal to update world space corners
  576. mRecalcWorldSpaceCorners = true;
  577. }
  578. //---------------------------------------------------------------------
  579. void Frustum::calcViewMatrixRelative(const Vector3& relPos, Matrix4& matToUpdate) const
  580. {
  581. Matrix4 matTrans = Matrix4::IDENTITY;
  582. matTrans.setTrans(relPos);
  583. matToUpdate = getViewMatrix() * matTrans;
  584. }
  585. //-----------------------------------------------------------------------
  586. void Frustum::updateView(void) const
  587. {
  588. if (isViewOutOfDate())
  589. {
  590. updateViewImpl();
  591. }
  592. }
  593. //-----------------------------------------------------------------------
  594. void Frustum::updateFrustumPlanesImpl(void) const
  595. {
  596. // -------------------------
  597. // Update the frustum planes
  598. // -------------------------
  599. Matrix4 combo = mProjMatrix * mViewMatrix;
  600. mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.x = combo[3][0] + combo[0][0];
  601. mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.y = combo[3][1] + combo[0][1];
  602. mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.z = combo[3][2] + combo[0][2];
  603. mFrustumPlanes[FRUSTUM_PLANE_LEFT].d = combo[3][3] + combo[0][3];
  604. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.x = combo[3][0] - combo[0][0];
  605. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.y = combo[3][1] - combo[0][1];
  606. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.z = combo[3][2] - combo[0][2];
  607. mFrustumPlanes[FRUSTUM_PLANE_RIGHT].d = combo[3][3] - combo[0][3];
  608. mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.x = combo[3][0] - combo[1][0];
  609. mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.y = combo[3][1] - combo[1][1];
  610. mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.z = combo[3][2] - combo[1][2];
  611. mFrustumPlanes[FRUSTUM_PLANE_TOP].d = combo[3][3] - combo[1][3];
  612. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.x = combo[3][0] + combo[1][0];
  613. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.y = combo[3][1] + combo[1][1];
  614. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.z = combo[3][2] + combo[1][2];
  615. mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].d = combo[3][3] + combo[1][3];
  616. mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.x = combo[3][0] + combo[2][0];
  617. mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.y = combo[3][1] + combo[2][1];
  618. mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.z = combo[3][2] + combo[2][2];
  619. mFrustumPlanes[FRUSTUM_PLANE_NEAR].d = combo[3][3] + combo[2][3];
  620. mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.x = combo[3][0] - combo[2][0];
  621. mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.y = combo[3][1] - combo[2][1];
  622. mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.z = combo[3][2] - combo[2][2];
  623. mFrustumPlanes[FRUSTUM_PLANE_FAR].d = combo[3][3] - combo[2][3];
  624. // Renormalise any normals which were not unit length
  625. for(int i=0; i<6; i++ )
  626. {
  627. Real length = mFrustumPlanes[i].normal.normalise();
  628. mFrustumPlanes[i].d /= length;
  629. }
  630. mRecalcFrustumPlanes = false;
  631. }
  632. //-----------------------------------------------------------------------
  633. void Frustum::updateFrustumPlanes(void) const
  634. {
  635. updateView();
  636. updateFrustum();
  637. if (mRecalcFrustumPlanes)
  638. {
  639. updateFrustumPlanesImpl();
  640. }
  641. }
  642. //-----------------------------------------------------------------------
  643. void Frustum::updateWorldSpaceCornersImpl(void) const
  644. {
  645. Matrix4 eyeToWorld = mViewMatrix.inverseAffine();
  646. // Note: Even though we can dealing with general projection matrix here,
  647. // but because it's incompatibly with infinite far plane, thus, we
  648. // still need to working with projection parameters.
  649. // Calc near plane corners
  650. Real nearLeft, nearRight, nearBottom, nearTop;
  651. calcProjectionParameters(nearLeft, nearRight, nearBottom, nearTop);
  652. // Treat infinite fardist as some arbitrary far value
  653. Real farDist = (mFarDist == 0) ? 100000 : mFarDist;
  654. // Calc far palne corners
  655. Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1;
  656. Real farLeft = nearLeft * radio;
  657. Real farRight = nearRight * radio;
  658. Real farBottom = nearBottom * radio;
  659. Real farTop = nearTop * radio;
  660. // near
  661. mWorldSpaceCorners[0] = eyeToWorld.transformAffine(Vector3(nearRight, nearTop, -mNearDist));
  662. mWorldSpaceCorners[1] = eyeToWorld.transformAffine(Vector3(nearLeft, nearTop, -mNearDist));
  663. mWorldSpaceCorners[2] = eyeToWorld.transformAffine(Vector3(nearLeft, nearBottom, -mNearDist));
  664. mWorldSpaceCorners[3] = eyeToWorld.transformAffine(Vector3(nearRight, nearBottom, -mNearDist));
  665. // far
  666. mWorldSpaceCorners[4] = eyeToWorld.transformAffine(Vector3(farRight, farTop, -farDist));
  667. mWorldSpaceCorners[5] = eyeToWorld.transformAffine(Vector3(farLeft, farTop, -farDist));
  668. mWorldSpaceCorners[6] = eyeToWorld.transformAffine(Vector3(farLeft, farBottom, -farDist));
  669. mWorldSpaceCorners[7] = eyeToWorld.transformAffine(Vector3(farRight, farBottom, -farDist));
  670. mRecalcWorldSpaceCorners = false;
  671. }
  672. //-----------------------------------------------------------------------
  673. void Frustum::updateWorldSpaceCorners(void) const
  674. {
  675. updateView();
  676. if (mRecalcWorldSpaceCorners)
  677. {
  678. updateWorldSpaceCornersImpl();
  679. }
  680. }
  681. //-----------------------------------------------------------------------
  682. Real Frustum::getAspectRatio(void) const
  683. {
  684. return mAspect;
  685. }
  686. //-----------------------------------------------------------------------
  687. void Frustum::setAspectRatio(Real r)
  688. {
  689. mAspect = r;
  690. invalidateFrustum();
  691. }
  692. //-----------------------------------------------------------------------
  693. const AxisAlignedBox& Frustum::getBoundingBox(void) const
  694. {
  695. return mBoundingBox;
  696. }
  697. //-----------------------------------------------------------------------
  698. Real Frustum::getBoundingRadius(void) const
  699. {
  700. return (mFarDist == 0)? 100000 : mFarDist;
  701. }
  702. // -------------------------------------------------------------------
  703. void Frustum::invalidateFrustum() const
  704. {
  705. mRecalcFrustum = true;
  706. mRecalcFrustumPlanes = true;
  707. mRecalcWorldSpaceCorners = true;
  708. mRecalcVertexData = true;
  709. }
  710. // -------------------------------------------------------------------
  711. void Frustum::invalidateView() const
  712. {
  713. mRecalcView = true;
  714. mRecalcFrustumPlanes = true;
  715. mRecalcWorldSpaceCorners = true;
  716. }
  717. // -------------------------------------------------------------------
  718. const Vector3* Frustum::getWorldSpaceCorners(void) const
  719. {
  720. updateWorldSpaceCorners();
  721. return mWorldSpaceCorners;
  722. }
  723. //-----------------------------------------------------------------------
  724. void Frustum::setProjectionType(ProjectionType pt)
  725. {
  726. mProjType = pt;
  727. invalidateFrustum();
  728. }
  729. //-----------------------------------------------------------------------
  730. ProjectionType Frustum::getProjectionType(void) const
  731. {
  732. return mProjType;
  733. }
  734. //-----------------------------------------------------------------------
  735. const Vector3& Frustum::getPositionForViewUpdate(void) const
  736. {
  737. return mLastParentPosition;
  738. }
  739. //-----------------------------------------------------------------------
  740. const Quaternion& Frustum::getOrientationForViewUpdate(void) const
  741. {
  742. return mLastParentOrientation;
  743. }
  744. //---------------------------------------------------------------------
  745. bool Frustum::projectSphere(const Sphere& sphere,
  746. Real* left, Real* top, Real* right, Real* bottom) const
  747. {
  748. // See http://www.gamasutra.com/features/20021011/lengyel_06.htm
  749. // Transform light position into camera space
  750. updateView();
  751. Vector3 eyeSpacePos = mViewMatrix.transformAffine(sphere.getCenter());
  752. // initialise
  753. *left = *bottom = -1.0f;
  754. *right = *top = 1.0f;
  755. if (eyeSpacePos.z < 0)
  756. {
  757. updateFrustum();
  758. const Matrix4& projMatrix = getProjectionMatrix();
  759. Real r = sphere.getRadius();
  760. Real rsq = r * r;
  761. // early-exit
  762. if (eyeSpacePos.squaredLength() <= rsq)
  763. return false;
  764. Real Lxz = Math::Sqr(eyeSpacePos.x) + Math::Sqr(eyeSpacePos.z);
  765. Real Lyz = Math::Sqr(eyeSpacePos.y) + Math::Sqr(eyeSpacePos.z);
  766. // Find the tangent planes to the sphere
  767. // XZ first
  768. // calculate quadratic discriminant: b*b - 4ac
  769. // x = Nx
  770. // a = Lx^2 + Lz^2
  771. // b = -2rLx
  772. // c = r^2 - Lz^2
  773. Real a = Lxz;
  774. Real b = -2.0f * r * eyeSpacePos.x;
  775. Real c = rsq - Math::Sqr(eyeSpacePos.z);
  776. Real D = b*b - 4.0f*a*c;
  777. // two roots?
  778. if (D > 0)
  779. {
  780. Real sqrootD = Math::Sqrt(D);
  781. // solve the quadratic to get the components of the normal
  782. Real Nx0 = (-b + sqrootD) / (2 * a);
  783. Real Nx1 = (-b - sqrootD) / (2 * a);
  784. // Derive Z from this
  785. Real Nz0 = (r - Nx0 * eyeSpacePos.x) / eyeSpacePos.z;
  786. Real Nz1 = (r - Nx1 * eyeSpacePos.x) / eyeSpacePos.z;
  787. // Get the point of tangency
  788. // Only consider points of tangency in front of the camera
  789. Real Pz0 = (Lxz - rsq) / (eyeSpacePos.z - ((Nz0 / Nx0) * eyeSpacePos.x));
  790. if (Pz0 < 0)
  791. {
  792. // Project point onto near plane in worldspace
  793. Real nearx0 = (Nz0 * mNearDist) / Nx0;
  794. // now we need to map this to viewport coords
  795. // use projection matrix since that will take into account all factors
  796. Vector3 relx0 = projMatrix * Vector3(nearx0, 0, -mNearDist);
  797. // find out whether this is a left side or right side
  798. Real Px0 = -(Pz0 * Nz0) / Nx0;
  799. if (Px0 > eyeSpacePos.x)
  800. {
  801. *right = std::min(*right, relx0.x);
  802. }
  803. else
  804. {
  805. *left = std::max(*left, relx0.x);
  806. }
  807. }
  808. Real Pz1 = (Lxz - rsq) / (eyeSpacePos.z - ((Nz1 / Nx1) * eyeSpacePos.x));
  809. if (Pz1 < 0)
  810. {
  811. // Project point onto near plane in worldspace
  812. Real nearx1 = (Nz1 * mNearDist) / Nx1;
  813. // now we need to map this to viewport coords
  814. // use projection matrix since that will take into account all factors
  815. Vector3 relx1 = projMatrix * Vector3(nearx1, 0, -mNearDist);
  816. // find out whether this is a left side or right side
  817. Real Px1 = -(Pz1 * Nz1) / Nx1;
  818. if (Px1 > eyeSpacePos.x)
  819. {
  820. *right = std::min(*right, relx1.x);
  821. }
  822. else
  823. {
  824. *left = std::max(*left, relx1.x);
  825. }
  826. }
  827. }
  828. // Now YZ
  829. // calculate quadratic discriminant: b*b - 4ac
  830. // x = Ny
  831. // a = Ly^2 + Lz^2
  832. // b = -2rLy
  833. // c = r^2 - Lz^2
  834. a = Lyz;
  835. b = -2.0f * r * eyeSpacePos.y;
  836. c = rsq - Math::Sqr(eyeSpacePos.z);
  837. D = b*b - 4.0f*a*c;
  838. // two roots?
  839. if (D > 0)
  840. {
  841. Real sqrootD = Math::Sqrt(D);
  842. // solve the quadratic to get the components of the normal
  843. Real Ny0 = (-b + sqrootD) / (2 * a);
  844. Real Ny1 = (-b - sqrootD) / (2 * a);
  845. // Derive Z from this
  846. Real Nz0 = (r - Ny0 * eyeSpacePos.y) / eyeSpacePos.z;
  847. Real Nz1 = (r - Ny1 * eyeSpacePos.y) / eyeSpacePos.z;
  848. // Get the point of tangency
  849. // Only consider points of tangency in front of the camera
  850. Real Pz0 = (Lyz - rsq) / (eyeSpacePos.z - ((Nz0 / Ny0) * eyeSpacePos.y));
  851. if (Pz0 < 0)
  852. {
  853. // Project point onto near plane in worldspace
  854. Real neary0 = (Nz0 * mNearDist) / Ny0;
  855. // now we need to map this to viewport coords
  856. // use projection matriy since that will take into account all factors
  857. Vector3 rely0 = projMatrix * Vector3(0, neary0, -mNearDist);
  858. // find out whether this is a top side or bottom side
  859. Real Py0 = -(Pz0 * Nz0) / Ny0;
  860. if (Py0 > eyeSpacePos.y)
  861. {
  862. *top = std::min(*top, rely0.y);
  863. }
  864. else
  865. {
  866. *bottom = std::max(*bottom, rely0.y);
  867. }
  868. }
  869. Real Pz1 = (Lyz - rsq) / (eyeSpacePos.z - ((Nz1 / Ny1) * eyeSpacePos.y));
  870. if (Pz1 < 0)
  871. {
  872. // Project point onto near plane in worldspace
  873. Real neary1 = (Nz1 * mNearDist) / Ny1;
  874. // now we need to map this to viewport coords
  875. // use projection matriy since that will take into account all factors
  876. Vector3 rely1 = projMatrix * Vector3(0, neary1, -mNearDist);
  877. // find out whether this is a top side or bottom side
  878. Real Py1 = -(Pz1 * Nz1) / Ny1;
  879. if (Py1 > eyeSpacePos.y)
  880. {
  881. *top = std::min(*top, rely1.y);
  882. }
  883. else
  884. {
  885. *bottom = std::max(*bottom, rely1.y);
  886. }
  887. }
  888. }
  889. }
  890. return (*left != -1.0f) || (*top != 1.0f) || (*right != 1.0f) || (*bottom != -1.0f);
  891. }
  892. //---------------------------------------------------------------------
  893. void Frustum::setCustomViewMatrix(bool enable, const Matrix4& viewMatrix)
  894. {
  895. mCustomViewMatrix = enable;
  896. if (enable)
  897. {
  898. assert(viewMatrix.isAffine());
  899. mViewMatrix = viewMatrix;
  900. }
  901. invalidateView();
  902. }
  903. //---------------------------------------------------------------------
  904. void Frustum::setCustomProjectionMatrix(bool enable, const Matrix4& projMatrix)
  905. {
  906. mCustomProjMatrix = enable;
  907. if (enable)
  908. {
  909. mProjMatrix = projMatrix;
  910. }
  911. invalidateFrustum();
  912. }
  913. //---------------------------------------------------------------------
  914. void Frustum::setOrthoWindow(Real w, Real h)
  915. {
  916. mOrthoHeight = h;
  917. mAspect = w / h;
  918. invalidateFrustum();
  919. }
  920. //---------------------------------------------------------------------
  921. void Frustum::setOrthoWindowHeight(Real h)
  922. {
  923. mOrthoHeight = h;
  924. invalidateFrustum();
  925. }
  926. //---------------------------------------------------------------------
  927. void Frustum::setOrthoWindowWidth(Real w)
  928. {
  929. mOrthoHeight = w / mAspect;
  930. invalidateFrustum();
  931. }
  932. //---------------------------------------------------------------------
  933. Real Frustum::getOrthoWindowHeight() const
  934. {
  935. return mOrthoHeight;
  936. }
  937. //---------------------------------------------------------------------
  938. Real Frustum::getOrthoWindowWidth() const
  939. {
  940. return mOrthoHeight * mAspect;
  941. }
  942. //---------------------------------------------------------------------
  943. void Frustum::setFrustumExtents(Real left, Real right, Real top, Real bottom)
  944. {
  945. mFrustumExtentsManuallySet = true;
  946. mLeft = left;
  947. mRight = right;
  948. mTop = top;
  949. mBottom = bottom;
  950. invalidateFrustum();
  951. }
  952. //---------------------------------------------------------------------
  953. void Frustum::resetFrustumExtents()
  954. {
  955. mFrustumExtentsManuallySet = false;
  956. invalidateFrustum();
  957. }
  958. //---------------------------------------------------------------------
  959. void Frustum::getFrustumExtents(Real& outleft, Real& outright, Real& outtop, Real& outbottom) const
  960. {
  961. updateFrustum();
  962. outleft = mLeft;
  963. outright = mRight;
  964. outtop = mTop;
  965. outbottom = mBottom;
  966. }
  967. } // namespace Ogre