//// Application.cpp// interface//// Created by Andrzej Kapolka on 5/10/13.// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. #include <sstream> #include <stdlib.h>#include <cmath> #ifdef _WIN32#include "Syssocket.h"#include "Systime.h"#else#include <sys/time.h>#include <arpa/inet.h>#include <ifaddrs.h>#endif #include <glm/gtx/quaternion.hpp>#include <glm/gtx/vector_angle.hpp> // include this before QGLWidget, which includes an earlier version of OpenGL#include "InterfaceConfig.h" #include <QActionGroup>#include <QColorDialog>#include <QDesktopWidget>#include <QCheckBox>#include <QImage>#include <QKeyEvent>#include <QMainWindow>#include <QMenuBar>#include <QMouseEvent>#include <QNetworkAccessManager>#include <QOpenGLFramebufferObject>#include <QWheelEvent>#include <QSettings>#include <QShortcut>#include <QTimer>#include <QUrl>#include <QtDebug>#include <QFileDialog>#include <QDesktopServices> #include <NodeTypes.h>#include <AudioInjectionManager.h>#include <AudioInjector.h>#include <Logstash.h>#include <OctalCode.h>#include <PacketHeaders.h>#include <PairingHandler.h>#include <PerfStat.h> #include <VoxelSceneStats.h> #include "Application.h"#include "LogDisplay.h"#include "Menu.h"#include "Swatch.h"#include "Util.h"#include "devices/LeapManager.h"#include "devices/OculusManager.h"#include "renderer/ProgramObject.h"#include "ui/TextRenderer.h" using namespace std; // Starfield informationstatic char STAR_FILE[] = "http://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";static char STAR_CACHE_FILE[] = "cachedStars.txt"; static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff // in the idle loop? (60 FPS is default)static QTimer* idleTimer = NULL; const int STARTUP_JITTER_SAMPLES = PACKET_LENGTH_SAMPLES_PER_CHANNEL / 2; // Startup optimistically with small jitter buffer that // will start playback on the second received audio packet. void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); LogDisplay::instance.addMessage(message.toLocal8Bit().constData());} Application::Application(int& argc, char** argv, timeval &startup_time) : QApplication(argc, argv), _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), _displayLevels(false), _frameCount(0), _fps(120.0f), _justStarted(true), _voxelImporter(_window), _wantToKillLocalVoxels(false), _audioScope(256, 200, true), _mouseX(0), _mouseY(0), _touchAvgX(0.0f), _touchAvgY(0.0f), _isTouchPressed(false), _yawFromTouch(0.0f), _pitchFromTouch(0.0f), _mousePressed(false), _isHoverVoxel(false), _isHoverVoxelSounding(false), _mouseVoxelScale(1.0f / 1024.0f), _justEditedVoxel(false), _isLookingAtOtherAvatar(false), _lookatIndicatorScale(1.0f), _perfStatsOn(false), _chatEntryOn(false), _oculusProgram(0), _oculusDistortionScale(1.25),#ifndef _WIN32 _audio(&_audioScope, STARTUP_JITTER_SAMPLES),#endif _stopNetworkReceiveThread(false), _voxelProcessor(), _voxelEditSender(this), _packetCount(0), _packetsPerSecond(0), _bytesPerSecond(0), _bytesCount(0), _swatch(NULL), _pasteMode(false){ _applicationStartupTime = startup_time; _window->setWindowTitle("Interface"); qInstallMessageHandler(messageHandler); unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast<const char**>(argv); const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); if (portStr) { listenPort = atoi(portStr); } NodeList::createInstance(NODE_TYPE_AGENT, listenPort); NodeList::getInstance()->addHook(&_voxels); NodeList::getInstance()->addHook(this); // network receive thread and voxel parsing thread are both controlled by the --nonblocking command line _enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking"); if (!_enableNetworkThread) { NodeList::getInstance()->getNodeSocket()->setBlocking(false); } // setup QSettings #ifdef Q_OS_MAC QString resourcesPath = QCoreApplication::applicationDirPath() + "/../Resources";#else QString resourcesPath = QCoreApplication::applicationDirPath() + "/resources";#endif // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo(resourcesPath + "/info/ApplicationInfo.ini", QSettings::IniFormat); // set the associated application properties applicationInfo.beginGroup("INFO"); setApplicationName(applicationInfo.value("name").toString()); setApplicationVersion(applicationInfo.value("version").toString()); setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); _settings = new QSettings(this); // check if there is a saved domain server hostname // this must be done now instead of with the other setting checks to allow manual override with // --domain or --local options NodeList::getInstance()->loadData(_settings); const char* domainIP = getCmdOption(argc, constArgv, "--domain"); if (domainIP) { NodeList::getInstance()->setDomainIP(domainIP); } // Handle Local Domain testing with the --local command line if (cmdOptionExists(argc, constArgv, "--local")) { qDebug("Local Domain MODE!\n"); NodeList::getInstance()->setDomainIPToLocalhost(); } // Check to see if the user passed in a command line option for loading a local // Voxel File. _voxelsFilename = getCmdOption(argc, constArgv, "-i"); // the callback for our instance of NodeList is attachNewHeadToNode NodeList::getInstance()->linkedDataCreateCallback = &attachNewHeadToNode; #ifdef _WIN32 WSADATA WsaData; int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData); #endif // tell the NodeList instance who to tell the domain server we care about const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER}; NodeList::getInstance()->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); // start the nodeList threads NodeList::getInstance()->startSilentNodeRemovalThread(); _window->setCentralWidget(_glWidget); // call Menu getInstance static method to set up the menu _window->setMenuBar(Menu::getInstance()); _networkAccessManager = new QNetworkAccessManager(this); QRect available = desktop()->availableGeometry(); _window->resize(available.size()); _window->setVisible(true); _glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocus(); // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); // initialization continues in initializeGL when OpenGL context is ready // Tell our voxel edit sender about our known jurisdictions _voxelEditSender.setVoxelServerJurisdictions(&_voxelServerJurisdictions);} Application::~Application() { NodeList::getInstance()->removeHook(&_voxels); NodeList::getInstance()->removeHook(this); _sharedVoxelSystem.changeTree(new VoxelTree); _audio.shutdown(); delete Menu::getInstance(); delete _oculusProgram; delete _settings; delete _networkAccessManager; delete _followMode; delete _glWidget;} void Application::initializeGL() { qDebug( "Created Display Window.\n" ); // initialize glut for shape drawing; Qt apparently initializes it on OS X #ifndef __APPLE__ int argc = 0; glutInit(&argc, 0); #endif // Before we render anything, let's set up our viewFrustumOffsetCamera with a sufficiently large // field of view and near and far clip to make it interesting. //viewFrustumOffsetCamera.setFieldOfView(90.0); _viewFrustumOffsetCamera.setNearClip(0.1); _viewFrustumOffsetCamera.setFarClip(500.0 * TREE_SCALE); initDisplay(); qDebug( "Initialized Display.\n" ); init(); qDebug( "Init() complete.\n" ); // Check to see if the user passed in a command line option for randomizing colors bool wantColorRandomizer = !arguments().contains("--NoColorRandomizer"); // Check to see if the user passed in a command line option for loading a local // Voxel File. If so, load it now. if (!_voxelsFilename.isEmpty()) { _voxels.loadVoxelsFile(_voxelsFilename.constData(), wantColorRandomizer); qDebug("Local Voxel File loaded.\n"); } // create thread for receipt of data via UDP if (_enableNetworkThread) { pthread_create(&_networkReceiveThread, NULL, networkReceive, NULL); qDebug("Network receive thread created.\n"); } // create thread for parsing of voxel data independent of the main network and rendering threads _voxelProcessor.initialize(_enableProcessVoxelsThread); _voxelEditSender.initialize(_enableProcessVoxelsThread); if (_enableProcessVoxelsThread) { qDebug("Voxel parsing thread created.\n"); } // call terminate before exiting connect(this, SIGNAL(aboutToQuit()), SLOT(terminate())); // call our timer function every second QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(timer())); timer->start(1000); // call our idle function whenever we can idleTimer = new QTimer(this); connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); idleTimer->start(0); _idleLoopStdev.reset(); if (_justStarted) { float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime)) / 1000000.0; _justStarted = false; char title[50]; sprintf(title, "Interface: %4.2f seconds\n", startupTime); qDebug("%s", title); _window->setWindowTitle(title); const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time"; // ask the Logstash class to record the startup time Logstash::stashValue(STAT_TYPE_TIMER, LOGSTASH_INTERFACE_START_TIME_KEY, startupTime); } // update before the first render update(0.0f); // now that things are drawn - if this is an OS X release build we can check for an update#if defined(Q_OS_MAC) && defined(QT_NO_DEBUG) Menu::getInstance()->checkForUpdates();#endif} void Application::paintGL() { PerfStat("display"); glEnable(GL_LINE_SMOOTH); if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness (100.0f); _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); _myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); } else if (OculusManager::isConnected()) { _myCamera.setUpShift (0.0f); _myCamera.setDistance (0.0f); _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing _myCamera.setTargetPosition(_myAvatar.getHeadJointPosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { _myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay _myCamera.setTargetPosition(_myAvatar.getUprightEyeLevelPosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); } // Update camera position _myCamera.update( 1.f/_fps ); // Note: whichCamera is used to pick between the normal camera myCamera for our // main camera, vs, an alternate camera. The alternate camera we support right now // is the viewFrustumOffsetCamera. But theoretically, we could use this same mechanism // to add other cameras. // // Why have two cameras? Well, one reason is that because in the case of the renderViewFrustum() // code, we want to keep the state of "myCamera" intact, so we can render what the view frustum of // myCamera is. But we also want to do meaningful camera transforms on OpenGL for the offset camera Camera whichCamera = _myCamera; if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { ViewFrustumOffset viewFrustumOffset = Menu::getInstance()->getViewFrustumOffset(); // set the camera to third-person view but offset so we can see the frustum _viewFrustumOffsetCamera.setTargetPosition(_myCamera.getTargetPosition()); _viewFrustumOffsetCamera.setTargetRotation(_myCamera.getTargetRotation() * glm::quat(glm::radians(glm::vec3( viewFrustumOffset.pitch, viewFrustumOffset.yaw, viewFrustumOffset.roll)))); _viewFrustumOffsetCamera.setUpShift(viewFrustumOffset.up); _viewFrustumOffsetCamera.setDistance(viewFrustumOffset.distance); _viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation _viewFrustumOffsetCamera.update(1.f/_fps); whichCamera = _viewFrustumOffsetCamera; } if (OculusManager::isConnected()) { displayOculus(whichCamera); } else { _glowEffect.prepare(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); displaySide(whichCamera); glPopMatrix(); _glowEffect.render(); displayOverlay(); } _frameCount++;} void Application::resetCamerasOnResizeGL(Camera& camera, int width, int height) { float aspectRatio = ((float)width/(float)height); // based on screen resize if (OculusManager::isConnected()) { // more magic numbers; see Oculus SDK docs, p. 32 camera.setAspectRatio(aspectRatio *= 0.5); camera.setFieldOfView(2 * atan((0.0468 * _oculusDistortionScale) / 0.041) * (180 / PIf)); } else { camera.setAspectRatio(aspectRatio); camera.setFieldOfView(Menu::getInstance()->getFieldOfView()); }} void Application::resizeGL(int width, int height) { resetCamerasOnResizeGL(_viewFrustumOffsetCamera, width, height); resetCamerasOnResizeGL(_myCamera, width, height); // Tell our viewFrustum about this change, using the application camera loadViewFrustum(_myCamera, _viewFrustum); glViewport(0, 0, width, height); // shouldn't this account for the menu??? updateProjectionMatrix(); glLoadIdentity();} void Application::updateProjectionMatrix() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); float left, right, bottom, top, nearVal, farVal; glm::vec4 nearClipPlane, farClipPlane; _viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); // If we're in Display Frustum mode, then we want to use the slightly adjust near/far clip values of the // _viewFrustumOffsetCamera, so that we can see more of the application content in the application's frustum if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { nearVal = _viewFrustumOffsetCamera.getNearClip(); farVal = _viewFrustumOffsetCamera.getFarClip(); } glFrustum(left, right, bottom, top, nearVal, farVal); glMatrixMode(GL_MODELVIEW);} void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) { Application* self = getInstance(); for (int i = 0; i < numNodeTypes; ++i) { // Intercept data to voxel server when voxels are disabled if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { continue; } // Perform the broadcast for one type int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, & nodeTypes[i], 1); // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise) BandwidthMeter::ChannelIndex channel; switch (nodeTypes[i]) { case NODE_TYPE_AGENT: case NODE_TYPE_AVATAR_MIXER: channel = BandwidthMeter::AVATARS; break; case NODE_TYPE_VOXEL_SERVER: channel = BandwidthMeter::VOXELS; break; default: continue; } self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); }} void Application::keyPressEvent(QKeyEvent* event) { if (activeWindow() == _window) { if (_chatEntryOn) { if (_chatEntry.keyPressEvent(event)) { _myAvatar.setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ? DELETE_KEY_DOWN : INSERT_KEY_DOWN); _myAvatar.setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR)); } else { _myAvatar.setChatMessage(_chatEntry.getContents()); _chatEntry.clear(); _chatEntryOn = false; setMenuShortcutsEnabled(true); } return; } Lines 1–500 of 3339. Load more
// // Application.cpp // interface // // Created by Andrzej Kapolka on 5/10/13. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. #include <sstream> #include <stdlib.h> #include <cmath> #ifdef _WIN32 #include "Syssocket.h" #include "Systime.h" #else #include <sys/time.h> #include <arpa/inet.h> #include <ifaddrs.h> #endif #include <glm/gtx/quaternion.hpp> #include <glm/gtx/vector_angle.hpp> // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" #include <QActionGroup> #include <QColorDialog> #include <QDesktopWidget> #include <QCheckBox> #include <QImage> #include <QKeyEvent> #include <QMainWindow> #include <QMenuBar> #include <QMouseEvent> #include <QNetworkAccessManager> #include <QOpenGLFramebufferObject> #include <QWheelEvent> #include <QSettings> #include <QShortcut> #include <QTimer> #include <QUrl> #include <QtDebug> #include <QFileDialog> #include <QDesktopServices> #include <NodeTypes.h> #include <AudioInjectionManager.h> #include <AudioInjector.h> #include <Logstash.h> #include <OctalCode.h> #include <PacketHeaders.h> #include <PairingHandler.h> #include <PerfStat.h> #include <VoxelSceneStats.h> #include "Application.h" #include "LogDisplay.h" #include "Menu.h" #include "Swatch.h" #include "Util.h" #include "devices/LeapManager.h" #include "devices/OculusManager.h" #include "renderer/ProgramObject.h" #include "ui/TextRenderer.h" using namespace std; // Starfield information static char STAR_FILE[] = "http://s3-us-west-1.amazonaws.com/highfidelity/stars.txt"; static char STAR_CACHE_FILE[] = "cachedStars.txt"; static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff // in the idle loop? (60 FPS is default) static QTimer* idleTimer = NULL; const int STARTUP_JITTER_SAMPLES = PACKET_LENGTH_SAMPLES_PER_CHANNEL / 2; // Startup optimistically with small jitter buffer that // will start playback on the second received audio packet. void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); LogDisplay::instance.addMessage(message.toLocal8Bit().constData()); } Application::Application(int& argc, char** argv, timeval &startup_time) : QApplication(argc, argv), _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), _displayLevels(false), _frameCount(0), _fps(120.0f), _justStarted(true), _voxelImporter(_window), _wantToKillLocalVoxels(false), _audioScope(256, 200, true), _mouseX(0), _mouseY(0), _touchAvgX(0.0f), _touchAvgY(0.0f), _isTouchPressed(false), _yawFromTouch(0.0f), _pitchFromTouch(0.0f), _mousePressed(false), _isHoverVoxel(false), _isHoverVoxelSounding(false), _mouseVoxelScale(1.0f / 1024.0f), _justEditedVoxel(false), _isLookingAtOtherAvatar(false), _lookatIndicatorScale(1.0f), _perfStatsOn(false), _chatEntryOn(false), _oculusProgram(0), _oculusDistortionScale(1.25), #ifndef _WIN32 _audio(&_audioScope, STARTUP_JITTER_SAMPLES), #endif _stopNetworkReceiveThread(false), _voxelProcessor(), _voxelEditSender(this), _packetCount(0), _packetsPerSecond(0), _bytesPerSecond(0), _bytesCount(0), _swatch(NULL), _pasteMode(false) { _applicationStartupTime = startup_time; _window->setWindowTitle("Interface"); qInstallMessageHandler(messageHandler); unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast<const char**>(argv); const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); if (portStr) { listenPort = atoi(portStr); } NodeList::createInstance(NODE_TYPE_AGENT, listenPort); NodeList::getInstance()->addHook(&_voxels); NodeList::getInstance()->addHook(this); // network receive thread and voxel parsing thread are both controlled by the --nonblocking command line _enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking"); if (!_enableNetworkThread) { NodeList::getInstance()->getNodeSocket()->setBlocking(false); } // setup QSettings #ifdef Q_OS_MAC QString resourcesPath = QCoreApplication::applicationDirPath() + "/../Resources"; #else QString resourcesPath = QCoreApplication::applicationDirPath() + "/resources"; #endif // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo(resourcesPath + "/info/ApplicationInfo.ini", QSettings::IniFormat); // set the associated application properties applicationInfo.beginGroup("INFO"); setApplicationName(applicationInfo.value("name").toString()); setApplicationVersion(applicationInfo.value("version").toString()); setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); _settings = new QSettings(this); // check if there is a saved domain server hostname // this must be done now instead of with the other setting checks to allow manual override with // --domain or --local options NodeList::getInstance()->loadData(_settings); const char* domainIP = getCmdOption(argc, constArgv, "--domain"); if (domainIP) { NodeList::getInstance()->setDomainIP(domainIP); } // Handle Local Domain testing with the --local command line if (cmdOptionExists(argc, constArgv, "--local")) { qDebug("Local Domain MODE!\n"); NodeList::getInstance()->setDomainIPToLocalhost(); } // Check to see if the user passed in a command line option for loading a local // Voxel File. _voxelsFilename = getCmdOption(argc, constArgv, "-i"); // the callback for our instance of NodeList is attachNewHeadToNode NodeList::getInstance()->linkedDataCreateCallback = &attachNewHeadToNode; #ifdef _WIN32 WSADATA WsaData; int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData); #endif // tell the NodeList instance who to tell the domain server we care about const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER}; NodeList::getInstance()->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); // start the nodeList threads NodeList::getInstance()->startSilentNodeRemovalThread(); _window->setCentralWidget(_glWidget); // call Menu getInstance static method to set up the menu _window->setMenuBar(Menu::getInstance()); _networkAccessManager = new QNetworkAccessManager(this); QRect available = desktop()->availableGeometry(); _window->resize(available.size()); _window->setVisible(true); _glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocus(); // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); // initialization continues in initializeGL when OpenGL context is ready // Tell our voxel edit sender about our known jurisdictions _voxelEditSender.setVoxelServerJurisdictions(&_voxelServerJurisdictions); } Application::~Application() { NodeList::getInstance()->removeHook(&_voxels); NodeList::getInstance()->removeHook(this); _sharedVoxelSystem.changeTree(new VoxelTree); _audio.shutdown(); delete Menu::getInstance(); delete _oculusProgram; delete _settings; delete _networkAccessManager; delete _followMode; delete _glWidget; } void Application::initializeGL() { qDebug( "Created Display Window.\n" ); // initialize glut for shape drawing; Qt apparently initializes it on OS X #ifndef __APPLE__ int argc = 0; glutInit(&argc, 0); #endif // Before we render anything, let's set up our viewFrustumOffsetCamera with a sufficiently large // field of view and near and far clip to make it interesting. //viewFrustumOffsetCamera.setFieldOfView(90.0); _viewFrustumOffsetCamera.setNearClip(0.1); _viewFrustumOffsetCamera.setFarClip(500.0 * TREE_SCALE); initDisplay(); qDebug( "Initialized Display.\n" ); init(); qDebug( "Init() complete.\n" ); // Check to see if the user passed in a command line option for randomizing colors bool wantColorRandomizer = !arguments().contains("--NoColorRandomizer"); // Check to see if the user passed in a command line option for loading a local // Voxel File. If so, load it now. if (!_voxelsFilename.isEmpty()) { _voxels.loadVoxelsFile(_voxelsFilename.constData(), wantColorRandomizer); qDebug("Local Voxel File loaded.\n"); } // create thread for receipt of data via UDP if (_enableNetworkThread) { pthread_create(&_networkReceiveThread, NULL, networkReceive, NULL); qDebug("Network receive thread created.\n"); } // create thread for parsing of voxel data independent of the main network and rendering threads _voxelProcessor.initialize(_enableProcessVoxelsThread); _voxelEditSender.initialize(_enableProcessVoxelsThread); if (_enableProcessVoxelsThread) { qDebug("Voxel parsing thread created.\n"); } // call terminate before exiting connect(this, SIGNAL(aboutToQuit()), SLOT(terminate())); // call our timer function every second QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(timer())); timer->start(1000); // call our idle function whenever we can idleTimer = new QTimer(this); connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); idleTimer->start(0); _idleLoopStdev.reset(); if (_justStarted) { float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime)) / 1000000.0; _justStarted = false; char title[50]; sprintf(title, "Interface: %4.2f seconds\n", startupTime); qDebug("%s", title); _window->setWindowTitle(title); const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time"; // ask the Logstash class to record the startup time Logstash::stashValue(STAT_TYPE_TIMER, LOGSTASH_INTERFACE_START_TIME_KEY, startupTime); } // update before the first render update(0.0f); // now that things are drawn - if this is an OS X release build we can check for an update #if defined(Q_OS_MAC) && defined(QT_NO_DEBUG) Menu::getInstance()->checkForUpdates(); #endif } void Application::paintGL() { PerfStat("display"); glEnable(GL_LINE_SMOOTH); if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness (100.0f); _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); _myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); } else if (OculusManager::isConnected()) { _myCamera.setUpShift (0.0f); _myCamera.setDistance (0.0f); _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing _myCamera.setTargetPosition(_myAvatar.getHeadJointPosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { _myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay _myCamera.setTargetPosition(_myAvatar.getUprightEyeLevelPosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); } // Update camera position _myCamera.update( 1.f/_fps ); // Note: whichCamera is used to pick between the normal camera myCamera for our // main camera, vs, an alternate camera. The alternate camera we support right now // is the viewFrustumOffsetCamera. But theoretically, we could use this same mechanism // to add other cameras. // // Why have two cameras? Well, one reason is that because in the case of the renderViewFrustum() // code, we want to keep the state of "myCamera" intact, so we can render what the view frustum of // myCamera is. But we also want to do meaningful camera transforms on OpenGL for the offset camera Camera whichCamera = _myCamera; if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { ViewFrustumOffset viewFrustumOffset = Menu::getInstance()->getViewFrustumOffset(); // set the camera to third-person view but offset so we can see the frustum _viewFrustumOffsetCamera.setTargetPosition(_myCamera.getTargetPosition()); _viewFrustumOffsetCamera.setTargetRotation(_myCamera.getTargetRotation() * glm::quat(glm::radians(glm::vec3( viewFrustumOffset.pitch, viewFrustumOffset.yaw, viewFrustumOffset.roll)))); _viewFrustumOffsetCamera.setUpShift(viewFrustumOffset.up); _viewFrustumOffsetCamera.setDistance(viewFrustumOffset.distance); _viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation _viewFrustumOffsetCamera.update(1.f/_fps); whichCamera = _viewFrustumOffsetCamera; } if (OculusManager::isConnected()) { displayOculus(whichCamera); } else { _glowEffect.prepare(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); displaySide(whichCamera); glPopMatrix(); _glowEffect.render(); displayOverlay(); } _frameCount++; } void Application::resetCamerasOnResizeGL(Camera& camera, int width, int height) { float aspectRatio = ((float)width/(float)height); // based on screen resize if (OculusManager::isConnected()) { // more magic numbers; see Oculus SDK docs, p. 32 camera.setAspectRatio(aspectRatio *= 0.5); camera.setFieldOfView(2 * atan((0.0468 * _oculusDistortionScale) / 0.041) * (180 / PIf)); } else { camera.setAspectRatio(aspectRatio); camera.setFieldOfView(Menu::getInstance()->getFieldOfView()); } } void Application::resizeGL(int width, int height) { resetCamerasOnResizeGL(_viewFrustumOffsetCamera, width, height); resetCamerasOnResizeGL(_myCamera, width, height); // Tell our viewFrustum about this change, using the application camera loadViewFrustum(_myCamera, _viewFrustum); glViewport(0, 0, width, height); // shouldn't this account for the menu??? updateProjectionMatrix(); glLoadIdentity(); } void Application::updateProjectionMatrix() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); float left, right, bottom, top, nearVal, farVal; glm::vec4 nearClipPlane, farClipPlane; _viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); // If we're in Display Frustum mode, then we want to use the slightly adjust near/far clip values of the // _viewFrustumOffsetCamera, so that we can see more of the application content in the application's frustum if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { nearVal = _viewFrustumOffsetCamera.getNearClip(); farVal = _viewFrustumOffsetCamera.getFarClip(); } glFrustum(left, right, bottom, top, nearVal, farVal); glMatrixMode(GL_MODELVIEW); } void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) { Application* self = getInstance(); for (int i = 0; i < numNodeTypes; ++i) { // Intercept data to voxel server when voxels are disabled if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { continue; } // Perform the broadcast for one type int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, & nodeTypes[i], 1); // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise) BandwidthMeter::ChannelIndex channel; switch (nodeTypes[i]) { case NODE_TYPE_AGENT: case NODE_TYPE_AVATAR_MIXER: channel = BandwidthMeter::AVATARS; break; case NODE_TYPE_VOXEL_SERVER: channel = BandwidthMeter::VOXELS; break; default: continue; } self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); } } void Application::keyPressEvent(QKeyEvent* event) { if (activeWindow() == _window) { if (_chatEntryOn) { if (_chatEntry.keyPressEvent(event)) { _myAvatar.setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ? DELETE_KEY_DOWN : INSERT_KEY_DOWN); _myAvatar.setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR)); } else { _myAvatar.setChatMessage(_chatEntry.getContents()); _chatEntry.clear(); _chatEntryOn = false; setMenuShortcutsEnabled(true); } return; }