Showing the changes to interface/src/Application.cpp (1 of 285 files). Show all
Unified · Split
| @@ -35,7 +35,6 @@ · 34 unmodified lines | ||
| 35 | 35 | #include <QMouseEvent> |
| 36 | 36 | #include <QNetworkReply> |
| 37 | 37 | #include <QNetworkDiskCache> |
| 38 | −#include <QOpenGLFramebufferObject> | |
| 39 | 38 | #include <QObject> |
| 40 | 39 | #include <QWheelEvent> |
| 41 | 40 | #include <QScreen> |
| @@ -64,6 +63,7 @@ · 22 unmodified lines | ||
| 64 | 63 | #include <GlowEffect.h> |
| 65 | 64 | #include <HFActionEvent.h> |
| 66 | 65 | #include <HFBackEvent.h> |
| 66 | +#include <VrMenu.h> | |
| 67 | 67 | #include <LogHandler.h> |
| 68 | 68 | #include <MainWindow.h> |
| 69 | 69 | #include <ModelEntityItem.h> |
| @@ -83,6 +83,7 @@ · 13 unmodified lines | ||
| 83 | 83 | #include <TextRenderer.h> |
| 84 | 84 | #include <UserActivityLogger.h> |
| 85 | 85 | #include <UUID.h> |
| 86 | +#include <MessageDialog.h> | |
| 86 | 87 | |
| 87 | 88 | #include <SceneScriptingInterface.h> |
| 88 | 89 | |
| @@ -109,7 +110,6 @@ · 20 unmodified lines | ||
| 109 | 110 | #include "devices/MIDIManager.h" |
| 110 | 111 | #include "devices/OculusManager.h" |
| 111 | 112 | #include "devices/TV3DManager.h" |
| 112 | −#include "devices/Visage.h" | |
| 113 | 113 | |
| 114 | 114 | #include "gpu/Batch.h" |
| 115 | 115 | #include "gpu/GLBackend.h" |
| @@ -137,6 +137,7 @@ · 21 unmodified lines | ||
| 137 | 137 | #include "ui/Snapshot.h" |
| 138 | 138 | #include "ui/StandAloneJSConsole.h" |
| 139 | 139 | #include "ui/Stats.h" |
| 140 | +#include "ui/AddressBarDialog.h" | |
| 140 | 141 | |
| 141 | 142 | // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU |
| 142 | 143 | #if defined(Q_OS_WIN) |
| @@ -145,6 +146,23 @@ extern "C" { · 2 unmodified lines | ||
| 145 | 146 | } |
| 146 | 147 | #endif |
| 147 | 148 | |
| 149 | +enum CustomEventTypes { | |
| 150 | + Lambda = QEvent::User + 1 | |
| 151 | +}; | |
| 152 | + | |
| 153 | +class LambdaEvent : public QEvent { | |
| 154 | + std::function<void()> _fun; | |
| 155 | +public: | |
| 156 | + LambdaEvent(const std::function<void()> & fun) : | |
| 157 | + QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) { | |
| 158 | + } | |
| 159 | + LambdaEvent(std::function<void()> && fun) : | |
| 160 | + QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) { | |
| 161 | + } | |
| 162 | + void call() { _fun(); } | |
| 163 | +}; | |
| 164 | + | |
| 165 | + | |
| 148 | 166 | using namespace std; |
| 149 | 167 | |
| 150 | 168 | // Starfield information |
| @@ -165,8 +183,6 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D · 14 unmodified lines | ||
| 165 | 183 | |
| 166 | 184 | const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js"; |
| 167 | 185 | |
| 168 | −bool renderCollisionHulls = false; | |
| 169 | − | |
| 170 | 186 | #ifdef Q_OS_WIN |
| 171 | 187 | class MyNativeEventFilter : public QAbstractNativeEventFilter { |
| 172 | 188 | public: |
| @@ -209,8 +225,12 @@ public: · 36 unmodified lines | ||
| 209 | 225 | |
| 210 | 226 | void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { |
| 211 | 227 | QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); |
| 212 | − | |
| 228 | + | |
| 213 | 229 | if (!logMessage.isEmpty()) { |
| 230 | +#ifdef Q_OS_WIN | |
| 231 | + OutputDebugStringA(logMessage.toLocal8Bit().constData()); | |
| 232 | + OutputDebugStringA("\n"); | |
| 233 | +#endif | |
| 214 | 234 | Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage + "\n")); |
| 215 | 235 | } |
| 216 | 236 | } |
| @@ -243,7 +263,6 @@ bool setupEssentials(int& argc, char** argv) { · 26 unmodified lines | ||
| 243 | 263 | auto ambientOcclusionEffect = DependencyManager::set<AmbientOcclusionEffect>(); |
| 244 | 264 | auto textureCache = DependencyManager::set<TextureCache>(); |
| 245 | 265 | auto animationCache = DependencyManager::set<AnimationCache>(); |
| 246 | − auto visage = DependencyManager::set<Visage>(); | |
| 247 | 266 | auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>(); |
| 248 | 267 | auto modelBlender = DependencyManager::set<ModelBlender>(); |
| 249 | 268 | auto audioToolBox = DependencyManager::set<AudioToolBox>(); |
| @@ -260,6 +279,7 @@ bool setupEssentials(int& argc, char** argv) { · 10 unmodified lines | ||
| 260 | 279 | #endif |
| 261 | 280 | auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>(); |
| 262 | 281 | auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>(); |
| 282 | + auto offscreenUi = DependencyManager::set<OffscreenUi>(); | |
| 263 | 283 | |
| 264 | 284 | return true; |
| 265 | 285 | } |
| @@ -317,8 +337,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 51 unmodified lines | ||
| 317 | 337 | #ifdef Q_OS_WIN |
| 318 | 338 | installNativeEventFilter(&MyNativeEventFilter::getInstance()); |
| 319 | 339 | #endif |
| 340 | + | |
| 320 | 341 | |
| 321 | 342 | _logger = new FileLogger(this); // After setting organization name in order to get correct directory |
| 343 | + | |
| 322 | 344 | qInstallMessageHandler(messageHandler); |
| 323 | 345 | |
| 324 | 346 | QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf"); |
| @@ -336,9 +358,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 11 unmodified lines | ||
| 336 | 358 | |
| 337 | 359 | _bookmarks = new Bookmarks(); // Before setting up the menu |
| 338 | 360 | |
| 339 | − // call Menu getInstance static method to set up the menu | |
| 340 | − _window->setMenuBar(Menu::getInstance()); | |
| 341 | − | |
| 342 | 361 | _runningScriptsWidget = new RunningScriptsWidget(_window); |
| 343 | 362 | |
| 344 | 363 | // start the nodeThread so its event loop is running |
| @@ -445,6 +464,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 100 unmodified lines | ||
| 445 | 464 | addressManager->setOrientationGetter(getOrientationForPath); |
| 446 | 465 | |
| 447 | 466 | connect(addressManager.data(), &AddressManager::rootPlaceNameChanged, this, &Application::updateWindowTitle); |
| 467 | + connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); | |
| 448 | 468 | |
| 449 | 469 | #ifdef _WIN32 |
| 450 | 470 | WSADATA WsaData; |
| @@ -562,8 +582,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 111 unmodified lines | ||
| 562 | 582 | #endif |
| 563 | 583 | |
| 564 | 584 | this->installEventFilter(this); |
| 585 | + // The offscreen UI needs to intercept the mouse and keyboard | |
| 586 | + // events coming from the onscreen window | |
| 587 | + _glWidget->installEventFilter(DependencyManager::get<OffscreenUi>().data()); | |
| 565 | 588 | } |
| 566 | 589 | |
| 590 | + | |
| 567 | 591 | void Application::aboutToQuit() { |
| 568 | 592 | emit beforeAboutToQuit(); |
| 569 | 593 | |
| @@ -612,6 +636,10 @@ void Application::cleanupBeforeQuit() { · 42 unmodified lines | ||
| 612 | 636 | |
| 613 | 637 | // destroy the AudioClient so it and its thread have a chance to go down safely |
| 614 | 638 | DependencyManager::destroy<AudioClient>(); |
| 639 | + | |
| 640 | +#ifdef HAVE_DDE | |
| 641 | + DependencyManager::destroy<DdeFaceTracker>(); | |
| 642 | +#endif | |
| 615 | 643 | } |
| 616 | 644 | |
| 617 | 645 | Application::~Application() { |
| @@ -632,6 +660,7 @@ Application::~Application() { · 14 unmodified lines | ||
| 632 | 660 | |
| 633 | 661 | getActiveRenderPlugin()->deactivate(); |
| 634 | 662 | |
| 663 | + DependencyManager::destroy<OffscreenUi>(); | |
| 635 | 664 | DependencyManager::destroy<AvatarManager>(); |
| 636 | 665 | DependencyManager::destroy<AnimationCache>(); |
| 637 | 666 | DependencyManager::destroy<TextureCache>(); |
| @@ -692,6 +721,17 @@ void Application::initializeGL() { · 54 unmodified lines | ||
| 692 | 721 | initDisplay(); |
| 693 | 722 | qCDebug(interfaceapp, "Initialized Display."); |
| 694 | 723 | |
| 724 | + // The UI can't be created until the primary OpenGL | |
| 725 | + // context is created, because it needs to share | |
| 726 | + // texture resources | |
| 727 | + initializeUi(); | |
| 728 | + qCDebug(interfaceapp, "Initialized Offscreen UI."); | |
| 729 | + _glWidget->makeCurrent(); | |
| 730 | + | |
| 731 | + // call Menu getInstance static method to set up the menu | |
| 732 | + // Needs to happen AFTER the QML UI initialization | |
| 733 | + _window->setMenuBar(Menu::getInstance()); | |
| 734 | + | |
| 695 | 735 | init(); |
| 696 | 736 | qCDebug(interfaceapp, "init() complete."); |
| 697 | 737 | |
| @@ -719,10 +759,45 @@ void Application::initializeGL() { · 21 unmodified lines | ||
| 719 | 759 | |
| 720 | 760 | // update before the first render |
| 721 | 761 | update(1.0f / _fps); |
| 722 | − | |
| 762 | + | |
| 723 | 763 | InfoView::showFirstTime(INFO_HELP_PATH); |
| 724 | 764 | } |
| 725 | 765 | |
| 766 | +void Application::initializeUi() { | |
| 767 | + AddressBarDialog::registerType(); | |
| 768 | + LoginDialog::registerType(); | |
| 769 | + MessageDialog::registerType(); | |
| 770 | + VrMenu::registerType(); | |
| 771 | + | |
| 772 | + auto offscreenUi = DependencyManager::get<OffscreenUi>(); | |
| 773 | + offscreenUi->create(_glWidget->context()->contextHandle()); | |
| 774 | + offscreenUi->resize(_glWidget->size()); | |
| 775 | + offscreenUi->setProxyWindow(_window->windowHandle()); | |
| 776 | + offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); | |
| 777 | + offscreenUi->load("Root.qml"); | |
| 778 | + offscreenUi->load("RootMenu.qml"); | |
| 779 | + VrMenu::load(); | |
| 780 | + VrMenu::executeQueuedLambdas(); | |
| 781 | + offscreenUi->setMouseTranslator([this](const QPointF& p){ | |
| 782 | + if (OculusManager::isConnected()) { | |
| 783 | + glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p)); | |
| 784 | + return QPointF(pos.x, pos.y); | |
| 785 | + } | |
| 786 | + return QPointF(p); | |
| 787 | + }); | |
| 788 | + offscreenUi->resume(); | |
| 789 | + connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect & r){ | |
| 790 | + static qreal oldDevicePixelRatio = 0; | |
| 791 | + qreal devicePixelRatio = _glWidget->devicePixelRatio(); | |
| 792 | + if (devicePixelRatio != oldDevicePixelRatio) { | |
| 793 | + oldDevicePixelRatio = devicePixelRatio; | |
| 794 | + qDebug() << "Device pixel ratio changed, triggering GL resize"; | |
| 795 | + resizeGL(_glWidget->width(), | |
| 796 | + _glWidget->height()); | |
| 797 | + } | |
| 798 | + }); | |
| 799 | +} | |
| 800 | + | |
| 726 | 801 | void Application::paintGL() { |
| 727 | 802 | PROFILE_RANGE(__FUNCTION__); |
| 728 | 803 | PerformanceTimer perfTimer("paintGL"); |
| @@ -777,7 +852,7 @@ void Application::paintGL() { · 48 unmodified lines | ||
| 777 | 852 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| 778 | 853 | |
| 779 | 854 | // Viewport is assigned to the size of the framebuffer |
| 780 | − QSize size = DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->size(); | |
| 855 | + QSize size = DependencyManager::get<TextureCache>()->getFrameBufferSize(); | |
| 781 | 856 | glViewport(0, 0, size.width(), size.height()); |
| 782 | 857 | |
| 783 | 858 | glMatrixMode(GL_MODELVIEW); |
| @@ -789,7 +864,19 @@ void Application::paintGL() { · 5 unmodified lines | ||
| 789 | 864 | if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { |
| 790 | 865 | _rearMirrorTools->render(true); |
| 791 | 866 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { |
| 867 | +<<<<<<< HEAD | |
| 792 | 868 | renderRearViewMirror(_mirrorViewRect); |
| 869 | +======= | |
| 870 | + renderRearViewMirror(_mirrorViewRect); | |
| 871 | + } | |
| 872 | + | |
| 873 | + DependencyManager::get<GlowEffect>()->render(); | |
| 874 | + | |
| 875 | + { | |
| 876 | + PerformanceTimer perfTimer("renderOverlay"); | |
| 877 | + _applicationOverlay.renderOverlay(); | |
| 878 | + _applicationOverlay.displayOverlayTexture(); | |
| 879 | +>>>>>>> master | |
| 793 | 880 | } |
| 794 | 881 | } |
| 795 | 882 | primaryFbo->release(); |
| @@ -864,6 +951,9 @@ void Application::resizeGL(int width, int height) { · 68 unmodified lines | ||
| 864 | 951 | updateProjectionMatrix(); |
| 865 | 952 | glLoadIdentity(); |
| 866 | 953 | |
| 954 | + auto offscreenUi = DependencyManager::get<OffscreenUi>(); | |
| 955 | + offscreenUi->resize(_glWidget->size()); | |
| 956 | + | |
| 867 | 957 | // update Stats width |
| 868 | 958 | // let's set horizontal offset to give stats some margin to mirror |
| 869 | 959 | int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; |
| @@ -913,6 +1003,7 @@ bool Application::importSVOFromURL(const QString& urlString) { · 43 unmodified lines | ||
| 913 | 1003 | |
| 914 | 1004 | bool Application::event(QEvent* event) { |
| 915 | 1005 | switch (event->type()) { |
| 1006 | +<<<<<<< HEAD | |
| 916 | 1007 | case QEvent::MouseMove: |
| 917 | 1008 | mouseMoveEvent((QMouseEvent*)event); |
| 918 | 1009 | return true; |
| @@ -948,6 +1039,48 @@ bool Application::event(QEvent* event) { · 29 unmodified lines | ||
| 948 | 1039 | |
| 949 | 1040 | default: |
| 950 | 1041 | break; |
| 1042 | +======= | |
| 1043 | + case Lambda: | |
| 1044 | + ((LambdaEvent*)event)->call(); | |
| 1045 | + return true; | |
| 1046 | + | |
| 1047 | + case QEvent::MouseMove: | |
| 1048 | + mouseMoveEvent((QMouseEvent*)event); | |
| 1049 | + return true; | |
| 1050 | + case QEvent::MouseButtonPress: | |
| 1051 | + mousePressEvent((QMouseEvent*)event); | |
| 1052 | + return true; | |
| 1053 | + case QEvent::MouseButtonRelease: | |
| 1054 | + mouseReleaseEvent((QMouseEvent*)event); | |
| 1055 | + return true; | |
| 1056 | + case QEvent::KeyPress: | |
| 1057 | + keyPressEvent((QKeyEvent*)event); | |
| 1058 | + return true; | |
| 1059 | + case QEvent::KeyRelease: | |
| 1060 | + keyReleaseEvent((QKeyEvent*)event); | |
| 1061 | + return true; | |
| 1062 | + case QEvent::FocusOut: | |
| 1063 | + focusOutEvent((QFocusEvent*)event); | |
| 1064 | + return true; | |
| 1065 | + case QEvent::TouchBegin: | |
| 1066 | + touchBeginEvent(static_cast<QTouchEvent*>(event)); | |
| 1067 | + event->accept(); | |
| 1068 | + return true; | |
| 1069 | + case QEvent::TouchEnd: | |
| 1070 | + touchEndEvent(static_cast<QTouchEvent*>(event)); | |
| 1071 | + return true; | |
| 1072 | + case QEvent::TouchUpdate: | |
| 1073 | + touchUpdateEvent(static_cast<QTouchEvent*>(event)); | |
| 1074 | + return true; | |
| 1075 | + case QEvent::Wheel: | |
| 1076 | + wheelEvent(static_cast<QWheelEvent*>(event)); | |
| 1077 | + return true; | |
| 1078 | + case QEvent::Drop: | |
| 1079 | + dropEvent(static_cast<QDropEvent*>(event)); | |
| 1080 | + return true; | |
| 1081 | + default: | |
| 1082 | + break; | |
| 1083 | +>>>>>>> master | |
| 951 | 1084 | } |
| 952 | 1085 | |
| 953 | 1086 | // handle custom URL |
| @@ -969,13 +1102,18 @@ bool Application::event(QEvent* event) { · 15 unmodified lines | ||
| 969 | 1102 | if (HFActionEvent::types().contains(event->type())) { |
| 970 | 1103 | _controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event)); |
| 971 | 1104 | } |
| 972 | − | |
| 1105 | + | |
| 973 | 1106 | return QApplication::event(event); |
| 974 | 1107 | } |
| 975 | 1108 | |
| 976 | 1109 | bool Application::eventFilter(QObject* object, QEvent* event) { |
| 977 | 1110 | |
| 978 | 1111 | if (event->type() == QEvent::ShortcutOverride) { |
| 1112 | + if (DependencyManager::get<OffscreenUi>()->shouldSwallowShortcut(event)) { | |
| 1113 | + event->accept(); | |
| 1114 | + return true; | |
| 1115 | + } | |
| 1116 | + | |
| 979 | 1117 | // Filter out captured keys before they're used for shortcut actions. |
| 980 | 1118 | if (_controllerScriptingInterface.isKeyCaptured(static_cast<QKeyEvent*>(event))) { |
| 981 | 1119 | event->accept(); |
| @@ -986,8 +1124,10 @@ bool Application::eventFilter(QObject* object, QEvent* event) { · 4 unmodified lines | ||
| 986 | 1124 | return false; |
| 987 | 1125 | } |
| 988 | 1126 | |
| 989 | −void Application::keyPressEvent(QKeyEvent* event) { | |
| 1127 | +static bool _altPressed{ false }; | |
| 990 | 1128 | |
| 1129 | +void Application::keyPressEvent(QKeyEvent* event) { | |
| 1130 | + _altPressed = event->key() == Qt::Key_Alt; | |
| 991 | 1131 | _keysPressed.insert(event->key()); |
| 992 | 1132 | |
| 993 | 1133 | _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts |
| @@ -1003,12 +1143,19 @@ void Application::keyPressEvent(QKeyEvent* event) { · 9 unmodified lines | ||
| 1003 | 1143 | bool isOption = event->modifiers().testFlag(Qt::AltModifier); |
| 1004 | 1144 | switch (event->key()) { |
| 1005 | 1145 | break; |
| 1146 | + case Qt::Key_Enter: | |
| 1147 | + case Qt::Key_Return: | |
| 1148 | + Menu::getInstance()->triggerOption(MenuOption::AddressBar); | |
| 1149 | + break; | |
| 1150 | + | |
| 1006 | 1151 | case Qt::Key_L: |
| 1007 | − if (isShifted) { | |
| 1008 | − Menu::getInstance()->triggerOption(MenuOption::LodTools); | |
| 1009 | − } else if (isMeta) { | |
| 1152 | + if (isShifted && isMeta) { | |
| 1010 | 1153 | Menu::getInstance()->triggerOption(MenuOption::Log); |
| 1011 | − } | |
| 1154 | + } else if (isMeta) { | |
| 1155 | + Menu::getInstance()->triggerOption(MenuOption::AddressBar); | |
| 1156 | + } else if (isShifted) { | |
| 1157 | + Menu::getInstance()->triggerOption(MenuOption::LodTools); | |
| 1158 | + } | |
| 1012 | 1159 | break; |
| 1013 | 1160 | |
| 1014 | 1161 | case Qt::Key_E: |
| @@ -1068,11 +1215,6 @@ void Application::keyPressEvent(QKeyEvent* event) { · 53 unmodified lines | ||
| 1068 | 1215 | } |
| 1069 | 1216 | break; |
| 1070 | 1217 | |
| 1071 | − case Qt::Key_Return: | |
| 1072 | − case Qt::Key_Enter: | |
| 1073 | − Menu::getInstance()->triggerOption(MenuOption::AddressBar); | |
| 1074 | − break; | |
| 1075 | − | |
| 1076 | 1218 | case Qt::Key_Backslash: |
| 1077 | 1219 | Menu::getInstance()->triggerOption(MenuOption::Chat); |
| 1078 | 1220 | break; |
| @@ -1226,11 +1368,6 @@ void Application::keyPressEvent(QKeyEvent* event) { · 147 unmodified lines | ||
| 1226 | 1368 | break; |
| 1227 | 1369 | } |
| 1228 | 1370 | |
| 1229 | − case Qt::Key_Comma: { | |
| 1230 | − renderCollisionHulls = !renderCollisionHulls; | |
| 1231 | − break; | |
| 1232 | − } | |
| 1233 | − | |
| 1234 | 1371 | default: |
| 1235 | 1372 | event->ignore(); |
| 1236 | 1373 | break; |
| @@ -1238,7 +1375,19 @@ void Application::keyPressEvent(QKeyEvent* event) { · 1 unmodified line | ||
| 1238 | 1375 | } |
| 1239 | 1376 | } |
| 1240 | 1377 | |
| 1378 | + | |
| 1379 | +//#define VR_MENU_ONLY_IN_HMD | |
| 1380 | + | |
| 1241 | 1381 | void Application::keyReleaseEvent(QKeyEvent* event) { |
| 1382 | + if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) { | |
| 1383 | +#ifdef VR_MENU_ONLY_IN_HMD | |
| 1384 | + if (OculusManager::isConnected()) { | |
| 1385 | +#endif | |
| 1386 | + VrMenu::toggle(); | |
| 1387 | +#ifdef VR_MENU_ONLY_IN_HMD | |
| 1388 | + } | |
| 1389 | +#endif | |
| 1390 | + } | |
| 1242 | 1391 | |
| 1243 | 1392 | _keysPressed.remove(event->key()); |
| 1244 | 1393 | |
| @@ -1249,6 +1398,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { · 4 unmodified lines | ||
| 1249 | 1398 | return; |
| 1250 | 1399 | } |
| 1251 | 1400 | |
| 1401 | + | |
| 1252 | 1402 | switch (event->key()) { |
| 1253 | 1403 | case Qt::Key_E: |
| 1254 | 1404 | case Qt::Key_PageUp: |
| @@ -1440,9 +1590,11 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { · 185 unmodified lines | ||
| 1440 | 1590 | } |
| 1441 | 1591 | |
| 1442 | 1592 | void Application::touchUpdateEvent(QTouchEvent* event) { |
| 1443 | − TouchEvent thisEvent(*event, _lastTouchEvent); | |
| 1444 | − _controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts | |
| 1445 | − _lastTouchEvent = thisEvent; | |
| 1593 | + if (event->type() == QEvent::TouchUpdate) { | |
| 1594 | + TouchEvent thisEvent(*event, _lastTouchEvent); | |
| 1595 | + _controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts | |
| 1596 | + _lastTouchEvent = thisEvent; | |
| 1597 | + } | |
| 1446 | 1598 | |
| 1447 | 1599 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1448 | 1600 | if (_controllerScriptingInterface.isTouchCaptured()) { |
| @@ -1530,6 +1682,17 @@ void Application::dropEvent(QDropEvent *event) { · 81 unmodified lines | ||
| 1530 | 1682 | } |
| 1531 | 1683 | } |
| 1532 | 1684 | |
| 1685 | +void Application::dragEnterEvent(QDragEnterEvent* event) { | |
| 1686 | + const QMimeData* mimeData = event->mimeData(); | |
| 1687 | + foreach(QUrl url, mimeData->urls()) { | |
| 1688 | + auto urlString = url.toString(); | |
| 1689 | + if (canAcceptURL(urlString)) { | |
| 1690 | + event->acceptProposedAction(); | |
| 1691 | + break; | |
| 1692 | + } | |
| 1693 | + } | |
| 1694 | +} | |
| 1695 | + | |
| 1533 | 1696 | bool Application::acceptSnapshot(const QString& urlString) { |
| 1534 | 1697 | QUrl url(urlString); |
| 1535 | 1698 | QString snapshotPath = url.toLocalFile(); |
| @@ -1767,21 +1930,21 @@ int Application::getMouseDragStartedY() const { · 231 unmodified lines | ||
| 1767 | 1930 | |
| 1768 | 1931 | FaceTracker* Application::getActiveFaceTracker() { |
| 1769 | 1932 | auto faceshift = DependencyManager::get<Faceshift>(); |
| 1770 | − auto visage = DependencyManager::get<Visage>(); | |
| 1771 | 1933 | auto dde = DependencyManager::get<DdeFaceTracker>(); |
| 1772 | 1934 | |
| 1773 | 1935 | return (dde->isActive() ? static_cast<FaceTracker*>(dde.data()) : |
| 1774 | − (faceshift->isActive() ? static_cast<FaceTracker*>(faceshift.data()) : | |
| 1775 | − (visage->isActive() ? static_cast<FaceTracker*>(visage.data()) : NULL))); | |
| 1936 | + (faceshift->isActive() ? static_cast<FaceTracker*>(faceshift.data()) : NULL)); | |
| 1776 | 1937 | } |
| 1777 | 1938 | |
| 1778 | 1939 | void Application::setActiveFaceTracker() { |
| 1779 | 1940 | #ifdef HAVE_FACESHIFT |
| 1780 | 1941 | DependencyManager::get<Faceshift>()->setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift)); |
| 1781 | −#endif | |
| 1782 | − DependencyManager::get<DdeFaceTracker>()->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::DDEFaceRegression)); | |
| 1783 | −#ifdef HAVE_VISAGE | |
| 1784 | − DependencyManager::get<Visage>()->updateEnabled(); | |
| 1942 | +#endif | |
| 1943 | +#ifdef HAVE_DDE | |
| 1944 | + bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera); | |
| 1945 | + Menu::getInstance()->getActionForOption(MenuOption::UseAudioForMouth)->setVisible(isUsingDDE); | |
| 1946 | + Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE); | |
| 1947 | + DependencyManager::get<DdeFaceTracker>()->setEnabled(isUsingDDE); | |
| 1785 | 1948 | #endif |
| 1786 | 1949 | } |
| 1787 | 1950 | |
| @@ -1958,7 +2121,6 @@ void Application::init() { · 170 unmodified lines | ||
| 1958 | 2121 | // initialize our face trackers after loading the menu settings |
| 1959 | 2122 | DependencyManager::get<Faceshift>()->init(); |
| 1960 | 2123 | DependencyManager::get<DdeFaceTracker>()->init(); |
| 1961 | − DependencyManager::get<Visage>()->init(); | |
| 1962 | 2124 | |
| 1963 | 2125 | Leapmotion::init(); |
| 1964 | 2126 | RealSense::init(); |
| @@ -2268,6 +2430,7 @@ void Application::update(float deltaTime) { · 303 unmodified lines | ||
| 2268 | 2430 | PerformanceTimer perfTimer("physics"); |
| 2269 | 2431 | _myAvatar->relayDriveKeysToCharacterController(); |
| 2270 | 2432 | _physicsEngine.stepSimulation(); |
| 2433 | + _physicsEngine.dumpStatsIfNecessary(); | |
| 2271 | 2434 | } |
| 2272 | 2435 | |
| 2273 | 2436 | if (!_aboutToQuit) { |
| @@ -2675,8 +2838,9 @@ void Application::updateShadowMap() { · 401 unmodified lines | ||
| 2675 | 2838 | activeRenderingThread = QThread::currentThread(); |
| 2676 | 2839 | |
| 2677 | 2840 | PerformanceTimer perfTimer("shadowMap"); |
| 2678 | − QOpenGLFramebufferObject* fbo = DependencyManager::get<TextureCache>()->getShadowFramebufferObject(); | |
| 2679 | − fbo->bind(); | |
| 2841 | + auto shadowFramebuffer = DependencyManager::get<TextureCache>()->getShadowFramebuffer(); | |
| 2842 | + glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(shadowFramebuffer)); | |
| 2843 | + | |
| 2680 | 2844 | glEnable(GL_DEPTH_TEST); |
| 2681 | 2845 | glClear(GL_DEPTH_BUFFER_BIT); |
| 2682 | 2846 | |
| @@ -2692,16 +2856,18 @@ void Application::updateShadowMap() { · 9 unmodified lines | ||
| 2692 | 2856 | loadViewFrustum(_myCamera, _viewFrustum); |
| 2693 | 2857 | |
| 2694 | 2858 | int matrixCount = 1; |
| 2695 | − int targetSize = fbo->width(); | |
| 2859 | + //int targetSize = fbo->width(); | |
| 2860 | + int sourceSize = shadowFramebuffer->getWidth(); | |
| 2861 | + int targetSize = shadowFramebuffer->getWidth(); | |
| 2696 | 2862 | float targetScale = 1.0f; |
| 2697 | 2863 | if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { |
| 2698 | 2864 | matrixCount = CASCADED_SHADOW_MATRIX_COUNT; |
| 2699 | − targetSize = fbo->width() / 2; | |
| 2865 | + targetSize = sourceSize / 2; | |
| 2700 | 2866 | targetScale = 0.5f; |
| 2701 | 2867 | } |
| 2702 | 2868 | for (int i = 0; i < matrixCount; i++) { |
| 2703 | 2869 | const glm::vec2& coord = MAP_COORDS[i]; |
| 2704 | − glViewport(coord.s * fbo->width(), coord.t * fbo->height(), targetSize, targetSize); | |
| 2870 | + glViewport(coord.s * sourceSize, coord.t * sourceSize, targetSize, targetSize); | |
| 2705 | 2871 | |
| 2706 | 2872 | // if simple shadow then since the resolution is twice as much as with cascaded, cover 2 regions with the map, not just one |
| 2707 | 2873 | int regionIncrement = (matrixCount == 1 ? 2 : 1); |
| @@ -2824,7 +2990,13 @@ void Application::updateShadowMap() { · 116 unmodified lines | ||
| 2824 | 2990 | glMatrixMode(GL_MODELVIEW); |
| 2825 | 2991 | } |
| 2826 | 2992 | |
| 2993 | +<<<<<<< HEAD | |
| 2827 | 2994 | fbo->release(); |
| 2995 | +======= | |
| 2996 | + // fbo->release(); | |
| 2997 | + | |
| 2998 | + glViewport(0, 0, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); | |
| 2999 | +>>>>>>> master | |
| 2828 | 3000 | activeRenderingThread = nullptr; |
| 2829 | 3001 | } |
| 2830 | 3002 | |
| @@ -2864,7 +3036,8 @@ int Application::getBoundaryLevelAdjust() const { · 33 unmodified lines | ||
| 2864 | 3036 | } |
| 2865 | 3037 | |
| 2866 | 3038 | QImage Application::renderAvatarBillboard() { |
| 2867 | − DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->bind(); | |
| 3039 | + auto primaryFramebuffer = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer(); | |
| 3040 | + glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer)); | |
| 2868 | 3041 | |
| 2869 | 3042 | // the "glow" here causes an alpha of one |
| 2870 | 3043 | Glower glower; |
| @@ -2879,7 +3052,7 @@ QImage Application::renderAvatarBillboard() { · 8 unmodified lines | ||
| 2879 | 3052 | QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); |
| 2880 | 3053 | glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); |
| 2881 | 3054 | |
| 2882 | − DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->release(); | |
| 3055 | + glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
| 2883 | 3056 | |
| 2884 | 3057 | return image; |
| 2885 | 3058 | } |
| @@ -3036,13 +3209,21 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs · 150 unmodified lines | ||
| 3036 | 3209 | PerformanceTimer perfTimer("entities"); |
| 3037 | 3210 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), |
| 3038 | 3211 | "Application::displaySide() ... entities..."); |
| 3039 | − if (renderCollisionHulls) { | |
| 3040 | − _entities.render(RenderArgs::DEBUG_RENDER_MODE, renderSide); | |
| 3041 | − } else if (theCamera.getMode() == CAMERA_MODE_MIRROR) { | |
| 3042 | − _entities.render(RenderArgs::MIRROR_RENDER_MODE, renderSide); | |
| 3043 | − } else { | |
| 3044 | − _entities.render(RenderArgs::DEFAULT_RENDER_MODE, renderSide); | |
| 3212 | + | |
| 3213 | + RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; | |
| 3214 | + RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE; | |
| 3215 | + | |
| 3216 | + if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { | |
| 3217 | + renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS); | |
| 3218 | + } | |
| 3219 | + if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) { | |
| 3220 | + renderDebugFlags = | |
| 3221 | + (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP); | |
| 3045 | 3222 | } |
| 3223 | + if (theCamera.getMode() == CAMERA_MODE_MIRROR) { | |
| 3224 | + renderMode = RenderArgs::MIRROR_RENDER_MODE; | |
| 3225 | + } | |
| 3226 | + _entities.render(renderMode, renderSide, renderDebugFlags); | |
| 3046 | 3227 | } |
| 3047 | 3228 | |
| 3048 | 3229 | // render JS/scriptable overlays |
| @@ -3071,7 +3252,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs · 22 unmodified lines | ||
| 3071 | 3252 | { |
| 3072 | 3253 | DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight()); |
| 3073 | 3254 | auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage(); |
| 3074 | − DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity()); | |
| 3255 | + DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity()); | |
| 3075 | 3256 | DependencyManager::get<DeferredLightingEffect>()->setGlobalAtmosphere(skyStage->getAtmosphere()); |
| 3076 | 3257 | |
| 3077 | 3258 | PROFILE_RANGE("DeferredLighting"); |
| @@ -3253,7 +3434,6 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 175 unmodified lines | ||
| 3253 | 3434 | |
| 3254 | 3435 | void Application::resetSensors() { |
| 3255 | 3436 | DependencyManager::get<Faceshift>()->reset(); |
| 3256 | − DependencyManager::get<Visage>()->reset(); | |
| 3257 | 3437 | DependencyManager::get<DdeFaceTracker>()->reset(); |
| 3258 | 3438 | |
| 3259 | 3439 | getActiveRenderPlugin()->resetSensors(); |
| @@ -3715,17 +3895,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { · 455 unmodified lines | ||
| 3715 | 3895 | } |
| 3716 | 3896 | |
| 3717 | 3897 | // Download the FST file, to attempt to determine its model type |
| 3718 | − QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); | |
| 3719 | − QNetworkRequest networkRequest = QNetworkRequest(url); | |
| 3720 | − networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); | |
| 3721 | − QNetworkReply* reply = networkAccessManager.get(networkRequest); | |
| 3722 | − qCDebug(interfaceapp) << "Downloading avatar file at " << url; | |
| 3723 | − QEventLoop loop; | |
| 3724 | − QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); | |
| 3725 | − loop.exec(); | |
| 3726 | − QByteArray fstContents = reply->readAll(); | |
| 3727 | − delete reply; | |
| 3728 | − QVariantHash fstMapping = FSTReader::readMapping(fstContents); | |
| 3898 | + QVariantHash fstMapping = FSTReader::downloadMapping(url); | |
| 3729 | 3899 | |
| 3730 | 3900 | FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping); |
| 3731 | 3901 | |
| @@ -3736,26 +3906,27 @@ bool Application::askToSetAvatarUrl(const QString& url) { · 4 unmodified lines | ||
| 3736 | 3906 | QPushButton* bodyButton = NULL; |
| 3737 | 3907 | QPushButton* bodyAndHeadButton = NULL; |
| 3738 | 3908 | |
| 3909 | + QString modelName = fstMapping["name"].toString(); | |
| 3739 | 3910 | QString message; |
| 3740 | 3911 | QString typeInfo; |
| 3741 | 3912 | switch (modelType) { |
| 3742 | 3913 | case FSTReader::HEAD_MODEL: |
| 3743 | − message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar head?"); | |
| 3914 | + message = QString("Would you like to use '") + modelName + QString("' for your avatar head?"); | |
| 3744 | 3915 | headButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole); |
| 3745 | 3916 | break; |
| 3746 | 3917 | |
| 3747 | 3918 | case FSTReader::BODY_ONLY_MODEL: |
| 3748 | − message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar body?"); | |
| 3919 | + message = QString("Would you like to use '") + modelName + QString("' for your avatar body?"); | |
| 3749 | 3920 | bodyButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole); |
| 3750 | 3921 | break; |
| 3751 | 3922 | |
| 3752 | 3923 | case FSTReader::HEAD_AND_BODY_MODEL: |
| 3753 | − message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar?"); | |
| 3924 | + message = QString("Would you like to use '") + modelName + QString("' for your avatar?"); | |
| 3754 | 3925 | bodyAndHeadButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole); |
| 3755 | 3926 | break; |
| 3756 | 3927 | |
| 3757 | 3928 | default: |
| 3758 | − message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for some part of your avatar head?"); | |
| 3929 | + message = QString("Would you like to use '") + modelName + QString("' for some part of your avatar head?"); | |
| 3759 | 3930 | headButton = msgBox.addButton(tr("Use for Head"), QMessageBox::ActionRole); |
| 3760 | 3931 | bodyButton = msgBox.addButton(tr("Use for Body"), QMessageBox::ActionRole); |
| 3761 | 3932 | bodyAndHeadButton = msgBox.addButton(tr("Use for Body and Head"), QMessageBox::ActionRole); |
| @@ -3768,34 +3939,18 @@ bool Application::askToSetAvatarUrl(const QString& url) { · 6 unmodified lines | ||
| 3768 | 3939 | msgBox.exec(); |
| 3769 | 3940 | |
| 3770 | 3941 | if (msgBox.clickedButton() == headButton) { |
| 3771 | − qCDebug(interfaceapp) << "Chose to use for head: " << url; | |
| 3772 | − _myAvatar->setFaceModelURL(url); | |
| 3773 | − UserActivityLogger::getInstance().changedModel("head", url); | |
| 3774 | − _myAvatar->sendIdentityPacket(); | |
| 3775 | − emit faceURLChanged(url); | |
| 3942 | + _myAvatar->useHeadURL(url, modelName); | |
| 3943 | + emit headURLChanged(url, modelName); | |
| 3776 | 3944 | } else if (msgBox.clickedButton() == bodyButton) { |
| 3777 | − qCDebug(interfaceapp) << "Chose to use for body: " << url; | |
| 3778 | − _myAvatar->setSkeletonModelURL(url); | |
| 3779 | − // if the head is empty, reset it to the default head. | |
| 3780 | − if (_myAvatar->getFaceModelURLString().isEmpty()) { | |
| 3781 | − _myAvatar->setFaceModelURL(DEFAULT_HEAD_MODEL_URL); | |
| 3782 | − emit faceURLChanged(DEFAULT_HEAD_MODEL_URL.toString()); | |
| 3783 | − UserActivityLogger::getInstance().changedModel("head", DEFAULT_HEAD_MODEL_URL.toString()); | |
| 3784 | − } | |
| 3785 | − UserActivityLogger::getInstance().changedModel("skeleton", url); | |
| 3786 | − _myAvatar->sendIdentityPacket(); | |
| 3787 | − emit skeletonURLChanged(url); | |
| 3945 | + _myAvatar->useBodyURL(url, modelName); | |
| 3946 | + emit bodyURLChanged(url, modelName); | |
| 3788 | 3947 | } else if (msgBox.clickedButton() == bodyAndHeadButton) { |
| 3789 | − qCDebug(interfaceapp) << "Chose to use for body + head: " << url; | |
| 3790 | − _myAvatar->setFaceModelURL(QString()); | |
| 3791 | − _myAvatar->setSkeletonModelURL(url); | |
| 3792 | − UserActivityLogger::getInstance().changedModel("skeleton", url); | |
| 3793 | − _myAvatar->sendIdentityPacket(); | |
| 3794 | − emit faceURLChanged(QString()); | |
| 3795 | − emit skeletonURLChanged(url); | |
| 3948 | + _myAvatar->useFullAvatarURL(url, modelName); | |
| 3949 | + emit fullAvatarURLChanged(url, modelName); | |
| 3796 | 3950 | } else { |
| 3797 | 3951 | qCDebug(interfaceapp) << "Declined to use the avatar: " << url; |
| 3798 | 3952 | } |
| 3953 | + | |
| 3799 | 3954 | return true; |
| 3800 | 3955 | } |
| 3801 | 3956 | |
| @@ -3885,6 +4040,9 @@ void Application::stopAllScripts(bool restart) { · 83 unmodified lines | ||
| 3885 | 4040 | // stops all current running scripts |
| 3886 | 4041 | for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin(); |
| 3887 | 4042 | it != _scriptEnginesHash.constEnd(); it++) { |
| 4043 | + if (it.value()->isFinished()) { | |
| 4044 | + continue; | |
| 4045 | + } | |
| 3888 | 4046 | if (restart && it.value()->isUserLoaded()) { |
| 3889 | 4047 | connect(it.value(), SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&))); |
| 3890 | 4048 | } |
| @@ -4307,10 +4465,8 @@ void Application::checkSkeleton() { · 416 unmodified lines | ||
| 4307 | 4465 | msgBox.setIcon(QMessageBox::Warning); |
| 4308 | 4466 | msgBox.exec(); |
| 4309 | 4467 | |
| 4310 | − _myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL); | |
| 4311 | − _myAvatar->sendIdentityPacket(); | |
| 4468 | + _myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default"); | |
| 4312 | 4469 | } else { |
| 4313 | − _myAvatar->updateCharacterController(); | |
| 4314 | 4470 | _physicsEngine.setCharacterController(_myAvatar->getCharacterController()); |
| 4315 | 4471 | } |
| 4316 | 4472 | } |
| @@ -4334,6 +4490,10 @@ void Application::friendsWindowClosed() { · 17 unmodified lines | ||
| 4334 | 4490 | } |
| 4335 | 4491 | |
| 4336 | 4492 | |
| 4493 | +void Application::postLambdaEvent(std::function<void()> f) { | |
| 4494 | + QCoreApplication::postEvent(this, new LambdaEvent(f)); | |
| 4495 | +} | |
| 4496 | + | |
| 4337 | 4497 | PickRay Application::computePickRay() const { |
| 4338 | 4498 | return computePickRay(getTrueMouseX(), getTrueMouseY()); |
| 4339 | 4499 | } |