Merge remote-tracking branch 'hifi/master' into android_ui_not_move_camera

This commit is contained in:
Cristian Duarte 2018-03-20 18:52:20 -03:00
commit d7081b261b
28 changed files with 412 additions and 144 deletions

View file

@ -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* # ANDROID will not be set until after the `project` statement. This is the *ONLY*
# place you need to use `HIFI_ANDROID` instead of `ANDROID` # place you need to use `HIFI_ANDROID` instead of `ANDROID`
if (WIN32 AND NOT HIFI_ANDROID) if (WIN32 AND NOT HIFI_ANDROID)
@ -61,8 +61,6 @@ else()
endif() endif()
option(DISABLE_KTX_CACHE "Disable KTX Cache" OFF) option(DISABLE_KTX_CACHE "Disable KTX Cache" OFF)
set(PLATFORM_QT_GL OpenGL) set(PLATFORM_QT_GL OpenGL)
if (USE_GLES) if (USE_GLES)
@ -132,8 +130,8 @@ set_packaging_parameters()
# FIXME hack to work on the proper Android toolchain # FIXME hack to work on the proper Android toolchain
if (ANDROID) if (ANDROID)
add_subdirectory(android/app) add_subdirectory(android/app)
return() return()
endif() endif()
# add subdirectories for all targets # add subdirectories for all targets
@ -148,16 +146,14 @@ if (BUILD_SERVER)
endif() endif()
if (BUILD_CLIENT) if (BUILD_CLIENT)
add_subdirectory(interface) add_subdirectory(interface)
set_target_properties(interface PROPERTIES FOLDER "Apps") set_target_properties(interface PROPERTIES FOLDER "Apps")
if (ANDROID)
add_subdirectory(gvr-interface) option(USE_SIXENSE "Build Interface with sixense library/plugin" OFF)
set_target_properties(gvr-interface PROPERTIES FOLDER "Apps")
endif()
endif() endif()
if (BUILD_CLIENT OR BUILD_SERVER) if (BUILD_CLIENT OR BUILD_SERVER)
add_subdirectory(plugins) add_subdirectory(plugins)
endif() endif()
# BUILD_TOOLS option will be handled inside the tools's CMakeLists.txt because 'scribe' tool is required for build anyway # BUILD_TOOLS option will be handled inside the tools's CMakeLists.txt because 'scribe' tool is required for build anyway

View file

