//----------------------------------------------------------------------------- const Matrix4& AutoParamDataSource::getProjectionMatrix(void) const { if (mProjMatrixDirty) { // NB use API-independent projection matrix since GPU programs // bypass the API-specific handedness and use right-handed coords if (mCurrentRenderable && mCurrentRenderable->getUseIdentityProjection()) { // Use identity projection matrix, still need to take RS depth into account. RenderSystem* rs = Root::getSingleton().getRenderSystem(); rs->_convertProjectionMatrix(Matrix4::IDENTITY, mProjectionMatrix, true); } else { mProjectionMatrix = mCurrentCamera->getProjectionMatrixWithRSDepth(); } if (mCurrentRenderTarget && mCurrentRenderTarget->requiresTextureFlipping()) { // Because we're not using setProjectionMatrix, this needs to be done here // Invert transformed y mProjectionMatrix[1][0] = -mProjectionMatrix[1][0]; mProjectionMatrix[1][1] = -mProjectionMatrix[1][1]; mProjectionMatrix[1][2] = -mProjectionMatrix[1][2]; mProjectionMatrix[1][3] = -mProjectionMatrix[1][3]; } mProjMatrixDirty = false; } return mProjectionMatrix; }
//----------------------------------------------------------------------- void Frustum::updateFrustumImpl(void) const { // Common calcs Real left, right, bottom, top; calcProjectionParameters(left, right, bottom, top); if (!mCustomProjMatrix) { // The code below will dealing with general projection // parameters, similar glFrustum and glOrtho. // Doesn't optimise manually except division operator, so the // code more self-explaining. Real inv_w = 1 / (right - left); Real inv_h = 1 / (top - bottom); Real inv_d = 1 / (mFarDist - mNearDist); // Recalc if frustum params changed if (mProjType == PT_PERSPECTIVE) { // Calc matrix elements Real A = 2 * mNearDist * inv_w; Real B = 2 * mNearDist * inv_h; Real C = (right + left) * inv_w; Real D = (top + bottom) * inv_h; Real q, qn; if (mFarDist == 0) { // Infinite far plane q = Frustum::INFINITE_FAR_PLANE_ADJUST - 1; qn = mNearDist * (Frustum::INFINITE_FAR_PLANE_ADJUST - 2); } else { q = - (mFarDist + mNearDist) * inv_d; qn = -2 * (mFarDist * mNearDist) * inv_d; } // NB: This creates 'uniform' perspective projection matrix, // which depth range [-1,1], right-handed rules // // [ A 0 C 0 ] // [ 0 B D 0 ] // [ 0 0 q qn ] // [ 0 0 -1 0 ] // // A = 2 * near / (right - left) // B = 2 * near / (top - bottom) // C = (right + left) / (right - left) // D = (top + bottom) / (top - bottom) // q = - (far + near) / (far - near) // qn = - 2 * (far * near) / (far - near) mProjMatrix = Matrix4::ZERO; mProjMatrix[0][0] = A; mProjMatrix[0][2] = C; mProjMatrix[1][1] = B; mProjMatrix[1][2] = D; mProjMatrix[2][2] = q; mProjMatrix[2][3] = qn; mProjMatrix[3][2] = -1; if (mObliqueDepthProjection) { // Translate the plane into view space // Don't use getViewMatrix here, incase overrided by // camera and return a cull frustum view matrix updateView(); Plane plane = mViewMatrix * mObliqueProjPlane; // Thanks to Eric Lenyel for posting this calculation // at www.terathon.com // Calculate the clip-space corner point opposite the // clipping plane // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and // transform it into camera space by multiplying it // by the inverse of the projection matrix /* generalised version Vector4 q = matrix.inverse() * Vector4(Math::Sign(plane.normal.x), Math::Sign(plane.normal.y), 1.0f, 1.0f); */ Vector4 q; q.x = (Math::Sign(plane.normal.x) + mProjMatrix[0][2]) / mProjMatrix[0][0]; q.y = (Math::Sign(plane.normal.y) + mProjMatrix[1][2]) / mProjMatrix[1][1]; q.z = -1; q.w = (1 + mProjMatrix[2][2]) / mProjMatrix[2][3]; // Calculate the scaled plane vector Vector4 clipPlane4d(plane.normal.x, plane.normal.y, plane.normal.z, plane.d); Vector4 c = clipPlane4d * (2 / (clipPlane4d.dotProduct(q))); // Replace the third row of the projection matrix mProjMatrix[2][0] = c.x; mProjMatrix[2][1] = c.y; mProjMatrix[2][2] = c.z + 1; mProjMatrix[2][3] = c.w; } } // perspective else if (mProjType == PT_ORTHOGRAPHIC) { Real A = 2 * inv_w; Real B = 2 * inv_h; Real C = - (right + left) * inv_w; Real D = - (top + bottom) * inv_h; Real q, qn; if (mFarDist == 0) { // Can not do infinite far plane here, avoid divided zero only q = - Frustum::INFINITE_FAR_PLANE_ADJUST / mNearDist; qn = - Frustum::INFINITE_FAR_PLANE_ADJUST - 1; } else { q = - 2 * inv_d; qn = - (mFarDist + mNearDist) * inv_d; } // NB: This creates 'uniform' orthographic projection matrix, // which depth range [-1,1], right-handed rules // // [ A 0 0 C ] // [ 0 B 0 D ] // [ 0 0 q qn ] // [ 0 0 0 1 ] // // A = 2 * / (right - left) // B = 2 * / (top - bottom) // C = - (right + left) / (right - left) // D = - (top + bottom) / (top - bottom) // q = - 2 / (far - near) // qn = - (far + near) / (far - near) mProjMatrix = Matrix4::ZERO; mProjMatrix[0][0] = A; mProjMatrix[0][3] = C; mProjMatrix[1][1] = B; mProjMatrix[1][3] = D; mProjMatrix[2][2] = q; mProjMatrix[2][3] = qn; mProjMatrix[3][3] = 1; } // ortho } // !mCustomProjMatrix RenderSystem* renderSystem = Root::getSingleton().getRenderSystem(); // API specific renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRS); // API specific for Gpu Programs renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRSDepth, true); // Calculate bounding box (local) // Box is from 0, down -Z, max dimensions as determined from far plane // If infinite view frustum just pick a far value Real farDist = (mFarDist == 0) ? 100000 : mFarDist; // Near plane bounds Vector3 min(left, bottom, -farDist); Vector3 max(right, top, 0); if (mCustomProjMatrix) { // Some custom projection matrices can have unusual inverted settings // So make sure the AABB is the right way around to start with Vector3 tmp = min; min.makeFloor(max); max.makeCeil(tmp); } if (mProjType == PT_PERSPECTIVE) { // Merge with far plane bounds Real radio = farDist / mNearDist; min.makeFloor(Vector3(left * radio, bottom * radio, -farDist)); max.makeCeil(Vector3(right * radio, top * radio, 0)); } mBoundingBox.setExtents(min, max); mRecalcFrustum = false; // Signal to update frustum clipping planes mRecalcFrustumPlanes = true; }