/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org Copyright (c) 2000-2011 Torus Knot Software Ltd Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------------- */ #include "CmCamera.h" #include "CmMath.h" #include "CmMatrix3.h" #include "CmAxisAlignedBox.h" #include "CmSphere.h" #include "OgreException.h" #include "CmRenderSystem.h" #if CM_PLATFORM == OGRE_PLATFORM_IPHONE #include "macUtils.h" #endif namespace CamelotEngine { //----------------------------------------------------------------------- Camera::Camera( const String& name) : Frustum(name), mOrientation(Quaternion::IDENTITY), mPosition(Vector3::ZERO), mSceneDetail(PM_SOLID), mWindowSet(false), mLastViewport(0), mAutoAspectRatio(false), mCullFrustum(0) { // Reasonable defaults to camera params mFOVy = Radian(Math::PI/4.0f); mNearDist = 100.0f; mFarDist = 100000.0f; mAspect = 1.33333333333333f; mProjType = PT_PERSPECTIVE; setFixedYawAxis(true); // Default to fixed yaw, like freelook since most people expect this invalidateFrustum(); invalidateView(); // Init matrices mViewMatrix = Matrix4::ZERO; mProjMatrixRS = Matrix4::ZERO; } //----------------------------------------------------------------------- Camera::~Camera() { } //----------------------------------------------------------------------- void Camera::setPolygonMode(PolygonMode sd) { mSceneDetail = sd; } //----------------------------------------------------------------------- PolygonMode Camera::getPolygonMode(void) const { return mSceneDetail; } //----------------------------------------------------------------------- void Camera::setPosition(float x, float y, float z) { mPosition.x = x; mPosition.y = y; mPosition.z = z; invalidateView(); } //----------------------------------------------------------------------- void Camera::setPosition(const Vector3& vec) { mPosition = vec; invalidateView(); } //----------------------------------------------------------------------- const Vector3& Camera::getPosition(void) const { return mPosition; } //----------------------------------------------------------------------- void Camera::move(const Vector3& vec) { mPosition = mPosition + vec; invalidateView(); } //----------------------------------------------------------------------- void Camera::moveRelative(const Vector3& vec) { // Transform the axes of the relative vector by camera's local axes Vector3 trans = mOrientation * vec; mPosition = mPosition + trans; invalidateView(); } //----------------------------------------------------------------------- void Camera::setDirection(float x, float y, float z) { setDirection(Vector3(x,y,z)); } //----------------------------------------------------------------------- void Camera::setDirection(const Vector3& vec) { // Do nothing if given a zero vector // (Replaced assert since this could happen with auto tracking camera and // camera passes through the lookAt point) if (vec == Vector3::ZERO) return; // Remember, camera points down -Z of local axes! // Therefore reverse direction of direction vector before determining local Z Vector3 zAdjustVec = -vec; zAdjustVec.normalise(); Quaternion targetWorldOrientation; if( mYawFixed ) { Vector3 xVec = mYawFixedAxis.crossProduct( zAdjustVec ); xVec.normalise(); Vector3 yVec = zAdjustVec.crossProduct( xVec ); yVec.normalise(); targetWorldOrientation.FromAxes( xVec, yVec, zAdjustVec ); } else { // Get axes from current quaternion Vector3 axes[3]; updateView(); mRealOrientation.ToAxes(axes); Quaternion rotQuat; if ( (axes[2]+zAdjustVec).squaredLength() < 0.00005f) { // Oops, a 180 degree turn (infinite possible rotation axes) // Default to yaw i.e. use current UP rotQuat.FromAngleAxis(Radian(Math::PI), axes[1]); } else { // Derive shortest arc to new direction rotQuat = axes[2].getRotationTo(zAdjustVec); } targetWorldOrientation = rotQuat * mRealOrientation; } // transform to parent space // TODO PORT - Can't get orientation from parent until we hook it up properly as a Component // if (mParentNode) // { // mOrientation = // mParentNode->_getDerivedOrientation().Inverse() * targetWorldOrientation; // } //else { mOrientation = targetWorldOrientation; } // TODO If we have a fixed yaw axis, we mustn't break it by using the // shortest arc because this will sometimes cause a relative yaw // which will tip the camera invalidateView(); } //----------------------------------------------------------------------- Vector3 Camera::getDirection(void) const { // Direction points down -Z by default return mOrientation * -Vector3::UNIT_Z; } //----------------------------------------------------------------------- Vector3 Camera::getUp(void) const { return mOrientation * Vector3::UNIT_Y; } //----------------------------------------------------------------------- Vector3 Camera::getRight(void) const { return mOrientation * Vector3::UNIT_X; } //----------------------------------------------------------------------- void Camera::lookAt(const Vector3& targetPoint) { updateView(); this->setDirection(targetPoint - mRealPosition); } //----------------------------------------------------------------------- void Camera::lookAt( float x, float y, float z ) { Vector3 vTemp( x, y, z ); this->lookAt(vTemp); } //----------------------------------------------------------------------- void Camera::roll(const Radian& angle) { // Rotate around local Z axis Vector3 zAxis = mOrientation * Vector3::UNIT_Z; rotate(zAxis, angle); invalidateView(); } //----------------------------------------------------------------------- void Camera::yaw(const Radian& angle) { Vector3 yAxis; if (mYawFixed) { // Rotate around fixed yaw axis yAxis = mYawFixedAxis; } else { // Rotate around local Y axis yAxis = mOrientation * Vector3::UNIT_Y; } rotate(yAxis, angle); invalidateView(); } //----------------------------------------------------------------------- void Camera::pitch(const Radian& angle) { // Rotate around local X axis Vector3 xAxis = mOrientation * Vector3::UNIT_X; rotate(xAxis, angle); invalidateView(); } //----------------------------------------------------------------------- void Camera::rotate(const Vector3& axis, const Radian& angle) { Quaternion q; q.FromAngleAxis(angle,axis); rotate(q); } //----------------------------------------------------------------------- void Camera::rotate(const Quaternion& q) { // Note the order of the mult, i.e. q comes after // Normalise the quat to avoid cumulative problems with precision Quaternion qnorm = q; qnorm.normalise(); mOrientation = qnorm * mOrientation; invalidateView(); } //----------------------------------------------------------------------- bool Camera::isViewOutOfDate(void) const { // Overridden from Frustum to use local orientation / position offsets // Attached to node? // TODO PORT - Can't get orientation/position from parent until we hook it up properly as a Component //if (mParentNode != 0) //{ // if (mRecalcView || // mParentNode->_getDerivedOrientation() != mLastParentOrientation || // mParentNode->_getDerivedPosition() != mLastParentPosition) // { // // Ok, we're out of date with SceneNode we're attached to // mLastParentOrientation = mParentNode->_getDerivedOrientation(); // mLastParentPosition = mParentNode->_getDerivedPosition(); // mRealOrientation = mLastParentOrientation * mOrientation; // mRealPosition = (mLastParentOrientation * mPosition) + mLastParentPosition; // mRecalcView = true; // mRecalcWindow = true; // } //} //else { // Rely on own updates mRealOrientation = mOrientation; mRealPosition = mPosition; } // Deriving reflected orientation / position if (mRecalcView) { mDerivedOrientation = mRealOrientation; mDerivedPosition = mRealPosition; } return mRecalcView; } // ------------------------------------------------------------------- void Camera::invalidateView() const { mRecalcWindow = true; Frustum::invalidateView(); } // ------------------------------------------------------------------- void Camera::invalidateFrustum(void) const { mRecalcWindow = true; Frustum::invalidateFrustum(); } //----------------------------------------------------------------------- void Camera::_renderScene(Viewport *vp, bool includeOverlays) { // TODO PORT - I'm not going to be rendering the scene like this (yet), but I think I will do it eventually //mSceneMgr->_renderScene(this, vp, includeOverlays); } //----------------------------------------------------------------------- void Camera::setFixedYawAxis(bool useFixed, const Vector3& fixedAxis) { mYawFixed = useFixed; mYawFixedAxis = fixedAxis; } //----------------------------------------------------------------------- const Quaternion& Camera::getOrientation(void) const { return mOrientation; } //----------------------------------------------------------------------- void Camera::setOrientation(const Quaternion& q) { mOrientation = q; mOrientation.normalise(); invalidateView(); } //----------------------------------------------------------------------- const Quaternion& Camera::getDerivedOrientation(void) const { updateView(); return mDerivedOrientation; } //----------------------------------------------------------------------- const Vector3& Camera::getDerivedPosition(void) const { updateView(); return mDerivedPosition; } //----------------------------------------------------------------------- Vector3 Camera::getDerivedDirection(void) const { // Direction points down -Z updateView(); return mDerivedOrientation * Vector3::NEGATIVE_UNIT_Z; } //----------------------------------------------------------------------- Vector3 Camera::getDerivedUp(void) const { updateView(); return mDerivedOrientation * Vector3::UNIT_Y; } //----------------------------------------------------------------------- Vector3 Camera::getDerivedRight(void) const { updateView(); return mDerivedOrientation * Vector3::UNIT_X; } //----------------------------------------------------------------------- const Quaternion& Camera::getRealOrientation(void) const { updateView(); return mRealOrientation; } //----------------------------------------------------------------------- const Vector3& Camera::getRealPosition(void) const { updateView(); return mRealPosition; } //----------------------------------------------------------------------- Vector3 Camera::getRealDirection(void) const { // Direction points down -Z updateView(); return mRealOrientation * Vector3::NEGATIVE_UNIT_Z; } //----------------------------------------------------------------------- Vector3 Camera::getRealUp(void) const { updateView(); return mRealOrientation * Vector3::UNIT_Y; } //----------------------------------------------------------------------- Vector3 Camera::getRealRight(void) const { updateView(); return mRealOrientation * Vector3::UNIT_X; } //----------------------------------------------------------------------- Ray Camera::getCameraToViewportRay(float screenX, float screenY) const { Ray ret; getCameraToViewportRay(screenX, screenY, &ret); return ret; } //--------------------------------------------------------------------- void Camera::getCameraToViewportRay(float screenX, float screenY, Ray* outRay) const { Matrix4 inverseVP = (getProjectionMatrix() * getViewMatrix(true)).inverse(); float nx = (2.0f * screenX) - 1.0f; float ny = 1.0f - (2.0f * screenY); Vector3 nearPoint(nx, ny, -1.f); // Use midPoint rather than far point to avoid issues with infinite projection Vector3 midPoint (nx, ny, 0.0f); // Get ray origin and ray target on near plane in world space Vector3 rayOrigin, rayTarget; rayOrigin = inverseVP * nearPoint; rayTarget = inverseVP * midPoint; Vector3 rayDirection = rayTarget - rayOrigin; rayDirection.normalise(); outRay->setOrigin(rayOrigin); outRay->setDirection(rayDirection); } // ------------------------------------------------------------------- void Camera::setWindow (float Left, float Top, float Right, float Bottom) { mWLeft = Left; mWTop = Top; mWRight = Right; mWBottom = Bottom; mWindowSet = true; mRecalcWindow = true; } // ------------------------------------------------------------------- void Camera::resetWindow () { mWindowSet = false; } // ------------------------------------------------------------------- void Camera::setWindowImpl() const { if (!mWindowSet || !mRecalcWindow) return; // Calculate general projection parameters float vpLeft, vpRight, vpBottom, vpTop; calcProjectionParameters(vpLeft, vpRight, vpBottom, vpTop); float vpWidth = vpRight - vpLeft; float vpHeight = vpTop - vpBottom; float wvpLeft = vpLeft + mWLeft * vpWidth; float wvpRight = vpLeft + mWRight * vpWidth; float wvpTop = vpTop - mWTop * vpHeight; float wvpBottom = vpTop - mWBottom * vpHeight; Vector3 vp_ul (wvpLeft, wvpTop, -mNearDist); Vector3 vp_ur (wvpRight, wvpTop, -mNearDist); Vector3 vp_bl (wvpLeft, wvpBottom, -mNearDist); Vector3 vp_br (wvpRight, wvpBottom, -mNearDist); Matrix4 inv = mViewMatrix.inverseAffine(); Vector3 vw_ul = inv.transformAffine(vp_ul); Vector3 vw_ur = inv.transformAffine(vp_ur); Vector3 vw_bl = inv.transformAffine(vp_bl); Vector3 vw_br = inv.transformAffine(vp_br); mWindowClipPlanes.clear(); if (mProjType == PT_PERSPECTIVE) { Vector3 position = getPositionForViewUpdate(); mWindowClipPlanes.push_back(Plane(position, vw_bl, vw_ul)); mWindowClipPlanes.push_back(Plane(position, vw_ul, vw_ur)); mWindowClipPlanes.push_back(Plane(position, vw_ur, vw_br)); mWindowClipPlanes.push_back(Plane(position, vw_br, vw_bl)); } else { Vector3 x_axis(inv[0][0], inv[0][1], inv[0][2]); Vector3 y_axis(inv[1][0], inv[1][1], inv[1][2]); x_axis.normalise(); y_axis.normalise(); mWindowClipPlanes.push_back(Plane( x_axis, vw_bl)); mWindowClipPlanes.push_back(Plane(-x_axis, vw_ur)); mWindowClipPlanes.push_back(Plane( y_axis, vw_bl)); mWindowClipPlanes.push_back(Plane(-y_axis, vw_ur)); } mRecalcWindow = false; } // ------------------------------------------------------------------- const vector::type& Camera::getWindowPlanes(void) const { updateView(); setWindowImpl(); return mWindowClipPlanes; } // ------------------------------------------------------------------- float Camera::getBoundingRadius(void) const { // return a little bigger than the near distance // just to keep things just outside return mNearDist * 1.5f; } //----------------------------------------------------------------------- const Vector3& Camera::getPositionForViewUpdate(void) const { // Note no update, because we're calling this from the update! return mRealPosition; } //----------------------------------------------------------------------- const Quaternion& Camera::getOrientationForViewUpdate(void) const { return mRealOrientation; } //----------------------------------------------------------------------- bool Camera::getAutoAspectRatio(void) const { return mAutoAspectRatio; } //----------------------------------------------------------------------- void Camera::setAutoAspectRatio(bool autoratio) { mAutoAspectRatio = autoratio; } //----------------------------------------------------------------------- bool Camera::isVisible(const AxisAlignedBox& bound, FrustumPlane* culledBy) const { if (mCullFrustum) { return mCullFrustum->isVisible(bound, culledBy); } else { return Frustum::isVisible(bound, culledBy); } } //----------------------------------------------------------------------- bool Camera::isVisible(const Sphere& bound, FrustumPlane* culledBy) const { if (mCullFrustum) { return mCullFrustum->isVisible(bound, culledBy); } else { return Frustum::isVisible(bound, culledBy); } } //----------------------------------------------------------------------- bool Camera::isVisible(const Vector3& vert, FrustumPlane* culledBy) const { if (mCullFrustum) { return mCullFrustum->isVisible(vert, culledBy); } else { return Frustum::isVisible(vert, culledBy); } } //----------------------------------------------------------------------- const Vector3* Camera::getWorldSpaceCorners(void) const { if (mCullFrustum) { return mCullFrustum->getWorldSpaceCorners(); } else { return Frustum::getWorldSpaceCorners(); } } //----------------------------------------------------------------------- const Plane& Camera::getFrustumPlane( unsigned short plane ) const { if (mCullFrustum) { return mCullFrustum->getFrustumPlane(plane); } else { return Frustum::getFrustumPlane(plane); } } //----------------------------------------------------------------------- bool Camera::projectSphere(const Sphere& sphere, float* left, float* top, float* right, float* bottom) const { if (mCullFrustum) { return mCullFrustum->projectSphere(sphere, left, top, right, bottom); } else { return Frustum::projectSphere(sphere, left, top, right, bottom); } } //----------------------------------------------------------------------- float Camera::getNearClipDistance(void) const { if (mCullFrustum) { return mCullFrustum->getNearClipDistance(); } else { return Frustum::getNearClipDistance(); } } //----------------------------------------------------------------------- float Camera::getFarClipDistance(void) const { if (mCullFrustum) { return mCullFrustum->getFarClipDistance(); } else { return Frustum::getFarClipDistance(); } } //----------------------------------------------------------------------- const Matrix4& Camera::getViewMatrix(void) const { if (mCullFrustum) { return mCullFrustum->getViewMatrix(); } else { return Frustum::getViewMatrix(); } } //----------------------------------------------------------------------- const Matrix4& Camera::getViewMatrix(bool ownFrustumOnly) const { if (ownFrustumOnly) { return Frustum::getViewMatrix(); } else { return getViewMatrix(); } } //----------------------------------------------------------------------- //_______________________________________________________ //| | //| getRayForwardIntersect | //| ----------------------------- | //| get the intersections of frustum rays with a plane | //| of interest. The plane is assumed to have constant | //| z. If this is not the case, rays | //| should be rotated beforehand to work in a | //| coordinate system in which this is true. | //|_____________________________________________________| // vector::type Camera::getRayForwardIntersect(const Vector3& anchor, const Vector3 *dir, float planeOffset) const { vector::type res; if(!dir) return res; int infpt[4] = {0, 0, 0, 0}; // 0=finite, 1=infinite, 2=straddles infinity Vector3 vec[4]; // find how much the anchor point must be displaced in the plane's // constant variable float delta = planeOffset - anchor.z; // now set the intersection point and note whether it is a // point at infinity or straddles infinity unsigned int i; for (i=0; i<4; i++) { float test = dir[i].z * delta; if (test == 0.0) { vec[i] = dir[i]; infpt[i] = 1; } else { float lambda = delta / dir[i].z; vec[i] = anchor + (lambda * dir[i]); if(test < 0.0) infpt[i] = 2; } } for (i=0; i<4; i++) { // store the finite intersection points if (infpt[i] == 0) res.push_back(Vector4(vec[i].x, vec[i].y, vec[i].z, 1.0)); else { // handle the infinite points of intersection; // cases split up into the possible frustum planes // pieces which may contain a finite intersection point int nextind = (i+1) % 4; int prevind = (i+3) % 4; if ((infpt[prevind] == 0) || (infpt[nextind] == 0)) { if (infpt[i] == 1) res.push_back(Vector4(vec[i].x, vec[i].y, vec[i].z, 0.0)); else { // handle the intersection points that straddle infinity (back-project) if(infpt[prevind] == 0) { Vector3 temp = vec[prevind] - vec[i]; res.push_back(Vector4(temp.x, temp.y, temp.z, 0.0)); } if(infpt[nextind] == 0) { Vector3 temp = vec[nextind] - vec[i]; res.push_back(Vector4(temp.x, temp.y, temp.z, 0.0)); } } } // end if we need to add an intersection point to the list } // end if infinite point needs to be considered } // end loop over frustun corners // we end up with either 0, 3, 4, or 5 intersection points return res; } //_______________________________________________________ //| | //| forwardIntersect | //| ----------------------------- | //| Forward intersect the camera's frustum rays with | //| a specified plane of interest. | //| Note that if the frustum rays shoot out and would | //| back project onto the plane, this means the forward | //| intersection of the frustum would occur at the | //| line at infinity. | //|_____________________________________________________| // void Camera::forwardIntersect(const Plane& worldPlane, vector::type* intersect3d) const { if(!intersect3d) return; Vector3 trCorner = getWorldSpaceCorners()[0]; Vector3 tlCorner = getWorldSpaceCorners()[1]; Vector3 blCorner = getWorldSpaceCorners()[2]; Vector3 brCorner = getWorldSpaceCorners()[3]; // need some sort of rotation that will bring the plane normal to the z axis Plane pval = worldPlane; if(pval.normal.z < 0.0) { pval.normal *= -1.0; pval.d *= -1.0; } Quaternion invPlaneRot = pval.normal.getRotationTo(Vector3::UNIT_Z); // get rotated light Vector3 lPos = invPlaneRot * getDerivedPosition(); Vector3 vec[4]; vec[0] = invPlaneRot * trCorner - lPos; vec[1] = invPlaneRot * tlCorner - lPos; vec[2] = invPlaneRot * blCorner - lPos; vec[3] = invPlaneRot * brCorner - lPos; // compute intersection points on plane vector::type iPnt = getRayForwardIntersect(lPos, vec, -pval.d); // return wanted data if(intersect3d) { Quaternion planeRot = invPlaneRot.Inverse(); (*intersect3d).clear(); for(unsigned int i=0; isetPosition(cam->getPosition()); this->setProjectionType(cam->getProjectionType()); this->setOrientation(cam->getOrientation()); this->setAspectRatio(cam->getAspectRatio()); this->setNearClipDistance(cam->getNearClipDistance()); this->setFarClipDistance(cam->getFarClipDistance()); this->setFOVy(cam->getFOVy()); this->setFocalLength(cam->getFocalLength()); // Don't do these, they're not base settings and can cause referencing issues //this->setLodCamera(cam->getLodCamera()); //this->setCullingFrustum(cam->getCullingFrustum()); } } // namespace CamelotEngine