diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 08ffa436dc..21e6575931 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -57,6 +57,7 @@ #include "Util.h" #include "devices/LeapManager.h" #include "devices/OculusManager.h" +#include "devices/TV3DManager.h" #include "renderer/ProgramObject.h" #include "ui/TextRenderer.h" #include "InfoView.h" @@ -439,7 +440,10 @@ void Application::paintGL() { if (OculusManager::isConnected()) { OculusManager::display(whichCamera); - + } else if (TV3DManager::isConnected()) { + _glowEffect.prepare(); + TV3DManager::display(whichCamera); + _glowEffect.render(); } else { _glowEffect.prepare(); @@ -474,8 +478,13 @@ void Application::paintGL() { _mirrorCamera.update(1.0f/_fps); // set the bounds of rear mirror view - glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), _mirrorViewRect.width(), _mirrorViewRect.height()); - glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), _mirrorViewRect.width(), _mirrorViewRect.height()); + float mirrorX = _mirrorViewRect.x(); + float mirrorY = _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(); + float mirrorW = _mirrorViewRect.width(); + float mirrorH = _mirrorViewRect.height(); + + glViewport(mirrorX, mirrorY, mirrorW, mirrorH); + glScissor(mirrorX, mirrorY, mirrorW, mirrorH); bool updateViewFrustum = false; updateProjectionMatrix(_mirrorCamera, updateViewFrustum); glEnable(GL_SCISSOR_TEST); @@ -501,14 +510,18 @@ void Application::paintGL() { _myAvatar.getSkeletonModel().setTranslation(_myAvatar.getHead().getFaceModel().getTranslation() - neckPosition); - displaySide(_mirrorCamera, true); + //displaySide(_mirrorCamera, true); + displaySide(whichCamera); + // restore absolute translations _myAvatar.getSkeletonModel().setTranslation(absoluteSkeletonTranslation); _myAvatar.getHead().getFaceModel().setTranslation(absoluteFaceTranslation); } else { - displaySide(_mirrorCamera, true); + //displaySide(_mirrorCamera, true); + displaySide(whichCamera); + } glPopMatrix(); @@ -531,7 +544,8 @@ void Application::paintGL() { void Application::resetCamerasOnResizeGL(Camera& camera, int width, int height) { if (OculusManager::isConnected()) { OculusManager::configureCamera(camera, width, height); - + } else if (TV3DManager::isConnected()) { + TV3DManager::configureCamera(camera, width, height); } else { camera.setAspectRatio((float)width / height); camera.setFieldOfView(Menu::getInstance()->getFieldOfView()); @@ -910,7 +924,9 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_J: if (isShifted) { _viewFrustum.setFocalLength(_viewFrustum.getFocalLength() - 0.1f); - + if (TV3DManager::isConnected()) { + TV3DManager::configureCamera(_myCamera, _glWidget->width(),_glWidget->height()); + } } else { _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(-0.001, 0, 0)); } @@ -920,6 +936,9 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_M: if (isShifted) { _viewFrustum.setFocalLength(_viewFrustum.getFocalLength() + 0.1f); + if (TV3DManager::isConnected()) { + TV3DManager::configureCamera(_myCamera, _glWidget->width(),_glWidget->height()); + } } else { _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0.001, 0, 0)); @@ -1828,6 +1847,13 @@ void Application::init() { "trigger", Qt::QueuedConnection); } + + TV3DManager::connect(); + if (TV3DManager::isConnected()) { + QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), + "trigger", + Qt::QueuedConnection); + } LeapManager::initialize(); @@ -2415,7 +2441,7 @@ void Application::updateCamera(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateCamera()"); - if (!OculusManager::isConnected()) { + if (!OculusManager::isConnected() && !TV3DManager::isConnected()) { if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); @@ -3800,7 +3826,7 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { return; } PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... Avatars..."); + "Application::renderAvatars()"); if (!selfAvatarOnly) { // Render avatars of other nodes @@ -4089,7 +4115,7 @@ void Application::resetSensors() { if (OculusManager::isConnected()) { OculusManager::reset(); } - + QCursor::setPos(_headMouseX, _headMouseY); _myAvatar.reset(); _myTransmitter.resetLevels(); diff --git a/interface/src/Application.h b/interface/src/Application.h index e917f6d63d..2c813d1068 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -255,11 +255,13 @@ private slots: void resetSensors(); -private: - void resetCamerasOnResizeGL(Camera& camera, int width, int height); +public: void updateProjectionMatrix(); void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true); +private: + void resetCamerasOnResizeGL(Camera& camera, int width, int height); + static bool sendVoxelsOperation(OctreeElement* node, void* extraData); static void processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes); static void processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 98b807fea2..286959d413 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -232,6 +232,8 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, false); + QMenu* avatarSizeMenu = viewMenu->addMenu("Avatar Size"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 73bb0472b4..f5b41d904e 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -178,6 +178,7 @@ namespace MenuOption { const QString FilterSixense = "Smooth Sixense Movement"; const QString DontRenderVoxels = "Don't call _voxels.render()"; const QString DontCallOpenGLForVoxels = "Don't call glDrawRangeElementsEXT() for Voxels"; + const QString Enable3DTVMode = "Enable 3DTV Mode"; const QString EnableOcclusionCulling = "Enable Occlusion Culling"; const QString EnableVoxelPacketCompression = "Enable Voxel Packet Compression"; const QString EchoServerAudio = "Echo Server Audio"; diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp new file mode 100644 index 0000000000..eb4e8ad4c0 --- /dev/null +++ b/interface/src/devices/TV3DManager.cpp @@ -0,0 +1,124 @@ +// +// TV3DManager.cpp +// hifi +// +// Created by Stephen Birarda on 5/9/13. +// Copyright (c) 2012 High Fidelity, Inc. All rights reserved. +// + +#include + +#include + +#include "Application.h" +#include "InterfaceConfig.h" +#include "TV3DManager.h" +#include "Menu.h" + +int TV3DManager::_screenWidth = 1; +int TV3DManager::_screenHeight = 1; +double TV3DManager::_aspect = 1.0; +eyeFrustum TV3DManager::_leftEye; +eyeFrustum TV3DManager::_rightEye; + + +bool TV3DManager::isConnected() { + return Menu::getInstance()->isOptionChecked(MenuOption::Enable3DTVMode); +} + +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 = 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); //sets top of frustum based on fovy and near clipping plane + double right = _aspect * top; // sets right of frustum based on aspect ratio + double frustumshift = (IOD / 2) * nearZ / screenZ; + + _leftEye.top = top; + _leftEye.bottom = -top; + _leftEye.left = -right + frustumshift; + _leftEye.right = right + frustumshift; + _leftEye.modelTranslation = IOD / 2; + + _rightEye.top = top; + _rightEye.bottom = -top; + _rightEye.left = -right - frustumshift; + _rightEye.right = right - frustumshift; + _rightEye.modelTranslation = -IOD / 2; +} + +void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int screenHeight) { + if (screenHeight == 0) { + screenHeight = 1; // prevent divide by 0 + } + _screenWidth = screenWidth; + _screenHeight = screenHeight; + _aspect= (double)_screenWidth / (double)_screenHeight; + setFrustum(whichCamera); + + glViewport (0, 0, _screenWidth, _screenHeight); // sets drawing viewport + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void TV3DManager::display(Camera& whichCamera) { + double nearZ = whichCamera.getNearClip(); // near clipping plane + double farZ = whichCamera.getFarClip(); // far clipping plane + + // left eye portal + int portalX = 0; + int portalY = 0; + int portalW = Application::getInstance()->getGLWidget()->width() / 2; + int portalH = Application::getInstance()->getGLWidget()->height(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glEnable(GL_SCISSOR_TEST); + // render left side view + glViewport(portalX, portalY, portalW, portalH); + glScissor(portalX, portalY, portalW, portalH); + + glPushMatrix(); + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); // reset projection matrix + glFrustum(_leftEye.left, _leftEye.right, _leftEye.bottom, _leftEye.top, nearZ, farZ); // set left view frustum + glTranslatef(_leftEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + Application::getInstance()->displaySide(whichCamera); + } + glPopMatrix(); + glDisable(GL_SCISSOR_TEST); + + // render right side view + portalX = Application::getInstance()->getGLWidget()->width() / 2; + glEnable(GL_SCISSOR_TEST); + // render left side view + glViewport(portalX, portalY, portalW, portalH); + glScissor(portalX, portalY, portalW, portalH); + glPushMatrix(); + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); // reset projection matrix + glFrustum(_rightEye.left, _rightEye.right, _rightEye.bottom, _rightEye.top, nearZ, farZ); // set left view frustum + glTranslatef(_rightEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + Application::getInstance()->displaySide(whichCamera); + } + glPopMatrix(); + glDisable(GL_SCISSOR_TEST); + + // reset the viewport to how we started + glViewport(0, 0, Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height()); +} \ No newline at end of file diff --git a/interface/src/devices/TV3DManager.h b/interface/src/devices/TV3DManager.h new file mode 100644 index 0000000000..edea489745 --- /dev/null +++ b/interface/src/devices/TV3DManager.h @@ -0,0 +1,41 @@ +// +// TV3DManager.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/24/2013 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__TV3DManager__ +#define __hifi__TV3DManager__ + +#include + +class Camera; + +struct eyeFrustum { + double left; + double right; + double bottom; + double top; + float modelTranslation; +}; + + +/// Handles interaction with 3D TVs +class TV3DManager { +public: + static void connect(); + static bool isConnected(); + static void configureCamera(Camera& camera, int screenWidth, int screenHeight); + static void display(Camera& whichCamera); +private: + static void setFrustum(Camera& whichCamera); + static int _screenWidth; + static int _screenHeight; + static double _aspect; + static eyeFrustum _leftEye; + static eyeFrustum _rightEye; +}; + +#endif /* defined(__hifi__TV3DManager__) */