@ -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 - [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 - [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 - [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. Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.

View file

@ -4,7 +4,7 @@ cmake_policy(SET CMP0046 OLD)
include(ExternalProject) include(ExternalProject)
set(QUAZIP_CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=<INSTALL_DIR>/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON) set(QUAZIP_CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=<INSTALL_DIR>/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON)
if (APPLE) if (APPLE)
else () else ()

View file

@ -46,9 +46,18 @@ macro(GENERATE_INSTALLERS)
set(UNINSTALLER_HEADER_IMAGE "") set(UNINSTALLER_HEADER_IMAGE "")
fix_path_for_nsis(${_UNINSTALLER_HEADER_BAD_PATH} 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 # we use external libraries that still need the 120 (VS2013) redistributables
# will call it during the install # so we include them as well until those external libraries are updated
install(CODE "file(DOWNLOAD https://go.microsoft.com/fwlink/?LinkId=746572 \"\${CMAKE_INSTALL_PREFIX}/vcredist_x64.exe\")") # 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) elseif (APPLE)
# produce a drag and drop DMG on OS X # produce a drag and drop DMG on OS X
set(CPACK_GENERATOR "DragNDrop") set(CPACK_GENERATOR "DragNDrop")

View file

@ -39,7 +39,9 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT)
add_custom_command( add_custom_command(
TARGET ${TARGET_NAME} TARGET ${TARGET_NAME}
POST_BUILD POST_BUILD
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> \"$<TARGET_FILE:${TARGET_NAME}>\"" COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND}\
${EXTRA_DEPLOY_OPTIONS} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release>\
--no-compiler-runtime --no-opengl-sw --no-angle -no-system-d3d-compiler \"$<TARGET_FILE:${TARGET_NAME}>\""
) )
set(QTAUDIO_PATH "$<TARGET_FILE_DIR:${TARGET_NAME}>/audio") set(QTAUDIO_PATH "$<TARGET_FILE_DIR:${TARGET_NAME}>/audio")

View file

@ -149,6 +149,8 @@ macro(SET_PACKAGING_PARAMETERS)
set(CLIENT_LAUNCH_NOW_REG_KEY "ClientLaunchAfterInstall") set(CLIENT_LAUNCH_NOW_REG_KEY "ClientLaunchAfterInstall")
set(SERVER_LAUNCH_NOW_REG_KEY "ServerLaunchAfterInstall") set(SERVER_LAUNCH_NOW_REG_KEY "ServerLaunchAfterInstall")
set(CUSTOM_INSTALL_REG_KEY "CustomInstall") set(CUSTOM_INSTALL_REG_KEY "CustomInstall")
set(CLIENT_ID_REG_KEY "ClientGUID")
set(GA_TRACKING_ID $ENV{GA_TRACKING_ID})
endif () endif ()
# setup component categories for installer # setup component categories for installer

View file

@ -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(SERVER_LAUNCH_NOW_REG_KEY "@SERVER_LAUNCH_NOW_REG_KEY@")
set(CLIENT_LAUNCH_NOW_REG_KEY "@CLIENT_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(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(INSTALLER_HEADER_IMAGE "@INSTALLER_HEADER_IMAGE@")
set(UNINSTALLER_HEADER_IMAGE "@UNINSTALLER_HEADER_IMAGE@") set(UNINSTALLER_HEADER_IMAGE "@UNINSTALLER_HEADER_IMAGE@")
set(ADD_REMOVE_ICON_PATH "@ADD_REMOVE_ICON_PATH@") set(ADD_REMOVE_ICON_PATH "@ADD_REMOVE_ICON_PATH@")

View file

@ -319,6 +319,78 @@ Function DownloadFile
FunctionEnd FunctionEnd
!endif !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 ; Installation types
@ -342,28 +414,32 @@ SectionEnd
;-------------------------------- ;--------------------------------
;Pages ;Pages
!define MUI_CUSTOMFUNCTION_ABORT OnUserAbort
!define MUI_PAGE_CUSTOMFUNCTION_PRE PageWelcomePre
!insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_WELCOME
!define MUI_PAGE_CUSTOMFUNCTION_PRE PageLicensePre
!insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
Page custom InstallTypesPage ReadInstallTypes Page custom InstallTypesPage ReadInstallTypes
!define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction !define MUI_PAGE_CUSTOMFUNCTION_PRE PageDirectoryPre
!insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_DIRECTORY
;Start Menu Folder Page Configuration ;Start Menu Folder Page Configuration
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!define MUI_PAGE_CUSTOMFUNCTION_PRE PageStartMenuPre
!define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
!define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction !define MUI_PAGE_CUSTOMFUNCTION_PRE PageComponentsPre
@CPACK_NSIS_PAGE_COMPONENTS@ @CPACK_NSIS_PAGE_COMPONENTS@
Page custom PostInstallOptionsPage ReadPostInstallOptions Page custom PostInstallOptionsPage ReadPostInstallOptions
!define MUI_PAGE_CUSTOMFUNCTION_PRE PageInstallFilesPre
!insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_CONFIRM
@ -452,8 +528,40 @@ Var CopyFromProductionCheckbox
Var ExpressInstallRadioButton Var ExpressInstallRadioButton
Var CustomInstallRadioButton Var CustomInstallRadioButton
Var InstallTypeDialog Var InstallTypeDialog
Var Express
Var CustomInstallTemporaryState 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 !macro SetInstallOption Checkbox OptionName Default
; reads the value for the given install option to the registry ; reads the value for the given install option to the registry
@ -472,6 +580,8 @@ Var CustomInstallTemporaryState
!macroend !macroend
Function InstallTypesPage Function InstallTypesPage
!insertmacro GoogleAnalytics "Installer" "Install Types" "" ""
!insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install" !insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install"
nsDialogs::Create 1018 nsDialogs::Create 1018
@ -523,14 +633,10 @@ Function ChangeCustomLabel
Pop $R1 Pop $R1
FunctionEnd 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 Function PostInstallOptionsPage
!insertmacro MaybeSkipPage
!insertmacro GoogleAnalytics "Installer" "Post Install Options" "" ""
!insertmacro MUI_HEADER_TEXT "Setup Options" "" !insertmacro MUI_HEADER_TEXT "Setup Options" ""
nsDialogs::Create 1018 nsDialogs::Create 1018
@ -540,11 +646,6 @@ Function PostInstallOptionsPage
Abort Abort
${EndIf} ${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 $CurrentOffset 0
StrCpy $OffsetUnits u StrCpy $OffsetUnits u
@ -837,9 +938,6 @@ Section "-Core installation"
Delete "$INSTDIR\ui_resources_200_percent.pak" Delete "$INSTDIR\ui_resources_200_percent.pak"
Delete "$INSTDIR\vccorlib120.dll" Delete "$INSTDIR\vccorlib120.dll"
Delete "$INSTDIR\version" Delete "$INSTDIR\version"
Delete "$INSTDIR\msvcr140.dll"
Delete "$INSTDIR\msvcp140.dll"
Delete "$INSTDIR\vcruntime140.dll"
Delete "$INSTDIR\xinput1_3.dll" Delete "$INSTDIR\xinput1_3.dll"
; Delete old desktop shortcuts before they were renamed during Sandbox rename ; 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 the incorrectly cased Raleway font
Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" 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) ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console)
RMDir /r "$INSTDIR\Interface" RMDir /r "$INSTDIR\Interface"
Delete "$INSTDIR\vcredist_x64.exe"
;Use the entire tree produced by the INSTALL target. Keep the ;Use the entire tree produced by the INSTALL target. Keep the
;list of directories here in sync with the RMDir commands below. ;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 ; Handle whichever post install options were set
Call HandlePostInstallOptions Call HandlePostInstallOptions
!insertmacro GoogleAnalytics "Installer" "Done" "" ""
SectionEnd SectionEnd
!include nsProcess.nsh !include nsProcess.nsh
@ -979,7 +1075,7 @@ SectionEnd
${If} $R0 == 0 ${If} $R0 == 0
; the process is running, ask the user to close it ; the process is running, ask the user to close it
${If} "${displayName}" == "@CONSOLE_DISPLAY_NAME@" ${If} "${displayName}" == "@CONSOLE_DISPLAY_NAME@"
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \ 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." \ "${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 /SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0
${EndIf} ${EndIf}
!insertmacro GoogleAnalytics "Installer" "Abort" "${displayName} Running" ""
; If the user decided to cancel, stop the current installer/uninstaller ; If the user decided to cancel, stop the current installer/uninstaller
Abort Abort
@ -1219,6 +1317,11 @@ Function .onInit
Quit Quit
!endif !endif
!insertmacro InitGAClientID
!insertmacro GetCampaignName $CampaignName
!insertmacro GoogleAnalytics "Installer" "Start" "$CampaignName" ""
; make sure none of the installed applications are still running ; make sure none of the installed applications are still running
!insertmacro CheckForRunningApplications "installed" "Installer" !insertmacro CheckForRunningApplications "installed" "Installer"
${nsProcess::Unload} ${nsProcess::Unload}

View file

@ -20,6 +20,7 @@
#include <QJsonArray> #include <QJsonArray>
#include <QProcess> #include <QProcess>
#include <QSharedMemory> #include <QSharedMemory>
#include <QRegularExpression>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTimer> #include <QTimer>
#include <QUrlQuery> #include <QUrlQuery>
@ -727,7 +728,7 @@ void DomainServer::setupNodeListAndAssignments() {
packetReceiver.registerListener(PacketType::OctreeDataPersist, this, "processOctreeDataPersistMessage"); packetReceiver.registerListener(PacketType::OctreeDataPersist, this, "processOctreeDataPersistMessage");
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacementRequest"); packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacementRequest");
packetReceiver.registerListener(PacketType::OctreeFileReplacementFromUrl, this, "handleOctreeFileReplacementFromURLRequest"); packetReceiver.registerListener(PacketType::DomainContentReplacementFromUrl, this, "handleDomainContentReplacementFromURLRequest");
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket // set a custom packetVersionMatch as the verify packet operator for the udt::Socket
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified); nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
@ -736,7 +737,6 @@ void DomainServer::setupNodeListAndAssignments() {
auto assetClient = DependencyManager::set<AssetClient>(); auto assetClient = DependencyManager::set<AssetClient>();
assetClient->moveToThread(&_assetClientThread); assetClient->moveToThread(&_assetClientThread);
_assetClientThread.start(); _assetClientThread.start();
// add whatever static assignments that have been parsed to the queue // add whatever static assignments that have been parsed to the queue
addStaticAssignmentsToQueue(); addStaticAssignmentsToQueue();
} }
@ -2136,7 +2136,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
} else if (url.path().startsWith(URI_API_BACKUPS_ID)) { } else if (url.path().startsWith(URI_API_BACKUPS_ID)) {
auto id = url.path().mid(QString(URI_API_BACKUPS_ID).length()); auto id = url.path().mid(QString(URI_API_BACKUPS_ID).length());
auto deferred = makePromise("consolidateBackup"); auto deferred = makePromise("consolidateBackup");
deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) { deferred->then([connectionPtr, JSON_MIME_TYPE, id](QString error, QVariantMap result) {
if (!connectionPtr) { if (!connectionPtr) {
return; return;
} }
@ -2147,7 +2147,14 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
auto path = result["backupFilePath"].toString(); auto path = result["backupFilePath"].toString();
auto file { std::unique_ptr<QFile>(new QFile(path)) }; auto file { std::unique_ptr<QFile>(new QFile(path)) };
if (file->open(QIODevice::ReadOnly)) { if (file->open(QIODevice::ReadOnly)) {
connectionPtr->respond(HTTPConnection::StatusCode200, std::move(file)); constexpr const char* CONTENT_TYPE_ZIP = "application/zip";
auto downloadedFilename = id;
downloadedFilename.replace(QRegularExpression(".zip$"), ".content.zip");
auto contentDisposition = "attachment; filename=\"" + downloadedFilename + "\"";
connectionPtr->respond(HTTPConnection::StatusCode200, std::move(file), CONTENT_TYPE_ZIP, {
{ "Content-Disposition", contentDisposition.toUtf8() }
});
} else { } else {
qCritical(domain_server) << "Unable to load consolidated backup at:" << path << result; qCritical(domain_server) << "Unable to load consolidated backup at:" << path << result;
connectionPtr->respond(HTTPConnection::StatusCode500, "Error opening backup"); connectionPtr->respond(HTTPConnection::StatusCode500, "Error opening backup");
@ -3429,13 +3436,10 @@ void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) {
} }
} }
void DomainServer::handleOctreeFileReplacementFromURLRequest(QSharedPointer<ReceivedMessage> message) { void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer<ReceivedMessage> message) {
qInfo() << "Received request to replace content from a url"; qInfo() << "Received request to replace content from a url";
auto node = DependencyManager::get<LimitedNodeList>()->findNodeWithAddr(message->getSenderSockAddr()); auto node = DependencyManager::get<LimitedNodeList>()->findNodeWithAddr(message->getSenderSockAddr());
if (node) { if (node && node->getCanReplaceContent()) {
qDebug() << "Found node: " << node->getCanReplaceContent();
}
if (node->getCanReplaceContent()) {
// Convert message data into our URL // Convert message data into our URL
QString url(message->getMessage()); QString url(message->getMessage());
QUrl modelsURL = QUrl(url, QUrl::StrictMode); QUrl modelsURL = QUrl(url, QUrl::StrictMode);
@ -3448,7 +3452,12 @@ void DomainServer::handleOctreeFileReplacementFromURLRequest(QSharedPointer<Rece
connect(reply, &QNetworkReply::finished, [this, reply, modelsURL]() { connect(reply, &QNetworkReply::finished, [this, reply, modelsURL]() {
QNetworkReply::NetworkError networkError = reply->error(); QNetworkReply::NetworkError networkError = reply->error();
if (networkError == QNetworkReply::NoError) { if (networkError == QNetworkReply::NoError) {
handleOctreeFileReplacement(reply->readAll()); if (modelsURL.fileName().endsWith(".json.gz")) {
handleOctreeFileReplacement(reply->readAll());
} else if (modelsURL.fileName().endsWith(".zip")) {
auto deferred = makePromise("recoverFromUploadedBackup");
_contentManager->recoverFromUploadedBackup(deferred, reply->readAll());
}
} else { } else {
qDebug() << "Error downloading JSON from specified file: " << modelsURL; qDebug() << "Error downloading JSON from specified file: " << modelsURL;
} }
@ -3456,12 +3465,9 @@ void DomainServer::handleOctreeFileReplacementFromURLRequest(QSharedPointer<Rece
} }
} }
void DomainServer::handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message) { void DomainServer::handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message) {
auto node = DependencyManager::get<NodeList>()->nodeWithUUID(message->getSourceID()); auto node = DependencyManager::get<NodeList>()->nodeWithUUID(message->getSourceID());
if (node->getCanReplaceContent()) { if (node->getCanReplaceContent()) {
handleOctreeFileReplacement(message->readAll()); handleOctreeFileReplacement(message->readAll());
} }
} }

View file

@ -91,7 +91,7 @@ private slots:
void processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message); void processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message);
void processICEServerHeartbeatACK(QSharedPointer<ReceivedMessage> message); void processICEServerHeartbeatACK(QSharedPointer<ReceivedMessage> message);
void handleOctreeFileReplacementFromURLRequest(QSharedPointer<ReceivedMessage> message); void handleDomainContentReplacementFromURLRequest(QSharedPointer<ReceivedMessage> message);
void handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message); void handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message);
void handleOctreeFileReplacement(QByteArray octreeFile); void handleOctreeFileReplacement(QByteArray octreeFile);

View file

@ -191,7 +191,11 @@ add_dependencies(${TARGET_NAME} resources)
if (WIN32) if (WIN32)
# These are external plugins, but we need to do the 'add dependency' here so that their # These are external plugins, but we need to do the 'add dependency' here so that their
# binary directories get added to the fixup path # 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(sdl2)
add_dependency_external_projects(OpenVR) add_dependency_external_projects(OpenVR)
add_dependency_external_projects(neuron) add_dependency_external_projects(neuron)
@ -328,13 +332,13 @@ if (APPLE)
else() else()
# copy the resources files beside the executable # copy the resources files beside the executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${RESOURCES_RCC}" "${RESOURCES_RCC}"
"$<TARGET_FILE_DIR:interface>" "$<TARGET_FILE_DIR:interface>"
# FIXME, the edit script code loads HTML from the scripts folder # FIXME, the edit script code loads HTML from the scripts folder
# which in turn relies on CSS that refers to the fonts. In theory # 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 # 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, # 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 # so we have to retain a copy of the fonts outside of the resources binary
COMMAND "${CMAKE_COMMAND}" -E copy_directory COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${PROJECT_SOURCE_DIR}/resources/fonts" "${PROJECT_SOURCE_DIR}/resources/fonts"
@ -389,3 +393,6 @@ endif()
add_dependency_external_projects(GifCreator) add_dependency_external_projects(GifCreator)
find_package(GifCreator REQUIRED) find_package(GifCreator REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GIFCREATOR_INCLUDE_DIRS}) 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)

