diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index 856b6a1c00..30b9ef205b 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -258,11 +258,11 @@ bool OctreeQueryNode::updateCurrentViewFrustum() { float originalFOV = getCameraFov(); float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND; - newestViewFrustum.setProjection(glm::perspective( - glm::radians(wideFOV), // hack - getCameraAspectRatio(), - getCameraNearClip(), - getCameraFarClip())); + newestViewFrustum.setFieldOfView(wideFOV); // hack + newestViewFrustum.setAspectRatio(getCameraAspectRatio()); + newestViewFrustum.setNearClip(getCameraNearClip()); + newestViewFrustum.setFarClip(getCameraFarClip()); + newestViewFrustum.setEyeOffsetPosition(getCameraEyeOffsetPosition()); // if there has been a change, then recalculate if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 98ccb4911d..01e094e786 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -19,7 +19,6 @@ #include #include #include -#include // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" @@ -966,11 +965,12 @@ void Application::showEditEntitiesHelp() { void Application::resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size) { if (OculusManager::isConnected()) { - OculusManager::configureCamera(camera); + OculusManager::configureCamera(camera, size.x, size.y); } else if (TV3DManager::isConnected()) { TV3DManager::configureCamera(camera, size.x, size.y); } else { - camera.setProjection(glm::perspective(glm::radians(_fieldOfView.get()), (float)size.x / size.y, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); + camera.setAspectRatio((float)size.x / size.y); + camera.setFieldOfView(_fieldOfView.get()); } } @@ -984,7 +984,7 @@ void Application::resizeGL() { renderSize = _glWidget->getDeviceSize() * getRenderResolutionScale(); } if (_renderResolution == toGlm(renderSize)) { - return; + return; } _renderResolution = toGlm(renderSize); @@ -1011,15 +1011,25 @@ void Application::updateProjectionMatrix() { } void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum) { - _projectionMatrix = camera.getProjection(); - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(glm::value_ptr(_projectionMatrix)); + glLoadIdentity(); + + float left, right, bottom, top, nearVal, farVal; + glm::vec4 nearClipPlane, farClipPlane; // Tell our viewFrustum about this change, using the application camera if (updateViewFrustum) { loadViewFrustum(camera, _viewFrustum); - } + _viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + } else { + ViewFrustum tempViewFrustum; + loadViewFrustum(camera, tempViewFrustum); + tempViewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + } + glFrustum(left, right, bottom, top, nearVal, farVal); + + // save matrix + glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&_projectionMatrix); glMatrixMode(GL_MODELVIEW); } @@ -1240,7 +1250,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; -#if 0 case Qt::Key_I: if (isShifted) { _myCamera.setEyeOffsetOrientation(glm::normalize( @@ -1305,8 +1314,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } updateProjectionMatrix(); break; -#endif - case Qt::Key_H: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Mirror); @@ -2624,7 +2631,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node _octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio()); _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip()); _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip()); - _octreeQuery.setCameraEyeOffsetPosition(glm::vec3()); + _octreeQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition()); auto lodManager = DependencyManager::get(); _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); @@ -2828,11 +2835,25 @@ QRect Application::getDesirableApplicationGeometry() { // void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { // We will use these below, from either the camera or head vectors calculated above - viewFrustum.setProjection(camera.getProjection()); + glm::vec3 position(camera.getPosition()); + float fov = camera.getFieldOfView(); // degrees + float nearClip = camera.getNearClip(); + float farClip = camera.getFarClip(); + float aspectRatio = camera.getAspectRatio(); + + glm::quat rotation = camera.getRotation(); // Set the viewFrustum up with the correct position and orientation of the camera - viewFrustum.setPosition(camera.getPosition()); - viewFrustum.setOrientation(camera.getRotation()); + viewFrustum.setPosition(position); + viewFrustum.setOrientation(rotation); + + // Also make sure it's got the correct lens details from the camera + viewFrustum.setAspectRatio(aspectRatio); + viewFrustum.setFieldOfView(fov); // degrees + viewFrustum.setNearClip(nearClip); + viewFrustum.setFarClip(farClip); + viewFrustum.setEyeOffsetPosition(camera.getEyeOffsetPosition()); + viewFrustum.setEyeOffsetOrientation(camera.getEyeOffsetOrientation()); // Ask the ViewFrustum class to calculate our corners viewFrustum.calculate(); @@ -2933,7 +2954,13 @@ void Application::updateShadowMap() { glm::vec3 shadowFrustumCenter = rotation * ((minima + maxima) * 0.5f); _shadowViewFrustum.setPosition(shadowFrustumCenter); _shadowViewFrustum.setOrientation(rotation); - _shadowViewFrustum.setProjection(glm::ortho(minima.x, maxima.x, minima.y, maxima.y, minima.z, maxima.z)); + _shadowViewFrustum.setOrthographic(true); + _shadowViewFrustum.setWidth(maxima.x - minima.x); + _shadowViewFrustum.setHeight(maxima.y - minima.y); + _shadowViewFrustum.setNearClip(minima.z); + _shadowViewFrustum.setFarClip(maxima.z); + _shadowViewFrustum.setEyeOffsetPosition(glm::vec3()); + _shadowViewFrustum.setEyeOffsetOrientation(glm::quat()); _shadowViewFrustum.calculate(); glMatrixMode(GL_PROJECTION); @@ -3120,6 +3147,12 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs glFrontFace(GL_CCW); } + glm::vec3 eyeOffsetPos = theCamera.getEyeOffsetPosition(); + glm::quat eyeOffsetOrient = theCamera.getEyeOffsetOrientation(); + glm::vec3 eyeOffsetAxis = glm::axis(eyeOffsetOrient); + glRotatef(-glm::degrees(glm::angle(eyeOffsetOrient)), eyeOffsetAxis.x, eyeOffsetAxis.y, eyeOffsetAxis.z); + glTranslatef(-eyeOffsetPos.x, -eyeOffsetPos.y, -eyeOffsetPos.z); + // transform view according to theCamera // could be myCamera (if in normal mode) // or could be viewFrustumOffsetCamera if in offset mode @@ -3137,6 +3170,8 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs Transform viewTransform; viewTransform.setTranslation(theCamera.getPosition()); viewTransform.setRotation(rotation); + viewTransform.postTranslate(eyeOffsetPos); + viewTransform.postRotate(eyeOffsetOrient); if (theCamera.getMode() == CAMERA_MODE_MIRROR) { viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); } @@ -3230,7 +3265,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs // finally render the starfield if (hasStars) { - _stars.render(_displayViewFrustum.getFieldOfView(), _displayViewFrustum.getAspectRatio(), _displayViewFrustum.getNearClip(), alpha); + _stars.render(theCamera.getFieldOfView(), theCamera.getAspectRatio(), theCamera.getNearClip(), alpha); } // draw the sky dome @@ -3464,16 +3499,15 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { // Grab current viewport to reset it at the end int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); - float aspect = (float)region.width() / region.height(); - float fov = MIRROR_FIELD_OF_VIEW; // bool eyeRelativeCamera = false; if (billboard) { - fov = BILLBOARD_FIELD_OF_VIEW; // degees + _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees _mirrorCamera.setPosition(_myAvatar->getPosition() + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { + _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees _mirrorCamera.setPosition(_myAvatar->getChestPosition() + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); @@ -3494,10 +3528,12 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further // investigated in order to adapt the technique while fixing the head rendering issue, // but the complexity of the hack suggests that a better approach + _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); } - _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); + _mirrorCamera.setAspectRatio((float)region.width() / region.height()); + _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); // set the bounds of rear mirror view diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index e501b91dea..1016d60ccf 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -48,7 +48,10 @@ QString modeToString(CameraMode mode) { Camera::Camera() : _mode(CAMERA_MODE_THIRD_PERSON), _position(0.0f, 0.0f, 0.0f), - _projection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), 16.0f/9.0f, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)), + _fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES), + _aspectRatio(16.0f/9.0f), + _nearClip(DEFAULT_NEAR_CLIP), // default + _farClip(DEFAULT_FAR_CLIP), // default _hmdPosition(), _hmdRotation(), _isKeepLookingAt(false), @@ -88,13 +91,32 @@ void Camera::setHmdRotation(const glm::quat& hmdRotation) { } } +float Camera::getFarClip() const { + return (_farClip < std::numeric_limits::max()) + ? _farClip + : std::numeric_limits::max() - 1; +} + void Camera::setMode(CameraMode mode) { _mode = mode; emit modeUpdated(modeToString(mode)); } -void Camera::setProjection(const glm::mat4& projection) { - _projection = projection; + +void Camera::setFieldOfView(float f) { + _fieldOfView = f; +} + +void Camera::setAspectRatio(float a) { + _aspectRatio = a; +} + +void Camera::setNearClip(float n) { + _nearClip = n; +} + +void Camera::setFarClip(float f) { + _farClip = f; } PickRay Camera::computePickRay(float x, float y) { diff --git a/interface/src/Camera.h b/interface/src/Camera.h index b9518aa041..e06e12f7dc 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -43,24 +43,36 @@ public: void update( float deltaTime ); - void setPosition(const glm::vec3& position); void setRotation(const glm::quat& rotation); - void setProjection(const glm::mat4 & projection); void setHmdPosition(const glm::vec3& hmdPosition); void setHmdRotation(const glm::quat& hmdRotation); + void setMode(CameraMode m); + void setFieldOfView(float f); + void setAspectRatio(float a); + void setNearClip(float n); + void setFarClip(float f); + void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; } + void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; } glm::quat getRotation() const { return _rotation * _hmdRotation; } - glm::vec3 getPosition() const { return _position + _hmdPosition; } - const glm::mat4& getProjection() const { return _projection; } const glm::vec3& getHmdPosition() const { return _hmdPosition; } const glm::quat& getHmdRotation() const { return _hmdRotation; } + CameraMode getMode() const { return _mode; } - + float getFieldOfView() const { return _fieldOfView; } + float getAspectRatio() const { return _aspectRatio; } + float getNearClip() const { return _nearClip; } + float getFarClip() const; + const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; } + const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; } public slots: QString getModeString() const; void setModeString(const QString& mode); + glm::vec3 getPosition() const { return _position + _hmdPosition; } + void setPosition(const glm::vec3& position); + void setOrientation(const glm::quat& orientation) { setRotation(orientation); } glm::quat getOrientation() const { return getRotation(); } @@ -83,8 +95,13 @@ signals: private: CameraMode _mode; glm::vec3 _position; + float _fieldOfView; // degrees + float _aspectRatio; + float _nearClip; + float _farClip; + glm::vec3 _eyeOffsetPosition; + glm::quat _eyeOffsetOrientation; glm::quat _rotation; - glm::mat4 _projection; glm::vec3 _hmdPosition; glm::quat _hmdRotation; bool _isKeepLookingAt; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e573110157..fe4485b5db 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -862,7 +862,7 @@ void MyAvatar::updateLookAtTargetAvatar() { glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); - float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f; + float smallestAngleTo = glm::radians(Application::getInstance()->getCamera()->getFieldOfView()) / 2.0f; const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 75b6ca4e45..f4693d3c08 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -184,7 +184,7 @@ void OculusManager::connect() { if (!_camera) { _camera = new Camera; - configureCamera(*_camera); // no need to use screen dimensions; they're ignored + configureCamera(*_camera, 0, 0); // no need to use screen dimensions; they're ignored } #ifdef OVR_CLIENT_DISTORTION if (!_programInitialized) { @@ -449,19 +449,9 @@ void OculusManager::endFrameTiming() { } //Sets the camera FoV and aspect ratio -void OculusManager::configureCamera(Camera& camera) { - ovrFovPort fov; - if (_activeEye == ovrEye_Count) { - // When not rendering, provide a FOV encompasing both eyes - fov = _eyeFov[0]; - fov.RightTan = _eyeFov[1].RightTan; - } else { - // When rendering, provide the exact FOV - fov = _eyeFov[_activeEye]; - } - // Convert the FOV to the correct projection matrix - glm::mat4 projection = toGlm(ovrMatrix4f_Projection(fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); - camera.setProjection(projection); +void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) { + camera.setAspectRatio(_renderTargetSize.w * 0.5f / _renderTargetSize.h); + camera.setFieldOfView(atan(_eyeFov[0].UpTan) * DEGREES_PER_RADIAN * 2.0f); } //Displays everything for the oculus, frame timing must be active @@ -554,8 +544,7 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati glm::quat orientation; glm::vec3 trackerPosition; - auto deviceSize = qApp->getDeviceSize(); - + ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); ovrVector3f ovrHeadPosition = ts.HeadPose.ThePose.Position; @@ -593,11 +582,9 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati whichCamera.setHmdPosition(trackerPosition); whichCamera.setHmdRotation(orientation); - // Update our camera to what the application camera is doing _camera->setRotation(whichCamera.getRotation()); _camera->setPosition(whichCamera.getPosition()); - configureCamera(*_camera); // Store the latest left and right eye render locations for things that need to know glm::vec3 thisEyePosition = position + trackerPosition + @@ -608,17 +595,25 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati _camera->update(1.0f / Application::getInstance()->getFps()); glMatrixMode(GL_PROJECTION); - glLoadMatrixf(glm::value_ptr(_camera->getProjection())); - - glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + const ovrFovPort& port = _eyeFov[_activeEye]; + float nearClip = whichCamera.getNearClip(), farClip = whichCamera.getFarClip(); + glFrustum(-nearClip * port.LeftTan, nearClip * port.RightTan, -nearClip * port.DownTan, + nearClip * port.UpTan, nearClip, farClip); ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport; vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale; vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale; - + glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // HACK: instead of passing the stereo eye offset directly in the matrix, pass it in the camera offset + //glTranslatef(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z); + + _camera->setEyeOffsetPosition(glm::vec3(-_eyeRenderDesc[eye].HmdToEyeViewOffset.x, -_eyeRenderDesc[eye].HmdToEyeViewOffset.y, -_eyeRenderDesc[eye].HmdToEyeViewOffset.z)); Application::getInstance()->displaySide(*_camera, false, RenderArgs::MONO); applicationOverlay.displayOverlayTextureHmd(*_camera); @@ -642,6 +637,7 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati glPopMatrix(); // restore our normal viewport + auto deviceSize = qApp->getDeviceSize(); glViewport(0, 0, deviceSize.width(), deviceSize.height()); #if 0 @@ -655,22 +651,16 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati //Wait till time-warp to reduce latency ovr_WaitTillTime(_hmdFrameTiming.TimewarpPointSeconds); -#ifdef DEBUG_RENDER_WITHOUT_DISTORTION - auto fboSize = finalFbo->getSize(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); - glBlitFramebuffer( - 0, 0, fboSize.x, fboSize.y, - 0, 0, deviceSize.width(), deviceSize.height(), - GL_COLOR_BUFFER_BIT, GL_NEAREST); -#else //Clear the color buffer to ensure that there isnt any residual color //Left over from when OR was not connected. glClear(GL_COLOR_BUFFER_BIT); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0))); + //Renders the distorted mesh onto the screen renderDistortionMesh(eyeRenderPose); + glBindTexture(GL_TEXTURE_2D, 0); -#endif glCanvas->swapBuffers(); #else diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index a6c3bbf4d5..e41c6e8f9b 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -60,7 +60,7 @@ public: static void beginFrameTiming(); static void endFrameTiming(); static bool allowSwap(); - static void configureCamera(Camera& camera); + static void configureCamera(Camera& camera, int screenWidth, int screenHeight); static void display(QGLWidget * glCanvas, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera); static void reset(); diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index d90664131f..5d60bf7e19 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -43,9 +43,9 @@ void TV3DManager::connect() { void TV3DManager::setFrustum(Camera& whichCamera) { const double DTR = 0.0174532925; // degree to radians const double IOD = 0.05; //intraocular distance - double fovy = DEFAULT_FIELD_OF_VIEW_DEGREES; // field of view in y-axis - double nearZ = DEFAULT_NEAR_CLIP; // near clipping plane - double screenZ = 0.25f; // screen projection plane + double fovy = whichCamera.getFieldOfView(); // field of view in y-axis + double nearZ = whichCamera.getNearClip(); // near clipping plane + double screenZ = Application::getInstance()->getViewFrustum()->getFocalLength(); // screen projection plane double top = nearZ * tan(DTR * fovy / 2.0); //sets top of frustum based on fovy and near clipping plane double right = _aspect * top; // sets right of frustum based on aspect ratio @@ -81,8 +81,8 @@ void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int scre } void TV3DManager::display(Camera& whichCamera) { - double nearZ = DEFAULT_NEAR_CLIP; // near clipping plane - double farZ = DEFAULT_FAR_CLIP; // far clipping plane + double nearZ = whichCamera.getNearClip(); // near clipping plane + double farZ = whichCamera.getFarClip(); // far clipping plane // left eye portal int portalX = 0; @@ -125,6 +125,7 @@ void TV3DManager::display(Camera& whichCamera) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0)); Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO); applicationOverlay.displayOverlayTextureStereo(whichCamera, _aspect, fov); @@ -153,6 +154,7 @@ void TV3DManager::display(Camera& whichCamera) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0)); Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO); applicationOverlay.displayOverlayTextureStereo(whichCamera, _aspect, fov); diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index 90840854ad..0da694833a 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -20,7 +20,10 @@ OctreeHeadlessViewer::OctreeHeadlessViewer() : _boundaryLevelAdjust(0), _maxPacketsPerSecond(DEFAULT_MAX_OCTREE_PPS) { - _viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); + _viewFrustum.setFieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES); + _viewFrustum.setAspectRatio(DEFAULT_ASPECT_RATIO); + _viewFrustum.setNearClip(DEFAULT_NEAR_CLIP); + _viewFrustum.setFarClip(DEFAULT_FAR_CLIP); } OctreeHeadlessViewer::~OctreeHeadlessViewer() { @@ -64,8 +67,7 @@ void OctreeHeadlessViewer::queryOctree() { _octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio()); _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip()); _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip()); - _octreeQuery.setCameraEyeOffsetPosition(glm::vec3()); - + _octreeQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition()); _octreeQuery.setOctreeSizeScale(getVoxelSizeScale()); _octreeQuery.setBoundaryLevelAdjust(getBoundaryLevelAdjust()); diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index 2694314b57..11ed24799d 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -14,7 +14,7 @@ #include #include #include -#include + #include #include @@ -37,36 +37,6 @@ void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) { _direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FRONT, 0.0f)); } -// Order cooresponds to the order defined in the BoxVertex enum. -static const glm::vec4 NDC_VALUES[8] = { - glm::vec4(-1, -1, -1, 1), - glm::vec4(1, -1, -1, 1), - glm::vec4(1, 1, -1, 1), - glm::vec4(-1, 1, -1, 1), - glm::vec4(-1, -1, 1, 1), - glm::vec4(1, -1, 1, 1), - glm::vec4(1, 1, 1, 1), - glm::vec4(-1, 1, 1, 1), -}; - -void ViewFrustum::setProjection(const glm::mat4& projection) { - _projection = projection; - _inverseProjection = glm::inverse(projection); - - // compute our dimensions the usual way - for (int i = 0; i < 8; ++i) { - _corners[i] = _inverseProjection * NDC_VALUES[i]; - _corners[i] /= _corners[i].w; - } - _nearClip = -_corners[BOTTOM_LEFT_NEAR].z; - _farClip = -_corners[BOTTOM_LEFT_FAR].z; - _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / - (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); - glm::vec3 right = glm::normalize(glm::vec3(_corners[TOP_RIGHT_NEAR])); - glm::vec3 left = glm::normalize(glm::vec3(_corners[TOP_LEFT_NEAR])); - _fieldOfView = abs(glm::degrees(glm::angle(right, left))); -} - // ViewFrustum::calculateViewFrustum() // // Description: this will calculate the view frustum bounds for a given position and direction @@ -75,16 +45,48 @@ void ViewFrustum::setProjection(const glm::mat4& projection) { // http://www.lighthouse3d.com/tutorials/view-frustum-culling/view-frustums-shape/ // void ViewFrustum::calculate() { + if (_orthographic) { + calculateOrthographic(); + return; + } + + // compute the off-axis frustum parameters as we would for glFrustum + float left, right, bottom, top, nearVal, farVal; + glm::vec4 nearClipPlane, farClipPlane; + computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + + // start with the corners of the near frustum window + glm::vec3 topLeft(left, top, -nearVal); + glm::vec3 topRight(right, top, -nearVal); + glm::vec3 bottomLeft(left, bottom, -nearVal); + glm::vec3 bottomRight(right, bottom, -nearVal); // find the intersections of the rays through the corners with the clip planes in view space, // then transform them to world space - glm::mat4 worldMatrix = glm::translate(_position) * glm::mat4(glm::mat3(_right, _up, -_direction)); - glm::vec4 v; - for (int i = 0; i < 8; ++i) { - v = worldMatrix * _corners[i]; - v /= v.w; - _cornersWorld[i] = glm::vec3(v); - } + glm::mat4 worldMatrix = glm::translate(_position) * glm::mat4(glm::mat3(_right, _up, -_direction)) * + glm::translate(_eyeOffsetPosition) * glm::mat4_cast(_eyeOffsetOrientation); + _farTopLeft = glm::vec3(worldMatrix * glm::vec4(topLeft * + (-farClipPlane.w / glm::dot(topLeft, glm::vec3(farClipPlane))), 1.0f)); + _farTopRight = glm::vec3(worldMatrix * glm::vec4(topRight * + (-farClipPlane.w / glm::dot(topRight, glm::vec3(farClipPlane))), 1.0f)); + _farBottomLeft = glm::vec3(worldMatrix * glm::vec4(bottomLeft * + (-farClipPlane.w / glm::dot(bottomLeft, glm::vec3(farClipPlane))), 1.0f)); + _farBottomRight = glm::vec3(worldMatrix * glm::vec4(bottomRight * + (-farClipPlane.w / glm::dot(bottomRight, glm::vec3(farClipPlane))), 1.0f)); + _nearTopLeft = glm::vec3(worldMatrix * glm::vec4(topLeft * + (-nearClipPlane.w / glm::dot(topLeft, glm::vec3(nearClipPlane))), 1.0f)); + _nearTopRight = glm::vec3(worldMatrix * glm::vec4(topRight * + (-nearClipPlane.w / glm::dot(topRight, glm::vec3(nearClipPlane))), 1.0f)); + _nearBottomLeft = glm::vec3(worldMatrix * glm::vec4(bottomLeft * + (-nearClipPlane.w / glm::dot(bottomLeft, glm::vec3(nearClipPlane))), 1.0f)); + _nearBottomRight = glm::vec3(worldMatrix * glm::vec4(bottomRight * + (-nearClipPlane.w / glm::dot(bottomRight, glm::vec3(nearClipPlane))), 1.0f)); + + // compute the offset position and axes in world space + _offsetPosition = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + _offsetDirection = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f)); + _offsetUp = glm::vec3(worldMatrix * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)); + _offsetRight = glm::vec3(worldMatrix * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); // compute the six planes // The planes are defined such that the normal points towards the inside of the view frustum. @@ -97,26 +99,73 @@ void ViewFrustum::calculate() { // the function set3Points assumes that the points are given in counter clockwise order, assume you // are inside the frustum, facing the plane. Start with any point, and go counter clockwise for // three consecutive points - _planes[TOP_PLANE].set3Points(_cornersWorld[TOP_RIGHT_NEAR], _cornersWorld[TOP_LEFT_NEAR], _cornersWorld[TOP_LEFT_FAR]); - _planes[BOTTOM_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_RIGHT_FAR]); - _planes[LEFT_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[TOP_LEFT_FAR]); - _planes[RIGHT_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[TOP_RIGHT_FAR]); - _planes[NEAR_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[TOP_LEFT_NEAR]); - _planes[FAR_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[TOP_RIGHT_FAR]); + + _planes[TOP_PLANE ].set3Points(_nearTopRight,_nearTopLeft,_farTopLeft); + _planes[BOTTOM_PLANE].set3Points(_nearBottomLeft,_nearBottomRight,_farBottomRight); + _planes[LEFT_PLANE ].set3Points(_nearBottomLeft,_farBottomLeft,_farTopLeft); + _planes[RIGHT_PLANE ].set3Points(_farBottomRight,_nearBottomRight,_nearTopRight); + _planes[NEAR_PLANE ].set3Points(_nearBottomRight,_nearBottomLeft,_nearTopLeft); + _planes[FAR_PLANE ].set3Points(_farBottomLeft,_farBottomRight,_farTopRight); // Also calculate our projection matrix in case people want to project points... // Projection matrix : Field of View, ratio, display range : near to far + const float CLIP_NUDGE = 1.0f; + float farClip = (_farClip != _nearClip) ? _farClip : _nearClip + CLIP_NUDGE; // don't allow near and far to be equal + glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, farClip); glm::vec3 lookAt = _position + _direction; glm::mat4 view = glm::lookAt(_position, lookAt, _up); // Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it) - _ourModelViewProjectionMatrix = _projection * view; // Remember, matrix multiplication is the other way around + _ourModelViewProjectionMatrix = projection * view; // Remember, matrix multiplication is the other way around // Set up our keyhole bounding box... glm::vec3 corner = _position - _keyholeRadius; _keyholeBoundingCube = AACube(corner,(_keyholeRadius * 2.0f)); } +void ViewFrustum::calculateOrthographic() { + float halfWidth = _width * 0.5f; + float halfHeight = _height * 0.5f; + + // find the corners of the view box in world space + glm::mat4 worldMatrix = glm::translate(_position) * glm::mat4(glm::mat3(_right, _up, -_direction)) * + glm::translate(_eyeOffsetPosition) * glm::mat4_cast(_eyeOffsetOrientation); + _farTopLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, halfHeight, -_farClip, 1.0f)); + _farTopRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, halfHeight, -_farClip, 1.0f)); + _farBottomLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, -halfHeight, -_farClip, 1.0f)); + _farBottomRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, -halfHeight, -_farClip, 1.0f)); + _nearTopLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, halfHeight, -_nearClip, 1.0f)); + _nearTopRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, halfHeight, -_nearClip, 1.0f)); + _nearBottomLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, -halfHeight, -_nearClip, 1.0f)); + _nearBottomRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, -halfHeight, -_nearClip, 1.0f)); + + // compute the offset position and axes in world space + _offsetPosition = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + _offsetDirection = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f)); + _offsetUp = glm::vec3(worldMatrix * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)); + _offsetRight = glm::vec3(worldMatrix * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); + + _planes[TOP_PLANE].set3Points(_nearTopRight, _nearTopLeft, _farTopLeft); + _planes[BOTTOM_PLANE].set3Points(_nearBottomLeft, _nearBottomRight, _farBottomRight); + _planes[LEFT_PLANE].set3Points(_nearBottomLeft, _farBottomLeft, _farTopLeft); + _planes[RIGHT_PLANE].set3Points(_farBottomRight, _nearBottomRight, _nearTopRight); + _planes[NEAR_PLANE].set3Points(_nearBottomRight, _nearBottomLeft, _nearTopLeft); + _planes[FAR_PLANE].set3Points(_farBottomLeft, _farBottomRight, _farTopRight); + + // Also calculate our projection matrix in case people want to project points... + // Projection matrix : Field of View, ratio, display range : near to far + glm::mat4 projection = glm::ortho(-halfWidth, halfWidth, -halfHeight, halfHeight, _nearClip, _farClip); + glm::vec3 lookAt = _position + _direction; + glm::mat4 view = glm::lookAt(_position, lookAt, _up); + + // Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it) + _ourModelViewProjectionMatrix = projection * view; // Remember, matrix multiplication is the other way around + + // Set up our keyhole bounding box... + glm::vec3 corner = _position - _keyholeRadius; + _keyholeBoundingCube = AACube(corner, (_keyholeRadius * 2.0f)); +} + //enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE }; const char* ViewFrustum::debugPlaneName (int plane) const { switch (plane) { @@ -256,6 +305,7 @@ ViewFrustum::location ViewFrustum::pointInFrustum(const glm::vec3& point) const return keyholeResult; // escape early will be the value from checking the keyhole } } + return regularResult; } @@ -379,7 +429,9 @@ bool ViewFrustum::matches(const ViewFrustum& compareTo, bool debug) const { testMatches(compareTo._aspectRatio, _aspectRatio) && testMatches(compareTo._nearClip, _nearClip) && testMatches(compareTo._farClip, _farClip) && - testMatches(compareTo._focalLength, _focalLength); + testMatches(compareTo._focalLength, _focalLength) && + testMatches(compareTo._eyeOffsetPosition, _eyeOffsetPosition) && + testMatches(compareTo._eyeOffsetOrientation, _eyeOffsetOrientation); if (!result && debug) { qCDebug(octree, "ViewFrustum::matches()... result=%s", debug::valueOf(result)); @@ -414,6 +466,15 @@ bool ViewFrustum::matches(const ViewFrustum& compareTo, bool debug) const { qCDebug(octree, "%s -- compareTo._focalLength=%f _focalLength=%f", (testMatches(compareTo._focalLength, _focalLength) ? "MATCHES " : "NO MATCH"), compareTo._focalLength, _focalLength); + qCDebug(octree, "%s -- compareTo._eyeOffsetPosition=%f,%f,%f _eyeOffsetPosition=%f,%f,%f", + (testMatches(compareTo._eyeOffsetPosition, _eyeOffsetPosition) ? "MATCHES " : "NO MATCH"), + compareTo._eyeOffsetPosition.x, compareTo._eyeOffsetPosition.y, compareTo._eyeOffsetPosition.z, + _eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z); + qCDebug(octree, "%s -- compareTo._eyeOffsetOrientation=%f,%f,%f,%f _eyeOffsetOrientation=%f,%f,%f,%f", + (testMatches(compareTo._eyeOffsetOrientation, _eyeOffsetOrientation) ? "MATCHES " : "NO MATCH"), + compareTo._eyeOffsetOrientation.x, compareTo._eyeOffsetOrientation.y, + compareTo._eyeOffsetOrientation.z, compareTo._eyeOffsetOrientation.w, + _eyeOffsetOrientation.x, _eyeOffsetOrientation.y, _eyeOffsetOrientation.z, _eyeOffsetOrientation.w); } return result; } @@ -424,6 +485,9 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const const float POSITION_SIMILAR_ENOUGH = 5.0f; // 5 meters float positionDistance = glm::distance(_position, compareTo._position); + const float EYEOFFSET_POSITION_SIMILAR_ENOUGH = 0.15f; // 0.15 meters + float eyeOffsetpositionDistance = glm::distance(_eyeOffsetPosition, compareTo._eyeOffsetPosition); + // Compute the angular distance between the two orientations const float ORIENTATION_SIMILAR_ENOUGH = 10.0f; // 10 degrees in any direction glm::quat dQOrientation = _orientation * glm::inverse(compareTo._orientation); @@ -432,14 +496,23 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const angleOrientation = 0.0f; } + glm::quat dQEyeOffsetOrientation = _eyeOffsetOrientation * glm::inverse(compareTo._eyeOffsetOrientation); + float angleEyeOffsetOrientation = compareTo._eyeOffsetOrientation == _eyeOffsetOrientation + ? 0.0f : glm::degrees(glm::angle(dQEyeOffsetOrientation)); + if (isNaN(angleEyeOffsetOrientation)) { + angleEyeOffsetOrientation = 0.0f; + } + bool result = - testMatches(0, positionDistance, POSITION_SIMILAR_ENOUGH) && - testMatches(0, angleOrientation, ORIENTATION_SIMILAR_ENOUGH) && + testMatches(0, positionDistance, POSITION_SIMILAR_ENOUGH) && + testMatches(0, angleOrientation, ORIENTATION_SIMILAR_ENOUGH) && testMatches(compareTo._fieldOfView, _fieldOfView) && testMatches(compareTo._aspectRatio, _aspectRatio) && testMatches(compareTo._nearClip, _nearClip) && testMatches(compareTo._farClip, _farClip) && - testMatches(compareTo._focalLength, _focalLength); + testMatches(compareTo._focalLength, _focalLength) && + testMatches(0, eyeOffsetpositionDistance, EYEOFFSET_POSITION_SIMILAR_ENOUGH) && + testMatches(0, angleEyeOffsetOrientation, ORIENTATION_SIMILAR_ENOUGH); if (!result && debug) { @@ -456,7 +529,7 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const qCDebug(octree, "%s -- angleOrientation=%f", (testMatches(0, angleOrientation, ORIENTATION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), angleOrientation); - + qCDebug(octree, "%s -- compareTo._fieldOfView=%f _fieldOfView=%f", (testMatches(compareTo._fieldOfView, _fieldOfView) ? "MATCHES " : "NO MATCH"), compareTo._fieldOfView, _fieldOfView); @@ -472,6 +545,19 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const qCDebug(octree, "%s -- compareTo._focalLength=%f _focalLength=%f", (testMatches(compareTo._focalLength, _focalLength) ? "MATCHES " : "NO MATCH"), compareTo._focalLength, _focalLength); + + qCDebug(octree, "%s -- compareTo._eyeOffsetPosition=%f,%f,%f _eyeOffsetPosition=%f,%f,%f", + (testMatches(compareTo._eyeOffsetPosition, _eyeOffsetPosition, POSITION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), + compareTo._eyeOffsetPosition.x, compareTo._eyeOffsetPosition.y, compareTo._eyeOffsetPosition.z, + _eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z); + + qCDebug(octree, "%s -- eyeOffsetpositionDistance=%f", + (testMatches(0,eyeOffsetpositionDistance, EYEOFFSET_POSITION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), + eyeOffsetpositionDistance); + + qCDebug(octree, "%s -- angleEyeOffsetOrientation=%f", + (testMatches(0, angleEyeOffsetOrientation, ORIENTATION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"), + angleEyeOffsetOrientation); } return result; } @@ -484,19 +570,39 @@ PickRay ViewFrustum::computePickRay(float x, float y) { } void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const { - origin = _cornersWorld[TOP_LEFT_NEAR] + x * (_cornersWorld[TOP_RIGHT_NEAR] - _cornersWorld[TOP_LEFT_NEAR]) + - y * (_cornersWorld[BOTTOM_LEFT_NEAR] - _cornersWorld[TOP_LEFT_NEAR]); - direction = glm::normalize(origin - _position); + origin = _nearTopLeft + x * (_nearTopRight - _nearTopLeft) + y * (_nearBottomLeft - _nearTopLeft); + direction = glm::normalize(origin - (_position + _orientation * _eyeOffsetPosition)); } void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearValue, float& farValue, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const { + // compute our dimensions the usual way + float hheight = _nearClip * tanf(_fieldOfView * 0.5f * RADIANS_PER_DEGREE); + float hwidth = _aspectRatio * hheight; + if (isOrthographic()) { + hheight = getHeight(); + hwidth = getWidth(); + } + + // get our frustum corners in view space + glm::mat4 eyeMatrix = glm::mat4_cast(glm::inverse(_eyeOffsetOrientation)) * glm::translate(-_eyeOffsetPosition); + glm::vec4 corners[8]; + float farScale = _farClip / _nearClip; + corners[0] = eyeMatrix * glm::vec4(-hwidth, -hheight, -_nearClip, 1.0f); + corners[1] = eyeMatrix * glm::vec4(hwidth, -hheight, -_nearClip, 1.0f); + corners[2] = eyeMatrix * glm::vec4(hwidth, hheight, -_nearClip, 1.0f); + corners[3] = eyeMatrix * glm::vec4(-hwidth, hheight, -_nearClip, 1.0f); + corners[4] = eyeMatrix * glm::vec4(-hwidth * farScale, -hheight * farScale, -_farClip, 1.0f); + corners[5] = eyeMatrix * glm::vec4(hwidth * farScale, -hheight * farScale, -_farClip, 1.0f); + corners[6] = eyeMatrix * glm::vec4(hwidth * farScale, hheight * farScale, -_farClip, 1.0f); + corners[7] = eyeMatrix * glm::vec4(-hwidth * farScale, hheight * farScale, -_farClip, 1.0f); + // find the minimum and maximum z values, which will be our near and far clip distances nearValue = FLT_MAX; farValue = -FLT_MAX; for (int i = 0; i < 8; i++) { - nearValue = min(nearValue, -_corners[i].z); - farValue = max(farValue, -_corners[i].z); + nearValue = min(nearValue, -corners[i].z); + farValue = max(farValue, -corners[i].z); } // make sure the near clip isn't too small to be valid @@ -504,9 +610,9 @@ void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom nearValue = max(MIN_NEAR, nearValue); // get the near/far normal and use it to find the clip planes - glm::vec4 normal = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); - nearClipPlane = glm::vec4(-normal.x, -normal.y, -normal.z, glm::dot(normal, _corners[0])); - farClipPlane = glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _corners[4])); + glm::vec4 normal = eyeMatrix * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); + nearClipPlane = glm::vec4(-normal.x, -normal.y, -normal.z, glm::dot(normal, corners[0])); + farClipPlane = glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, corners[4])); // compute the focal proportion (zero is near clip, one is far clip) float focalProportion = (_focalLength - _nearClip) / (_farClip - _nearClip); @@ -517,7 +623,7 @@ void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom bottom = FLT_MAX; top = -FLT_MAX; for (int i = 0; i < 4; i++) { - glm::vec4 corner = glm::mix(_corners[i], _corners[i + 4], focalProportion); + glm::vec4 corner = glm::mix(corners[i], corners[i + 4], focalProportion); glm::vec4 intersection = corner * (-nearValue / corner.z); left = min(left, intersection.x); right = max(right, intersection.x); @@ -538,6 +644,9 @@ void ViewFrustum::printDebugDetails() const { qCDebug(octree, "_nearClip=%f", _nearClip); qCDebug(octree, "_farClip=%f", _farClip); qCDebug(octree, "_focalLength=%f", _focalLength); + qCDebug(octree, "_eyeOffsetPosition=%f,%f,%f", _eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z ); + qCDebug(octree, "_eyeOffsetOrientation=%f,%f,%f,%f", _eyeOffsetOrientation.x, _eyeOffsetOrientation.y, _eyeOffsetOrientation.z, + _eyeOffsetOrientation.w ); } glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const { @@ -737,7 +846,20 @@ float ViewFrustum::distanceToCamera(const glm::vec3& point) const { } void ViewFrustum::evalProjectionMatrix(glm::mat4& proj) const { - proj = _projection; + if (isOrthographic()) { + glm::vec3 frustumCenter = glm::inverse( _orientation) * _position; + + proj = glm::ortho(frustumCenter.x -0.5f * getWidth(), + frustumCenter.x +0.5f * getWidth(), + frustumCenter.y -0.5f * getHeight(), + frustumCenter.y +0.5f * getHeight(), + -getFarClip(), -getNearClip()); + } else { + float left, right, bottom, top, near, far; + glm::vec4 clip0, clip1; + computeOffAxisFrustum(left, right, bottom, top, near, far, clip0, clip1); + proj = glm::perspective(glm::radians(getFieldOfView()), getAspectRatio(), getNearClip(), getFarClip()); + } } void ViewFrustum::evalViewTransform(Transform& view) const { diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 0422120e51..5b20126293 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -50,11 +50,19 @@ public: const glm::vec3& getRight() const { return _right; } // setters for lens attributes - void setProjection(const glm::mat4 & projection); - void getFocalLength(float focalLength) { _focalLength = focalLength; } + void setOrthographic(bool orthographic) { _orthographic = orthographic; } + void setWidth(float width) { _width = width; } + void setHeight(float height) { _height = height; } + void setFieldOfView(float f) { _fieldOfView = f; } + void setAspectRatio(float a) { _aspectRatio = a; } + void setNearClip(float n) { _nearClip = n; } + void setFarClip(float f) { _farClip = f; } + void setFocalLength(float length) { _focalLength = length; } + void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; } + void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; } // getters for lens attributes - const glm::mat4 getProjection() const { return _projection; }; + bool isOrthographic() const { return _orthographic; } float getWidth() const { return _width; } float getHeight() const { return _height; } float getFieldOfView() const { return _fieldOfView; } @@ -62,16 +70,23 @@ public: float getNearClip() const { return _nearClip; } float getFarClip() const { return _farClip; } float getFocalLength() const { return _focalLength; } + const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; } + const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; } - const glm::vec3& getFarTopLeft() const { return _cornersWorld[TOP_LEFT_FAR]; } - const glm::vec3& getFarTopRight() const { return _cornersWorld[TOP_RIGHT_FAR]; } - const glm::vec3& getFarBottomLeft() const { return _cornersWorld[BOTTOM_LEFT_FAR]; } - const glm::vec3& getFarBottomRight() const { return _cornersWorld[BOTTOM_RIGHT_FAR]; } + const glm::vec3& getOffsetPosition() const { return _offsetPosition; } + const glm::vec3& getOffsetDirection() const { return _offsetDirection; } + const glm::vec3& getOffsetUp() const { return _offsetUp; } + const glm::vec3& getOffsetRight() const { return _offsetRight; } - const glm::vec3& getNearTopLeft() const { return _cornersWorld[TOP_LEFT_NEAR]; } - const glm::vec3& getNearTopRight() const { return _cornersWorld[TOP_RIGHT_NEAR]; } - const glm::vec3& getNearBottomLeft() const { return _cornersWorld[BOTTOM_LEFT_NEAR]; } - const glm::vec3& getNearBottomRight() const { return _cornersWorld[BOTTOM_RIGHT_NEAR]; } + const glm::vec3& getFarTopLeft() const { return _farTopLeft; } + const glm::vec3& getFarTopRight() const { return _farTopRight; } + const glm::vec3& getFarBottomLeft() const { return _farBottomLeft; } + const glm::vec3& getFarBottomRight() const { return _farBottomRight; } + + const glm::vec3& getNearTopLeft() const { return _nearTopLeft; } + const glm::vec3& getNearTopRight() const { return _nearTopRight; } + const glm::vec3& getNearBottomLeft() const { return _nearBottomLeft; } + const glm::vec3& getNearBottomRight() const { return _nearBottomRight; } // get/set for keyhole attribute void setKeyholeRadius(float keyholdRadius) { _keyholeRadius = keyholdRadius; } @@ -117,33 +132,49 @@ private: ViewFrustum::location cubeInKeyhole(const AACube& cube) const; ViewFrustum::location boxInKeyhole(const AABox& box) const; + void calculateOrthographic(); + // camera location/orientation attributes - glm::vec3 _position; // the position in world-frame - glm::quat _orientation; - - // Lens attributes - glm::mat4 _projection; + glm::vec3 _position = glm::vec3(0.0f); // the position in world-frame + glm::quat _orientation = glm::quat(); // calculated for orientation glm::vec3 _direction = IDENTITY_FRONT; glm::vec3 _up = IDENTITY_UP; glm::vec3 _right = IDENTITY_RIGHT; - // keyhole attributes - float _keyholeRadius = DEFAULT_KEYHOLE_RADIUS; - AACube _keyholeBoundingCube; - - // Calculated values - glm::mat4 _inverseProjection; + // Lens attributes + bool _orthographic = false; float _width = 1.0f; float _height = 1.0f; float _aspectRatio = 1.0f; float _nearClip = DEFAULT_NEAR_CLIP; float _farClip = DEFAULT_FAR_CLIP; float _focalLength = 0.25f; + glm::vec3 _eyeOffsetPosition = glm::vec3(0.0f); + glm::quat _eyeOffsetOrientation = glm::quat(); + + // in Degrees, doesn't apply to HMD like Oculus float _fieldOfView = DEFAULT_FIELD_OF_VIEW_DEGREES; - glm::vec4 _corners[8]; - glm::vec3 _cornersWorld[8]; + + // keyhole attributes + float _keyholeRadius = DEFAULT_KEYHOLE_RADIUS; + AACube _keyholeBoundingCube; + + + // Calculated values + glm::vec3 _offsetPosition = glm::vec3(0.0f); + glm::vec3 _offsetDirection = glm::vec3(0.0f); + glm::vec3 _offsetUp = glm::vec3(0.0f); + glm::vec3 _offsetRight = glm::vec3(0.0f); + glm::vec3 _farTopLeft = glm::vec3(0.0f); + glm::vec3 _farTopRight = glm::vec3(0.0f); + glm::vec3 _farBottomLeft = glm::vec3(0.0f); + glm::vec3 _farBottomRight = glm::vec3(0.0f); + glm::vec3 _nearTopLeft = glm::vec3(0.0f); + glm::vec3 _nearTopRight = glm::vec3(0.0f); + glm::vec3 _nearBottomLeft = glm::vec3(0.0f); + glm::vec3 _nearBottomRight = glm::vec3(0.0f); enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE }; ::Plane _planes[6]; // How will this be used?