diff --git a/CMakeLists.txt b/CMakeLists.txt index 25fd4731e6..ff9fbe9244 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# If we're running under the gradle build, HIFI_ANDROID will be set here, but +# If we're running under the gradle build, HIFI_ANDROID will be set here, but # ANDROID will not be set until after the `project` statement. This is the *ONLY* # place you need to use `HIFI_ANDROID` instead of `ANDROID` if (WIN32 AND NOT HIFI_ANDROID) @@ -61,8 +61,6 @@ else() endif() option(DISABLE_KTX_CACHE "Disable KTX Cache" OFF) - - set(PLATFORM_QT_GL OpenGL) if (USE_GLES) @@ -132,8 +130,8 @@ set_packaging_parameters() # FIXME hack to work on the proper Android toolchain if (ANDROID) - add_subdirectory(android/app) - return() + add_subdirectory(android/app) + return() endif() # add subdirectories for all targets @@ -148,16 +146,14 @@ if (BUILD_SERVER) endif() if (BUILD_CLIENT) - add_subdirectory(interface) - set_target_properties(interface PROPERTIES FOLDER "Apps") - if (ANDROID) - add_subdirectory(gvr-interface) - set_target_properties(gvr-interface PROPERTIES FOLDER "Apps") - endif() + add_subdirectory(interface) + set_target_properties(interface PROPERTIES FOLDER "Apps") + + option(USE_SIXENSE "Build Interface with sixense library/plugin" OFF) endif() if (BUILD_CLIENT OR BUILD_SERVER) - add_subdirectory(plugins) + add_subdirectory(plugins) endif() # BUILD_TOOLS option will be handled inside the tools's CMakeLists.txt because 'scribe' tool is required for build anyway diff --git a/INSTALL.md b/INSTALL.md index 79d7e96977..e07d28a43d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -17,6 +17,8 @@ To produce an executable installer on Windows, the following are required: - [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.0b3 - [UAC Plug-in for Nullsoft](http://nsis.sourceforge.net/UAC_plug-in) - 0.2.4c - [nsProcess Plug-in for Nullsoft](http://nsis.sourceforge.net/NsProcess_plugin) - 1.6 +- [Inetc Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0 +- [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0 Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System. diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 702636dd01..032f83e8be 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -46,9 +46,18 @@ macro(GENERATE_INSTALLERS) set(UNINSTALLER_HEADER_IMAGE "") fix_path_for_nsis(${_UNINSTALLER_HEADER_BAD_PATH} UNINSTALLER_HEADER_IMAGE) - # grab the latest VC redist (2017) and add it to the installer, our NSIS template - # will call it during the install - install(CODE "file(DOWNLOAD https://go.microsoft.com/fwlink/?LinkId=746572 \"\${CMAKE_INSTALL_PREFIX}/vcredist_x64.exe\")") + # we use external libraries that still need the 120 (VS2013) redistributables + # so we include them as well until those external libraries are updated + # to use the redistributables that match what we build our applications for + set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS + "C:/Windows/System32/msvcp120.dll" + "C:/Windows/System32/msvcr120.dll" + ) + + set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) + set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR}) + set(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT ${CLIENT_COMPONENT}) + include(InstallRequiredSystemLibraries) elseif (APPLE) # produce a drag and drop DMG on OS X set(CPACK_GENERATOR "DragNDrop") diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index d324776572..29f4617a6f 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -39,7 +39,9 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> \"$\"" + COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND}\ + ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release>\ + --no-compiler-runtime --no-opengl-sw --no-angle -no-system-d3d-compiler \"$\"" ) set(QTAUDIO_PATH "$/audio") diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index e26f81edd9..a962504e72 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -149,6 +149,8 @@ macro(SET_PACKAGING_PARAMETERS) set(CLIENT_LAUNCH_NOW_REG_KEY "ClientLaunchAfterInstall") set(SERVER_LAUNCH_NOW_REG_KEY "ServerLaunchAfterInstall") set(CUSTOM_INSTALL_REG_KEY "CustomInstall") + set(CLIENT_ID_REG_KEY "ClientGUID") + set(GA_TRACKING_ID $ENV{GA_TRACKING_ID}) endif () # setup component categories for installer diff --git a/cmake/templates/CPackProperties.cmake.in b/cmake/templates/CPackProperties.cmake.in index b91d78f628..1a0fa2fac7 100644 --- a/cmake/templates/CPackProperties.cmake.in +++ b/cmake/templates/CPackProperties.cmake.in @@ -41,6 +41,8 @@ set(CONSOLE_STARTUP_REG_KEY "@CONSOLE_STARTUP_REG_KEY@") set(SERVER_LAUNCH_NOW_REG_KEY "@SERVER_LAUNCH_NOW_REG_KEY@") set(CLIENT_LAUNCH_NOW_REG_KEY "@CLIENT_LAUNCH_NOW_REG_KEY@") set(CUSTOM_INSTALL_REG_KEY "@CUSTOM_INSTALL_REG_KEY@") +set(GA_TRACKING_ID "@GA_TRACKING_ID@") +set(CLIENT_ID_REG_KEY "@CLIENT_ID_REG_KEY@") set(INSTALLER_HEADER_IMAGE "@INSTALLER_HEADER_IMAGE@") set(UNINSTALLER_HEADER_IMAGE "@UNINSTALLER_HEADER_IMAGE@") set(ADD_REMOVE_ICON_PATH "@ADD_REMOVE_ICON_PATH@") diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index c1bfebe2c4..f174727f95 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -319,6 +319,78 @@ Function DownloadFile FunctionEnd !endif + +!include NSISpcre.nsh +!insertmacro REMatches + +Var CampaignName + +!macro GetCampaignName RetVar + Call GetCampaignName + Pop ${RetVar} +!macroend + +Function GetCampaignName + Push $0 ; Stash $0 + + ; Parse filename out of the path + ${RECaptureMatches} $0 "([^\\]*\\)*(.*)\.exe" $EXEPATH 0 + ${If} $0 == 2 + Pop $0 ; Discard Path + Pop $0 ; Recover filename + ; Parse campaign out of the filename + ${RECaptureMatches} $0 "HighFidelity-([^-]*-)Beta-.*" $0 0 + ${If} $0 == 1 + Pop $0 ; Recover campaign name + StrCpy $0 $0 -1 0 ; Remove trailing - and copy to _RetVar + ${Else} + StrCpy $0 "" + ${EndIf} + ${Else} + StrCpy $0 "" + ${EndIf} + + Exch $0 ; Restore $0 and push result +FunctionEnd + +!macro CreateGUID RetVar + System::Call 'ole32::CoCreateGuid(g .s)' + Pop ${RetVar} + ; Strip opening and closing braces + StrCpy ${RetVar} ${RetVar} -1 1 +!macroend + +Var GAClientID + +!macro InitGAClientID + ; Generate a new GUID on every run for now + !insertmacro CreateGUID $GAClientID +!macroend + +!macro GoogleAnalytics Category Action Label Value + ${If} "@GA_TRACKING_ID@" != "" + Push $0 + Push $1 + + StrCpy $0 "https://google-analytics.com/collect?v=1&tid=@GA_TRACKING_ID@" + StrCpy $0 "$0&cid=$GAClientID&t=event&ec=${Category}&ea=${Action}" + + ${If} "${Label}" != "" + StrCpy $0 "$0&el=${Label}" + ${EndIf} + ${If} "${Value}" != "" + StrCpy $0 "$0&ev=${Value}" + ${EndIf} + + GetTempFileName $1 + inetc::get /SILENT $0 $1 /END + Delete $1 + + Pop $1 + Pop $0 + ${EndIf} +!macroend + ;-------------------------------- ; Installation types @@ -342,28 +414,32 @@ SectionEnd ;-------------------------------- ;Pages + !define MUI_CUSTOMFUNCTION_ABORT OnUserAbort + + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageWelcomePre !insertmacro MUI_PAGE_WELCOME + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageLicensePre !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" Page custom InstallTypesPage ReadInstallTypes - !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageDirectoryPre !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" - - !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageStartMenuPre !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER - !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageComponentsPre @CPACK_NSIS_PAGE_COMPONENTS@ Page custom PostInstallOptionsPage ReadPostInstallOptions + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageInstallFilesPre !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_UNPAGE_CONFIRM @@ -452,8 +528,40 @@ Var CopyFromProductionCheckbox Var ExpressInstallRadioButton Var CustomInstallRadioButton Var InstallTypeDialog -Var Express Var CustomInstallTemporaryState +Var Express + +!macro MaybeSkipPage + ; Check if Express is set, if so, abort the post install options page + ${If} $Express == "1" + Abort + ${EndIf} +!macroend + +Function OnUserAbort + !insertmacro GoogleAnalytics "Installer" "Abort" "User Abort" "" +FunctionEnd +Function PageWelcomePre + !insertmacro GoogleAnalytics "Installer" "Welcome" "" "" +FunctionEnd +Function PageLicensePre + !insertmacro GoogleAnalytics "Installer" "License" "" "" +FunctionEnd +Function PageDirectoryPre + !insertmacro MaybeSkipPage + !insertmacro GoogleAnalytics "Installer" "Directory" "" "" +FunctionEnd +Function PageStartMenuPre + !insertmacro MaybeSkipPage + !insertmacro GoogleAnalytics "Installer" "StartMenu" "" "" +FunctionEnd +Function PageComponentsPre + !insertmacro MaybeSkipPage + !insertmacro GoogleAnalytics "Installer" "Components" "" "" +FunctionEnd +Function PageInstallFilesPre + !insertmacro GoogleAnalytics "Installer" "Install" "" "" +FunctionEnd !macro SetInstallOption Checkbox OptionName Default ; reads the value for the given install option to the registry @@ -472,6 +580,8 @@ Var CustomInstallTemporaryState !macroend Function InstallTypesPage + !insertmacro GoogleAnalytics "Installer" "Install Types" "" "" + !insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install" nsDialogs::Create 1018 @@ -523,14 +633,10 @@ Function ChangeCustomLabel Pop $R1 FunctionEnd -Function AbortFunction - ; Check if Express is set, if so, abort the post install options page - StrCmp $Express "1" 0 end - Abort - end: -FunctionEnd - Function PostInstallOptionsPage + !insertmacro MaybeSkipPage + !insertmacro GoogleAnalytics "Installer" "Post Install Options" "" "" + !insertmacro MUI_HEADER_TEXT "Setup Options" "" nsDialogs::Create 1018 @@ -540,11 +646,6 @@ Function PostInstallOptionsPage Abort ${EndIf} - ; Check if Express is set, if so, abort the post install options page - StrCmp $Express "1" 0 end - Abort - end: - StrCpy $CurrentOffset 0 StrCpy $OffsetUnits u @@ -837,9 +938,6 @@ Section "-Core installation" Delete "$INSTDIR\ui_resources_200_percent.pak" Delete "$INSTDIR\vccorlib120.dll" Delete "$INSTDIR\version" - Delete "$INSTDIR\msvcr140.dll" - Delete "$INSTDIR\msvcp140.dll" - Delete "$INSTDIR\vcruntime140.dll" Delete "$INSTDIR\xinput1_3.dll" ; Delete old desktop shortcuts before they were renamed during Sandbox rename @@ -858,11 +956,8 @@ Section "-Core installation" ; Rename the incorrectly cased Raleway font Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" - ExecWait "$INSTDIR\vcredist_x64.exe /install /q /norestart" - ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" - Delete "$INSTDIR\vcredist_x64.exe" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. @@ -965,6 +1060,7 @@ Section "-Core installation" ; Handle whichever post install options were set Call HandlePostInstallOptions + !insertmacro GoogleAnalytics "Installer" "Done" "" "" SectionEnd !include nsProcess.nsh @@ -979,7 +1075,7 @@ SectionEnd ${If} $R0 == 0 ; the process is running, ask the user to close it - + ${If} "${displayName}" == "@CONSOLE_DISPLAY_NAME@" MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \ "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it in the system tray and click Retry to continue." \ @@ -992,6 +1088,8 @@ SectionEnd /SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0 ${EndIf} + !insertmacro GoogleAnalytics "Installer" "Abort" "${displayName} Running" "" + ; If the user decided to cancel, stop the current installer/uninstaller Abort @@ -1219,6 +1317,11 @@ Function .onInit Quit !endif + !insertmacro InitGAClientID + !insertmacro GetCampaignName $CampaignName + + !insertmacro GoogleAnalytics "Installer" "Start" "$CampaignName" "" + ; make sure none of the installed applications are still running !insertmacro CheckForRunningApplications "installed" "Installer" ${nsProcess::Unload} diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 49877cacc3..4ce35027f2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -191,7 +191,11 @@ add_dependencies(${TARGET_NAME} resources) if (WIN32) # These are external plugins, but we need to do the 'add dependency' here so that their # binary directories get added to the fixup path - add_dependency_external_projects(sixense) + + if (USE_SIXENSE) + add_dependency_external_projects(sixense) + endif () + add_dependency_external_projects(sdl2) add_dependency_external_projects(OpenVR) add_dependency_external_projects(neuron) @@ -328,13 +332,13 @@ if (APPLE) else() # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_if_different - "${RESOURCES_RCC}" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "${RESOURCES_RCC}" "$" # FIXME, the edit script code loads HTML from the scripts folder # which in turn relies on CSS that refers to the fonts. In theory - # we should be able to modify the CSS to reference the QRC path to - # the ttf files, but doing so generates a CORS policy violation, + # we should be able to modify the CSS to reference the QRC path to + # the ttf files, but doing so generates a CORS policy violation, # so we have to retain a copy of the fonts outside of the resources binary COMMAND "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/resources/fonts" @@ -389,3 +393,6 @@ endif() add_dependency_external_projects(GifCreator) find_package(GifCreator REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GIFCREATOR_INCLUDE_DIRS}) + +# tell CMake to exclude ui_console.h for policy CMP0071 +set_property(SOURCE ui_console.h PROPERTY SKIP_AUTOMOC ON) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 370a5154c2..3ab4ab0dc7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2734,6 +2734,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); + surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); surfaceContext->setContextProperty("Selection", DependencyManager::get().data()); surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); surfaceContext->setContextProperty("Wallet", DependencyManager::get().data()); @@ -7448,10 +7449,35 @@ bool Application::isThrottleRendering() const { } bool Application::hasFocus() const { - if (_displayPlugin) { - return getActiveDisplayPlugin()->hasFocus(); + bool result = (QApplication::activeWindow() != nullptr); +#if defined(Q_OS_WIN) + // On Windows, QWidget::activateWindow() - as called in setFocus() - makes the application's taskbar icon flash but doesn't + // take user focus away from their current window. So also check whether the application is the user's current foreground + // window. + result = result && (HWND)QApplication::activeWindow()->winId() == GetForegroundWindow(); +#endif + return result; +} + +void Application::setFocus() { + // Note: Windows doesn't allow a user focus to be taken away from another application. Instead, it changes the color of and + // flashes the taskbar icon. + auto window = qApp->getWindow(); + window->activateWindow(); +} + +void Application::raise() { + auto windowState = qApp->getWindow()->windowState(); + if (windowState & Qt::WindowMinimized) { + if (windowState & Qt::WindowMaximized) { + qApp->getWindow()->showMaximized(); + } else if (windowState & Qt::WindowFullScreen) { + qApp->getWindow()->showFullScreen(); + } else { + qApp->getWindow()->showNormal(); + } } - return (QApplication::activeWindow() != nullptr); + qApp->getWindow()->raise(); } void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 597641e769..00fd07b84e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -161,6 +161,8 @@ public: QRect getRecommendedHUDRect() const; glm::vec2 getDeviceSize() const; bool hasFocus() const; + void setFocus(); + void raise(); void showCursor(const Cursor::Icon& cursor); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index b96cc229ae..78f55f0d6e 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -74,16 +74,14 @@ QScriptValue WindowScriptingInterface::hasFocus() { void WindowScriptingInterface::setFocus() { // It's forbidden to call focus() from another thread. qApp->postLambdaEvent([] { - auto window = qApp->getWindow(); - window->activateWindow(); - window->setFocus(); + qApp->setFocus(); }); } void WindowScriptingInterface::raiseMainWindow() { // It's forbidden to call raise() from another thread. qApp->postLambdaEvent([] { - qApp->getWindow()->raise(); + qApp->raise(); }); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 444f11749d..997e425e53 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -62,13 +62,14 @@ public slots: QScriptValue hasFocus(); /**jsdoc - * Make the Interface window have focus. + * Make the Interface window have focus. On Windows, if Interface doesn't already have focus, the task bar icon flashes to + * indicate that Interface wants attention but focus isn't taken away from the application that the user is using. * @function Window.setFocus */ void setFocus(); /**jsdoc - * Raise the Interface window if it is minimized, and give it focus. + * Raise the Interface window if it is minimized. If raised, the window gains focus. * @function Window.raiseMainWindow */ void raiseMainWindow(); diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 57d1cc0acf..10050c94d0 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -63,6 +63,19 @@ static const float OPAQUE_ALPHA_THRESHOLD = 0.99f; const QString Web3DOverlay::TYPE = "web3d"; const QString Web3DOverlay::QML = "Web3DOverlay.qml"; + +static auto qmlSurfaceDeleter = [](OffscreenQmlSurface* surface) { + AbstractViewStateInterface::instance()->postLambdaEvent([surface] { + if (AbstractViewStateInterface::instance()->isAboutToQuit()) { + // WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown + // if the application has already stopped its event loop, delete must be explicit + delete surface; + } else { + surface->deleteLater(); + } + }); +}; + Web3DOverlay::Web3DOverlay() { _touchDevice.setCapabilities(QTouchDevice::Position); _touchDevice.setType(QTouchDevice::TouchScreen); @@ -75,7 +88,8 @@ Web3DOverlay::Web3DOverlay() { connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface); //need to be intialized before Tablet 1st open - _webSurface = DependencyManager::get()->acquire(_url); + _webSurface = DependencyManager::get()->acquire(QML); + _cachedWebSurface = true; _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED @@ -114,6 +128,7 @@ void Web3DOverlay::destroyWebSurface() { if (!_webSurface) { return; } + QQuickItem* rootItem = _webSurface->getRootItem(); if (rootItem && rootItem->objectName() == "tabletRoot") { @@ -135,10 +150,15 @@ void Web3DOverlay::destroyWebSurface() { QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); - auto offscreenCache = DependencyManager::get(); - // FIXME prevents crash on shutdown, but we shoudln't have to do this check - if (offscreenCache) { - offscreenCache->release(QML, _webSurface); + + // If the web surface was fetched out of the cache, release it back into the cache + if (_cachedWebSurface) { + auto offscreenCache = DependencyManager::get(); + // FIXME prevents crash on shutdown, but we shoudln't have to do this check + if (offscreenCache) { + offscreenCache->release(QML, _webSurface); + } + _cachedWebSurface = false; } _webSurface.reset(); } @@ -147,6 +167,8 @@ void Web3DOverlay::buildWebSurface() { if (_webSurface) { return; } + // FIXME the context save here is most likely unecessary since the QML surfaces now render + // off the main thread, and all GL context work is done off the main thread (I *think*) gl::withSavedContext([&] { // FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces // and the current rendering load) @@ -156,10 +178,13 @@ void Web3DOverlay::buildWebSurface() { if (isWebContent()) { _webSurface = DependencyManager::get()->acquire(QML); + _cachedWebSurface = true; _webSurface->getRootItem()->setProperty("url", _url); _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); } else { - _webSurface = DependencyManager::get()->acquire(_url); + _webSurface = QSharedPointer(new OffscreenQmlSurface(), qmlSurfaceDeleter); + _webSurface->load(_url); + _cachedWebSurface = false; setupQmlSurface(); } _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition())); diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 4098e98488..d888424cbc 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -88,6 +88,7 @@ private: InputMode _inputMode { Touch }; QSharedPointer _webSurface; + bool _cachedWebSurface{ false }; gpu::TexturePointer _texture; QString _url; QString _scriptURL; diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 6a19a34727..47a213cf71 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -21,10 +21,6 @@ glm::uvec2 NullDisplayPlugin::getRecommendedRenderSize() const { return glm::uvec2(100, 100); } -bool NullDisplayPlugin::hasFocus() const { - return false; -} - void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) { if (frame) { _gpuContext->consumeFrameUpdates(frame); diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index 97b71b5780..11563b3798 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -16,7 +16,6 @@ public: grouping getGrouping() const override { return DEVELOPER; } glm::uvec2 getRecommendedRenderSize() const override; - bool hasFocus() const override; void submitFrame(const gpu::FramePointer& newFrame) override; QImage getScreenshot(float aspectRatio = 0.0f) const override; QImage getSecondaryCameraScreenshot() const override; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index db9b86b9dd..9597be7e53 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -831,11 +831,6 @@ glm::uvec2 OpenGLDisplayPlugin::getSurfaceSize() const { return result; } -bool OpenGLDisplayPlugin::hasFocus() const { - auto window = _container->getPrimaryWidget(); - return window ? window->hasFocus() : false; -} - void OpenGLDisplayPlugin::assertNotPresentThread() const { Q_ASSERT(QThread::currentThread() != _presentThread); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index bde7984ec0..1cc060161b 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -98,8 +98,6 @@ protected: virtual void compositePointer(); virtual void compositeExtra() {}; - virtual bool hasFocus() const override; - // These functions must only be called on the presentation thread virtual void customizeContext(); virtual void uncustomizeContext(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 52b9364e98..bd00ded12d 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -239,7 +239,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { } bool WebEntityRenderer::hasWebSurface() { - return (bool)_webSurface; + return (bool)_webSurface && _webSurface->getRootItem(); } bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) { @@ -303,7 +303,7 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) { _fadeStartTime = usecTimestampNow(); _webSurface->resume(); - return true; + return _webSurface->getRootItem(); } void WebEntityRenderer::destroyWebSurface() { diff --git a/libraries/entities/src/MaterialEntityItem.cpp b/libraries/entities/src/MaterialEntityItem.cpp index 44ef34a3a4..137d6ef396 100644 --- a/libraries/entities/src/MaterialEntityItem.cpp +++ b/libraries/entities/src/MaterialEntityItem.cpp @@ -157,7 +157,7 @@ void MaterialEntityItem::setMaterialURL(const QString& materialURLString, bool u } if (usingUserData) { - _parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(getUserData().toUtf8())); + _parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(getUserData().toUtf8()), materialURLString); // Since our material changed, the current name might not be valid anymore, so we need to update setCurrentMaterialName(_currentMaterialName); diff --git a/libraries/model-networking/src/model-networking/MaterialCache.cpp b/libraries/model-networking/src/model-networking/MaterialCache.cpp index cf3e255e0c..f0cbfc914a 100644 --- a/libraries/model-networking/src/model-networking/MaterialCache.cpp +++ b/libraries/model-networking/src/model-networking/MaterialCache.cpp @@ -20,7 +20,7 @@ void NetworkMaterialResource::downloadFinished(const QByteArray& data) { parsedMaterials.reset(); if (_url.toString().contains(".json")) { - parsedMaterials = parseJSONMaterials(QJsonDocument::fromJson(data)); + parsedMaterials = parseJSONMaterials(QJsonDocument::fromJson(data), _url); } // TODO: parse other material types @@ -75,7 +75,7 @@ bool NetworkMaterialResource::parseJSONColor(const QJsonValue& array, glm::vec3& * @property {number} materialVersion=1 - The version of the material. Currently not used. * @property {Material|Material[]} materials - The details of the material or materials. */ -NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMaterials(const QJsonDocument& materialJSON) { +NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl) { ParsedMaterials toReturn; if (!materialJSON.isNull() && materialJSON.isObject()) { QJsonObject materialJSONObject = materialJSON.object(); @@ -91,13 +91,13 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater QJsonArray materials = materialsValue.toArray(); for (auto material : materials) { if (!material.isNull() && material.isObject()) { - auto parsedMaterial = parseJSONMaterial(material.toObject()); + auto parsedMaterial = parseJSONMaterial(material.toObject(), baseUrl); toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second; toReturn.names.push_back(parsedMaterial.first); } } } else if (materialsValue.isObject()) { - auto parsedMaterial = parseJSONMaterial(materialsValue.toObject()); + auto parsedMaterial = parseJSONMaterial(materialsValue.toObject(), baseUrl); toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second; toReturn.names.push_back(parsedMaterial.first); } @@ -138,7 +138,7 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * @property {string} lightMap - URL of light map texture image. Currently not used. */ // Note: See MaterialEntityItem.h for default values used in practice. -std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON) { +std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) { std::string name = ""; std::shared_ptr material = std::make_shared(); for (auto& key : materialJSON.keys()) { @@ -199,57 +199,58 @@ std::pair> NetworkMaterialResource } else if (key == "albedoMap") { auto value = materialJSON.value(key); if (value.isString()) { + QString urlString = value.toString(); bool useAlphaChannel = false; auto opacityMap = materialJSON.find("opacityMap"); - if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == value.toString()) { + if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == urlString) { useAlphaChannel = true; } - material->setAlbedoMap(value.toString(), useAlphaChannel); + material->setAlbedoMap(baseUrl.resolved(urlString), useAlphaChannel); } } else if (key == "roughnessMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setRoughnessMap(value.toString(), false); + material->setRoughnessMap(baseUrl.resolved(value.toString()), false); } } else if (key == "glossMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setRoughnessMap(value.toString(), true); + material->setRoughnessMap(baseUrl.resolved(value.toString()), true); } } else if (key == "metallicMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setMetallicMap(value.toString(), false); + material->setMetallicMap(baseUrl.resolved(value.toString()), false); } } else if (key == "specularMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setMetallicMap(value.toString(), true); + material->setMetallicMap(baseUrl.resolved(value.toString()), true); } } else if (key == "normalMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setNormalMap(value.toString(), false); + material->setNormalMap(baseUrl.resolved(value.toString()), false); } } else if (key == "bumpMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setNormalMap(value.toString(), true); + material->setNormalMap(baseUrl.resolved(value.toString()), true); } } else if (key == "occlusionMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setOcclusionMap(value.toString()); + material->setOcclusionMap(baseUrl.resolved(value.toString())); } } else if (key == "scatteringMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setScatteringMap(value.toString()); + material->setScatteringMap(baseUrl.resolved(value.toString())); } } else if (key == "lightMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setLightmapMap(value.toString()); + material->setLightmapMap(baseUrl.resolved(value.toString())); } } } diff --git a/libraries/model-networking/src/model-networking/MaterialCache.h b/libraries/model-networking/src/model-networking/MaterialCache.h index 468a12c677..074cd6c98d 100644 --- a/libraries/model-networking/src/model-networking/MaterialCache.h +++ b/libraries/model-networking/src/model-networking/MaterialCache.h @@ -37,8 +37,8 @@ public: ParsedMaterials parsedMaterials; - static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON); - static std::pair> parseJSONMaterial(const QJsonObject& materialJSON); + static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl); + static std::pair> parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl); private: static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index b253ac4af3..f17cdbb7e8 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -556,58 +556,58 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, im return nullptr; } -void NetworkMaterial::setAlbedoMap(const QString& url, bool useAlphaChannel) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); +void NetworkMaterial::setAlbedoMap(const QUrl& url, bool useAlphaChannel) { + auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); if (map) { map->setUseAlphaChannel(useAlphaChannel); setTextureMap(MapChannel::ALBEDO_MAP, map); } } -void NetworkMaterial::setNormalMap(const QString& url, bool isBumpmap) { - auto map = fetchTextureMap(QUrl(url), isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP); +void NetworkMaterial::setNormalMap(const QUrl& url, bool isBumpmap) { + auto map = fetchTextureMap(url, isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP); if (map) { setTextureMap(MapChannel::NORMAL_MAP, map); } } -void NetworkMaterial::setRoughnessMap(const QString& url, bool isGloss) { - auto map = fetchTextureMap(QUrl(url), isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); +void NetworkMaterial::setRoughnessMap(const QUrl& url, bool isGloss) { + auto map = fetchTextureMap(url, isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); if (map) { setTextureMap(MapChannel::ROUGHNESS_MAP, map); } } -void NetworkMaterial::setMetallicMap(const QString& url, bool isSpecular) { - auto map = fetchTextureMap(QUrl(url), isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP); +void NetworkMaterial::setMetallicMap(const QUrl& url, bool isSpecular) { + auto map = fetchTextureMap(url, isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP); if (map) { setTextureMap(MapChannel::METALLIC_MAP, map); } } -void NetworkMaterial::setOcclusionMap(const QString& url) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); +void NetworkMaterial::setOcclusionMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); if (map) { setTextureMap(MapChannel::OCCLUSION_MAP, map); } } -void NetworkMaterial::setEmissiveMap(const QString& url) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); +void NetworkMaterial::setEmissiveMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); if (map) { setTextureMap(MapChannel::EMISSIVE_MAP, map); } } -void NetworkMaterial::setScatteringMap(const QString& url) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP); +void NetworkMaterial::setScatteringMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP); if (map) { setTextureMap(MapChannel::SCATTERING_MAP, map); } } -void NetworkMaterial::setLightmapMap(const QString& url) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); +void NetworkMaterial::setLightmapMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); if (map) { //map->setTextureTransform(_lightmapTransform); //map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index bbb00d72eb..4dfa8b17ea 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -164,14 +164,14 @@ public: NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); NetworkMaterial(const NetworkMaterial& material); - void setAlbedoMap(const QString& url, bool useAlphaChannel); - void setNormalMap(const QString& url, bool isBumpmap); - void setRoughnessMap(const QString& url, bool isGloss); - void setMetallicMap(const QString& url, bool isSpecular); - void setOcclusionMap(const QString& url); - void setEmissiveMap(const QString& url); - void setScatteringMap(const QString& url); - void setLightmapMap(const QString& url); + void setAlbedoMap(const QUrl& url, bool useAlphaChannel); + void setNormalMap(const QUrl& url, bool isBumpmap); + void setRoughnessMap(const QUrl& url, bool isGloss); + void setMetallicMap(const QUrl& url, bool isSpecular); + void setOcclusionMap(const QUrl& url); + void setEmissiveMap(const QUrl& url); + void setScatteringMap(const QUrl& url); + void setLightmapMap(const QUrl& url); protected: friend class Geometry; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index e95084df52..ff6801109e 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -140,9 +140,6 @@ public: virtual void setContext(const gpu::ContextPointer& context) final { _gpuContext = context; } virtual void submitFrame(const gpu::FramePointer& newFrame) = 0; - // Does the rendering surface have current focus? - virtual bool hasFocus() const = 0; - // The size of the rendering target (may be larger than the device size due to distortion) virtual glm::uvec2 getRecommendedRenderSize() const = 0; diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 7fece45b2f..319b6ad415 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -8,6 +8,9 @@ include_hifi_library_headers(audio) include_hifi_library_headers(networking) include_hifi_library_headers(octree) +# tell CMake to exclude qrc_fonts.cpp for policy CMP0071 +set_property(SOURCE qrc_fonts.cpp PROPERTY SKIP_AUTOMOC ON) + if (NOT ANDROID) target_nsight() endif () diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index ecfb4847e4..5c64d4b584 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -47,6 +47,8 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr } } +const graphics::MaterialPointer MeshPartPayload::DEFAULT_MATERIAL = std::make_shared(); + MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, graphics::MaterialPointer material) { updateMeshPart(mesh, partIndex); addMaterial(graphics::MaterialLayer(material, 0)); @@ -99,7 +101,7 @@ void MeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShad builder.withSubMetaCulled(); } - if (_drawMaterials.top().material) { + if (topMaterialExists()) { auto matKey = _drawMaterials.top().material->getKey(); if (matKey.isTranslucent()) { builder.withTransparent(); @@ -119,7 +121,7 @@ Item::Bound MeshPartPayload::getBound() const { ShapeKey MeshPartPayload::getShapeKey() const { graphics::MaterialKey drawMaterialKey; - if (_drawMaterials.top().material) { + if (topMaterialExists()) { drawMaterialKey = _drawMaterials.top().material->getKey(); } @@ -171,7 +173,7 @@ void MeshPartPayload::render(RenderArgs* args) { bindMesh(batch); // apply material properties - RenderPipelines::bindMaterial(_drawMaterials.top().material, batch, args->_enableTexturing); + RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing); args->_details._materialSwitches++; // Draw! @@ -356,7 +358,7 @@ void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCas builder.withDeformed(); } - if (_drawMaterials.top().material) { + if (topMaterialExists()) { auto matKey = _drawMaterials.top().material->getKey(); if (matKey.isTranslucent()) { builder.withTransparent(); @@ -387,7 +389,7 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe } graphics::MaterialKey drawMaterialKey; - if (_drawMaterials.top().material) { + if (topMaterialExists()) { drawMaterialKey = _drawMaterials.top().material->getKey(); } @@ -469,7 +471,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { bindMesh(batch); // apply material properties - RenderPipelines::bindMaterial(_drawMaterials.top().material, batch, args->_enableTexturing); + RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing); args->_details._materialSwitches++; // Draw! diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 087550345d..08ad7a8311 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -65,15 +65,18 @@ public: graphics::Mesh::Part _drawPart; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } - size_t getMaterialTextureSize() { return _drawMaterials.top().material ? _drawMaterials.top().material->getTextureSize() : 0; } - int getMaterialTextureCount() { return _drawMaterials.top().material ? _drawMaterials.top().material->getTextureCount() : 0; } - bool hasTextureInfo() const { return _drawMaterials.top().material ? _drawMaterials.top().material->hasTextureInfo() : false; } + size_t getMaterialTextureSize() { return topMaterialExists() ? _drawMaterials.top().material->getTextureSize() : 0; } + int getMaterialTextureCount() { return topMaterialExists() ? _drawMaterials.top().material->getTextureCount() : 0; } + bool hasTextureInfo() const { return topMaterialExists() ? _drawMaterials.top().material->hasTextureInfo() : false; } void addMaterial(graphics::MaterialLayer material); void removeMaterial(graphics::MaterialPointer material); protected: + static const graphics::MaterialPointer DEFAULT_MATERIAL; render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() }; + + bool topMaterialExists() const { return !_drawMaterials.empty() && _drawMaterials.top().material; } }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2617ef12ad..70f873734a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1604,7 +1604,8 @@ void Model::createVisibleRenderItemSet() { int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); - _modelMeshMaterialNames.push_back(getGeometry()->getShapeMaterial(shapeID)->getName()); + auto material = getGeometry()->getShapeMaterial(shapeID); + _modelMeshMaterialNames.push_back(material ? material->getName() : ""); _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i }); shapeID++; } diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 06cf929368..4a0f52e272 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -20,8 +20,12 @@ if (NOT SERVER_ONLY AND NOT ANDROID) add_subdirectory(${DIR}) set(DIR "oculusLegacy") add_subdirectory(${DIR}) - set(DIR "hifiSixense") - add_subdirectory(${DIR}) + + if (USE_SIXENSE) + set(DIR "hifiSixense") + add_subdirectory(${DIR}) + endif() + set(DIR "hifiSpacemouse") add_subdirectory(${DIR}) set(DIR "hifiNeuron") diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 638cadf574..893b7f48b1 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -19,7 +19,7 @@ if (WIN32 AND (NOT USE_GLES)) set(TARGET_NAME oculus) setup_hifi_plugin(Multimedia) link_hifi_libraries( - shared task gl gpu gpu-gl controllers ui qml + shared task gl gpu ${PLATFORM_GL_BACKEND} controllers ui qml plugins ui-plugins display-plugins input-plugins audio-client networking render-utils ${PLATFORM_GL_BACKEND} diff --git a/scripts/developer/tests/webOverlayTool.js b/scripts/developer/tests/webOverlayTool.js new file mode 100644 index 0000000000..1a3aa35205 --- /dev/null +++ b/scripts/developer/tests/webOverlayTool.js @@ -0,0 +1,102 @@ +// webSpawnTool.js +// +// Stress tests the rendering of web surfaces over time +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +SPAWNER = function (properties) { + properties = properties || {}; + var RADIUS = properties.radius || 5.0; // Spawn within this radius (square) + var SPAWN_COUNT = properties.count || 10000; // number of entities to spawn + var SPAWN_LIMIT = properties.spawnLimit || 1; + var SPAWN_INTERVAL = properties.spawnInterval || properties.interval || 2; + var SPAWN_LIFETIME = properties.lifetime || 10; // Entity timeout (when/if we crash, we need the entities to delete themselves) + + function randomPositionXZ(center, radius) { + return { + x: center.x + (Math.random() * radius * 2.0) - radius, + y: center.y, + z: center.z + (Math.random() * radius * 2.0) - radius + }; + } + + function makeObject(properties) { + + var overlay = Overlays.addOverlay("web3d", { + name: "Web", + url: "https://www.reddit.com/r/random", + localPosition: randomPositionXZ( { x: 0, y: 0, z: -1 }, 1), + localRotation: Quat.angleAxis(180, Vec3.Y_AXIS), + dimensions: {x: .8, y: .45, z: 0.1}, + color: { red: 255, green: 255, blue: 255 }, + alpha: 1.0, + showKeyboardFocusHighlight: false, + visible: true + }); + + var now = Date.now(); + + return { + destroy: function () { + Overlays.deleteOverlay(overlay) + }, + getAge: function () { + return (Date.now() - now) / 1000.0; + } + }; + } + + + var items = []; + var toCreate = 0; + var spawned = 0; + var spawnTimer = 0.0; + var keepAliveTimer = 0.0; + + function clear () { + } + + function create() { + toCreate = SPAWN_COUNT; + Script.update.connect(spawn); + } + + function spawn(dt) { + if (toCreate <= 0) { + Script.update.disconnect(spawn); + print("Finished spawning"); + } + else if ((spawnTimer -= dt) < 0.0){ + spawnTimer = SPAWN_INTERVAL; + + var n = Math.min(toCreate, SPAWN_LIMIT); + print("Spawning " + n + " items (" + (spawned += n) + ")"); + + toCreate -= n; + for (; n > 0; --n) { + items.push(makeObject()); + } + } + } + + function despawn() { + print("despawning"); + items.forEach(function (item) { + item.destroy(); + }); + item = []; + } + + function init () { + Script.update.disconnect(init); + Script.scriptEnding.connect(despawn); + clear(); + create(); + } + + Script.update.connect(init); +}; + +SPAWNER(); diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 66ca28f7db..fd4d8d88dd 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -14,7 +14,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries link_hifi_libraries( shared task networking animation - ktx image octree gl gpu gpu-gl + ktx image octree gl gpu ${PLATFORM_GL_BACKEND} render render-utils graphics fbx model-networking graphics-scripting entities entities-renderer audio avatars script-engine