View file

@ -351,6 +351,7 @@ static const QString OBJ_EXTENSION = ".obj";
static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString AVA_JSON_EXTENSION = ".ava.json";
static const QString WEB_VIEW_TAG = "noDownload=true"; static const QString WEB_VIEW_TAG = "noDownload=true";
static const QString ZIP_EXTENSION = ".zip"; static const QString ZIP_EXTENSION = ".zip";
static const QString CONTENT_ZIP_EXTENSION = ".content.zip";
static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f;
@ -378,7 +379,7 @@ static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
static const QString DOMAIN_SPAWNING_POINT = "/0, -10, 0"; static const QString DOMAIN_SPAWNING_POINT = "/0, -10, 0";
const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions { const std::vector<std::pair<QString, Application::AcceptURLMethod>> Application::_acceptedExtensions {
{ SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_EXTENSION, &Application::importSVOFromURL },
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL },
{ AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }, { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl },
@ -386,6 +387,7 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi
{ JS_EXTENSION, &Application::askToLoadScript }, { JS_EXTENSION, &Application::askToLoadScript },
{ FST_EXTENSION, &Application::askToSetAvatarUrl }, { FST_EXTENSION, &Application::askToSetAvatarUrl },
{ JSON_GZ_EXTENSION, &Application::askToReplaceDomainContent }, { JSON_GZ_EXTENSION, &Application::askToReplaceDomainContent },
{ CONTENT_ZIP_EXTENSION, &Application::askToReplaceDomainContent },
{ ZIP_EXTENSION, &Application::importFromZIP }, { ZIP_EXTENSION, &Application::importFromZIP },
{ JPG_EXTENSION, &Application::importImage }, { JPG_EXTENSION, &Application::importImage },
{ PNG_EXTENSION, &Application::importImage } { PNG_EXTENSION, &Application::importImage }
@ -2704,6 +2706,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data()); surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data()); surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data()); surfaceContext->setContextProperty("Wallet", DependencyManager::get<WalletScriptingInterface>().data());
@ -6173,11 +6176,9 @@ bool Application::canAcceptURL(const QString& urlString) const {
} else if (urlString.startsWith(HIFI_URL_SCHEME)) { } else if (urlString.startsWith(HIFI_URL_SCHEME)) {
return true; return true;
} }
QHashIterator<QString, AcceptURLMethod> i(_acceptedExtensions);
QString lowerPath = url.path().toLower(); QString lowerPath = url.path().toLower();
while (i.hasNext()) { for (auto& pair : _acceptedExtensions) {
i.next(); if (lowerPath.endsWith(pair.first, Qt::CaseInsensitive)) {
if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) {
return true; return true;
} }
} }
@ -6194,12 +6195,10 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
} }
QUrl url(urlString); QUrl url(urlString);
QHashIterator<QString, AcceptURLMethod> i(_acceptedExtensions);
QString lowerPath = url.path().toLower(); QString lowerPath = url.path().toLower();
while (i.hasNext()) { for (auto& pair : _acceptedExtensions) {
i.next(); if (lowerPath.endsWith(pair.first, Qt::CaseInsensitive)) {
if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) { AcceptURLMethod method = pair.second;
AcceptURLMethod method = i.value();
return (this->*method)(urlString); return (this->*method)(urlString);
} }
} }
@ -6386,13 +6385,11 @@ void Application::replaceDomainContent(const QString& url) {
QByteArray urlData(url.toUtf8()); QByteArray urlData(url.toUtf8());
auto limitedNodeList = DependencyManager::get<NodeList>(); auto limitedNodeList = DependencyManager::get<NodeList>();
const auto& domainHandler = limitedNodeList->getDomainHandler(); const auto& domainHandler = limitedNodeList->getDomainHandler();
limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) {
return node->getType() == NodeType::EntityServer && node->getActiveSocket(); auto octreeFilePacket = NLPacket::create(PacketType::DomainContentReplacementFromUrl, urlData.size(), true);
}, [&urlData, limitedNodeList, &domainHandler](const SharedNodePointer& octreeNode) { octreeFilePacket->write(urlData);
auto octreeFilePacket = NLPacket::create(PacketType::OctreeFileReplacementFromUrl, urlData.size(), true); limitedNodeList->sendPacket(std::move(octreeFilePacket), domainHandler.getSockAddr());
octreeFilePacket->write(urlData);
limitedNodeList->sendPacket(std::move(octreeFilePacket), domainHandler.getSockAddr());
});
auto addressManager = DependencyManager::get<AddressManager>(); auto addressManager = DependencyManager::get<AddressManager>();
addressManager->handleLookupString(DOMAIN_SPAWNING_POINT); addressManager->handleLookupString(DOMAIN_SPAWNING_POINT);
QString newHomeAddress = addressManager->getHost() + DOMAIN_SPAWNING_POINT; QString newHomeAddress = addressManager->getHost() + DOMAIN_SPAWNING_POINT;

