4406 lines · 181.7 KB · C++
RawDownload
////  Application.cpp//  interface////  Created by Andrzej Kapolka on 5/10/13.//  Copyright (c) 2013 High Fidelity, Inc. All rights reserved. #ifdef WIN32#include <Systime.h>#endif #include <sstream> #include <stdlib.h>#include <cmath>#include <math.h>  #include <glm/gtx/component_wise.hpp>#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 <QNetworkDiskCache>#include <QOpenGLFramebufferObject>#include <QWheelEvent>#include <QSettings>#include <QShortcut>#include <QTimer>#include <QUrl>#include <QtDebug>#include <QFileDialog>#include <QDesktopServices> #include <AudioInjector.h>#include <NodeTypes.h>#include <Logging.h>#include <OctalCode.h>#include <PacketHeaders.h>#include <PairingHandler.h>#include <ParticlesScriptingInterface.h>#include <PerfStat.h>#include <UUID.h>#include <VoxelSceneStats.h> #include "Application.h"#include "DataServerClient.h"#include "InterfaceVersion.h"#include "Menu.h"#include "Swatch.h"#include "Util.h"#include "devices/OculusManager.h"#include "devices/TV3DManager.h"#include "renderer/ProgramObject.h"#include "ui/TextRenderer.h"#include "InfoView.h" using namespace std; //  Starfield informationstatic unsigned STARFIELD_NUM_STARS = 50000;static unsigned STARFIELD_SEED = 1; 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 = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / 2;                                                 //  Startup optimistically with small jitter buffer that                                                 //  will start playback on the second received audio packet. const int MIRROR_VIEW_TOP_PADDING = 5;const int MIRROR_VIEW_LEFT_PADDING = 10;const int MIRROR_VIEW_WIDTH = 265;const int MIRROR_VIEW_HEIGHT = 215;const float MIRROR_FULLSCREEN_DISTANCE = 0.35f;const float MIRROR_REARVIEW_DISTANCE = 0.65f;const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) {    QString messageWithNewLine = message + "\n";    fprintf(stdout, "%s", messageWithNewLine.toLocal8Bit().constData());    Application::getInstance()->getLogger()->addMessage(messageWithNewLine.toLocal8Bit().constData());} Application::Application(int& argc, char** argv, timeval &startup_time) :        QApplication(argc, argv),        _window(new QMainWindow(desktop())),        _glWidget(new GLCanvas()),        _frameCount(0),        _fps(120.0f),        _justStarted(true),        _voxelImporter(_window),        _wantToKillLocalVoxels(false),        _audioScope(256, 200, true),        _profile(QString()),        _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),        _mouseX(0),        _mouseY(0),        _lastMouseMove(usecTimestampNow()),        _mouseHidden(false),        _seenMouseMove(false),        _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),        _mouseVoxelScaleInitialized(false),        _justEditedVoxel(false),        _isHighlightVoxel(false),        _nudgeStarted(false),        _lookingAlongX(false),        _lookingAwayFromOrigin(true),        _lookatTargetAvatar(NULL),        _lookatIndicatorScale(1.0f),        _chatEntryOn(false),        _audio(&_audioScope, STARTUP_JITTER_SAMPLES),        _enableProcessVoxelsThread(true),        _voxelProcessor(),        _voxelHideShowThread(&_voxels),        _voxelEditSender(this),        _particleEditSender(this),        _packetCount(0),        _packetsPerSecond(0),        _bytesPerSecond(0),        _bytesCount(0),        _recentMaxPackets(0),        _resetRecentMaxPacketsSoon(true),        _swatch(NULL),        _pasteMode(false),        _logger(new FileLogger()),        _persistThread(NULL){    _applicationStartupTime = startup_time;     switchToResourcesParentIfRequired();    QFontDatabase::addApplicationFont("resources/styles/Inconsolata.otf");    _window->setWindowTitle("Interface");     qInstallMessageHandler(messageHandler);     // call Menu getInstance static method to set up the menu    _window->setMenuBar(Menu::getInstance());     qDebug("[VERSION] Build sequence: %i", BUILD_VERSION);     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* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort);        // connect our processDatagrams slot to the QUDPSocket readyRead() signal    connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(processDatagrams()));     // put the audio processing on a separate thread    QThread* audioThread = new QThread(this);     _audio.moveToThread(audioThread);    connect(audioThread, SIGNAL(started()), &_audio, SLOT(start()));     audioThread->start();        connect(nodeList, SIGNAL(domainChanged(const QString&)), SLOT(domainChanged(const QString&)));     connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));    connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer)));    connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer)));     // read the ApplicationInfo.ini file for Name/Version/Domain information    QSettings applicationInfo("resources/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 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->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,        NODE_TYPE_PARTICLE_SERVER, NODE_TYPE_METAVOXEL_SERVER};    nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest));     QTimer* silentNodeTimer = new QTimer(this);    connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));    silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);     QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);     _networkAccessManager = new QNetworkAccessManager(this);    QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager);    cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");    _networkAccessManager->setCache(cache);     _window->setCentralWidget(_glWidget);     restoreSizeAndPosition();    _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);    _particleEditSender.setServerJurisdictions(&_particleServerJurisdictions);     Particle::setVoxelEditPacketSender(&_voxelEditSender);    Particle::setParticleEditPacketSender(&_particleEditSender);     // For now we're going to set the PPS for outbound packets to be super high, this is    // probably not the right long term solution. But for now, we're going to do this to    // allow you to move a particle around in your hand    _particleEditSender.setPacketsPerSecond(3000); // super high!!     // Set the sixense filtering    _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); } Application::~Application() {     qInstallMessageHandler(NULL);     // make sure we don't call the idle timer any more    delete idleTimer;     // ask the audio thread to quit and wait until it is done    _audio.thread()->quit();    _audio.thread()->wait();     storeSizeAndPosition();     _sharedVoxelSystem.changeTree(new VoxelTree);     VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown    Menu::getInstance()->deleteLater();     delete _logger;    delete _settings;    delete _glWidget;} void Application::restoreSizeAndPosition() {    QSettings* settings = new QSettings(this);    QRect available = desktop()->availableGeometry();     settings->beginGroup("Window");     int x = (int)loadSetting(settings, "x", 0);    int y = (int)loadSetting(settings, "y", 0);    _window->move(x, y);     int width = (int)loadSetting(settings, "width", available.width());    int height = (int)loadSetting(settings, "height", available.height());    _window->resize(width, height);     settings->endGroup();} void Application::storeSizeAndPosition() {    QSettings* settings = new QSettings(this);     settings->beginGroup("Window");     settings->setValue("width", _window->rect().width());    settings->setValue("height", _window->rect().height());     settings->setValue("x", _window->pos().x());    settings->setValue("y", _window->pos().y());     settings->endGroup();} void Application::initializeGL() {    qDebug( "Created Display Window.");     // initialize glut for shape drawing; Qt apparently initializes it on OS X    #ifndef __APPLE__    int argc = 0;    glutInit(&argc, 0);    #endif 	#ifdef WIN32    GLenum err = glewInit();    if (GLEW_OK != err) {      /* Problem: glewInit failed, something is seriously wrong. */      qDebug("Error: %s\n", glewGetErrorString(err));    }    qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));    #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.1f);    _viewFrustumOffsetCamera.setFarClip(500.0f * TREE_SCALE);     initDisplay();    qDebug( "Initialized Display.");     init();    qDebug( "init() complete.");     // create thread for parsing of voxel data independent of the main network and rendering threads    _voxelProcessor.initialize(_enableProcessVoxelsThread);    _voxelEditSender.initialize(_enableProcessVoxelsThread);    _voxelHideShowThread.initialize(_enableProcessVoxelsThread);    _particleEditSender.initialize(_enableProcessVoxelsThread);    if (_enableProcessVoxelsThread) {        qDebug("Voxel parsing thread created.");    }     // 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;        qDebug("Startup time: %4.2f seconds.", startupTime);        const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time";         // ask the Logstash class to record the startup time        Logging::stashValue(STAT_TYPE_TIMER, LOGSTASH_INTERFACE_START_TIME_KEY, startupTime);    }     // update before the first render    update(0.0f);     InfoView::showFirstTime();} void Application::paintGL() {    PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));    bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);    PerformanceWarning warn(showWarnings, "Application::paintGL()");     glEnable(GL_LINE_SMOOTH);     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.getHead().calculateAverageEyePosition());        _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.getHead().calculateAverageEyePosition());        _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation());     } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {        _myCamera.setTightness     (0.0f);     //  Camera is directly connected to head without smoothing        _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());        _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation());     } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {        _myCamera.setTightness(0.0f);        float headHeight = _myAvatar.getHead().calculateAverageEyePosition().y - _myAvatar.getPosition().y;        _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar.getScale());        _myCamera.setTargetPosition(_myAvatar.getPosition() + glm::vec3(0, headHeight, 0));        _myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));    }     // 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 (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) {        updateShadowMap();    }     if (OculusManager::isConnected()) {        OculusManager::display(whichCamera);    } else if (TV3DManager::isConnected()) {        _glowEffect.prepare();        TV3DManager::display(whichCamera);        _glowEffect.render();    } else {        _glowEffect.prepare();         glMatrixMode(GL_MODELVIEW);        glPushMatrix();        glLoadIdentity();        displaySide(whichCamera);        glPopMatrix();         _glowEffect.render();         if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {             bool eyeRelativeCamera = false;            if (_rearMirrorTools->getZoomLevel() == BODY) {                _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar.getScale());                _mirrorCamera.setTargetPosition(_myAvatar.getChestJointPosition());            } else { // HEAD zoom level                _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar.getScale());                if (_myAvatar.getSkeletonModel().isActive() && _myAvatar.getHead().getFaceModel().isActive()) {                    // as a hack until we have a better way of dealing with coordinate precision issues, reposition the                    // face/body so that the average eye position lies at the origin                    eyeRelativeCamera = true;                    _mirrorCamera.setTargetPosition(glm::vec3());                 } else {                    _mirrorCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition());                }            }             _mirrorCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));            _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());            bool updateViewFrustum = false;            updateProjectionMatrix(_mirrorCamera, updateViewFrustum);            glEnable(GL_SCISSOR_TEST);            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);             // render rear mirror view            glPushMatrix();

Lines 1–500 of 4406.