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 | |
---|
7 | Copyright (c) 2000-2006 Torus Knot Software Ltd |
---|
8 | Also see acknowledgements in Readme.html |
---|
9 | |
---|
10 | This program is free software; you can redistribute it and/or modify it under |
---|
11 | the terms of the GNU Lesser General Public License as published by the Free Software |
---|
12 | Foundation; either version 2 of the License, or (at your option) any later |
---|
13 | version. |
---|
14 | |
---|
15 | This program is distributed in the hope that it will be useful, but WITHOUT |
---|
16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
---|
17 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
---|
18 | |
---|
19 | You should have received a copy of the GNU Lesser General Public License along with |
---|
20 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
---|
21 | Place - Suite 330, Boston, MA 02111-1307, USA, or go to |
---|
22 | http://www.gnu.org/copyleft/lesser.txt. |
---|
23 | |
---|
24 | You may alternatively use this source under the terms of a specific version of |
---|
25 | the OGRE Unrestricted License provided you have obtained such a license from |
---|
26 | Torus Knot Software Ltd. |
---|
27 | ----------------------------------------------------------------------------- |
---|
28 | */ |
---|
29 | #include "OgreStableHeaders.h" |
---|
30 | #include "OgreFrustum.h" |
---|
31 | |
---|
32 | #include "OgreMath.h" |
---|
33 | #include "OgreMatrix3.h" |
---|
34 | #include "OgreSceneNode.h" |
---|
35 | #include "OgreSphere.h" |
---|
36 | #include "OgreLogManager.h" |
---|
37 | #include "OgreException.h" |
---|
38 | #include "OgreRoot.h" |
---|
39 | #include "OgreCamera.h" |
---|
40 | #include "OgreHardwareBufferManager.h" |
---|
41 | #include "OgreHardwareVertexBuffer.h" |
---|
42 | #include "OgreHardwareIndexBuffer.h" |
---|
43 | #include "OgreMaterialManager.h" |
---|
44 | #include "OgreRenderSystem.h" |
---|
45 | |
---|
46 | namespace Ogre { |
---|
47 | |
---|
48 | String Frustum::msMovableType = "Frustum"; |
---|
49 | const Real Frustum::INFINITE_FAR_PLANE_ADJUST = 0.00001; |
---|
50 | //----------------------------------------------------------------------- |
---|
51 | Frustum::Frustum() : |
---|
52 | mProjType(PT_PERSPECTIVE), |
---|
53 | mFOVy(Radian(Math::PI/4.0)), |
---|
54 | mFarDist(100000.0f), |
---|
55 | mNearDist(100.0f), |
---|
56 | mAspect(1.33333333333333f), |
---|
57 | mFrustumOffset(Vector2::ZERO), |
---|
58 | mFocalLength(1.0f), |
---|
59 | mLastParentOrientation(Quaternion::IDENTITY), |
---|
60 | mLastParentPosition(Vector3::ZERO), |
---|
61 | mRecalcFrustum(true), |
---|
62 | mRecalcView(true), |
---|
63 | mRecalcFrustumPlanes(true), |
---|
64 | mRecalcWorldSpaceCorners(true), |
---|
65 | mRecalcVertexData(true), |
---|
66 | mCustomViewMatrix(false), |
---|
67 | mCustomProjMatrix(false), |
---|
68 | mReflect(false), |
---|
69 | mLinkedReflectPlane(0), |
---|
70 | mObliqueDepthProjection(false), |
---|
71 | mLinkedObliqueProjPlane(0) |
---|
72 | { |
---|
73 | // Initialise material |
---|
74 | mMaterial = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting"); |
---|
75 | |
---|
76 | // Alter superclass members |
---|
77 | mVisible = false; |
---|
78 | mParentNode = 0; |
---|
79 | |
---|
80 | mLastLinkedReflectionPlane.normal = Vector3::ZERO; |
---|
81 | mLastLinkedObliqueProjPlane.normal = Vector3::ZERO; |
---|
82 | |
---|
83 | |
---|
84 | updateView(); |
---|
85 | updateFrustum(); |
---|
86 | } |
---|
87 | |
---|
88 | //----------------------------------------------------------------------- |
---|
89 | Frustum::~Frustum() |
---|
90 | { |
---|
91 | // Do nothing |
---|
92 | } |
---|
93 | |
---|
94 | //----------------------------------------------------------------------- |
---|
95 | void Frustum::setFOVy(const Radian& fov) |
---|
96 | { |
---|
97 | mFOVy = fov; |
---|
98 | invalidateFrustum(); |
---|
99 | } |
---|
100 | |
---|
101 | //----------------------------------------------------------------------- |
---|
102 | const Radian& Frustum::getFOVy(void) const |
---|
103 | { |
---|
104 | return mFOVy; |
---|
105 | } |
---|
106 | |
---|
107 | |
---|
108 | //----------------------------------------------------------------------- |
---|
109 | void Frustum::setFarClipDistance(Real farPlane) |
---|
110 | { |
---|
111 | mFarDist = farPlane; |
---|
112 | invalidateFrustum(); |
---|
113 | } |
---|
114 | |
---|
115 | //----------------------------------------------------------------------- |
---|
116 | Real Frustum::getFarClipDistance(void) const |
---|
117 | { |
---|
118 | return mFarDist; |
---|
119 | } |
---|
120 | |
---|
121 | //----------------------------------------------------------------------- |
---|
122 | void Frustum::setNearClipDistance(Real nearPlane) |
---|
123 | { |
---|
124 | if (nearPlane <= 0) |
---|
125 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Near clip distance must be greater than zero.", |
---|
126 | "Frustum::setNearClipDistance"); |
---|
127 | mNearDist = nearPlane; |
---|
128 | invalidateFrustum(); |
---|
129 | } |
---|
130 | |
---|
131 | //----------------------------------------------------------------------- |
---|
132 | Real Frustum::getNearClipDistance(void) const |
---|
133 | { |
---|
134 | return mNearDist; |
---|
135 | } |
---|
136 | |
---|
137 | //--------------------------------------------------------------------- |
---|
138 | void Frustum::setFrustumOffset(const Vector2& offset) |
---|
139 | { |
---|
140 | mFrustumOffset = offset; |
---|
141 | invalidateFrustum(); |
---|
142 | } |
---|
143 | //--------------------------------------------------------------------- |
---|
144 | void Frustum::setFrustumOffset(Real horizontal, Real vertical) |
---|
145 | { |
---|
146 | setFrustumOffset(Vector2(horizontal, vertical)); |
---|
147 | } |
---|
148 | //--------------------------------------------------------------------- |
---|
149 | const Vector2& Frustum::getFrustumOffset() const |
---|
150 | { |
---|
151 | return mFrustumOffset; |
---|
152 | } |
---|
153 | //--------------------------------------------------------------------- |
---|
154 | void Frustum::setFocalLength(Real focalLength) |
---|
155 | { |
---|
156 | if (focalLength <= 0) |
---|
157 | { |
---|
158 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, |
---|
159 | "Focal length must be greater than zero.", |
---|
160 | "Frustum::setFocalLength"); |
---|
161 | } |
---|
162 | |
---|
163 | mFocalLength = focalLength; |
---|
164 | invalidateFrustum(); |
---|
165 | } |
---|
166 | //--------------------------------------------------------------------- |
---|
167 | Real Frustum::getFocalLength() const |
---|
168 | { |
---|
169 | return mFocalLength; |
---|
170 | } |
---|
171 | //----------------------------------------------------------------------- |
---|
172 | const Matrix4& Frustum::getProjectionMatrix(void) const |
---|
173 | { |
---|
174 | |
---|
175 | updateFrustum(); |
---|
176 | |
---|
177 | return mProjMatrix; |
---|
178 | } |
---|
179 | //----------------------------------------------------------------------- |
---|
180 | const Matrix4& Frustum::getProjectionMatrixWithRSDepth(void) const |
---|
181 | { |
---|
182 | |
---|
183 | updateFrustum(); |
---|
184 | |
---|
185 | return mProjMatrixRSDepth; |
---|
186 | } |
---|
187 | //----------------------------------------------------------------------- |
---|
188 | const Matrix4& Frustum::getProjectionMatrixRS(void) const |
---|
189 | { |
---|
190 | |
---|
191 | updateFrustum(); |
---|
192 | |
---|
193 | return mProjMatrixRS; |
---|
194 | } |
---|
195 | //----------------------------------------------------------------------- |
---|
196 | const Matrix4& Frustum::getViewMatrix(void) const |
---|
197 | { |
---|
198 | updateView(); |
---|
199 | |
---|
200 | return mViewMatrix; |
---|
201 | |
---|
202 | } |
---|
203 | |
---|
204 | //----------------------------------------------------------------------- |
---|
205 | const Plane* Frustum::getFrustumPlanes(void) const |
---|
206 | { |
---|
207 | // Make any pending updates to the calculated frustum planes |
---|
208 | updateFrustumPlanes(); |
---|
209 | |
---|
210 | return mFrustumPlanes; |
---|
211 | } |
---|
212 | |
---|
213 | //----------------------------------------------------------------------- |
---|
214 | const Plane& Frustum::getFrustumPlane(unsigned short plane) const |
---|
215 | { |
---|
216 | // Make any pending updates to the calculated frustum planes |
---|
217 | updateFrustumPlanes(); |
---|
218 | |
---|
219 | return mFrustumPlanes[plane]; |
---|
220 | |
---|
221 | } |
---|
222 | |
---|
223 | //----------------------------------------------------------------------- |
---|
224 | bool Frustum::isVisible(const AxisAlignedBox& bound, FrustumPlane* culledBy) const |
---|
225 | { |
---|
226 | // Null boxes always invisible |
---|
227 | if (bound.isNull()) return false; |
---|
228 | |
---|
229 | // Infinite boxes always visible |
---|
230 | if (bound.isInfinite()) return true; |
---|
231 | |
---|
232 | // Make any pending updates to the calculated frustum planes |
---|
233 | updateFrustumPlanes(); |
---|
234 | |
---|
235 | // Get centre of the box |
---|
236 | Vector3 centre = bound.getCenter(); |
---|
237 | // Get the half-size of the box |
---|
238 | Vector3 halfSize = bound.getHalfSize(); |
---|
239 | |
---|
240 | // For each plane, see if all points are on the negative side |
---|
241 | // If so, object is not visible |
---|
242 | for (int plane = 0; plane < 6; ++plane) |
---|
243 | { |
---|
244 | // Skip far plane if infinite view frustum |
---|
245 | if (plane == FRUSTUM_PLANE_FAR && mFarDist == 0) |
---|
246 | continue; |
---|
247 | |
---|
248 | Plane::Side side = mFrustumPlanes[plane].getSide(centre, halfSize); |
---|
249 | if (side == Plane::NEGATIVE_SIDE) |
---|
250 | { |
---|
251 | // ALL corners on negative side therefore out of view |
---|
252 | if (culledBy) |
---|
253 | *culledBy = (FrustumPlane)plane; |
---|
254 | return false; |
---|
255 | } |
---|
256 | |
---|
257 | } |
---|
258 | |
---|
259 | return true; |
---|
260 | } |
---|
261 | |
---|
262 | //----------------------------------------------------------------------- |
---|
263 | bool Frustum::isVisible(const Vector3& vert, FrustumPlane* culledBy) const |
---|
264 | { |
---|
265 | // Make any pending updates to the calculated frustum planes |
---|
266 | updateFrustumPlanes(); |
---|
267 | |
---|
268 | // For each plane, see if all points are on the negative side |
---|
269 | // If so, object is not visible |
---|
270 | for (int plane = 0; plane < 6; ++plane) |
---|
271 | { |
---|
272 | // Skip far plane if infinite view frustum |
---|
273 | if (plane == FRUSTUM_PLANE_FAR && mFarDist == 0) |
---|
274 | continue; |
---|
275 | |
---|
276 | if (mFrustumPlanes[plane].getSide(vert) == Plane::NEGATIVE_SIDE) |
---|
277 | { |
---|
278 | // ALL corners on negative side therefore out of view |
---|
279 | if (culledBy) |
---|
280 | *culledBy = (FrustumPlane)plane; |
---|
281 | return false; |
---|
282 | } |
---|
283 | |
---|
284 | } |
---|
285 | |
---|
286 | return true; |
---|
287 | } |
---|
288 | |
---|
289 | //----------------------------------------------------------------------- |
---|
290 | bool Frustum::isVisible(const Sphere& sphere, FrustumPlane* culledBy) const |
---|
291 | { |
---|
292 | // Make any pending updates to the calculated frustum planes |
---|
293 | updateFrustumPlanes(); |
---|
294 | |
---|
295 | // For each plane, see if sphere is on negative side |
---|
296 | // If so, object is not visible |
---|
297 | for (int plane = 0; plane < 6; ++plane) |
---|
298 | { |
---|
299 | // Skip far plane if infinite view frustum |
---|
300 | if (plane == FRUSTUM_PLANE_FAR && mFarDist == 0) |
---|
301 | continue; |
---|
302 | |
---|
303 | // If the distance from sphere center to plane is negative, and 'more negative' |
---|
304 | // than the radius of the sphere, sphere is outside frustum |
---|
305 | if (mFrustumPlanes[plane].getDistance(sphere.getCenter()) < -sphere.getRadius()) |
---|
306 | { |
---|
307 | // ALL corners on negative side therefore out of view |
---|
308 | if (culledBy) |
---|
309 | *culledBy = (FrustumPlane)plane; |
---|
310 | return false; |
---|
311 | } |
---|
312 | |
---|
313 | } |
---|
314 | |
---|
315 | return true; |
---|
316 | } |
---|
317 | //----------------------------------------------------------------------- |
---|
318 | void Frustum::calcProjectionParameters(Real& left, Real& right, Real& bottom, Real& top) const |
---|
319 | { |
---|
320 | if (mCustomProjMatrix) |
---|
321 | { |
---|
322 | // Convert clipspace corners to camera space |
---|
323 | Matrix4 invProj = mProjMatrix.inverse(); |
---|
324 | Vector3 topLeft(-0.5f, 0.5f, 0.0f); |
---|
325 | Vector3 bottomRight(0.5f, -0.5f, 0.0f); |
---|
326 | |
---|
327 | topLeft = invProj * topLeft; |
---|
328 | bottomRight = invProj * bottomRight; |
---|
329 | |
---|
330 | left = topLeft.x; |
---|
331 | top = topLeft.y; |
---|
332 | right = bottomRight.x; |
---|
333 | bottom = bottomRight.y; |
---|
334 | |
---|
335 | } |
---|
336 | else |
---|
337 | { |
---|
338 | // Calculate general projection parameters |
---|
339 | |
---|
340 | Radian thetaY (mFOVy * 0.5f); |
---|
341 | Real tanThetaY = Math::Tan(thetaY); |
---|
342 | Real tanThetaX = tanThetaY * mAspect; |
---|
343 | |
---|
344 | // Unknow how to apply frustum offset to orthographic camera, just ignore here |
---|
345 | Real nearFocal = (mProjType == PT_PERSPECTIVE) ? mNearDist / mFocalLength : 0; |
---|
346 | Real nearOffsetX = mFrustumOffset.x * nearFocal; |
---|
347 | Real nearOffsetY = mFrustumOffset.y * nearFocal; |
---|
348 | Real half_w = tanThetaX * mNearDist; |
---|
349 | Real half_h = tanThetaY * mNearDist; |
---|
350 | |
---|
351 | left = - half_w + nearOffsetX; |
---|
352 | right = + half_w + nearOffsetX; |
---|
353 | bottom = - half_h + nearOffsetY; |
---|
354 | top = + half_h + nearOffsetY; |
---|
355 | } |
---|
356 | } |
---|
357 | //----------------------------------------------------------------------- |
---|
358 | void Frustum::updateFrustumImpl(void) const |
---|
359 | { |
---|
360 | // Common calcs |
---|
361 | Real left, right, bottom, top; |
---|
362 | calcProjectionParameters(left, right, bottom, top); |
---|
363 | |
---|
364 | if (!mCustomProjMatrix) |
---|
365 | { |
---|
366 | |
---|
367 | // The code below will dealing with general projection |
---|
368 | // parameters, similar glFrustum and glOrtho. |
---|
369 | // Doesn't optimise manually except division operator, so the |
---|
370 | // code more self-explaining. |
---|
371 | |
---|
372 | Real inv_w = 1 / (right - left); |
---|
373 | Real inv_h = 1 / (top - bottom); |
---|
374 | Real inv_d = 1 / (mFarDist - mNearDist); |
---|
375 | |
---|
376 | // Recalc if frustum params changed |
---|
377 | if (mProjType == PT_PERSPECTIVE) |
---|
378 | { |
---|
379 | // Calc matrix elements |
---|
380 | Real A = 2 * mNearDist * inv_w; |
---|
381 | Real B = 2 * mNearDist * inv_h; |
---|
382 | Real C = (right + left) * inv_w; |
---|
383 | Real D = (top + bottom) * inv_h; |
---|
384 | Real q, qn; |
---|
385 | if (mFarDist == 0) |
---|
386 | { |
---|
387 | // Infinite far plane |
---|
388 | q = Frustum::INFINITE_FAR_PLANE_ADJUST - 1; |
---|
389 | qn = mNearDist * (Frustum::INFINITE_FAR_PLANE_ADJUST - 2); |
---|
390 | } |
---|
391 | else |
---|
392 | { |
---|
393 | q = - (mFarDist + mNearDist) * inv_d; |
---|
394 | qn = -2 * (mFarDist * mNearDist) * inv_d; |
---|
395 | } |
---|
396 | |
---|
397 | // NB: This creates 'uniform' perspective projection matrix, |
---|
398 | // which depth range [-1,1], right-handed rules |
---|
399 | // |
---|
400 | // [ A 0 C 0 ] |
---|
401 | // [ 0 B D 0 ] |
---|
402 | // [ 0 0 q qn ] |
---|
403 | // [ 0 0 -1 0 ] |
---|
404 | // |
---|
405 | // A = 2 * near / (right - left) |
---|
406 | // B = 2 * near / (top - bottom) |
---|
407 | // C = (right + left) / (right - left) |
---|
408 | // D = (top + bottom) / (top - bottom) |
---|
409 | // q = - (far + near) / (far - near) |
---|
410 | // qn = - 2 * (far * near) / (far - near) |
---|
411 | |
---|
412 | mProjMatrix = Matrix4::ZERO; |
---|
413 | mProjMatrix[0][0] = A; |
---|
414 | mProjMatrix[0][2] = C; |
---|
415 | mProjMatrix[1][1] = B; |
---|
416 | mProjMatrix[1][2] = D; |
---|
417 | mProjMatrix[2][2] = q; |
---|
418 | mProjMatrix[2][3] = qn; |
---|
419 | mProjMatrix[3][2] = -1; |
---|
420 | |
---|
421 | if (mObliqueDepthProjection) |
---|
422 | { |
---|
423 | // Translate the plane into view space |
---|
424 | |
---|
425 | // Don't use getViewMatrix here, incase overrided by |
---|
426 | // camera and return a cull frustum view matrix |
---|
427 | updateView(); |
---|
428 | Plane plane = mViewMatrix * mObliqueProjPlane; |
---|
429 | |
---|
430 | // Thanks to Eric Lenyel for posting this calculation |
---|
431 | // at www.terathon.com |
---|
432 | |
---|
433 | // Calculate the clip-space corner point opposite the |
---|
434 | // clipping plane |
---|
435 | // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and |
---|
436 | // transform it into camera space by multiplying it |
---|
437 | // by the inverse of the projection matrix |
---|
438 | |
---|
439 | /* generalised version |
---|
440 | Vector4 q = matrix.inverse() * |
---|
441 | Vector4(Math::Sign(plane.normal.x), |
---|
442 | Math::Sign(plane.normal.y), 1.0f, 1.0f); |
---|
443 | */ |
---|
444 | Vector4 q; |
---|
445 | q.x = (Math::Sign(plane.normal.x) + mProjMatrix[0][2]) / mProjMatrix[0][0]; |
---|
446 | q.y = (Math::Sign(plane.normal.y) + mProjMatrix[1][2]) / mProjMatrix[1][1]; |
---|
447 | q.z = -1; |
---|
448 | q.w = (1 + mProjMatrix[2][2]) / mProjMatrix[2][3]; |
---|
449 | |
---|
450 | // Calculate the scaled plane vector |
---|
451 | Vector4 clipPlane4d(plane.normal.x, plane.normal.y, plane.normal.z, plane.d); |
---|
452 | Vector4 c = clipPlane4d * (2 / (clipPlane4d.dotProduct(q))); |
---|
453 | |
---|
454 | // Replace the third row of the projection matrix |
---|
455 | mProjMatrix[2][0] = c.x; |
---|
456 | mProjMatrix[2][1] = c.y; |
---|
457 | mProjMatrix[2][2] = c.z + 1; |
---|
458 | mProjMatrix[2][3] = c.w; |
---|
459 | } |
---|
460 | } // perspective |
---|
461 | else if (mProjType == PT_ORTHOGRAPHIC) |
---|
462 | { |
---|
463 | Real A = 2 * inv_w; |
---|
464 | Real B = 2 * inv_h; |
---|
465 | Real C = - (right + left) * inv_w; |
---|
466 | Real D = - (top + bottom) * inv_h; |
---|
467 | Real q, qn; |
---|
468 | if (mFarDist == 0) |
---|
469 | { |
---|
470 | // Can not do infinite far plane here, avoid divided zero only |
---|
471 | q = - Frustum::INFINITE_FAR_PLANE_ADJUST / mNearDist; |
---|
472 | qn = - Frustum::INFINITE_FAR_PLANE_ADJUST - 1; |
---|
473 | } |
---|
474 | else |
---|
475 | { |
---|
476 | q = - 2 * inv_d; |
---|
477 | qn = - (mFarDist + mNearDist) * inv_d; |
---|
478 | } |
---|
479 | |
---|
480 | // NB: This creates 'uniform' orthographic projection matrix, |
---|
481 | // which depth range [-1,1], right-handed rules |
---|
482 | // |
---|
483 | // [ A 0 0 C ] |
---|
484 | // [ 0 B 0 D ] |
---|
485 | // [ 0 0 q qn ] |
---|
486 | // [ 0 0 0 1 ] |
---|
487 | // |
---|
488 | // A = 2 * / (right - left) |
---|
489 | // B = 2 * / (top - bottom) |
---|
490 | // C = - (right + left) / (right - left) |
---|
491 | // D = - (top + bottom) / (top - bottom) |
---|
492 | // q = - 2 / (far - near) |
---|
493 | // qn = - (far + near) / (far - near) |
---|
494 | |
---|
495 | mProjMatrix = Matrix4::ZERO; |
---|
496 | mProjMatrix[0][0] = A; |
---|
497 | mProjMatrix[0][3] = C; |
---|
498 | mProjMatrix[1][1] = B; |
---|
499 | mProjMatrix[1][3] = D; |
---|
500 | mProjMatrix[2][2] = q; |
---|
501 | mProjMatrix[2][3] = qn; |
---|
502 | mProjMatrix[3][3] = 1; |
---|
503 | } // ortho |
---|
504 | } // !mCustomProjMatrix |
---|
505 | |
---|
506 | RenderSystem* renderSystem = Root::getSingleton().getRenderSystem(); |
---|
507 | // API specific |
---|
508 | renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRS); |
---|
509 | // API specific for Gpu Programs |
---|
510 | renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRSDepth, true); |
---|
511 | |
---|
512 | |
---|
513 | // Calculate bounding box (local) |
---|
514 | // Box is from 0, down -Z, max dimensions as determined from far plane |
---|
515 | // If infinite view frustum just pick a far value |
---|
516 | Real farDist = (mFarDist == 0) ? 100000 : mFarDist; |
---|
517 | // Near plane bounds |
---|
518 | Vector3 min(left, bottom, -farDist); |
---|
519 | Vector3 max(right, top, 0); |
---|
520 | |
---|
521 | if (mCustomProjMatrix) |
---|
522 | { |
---|
523 | // Some custom projection matrices can have unusual inverted settings |
---|
524 | // So make sure the AABB is the right way around to start with |
---|
525 | Vector3 tmp = min; |
---|
526 | min.makeFloor(max); |
---|
527 | max.makeCeil(tmp); |
---|
528 | } |
---|
529 | |
---|
530 | if (mProjType == PT_PERSPECTIVE) |
---|
531 | { |
---|
532 | // Merge with far plane bounds |
---|
533 | Real radio = farDist / mNearDist; |
---|
534 | min.makeFloor(Vector3(left * radio, bottom * radio, -farDist)); |
---|
535 | max.makeCeil(Vector3(right * radio, top * radio, 0)); |
---|
536 | } |
---|
537 | mBoundingBox.setExtents(min, max); |
---|
538 | |
---|
539 | mRecalcFrustum = false; |
---|
540 | |
---|
541 | // Signal to update frustum clipping planes |
---|
542 | mRecalcFrustumPlanes = true; |
---|
543 | } |
---|
544 | //----------------------------------------------------------------------- |
---|
545 | void Frustum::updateFrustum(void) const |
---|
546 | { |
---|
547 | if (isFrustumOutOfDate()) |
---|
548 | { |
---|
549 | updateFrustumImpl(); |
---|
550 | } |
---|
551 | } |
---|
552 | |
---|
553 | //----------------------------------------------------------------------- |
---|
554 | void Frustum::updateVertexData(void) const |
---|
555 | { |
---|
556 | if (mRecalcVertexData) |
---|
557 | { |
---|
558 | if (mVertexData.vertexBufferBinding->getBufferCount() <= 0) |
---|
559 | { |
---|
560 | // Initialise vertex & index data |
---|
561 | mVertexData.vertexDeclaration->addElement(0, 0, VET_FLOAT3, VES_POSITION); |
---|
562 | mVertexData.vertexCount = 32; |
---|
563 | mVertexData.vertexStart = 0; |
---|
564 | mVertexData.vertexBufferBinding->setBinding( 0, |
---|
565 | HardwareBufferManager::getSingleton().createVertexBuffer( |
---|
566 | sizeof(float)*3, 32, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY) ); |
---|
567 | } |
---|
568 | |
---|
569 | // Note: Even though we can dealing with general projection matrix here, |
---|
570 | // but because it's incompatibly with infinite far plane, thus, we |
---|
571 | // still need to working with projection parameters. |
---|
572 | |
---|
573 | // Calc near plane corners |
---|
574 | Real vpLeft, vpRight, vpBottom, vpTop; |
---|
575 | calcProjectionParameters(vpLeft, vpRight, vpBottom, vpTop); |
---|
576 | |
---|
577 | // Treat infinite fardist as some arbitrary far value |
---|
578 | Real farDist = (mFarDist == 0) ? 100000 : mFarDist; |
---|
579 | |
---|
580 | // Calc far palne corners |
---|
581 | Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1; |
---|
582 | Real farLeft = vpLeft * radio; |
---|
583 | Real farRight = vpRight * radio; |
---|
584 | Real farBottom = vpBottom * radio; |
---|
585 | Real farTop = vpTop * radio; |
---|
586 | |
---|
587 | // Calculate vertex positions (local) |
---|
588 | // 0 is the origin |
---|
589 | // 1, 2, 3, 4 are the points on the near plane, top left first, clockwise |
---|
590 | // 5, 6, 7, 8 are the points on the far plane, top left first, clockwise |
---|
591 | HardwareVertexBufferSharedPtr vbuf = mVertexData.vertexBufferBinding->getBuffer(0); |
---|
592 | float* pFloat = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD)); |
---|
593 | |
---|
594 | // near plane (remember frustum is going in -Z direction) |
---|
595 | *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist; |
---|
596 | *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist; |
---|
597 | |
---|
598 | *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist; |
---|
599 | *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; |
---|
600 | |
---|
601 | *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; |
---|
602 | *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; |
---|
603 | |
---|
604 | *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; |
---|
605 | *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist; |
---|
606 | |
---|
607 | // far plane (remember frustum is going in -Z direction) |
---|
608 | *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist; |
---|
609 | *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist; |
---|
610 | |
---|
611 | *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist; |
---|
612 | *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist; |
---|
613 | |
---|
614 | *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist; |
---|
615 | *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist; |
---|
616 | |
---|
617 | *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist; |
---|
618 | *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist; |
---|
619 | |
---|
620 | // Sides of the pyramid |
---|
621 | *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f; |
---|
622 | *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist; |
---|
623 | |
---|
624 | *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f; |
---|
625 | *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist; |
---|
626 | |
---|
627 | *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f; |
---|
628 | *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; |
---|
629 | |
---|
630 | *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f; |
---|
631 | *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; |
---|
632 | |
---|
633 | // Sides of the box |
---|
634 | |
---|
635 | *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist; |
---|
636 | *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist; |
---|
637 | |
---|
638 | *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist; |
---|
639 | *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist; |
---|
640 | |
---|
641 | *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; |
---|
642 | *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist; |
---|
643 | |
---|
644 | *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; |
---|
645 | *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist; |
---|
646 | |
---|
647 | |
---|
648 | vbuf->unlock(); |
---|
649 | |
---|
650 | mRecalcVertexData = false; |
---|
651 | } |
---|
652 | } |
---|
653 | |
---|
654 | //----------------------------------------------------------------------- |
---|
655 | bool Frustum::isViewOutOfDate(void) const |
---|
656 | { |
---|
657 | // Attached to node? |
---|
658 | if (mParentNode) |
---|
659 | { |
---|
660 | if (mRecalcView || |
---|
661 | mParentNode->_getDerivedOrientation() != mLastParentOrientation || |
---|
662 | mParentNode->_getDerivedPosition() != mLastParentPosition) |
---|
663 | { |
---|
664 | // Ok, we're out of date with SceneNode we're attached to |
---|
665 | mLastParentOrientation = mParentNode->_getDerivedOrientation(); |
---|
666 | mLastParentPosition = mParentNode->_getDerivedPosition(); |
---|
667 | mRecalcView = true; |
---|
668 | } |
---|
669 | } |
---|
670 | // Deriving reflection from linked plane? |
---|
671 | if (mLinkedReflectPlane && |
---|
672 | !(mLastLinkedReflectionPlane == mLinkedReflectPlane->_getDerivedPlane())) |
---|
673 | { |
---|
674 | mReflectPlane = mLinkedReflectPlane->_getDerivedPlane(); |
---|
675 | mReflectMatrix = Math::buildReflectionMatrix(mReflectPlane); |
---|
676 | mLastLinkedReflectionPlane = mLinkedReflectPlane->_getDerivedPlane(); |
---|
677 | mRecalcView = true; |
---|
678 | } |
---|
679 | |
---|
680 | return mRecalcView; |
---|
681 | } |
---|
682 | |
---|
683 | //----------------------------------------------------------------------- |
---|
684 | bool Frustum::isFrustumOutOfDate(void) const |
---|
685 | { |
---|
686 | // Deriving custom near plane from linked plane? |
---|
687 | if (mObliqueDepthProjection) |
---|
688 | { |
---|
689 | // Out of date when view out of data since plane needs to be in view space |
---|
690 | if (isViewOutOfDate()) |
---|
691 | { |
---|
692 | mRecalcFrustum = true; |
---|
693 | } |
---|
694 | // Update derived plane |
---|
695 | if (mLinkedObliqueProjPlane && |
---|
696 | !(mLastLinkedObliqueProjPlane == mLinkedObliqueProjPlane->_getDerivedPlane())) |
---|
697 | { |
---|
698 | mObliqueProjPlane = mLinkedObliqueProjPlane->_getDerivedPlane(); |
---|
699 | mLastLinkedObliqueProjPlane = mObliqueProjPlane; |
---|
700 | mRecalcFrustum = true; |
---|
701 | } |
---|
702 | } |
---|
703 | |
---|
704 | return mRecalcFrustum; |
---|
705 | } |
---|
706 | |
---|
707 | //----------------------------------------------------------------------- |
---|
708 | void Frustum::updateViewImpl(void) const |
---|
709 | { |
---|
710 | // ---------------------- |
---|
711 | // Update the view matrix |
---|
712 | // ---------------------- |
---|
713 | |
---|
714 | // View matrix is: |
---|
715 | // |
---|
716 | // [ Lx Uy Dz Tx ] |
---|
717 | // [ Lx Uy Dz Ty ] |
---|
718 | // [ Lx Uy Dz Tz ] |
---|
719 | // [ 0 0 0 1 ] |
---|
720 | // |
---|
721 | // Where T = -(Transposed(Rot) * Pos) |
---|
722 | |
---|
723 | // This is most efficiently done using 3x3 Matrices |
---|
724 | |
---|
725 | // Get orientation from quaternion |
---|
726 | |
---|
727 | if (!mCustomViewMatrix) |
---|
728 | { |
---|
729 | Matrix3 rot; |
---|
730 | const Quaternion& orientation = getOrientationForViewUpdate(); |
---|
731 | const Vector3& position = getPositionForViewUpdate(); |
---|
732 | orientation.ToRotationMatrix(rot); |
---|
733 | |
---|
734 | // Make the translation relative to new axes |
---|
735 | Matrix3 rotT = rot.Transpose(); |
---|
736 | Vector3 trans = -rotT * position; |
---|
737 | |
---|
738 | // Make final matrix |
---|
739 | mViewMatrix = Matrix4::IDENTITY; |
---|
740 | mViewMatrix = rotT; // fills upper 3x3 |
---|
741 | mViewMatrix[0][3] = trans.x; |
---|
742 | mViewMatrix[1][3] = trans.y; |
---|
743 | mViewMatrix[2][3] = trans.z; |
---|
744 | |
---|
745 | // Deal with reflections |
---|
746 | if (mReflect) |
---|
747 | { |
---|
748 | mViewMatrix = mViewMatrix * mReflectMatrix; |
---|
749 | } |
---|
750 | } |
---|
751 | |
---|
752 | mRecalcView = false; |
---|
753 | |
---|
754 | // Signal to update frustum clipping planes |
---|
755 | mRecalcFrustumPlanes = true; |
---|
756 | // Signal to update world space corners |
---|
757 | mRecalcWorldSpaceCorners = true; |
---|
758 | // Signal to update frustum if oblique plane enabled, |
---|
759 | // since plane needs to be in view space |
---|
760 | if (mObliqueDepthProjection) |
---|
761 | { |
---|
762 | mRecalcFrustum = true; |
---|
763 | } |
---|
764 | } |
---|
765 | //----------------------------------------------------------------------- |
---|
766 | void Frustum::updateView(void) const |
---|
767 | { |
---|
768 | if (isViewOutOfDate()) |
---|
769 | { |
---|
770 | updateViewImpl(); |
---|
771 | } |
---|
772 | } |
---|
773 | |
---|
774 | //----------------------------------------------------------------------- |
---|
775 | void Frustum::updateFrustumPlanesImpl(void) const |
---|
776 | { |
---|
777 | // ------------------------- |
---|
778 | // Update the frustum planes |
---|
779 | // ------------------------- |
---|
780 | Matrix4 combo = mProjMatrix * mViewMatrix; |
---|
781 | |
---|
782 | mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.x = combo[3][0] + combo[0][0]; |
---|
783 | mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.y = combo[3][1] + combo[0][1]; |
---|
784 | mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.z = combo[3][2] + combo[0][2]; |
---|
785 | mFrustumPlanes[FRUSTUM_PLANE_LEFT].d = combo[3][3] + combo[0][3]; |
---|
786 | |
---|
787 | mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.x = combo[3][0] - combo[0][0]; |
---|
788 | mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.y = combo[3][1] - combo[0][1]; |
---|
789 | mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.z = combo[3][2] - combo[0][2]; |
---|
790 | mFrustumPlanes[FRUSTUM_PLANE_RIGHT].d = combo[3][3] - combo[0][3]; |
---|
791 | |
---|
792 | mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.x = combo[3][0] - combo[1][0]; |
---|
793 | mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.y = combo[3][1] - combo[1][1]; |
---|
794 | mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.z = combo[3][2] - combo[1][2]; |
---|
795 | mFrustumPlanes[FRUSTUM_PLANE_TOP].d = combo[3][3] - combo[1][3]; |
---|
796 | |
---|
797 | mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.x = combo[3][0] + combo[1][0]; |
---|
798 | mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.y = combo[3][1] + combo[1][1]; |
---|
799 | mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.z = combo[3][2] + combo[1][2]; |
---|
800 | mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].d = combo[3][3] + combo[1][3]; |
---|
801 | |
---|
802 | mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.x = combo[3][0] + combo[2][0]; |
---|
803 | mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.y = combo[3][1] + combo[2][1]; |
---|
804 | mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.z = combo[3][2] + combo[2][2]; |
---|
805 | mFrustumPlanes[FRUSTUM_PLANE_NEAR].d = combo[3][3] + combo[2][3]; |
---|
806 | |
---|
807 | mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.x = combo[3][0] - combo[2][0]; |
---|
808 | mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.y = combo[3][1] - combo[2][1]; |
---|
809 | mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.z = combo[3][2] - combo[2][2]; |
---|
810 | mFrustumPlanes[FRUSTUM_PLANE_FAR].d = combo[3][3] - combo[2][3]; |
---|
811 | |
---|
812 | // Renormalise any normals which were not unit length |
---|
813 | for(int i=0; i<6; i++ ) |
---|
814 | { |
---|
815 | float length = mFrustumPlanes[i].normal.normalise(); |
---|
816 | mFrustumPlanes[i].d /= length; |
---|
817 | } |
---|
818 | |
---|
819 | mRecalcFrustumPlanes = false; |
---|
820 | } |
---|
821 | //----------------------------------------------------------------------- |
---|
822 | void Frustum::updateFrustumPlanes(void) const |
---|
823 | { |
---|
824 | updateView(); |
---|
825 | updateFrustum(); |
---|
826 | |
---|
827 | if (mRecalcFrustumPlanes) |
---|
828 | { |
---|
829 | updateFrustumPlanesImpl(); |
---|
830 | } |
---|
831 | } |
---|
832 | //----------------------------------------------------------------------- |
---|
833 | void Frustum::updateWorldSpaceCornersImpl(void) const |
---|
834 | { |
---|
835 | Matrix4 eyeToWorld = mViewMatrix.inverseAffine(); |
---|
836 | |
---|
837 | // Note: Even though we can dealing with general projection matrix here, |
---|
838 | // but because it's incompatibly with infinite far plane, thus, we |
---|
839 | // still need to working with projection parameters. |
---|
840 | |
---|
841 | // Calc near plane corners |
---|
842 | Real nearLeft, nearRight, nearBottom, nearTop; |
---|
843 | calcProjectionParameters(nearLeft, nearRight, nearBottom, nearTop); |
---|
844 | |
---|
845 | // Treat infinite fardist as some arbitrary far value |
---|
846 | Real farDist = (mFarDist == 0) ? 100000 : mFarDist; |
---|
847 | |
---|
848 | // Calc far palne corners |
---|
849 | Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1; |
---|
850 | Real farLeft = nearLeft * radio; |
---|
851 | Real farRight = nearRight * radio; |
---|
852 | Real farBottom = nearBottom * radio; |
---|
853 | Real farTop = nearTop * radio; |
---|
854 | |
---|
855 | // near |
---|
856 | mWorldSpaceCorners[0] = eyeToWorld.transformAffine(Vector3(nearRight, nearTop, -mNearDist)); |
---|
857 | mWorldSpaceCorners[1] = eyeToWorld.transformAffine(Vector3(nearLeft, nearTop, -mNearDist)); |
---|
858 | mWorldSpaceCorners[2] = eyeToWorld.transformAffine(Vector3(nearLeft, nearBottom, -mNearDist)); |
---|
859 | mWorldSpaceCorners[3] = eyeToWorld.transformAffine(Vector3(nearRight, nearBottom, -mNearDist)); |
---|
860 | // far |
---|
861 | mWorldSpaceCorners[4] = eyeToWorld.transformAffine(Vector3(farRight, farTop, -farDist)); |
---|
862 | mWorldSpaceCorners[5] = eyeToWorld.transformAffine(Vector3(farLeft, farTop, -farDist)); |
---|
863 | mWorldSpaceCorners[6] = eyeToWorld.transformAffine(Vector3(farLeft, farBottom, -farDist)); |
---|
864 | mWorldSpaceCorners[7] = eyeToWorld.transformAffine(Vector3(farRight, farBottom, -farDist)); |
---|
865 | |
---|
866 | |
---|
867 | mRecalcWorldSpaceCorners = false; |
---|
868 | } |
---|
869 | //----------------------------------------------------------------------- |
---|
870 | void Frustum::updateWorldSpaceCorners(void) const |
---|
871 | { |
---|
872 | updateView(); |
---|
873 | |
---|
874 | if (mRecalcWorldSpaceCorners) |
---|
875 | { |
---|
876 | updateWorldSpaceCornersImpl(); |
---|
877 | } |
---|
878 | |
---|
879 | } |
---|
880 | |
---|
881 | //----------------------------------------------------------------------- |
---|
882 | Real Frustum::getAspectRatio(void) const |
---|
883 | { |
---|
884 | return mAspect; |
---|
885 | } |
---|
886 | |
---|
887 | //----------------------------------------------------------------------- |
---|
888 | void Frustum::setAspectRatio(Real r) |
---|
889 | { |
---|
890 | mAspect = r; |
---|
891 | invalidateFrustum(); |
---|
892 | } |
---|
893 | |
---|
894 | //----------------------------------------------------------------------- |
---|
895 | const AxisAlignedBox& Frustum::getBoundingBox(void) const |
---|
896 | { |
---|
897 | return mBoundingBox; |
---|
898 | } |
---|
899 | //----------------------------------------------------------------------- |
---|
900 | void Frustum::_updateRenderQueue(RenderQueue* queue) |
---|
901 | { |
---|
902 | // Add self |
---|
903 | queue->addRenderable(this); |
---|
904 | } |
---|
905 | //----------------------------------------------------------------------- |
---|
906 | const String& Frustum::getMovableType(void) const |
---|
907 | { |
---|
908 | return msMovableType; |
---|
909 | } |
---|
910 | //----------------------------------------------------------------------- |
---|
911 | Real Frustum::getBoundingRadius(void) const |
---|
912 | { |
---|
913 | return (mFarDist == 0)? 100000 : mFarDist; |
---|
914 | } |
---|
915 | //----------------------------------------------------------------------- |
---|
916 | const MaterialPtr& Frustum::getMaterial(void) const |
---|
917 | { |
---|
918 | return mMaterial; |
---|
919 | } |
---|
920 | //----------------------------------------------------------------------- |
---|
921 | void Frustum::getRenderOperation(RenderOperation& op) |
---|
922 | { |
---|
923 | updateVertexData(); |
---|
924 | op.operationType = RenderOperation::OT_LINE_LIST; |
---|
925 | op.useIndexes = false; |
---|
926 | op.vertexData = &mVertexData; |
---|
927 | } |
---|
928 | //----------------------------------------------------------------------- |
---|
929 | void Frustum::getWorldTransforms(Matrix4* xform) const |
---|
930 | { |
---|
931 | if (mParentNode) |
---|
932 | *xform = mParentNode->_getFullTransform(); |
---|
933 | else |
---|
934 | *xform = Matrix4::IDENTITY; |
---|
935 | } |
---|
936 | //----------------------------------------------------------------------- |
---|
937 | const Quaternion& Frustum::getWorldOrientation(void) const |
---|
938 | { |
---|
939 | if (mParentNode) |
---|
940 | return mParentNode->_getDerivedOrientation(); |
---|
941 | else |
---|
942 | return Quaternion::IDENTITY; |
---|
943 | } |
---|
944 | //----------------------------------------------------------------------- |
---|
945 | const Vector3& Frustum::getWorldPosition(void) const |
---|
946 | { |
---|
947 | if (mParentNode) |
---|
948 | return mParentNode->_getDerivedPosition(); |
---|
949 | else |
---|
950 | return Vector3::ZERO; |
---|
951 | } |
---|
952 | //----------------------------------------------------------------------- |
---|
953 | Real Frustum::getSquaredViewDepth(const Camera* cam) const |
---|
954 | { |
---|
955 | // Calc from centre |
---|
956 | if (mParentNode) |
---|
957 | return (cam->getDerivedPosition() |
---|
958 | - mParentNode->_getDerivedPosition()).squaredLength(); |
---|
959 | else |
---|
960 | return 0; |
---|
961 | } |
---|
962 | //----------------------------------------------------------------------- |
---|
963 | const LightList& Frustum::getLights(void) const |
---|
964 | { |
---|
965 | // N/A |
---|
966 | static LightList ll; |
---|
967 | return ll; |
---|
968 | } |
---|
969 | //----------------------------------------------------------------------- |
---|
970 | void Frustum::_notifyCurrentCamera(Camera* cam) |
---|
971 | { |
---|
972 | // Make sure bounding box up-to-date |
---|
973 | updateFrustum(); |
---|
974 | |
---|
975 | MovableObject::_notifyCurrentCamera(cam); |
---|
976 | } |
---|
977 | |
---|
978 | // ------------------------------------------------------------------- |
---|
979 | void Frustum::invalidateFrustum() const |
---|
980 | { |
---|
981 | mRecalcFrustum = true; |
---|
982 | mRecalcFrustumPlanes = true; |
---|
983 | mRecalcWorldSpaceCorners = true; |
---|
984 | mRecalcVertexData = true; |
---|
985 | } |
---|
986 | // ------------------------------------------------------------------- |
---|
987 | void Frustum::invalidateView() const |
---|
988 | { |
---|
989 | mRecalcView = true; |
---|
990 | mRecalcFrustumPlanes = true; |
---|
991 | mRecalcWorldSpaceCorners = true; |
---|
992 | } |
---|
993 | // ------------------------------------------------------------------- |
---|
994 | const Vector3* Frustum::getWorldSpaceCorners(void) const |
---|
995 | { |
---|
996 | updateWorldSpaceCorners(); |
---|
997 | |
---|
998 | return mWorldSpaceCorners; |
---|
999 | } |
---|
1000 | //----------------------------------------------------------------------- |
---|
1001 | void Frustum::setProjectionType(ProjectionType pt) |
---|
1002 | { |
---|
1003 | mProjType = pt; |
---|
1004 | invalidateFrustum(); |
---|
1005 | } |
---|
1006 | |
---|
1007 | //----------------------------------------------------------------------- |
---|
1008 | ProjectionType Frustum::getProjectionType(void) const |
---|
1009 | { |
---|
1010 | return mProjType; |
---|
1011 | } |
---|
1012 | //----------------------------------------------------------------------- |
---|
1013 | const Vector3& Frustum::getPositionForViewUpdate(void) const |
---|
1014 | { |
---|
1015 | return mLastParentPosition; |
---|
1016 | } |
---|
1017 | //----------------------------------------------------------------------- |
---|
1018 | const Quaternion& Frustum::getOrientationForViewUpdate(void) const |
---|
1019 | { |
---|
1020 | return mLastParentOrientation; |
---|
1021 | } |
---|
1022 | //----------------------------------------------------------------------- |
---|
1023 | void Frustum::enableReflection(const Plane& p) |
---|
1024 | { |
---|
1025 | mReflect = true; |
---|
1026 | mReflectPlane = p; |
---|
1027 | mLinkedReflectPlane = 0; |
---|
1028 | mReflectMatrix = Math::buildReflectionMatrix(p); |
---|
1029 | invalidateView(); |
---|
1030 | |
---|
1031 | } |
---|
1032 | //----------------------------------------------------------------------- |
---|
1033 | void Frustum::enableReflection(const MovablePlane* p) |
---|
1034 | { |
---|
1035 | mReflect = true; |
---|
1036 | mLinkedReflectPlane = p; |
---|
1037 | mReflectPlane = mLinkedReflectPlane->_getDerivedPlane(); |
---|
1038 | mReflectMatrix = Math::buildReflectionMatrix(mReflectPlane); |
---|
1039 | mLastLinkedReflectionPlane = mLinkedReflectPlane->_getDerivedPlane(); |
---|
1040 | invalidateView(); |
---|
1041 | } |
---|
1042 | //----------------------------------------------------------------------- |
---|
1043 | void Frustum::disableReflection(void) |
---|
1044 | { |
---|
1045 | mReflect = false; |
---|
1046 | mLinkedReflectPlane = 0; |
---|
1047 | mLastLinkedReflectionPlane.normal = Vector3::ZERO; |
---|
1048 | invalidateView(); |
---|
1049 | } |
---|
1050 | //--------------------------------------------------------------------- |
---|
1051 | bool Frustum::projectSphere(const Sphere& sphere, |
---|
1052 | Real* left, Real* top, Real* right, Real* bottom) const |
---|
1053 | { |
---|
1054 | // See http://www.gamasutra.com/features/20021011/lengyel_06.htm |
---|
1055 | // Transform light position into camera space |
---|
1056 | |
---|
1057 | updateView(); |
---|
1058 | Vector3 eyeSpacePos = mViewMatrix.transformAffine(sphere.getCenter()); |
---|
1059 | |
---|
1060 | // initialise |
---|
1061 | *left = *bottom = -1.0f; |
---|
1062 | *right = *top = 1.0f; |
---|
1063 | |
---|
1064 | if (eyeSpacePos.z < 0) |
---|
1065 | { |
---|
1066 | updateFrustum(); |
---|
1067 | const Matrix4& projMatrix = getProjectionMatrix(); |
---|
1068 | Real r = sphere.getRadius(); |
---|
1069 | Real rsq = r * r; |
---|
1070 | |
---|
1071 | // early-exit |
---|
1072 | if (eyeSpacePos.squaredLength() <= rsq) |
---|
1073 | return false; |
---|
1074 | |
---|
1075 | Real Lxz = Math::Sqr(eyeSpacePos.x) + Math::Sqr(eyeSpacePos.z); |
---|
1076 | Real Lyz = Math::Sqr(eyeSpacePos.y) + Math::Sqr(eyeSpacePos.z); |
---|
1077 | |
---|
1078 | // Find the tangent planes to the sphere |
---|
1079 | // XZ first |
---|
1080 | // calculate quadratic discriminant: b*b - 4ac |
---|
1081 | // x = Nx |
---|
1082 | // a = Lx^2 + Lz^2 |
---|
1083 | // b = -2rLx |
---|
1084 | // c = r^2 - Lz^2 |
---|
1085 | Real a = Lxz; |
---|
1086 | Real b = -2.0 * r * eyeSpacePos.x; |
---|
1087 | Real c = rsq - Math::Sqr(eyeSpacePos.z); |
---|
1088 | Real D = b*b - 4*a*c; |
---|
1089 | |
---|
1090 | // two roots? |
---|
1091 | if (D > 0) |
---|
1092 | { |
---|
1093 | Real sqrootD = Math::Sqrt(D); |
---|
1094 | // solve the quadratic to get the components of the normal |
---|
1095 | Real Nx0 = (-b + sqrootD) / (2 * a); |
---|
1096 | Real Nx1 = (-b - sqrootD) / (2 * a); |
---|
1097 | |
---|
1098 | // Derive Z from this |
---|
1099 | Real Nz0 = (r - Nx0 * eyeSpacePos.x) / eyeSpacePos.z; |
---|
1100 | Real Nz1 = (r - Nx1 * eyeSpacePos.x) / eyeSpacePos.z; |
---|
1101 | |
---|
1102 | // Get the point of tangency |
---|
1103 | // Only consider points of tangency in front of the camera |
---|
1104 | Real Pz0 = (Lxz - rsq) / (eyeSpacePos.z - ((Nz0 / Nx0) * eyeSpacePos.x)); |
---|
1105 | if (Pz0 < 0) |
---|
1106 | { |
---|
1107 | // Project point onto near plane in worldspace |
---|
1108 | Real nearx0 = (Nz0 * mNearDist) / Nx0; |
---|
1109 | // now we need to map this to viewport coords |
---|
1110 | // use projection matrix since that will take into account all factors |
---|
1111 | Vector3 relx0 = projMatrix * Vector3(nearx0, 0, -mNearDist); |
---|
1112 | |
---|
1113 | // find out whether this is a left side or right side |
---|
1114 | Real Px0 = -(Pz0 * Nz0) / Nx0; |
---|
1115 | if (Px0 > eyeSpacePos.x) |
---|
1116 | { |
---|
1117 | *right = std::min(*right, relx0.x); |
---|
1118 | } |
---|
1119 | else |
---|
1120 | { |
---|
1121 | *left = std::max(*left, relx0.x); |
---|
1122 | } |
---|
1123 | } |
---|
1124 | Real Pz1 = (Lxz - rsq) / (eyeSpacePos.z - ((Nz1 / Nx1) * eyeSpacePos.x)); |
---|
1125 | if (Pz1 < 0) |
---|
1126 | { |
---|
1127 | // Project point onto near plane in worldspace |
---|
1128 | Real nearx1 = (Nz1 * mNearDist) / Nx1; |
---|
1129 | // now we need to map this to viewport coords |
---|
1130 | // use projection matrix since that will take into account all factors |
---|
1131 | Vector3 relx1 = projMatrix * Vector3(nearx1, 0, -mNearDist); |
---|
1132 | |
---|
1133 | // find out whether this is a left side or right side |
---|
1134 | Real Px1 = -(Pz1 * Nz1) / Nx1; |
---|
1135 | if (Px1 > eyeSpacePos.x) |
---|
1136 | { |
---|
1137 | *right = std::min(*right, relx1.x); |
---|
1138 | } |
---|
1139 | else |
---|
1140 | { |
---|
1141 | *left = std::max(*left, relx1.x); |
---|
1142 | } |
---|
1143 | } |
---|
1144 | } |
---|
1145 | |
---|
1146 | |
---|
1147 | // Now YZ |
---|
1148 | // calculate quadratic discriminant: b*b - 4ac |
---|
1149 | // x = Ny |
---|
1150 | // a = Ly^2 + Lz^2 |
---|
1151 | // b = -2rLy |
---|
1152 | // c = r^2 - Lz^2 |
---|
1153 | a = Lyz; |
---|
1154 | b = -2.0 * r * eyeSpacePos.y; |
---|
1155 | c = rsq - Math::Sqr(eyeSpacePos.z); |
---|
1156 | D = b*b - 4*a*c; |
---|
1157 | |
---|
1158 | // two roots? |
---|
1159 | if (D > 0) |
---|
1160 | { |
---|
1161 | Real sqrootD = Math::Sqrt(D); |
---|
1162 | // solve the quadratic to get the components of the normal |
---|
1163 | Real Ny0 = (-b + sqrootD) / (2 * a); |
---|
1164 | Real Ny1 = (-b - sqrootD) / (2 * a); |
---|
1165 | |
---|
1166 | // Derive Z from this |
---|
1167 | Real Nz0 = (r - Ny0 * eyeSpacePos.y) / eyeSpacePos.z; |
---|
1168 | Real Nz1 = (r - Ny1 * eyeSpacePos.y) / eyeSpacePos.z; |
---|
1169 | |
---|
1170 | // Get the point of tangency |
---|
1171 | // Only consider points of tangency in front of the camera |
---|
1172 | Real Pz0 = (Lyz - rsq) / (eyeSpacePos.z - ((Nz0 / Ny0) * eyeSpacePos.y)); |
---|
1173 | if (Pz0 < 0) |
---|
1174 | { |
---|
1175 | // Project point onto near plane in worldspace |
---|
1176 | Real neary0 = (Nz0 * mNearDist) / Ny0; |
---|
1177 | // now we need to map this to viewport coords |
---|
1178 | // use projection matriy since that will take into account all factors |
---|
1179 | Vector3 rely0 = projMatrix * Vector3(0, neary0, -mNearDist); |
---|
1180 | |
---|
1181 | // find out whether this is a top side or bottom side |
---|
1182 | Real Py0 = -(Pz0 * Nz0) / Ny0; |
---|
1183 | if (Py0 > eyeSpacePos.y) |
---|
1184 | { |
---|
1185 | *top = std::min(*top, rely0.y); |
---|
1186 | } |
---|
1187 | else |
---|
1188 | { |
---|
1189 | *bottom = std::max(*bottom, rely0.y); |
---|
1190 | } |
---|
1191 | } |
---|
1192 | Real Pz1 = (Lyz - rsq) / (eyeSpacePos.z - ((Nz1 / Ny1) * eyeSpacePos.y)); |
---|
1193 | if (Pz1 < 0) |
---|
1194 | { |
---|
1195 | // Project point onto near plane in worldspace |
---|
1196 | Real neary1 = (Nz1 * mNearDist) / Ny1; |
---|
1197 | // now we need to map this to viewport coords |
---|
1198 | // use projection matriy since that will take into account all factors |
---|
1199 | Vector3 rely1 = projMatrix * Vector3(0, neary1, -mNearDist); |
---|
1200 | |
---|
1201 | // find out whether this is a top side or bottom side |
---|
1202 | Real Py1 = -(Pz1 * Nz1) / Ny1; |
---|
1203 | if (Py1 > eyeSpacePos.y) |
---|
1204 | { |
---|
1205 | *top = std::min(*top, rely1.y); |
---|
1206 | } |
---|
1207 | else |
---|
1208 | { |
---|
1209 | *bottom = std::max(*bottom, rely1.y); |
---|
1210 | } |
---|
1211 | } |
---|
1212 | } |
---|
1213 | } |
---|
1214 | |
---|
1215 | return (*left != -1.0f) || (*top != 1.0f) || (*right != 1.0f) || (*bottom != -1.0f); |
---|
1216 | |
---|
1217 | } |
---|
1218 | //--------------------------------------------------------------------- |
---|
1219 | void Frustum::enableCustomNearClipPlane(const MovablePlane* plane) |
---|
1220 | { |
---|
1221 | mObliqueDepthProjection = true; |
---|
1222 | mLinkedObliqueProjPlane = plane; |
---|
1223 | mObliqueProjPlane = plane->_getDerivedPlane(); |
---|
1224 | invalidateFrustum(); |
---|
1225 | } |
---|
1226 | //--------------------------------------------------------------------- |
---|
1227 | void Frustum::enableCustomNearClipPlane(const Plane& plane) |
---|
1228 | { |
---|
1229 | mObliqueDepthProjection = true; |
---|
1230 | mLinkedObliqueProjPlane = 0; |
---|
1231 | mObliqueProjPlane = plane; |
---|
1232 | invalidateFrustum(); |
---|
1233 | } |
---|
1234 | //--------------------------------------------------------------------- |
---|
1235 | void Frustum::disableCustomNearClipPlane(void) |
---|
1236 | { |
---|
1237 | mObliqueDepthProjection = false; |
---|
1238 | mLinkedObliqueProjPlane = 0; |
---|
1239 | invalidateFrustum(); |
---|
1240 | } |
---|
1241 | //--------------------------------------------------------------------- |
---|
1242 | void Frustum::setCustomViewMatrix(bool enable, const Matrix4& viewMatrix) |
---|
1243 | { |
---|
1244 | mCustomViewMatrix = enable; |
---|
1245 | if (enable) |
---|
1246 | { |
---|
1247 | assert(viewMatrix.isAffine()); |
---|
1248 | mViewMatrix = viewMatrix; |
---|
1249 | } |
---|
1250 | invalidateView(); |
---|
1251 | } |
---|
1252 | //--------------------------------------------------------------------- |
---|
1253 | void Frustum::setCustomProjectionMatrix(bool enable, const Matrix4& projMatrix) |
---|
1254 | { |
---|
1255 | mCustomProjMatrix = enable; |
---|
1256 | if (enable) |
---|
1257 | { |
---|
1258 | mProjMatrix = projMatrix; |
---|
1259 | } |
---|
1260 | invalidateFrustum(); |
---|
1261 | } |
---|
1262 | |
---|
1263 | |
---|
1264 | |
---|
1265 | } // namespace Ogre |
---|