View file

@ -603,7 +603,7 @@ private:
GLCanvas* _glWidget{ nullptr }; GLCanvas* _glWidget{ nullptr };
typedef bool (Application::* AcceptURLMethod)(const QString &); typedef bool (Application::* AcceptURLMethod)(const QString &);
static const QHash<QString, AcceptURLMethod> _acceptedExtensions; static const std::vector<std::pair<QString, Application::AcceptURLMethod>> _acceptedExtensions;
glm::uvec2 _renderResolution; glm::uvec2 _renderResolution;

View file

@ -63,6 +63,19 @@ static const float OPAQUE_ALPHA_THRESHOLD = 0.99f;
const QString Web3DOverlay::TYPE = "web3d"; const QString Web3DOverlay::TYPE = "web3d";
const QString Web3DOverlay::QML = "Web3DOverlay.qml"; 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() { Web3DOverlay::Web3DOverlay() {
_touchDevice.setCapabilities(QTouchDevice::Position); _touchDevice.setCapabilities(QTouchDevice::Position);
_touchDevice.setType(QTouchDevice::TouchScreen); _touchDevice.setType(QTouchDevice::TouchScreen);
@ -75,7 +88,8 @@ Web3DOverlay::Web3DOverlay() {
connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface); connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface);
//need to be intialized before Tablet 1st open //need to be intialized before Tablet 1st open
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(_url); _webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(QML);
_cachedWebSurface = true;
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data()); _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED _webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", 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) { if (!_webSurface) {
return; return;
} }
QQuickItem* rootItem = _webSurface->getRootItem(); QQuickItem* rootItem = _webSurface->getRootItem();
if (rootItem && rootItem->objectName() == "tabletRoot") { if (rootItem && rootItem->objectName() == "tabletRoot") {
@ -135,10 +150,15 @@ void Web3DOverlay::destroyWebSurface() {
QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
auto offscreenCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
// FIXME prevents crash on shutdown, but we shoudln't have to do this check // If the web surface was fetched out of the cache, release it back into the cache
if (offscreenCache) { if (_cachedWebSurface) {
offscreenCache->release(QML, _webSurface); auto offscreenCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
// FIXME prevents crash on shutdown, but we shoudln't have to do this check
if (offscreenCache) {
offscreenCache->release(QML, _webSurface);
}
_cachedWebSurface = false;
} }
_webSurface.reset(); _webSurface.reset();
} }
@ -147,6 +167,8 @@ void Web3DOverlay::buildWebSurface() {
if (_webSurface) { if (_webSurface) {
return; 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([&] { gl::withSavedContext([&] {
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces // FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
// and the current rendering load) // and the current rendering load)
@ -156,10 +178,13 @@ void Web3DOverlay::buildWebSurface() {
if (isWebContent()) { if (isWebContent()) {
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(QML); _webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(QML);
_cachedWebSurface = true;
_webSurface->getRootItem()->setProperty("url", _url); _webSurface->getRootItem()->setProperty("url", _url);
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
} else { } else {
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(_url); _webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), qmlSurfaceDeleter);
_webSurface->load(_url);
_cachedWebSurface = false;
setupQmlSurface(); setupQmlSurface();
} }
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition())); _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition()));

View file

@ -88,6 +88,7 @@ private:
InputMode _inputMode { Touch }; InputMode _inputMode { Touch };
QSharedPointer<OffscreenQmlSurface> _webSurface; QSharedPointer<OffscreenQmlSurface> _webSurface;
bool _cachedWebSurface{ false };
gpu::TexturePointer _texture; gpu::TexturePointer _texture;
QString _url; QString _url;
QString _scriptURL; QString _scriptURL;

View file

@ -239,7 +239,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
} }
bool WebEntityRenderer::hasWebSurface() { bool WebEntityRenderer::hasWebSurface() {
return (bool)_webSurface; return (bool)_webSurface && _webSurface->getRootItem();
} }
bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) { bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
@ -303,7 +303,7 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
_fadeStartTime = usecTimestampNow(); _fadeStartTime = usecTimestampNow();
_webSurface->resume(); _webSurface->resume();
return true; return _webSurface->getRootItem();
} }
void WebEntityRenderer::destroyWebSurface() { void WebEntityRenderer::destroyWebSurface() {

View file

@ -157,7 +157,7 @@ void MaterialEntityItem::setMaterialURL(const QString& materialURLString, bool u
} }
if (usingUserData) { 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 // Since our material changed, the current name might not be valid anymore, so we need to update
setCurrentMaterialName(_currentMaterialName); setCurrentMaterialName(_currentMaterialName);

View file

@ -20,7 +20,7 @@ void NetworkMaterialResource::downloadFinished(const QByteArray& data) {
parsedMaterials.reset(); parsedMaterials.reset();
if (_url.toString().contains(".json")) { if (_url.toString().contains(".json")) {
parsedMaterials = parseJSONMaterials(QJsonDocument::fromJson(data)); parsedMaterials = parseJSONMaterials(QJsonDocument::fromJson(data), _url);
} }
// TODO: parse other material types // 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. <em>Currently not used.</em> * @property {number} materialVersion=1 - The version of the material. <em>Currently not used.</em>
* @property {Material|Material[]} materials - The details of the material or materials. * @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; ParsedMaterials toReturn;
if (!materialJSON.isNull() && materialJSON.isObject()) { if (!materialJSON.isNull() && materialJSON.isObject()) {
QJsonObject materialJSONObject = materialJSON.object(); QJsonObject materialJSONObject = materialJSON.object();
@ -91,13 +91,13 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
QJsonArray materials = materialsValue.toArray(); QJsonArray materials = materialsValue.toArray();
for (auto material : materials) { for (auto material : materials) {
if (!material.isNull() && material.isObject()) { if (!material.isNull() && material.isObject()) {
auto parsedMaterial = parseJSONMaterial(material.toObject()); auto parsedMaterial = parseJSONMaterial(material.toObject(), baseUrl);
toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second; toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second;
toReturn.names.push_back(parsedMaterial.first); toReturn.names.push_back(parsedMaterial.first);
} }
} }
} else if (materialsValue.isObject()) { } else if (materialsValue.isObject()) {
auto parsedMaterial = parseJSONMaterial(materialsValue.toObject()); auto parsedMaterial = parseJSONMaterial(materialsValue.toObject(), baseUrl);
toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second; toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second;
toReturn.names.push_back(parsedMaterial.first); toReturn.names.push_back(parsedMaterial.first);
} }
@ -138,7 +138,7 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
* @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em> * @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em>
*/ */
// Note: See MaterialEntityItem.h for default values used in practice. // Note: See MaterialEntityItem.h for default values used in practice.
std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON) { std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) {
std::string name = ""; std::string name = "";
std::shared_ptr<NetworkMaterial> material = std::make_shared<NetworkMaterial>(); std::shared_ptr<NetworkMaterial> material = std::make_shared<NetworkMaterial>();
for (auto& key : materialJSON.keys()) { for (auto& key : materialJSON.keys()) {
@ -199,57 +199,58 @@ std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource
} else if (key == "albedoMap") { } else if (key == "albedoMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
QString urlString = value.toString();
bool useAlphaChannel = false; bool useAlphaChannel = false;
auto opacityMap = materialJSON.find("opacityMap"); 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; useAlphaChannel = true;
} }
material->setAlbedoMap(value.toString(), useAlphaChannel); material->setAlbedoMap(baseUrl.resolved(urlString), useAlphaChannel);
} }
} else if (key == "roughnessMap") { } else if (key == "roughnessMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
material->setRoughnessMap(value.toString(), false); material->setRoughnessMap(baseUrl.resolved(value.toString()), false);
} }
} else if (key == "glossMap") { } else if (key == "glossMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
material->setRoughnessMap(value.toString(), true); material->setRoughnessMap(baseUrl.resolved(value.toString()), true);
} }
} else if (key == "metallicMap") { } else if (key == "metallicMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
material->setMetallicMap(value.toString(), false); material->setMetallicMap(baseUrl.resolved(value.toString()), false);
} }
} else if (key == "specularMap") { } else if (key == "specularMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
material->setMetallicMap(value.toString(), true); material->setMetallicMap(baseUrl.resolved(value.toString()), true);
} }
} else if (key == "normalMap") { } else if (key == "normalMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
material->setNormalMap(value.toString(), false); material->setNormalMap(baseUrl.resolved(value.toString()), false);
} }
} else if (key == "bumpMap") { } else if (key == "bumpMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
material->setNormalMap(value.toString(), true); material->setNormalMap(baseUrl.resolved(value.toString()), true);
} }
} else if (key == "occlusionMap") { } else if (key == "occlusionMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
material->setOcclusionMap(value.toString()); material->setOcclusionMap(baseUrl.resolved(value.toString()));
} }
} else if (key == "scatteringMap") { } else if (key == "scatteringMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
material->setScatteringMap(value.toString()); material->setScatteringMap(baseUrl.resolved(value.toString()));
} }
} else if (key == "lightMap") { } else if (key == "lightMap") {
auto value = materialJSON.value(key); auto value = materialJSON.value(key);
if (value.isString()) { if (value.isString()) {
material->setLightmapMap(value.toString()); material->setLightmapMap(baseUrl.resolved(value.toString()));
} }
} }
} }

View file

@ -37,8 +37,8 @@ public:
ParsedMaterials parsedMaterials; ParsedMaterials parsedMaterials;
static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON); static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl);
static std::pair<std::string, std::shared_ptr<NetworkMaterial>> parseJSONMaterial(const QJsonObject& materialJSON); static std::pair<std::string, std::shared_ptr<NetworkMaterial>> parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl);
private: private:
static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB); static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB);

View file

@ -556,58 +556,58 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, im
return nullptr; return nullptr;
} }
void NetworkMaterial::setAlbedoMap(const QString& url, bool useAlphaChannel) { void NetworkMaterial::setAlbedoMap(const QUrl& url, bool useAlphaChannel) {
auto map = fetchTextureMap(QUrl(url), image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
if (map) { if (map) {
map->setUseAlphaChannel(useAlphaChannel); map->setUseAlphaChannel(useAlphaChannel);
setTextureMap(MapChannel::ALBEDO_MAP, map); setTextureMap(MapChannel::ALBEDO_MAP, map);
} }
} }
void NetworkMaterial::setNormalMap(const QString& url, bool isBumpmap) { void NetworkMaterial::setNormalMap(const QUrl& url, bool isBumpmap) {
auto map = fetchTextureMap(QUrl(url), isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP); auto map = fetchTextureMap(url, isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
if (map) { if (map) {
setTextureMap(MapChannel::NORMAL_MAP, map); setTextureMap(MapChannel::NORMAL_MAP, map);
} }
} }
void NetworkMaterial::setRoughnessMap(const QString& url, bool isGloss) { void NetworkMaterial::setRoughnessMap(const QUrl& url, bool isGloss) {
auto map = fetchTextureMap(QUrl(url), isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); auto map = fetchTextureMap(url, isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP);
if (map) { if (map) {
setTextureMap(MapChannel::ROUGHNESS_MAP, map); setTextureMap(MapChannel::ROUGHNESS_MAP, map);
} }
} }
void NetworkMaterial::setMetallicMap(const QString& url, bool isSpecular) { void NetworkMaterial::setMetallicMap(const QUrl& url, bool isSpecular) {
auto map = fetchTextureMap(QUrl(url), isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP); auto map = fetchTextureMap(url, isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP);
if (map) { if (map) {
setTextureMap(MapChannel::METALLIC_MAP, map); setTextureMap(MapChannel::METALLIC_MAP, map);
} }
} }
void NetworkMaterial::setOcclusionMap(const QString& url) { void NetworkMaterial::setOcclusionMap(const QUrl& url) {
auto map = fetchTextureMap(QUrl(url), image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
if (map) { if (map) {
setTextureMap(MapChannel::OCCLUSION_MAP, map); setTextureMap(MapChannel::OCCLUSION_MAP, map);
} }
} }
void NetworkMaterial::setEmissiveMap(const QString& url) { void NetworkMaterial::setEmissiveMap(const QUrl& url) {
auto map = fetchTextureMap(QUrl(url), image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP);
if (map) { if (map) {
setTextureMap(MapChannel::EMISSIVE_MAP, map); setTextureMap(MapChannel::EMISSIVE_MAP, map);
} }
} }
void NetworkMaterial::setScatteringMap(const QString& url) { void NetworkMaterial::setScatteringMap(const QUrl& url) {
auto map = fetchTextureMap(QUrl(url), image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP); auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP);
if (map) { if (map) {
setTextureMap(MapChannel::SCATTERING_MAP, map); setTextureMap(MapChannel::SCATTERING_MAP, map);
} }
} }
void NetworkMaterial::setLightmapMap(const QString& url) { void NetworkMaterial::setLightmapMap(const QUrl& url) {
auto map = fetchTextureMap(QUrl(url), image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP);
if (map) { if (map) {
//map->setTextureTransform(_lightmapTransform); //map->setTextureTransform(_lightmapTransform);
//map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); //map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y);

View file

@ -164,14 +164,14 @@ public:
NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl);
NetworkMaterial(const NetworkMaterial& material); NetworkMaterial(const NetworkMaterial& material);
void setAlbedoMap(const QString& url, bool useAlphaChannel); void setAlbedoMap(const QUrl& url, bool useAlphaChannel);
void setNormalMap(const QString& url, bool isBumpmap); void setNormalMap(const QUrl& url, bool isBumpmap);
void setRoughnessMap(const QString& url, bool isGloss); void setRoughnessMap(const QUrl& url, bool isGloss);
void setMetallicMap(const QString& url, bool isSpecular); void setMetallicMap(const QUrl& url, bool isSpecular);
void setOcclusionMap(const QString& url); void setOcclusionMap(const QUrl& url);
void setEmissiveMap(const QString& url); void setEmissiveMap(const QUrl& url);
void setScatteringMap(const QString& url); void setScatteringMap(const QUrl& url);
void setLightmapMap(const QString& url); void setLightmapMap(const QUrl& url);
protected: protected:
friend class Geometry; friend class Geometry;

View file

@ -121,7 +121,7 @@ public:
ReplicatedAvatarIdentity, ReplicatedAvatarIdentity,
ReplicatedKillAvatar, ReplicatedKillAvatar,
ReplicatedBulkAvatarData, ReplicatedBulkAvatarData,
OctreeFileReplacementFromUrl, DomainContentReplacementFromUrl,
ChallengeOwnership, ChallengeOwnership,
EntityScriptCallMethod, EntityScriptCallMethod,
ChallengeOwnershipRequest, ChallengeOwnershipRequest,
@ -171,7 +171,7 @@ public:
<< PacketTypeEnum::Value::DomainServerPathResponse << PacketTypeEnum::Value::DomainServerAddedNode << PacketTypeEnum::Value::DomainServerPathResponse << PacketTypeEnum::Value::DomainServerAddedNode
<< PacketTypeEnum::Value::DomainServerConnectionToken << PacketTypeEnum::Value::DomainSettingsRequest << PacketTypeEnum::Value::DomainServerConnectionToken << PacketTypeEnum::Value::DomainSettingsRequest
<< PacketTypeEnum::Value::OctreeDataFileRequest << PacketTypeEnum::Value::OctreeDataFileReply << PacketTypeEnum::Value::OctreeDataFileRequest << PacketTypeEnum::Value::OctreeDataFileReply
<< PacketTypeEnum::Value::OctreeDataPersist << PacketTypeEnum::Value::OctreeFileReplacementFromUrl << PacketTypeEnum::Value::OctreeDataPersist << PacketTypeEnum::Value::DomainContentReplacementFromUrl
<< PacketTypeEnum::Value::DomainSettings << PacketTypeEnum::Value::ICEServerPeerInformation << PacketTypeEnum::Value::DomainSettings << PacketTypeEnum::Value::ICEServerPeerInformation
<< PacketTypeEnum::Value::ICEServerQuery << PacketTypeEnum::Value::ICEServerHeartbeat << PacketTypeEnum::Value::ICEServerQuery << PacketTypeEnum::Value::ICEServerHeartbeat
<< PacketTypeEnum::Value::ICEServerHeartbeatACK << PacketTypeEnum::Value::ICEPing << PacketTypeEnum::Value::ICEServerHeartbeatACK << PacketTypeEnum::Value::ICEPing

View file

@ -8,6 +8,9 @@ include_hifi_library_headers(audio)
include_hifi_library_headers(networking) include_hifi_library_headers(networking)
include_hifi_library_headers(octree) 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) if (NOT ANDROID)
target_nsight() target_nsight()
endif () endif ()

View file

@ -47,6 +47,8 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr
} }
} }
const graphics::MaterialPointer MeshPartPayload::DEFAULT_MATERIAL = std::make_shared<graphics::Material>();
MeshPartPayload::MeshPartPayload(const std::shared_ptr<const graphics::Mesh>& mesh, int partIndex, graphics::MaterialPointer material) { MeshPartPayload::MeshPartPayload(const std::shared_ptr<const graphics::Mesh>& mesh, int partIndex, graphics::MaterialPointer material) {
updateMeshPart(mesh, partIndex); updateMeshPart(mesh, partIndex);
addMaterial(graphics::MaterialLayer(material, 0)); addMaterial(graphics::MaterialLayer(material, 0));
@ -99,7 +101,7 @@ void MeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShad
builder.withSubMetaCulled(); builder.withSubMetaCulled();
} }
if (_drawMaterials.top().material) { if (topMaterialExists()) {
auto matKey = _drawMaterials.top().material->getKey(); auto matKey = _drawMaterials.top().material->getKey();
if (matKey.isTranslucent()) { if (matKey.isTranslucent()) {
builder.withTransparent(); builder.withTransparent();
@ -119,7 +121,7 @@ Item::Bound MeshPartPayload::getBound() const {
ShapeKey MeshPartPayload::getShapeKey() const { ShapeKey MeshPartPayload::getShapeKey() const {
graphics::MaterialKey drawMaterialKey; graphics::MaterialKey drawMaterialKey;
if (_drawMaterials.top().material) { if (topMaterialExists()) {
drawMaterialKey = _drawMaterials.top().material->getKey(); drawMaterialKey = _drawMaterials.top().material->getKey();
} }
@ -171,7 +173,7 @@ void MeshPartPayload::render(RenderArgs* args) {
bindMesh(batch); bindMesh(batch);
// apply material properties // 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++; args->_details._materialSwitches++;
// Draw! // Draw!
@ -356,7 +358,7 @@ void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCas
builder.withDeformed(); builder.withDeformed();
} }
if (_drawMaterials.top().material) { if (topMaterialExists()) {
auto matKey = _drawMaterials.top().material->getKey(); auto matKey = _drawMaterials.top().material->getKey();
if (matKey.isTranslucent()) { if (matKey.isTranslucent()) {
builder.withTransparent(); builder.withTransparent();
@ -387,7 +389,7 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe
} }
graphics::MaterialKey drawMaterialKey; graphics::MaterialKey drawMaterialKey;
if (_drawMaterials.top().material) { if (topMaterialExists()) {
drawMaterialKey = _drawMaterials.top().material->getKey(); drawMaterialKey = _drawMaterials.top().material->getKey();
} }
@ -469,7 +471,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
bindMesh(batch); bindMesh(batch);
// apply material properties // 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++; args->_details._materialSwitches++;
// Draw! // Draw!

View file

@ -65,15 +65,18 @@ public:
graphics::Mesh::Part _drawPart; graphics::Mesh::Part _drawPart;
size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; }
size_t getMaterialTextureSize() { return _drawMaterials.top().material ? _drawMaterials.top().material->getTextureSize() : 0; } size_t getMaterialTextureSize() { return topMaterialExists() ? _drawMaterials.top().material->getTextureSize() : 0; }
int getMaterialTextureCount() { return _drawMaterials.top().material ? _drawMaterials.top().material->getTextureCount() : 0; } int getMaterialTextureCount() { return topMaterialExists() ? _drawMaterials.top().material->getTextureCount() : 0; }
bool hasTextureInfo() const { return _drawMaterials.top().material ? _drawMaterials.top().material->hasTextureInfo() : false; } bool hasTextureInfo() const { return topMaterialExists() ? _drawMaterials.top().material->hasTextureInfo() : false; }
void addMaterial(graphics::MaterialLayer material); void addMaterial(graphics::MaterialLayer material);
void removeMaterial(graphics::MaterialPointer material); void removeMaterial(graphics::MaterialPointer material);
protected: protected:
static const graphics::MaterialPointer DEFAULT_MATERIAL;
render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() }; render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() };
bool topMaterialExists() const { return !_drawMaterials.empty() && _drawMaterials.top().material; }
}; };
namespace render { namespace render {

View file

@ -1604,7 +1604,8 @@ void Model::createVisibleRenderItemSet() {
int numParts = (int)mesh->getNumParts(); int numParts = (int)mesh->getNumParts();
for (int partIndex = 0; partIndex < numParts; partIndex++) { for (int partIndex = 0; partIndex < numParts; partIndex++) {
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(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 }); _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
shapeID++; shapeID++;
} }

View file

@ -20,8 +20,12 @@ if (NOT SERVER_ONLY AND NOT ANDROID)
add_subdirectory(${DIR}) add_subdirectory(${DIR})
set(DIR "oculusLegacy") set(DIR "oculusLegacy")
add_subdirectory(${DIR}) add_subdirectory(${DIR})
set(DIR "hifiSixense")
add_subdirectory(${DIR}) if (USE_SIXENSE)
set(DIR "hifiSixense")
add_subdirectory(${DIR})
endif()
set(DIR "hifiSpacemouse") set(DIR "hifiSpacemouse")
add_subdirectory(${DIR}) add_subdirectory(${DIR})
set(DIR "hifiNeuron") set(DIR "hifiNeuron")

View file

@ -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();