diff --git a/.gitignore b/.gitignore index f45572c388..1ffb93fe80 100644 --- a/.gitignore +++ b/.gitignore @@ -85,4 +85,7 @@ npm-debug.log android/app/src/main/assets # Resource binary file -interface/compiledResources \ No newline at end of file +interface/compiledResources + +# GPUCache +interface/resources/GPUCache/* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index bbec4a0884..25fd4731e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,11 @@ include("cmake/init.cmake") include("cmake/compiler.cmake") +if (BUILD_SCRIBE_ONLY) + add_subdirectory(tools/scribe) + return() +endif() + if (NOT DEFINED SERVER_ONLY) set(SERVER_ONLY 0) endif() diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt index 4c50cd8609..0ef1427306 100644 --- a/android/app/CMakeLists.txt +++ b/android/app/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME native-lib) setup_hifi_library() -link_hifi_libraries(shared networking gl gpu image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND}) +link_hifi_libraries(shared networking gl gpu qml image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND}) target_opengl() target_bullet() diff --git a/android/app/build.gradle b/android/app/build.gradle index 1cff708c3b..ba3d16ad4d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -18,7 +18,7 @@ android { '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_shared', '-DQT_CMAKE_PREFIX_PATH=' + HIFI_ANDROID_PRECOMPILED + '/qt/lib/cmake', - '-DNATIVE_SCRIBE=' + HIFI_ANDROID_PRECOMPILED + '/scribe', + '-DNATIVE_SCRIBE=' + HIFI_ANDROID_PRECOMPILED + '/scribe' + EXEC_SUFFIX, '-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED, '-DRELEASE_NUMBER=' + RELEASE_NUMBER, '-DRELEASE_TYPE=' + RELEASE_TYPE, diff --git a/android/build.gradle b/android/build.gradle index 1f2c563e1b..9e5e76f0e3 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -137,13 +137,16 @@ def packages = [ def scribeLocalFile='scribe' + EXEC_SUFFIX def scribeFile='scribe_linux_x86_64' -def scribeChecksum='c98678d9726bd8bbf1bab792acf3ff6c' +def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6' +def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO' if (Os.isFamily(Os.FAMILY_MAC)) { scribeFile = 'scribe_osx_x86_64' - scribeChecksum='a137ad62c1bf7cca739da219544a9a16' + scribeChecksum='72db9d32d4e1e50add755570ac5eb749' + scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { scribeFile = 'scribe_win32_x86_64.exe' - scribeChecksum='75c2ce9ed45d17de375e3988bfaba816' + scribeChecksum='678e43d290c90fda670c6fefe038a06d' + scribeVersion='GCCJxlmd2irvNOFWfZR0U1UCLHndHQrC' } def options = [ @@ -398,7 +401,7 @@ task copyDependencies(dependsOn: [ extractDependencies ]) { } task downloadScribe(type: Download) { - src baseUrl + scribeFile + src baseUrl + scribeFile + '?versionId=' + scribeVersion dest new File(baseFolder, scribeLocalFile) onlyIfNewer true } diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index acdc1cbc53..c73e8e1d34 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME assignment-client) -setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets) +setup_hifi_project(Core Gui Network Script Quick WebSockets) # Fix up the rpath so macdeployqt works if (APPLE) diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index 6d150a0dc3..abc2f7220d 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -129,6 +129,12 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { AvatarAudioStream* listenerAudioStream = static_cast(listener->getLinkedData())->getAvatarAudioStream(); AudioMixerClientData* listenerData = static_cast(listener->getLinkedData()); + // if we received an invalid position from this listener, then refuse to make them a mix + // because we don't know how to do it properly + if (!listenerAudioStream->hasValidPosition()) { + return false; + } + // zero out the mix for this listener memset(_mixSamples, 0, sizeof(_mixSamples)); @@ -170,7 +176,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { auto nodeID = node->getUUID(); // compute the node's max relative volume - float nodeVolume; + float nodeVolume = 0.0f; for (auto& streamPair : nodeData->getAudioStreams()) { auto nodeStream = streamPair.second; @@ -187,10 +193,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { } // max-heapify the nodes by relative volume - throttledNodes.push_back(std::make_pair(nodeVolume, node)); - if (!throttledNodes.empty()) { - std::push_heap(throttledNodes.begin(), throttledNodes.end()); - } + throttledNodes.push_back({ nodeVolume, node }); + std::push_heap(throttledNodes.begin(), throttledNodes.end()); } } }); @@ -244,12 +248,18 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { void AudioMixerSlave::throttleStream(AudioMixerClientData& listenerNodeData, const QUuid& sourceNodeID, const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd) { - addStream(listenerNodeData, sourceNodeID, listeningNodeStream, streamToAdd, true); + // only throttle this stream to the mix if it has a valid position, we won't know how to mix it otherwise + if (streamToAdd.hasValidPosition()) { + addStream(listenerNodeData, sourceNodeID, listeningNodeStream, streamToAdd, true); + } } void AudioMixerSlave::mixStream(AudioMixerClientData& listenerNodeData, const QUuid& sourceNodeID, const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd) { - addStream(listenerNodeData, sourceNodeID, listeningNodeStream, streamToAdd, false); + // only add the stream to the mix if it has a valid position, we won't know how to mix it otherwise + if (streamToAdd.hasValidPosition()) { + addStream(listenerNodeData, sourceNodeID, listeningNodeStream, streamToAdd, false); + } } void AudioMixerSlave::addStream(AudioMixerClientData& listenerNodeData, const QUuid& sourceNodeID, diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index 1919ecf00a..c4cd2d186a 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -39,56 +39,41 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) get_filename_component(SHADER_TARGET ${SHADER_FILE} NAME_WE) get_filename_component(SHADER_EXT ${SHADER_FILE} EXT) if(SHADER_EXT STREQUAL .slv) - set(SHADER_TARGET ${SHADER_TARGET}_vert.h) + set(SHADER_TYPE vert) elseif(${SHADER_EXT} STREQUAL .slf) - set(SHADER_TARGET ${SHADER_TARGET}_frag.h) + set(SHADER_TYPE frag) elseif(${SHADER_EXT} STREQUAL .slg) - set(SHADER_TARGET ${SHADER_TARGET}_geom.h) + set(SHADER_TYPE geom) endif() + set(SHADER_TARGET ${SHADER_TARGET}_${SHADER_TYPE}) set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}") + set(SHADER_TARGET_HEADER ${SHADER_TARGET}.h) + set(SHADER_TARGET_SOURCE ${SHADER_TARGET}.cpp) + set(SCRIBE_COMMAND scribe) # Target dependant Custom rule on the SHADER_FILE if (APPLE) set(GLPROFILE MAC_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) elseif (ANDROID) set(GLPROFILE LINUX_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - - # for an android build, we can't use the scribe that cmake would normally produce as a target, - # since it's unrunnable by the cross-compiling build machine - - # so, we require the compiling user to point us at a compiled executable version for their native toolchain - if (NOT NATIVE_SCRIBE) - find_program(NATIVE_SCRIBE scribe PATHS ${SCRIBE_PATH} ENV SCRIBE_PATH) - endif() - - if (NOT NATIVE_SCRIBE) - message(FATAL_ERROR "The High Fidelity scribe tool is required for shader pre-processing. \ - Please compile scribe using your native toolchain and set SCRIBE_PATH to the path containing the scribe executable in your ENV.\ - ") - endif () - - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS} DEPENDS ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) + set(SCRIBE_COMMAND ${NATIVE_SCRIBE}) elseif (UNIX) set(GLPROFILE LINUX_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) else () set(GLPROFILE PC_GL) - set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) - - add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE}) endif() + set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE}) + add_custom_command( + OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} + COMMAND ${SCRIBE_COMMAND} ${SCRIBE_ARGS} + DEPENDS ${SCRIBE_COMMAND} ${SHADER_INCLUDE_FILES} ${SHADER_FILE} + ) #output the generated file name - set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET} PARENT_SCOPE) + set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} PARENT_SCOPE) - file(GLOB INCLUDE_FILES ${SHADER_TARGET}) + file(GLOB INCLUDE_FILES ${SHADER_TARGET_HEADER}) endfunction() @@ -126,7 +111,7 @@ macro(AUTOSCRIBE_SHADER_LIB) if (WIN32) source_group("Shaders" FILES ${SHADER_INCLUDE_FILES}) source_group("Shaders" FILES ${SHADER_SOURCE_FILES}) - source_group("Shaders" FILES ${AUTOSCRIBE_SHADER_SRC}) + source_group("Shaders\\generated" FILES ${AUTOSCRIBE_SHADER_SRC}) endif() list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_INCLUDE_FILES}) @@ -136,4 +121,7 @@ macro(AUTOSCRIBE_SHADER_LIB) # Link library shaders, if they exist include_directories("${SHADERS_DIR}") + # Add search directory to find gpu/Shader.h + include_directories("${HIFI_LIBRARY_DIR}/gpu/src") + endmacro() diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index c6eaa03634..3aab7b4563 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -161,7 +161,7 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin } else if (_server->_settingsManager.hasPermissionsForMachineFingerprint(machineFingerprint)) { userPerms = _server->_settingsManager.getPermissionsForMachineFingerprint(machineFingerprint); #ifdef WANT_DEBUG - qDebug(() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms; + qDebug() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms; #endif } else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) { // this user comes from an IP we have in our permissions table, apply those permissions @@ -187,7 +187,7 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin } else if (_server->_settingsManager.hasPermissionsForMachineFingerprint(machineFingerprint)) { userPerms = _server->_settingsManager.getPermissionsForMachineFingerprint(machineFingerprint); #ifdef WANT_DEBUG - qDebug(() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms; + qDebug() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms; #endif } else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) { // this user comes from an IP we have in our permissions table, apply those permissions @@ -393,9 +393,12 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect QString verifiedUsername; // if this remains empty, consider this an anonymous connection attempt if (!username.isEmpty()) { - if (usernameSignature.isEmpty()) { + const QUuid& connectionToken = _connectionTokenHash.value(username.toLower()); + + if (usernameSignature.isEmpty() || connectionToken.isNull()) { // user is attempting to prove their identity to us, but we don't have enough information sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); + // ask for their public key right now to make sure we have it requestUserPublicKey(username, true); getGroupMemberships(username); // optimistically get started on group memberships diff --git a/gvr-interface/CMakeLists.txt b/gvr-interface/CMakeLists.txt index 7d6655e875..72f1096881 100644 --- a/gvr-interface/CMakeLists.txt +++ b/gvr-interface/CMakeLists.txt @@ -17,9 +17,9 @@ if (ANDROID) set(BUILD_SHARED_LIBS ON) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}") - setup_hifi_library(Gui Widgets AndroidExtras) + setup_hifi_library(Gui AndroidExtras) else () - setup_hifi_project(Gui Widgets) + setup_hifi_project(Gui) endif () include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 7a5d890d2a..728f1467e0 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME interface) project(${TARGET_NAME}) file(GLOB_RECURSE QML_SRC resources/qml/*.qml resources/qml/*.js) -add_custom_target(qml SOURCES ${QML_SRC}) +add_custom_target(qmls SOURCES ${QML_SRC}) GroupSources("resources/qml") function(JOIN VALUES GLUE OUTPUT) @@ -78,7 +78,7 @@ endif () find_package( Qt5 COMPONENTS - Gui Multimedia Network OpenGL Qml Quick Script Svg + Gui Widgets Multimedia Network Qml Quick Script Svg ${PLATFORM_QT_COMPONENTS} WebChannel WebSockets ) @@ -208,7 +208,7 @@ link_hifi_libraries( pointers recording fbx networking model-networking entities avatars trackers audio audio-client animation script-engine physics - render-utils entities-renderer avatars-renderer ui auto-updater midi + render-utils entities-renderer avatars-renderer ui qml auto-updater midi controllers plugins image trackers ui-plugins display-plugins input-plugins ${PLATFORM_GL_BACKEND} @@ -269,7 +269,7 @@ endif () target_link_libraries( ${TARGET_NAME} - Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL + Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::Widgets Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg Qt5::WebChannel ${PLATFORM_QT_LIBRARIES} diff --git a/interface/resources/icons/tablet-icons/bubble-a.svg b/interface/resources/icons/tablet-icons/bubble-a.svg index 553636bfbb..6f9c75a0c2 100644 --- a/interface/resources/icons/tablet-icons/bubble-a.svg +++ b/interface/resources/icons/tablet-icons/bubble-a.svg @@ -1,96 +1,15 @@ - - - -image/svg+xml \ No newline at end of file + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/bubble-i.svg b/interface/resources/icons/tablet-icons/bubble-i.svg index d7c8948e01..567a36d7ba 100644 --- a/interface/resources/icons/tablet-icons/bubble-i.svg +++ b/interface/resources/icons/tablet-icons/bubble-i.svg @@ -1,46 +1,19 @@ - - + - - - - - - - - - - - - - - - - + + + + diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index 421fa4b074..f493747c5e 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -19,21 +19,31 @@ import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet -// references XXX from root context - Rectangle { HifiConstants { id: hifi; } id: root; - property string marketplaceUrl; - property string certificateId; + property string marketplaceUrl: ""; + property string entityId: ""; + property string certificateId: ""; property string itemName: "--"; property string itemOwner: "--"; property string itemEdition: "--"; property string dateOfPurchase: "--"; + property string itemCost: "--"; + property string certTitleTextColor: hifi.colors.darkGray; + property string certTextColor: hifi.colors.white; + property string infoTextColor: hifi.colors.blueAccent; + // 0 means replace none + // 4 means replace all but "Item Edition" + // 5 means replace all 5 replaceable fields + property int certInfoReplaceMode: 5; property bool isLightbox: false; property bool isMyCert: false; - property bool isCertificateInvalid: false; + property bool useGoldCert: true; + property bool certificateInfoPending: true; + property int certificateStatus: 0; + property bool certificateStatusPending: true; // Style color: hifi.colors.faintGray; Connections { @@ -45,72 +55,135 @@ Rectangle { } else { root.marketplaceUrl = result.data.marketplace_item_url; root.isMyCert = result.isMyCert ? result.isMyCert : false; - root.itemOwner = root.isCertificateInvalid ? "--" : (root.isMyCert ? Account.username : - "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"); - root.itemEdition = root.isCertificateInvalid ? "Uncertified Copy" : - (result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run)); - root.dateOfPurchase = root.isCertificateInvalid ? "" : getFormattedDate(result.data.transfer_created_at * 1000); - root.itemName = result.data.marketplace_item_name; + + if (root.certInfoReplaceMode > 3) { + root.itemName = result.data.marketplace_item_name; + // "\u2022" is the Unicode character 'BULLET' - it's what's used in password fields on the web, etc + root.itemOwner = root.isMyCert ? Account.username : + "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"; + root.dateOfPurchase = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed"; + root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? result.data.cost : "Undisclosed"; + } + if (root.certInfoReplaceMode > 4) { + root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run); + } + + if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED + if (root.isMyCert) { + errorText.text = "This item is an uncertified copy of an item you purchased."; + } else { + errorText.text = "The person who placed this item doesn't own it."; + } + } if (result.data.invalid_reason || result.data.transfer_status[0] === "failed") { - titleBarText.text = "Invalid Certificate"; - titleBarText.color = hifi.colors.redHighlight; + root.useGoldCert = false; + root.certTitleTextColor = hifi.colors.redHighlight; + root.certTextColor = hifi.colors.redHighlight; + root.infoTextColor = hifi.colors.redHighlight; + titleBarText.text = "Certificate\nNo Longer Valid"; popText.text = ""; + showInMarketplaceButton.visible = false; + // "Edition" text previously set above in this function + // "Owner" text previously set above in this function + // "Purchase Date" text previously set above in this function + // "Purchase Price" text previously set above in this function if (result.data.invalid_reason) { errorText.text = result.data.invalid_reason; } } else if (result.data.transfer_status[0] === "pending") { + root.useGoldCert = false; + root.certTitleTextColor = hifi.colors.redHighlight; + root.certTextColor = hifi.colors.redHighlight; + root.infoTextColor = hifi.colors.redHighlight; titleBarText.text = "Certificate Pending"; + popText.text = ""; + showInMarketplaceButton.visible = true; + // "Edition" text previously set above in this function + // "Owner" text previously set above in this function + // "Purchase Date" text previously set above in this function + // "Purchase Price" text previously set above in this function errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " + "this entity will be cleaned up by the domain."; - errorText.color = hifi.colors.baseGray; } } + root.certificateInfoPending = false; } onUpdateCertificateStatus: { - if (certStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS - // NOP - } else if (certStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT - root.isCertificateInvalid = true; - errorText.text = "Verification of this certificate timed out."; - errorText.color = hifi.colors.redHighlight; - } else if (certStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED - root.isCertificateInvalid = true; - titleBarText.text = "Invalid Certificate"; - titleBarText.color = hifi.colors.redHighlight; - - popText.text = ""; - root.itemOwner = ""; - dateOfPurchaseHeader.text = ""; - root.dateOfPurchase = ""; - root.itemEdition = "Uncertified Copy"; - - errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item."; - errorText.color = hifi.colors.baseGray; - } else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED - root.isCertificateInvalid = true; - titleBarText.text = "Invalid Certificate"; - titleBarText.color = hifi.colors.redHighlight; - - popText.text = ""; - root.itemOwner = ""; - dateOfPurchaseHeader.text = ""; - root.dateOfPurchase = ""; - root.itemEdition = "Uncertified Copy"; - - errorText.text = "The avatar who rezzed this item doesn't own it."; - errorText.color = hifi.colors.baseGray; - } else { - console.log("Unknown certificate status received from ledger signal!"); - } + updateCertificateStatus(certStatus); } } - onCertificateIdChanged: { - if (certificateId !== "") { - Commerce.certificateInfo(certificateId); + function updateCertificateStatus(status) { + root.certificateStatus = status; + if (root.certificateStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS + root.useGoldCert = true; + root.certTitleTextColor = hifi.colors.darkGray; + root.certTextColor = hifi.colors.white; + root.infoTextColor = hifi.colors.blueAccent; + titleBarText.text = "Certificate"; + popText.text = "PROOF OF PROVENANCE"; + showInMarketplaceButton.visible = true; + root.certInfoReplaceMode = 5; + // "Item Name" text will be set in "onCertificateInfoResult()" + // "Edition" text will be set in "onCertificateInfoResult()" + // "Owner" text will be set in "onCertificateInfoResult()" + // "Purchase Date" text will be set in "onCertificateInfoResult()" + // "Purchase Price" text will be set in "onCertificateInfoResult()" + errorText.text = ""; + } else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT + root.useGoldCert = false; + root.certTitleTextColor = hifi.colors.redHighlight; + root.certTextColor = hifi.colors.redHighlight; + root.infoTextColor = hifi.colors.redHighlight; + titleBarText.text = "Request Timed Out"; + popText.text = ""; + showInMarketplaceButton.visible = false; + root.certInfoReplaceMode = 0; + root.itemName = ""; + root.itemEdition = ""; + root.itemOwner = ""; + root.dateOfPurchase = ""; + root.itemCost = ""; + errorText.text = "Your request to inspect this item timed out. Please try again later."; + } else if (root.certificateStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED + root.useGoldCert = false; + root.certTitleTextColor = hifi.colors.redHighlight; + root.certTextColor = hifi.colors.redHighlight; + root.infoTextColor = hifi.colors.redHighlight; + titleBarText.text = "Certificate\nNo Longer Valid"; + popText.text = ""; + showInMarketplaceButton.visible = true; + root.certInfoReplaceMode = 5; + // "Item Name" text will be set in "onCertificateInfoResult()" + // "Edition" text will be set in "onCertificateInfoResult()" + // "Owner" text will be set in "onCertificateInfoResult()" + // "Purchase Date" text will be set in "onCertificateInfoResult()" + // "Purchase Price" text will be set in "onCertificateInfoResult()" + errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item."; + } else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED + root.useGoldCert = false; + root.certTitleTextColor = hifi.colors.redHighlight; + root.certTextColor = hifi.colors.redHighlight; + root.infoTextColor = hifi.colors.redHighlight; + titleBarText.text = "Invalid Certificate"; + popText.text = ""; + showInMarketplaceButton.visible = true; + root.certInfoReplaceMode = 4; + // "Item Name" text will be set in "onCertificateInfoResult()" + root.itemEdition = "Uncertified Copy" + // "Owner" text will be set in "onCertificateInfoResult()" + // "Purchase Date" text will be set in "onCertificateInfoResult()" + // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Error Text" text will be set in "onCertificateInfoResult()" + } else { + console.log("Unknown certificate status received from ledger signal!"); } + + root.certificateStatusPending = false; + // We've gotten cert status - we are GO on getting the cert info + Commerce.certificateInfo(root.certificateId); } // This object is always used in a popup. @@ -122,9 +195,35 @@ Rectangle { hoverEnabled: true; } - Image { + Rectangle { + id: loadingOverlay; + z: 998; + + visible: root.certificateInfoPending || root.certificateStatusPending; anchors.fill: parent; - source: "images/cert-bg.jpg"; + color: Qt.rgba(0.0, 0.0, 0.0, 0.7); + + // This object is always used in a popup or full-screen Wallet section. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + } + + AnimatedImage { + source: "../common/images/loader.gif" + width: 96; + height: width; + anchors.verticalCenter: parent.verticalCenter; + anchors.horizontalCenter: parent.horizontalCenter; + } + } + + Image { + id: backgroundImage; + anchors.fill: parent; + source: root.useGoldCert ? "images/cert-bg-gold-split.png" : "images/nocert-bg-split.png"; } // Title text @@ -137,16 +236,17 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: 40; anchors.left: parent.left; - anchors.leftMargin: 45; + anchors.leftMargin: 36; anchors.right: parent.right; + anchors.rightMargin: 8; height: paintedHeight; // Style - color: hifi.colors.darkGray; + color: root.certTitleTextColor; + wrapMode: Text.WordWrap; } // Title text RalewayRegular { id: popText; - text: "Proof of Provenance"; // Text size size: 16; // Anchors @@ -154,9 +254,39 @@ Rectangle { anchors.topMargin: 4; anchors.left: titleBarText.left; anchors.right: titleBarText.right; - height: paintedHeight; + height: text === "" ? 0 : paintedHeight; // Style - color: hifi.colors.darkGray; + color: root.certTitleTextColor; + } + + // "Close" button + HiFiGlyphs { + z: 999; + id: closeGlyphButton; + text: hifi.glyphs.close; + color: hifi.colors.white; + size: 26; + anchors.top: parent.top; + anchors.topMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 10; + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.text = hifi.glyphs.closeInverted; + } + onExited: { + parent.text = hifi.glyphs.close; + } + onClicked: { + if (root.isLightbox) { + root.visible = false; + } else { + sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases}); + } + } + } } // @@ -164,11 +294,13 @@ Rectangle { // Item { id: certificateContainer; - anchors.top: popText.bottom; - anchors.topMargin: 30; - anchors.bottom: buttonsContainer.top; + anchors.top: titleBarText.top; + anchors.topMargin: 110; + anchors.bottom: infoContainer.top; anchors.left: parent.left; + anchors.leftMargin: titleBarText.anchors.leftMargin; anchors.right: parent.right; + anchors.rightMargin: 24; RalewayRegular { id: itemNameHeader; @@ -178,9 +310,7 @@ Rectangle { // Anchors anchors.top: parent.top; anchors.left: parent.left; - anchors.leftMargin: 45; anchors.right: parent.right; - anchors.rightMargin: 16; height: paintedHeight; // Style color: hifi.colors.darkGray; @@ -197,79 +327,30 @@ Rectangle { anchors.right: itemNameHeader.right; height: paintedHeight; // Style - color: hifi.colors.white; + color: root.certTextColor; elide: Text.ElideRight; MouseArea { + enabled: showInMarketplaceButton.visible; anchors.fill: parent; hoverEnabled: enabled; onClicked: { sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl}); } onEntered: itemName.color = hifi.colors.blueHighlight; - onExited: itemName.color = hifi.colors.white; + onExited: itemName.color = root.certTextColor; } } - RalewayRegular { - id: ownedByHeader; - text: "OWNER"; - // Text size - size: 16; - // Anchors - anchors.top: itemName.bottom; - anchors.topMargin: 28; - anchors.left: parent.left; - anchors.leftMargin: 45; - anchors.right: parent.right; - anchors.rightMargin: 16; - height: paintedHeight; - // Style - color: hifi.colors.darkGray; - } - RalewayRegular { - id: ownedBy; - text: root.itemOwner; - // Text size - size: 22; - // Anchors - anchors.top: ownedByHeader.bottom; - anchors.topMargin: 8; - anchors.left: ownedByHeader.left; - height: paintedHeight; - // Style - color: hifi.colors.white; - elide: Text.ElideRight; - } - AnonymousProRegular { - id: isMyCertText; - visible: root.isMyCert && !root.isCertificateInvalid; - text: "(Private)"; - size: 18; - // Anchors - anchors.top: ownedBy.top; - anchors.topMargin: 4; - anchors.bottom: ownedBy.bottom; - anchors.left: ownedBy.right; - anchors.leftMargin: 6; - anchors.right: ownedByHeader.right; - // Style - color: hifi.colors.white; - elide: Text.ElideRight; - verticalAlignment: Text.AlignVCenter; - } - RalewayRegular { id: editionHeader; text: "EDITION"; // Text size size: 16; // Anchors - anchors.top: ownedBy.bottom; + anchors.top: itemName.bottom; anchors.topMargin: 28; anchors.left: parent.left; - anchors.leftMargin: 45; anchors.right: parent.right; - anchors.rightMargin: 16; height: paintedHeight; // Style color: hifi.colors.darkGray; @@ -286,21 +367,117 @@ Rectangle { anchors.right: editionHeader.right; height: paintedHeight; // Style - color: hifi.colors.white; + color: root.certTextColor; + } + + // "Show In Marketplace" button + HifiControlsUit.Button { + id: showInMarketplaceButton; + enabled: root.marketplaceUrl; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.light; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 48; + anchors.right: parent.right; + width: 200; + height: 40; + text: "View In Market" + onClicked: { + sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl}); + } + } + } + // + // "CERTIFICATE" END + // + + // + // "INFO CONTAINER" START + // + Item { + id: infoContainer; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.leftMargin: titleBarText.anchors.leftMargin; + anchors.right: parent.right; + anchors.rightMargin: 24; + height: root.useGoldCert ? 220 : 372; + + RalewayRegular { + id: errorText; + visible: !root.useGoldCert; + // Text size + size: 20; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 36; + anchors.left: parent.left; + anchors.right: parent.right; + height: 116; + // Style + wrapMode: Text.WordWrap; + color: hifi.colors.baseGray; + verticalAlignment: Text.AlignTop; + } + + RalewayRegular { + id: ownedByHeader; + text: "OWNER"; + // Text size + size: 16; + // Anchors + anchors.top: errorText.visible ? errorText.bottom : parent.top; + anchors.topMargin: 28; + anchors.left: parent.left; + anchors.right: parent.right; + height: paintedHeight; + // Style + color: hifi.colors.darkGray; + } + + RalewayRegular { + id: ownedBy; + text: root.itemOwner; + // Text size + size: 22; + // Anchors + anchors.top: ownedByHeader.bottom; + anchors.topMargin: 8; + anchors.left: ownedByHeader.left; + height: paintedHeight; + // Style + color: root.infoTextColor; + elide: Text.ElideRight; + } + AnonymousProRegular { + id: isMyCertText; + visible: root.isMyCert && ownedBy.text !== "--" && ownedBy.text !== ""; + text: "(Private)"; + size: 18; + // Anchors + anchors.top: ownedBy.top; + anchors.topMargin: 4; + anchors.bottom: ownedBy.bottom; + anchors.left: ownedBy.right; + anchors.leftMargin: 6; + anchors.right: ownedByHeader.right; + // Style + color: root.infoTextColor; + elide: Text.ElideRight; + verticalAlignment: Text.AlignVCenter; } RalewayRegular { id: dateOfPurchaseHeader; - text: "DATE OF PURCHASE"; + text: "PURCHASE DATE"; // Text size size: 16; // Anchors - anchors.top: edition.bottom; + anchors.top: ownedBy.bottom; anchors.topMargin: 28; anchors.left: parent.left; - anchors.leftMargin: 45; - anchors.right: parent.right; - anchors.rightMargin: 16; + anchors.right: parent.horizontalCenter; + anchors.rightMargin: 8; height: paintedHeight; // Style color: hifi.colors.darkGray; @@ -317,73 +494,58 @@ Rectangle { anchors.right: dateOfPurchaseHeader.right; height: paintedHeight; // Style - color: hifi.colors.white; + color: root.infoTextColor; } RalewayRegular { - id: errorText; + id: priceHeader; + text: "PURCHASE PRICE"; // Text size - size: 20; + size: 16; // Anchors - anchors.top: dateOfPurchase.bottom; - anchors.topMargin: 36; - anchors.left: dateOfPurchase.left; - anchors.right: dateOfPurchase.right; - anchors.bottom: parent.bottom; - // Style - wrapMode: Text.WordWrap; - color: hifi.colors.redHighlight; - verticalAlignment: Text.AlignTop; - } - } - // - // "CERTIFICATE" END - // - - Item { - id: buttonsContainer; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 30; - anchors.left: parent.left; - anchors.right: parent.right; - height: 50; - - // "Cancel" button - HifiControlsUit.Button { - color: hifi.buttons.noneBorderlessWhite; - colorScheme: hifi.colorSchemes.light; - anchors.top: parent.top; - anchors.left: parent.left; - anchors.leftMargin: 30; - width: parent.width/2 - 50; - height: 50; - text: "close"; - onClicked: { - if (root.isLightbox) { - root.visible = false; - } else { - sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases}); - } - } - } - - // "Show In Marketplace" button - HifiControlsUit.Button { - id: showInMarketplaceButton; - enabled: root.marketplaceUrl; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.light; - anchors.top: parent.top; + anchors.top: ownedBy.bottom; + anchors.topMargin: 28; + anchors.left: parent.horizontalCenter; anchors.right: parent.right; - anchors.rightMargin: 30; - width: parent.width/2 - 50; - height: 50; - text: "View In Market" - onClicked: { - sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl}); - } + height: paintedHeight; + // Style + color: hifi.colors.darkGray; + } + HiFiGlyphs { + id: hfcGlyph; + visible: priceText.text !== "Undisclosed" && priceText.text !== ""; + text: hifi.glyphs.hfc; + // Size + size: 24; + // Anchors + anchors.top: priceHeader.bottom; + anchors.topMargin: 8; + anchors.left: priceHeader.left; + width: visible ? paintedWidth + 6 : 0; + height: 40; + // Style + color: root.infoTextColor; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignLeft; + } + AnonymousProRegular { + id: priceText; + text: root.itemCost; + // Text size + size: 18; + // Anchors + anchors.top: priceHeader.bottom; + anchors.topMargin: 8; + anchors.left: hfcGlyph.right; + anchors.right: priceHeader.right; + height: paintedHeight; + // Style + color: root.infoTextColor; } } + // + // "INFO CONTAINER" END + // // // FUNCTION DEFINITIONS START @@ -404,19 +566,17 @@ Rectangle { function fromScript(message) { switch (message.method) { case 'inspectionCertificate_setCertificateId': + resetCert(false); root.certificateId = message.certificateId; + if (message.entityId === "") { + updateCertificateStatus(1); // CERTIFICATE_STATUS_VERIFICATION_SUCCESS + } else { + root.entityId = message.entityId; + sendToScript({method: 'inspectionCertificate_requestOwnershipVerification', entity: root.entityId}); + } break; case 'inspectionCertificate_resetCert': - titleBarText.text = "Certificate"; - popText.text = "PROOF OF PURCHASE"; - root.certificateId = ""; - root.itemName = "--"; - root.itemOwner = "--"; - root.itemEdition = "--"; - root.dateOfPurchase = "--"; - root.marketplaceUrl = ""; - root.isMyCert = false; - errorText.text = ""; + resetCert(true); break; default: console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message)); @@ -424,7 +584,34 @@ Rectangle { } signal sendToScript(var message); + function resetCert(alsoResetCertID) { + if (alsoResetCertID) { + root.entityId = ""; + root.certificateId = ""; + } + root.certInfoReplaceMode = 5; + root.certificateInfoPending = true; + root.certificateStatusPending = true; + root.useGoldCert = true; + root.certTitleTextColor = hifi.colors.darkGray; + root.certTextColor = hifi.colors.white; + root.infoTextColor = hifi.colors.blueAccent; + titleBarText.text = "Certificate"; + popText.text = ""; + root.itemName = "--"; + root.itemOwner = "--"; + root.itemEdition = "--"; + root.dateOfPurchase = "--"; + root.marketplaceUrl = ""; + root.itemCost = "--"; + root.isMyCert = false; + errorText.text = ""; + } + function getFormattedDate(timestamp) { + if (timestamp === "--") { + return "--"; + } function addLeadingZero(n) { return n < 10 ? '0' + n : '' + n; } @@ -449,7 +636,7 @@ Rectangle { var min = addLeadingZero(a.getMinutes()); var sec = addLeadingZero(a.getSeconds()); - return year + '-' + month + '-' + day + '
' + drawnHour + ':' + min + amOrPm; + return year + '-' + month + '-' + day + ' ' + drawnHour + ':' + min + amOrPm; } // // FUNCTION DEFINITIONS END diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg-gold-split.png b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg-gold-split.png new file mode 100644 index 0000000000..14a17df0b1 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg-gold-split.png differ diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg deleted file mode 100644 index b39a55e4e8..0000000000 Binary files a/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg and /dev/null differ diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/images/nocert-bg-split.png b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/nocert-bg-split.png new file mode 100644 index 0000000000..b2f5a49265 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/nocert-bg-split.png differ diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml index ee1246c0c4..7dd72b904e 100644 --- a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml @@ -1115,7 +1115,7 @@ Item { AnimatedImage { id: sendingMoneyImage; - source: "./images/loader.gif" + source: "../../common/images/loader.gif" width: 96; height: width; anchors.verticalCenter: parent.verticalCenter; diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif deleted file mode 100644 index 0536bd1884..0000000000 Binary files a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif and /dev/null differ diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc new file mode 100644 index 0000000000..8da68e4e19 Binary files /dev/null and b/interface/resources/qml/js/Utils.jsc differ diff --git a/interface/resources/styles/log_dialog.qss b/interface/resources/styles/log_dialog.qss index e0ec17549d..ebf2fc7318 100644 --- a/interface/resources/styles/log_dialog.qss +++ b/interface/resources/styles/log_dialog.qss @@ -67,7 +67,7 @@ QPushButton#revealLogButton { font-size: 11px; } -QPushButton#showAllButton { +QPushButton#allLogsButton { font-family: Helvetica, Arial, sans-serif; background-color: #333333; color: #BBBBBB; @@ -112,4 +112,11 @@ QComboBox::drop-down { QComboBox::down-arrow { image: url(:/styles/filter.png); border-width: 0px; +} + +QLabel#messageCount { + font-family: Helvetica, Arial, sans-serif; + text-align: center; + color: #3d3d3d; + font-size: 11px; } \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1a54c94d53..337bcbcf35 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -318,7 +318,7 @@ static QTimer pingTimer; static bool DISABLE_WATCHDOG = true; #else static const QString DISABLE_WATCHDOG_FLAG{ "HIFI_DISABLE_WATCHDOG" }; -static bool DISABLE_WATCHDOG = QProcessEnvironment::systemEnvironment().contains(DISABLE_WATCHDOG_FLAG); +static bool DISABLE_WATCHDOG = nsightActive() || QProcessEnvironment::systemEnvironment().contains(DISABLE_WATCHDOG_FLAG); #endif #if defined(USE_GLES) @@ -415,20 +415,26 @@ public: *crashTrigger = 0xDEAD10CC; } + static void withPause(const std::function& lambda) { + pause(); + lambda(); + resume(); + } static void pause() { _paused = true; } static void resume() { - _paused = false; + // Update the heartbeat BEFORE resuming the checks updateHeartbeat(); + _paused = false; } void run() override { while (!_quit) { QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); // Don't do heartbeat detection under nsight - if (nsightActive() || _paused) { + if (_paused) { continue; } uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us @@ -797,6 +803,8 @@ std::shared_ptr _keyboardFocusHighlight{ nullptr }; OverlayID _keyboardFocusHighlightID{ UNKNOWN_OVERLAY_ID }; +OffscreenGLCanvas* _qmlShareContext { nullptr }; + // FIXME hack access to the internal share context for the Chromium helper // Normally we'd want to use QWebEngine::initialize(), but we can't because // our primary context is a QGLWidget, which can't easily be initialized to share @@ -1046,7 +1054,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); // you might think we could just do this in NodeList but we only want this connection for Interface - connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset); + connect(&nodeList->getDomainHandler(), SIGNAL(limitOfSilentDomainCheckInsReached()), + nodeList.data(), SLOT(reset())); auto dialogsManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog); @@ -2258,18 +2267,37 @@ void Application::initializeGL() { _isGLInitialized = true; } + // Build a shared canvas / context for the Chromium processes _glWidget->makeCurrent(); #if !defined(DISABLE_QML) + // Chromium rendering uses some GL functions that prevent nSight from capturing + // frames, so we only create the shared context if nsight is NOT active. if (!nsightActive()) { _chromiumShareContext = new OffscreenGLCanvas(); _chromiumShareContext->setObjectName("ChromiumShareContext"); _chromiumShareContext->create(_glWidget->qglContext()); _chromiumShareContext->makeCurrent(); + if (!_chromiumShareContext->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make chromium shared context current"); + } qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + } else { + qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering"; } #endif + // Build a shared canvas / context for the QML rendering + _glWidget->makeCurrent(); + _qmlShareContext = new OffscreenGLCanvas(); + _qmlShareContext->setObjectName("QmlShareContext"); + _qmlShareContext->create(_glWidget->qglContext()); + if (!_qmlShareContext->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make QML shared context current"); + } + OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext()); + _qmlShareContext->doneCurrent(); + _glWidget->makeCurrent(); gpu::Context::init(); qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK, @@ -2282,29 +2310,22 @@ void Application::initializeGL() { initDisplay(); qCDebug(interfaceapp, "Initialized Display."); -#ifdef Q_OS_OSX - // FIXME: on mac os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. - DeadlockWatchdogThread::pause(); -#endif - - // Set up the render engine - render::CullFunctor cullFunctor = LODManager::shouldRender; - static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; - _renderEngine->addJob("UpdateScene"); + // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. + DeadlockWatchdogThread::withPause([&] { + // Set up the render engine + render::CullFunctor cullFunctor = LODManager::shouldRender; + static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; + _renderEngine->addJob("UpdateScene"); #ifndef Q_OS_ANDROID - _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED); + _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED); #endif - _renderEngine->addJob("RenderMainView", cullFunctor, !DISABLE_DEFERRED, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0); + _renderEngine->addJob("RenderMainView", cullFunctor, !DISABLE_DEFERRED, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0); + _renderEngine->load(); + _renderEngine->registerScene(_main3DScene); - _renderEngine->load(); - _renderEngine->registerScene(_main3DScene); - - // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. - DependencyManager::get()->initializeShapePipelines(); - -#ifdef Q_OS_OSX - DeadlockWatchdogThread::resume(); -#endif + // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. + DependencyManager::get()->initializeShapePipelines(); + }); _offscreenContext = new OffscreenGLCanvas(); _offscreenContext->setObjectName("MainThreadContext"); @@ -2312,20 +2333,23 @@ void Application::initializeGL() { if (!_offscreenContext->makeCurrent()) { qFatal("Unable to make offscreen context current"); } + _offscreenContext->doneCurrent(); + _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); // The UI can't be created until the primary OpenGL // context is created, because it needs to share // texture resources // Needs to happen AFTER the render engine initialization to access its configuration + if (!_offscreenContext->makeCurrent()) { + qFatal("Unable to make offscreen context current"); + } + initializeUi(); qCDebug(interfaceapp, "Initialized Offscreen UI."); - _glWidget->makeCurrent(); - - - // call Menu getInstance static method to set up the menu - // Needs to happen AFTER the QML UI initialization - _window->setMenuBar(Menu::getInstance()); + if (!_offscreenContext->makeCurrent()) { + qFatal("Unable to make offscreen context current"); + } init(); qCDebug(interfaceapp, "init() complete."); @@ -2336,7 +2360,6 @@ void Application::initializeGL() { _idleLoopStdev.reset(); - _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); // Restore the primary GL content for the main thread if (!_offscreenContext->makeCurrent()) { @@ -2402,27 +2425,70 @@ void Application::initializeUi() { auto tabletScriptingInterface = DependencyManager::get(); tabletScriptingInterface->getTablet(SYSTEM_TABLET); } - auto offscreenUi = DependencyManager::get(); - offscreenUi->create(); - auto surfaceContext = offscreenUi->getSurfaceContext(); + auto offscreenUi = DependencyManager::get(); + connect(offscreenUi.data(), &hifi::qml::OffscreenSurface::rootContextCreated, + this, &Application::onDesktopRootContextCreated); + connect(offscreenUi.data(), &hifi::qml::OffscreenSurface::rootItemCreated, + this, &Application::onDesktopRootItemCreated); offscreenUi->setProxyWindow(_window->windowHandle()); // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to // support the window management and scripting proxies for VR use - offscreenUi->createDesktop(PathUtils::qmlUrl("hifi/Desktop.qml")); + DeadlockWatchdogThread::withPause([&] { + offscreenUi->createDesktop(PathUtils::qmlUrl("hifi/Desktop.qml")); + }); // FIXME either expose so that dialogs can set this themselves or // do better detection in the offscreen UI of what has focus offscreenUi->setNavigationFocused(false); - auto engine = surfaceContext->engine(); - connect(engine, &QQmlEngine::quit, [] { - qApp->quit(); - }); - - setupPreferences(); + _glWidget->installEventFilter(offscreenUi.data()); + offscreenUi->setMouseTranslator([=](const QPointF& pt) { + QPointF result = pt; + auto displayPlugin = getActiveDisplayPlugin(); + if (displayPlugin->isHmd()) { + getApplicationCompositor().handleRealMouseMoveEvent(false); + auto resultVec = getApplicationCompositor().getReticlePosition(); + result = QPointF(resultVec.x, resultVec.y); + } + return result.toPoint(); + }); + offscreenUi->resume(); + connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){ + resizeGL(); + }); + + // This will set up the input plugins UI + _activeInputPlugins.clear(); + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { + if (KeyboardMouseDevice::NAME == inputPlugin->getName()) { + _keyboardMouseDevice = std::dynamic_pointer_cast(inputPlugin); + } + if (TouchscreenDevice::NAME == inputPlugin->getName()) { + _touchscreenDevice = std::dynamic_pointer_cast(inputPlugin); + } + } + + auto compositorHelper = DependencyManager::get(); + connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, this, [=] { + if (isHMDMode()) { + showCursor(compositorHelper->getAllowMouseCapture() ? + Cursor::Manager::lookupIcon(_preferredCursor.get()) : + Cursor::Icon::SYSTEM); + } + }); + + // Pre-create a couple of Web3D overlays to speed up tablet UI + auto offscreenSurfaceCache = DependencyManager::get(); + offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1); + offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); +} + + +void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { + auto engine = surfaceContext->engine(); // in Qt 5.10.0 there is already an "Audio" object in the QML context // though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface" surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get().data()); @@ -2504,48 +2570,12 @@ void Application::initializeUi() { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); } - - _glWidget->installEventFilter(offscreenUi.data()); - offscreenUi->setMouseTranslator([=](const QPointF& pt) { - QPointF result = pt; - auto displayPlugin = getActiveDisplayPlugin(); - if (displayPlugin->isHmd()) { - getApplicationCompositor().handleRealMouseMoveEvent(false); - auto resultVec = getApplicationCompositor().getReticlePosition(); - result = QPointF(resultVec.x, resultVec.y); - } - return result.toPoint(); - }); - offscreenUi->resume(); - connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){ - resizeGL(); - }); - - // This will set up the input plugins UI - _activeInputPlugins.clear(); - foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { - if (KeyboardMouseDevice::NAME == inputPlugin->getName()) { - _keyboardMouseDevice = std::dynamic_pointer_cast(inputPlugin); - } - if (TouchscreenDevice::NAME == inputPlugin->getName()) { - _touchscreenDevice = std::dynamic_pointer_cast(inputPlugin); - } - } _window->setMenuBar(new Menu()); +} - auto compositorHelper = DependencyManager::get(); - connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, this, [=] { - if (isHMDMode()) { - showCursor(compositorHelper->getAllowMouseCapture() ? - Cursor::Manager::lookupIcon(_preferredCursor.get()) : - Cursor::Icon::SYSTEM); - } - }); - - // Pre-create a couple of Web3D overlays to speed up tablet UI - auto offscreenSurfaceCache = DependencyManager::get(); - offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1); - offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); +void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { + Stats::show(); + AvatarInputs::show(); } void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { @@ -2784,6 +2814,8 @@ void Application::resizeGL() { QMutexLocker viewLocker(&_viewMutex); _myCamera.loadViewFrustum(_viewFrustum); } + + DependencyManager::get()->resize(fromGlm(displayPlugin->getRecommendedUiSize())); } void Application::handleSandboxStatus(QNetworkReply* reply) { @@ -3992,7 +4024,7 @@ void Application::idle() { // Bit of a hack since there's no device pixel ratio change event I can find. if (offscreenUi->size() != fromGlm(uiSize)) { qCDebug(interfaceapp) << "Device pixel ratio changed, triggering resize to " << uiSize; - offscreenUi->resize(fromGlm(uiSize), true); + offscreenUi->resize(fromGlm(uiSize)); _offscreenContext->makeCurrent(); } } @@ -6933,10 +6965,10 @@ void Application::loadAvatarBrowser() const { DependencyManager::get()->openTablet(); } -void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { - postLambdaEvent([notify, includeAnimated, aspectRatio, this] { +void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { + postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] { // Get a screenshot and save it - QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio)); + QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename); // If we're not doing an animated snapshot as well... if (!includeAnimated) { // Tell the dependency manager that the capture of the still snapshot has taken place. @@ -6948,9 +6980,9 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa }); } -void Application::takeSecondaryCameraSnapshot() { - postLambdaEvent([this] { - QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot()); +void Application::takeSecondaryCameraSnapshot(const QString& filename) { + postLambdaEvent([filename, this] { + QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename); emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, true); }); } @@ -7324,9 +7356,7 @@ void Application::updateDisplayMode() { action->setChecked(true); } - _offscreenContext->makeCurrent(); offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); - _offscreenContext->makeCurrent(); getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); _displayPlugin = newDisplayPlugin; connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent, Qt::DirectConnection); diff --git a/interface/src/Application.h b/interface/src/Application.h index ddb8ce11e5..7821369b4a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -266,8 +266,10 @@ public: float getGameLoopRate() const { return _gameLoopCounter.rate(); } - void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f); - void takeSecondaryCameraSnapshot(); + // Note that takeSnapshot has a default value, as this method is used internally. + void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString()); + void takeSecondaryCameraSnapshot(const QString& filename); + void shareSnapshot(const QString& filename, const QUrl& href = QUrl("")); graphics::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; } @@ -389,6 +391,8 @@ public slots: void setPreferredCursor(const QString& cursor); private slots: + void onDesktopRootItemCreated(QQuickItem* qmlContext); + void onDesktopRootContextCreated(QQmlContext* qmlContext); void showDesktop(); void clearDomainOctreeDetails(); void clearDomainAvatars(); diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index e1f198eed2..5cc072df37 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -55,7 +55,7 @@ void Application::paintGL() { // If a display plugin loses it's underlying support, it // needs to be able to signal us to not use it if (!displayPlugin->beginFrameRender(_renderFrameCount)) { - updateDisplayMode(); + QMetaObject::invokeMethod(this, "updateDisplayMode"); return; } } diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 5b9c45eef5..0f177e3d15 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -121,13 +121,13 @@ void AvatarBookmarks::addBookmark() { const QVariant& avatarScale = myAvatar->getAvatarScale(); // If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION - QVariantMap *bookmark = new QVariantMap; - bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); - bookmark->insert(ENTRY_AVATAR_URL, avatarUrl); - bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale); - bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant()); + QVariantMap bookmark; + bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); + bookmark.insert(ENTRY_AVATAR_URL, avatarUrl); + bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale); + bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant()); - Bookmarks::addBookmarkToFile(bookmarkName, *bookmark); + Bookmarks::addBookmarkToFile(bookmarkName, bookmark); }); } diff --git a/interface/src/AvatarEntitiesBookmarks.cpp b/interface/src/AvatarEntitiesBookmarks.cpp index d108bf3a23..21d3657346 100644 --- a/interface/src/AvatarEntitiesBookmarks.cpp +++ b/interface/src/AvatarEntitiesBookmarks.cpp @@ -144,13 +144,13 @@ void AvatarEntitiesBookmarks::addBookmark() { const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString(); const QVariant& avatarScale = myAvatar->getAvatarScale(); - QVariantMap *bookmark = new QVariantMap; - bookmark->insert(ENTRY_VERSION, AVATAR_ENTITIES_BOOKMARK_VERSION); - bookmark->insert(ENTRY_AVATAR_URL, avatarUrl); - bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale); - bookmark->insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant()); + QVariantMap bookmark; + bookmark.insert(ENTRY_VERSION, AVATAR_ENTITIES_BOOKMARK_VERSION); + bookmark.insert(ENTRY_AVATAR_URL, avatarUrl); + bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale); + bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant()); - Bookmarks::addBookmarkToFile(bookmarkName, *bookmark); + Bookmarks::addBookmarkToFile(bookmarkName, bookmark); }); } diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 73547cdb5e..73408377c0 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -168,7 +168,7 @@ float LODManager::getDesktopLODDecreaseFPS() const { } float LODManager::getDesktopLODIncreaseFPS() const { - return glm::max(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_DESKTOP_FPS); + return glm::min(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_DESKTOP_FPS); } void LODManager::setHMDLODDecreaseFPS(float fps) { @@ -184,7 +184,7 @@ float LODManager::getHMDLODDecreaseFPS() const { } float LODManager::getHMDLODIncreaseFPS() const { - return glm::max(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_HMD_FPS); + return glm::min(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_HMD_FPS); } QString LODManager::getLODFeedbackText() { diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 7b10579077..a12f809efe 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -25,7 +25,7 @@ const float DEFAULT_DESKTOP_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_ const float DEFAULT_HMD_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_HMD_LOD_DOWN_FPS; // msec const float MAX_LIKELY_DESKTOP_FPS = 59.0f; // this is essentially, V-synch - 1 fps const float MAX_LIKELY_HMD_FPS = 74.0f; // this is essentially, V-synch - 1 fps -const float INCREASE_LOD_GAP_FPS = 15.0f; // fps +const float INCREASE_LOD_GAP_FPS = 10.0f; // fps // The default value DEFAULT_OCTREE_SIZE_SCALE means you can be 400 meters away from a 1 meter object in order to see it (which is ~20:20 vision). const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE; diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 66184513da..d4fe8574ca 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -31,6 +31,9 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit myAvatar->addHoldAction(this); } + _positionalTargetSet = true; + _rotationalTargetSet = true; + #if WANT_DEBUG qDebug() << "AvatarActionHold::AvatarActionHold" << (void*)this; #endif diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d2a9e798f8..ba4d48fd3f 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -959,6 +959,18 @@ void MyAvatar::restoreRoleAnimation(const QString& role) { _skeletonModel->getRig().restoreRoleAnimation(role); } +void MyAvatar::saveAvatarUrl() { + Settings settings; + settings.beginGroup("Avatar"); + if (qApp->getSaveAvatarOverrideUrl() || !qApp->getAvatarOverrideUrl().isValid() ) { + settings.setValue("fullAvatarURL", + _fullAvatarURLFromPreferences == AvatarData::defaultFullAvatarModelUrl() ? + "" : + _fullAvatarURLFromPreferences.toString()); + } + settings.endGroup(); +} + void MyAvatar::saveData() { Settings settings; settings.beginGroup("Avatar"); @@ -1452,6 +1464,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE); _headBoneSet.clear(); _cauterizationNeedsUpdate = true; + saveAvatarUrl(); emit skeletonChanged(); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 97e82b87f5..124902a1b1 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -646,6 +646,7 @@ private: void simulate(float deltaTime); void updateFromTrackers(float deltaTime); + void saveAvatarUrl(); virtual void render(RenderArgs* renderArgs) override; virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); } diff --git a/interface/src/scripting/LimitlessConnection.cpp b/interface/src/scripting/LimitlessConnection.cpp index b9f4eacd4b..f504136489 100644 --- a/interface/src/scripting/LimitlessConnection.cpp +++ b/interface/src/scripting/LimitlessConnection.cpp @@ -1,8 +1,10 @@ +#include "LimitlessConnection.h" + +#include #include #include #include #include -#include "LimitlessConnection.h" #include "LimitlessVoiceRecognitionScriptingInterface.h" LimitlessConnection::LimitlessConnection() : diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index d419d7484b..3a3d313fb4 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -396,11 +396,11 @@ QString WindowScriptingInterface::protocolSignature() { } int WindowScriptingInterface::getInnerWidth() { - return qApp->getDeviceSize().x; + return qApp->getWindow()->geometry().width(); } int WindowScriptingInterface::getInnerHeight() { - return qApp->getDeviceSize().y; + return qApp->getWindow()->geometry().height() - qApp->getPrimaryMenu()->geometry().height(); } glm::vec2 WindowScriptingInterface::getDeviceSize() const { @@ -430,12 +430,12 @@ bool WindowScriptingInterface::setDisplayTexture(const QString& name) { return qApp->getActiveDisplayPlugin()->setDisplayTexture(name); // Plugins that don't know how, answer false. } -void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { - qApp->takeSnapshot(notify, includeAnimated, aspectRatio); +void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { + qApp->takeSnapshot(notify, includeAnimated, aspectRatio, filename); } -void WindowScriptingInterface::takeSecondaryCameraSnapshot() { - qApp->takeSecondaryCameraSnapshot(); +void WindowScriptingInterface::takeSecondaryCameraSnapshot(const QString& filename) { + qApp->takeSecondaryCameraSnapshot(filename); } void WindowScriptingInterface::shareSnapshot(const QString& path, const QUrl& href) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index dc71611c5b..76decf4362 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -334,6 +334,8 @@ public slots: * @param {number} aspectRatio=0 - The width/height ratio of the snapshot required. If the value is 0 the * full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the * dimensions is adjusted in order to match the aspect ratio. + * @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ",jpg". + * otherwise, the image will be saved as 'hifi-snap-by--YYYY-MM-DD_HH-MM-SS' * @example Using the snapshot function and signals. * function onStillSnapshotTaken(path, notify) { * print("Still snapshot taken: " + path); @@ -355,15 +357,19 @@ public slots: * var notify = true; * var animated = true; * var aspect = 1920 / 1080; - * Window.takeSnapshot(notify, animated, aspect); + * var filename = QString(); + * Window.takeSnapshot(notify, animated, aspect, filename); */ - void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f); + void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString()); /**jsdoc * Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API. * @function Window.takeSecondaryCameraSnapshot + * @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ".jpg" + * + * var filename = QString(); */ - void takeSecondaryCameraSnapshot(); + void takeSecondaryCameraSnapshot(const QString& filename = QString()); /**jsdoc * Emit a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index ccac6d7207..fe79bec6ef 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -25,7 +25,6 @@ Setting::Handle showAudioToolsSetting { QStringList { "AvatarInputs", "sho AvatarInputs* AvatarInputs::getInstance() { if (!INSTANCE) { AvatarInputs::registerType(); - AvatarInputs::show(); Q_ASSERT(INSTANCE); } return INSTANCE; diff --git a/interface/src/ui/BaseLogDialog.cpp b/interface/src/ui/BaseLogDialog.cpp index 969f9895de..e27b622262 100644 --- a/interface/src/ui/BaseLogDialog.cpp +++ b/interface/src/ui/BaseLogDialog.cpp @@ -29,8 +29,8 @@ const int SEARCH_TOGGLE_BUTTON_WIDTH = 50; const int SEARCH_TEXT_WIDTH = 240; const int TIME_STAMP_LENGTH = 16; const int FONT_WEIGHT = 75; -const QColor HIGHLIGHT_COLOR = QColor("#3366CC"); -const QColor BOLD_COLOR = QColor("#445c8c"); +const QColor HIGHLIGHT_COLOR = QColor("#00B4EF"); +const QColor BOLD_COLOR = QColor("#1080B8"); const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]"; BaseLogDialog::BaseLogDialog(QWidget* parent) : QDialog(parent, Qt::Window) { @@ -182,6 +182,7 @@ void BaseLogDialog::updateSelection() { Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) { boldFormat.setFontWeight(FONT_WEIGHT); boldFormat.setForeground(BOLD_COLOR); + keywordFormat.setFontWeight(FONT_WEIGHT); keywordFormat.setForeground(HIGHLIGHT_COLOR); } diff --git a/interface/src/ui/LogDialog.cpp b/interface/src/ui/LogDialog.cpp index 108edbfd39..26a5a24de8 100644 --- a/interface/src/ui/LogDialog.cpp +++ b/interface/src/ui/LogDialog.cpp @@ -15,11 +15,12 @@ #include #include #include +#include #include const int REVEAL_BUTTON_WIDTH = 122; -const int CLEAR_FILTER_BUTTON_WIDTH = 80; +const int ALL_LOGS_BUTTON_WIDTH = 90; const int MARGIN_LEFT = 25; const int DEBUG_CHECKBOX_WIDTH = 70; const int INFO_CHECKBOX_WIDTH = 65; @@ -142,6 +143,11 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _filterDropdown->addItem("qml"); connect(_filterDropdown, static_cast(&QComboBox::currentIndexChanged), this, &LogDialog::handleFilterDropdownChanged); + _leftPad += COMBOBOX_WIDTH + MARGIN_LEFT + MARGIN_LEFT; + _messageCount = new QLabel("", this); + _messageCount->setObjectName("messageCount"); + _messageCount->show(); + _extraDebuggingBox = new QCheckBox("Extra debugging", this); if (_logger->extraDebugging()) { _extraDebuggingBox->setCheckState(Qt::Checked); @@ -149,12 +155,13 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog _extraDebuggingBox->show(); connect(_extraDebuggingBox, &QCheckBox::stateChanged, this, &LogDialog::handleExtraDebuggingCheckbox); - _clearFilterButton = new QPushButton("Clear Filters", this); + _allLogsButton = new QPushButton("All Messages", this); // set object name for css styling - _clearFilterButton->setObjectName("showAllButton"); - _clearFilterButton->show(); - connect(_clearFilterButton, &QPushButton::clicked, this, &LogDialog::handleClearFilterButton); - handleClearFilterButton(); + + _allLogsButton->setObjectName("allLogsButton"); + _allLogsButton->show(); + connect(_allLogsButton, &QPushButton::clicked, this, &LogDialog::handleAllLogsButton); + handleAllLogsButton(); auto windowGeometry = _windowGeometry.get(); if (windowGeometry.isValid()) { @@ -168,11 +175,15 @@ void LogDialog::resizeEvent(QResizeEvent* event) { ELEMENT_MARGIN, REVEAL_BUTTON_WIDTH, ELEMENT_HEIGHT); - _clearFilterButton->setGeometry(width() - ELEMENT_MARGIN - CLEAR_FILTER_BUTTON_WIDTH, + _allLogsButton->setGeometry(width() - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH, THIRD_ROW, - CLEAR_FILTER_BUTTON_WIDTH, + ALL_LOGS_BUTTON_WIDTH, ELEMENT_HEIGHT); - _extraDebuggingBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - CLEAR_FILTER_BUTTON_WIDTH, + _extraDebuggingBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH, + THIRD_ROW, + COMBOBOX_WIDTH, + ELEMENT_HEIGHT); + _messageCount->setGeometry(_leftPad, THIRD_ROW, COMBOBOX_WIDTH, ELEMENT_HEIGHT); @@ -187,13 +198,13 @@ void LogDialog::handleRevealButton() { _logger->locateLog(); } -void LogDialog::handleClearFilterButton() { +void LogDialog::handleAllLogsButton() { _logger->setExtraDebugging(false); _extraDebuggingBox->setCheckState(Qt::Unchecked); - _logger->setDebugPrint(false); - _debugPrintBox->setCheckState(Qt::Unchecked); - _logger->setInfoPrint(false); - _infoPrintBox->setCheckState(Qt::Unchecked); + _logger->setDebugPrint(true); + _debugPrintBox->setCheckState(Qt::Checked); + _logger->setInfoPrint(true); + _infoPrintBox->setCheckState(Qt::Checked); _logger->setCriticalPrint(true); _criticalPrintBox->setCheckState(Qt::Checked); _logger->setWarningPrint(true); @@ -270,40 +281,67 @@ void LogDialog::appendLogLine(QString logLine) { if (logLine.contains(DEBUG_TEXT, Qt::CaseSensitive)) { if (_logger->debugPrint()) { _logTextBox->appendPlainText(logLine.trimmed()); + _count++; + updateMessageCount(); } } else if (logLine.contains(INFO_TEXT, Qt::CaseSensitive)) { if (_logger->infoPrint()) { _logTextBox->appendPlainText(logLine.trimmed()); + _count++; + updateMessageCount(); } } else if (logLine.contains(CRITICAL_TEXT, Qt::CaseSensitive)) { if (_logger->criticalPrint()) { _logTextBox->appendPlainText(logLine.trimmed()); + _count++; + updateMessageCount(); } } else if (logLine.contains(WARNING_TEXT, Qt::CaseSensitive)) { if (_logger->warningPrint()) { _logTextBox->appendPlainText(logLine.trimmed()); + _count++; + updateMessageCount(); } } else if (logLine.contains(SUPPRESS_TEXT, Qt::CaseSensitive)) { if (_logger->suppressPrint()) { _logTextBox->appendPlainText(logLine.trimmed()); + _count++; + updateMessageCount(); } } else if (logLine.contains(FATAL_TEXT, Qt::CaseSensitive)) { if (_logger->fatalPrint()) { _logTextBox->appendPlainText(logLine.trimmed()); + _count++; + updateMessageCount(); } } else { - if (_logger->unknownPrint()) { + if (_logger->unknownPrint() && logLine.trimmed() != "") { _logTextBox->appendPlainText(logLine.trimmed()); + _count++; + updateMessageCount(); } } } } void LogDialog::printLogFile() { + _count = 0; _logTextBox->clear(); QString log = getCurrentLog(); QStringList logList = log.split('\n'); for (const auto& message : logList) { appendLogLine(message); } + updateMessageCount(); +} + +void LogDialog::updateMessageCount() { + _countLabel = QString::number(_count); + if (_count != 1) { + _countLabel.append(" log messages"); + } + else { + _countLabel.append(" log message"); + } + _messageCount->setText(_countLabel); } diff --git a/interface/src/ui/LogDialog.h b/interface/src/ui/LogDialog.h index 3cc7584fe8..eb92d4b381 100644 --- a/interface/src/ui/LogDialog.h +++ b/interface/src/ui/LogDialog.h @@ -18,6 +18,7 @@ class QCheckBox; class QPushButton; class QComboBox; +class QLabel; class QResizeEvent; class AbstractLoggerInterface; @@ -41,19 +42,21 @@ private slots: void handleFatalPrintBox(int); void handleUnknownPrintBox(int); void handleFilterDropdownChanged(int); - void handleClearFilterButton(); + void handleAllLogsButton(); + void printLogFile(); protected: void resizeEvent(QResizeEvent* event) override; void closeEvent(QCloseEvent* event) override; QString getCurrentLog() override; - void printLogFile(); + void updateMessageCount(); + private: QCheckBox* _extraDebuggingBox; QPushButton* _revealLogButton; - QPushButton* _clearFilterButton; + QPushButton* _allLogsButton; QCheckBox* _debugPrintBox; QCheckBox* _infoPrintBox; QCheckBox* _criticalPrintBox; @@ -62,10 +65,12 @@ private: QCheckBox* _fatalPrintBox; QCheckBox* _unknownPrintBox; QComboBox* _filterDropdown; + QLabel* _messageCount; QString _filterSelection; - + QString _countLabel; AbstractLoggerInterface* _logger; Setting::Handle _windowGeometry; + int _count = 0; }; #endif // hifi_LogDialog_h diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 59ecce5bc7..9b3089d78d 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -73,9 +73,9 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { return data; } -QString Snapshot::saveSnapshot(QImage image) { +QString Snapshot::saveSnapshot(QImage image, const QString& filename) { - QFile* snapshotFile = savedFileForSnapshot(image, false); + QFile* snapshotFile = savedFileForSnapshot(image, false, filename); // we don't need the snapshot file, so close it, grab its filename and delete it snapshotFile->close(); @@ -92,7 +92,7 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) { return static_cast(savedFileForSnapshot(image, true)); } -QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) { +QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename) { // adding URL to snapshot QUrl currentURL = DependencyManager::get()->currentShareableAddress(); @@ -104,7 +104,15 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) { QDateTime now = QDateTime::currentDateTime(); - QString filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT)); + // If user has requested specific filename then use it, else create the filename + // 'jpg" is appended, as the image is saved in jpg format. This is the case for all snapshots + // (see definition of FILENAME_PATH_FORMAT) + QString filename; + if (!userSelectedFilename.isNull()) { + filename = userSelectedFilename + ".jpg"; + } else { + filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT)); + } const int IMAGE_QUALITY = 100; diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 1246e1d004..62d3ed3db8 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -37,7 +37,7 @@ class Snapshot : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY public: - static QString saveSnapshot(QImage image); + static QString saveSnapshot(QImage image, const QString& filename); static QTemporaryFile* saveTempSnapshot(QImage image); static SnapshotMetaData* parseSnapshotData(QString snapshotPath); @@ -51,7 +51,7 @@ public slots: Q_INVOKABLE QString getSnapshotsLocation(); Q_INVOKABLE void setSnapshotsLocation(const QString& location); private: - static QFile* savedFileForSnapshot(QImage & image, bool isTemporary); + static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename = QString()); }; #endif // hifi_Snapshot_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index c157898e74..80f57bfe0e 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -48,7 +48,6 @@ QString getTextureMemoryPressureModeString(); Stats* Stats::getInstance() { if (!INSTANCE) { Stats::registerType(); - Stats::show(); Q_ASSERT(INSTANCE); } return INSTANCE; diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 23e09fe5ca..ff5a202910 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -181,6 +181,8 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { if (properties["parentID"].isValid()) { setParentID(QUuid(properties["parentID"].toString())); + bool success; + getParentPointer(success); // call this to hook-up the parent's back-pointers to its child overlays needRenderItemUpdate = true; } if (properties["parentJointIndex"].isValid()) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index ed7b811fb0..dd05e5c6a8 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -274,82 +274,88 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID auto nodeList = DependencyManager::get(); - if (entityProperties.getClientOnly()) { - if (entityProperties.verifyStaticCertificateProperties()) { - SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); + if (entityProperties.verifyStaticCertificateProperties()) { + if (entityProperties.getClientOnly()) { + SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); - if (entityServer) { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); - requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); - QJsonObject request; - request["certificate_id"] = entityProperties.getCertificateID(); - networkRequest.setUrl(requestURL); + if (entityServer) { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); + QJsonObject request; + request["certificate_id"] = entityProperties.getCertificateID(); + networkRequest.setUrl(requestURL); - QNetworkReply* networkReply = NULL; - networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - connect(networkReply, &QNetworkReply::finished, [=]() { - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); - if (networkReply->error() == QNetworkReply::NoError) { - if (!jsonObject["invalid_reason"].toString().isEmpty()) { - qCDebug(entities) << "invalid_reason not empty"; - } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { - qCDebug(entities) << "'transfer_status' is 'failed'"; - } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { - qCDebug(entities) << "'transfer_status' is 'pending'"; - } else { - QString ownerKey = jsonObject["transfer_recipient_key"].toString(); - - QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); - QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); - - int certIDByteArraySize = certID.length(); - int textByteArraySize = text.length(); - int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); - - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(textByteArraySize); - challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); - challengeOwnershipPacket->write(certID); - challengeOwnershipPacket->write(text); - challengeOwnershipPacket->write(nodeToChallengeByteArray); - nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); - - // Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); - return; + if (networkReply->error() == QNetworkReply::NoError) { + if (!jsonObject["invalid_reason"].toString().isEmpty()) { + qCDebug(entities) << "invalid_reason not empty"; + } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { + qCDebug(entities) << "'transfer_status' is 'failed'"; + } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { + qCDebug(entities) << "'transfer_status' is 'pending'"; } else { - startChallengeOwnershipTimer(); - } - } - } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << - "More info:" << networkReply->readAll(); - } + QString ownerKey = jsonObject["transfer_recipient_key"].toString(); - networkReply->deleteLater(); - }); - } else { - qCWarning(context_overlay) << "Couldn't get Entity Server!"; - } + QByteArray certID = entityProperties.getCertificateID().toUtf8(); + QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); + QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); + + int certIDByteArraySize = certID.length(); + int textByteArraySize = text.length(); + int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); + + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, + certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(textByteArraySize); + challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); + challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(text); + challengeOwnershipPacket->write(nodeToChallengeByteArray); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); + + // Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); + return; + } else { + startChallengeOwnershipTimer(); + } + } + } else { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << + "More info:" << networkReply->readAll(); + } + + networkReply->deleteLater(); + }); + } else { + qCWarning(context_overlay) << "Couldn't get Entity Server!"; + } } else { + // We don't currently verify ownership of entities that aren't Avatar Entities, + // so they always pass Ownership Verification. It's necessary to emit this signal + // so that the Inspection Certificate can continue its information-grabbing process. auto ledger = DependencyManager::get(); - _challengeOwnershipTimeoutTimer.stop(); - emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); - emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); - qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; + emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); } + } else { + auto ledger = DependencyManager::get(); + _challengeOwnershipTimeoutTimer.stop(); + emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); + emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); + qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; } } @@ -357,12 +363,10 @@ static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspection void ContextOverlayInterface::openInspectionCertificate() { // lets open the tablet to the inspection certificate QML if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) { + setLastInspectedEntity(_currentEntityWithContextOverlay); auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH); _hmdScriptingInterface->openTablet(); - - setLastInspectedEntity(_currentEntityWithContextOverlay); - requestOwnershipVerification(_lastInspectedEntity); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 6aad2a773b..fcdf2d5820 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -57,7 +57,7 @@ public: bool getEnabled() { return _enabled; } bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; } - void requestOwnershipVerification(const QUuid& entityID); + Q_INVOKABLE void requestOwnershipVerification(const QUuid& entityID); EntityPropertyFlags getEntityPropertyFlags() { return _entityPropertyFlags; } signals: diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 7200abf74e..c2e5ad1fb4 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -63,7 +63,7 @@ glm::vec3 Line3DOverlay::getEnd() const { localEnd = getLocalEnd(); worldEnd = localToWorld(localEnd, getParentID(), getParentJointIndex(), getScalesWithParent(), success); if (!success) { - qDebug() << "Line3DOverlay::getEnd failed"; + qDebug() << "Line3DOverlay::getEnd failed, parentID = " << getParentID(); } return worldEnd; } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 5fb4d58340..d1a2ff3943 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -80,7 +80,6 @@ Web3DOverlay::Web3DOverlay() { _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED _webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); - } Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : @@ -135,7 +134,11 @@ void Web3DOverlay::destroyWebSurface() { QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); - DependencyManager::get()->release(QML, _webSurface); + auto offscreenCache = DependencyManager::get(); + // FIXME prevents crash on shutdown, but we shoudln't have to do this check + if (offscreenCache) { + offscreenCache->release(QML, _webSurface); + } _webSurface.reset(); } @@ -201,6 +204,11 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags); _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); + + _webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + _webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); + // in Qt 5.10.0 there is already an "Audio" object in the QML context // though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface" _webSurface->getSurfaceContext()->setContextProperty("AudioScriptingInterface", DependencyManager::get().data()); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index a939db92aa..1750a9e53d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1244,7 +1244,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand"); int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm"); int elbowJointIndex = _animSkeleton->nameToJointIndex("LeftForeArm"); - if (!leftArmEnabled && elbowJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) { + if (!leftArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) { glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, true); // smooth toward desired pole vector from previous pole vector... to reduce jitter @@ -1291,7 +1291,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab int handJointIndex = _animSkeleton->nameToJointIndex("RightHand"); int armJointIndex = _animSkeleton->nameToJointIndex("RightArm"); int elbowJointIndex = _animSkeleton->nameToJointIndex("RightForeArm"); - if (!rightArmEnabled && elbowJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) { + if (!rightArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) { glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, false); // smooth toward desired pole vector from previous pole vector... to reduce jitter @@ -1754,7 +1754,6 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { const AnimPoseVec& relativeDefaultPoses = _animSkeleton->getRelativeDefaultPoses(); for (int i = 0; i < numJoints; i++) { const JointData& data = jointDataVec.at(i); - _internalPoseSet._relativePoses[i].scale() = Vectors::ONE; _internalPoseSet._relativePoses[i].rot() = rotations[i]; if (data.translationIsDefaultPose) { _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); diff --git a/libraries/audio/src/AbstractAudioInterface.cpp b/libraries/audio/src/AbstractAudioInterface.cpp index 376ecddd34..3d813eafd7 100644 --- a/libraries/audio/src/AbstractAudioInterface.cpp +++ b/libraries/audio/src/AbstractAudioInterface.cpp @@ -49,6 +49,9 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes audioPacket->writePrimitive(channelFlag); } + // at this point we'd better be sending the mixer a valid position, or it won't consider us for mixing + assert(!isNaN(transform.getTranslation())); + // pack the three float positions audioPacket->writePrimitive(transform.getTranslation()); // pack the orientation diff --git a/libraries/audio/src/PositionalAudioStream.cpp b/libraries/audio/src/PositionalAudioStream.cpp index f2c1b05ef7..49b34a894e 100644 --- a/libraries/audio/src/PositionalAudioStream.cpp +++ b/libraries/audio/src/PositionalAudioStream.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -76,6 +77,19 @@ int PositionalAudioStream::parsePositionalData(const QByteArray& positionalByteA QDataStream packetStream(positionalByteArray); packetStream.readRawData(reinterpret_cast(&_position), sizeof(_position)); + + // if the client sends us a bad position, flag it so that we don't consider this stream for mixing + if (glm::isnan(_position.x) || glm::isnan(_position.y) || glm::isnan(_position.z)) { + static const QString INVALID_POSITION_REGEX = "PositionalAudioStream unpacked invalid position for node"; + static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(INVALID_POSITION_REGEX); + + qDebug() << "PositionalAudioStream unpacked invalid position for node" << uuidStringWithoutCurlyBraces(getNodeID()); + + _hasValidPosition = false; + } else { + _hasValidPosition = true; + } + packetStream.readRawData(reinterpret_cast(&_orientation), sizeof(_orientation)); packetStream.readRawData(reinterpret_cast(&_avatarBoundingBoxCorner), sizeof(_avatarBoundingBoxCorner)); packetStream.readRawData(reinterpret_cast(&_avatarBoundingBoxScale), sizeof(_avatarBoundingBoxScale)); diff --git a/libraries/audio/src/PositionalAudioStream.h b/libraries/audio/src/PositionalAudioStream.h index c5cfa1991e..4c48ea3386 100644 --- a/libraries/audio/src/PositionalAudioStream.h +++ b/libraries/audio/src/PositionalAudioStream.h @@ -43,12 +43,15 @@ public: bool shouldLoopbackForNode() const { return _shouldLoopbackForNode; } bool isStereo() const { return _isStereo; } + PositionalAudioStream::Type getType() const { return _type; } + const glm::vec3& getPosition() const { return _position; } const glm::quat& getOrientation() const { return _orientation; } const glm::vec3& getAvatarBoundingBoxCorner() const { return _avatarBoundingBoxCorner; } const glm::vec3& getAvatarBoundingBoxScale() const { return _avatarBoundingBoxScale; } + bool hasValidPosition() const { return _hasValidPosition; } protected: // disallow copying of PositionalAudioStream objects @@ -75,6 +78,8 @@ protected: float _quietestTrailingFrameLoudness; float _quietestFrameLoudness; int _frameCounter; + + bool _hasValidPosition { false }; }; #endif // hifi_PositionalAudioStream_h diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt index 53edc692f2..1f740700c5 100644 --- a/libraries/avatars-renderer/CMakeLists.txt +++ b/libraries/avatars-renderer/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME avatars-renderer) AUTOSCRIBE_SHADER_LIB(gpu graphics render render-utils) -setup_hifi_library(Widgets Network Script) +setup_hifi_library(Network Script) link_hifi_libraries(shared gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer) include_hifi_library_headers(avatars) include_hifi_library_headers(networking) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a363fb6d15..f24bd51bde 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -707,7 +707,11 @@ public slots: void setJointMappingsFromNetworkReply(); void setSessionUUID(const QUuid& sessionUUID) { if (sessionUUID != getID()) { - setID(sessionUUID); + if (sessionUUID == QUuid()) { + setID(AVATAR_SELF_ID); + } else { + setID(sessionUUID); + } emit sessionUUIDChanged(); } } diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index 1d9d42d579..7d34258c96 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME display-plugins) AUTOSCRIBE_SHADER_LIB(gpu display-plugins) -setup_hifi_library(OpenGL) +setup_hifi_library(Gui) link_hifi_libraries(shared plugins ui-plugins gl ui render-utils ${PLATFORM_GL_BACKEND}) include_hifi_library_headers(gpu) include_hifi_library_headers(model-networking) diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index d35d5c5317..8b255d9974 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -395,8 +395,8 @@ void HmdDisplayPlugin::HUDRenderer::build() { void HmdDisplayPlugin::HUDRenderer::updatePipeline() { if (!pipeline) { - auto vs = gpu::Shader::createVertex(std::string(hmd_ui_vert)); - auto ps = gpu::Shader::createPixel(std::string(hmd_ui_frag)); + auto vs = hmd_ui_vert::getShader(); + auto ps = hmd_ui_frag::getShader(); auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram(*program, gpu::Shader::BindingSet()); uniformsLocation = program->getUniformBuffers().findLocation("hudBuffer"); diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index ea75367e1e..4eaa16a107 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME entities-renderer) AUTOSCRIBE_SHADER_LIB(gpu graphics procedural render render-utils) -setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image ui pointers) +setup_hifi_library(Network Script) +link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image qml ui pointers) include_hifi_library_headers(networking) include_hifi_library_headers(gl) include_hifi_library_headers(ktx) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index af95878213..b3a4a1a1ab 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -36,8 +36,8 @@ static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, co gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); PrepareStencil::testMask(*state); - auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert)); - auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag)); + auto vertShader = textured_particle_vert::getShader(); + auto fragShader = textured_particle_frag::getShader(); auto program = gpu::Shader::createProgram(vertShader, fragShader); _texturedPipeline = texturedPipeline = gpu::Pipeline::create(program, state); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 42110170a0..664b379654 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -48,8 +48,8 @@ struct PolyLineUniforms { static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) { if (!polylinePipeline) { - auto VS = gpu::Shader::createVertex(std::string(paintStroke_vert)); - auto PS = gpu::Shader::createPixel(std::string(paintStroke_frag)); + auto VS = paintStroke_vert::getShader(); + auto PS = paintStroke_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(VS, PS); #ifdef POLYLINE_ENTITY_USE_FADE_EFFECT auto fadeVS = gpu::Shader::createVertex(std::string(paintStroke_fade_vert)); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index abd14d017e..78a363d63c 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -27,6 +27,7 @@ #include #include "EntityTreeRenderer.h" + #include "polyvox_vert.h" #include "polyvox_frag.h" #include "polyvox_fade_vert.h" @@ -70,6 +71,7 @@ #include "StencilMaskPass.h" #include "EntityTreeRenderer.h" + #include "polyvox_vert.h" #include "polyvox_frag.h" #include "polyvox_fade_vert.h" @@ -1459,8 +1461,8 @@ static gpu::Stream::FormatPointer _vertexFormat; ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch) { if (!_pipelines[0]) { - gpu::ShaderPointer vertexShaders[2] = { gpu::Shader::createVertex(std::string(polyvox_vert)), gpu::Shader::createVertex(std::string(polyvox_fade_vert)) }; - gpu::ShaderPointer pixelShaders[2] = { gpu::Shader::createPixel(std::string(polyvox_frag)), gpu::Shader::createPixel(std::string(polyvox_fade_frag)) }; + gpu::ShaderPointer vertexShaders[2] = { polyvox_vert::getShader(), polyvox_fade_vert::getShader() }; + gpu::ShaderPointer pixelShaders[2] = { polyvox_frag::getShader(), polyvox_fade_frag::getShader() }; gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), MATERIAL_GPU_SLOT)); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index db0f0b729a..1e97067795 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -165,7 +165,7 @@ public: PolyVoxEntityRenderer(const EntityItemPointer& entity); protected: - virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape(); } + virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); } virtual ShapeKey getShapeKey() override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index cdee2c5ec9..0c4f8cbd39 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,8 +16,8 @@ #include #include -#include -#include +#include "render-utils/simple_vert.h" +#include "render-utils/simple_frag.h" //#define SHAPE_ENTITY_USE_FADE_EFFECT #ifdef SHAPE_ENTITY_USE_FADE_EFFECT @@ -32,8 +32,8 @@ static const float SPHERE_ENTITY_SCALE = 0.5f; ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { - _procedural._vertexSource = simple_vert; - _procedural._fragmentSource = simple_frag; + _procedural._vertexSource = simple_vert::getSource(); + _procedural._fragmentSource = simple_frag::getSource(); _procedural._opaqueState->setCullMode(gpu::State::CULL_NONE); _procedural._opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL); PrepareStencil::testMaskDrawShape(*_procedural._opaqueState); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 3789cca69a..f7aaa43b7d 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -221,19 +221,17 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) { }); }; - { - // FIXME use the surface cache instead of explicit creation - _webSurface = QSharedPointer(new OffscreenQmlSurface(), deleter); - _webSurface->create(); - } - + // FIXME use the surface cache instead of explicit creation + _webSurface = QSharedPointer(new OffscreenQmlSurface(), deleter); // FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces // and the current rendering load) _webSurface->setMaxFps(DEFAULT_MAX_FPS); - // FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml. - _webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant()); - // Let us interact with the keyboard - _webSurface->getSurfaceContext()->setContextProperty("tabletInterface", DependencyManager::get().data()); + QObject::connect(_webSurface.data(), &OffscreenQmlSurface::rootContextCreated, [this](QQmlContext* surfaceContext) { + // FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml. + surfaceContext->setContextProperty("desktop", QVariant()); + // Let us interact with the keyboard + surfaceContext->setContextProperty("tabletInterface", DependencyManager::get().data()); + }); _fadeStartTime = usecTimestampNow(); loadSourceURL(); _webSurface->resume(); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index c46409c4ee..322c91e3d3 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -241,7 +241,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen } #endif - updateKeyZoneItemFromEntity(); + updateKeyZoneItemFromEntity(entity); if (keyLightChanged) { updateKeySunFromEntity(entity); @@ -329,7 +329,7 @@ void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity // Set the keylight sunLight->setColor(ColorUtils::toVec3(_keyLightProperties.getColor())); sunLight->setIntensity(_keyLightProperties.getIntensity()); - sunLight->setDirection(_keyLightProperties.getDirection()); + sunLight->setDirection(entity->getTransform().getRotation() * _keyLightProperties.getDirection()); sunLight->setCastShadows(_keyLightProperties.getCastShadows()); } @@ -350,6 +350,8 @@ void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer& } else { setAmbientURL(_ambientLightProperties.getAmbientURL()); } + + ambientLight->setTransform(entity->getTransform().getInverseMatrix()); } void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) { @@ -379,7 +381,7 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeKeyLightRange())); haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(_hazeProperties.getHazeKeyLightAltitude())); - haze->setZoneTransform(entity->getTransform().getMatrix()); + haze->setTransform(entity->getTransform().getMatrix()); } void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { @@ -391,7 +393,10 @@ void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& setSkyboxURL(_skyboxProperties.getURL()); } -void ZoneEntityRenderer::updateKeyZoneItemFromEntity() { +void ZoneEntityRenderer::updateKeyZoneItemFromEntity(const TypedEntityPointer& entity) { + // Update rotation values + editSkybox()->setOrientation(entity->getTransform().getRotation()); + /* TODO: Implement the sun model behavior / Keep this code here for reference, this is how we { // Set the stage diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index 69f0663c06..c5824abef0 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -45,7 +45,7 @@ protected: virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; private: - void updateKeyZoneItemFromEntity(); + void updateKeyZoneItemFromEntity(const TypedEntityPointer& entity); void updateKeySunFromEntity(const TypedEntityPointer& entity); void updateAmbientLightFromEntity(const TypedEntityPointer& entity); void updateHazeFromEntity(const TypedEntityPointer& entity); diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index e40b218344..224d19fe96 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -180,6 +180,8 @@ public: float emissiveIntensity{ 1.0f }; float ambientFactor{ 1.0f }; + float bumpMultiplier { 1.0f }; // TODO: to be implemented + QString materialID; QString name; QString shadingModel; diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index ba93a49cb9..63fb93ae46 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -15,6 +15,7 @@ #include "OBJReader.h" #include // .obj files are not locale-specific. The C/ASCII charset applies. +#include #include #include @@ -35,6 +36,11 @@ QHash COMMENT_SCALE_HINTS = {{"This file uses centimeters as uni const QString SMART_DEFAULT_MATERIAL_NAME = "High Fidelity smart default material name"; +const float ILLUMINATION_MODEL_MIN_OPACITY = 0.1f; +const float ILLUMINATION_MODEL_APPLY_SHININESS = 0.5f; +const float ILLUMINATION_MODEL_APPLY_ROUGHNESS = 1.0f; +const float ILLUMINATION_MODEL_APPLY_NON_METALLIC = 0.0f; + namespace { template T& checked_at(QVector& vector, int i) { @@ -70,6 +76,7 @@ int OBJTokenizer::nextToken(bool allowSpaceChar /*= false*/) { } switch (ch) { case '#': { + _datum = ""; _comment = _device->readLine(); // stash comment for a future call to getComment return COMMENT_TOKEN; } @@ -256,7 +263,14 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { default: materials[matName] = currentMaterial; #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Last material shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << currentMaterial.specularColor << " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << currentMaterial.specularTextureFilename; + qCDebug(modelformat) << "OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel << + " shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << + " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << + currentMaterial.specularColor << " emissive color:" << currentMaterial.emissiveColor << + " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << + currentMaterial.specularTextureFilename << " emissive texture:" << + currentMaterial.emissiveTextureFilename << " bump texture:" << + currentMaterial.bumpTextureFilename; #endif return; } @@ -272,20 +286,46 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { qCDebug(modelformat) << "OBJ Reader Starting new material definition " << matName; #endif currentMaterial.diffuseTextureFilename = ""; + currentMaterial.emissiveTextureFilename = ""; + currentMaterial.specularTextureFilename = ""; + currentMaterial.bumpTextureFilename = ""; } else if (token == "Ns") { currentMaterial.shininess = tokenizer.getFloat(); - } else if ((token == "d") || (token == "Tr")) { + } else if (token == "Ni") { + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader Ignoring material Ni " << tokenizer.getFloat(); + #else + tokenizer.getFloat(); + #endif + } else if (token == "d") { currentMaterial.opacity = tokenizer.getFloat(); + } else if (token == "Tr") { + currentMaterial.opacity = 1.0f - tokenizer.getFloat(); + } else if (token == "illum") { + currentMaterial.illuminationModel = tokenizer.getFloat(); + } else if (token == "Tf") { + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader Ignoring material Tf " << tokenizer.getVec3(); + #else + tokenizer.getVec3(); + #endif } else if (token == "Ka") { #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3(); + qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3();; + #else + tokenizer.getVec3(); #endif } else if (token == "Kd") { currentMaterial.diffuseColor = tokenizer.getVec3(); + } else if (token == "Ke") { + currentMaterial.emissiveColor = tokenizer.getVec3(); } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); - } else if ((token == "map_Kd") || (token == "map_Ks")) { - QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8(); + } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump")) { + const QByteArray textureLine = tokenizer.getLineAsDatum(); + QByteArray filename; + OBJMaterialTextureOptions textureOptions; + parseTextureLine(textureLine, filename, textureOptions); if (filename.endsWith(".tga")) { #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << _url; @@ -294,11 +334,104 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } if (token == "map_Kd") { currentMaterial.diffuseTextureFilename = filename; - } else if( token == "map_Ks" ) { + } else if (token == "map_Ke") { + currentMaterial.emissiveTextureFilename = filename; + } else if (token == "map_Ks" ) { currentMaterial.specularTextureFilename = filename; + } else if ((token == "map_bump") || (token == "bump")) { + currentMaterial.bumpTextureFilename = filename; + currentMaterial.bumpTextureOptions = textureOptions; } } } +} + +void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions) { + // Texture options reference http://paulbourke.net/dataformats/mtl/ + // and https://wikivisually.com/wiki/Material_Template_Library + + std::istringstream iss(textureLine.toStdString()); + const std::vector parser(std::istream_iterator{iss}, std::istream_iterator()); + + uint i = 0; + while (i < parser.size()) { + if (i + 1 < parser.size() && parser[i][0] == '-') { + const std::string& option = parser[i++]; + if (option == "-blendu" || option == "-blendv") { + #ifdef WANT_DEBUG + const std::string& onoff = parser[i++]; + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << onoff.c_str(); + #endif + } else if (option == "-bm") { + const std::string& bm = parser[i++]; + textureOptions.bumpMultiplier = std::stof(bm); + } else if (option == "-boost") { + #ifdef WANT_DEBUG + const std::string& boost = parser[i++]; + float boostFloat = std::stof(boost); + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << boost.c_str(); + #endif + } else if (option == "-cc") { + #ifdef WANT_DEBUG + const std::string& onoff = parser[i++]; + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << onoff.c_str(); + #endif + } else if (option == "-clamp") { + #ifdef WANT_DEBUG + const std::string& onoff = parser[i++]; + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << onoff.c_str(); + #endif + } else if (option == "-imfchan") { + #ifdef WANT_DEBUG + const std::string& imfchan = parser[i++]; + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << imfchan.c_str(); + #endif + } else if (option == "-mm") { + if (i + 1 < parser.size()) { + #ifdef WANT_DEBUG + const std::string& mmBase = parser[i++]; + const std::string& mmGain = parser[i++]; + float mmBaseFloat = std::stof(mmBase); + float mmGainFloat = std::stof(mmGain); + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << mmBase.c_str() << mmGain.c_str(); + #endif + } + } else if (option == "-o" || option == "-s" || option == "-t") { + if (i + 2 < parser.size()) { + #ifdef WANT_DEBUG + const std::string& u = parser[i++]; + const std::string& v = parser[i++]; + const std::string& w = parser[i++]; + float uFloat = std::stof(u); + float vFloat = std::stof(v); + float wFloat = std::stof(w); + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << u.c_str() << v.c_str() << w.c_str(); + #endif + } + } else if (option == "-texres") { + #ifdef WANT_DEBUG + const std::string& texres = parser[i++]; + float texresFloat = std::stof(texres); + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << texres.c_str(); + #endif + } else if (option == "-type") { + #ifdef WANT_DEBUG + const std::string& type = parser[i++]; + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << type.c_str(); + #endif + } else if (option[0] == '-') { + #ifdef WANT_DEBUG + qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring unsupported texture option" << option.c_str(); + #endif + } + } else { // assume filename at end when no more options + std::string filenameString = parser[i++]; + while (i < parser.size()) { // filename has space in it + filenameString += " " + parser[i++]; + } + filename = filenameString.c_str(); + } + } } std::tuple requestData(QUrl& url) { @@ -745,7 +878,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, } geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor, objMaterial.specularColor, - glm::vec3(0.0f), + objMaterial.emissiveColor, objMaterial.shininess, objMaterial.opacity); FBXMaterial& fbxMaterial = geometry.materials[materialID]; @@ -759,17 +892,88 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, if (!objMaterial.specularTextureFilename.isEmpty()) { fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; } + if (!objMaterial.emissiveTextureFilename.isEmpty()) { + fbxMaterial.emissiveTexture.filename = objMaterial.emissiveTextureFilename; + } + if (!objMaterial.bumpTextureFilename.isEmpty()) { + fbxMaterial.normalTexture.filename = objMaterial.bumpTextureFilename; + fbxMaterial.normalTexture.isBumpmap = true; + fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier; + } modelMaterial->setEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor)); modelMaterial->setRoughness(graphics::Material::shininessToRoughness(fbxMaterial.shininess)); - if (fbxMaterial.opacity <= 0.0f) { - modelMaterial->setOpacity(1.0f); - } else { - modelMaterial->setOpacity(fbxMaterial.opacity); + bool applyTransparency = false; + bool applyShininess = false; + bool applyRoughness = false; + bool applyNonMetallic = false; + bool fresnelOn = false; + + // Illumination model reference http://paulbourke.net/dataformats/mtl/ + switch (objMaterial.illuminationModel) { + case 0: // Color on and Ambient off + // We don't support ambient = do nothing? + break; + case 1: // Color on and Ambient on + // We don't support ambient = do nothing? + break; + case 2: // Highlight on + // Change specular intensity = do nothing for now? + break; + case 3: // Reflection on and Ray trace on + applyShininess = true; + break; + case 4: // Transparency: Glass on and Reflection: Ray trace on + applyTransparency = true; + applyShininess = true; + break; + case 5: // Reflection: Fresnel on and Ray trace on + applyShininess = true; + fresnelOn = true; + break; + case 6: // Transparency: Refraction on and Reflection: Fresnel off and Ray trace on + applyTransparency = true; + applyNonMetallic = true; + applyShininess = true; + break; + case 7: // Transparency: Refraction on and Reflection: Fresnel on and Ray trace on + applyTransparency = true; + applyNonMetallic = true; + applyShininess = true; + fresnelOn = true; + break; + case 8: // Reflection on and Ray trace off + applyShininess = true; + break; + case 9: // Transparency: Glass on and Reflection: Ray trace off + applyTransparency = true; + applyNonMetallic = true; + applyRoughness = true; + break; + case 10: // Casts shadows onto invisible surfaces + // Do nothing? + break; + } + + if (applyTransparency) { + fbxMaterial.opacity = std::max(fbxMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY); } + if (applyShininess) { + modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_SHININESS); + } else if (applyRoughness) { + modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_ROUGHNESS); + } + if (applyNonMetallic) { + modelMaterial->setMetallic(ILLUMINATION_MODEL_APPLY_NON_METALLIC); + } + if (fresnelOn) { + modelMaterial->setFresnel(glm::vec3(1.0f)); + } + + modelMaterial->setOpacity(fbxMaterial.opacity); } return geometryPtr; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 45e3f79480..df356fada8 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -48,6 +48,11 @@ private: void addFrom(const OBJFace* face, int index); }; +class OBJMaterialTextureOptions { +public: + float bumpMultiplier { 1.0f }; +} +; // Materials and references to material names can come in any order, and different mesh parts can refer to the same material. // Therefore it would get pretty hacky to try to use FBXMeshPart to store these as we traverse the files. class OBJMaterial { @@ -56,11 +61,16 @@ public: float opacity; glm::vec3 diffuseColor; glm::vec3 specularColor; + glm::vec3 emissiveColor; QByteArray diffuseTextureFilename; QByteArray specularTextureFilename; + QByteArray emissiveTextureFilename; + QByteArray bumpTextureFilename; + OBJMaterialTextureOptions bumpTextureOptions; + int illuminationModel; bool used { false }; bool userSpecifiesUV { false }; - OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f) {} + OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f), illuminationModel(-1) {} }; class OBJReader: public QObject { // QObject so we can make network requests. @@ -84,6 +94,7 @@ private: bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); + void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions); bool isValidTexture(const QByteArray &filename); // true if the file exists. TODO?: check content-type header and that it is a supported format. int _partCounter { 0 }; diff --git a/libraries/gl/CMakeLists.txt b/libraries/gl/CMakeLists.txt index 9fc7a0c10f..925cf9b288 100644 --- a/libraries/gl/CMakeLists.txt +++ b/libraries/gl/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME gl) -setup_hifi_library(OpenGL Qml Quick) +setup_hifi_library(Gui Widgets Qml Quick) link_hifi_libraries(shared) target_opengl() diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index d5e3bb5a67..e3f85afa78 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -81,6 +81,10 @@ bool isRenderThread() { return QThread::currentThread() == RENDER_THREAD; } +#if defined(Q_OS_ANDROID) +#define USE_GLES 1 +#endif + namespace gl { void withSavedContext(const std::function& f) { // Save the original GL context, because creating a QML surface will create a new context @@ -91,4 +95,47 @@ namespace gl { savedContext->makeCurrent(savedSurface); } } + + bool checkGLError(const char* name) { + GLenum error = glGetError(); + if (!error) { + return false; + } + switch (error) { + case GL_INVALID_ENUM: + qCWarning(glLogging) << "GLBackend" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; + break; + case GL_INVALID_VALUE: + qCWarning(glLogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; + break; + case GL_INVALID_OPERATION: + qCWarning(glLogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + qCWarning(glLogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; + break; + case GL_OUT_OF_MEMORY: + qCWarning(glLogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; + break; +#if !defined(USE_GLES) + case GL_STACK_UNDERFLOW: + qCWarning(glLogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow."; + break; + case GL_STACK_OVERFLOW: + qCWarning(glLogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow."; + break; +#endif + } + return true; + } + + + bool checkGLErrorDebug(const char* name) { +#ifdef DEBUG + return checkGLError(name); +#else + Q_UNUSED(name); + return false; +#endif + } } diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index 581bb09360..dcbf5a9e77 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -13,6 +13,8 @@ #include #include +#include "GLLogging.h" + // 16 bits of depth precision #define DEFAULT_GL_DEPTH_BUFFER_BITS 16 // 8 bits of stencil buffer (typically you really only need 1 bit for functionality @@ -43,6 +45,13 @@ bool isRenderThread(); namespace gl { void withSavedContext(const std::function& f); -} + + bool checkGLError(const char* name); + + bool checkGLErrorDebug(const char* name); + +} // namespace gl + +#define CHECK_GL_ERROR() ::gl::checkGLErrorDebug(__FUNCTION__) #endif diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp index 5a015cd550..dc1c8036b0 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp @@ -8,12 +8,13 @@ #include "GLShared.h" #include +#include #include +#include #include #include -#include Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl") Q_LOGGING_CATEGORY(trace_render_gpu_gl, "trace.render.gpu.gl") @@ -21,47 +22,6 @@ Q_LOGGING_CATEGORY(trace_render_gpu_gl_detail, "trace.render.gpu.gl.detail") namespace gpu { namespace gl { -bool checkGLError(const char* name) { - GLenum error = glGetError(); - if (!error) { - return false; - } else { - switch (error) { - case GL_INVALID_ENUM: - qCWarning(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_INVALID_VALUE: - qCWarning(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; - break; - case GL_INVALID_OPERATION: - qCWarning(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - qCWarning(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_OUT_OF_MEMORY: - qCWarning(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; - break; - case GL_STACK_UNDERFLOW: - qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow."; - break; - case GL_STACK_OVERFLOW: - qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow."; - break; - } - return true; - } -} - -bool checkGLErrorDebug(const char* name) { -#ifdef DEBUG - return checkGLError(name); -#else - Q_UNUSED(name); - return false; -#endif -} - gpu::Size getFreeDedicatedMemory() { Size result { 0 }; static bool nvidiaMemorySupported { true }; diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl/src/gpu/gl/GLShared.h index 6c2948a736..ccdf0a5c41 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.h @@ -9,6 +9,7 @@ #define hifi_gpu_GLShared_h #include +#include #include #include #include @@ -114,9 +115,6 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = { GL_INT_2_10_10_10_REV, }; -bool checkGLError(const char* name = nullptr); -bool checkGLErrorDebug(const char* name = nullptr); - class GLBackend; template @@ -141,11 +139,8 @@ class GLShader; class GLTexture; struct ShaderObject; - } } // namespace gpu::gl -#define CHECK_GL_ERROR() gpu::gl::checkGLErrorDebug(__FUNCTION__) - #endif diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 925e83bb77..2834a8463c 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -413,7 +413,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; sourceMip._offset = bufferOffset; bufferOffset += sourceMip._size; - gpu::gl::checkGLError(); + ::gl::checkGLError(__FUNCTION__); } (void)CHECK_GL_ERROR(); @@ -458,7 +458,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui #endif glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat, sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); - gpu::gl::checkGLError(); + ::gl::checkGLError(__FUNCTION__); } } diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt index 16575a7018..ea69919f6d 100644 --- a/libraries/gpu-gles/CMakeLists.txt +++ b/libraries/gpu-gles/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME gpu-gles) -setup_hifi_library(Concurrent) +setup_hifi_library(Gui Concurrent) link_hifi_libraries(shared gl gpu) GroupSources("src") target_opengl() diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.h b/libraries/gpu-gles/src/gpu/gl/GLShared.h index dcce896a37..1341dd16fa 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gles/src/gpu/gl/GLShared.h @@ -9,11 +9,14 @@ #define hifi_gpu_GLShared_h #include +#include + #include #include #include #include + Q_DECLARE_LOGGING_CATEGORY(gpugllogging) Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl) Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail) @@ -143,8 +146,6 @@ struct ShaderObject; } } // namespace gpu::gl -#define CHECK_GL_ERROR() gpu::gl::checkGLErrorDebug(__FUNCTION__) - #endif diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 4504337789..d898411006 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -16,7 +16,7 @@ #include #include #include - +#include #include namespace gpu { diff --git a/libraries/gpu/src/gpu/StandardShaderLib.cpp b/libraries/gpu/src/gpu/StandardShaderLib.cpp index 0d8d131e0b..0d829fb21f 100755 --- a/libraries/gpu/src/gpu/StandardShaderLib.cpp +++ b/libraries/gpu/src/gpu/StandardShaderLib.cpp @@ -73,42 +73,42 @@ ShaderPointer StandardShaderLib::getProgram(GetShader getVS, GetShader getPS) { ShaderPointer StandardShaderLib::getDrawUnitQuadTexcoordVS() { if (!_drawUnitQuadTexcoordVS) { - _drawUnitQuadTexcoordVS = gpu::Shader::createVertex(std::string(DrawUnitQuadTexcoord_vert)); + _drawUnitQuadTexcoordVS = DrawUnitQuadTexcoord_vert::getShader(); } return _drawUnitQuadTexcoordVS; } ShaderPointer StandardShaderLib::getDrawTransformUnitQuadVS() { if (!_drawTransformUnitQuadVS) { - _drawTransformUnitQuadVS = gpu::Shader::createVertex(std::string(DrawTransformUnitQuad_vert)); + _drawTransformUnitQuadVS = DrawTransformUnitQuad_vert::getShader(); } return _drawTransformUnitQuadVS; } ShaderPointer StandardShaderLib::getDrawTexcoordRectTransformUnitQuadVS() { if (!_drawTexcoordRectTransformUnitQuadVS) { - _drawTexcoordRectTransformUnitQuadVS = gpu::Shader::createVertex(std::string(DrawTexcoordRectTransformUnitQuad_vert)); + _drawTexcoordRectTransformUnitQuadVS = DrawTexcoordRectTransformUnitQuad_vert::getShader(); } return _drawTexcoordRectTransformUnitQuadVS; } ShaderPointer StandardShaderLib::getDrawViewportQuadTransformTexcoordVS() { if (!_drawViewportQuadTransformTexcoordVS) { - _drawViewportQuadTransformTexcoordVS = gpu::Shader::createVertex(std::string(DrawViewportQuadTransformTexcoord_vert)); + _drawViewportQuadTransformTexcoordVS = DrawViewportQuadTransformTexcoord_vert::getShader(); } return _drawViewportQuadTransformTexcoordVS; } ShaderPointer StandardShaderLib::getDrawVertexPositionVS() { if (!_drawVertexPositionVS) { - _drawVertexPositionVS = gpu::Shader::createVertex(std::string(DrawVertexPosition_vert)); + _drawVertexPositionVS = DrawVertexPosition_vert::getShader(); } return _drawVertexPositionVS; } ShaderPointer StandardShaderLib::getDrawTransformVertexPositionVS() { if (!_drawTransformVertexPositionVS) { - _drawTransformVertexPositionVS = gpu::Shader::createVertex(std::string(DrawTransformVertexPosition_vert)); + _drawTransformVertexPositionVS = DrawTransformVertexPosition_vert::getShader(); } return _drawTransformVertexPositionVS; } @@ -122,42 +122,42 @@ ShaderPointer StandardShaderLib::getDrawNadaPS() { ShaderPointer StandardShaderLib::getDrawWhitePS() { if (!_drawWhitePS) { - _drawWhitePS = gpu::Shader::createPixel(std::string(DrawWhite_frag)); + _drawWhitePS = DrawWhite_frag::getShader(); } return _drawWhitePS; } ShaderPointer StandardShaderLib::getDrawColorPS() { if (!_drawColorPS) { - _drawColorPS = gpu::Shader::createPixel(std::string(DrawColor_frag)); + _drawColorPS = DrawColor_frag::getShader(); } return _drawColorPS; } ShaderPointer StandardShaderLib::getDrawTexturePS() { if (!_drawTexturePS) { - _drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag)); + _drawTexturePS = DrawTexture_frag::getShader(); } return _drawTexturePS; } ShaderPointer StandardShaderLib::getDrawTextureMirroredXPS() { if (!_drawTextureMirroredXPS) { - _drawTextureMirroredXPS = gpu::Shader::createPixel(std::string(DrawTextureMirroredX_frag)); + _drawTextureMirroredXPS = DrawTextureMirroredX_frag::getShader(); } return _drawTextureMirroredXPS; } ShaderPointer StandardShaderLib::getDrawTextureOpaquePS() { if (!_drawTextureOpaquePS) { - _drawTextureOpaquePS = gpu::Shader::createPixel(std::string(DrawTextureOpaque_frag)); + _drawTextureOpaquePS = DrawTextureOpaque_frag::getShader(); } return _drawTextureOpaquePS; } ShaderPointer StandardShaderLib::getDrawColoredTexturePS() { if (!_drawColoredTexturePS) { - _drawColoredTexturePS = gpu::Shader::createPixel(std::string(DrawColoredTexture_frag)); + _drawColoredTexturePS = DrawColoredTexture_frag::getShader(); } return _drawColoredTexturePS; } diff --git a/libraries/graphics/src/graphics/Haze.cpp b/libraries/graphics/src/graphics/Haze.cpp index c788bc57db..dfe70175f4 100644 --- a/libraries/graphics/src/graphics/Haze.cpp +++ b/libraries/graphics/src/graphics/Haze.cpp @@ -182,11 +182,11 @@ void Haze::setHazeBackgroundBlend(const float hazeBackgroundBlend) { } } -void Haze::setZoneTransform(const glm::mat4& zoneTransform) { +void Haze::setTransform(const glm::mat4& transform) { auto& params = _hazeParametersBuffer.get(); - if (params.zoneTransform == zoneTransform) { - _hazeParametersBuffer.edit().zoneTransform = zoneTransform; + if (params.transform != transform) { + _hazeParametersBuffer.edit().transform = transform; } } diff --git a/libraries/graphics/src/graphics/Haze.h b/libraries/graphics/src/graphics/Haze.h index eda9f1e407..c38fadef94 100644 --- a/libraries/graphics/src/graphics/Haze.h +++ b/libraries/graphics/src/graphics/Haze.h @@ -92,7 +92,7 @@ namespace graphics { void setHazeBackgroundBlend(const float hazeBackgroundBlend); - void setZoneTransform(const glm::mat4& zoneTransform); + void setTransform(const glm::mat4& transform); using UniformBufferView = gpu::BufferView; UniformBufferView getHazeParametersBuffer() const { return _hazeParametersBuffer; } @@ -113,7 +113,7 @@ namespace graphics { // bit 2 - set to activate directional light attenuation mode // bit 3 - set to blend between blend-in and blend-out colours - glm::mat4 zoneTransform; + glm::mat4 transform; // Amount of background (skybox) to display, overriding the haze effect for the background float hazeBackgroundBlend{ INITIAL_HAZE_BACKGROUND_BLEND }; diff --git a/libraries/graphics/src/graphics/Light.cpp b/libraries/graphics/src/graphics/Light.cpp index 94ec3a376a..50601299dd 100755 --- a/libraries/graphics/src/graphics/Light.cpp +++ b/libraries/graphics/src/graphics/Light.cpp @@ -165,3 +165,9 @@ void Light::setAmbientMapNumMips(uint16_t numMips) { _ambientSchemaBuffer.edit().mapNumMips = (float)numMips; } +void Light::setTransform(const glm::mat4& transform) { + if (_ambientSchemaBuffer.edit().transform != transform) { + _ambientSchemaBuffer.edit().transform = transform; + } +} + diff --git a/libraries/graphics/src/graphics/Light.h b/libraries/graphics/src/graphics/Light.h index 7497691185..ebe22d5593 100755 --- a/libraries/graphics/src/graphics/Light.h +++ b/libraries/graphics/src/graphics/Light.h @@ -152,6 +152,8 @@ public: void setAmbientMapNumMips(uint16_t numMips); uint16_t getAmbientMapNumMips() const { return (uint16_t) _ambientSchemaBuffer->mapNumMips; } + void setTransform(const glm::mat4& transform); + // Light Schema class LightSchema { public: @@ -165,7 +167,9 @@ public: float mapNumMips { 0.0f }; float spare1; float spare2; + gpu::SphericalHarmonics ambientSphere; + glm::mat4 transform; }; using LightSchemaBuffer = gpu::StructBuffer; diff --git a/libraries/graphics/src/graphics/Light.slh b/libraries/graphics/src/graphics/Light.slh index e1202ed6a0..12ac1f3c4b 100644 --- a/libraries/graphics/src/graphics/Light.slh +++ b/libraries/graphics/src/graphics/Light.slh @@ -34,7 +34,9 @@ vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradi // Light Ambient struct LightAmbient { vec4 _ambient; + SphericalHarmonics _ambientSphere; + mat4 transform; }; SphericalHarmonics getLightAmbientSphere(LightAmbient l) { return l._ambientSphere; } diff --git a/libraries/graphics/src/graphics/Skybox.cpp b/libraries/graphics/src/graphics/Skybox.cpp index e05a66104a..6ad0045aa9 100755 --- a/libraries/graphics/src/graphics/Skybox.cpp +++ b/libraries/graphics/src/graphics/Skybox.cpp @@ -37,6 +37,12 @@ void Skybox::setCubemap(const gpu::TexturePointer& cubemap) { } } +void Skybox::setOrientation(const glm::quat& orientation) { + // The zone rotations need to be negated + _orientation = orientation; + _orientation.w = -_orientation.w; +} + void Skybox::updateSchemaBuffer() const { auto blend = 0.0f; if (getCubemap() && getCubemap()->isDefined()) { @@ -85,8 +91,8 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky static std::once_flag once; std::call_once(once, [&] { { - auto skyVS = gpu::Shader::createVertex(std::string(skybox_vert)); - auto skyFS = gpu::Shader::createPixel(std::string(skybox_frag)); + auto skyVS = skybox_vert::getShader(); + auto skyFS = skybox_frag::getShader(); auto skyShader = gpu::Shader::createProgram(skyVS, skyFS); batch.runLambda([skyShader] { @@ -115,6 +121,10 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky Transform viewTransform; viewFrustum.evalViewTransform(viewTransform); + + // Orientate view transform to be relative to zone + viewTransform.setRotation(skybox.getOrientation() * viewTransform.getRotation()); + batch.setProjectionTransform(projMat); batch.setViewTransform(viewTransform); batch.setModelTransform(Transform()); // only for Mac diff --git a/libraries/graphics/src/graphics/Skybox.h b/libraries/graphics/src/graphics/Skybox.h index d06989a457..a739b9a745 100755 --- a/libraries/graphics/src/graphics/Skybox.h +++ b/libraries/graphics/src/graphics/Skybox.h @@ -37,6 +37,9 @@ public: void setCubemap(const gpu::TexturePointer& cubemap); const gpu::TexturePointer& getCubemap() const { return _cubemap; } + void setOrientation(const glm::quat& orientation); + const glm::quat getOrientation() const { return _orientation; } + virtual bool empty() { return _empty; } virtual void clear(); @@ -61,6 +64,8 @@ protected: mutable gpu::BufferView _schemaBuffer; gpu::TexturePointer _cubemap; + glm::quat _orientation; + bool _empty{ true }; }; typedef std::shared_ptr SkyboxPointer; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 4e1354d3aa..9296f61ad3 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include #include #include @@ -666,7 +666,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should } emit locationChangeRequired(newPosition, orientationChanged, - LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation, + trigger == LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation, shouldFace ); @@ -764,7 +764,7 @@ void AddressManager::copyAddress() { } // assume that the address is being copied because the user wants a shareable address - QApplication::clipboard()->setText(currentShareableAddress().toString()); + QGuiApplication::clipboard()->setText(currentShareableAddress().toString()); } void AddressManager::copyPath() { @@ -773,7 +773,7 @@ void AddressManager::copyPath() { return; } - QApplication::clipboard()->setText(currentPath()); + QGuiApplication::clipboard()->setText(currentPath()); } QString AddressManager::getDomainID() const { diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 3c20c3798f..411f8f5be2 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -98,6 +98,7 @@ void DomainHandler::softReset() { clearSettings(); _connectionDenialsSinceKeypairRegen = 0; + _checkInPacketsSinceLastReply = 0; // cancel the failure timeout for any pending requests for settings QMetaObject::invokeMethod(&_settingsTimer, "stop"); @@ -382,6 +383,9 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS + // so emit our signal that says that + qCDebug(networking) << "Limit of silent domain checkins reached"; + emit limitOfSilentDomainCheckInsReached(); + } +} diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 42ef2f627d..b72c172c3e 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -31,6 +31,8 @@ const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103; const quint16 DOMAIN_SERVER_HTTP_PORT = 40100; const quint16 DOMAIN_SERVER_HTTPS_PORT = 40101; +const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5; + class DomainHandler : public QObject { Q_OBJECT public: @@ -84,6 +86,10 @@ public: void softReset(); + int getCheckInPacketsSinceLastReply() const { return _checkInPacketsSinceLastReply; } + void sentCheckInPacket(); + void domainListReceived() { _checkInPacketsSinceLastReply = 0; } + /**jsdoc *

The reasons that you may be refused connection to a domain are defined by numeric values:

* @@ -165,6 +171,8 @@ signals: void domainConnectionRefused(QString reasonMessage, int reason, const QString& extraInfo); + void limitOfSilentDomainCheckInsReached(); + private: bool reasonSuggestsLogin(ConnectionRefusedReason reasonCode); void sendDisconnectPacket(); @@ -187,6 +195,7 @@ private: QSet _domainConnectionRefusals; bool _hasCheckedForAccessToken { false }; int _connectionDenialsSinceKeypairRegen { 0 }; + int _checkInPacketsSinceLastReply { 0 }; QTimer _apiRefreshTimer; }; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 1c18125433..9ad0cb5e79 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -44,7 +44,6 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) _ownerType(newOwnerType), _nodeTypesOfInterest(), _domainHandler(this), - _numNoReplyDomainCheckIns(0), _assignmentServerSocket(), _keepAlivePingTimer(this) { @@ -75,7 +74,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) connect(this, &LimitedNodeList::publicSockAddrChanged, this, &NodeList::sendDomainServerCheckIn); // clear our NodeList when the domain changes - connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset); + connect(&_domainHandler, SIGNAL(disconnectedFromDomain()), this, SLOT(resetFromDomainHandler())); // send an ICE heartbeat as soon as we get ice server information connect(&_domainHandler, &DomainHandler::iceSocketAndIDReceived, this, &NodeList::handleICEConnectionToDomainServer); @@ -92,10 +91,10 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) connect(accountManager.data(), &AccountManager::newKeypair, this, &NodeList::sendDomainServerCheckIn); // clear out NodeList when login is finished - connect(accountManager.data(), &AccountManager::loginComplete , this, &NodeList::reset); + connect(accountManager.data(), SIGNAL(loginComplete()) , this, SLOT(reset())); // clear our NodeList when logout is requested - connect(accountManager.data(), &AccountManager::logoutComplete , this, &NodeList::reset); + connect(accountManager.data(), SIGNAL(logoutComplete()) , this, SLOT(reset())); // anytime we get a new node we will want to attempt to punch to it connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch); @@ -231,16 +230,14 @@ void NodeList::processICEPingPacket(QSharedPointer message) { sendPacket(std::move(replyPacket), message->getSenderSockAddr()); } -void NodeList::reset() { +void NodeList::reset(bool skipDomainHandlerReset) { if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "reset"); + QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, skipDomainHandlerReset)); return; } LimitedNodeList::reset(); - _numNoReplyDomainCheckIns = 0; - // lock and clear our set of ignored IDs _ignoredSetLock.lockForWrite(); _ignoredNodeIDs.clear(); @@ -255,7 +252,7 @@ void NodeList::reset() { _avatarGainMap.clear(); _avatarGainMapLock.unlock(); - if (sender() != &_domainHandler) { + if (!skipDomainHandlerReset) { // clear the domain connection information, unless they're the ones that asked us to reset _domainHandler.softReset(); } @@ -410,15 +407,8 @@ void NodeList::sendDomainServerCheckIn() { sendPacket(std::move(domainPacket), _domainHandler.getSockAddr()); - if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { - // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS - // so emit our signal that says that - qCDebug(networking) << "Limit of silent domain checkins reached"; - emit limitOfSilentDomainCheckInsReached(); - } - - // increment the count of un-replied check-ins - _numNoReplyDomainCheckIns++; + // let the domain handler know we sent another check in or connect packet + _domainHandler.sentCheckInPacket(); } } @@ -585,7 +575,7 @@ void NodeList::processDomainServerList(QSharedPointer message) } // this is a packet from the domain server, reset the count of un-replied check-ins - _numNoReplyDomainCheckIns = 0; + _domainHandler.domainListReceived(); // emit our signal so listeners know we just heard from the DS emit receivedDomainServerList(); @@ -999,7 +989,7 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) { } sendPacket(std::move(setAvatarGainPacket), *audioMixer); - QWriteLocker{ &_avatarGainMapLock }; + QWriteLocker lock{ &_avatarGainMapLock }; _avatarGainMap[nodeID] = gain; } else { @@ -1011,7 +1001,7 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) { } float NodeList::getAvatarGain(const QUuid& nodeID) { - QReadLocker{ &_avatarGainMapLock }; + QReadLocker lock{ &_avatarGainMapLock }; auto it = _avatarGainMap.find(nodeID); if (it != _avatarGainMap.cend()) { return it->second; diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 0ebc1f0b22..2dd9d3c869 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -38,8 +38,6 @@ const quint64 DOMAIN_SERVER_CHECK_IN_MSECS = 1 * 1000; -const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5; - using PacketOrPacketList = std::pair, std::unique_ptr>; using NodePacketOrPacketListPair = std::pair; @@ -62,7 +60,6 @@ public: Q_INVOKABLE qint64 sendStats(QJsonObject statsObject, HifiSockAddr destination); Q_INVOKABLE qint64 sendStatsToDomainServer(QJsonObject statsObject); - int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; } DomainHandler& getDomainHandler() { return _domainHandler; } const NodeSet& getNodeInterestSet() const { return _nodeTypesOfInterest; } @@ -96,7 +93,9 @@ public: void removeFromIgnoreMuteSets(const QUuid& nodeID); public slots: - void reset(); + void reset(bool skipDomainHandlerReset = false); + void resetFromDomainHandler() { reset(true); } + void sendDomainServerCheckIn(); void handleDSPathQuery(const QString& newPath); @@ -119,7 +118,6 @@ public slots: #endif signals: - void limitOfSilentDomainCheckInsReached(); void receivedDomainServerList(); void ignoredNode(const QUuid& nodeID, bool enabled); void ignoreRadiusEnabledChanged(bool isIgnored); @@ -161,7 +159,6 @@ private: std::atomic _ownerType; NodeSet _nodeTypesOfInterest; DomainHandler _domainHandler; - int _numNoReplyDomainCheckIns; HifiSockAddr _assignmentServerSocket; bool _isShuttingDown { false }; QTimer _keepAlivePingTimer; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 5cafb4caa2..9ad7c36179 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::AvatarJointDefaultPoseFlags); + return static_cast(AvatarMixerPacketVersion::FBXReaderNodeReparenting); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); case PacketType::ICEServerHeartbeat: diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp index 629d8baefe..03e6533c87 100644 --- a/libraries/physics/src/ObjectActionTractor.cpp +++ b/libraries/physics/src/ObjectActionTractor.cpp @@ -77,13 +77,18 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { return false; } + bool doLinearTraction = _positionalTargetSet && (_linearTimeScale < MAX_TRACTOR_TIMESCALE); + bool doAngularTraction = _rotationalTargetSet && (_angularTimeScale < MAX_TRACTOR_TIMESCALE); + if (!doLinearTraction && !doAngularTraction) { + // nothing to do + return false; + } + glm::quat rotation; glm::vec3 position; glm::vec3 angularVelocity; - bool linearValid = false; int linearTractorCount = 0; - bool angularValid = false; int angularTractorCount = 0; QList tractorDerivedActions; @@ -105,7 +110,6 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { linearTimeScale, angularTimeScale); if (success) { if (angularTimeScale < MAX_TRACTOR_TIMESCALE) { - angularValid = true; angularTractorCount++; angularVelocity += angularVelocityForAction; if (tractorAction.get() == this) { @@ -115,43 +119,40 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { } if (linearTimeScale < MAX_TRACTOR_TIMESCALE) { - linearValid = true; linearTractorCount++; position += positionForAction; } } } - if ((angularValid && angularTractorCount > 0) || (linearValid && linearTractorCount > 0)) { + if (angularTractorCount > 0 || linearTractorCount > 0) { withWriteLock([&]{ - if (linearValid && linearTractorCount > 0) { + if (doLinearTraction && linearTractorCount > 0) { position /= linearTractorCount; - if (_positionalTargetSet) { - _lastPositionTarget = _positionalTarget; - } else { - _lastPositionTarget = position; - } + _lastPositionTarget = _positionalTarget; _positionalTarget = position; if (deltaTimeStep > EPSILON) { - // blend the new velocity with the old (low-pass filter) - glm::vec3 newVelocity = (1.0f / deltaTimeStep) * (position - _lastPositionTarget); - const float blend = 0.25f; - _linearVelocityTarget = (1.0f - blend) * _linearVelocityTarget + blend * newVelocity; + if (_havePositionTargetHistory) { + // blend the new velocity with the old (low-pass filter) + glm::vec3 newVelocity = (1.0f / deltaTimeStep) * (_positionalTarget - _lastPositionTarget); + const float blend = 0.25f; + _linearVelocityTarget = (1.0f - blend) * _linearVelocityTarget + blend * newVelocity; + } else { + _havePositionTargetHistory = true; + } } - _positionalTargetSet = true; _active = true; } - if (angularValid && angularTractorCount > 0) { + if (doAngularTraction && angularTractorCount > 0) { angularVelocity /= angularTractorCount; _rotationalTarget = rotation; _angularVelocityTarget = angularVelocity; - _rotationalTargetSet = true; _active = true; } }); } - return linearValid || angularValid; + return true; } @@ -241,7 +242,9 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) { // targets are required, tractor-constants are optional bool ok = true; positionalTarget = EntityDynamicInterface::extractVec3Argument("tractor action", arguments, "targetPosition", ok, false); - if (!ok) { + if (ok) { + _positionalTargetSet = true; + } else { positionalTarget = _desiredPositionalTarget; } ok = true; @@ -252,7 +255,9 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) { ok = true; rotationalTarget = EntityDynamicInterface::extractQuatArgument("tractor action", arguments, "targetRotation", ok, false); - if (!ok) { + if (ok) { + _rotationalTargetSet = true; + } else { rotationalTarget = _desiredRotationalTarget; } diff --git a/libraries/physics/src/ObjectActionTractor.h b/libraries/physics/src/ObjectActionTractor.h index a17526f5f9..2273a1e720 100644 --- a/libraries/physics/src/ObjectActionTractor.h +++ b/libraries/physics/src/ObjectActionTractor.h @@ -39,6 +39,7 @@ protected: glm::vec3 _lastPositionTarget; float _linearTimeScale; bool _positionalTargetSet; + bool _havePositionTargetHistory { false }; glm::quat _rotationalTarget; glm::quat _desiredRotationalTarget; diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index 6067c0fbed..1c0d74c45a 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME plugins) -setup_hifi_library(OpenGL) +setup_hifi_library(Gui) link_hifi_libraries(shared networking) include_hifi_library_headers(gpu) diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index 630520957a..dbd6845676 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -214,9 +214,10 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() { } void PluginManager::disableDisplayPlugin(const QString& name) { - std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){ + auto it = std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){ return plugin->getName() == name; }); + _displayPlugins.erase(it, _displayPlugins.end()); } diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 7b718515a8..52c56d1e7d 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -241,7 +241,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm std::string fragmentShaderSource = _fragmentSource; size_t replaceIndex = fragmentShaderSource.find(PROCEDURAL_COMMON_BLOCK); if (replaceIndex != std::string::npos) { - fragmentShaderSource.replace(replaceIndex, PROCEDURAL_COMMON_BLOCK.size(), ProceduralCommon_frag); + fragmentShaderSource.replace(replaceIndex, PROCEDURAL_COMMON_BLOCK.size(), ProceduralCommon_frag::getSource()); } replaceIndex = fragmentShaderSource.find(PROCEDURAL_VERSION); diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index f0c60e2101..43633e47ad 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -19,8 +19,8 @@ #include ProceduralSkybox::ProceduralSkybox() : graphics::Skybox() { - _procedural._vertexSource = skybox_vert; - _procedural._fragmentSource = skybox_frag; + _procedural._vertexSource = skybox_vert::getSource(); + _procedural._fragmentSource = skybox_frag::getSource(); // Adjust the pipeline state for background using the stencil test _procedural.setDoesFade(false); // Must match PrepareStencil::STENCIL_BACKGROUND diff --git a/libraries/qml/CMakeLists.txt b/libraries/qml/CMakeLists.txt new file mode 100644 index 0000000000..f8ed7acdb1 --- /dev/null +++ b/libraries/qml/CMakeLists.txt @@ -0,0 +1,6 @@ +set(TARGET_NAME qml) +setup_hifi_library(Multimedia Network Qml Quick WebChannel WebSockets ${PLATFORM_QT_COMPONENTS}) +link_hifi_libraries(shared networking gl) + +# Required for some low level GL interaction in the OffscreenQMLSurface +target_opengl() diff --git a/libraries/qml/src/qml/Logging.cpp b/libraries/qml/src/qml/Logging.cpp new file mode 100644 index 0000000000..c809cab944 --- /dev/null +++ b/libraries/qml/src/qml/Logging.cpp @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis 2016/03/01 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Logging.h" + +Q_LOGGING_CATEGORY(qmlLogging, "hifi.qml") diff --git a/libraries/qml/src/qml/Logging.h b/libraries/qml/src/qml/Logging.h new file mode 100644 index 0000000000..487566701d --- /dev/null +++ b/libraries/qml/src/qml/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis 2018/01/04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Controllers_Logging_h +#define hifi_Controllers_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(qmlLogging) + +#endif diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp new file mode 100644 index 0000000000..87fc8a3025 --- /dev/null +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -0,0 +1,355 @@ +// +// Created by Bradley Austin Davis on 2015-05-13 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "OffscreenSurface.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "Logging.h" +#include "impl/SharedObject.h" +#include "impl/TextureCache.h" + +using namespace hifi::qml; +using namespace hifi::qml::impl; + +static uvec2 clampSize(const uvec2& size, uint32_t maxDimension) { + return glm::clamp(size, glm::uvec2(1), glm::uvec2(maxDimension)); +} + +static QSize clampSize(const QSize& qsize, uint32_t maxDimension) { + return fromGlm(clampSize(toGlm(qsize), maxDimension)); +} + +const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QQuickItem*) {}; + +void OffscreenSurface::initializeEngine(QQmlEngine* engine) { +} + +using namespace hifi::qml::impl; + +size_t OffscreenSurface::getUsedTextureMemory() { + return SharedObject::getTextureCache().getUsedTextureMemory(); +} + +void OffscreenSurface::setSharedContext(QOpenGLContext* sharedContext) { + SharedObject::setSharedContext(sharedContext); +} + +std::function OffscreenSurface::getDiscardLambda() { + return [](uint32_t texture, void* fence) { + SharedObject::getTextureCache().releaseTexture({ texture, static_cast(fence) }); + }; +} + +OffscreenSurface::OffscreenSurface() + : _sharedObject(new impl::SharedObject()) { +} + +OffscreenSurface::~OffscreenSurface() { + disconnect(qApp); + _sharedObject->destroy(); +} + +bool OffscreenSurface::fetchTexture(TextureAndFence& textureAndFence) { + if (!_sharedObject) { + return false; + } + hifi::qml::impl::TextureAndFence typedTextureAndFence; + bool result = _sharedObject->fetchTexture(typedTextureAndFence); + textureAndFence = typedTextureAndFence; + return result; +} + +void OffscreenSurface::resize(const QSize& newSize_) { + const uint32_t MAX_OFFSCREEN_DIMENSION = 4096; + _sharedObject->setSize(clampSize(newSize_, MAX_OFFSCREEN_DIMENSION)); +} + +QQuickItem* OffscreenSurface::getRootItem() { + return _sharedObject->getRootItem(); +} + +void OffscreenSurface::clearCache() { + _sharedObject->getContext()->engine()->clearComponentCache(); +} + +QPointF OffscreenSurface::mapToVirtualScreen(const QPointF& originalPoint) { + return _mouseTranslator(originalPoint); +} + +/////////////////////////////////////////////////////// +// +// Event handling customization +// + +bool OffscreenSurface::filterEnabled(QObject* originalDestination, QEvent* event) const { + if (!_sharedObject || _sharedObject->getWindow() == originalDestination) { + return false; + } + // Only intercept events while we're in an active state + if (_sharedObject->isPaused()) { + return false; + } + return true; +} + +bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event) { + if (!filterEnabled(originalDestination, event)) { + return false; + } +#ifdef DEBUG + // Don't intercept our own events, or we enter an infinite recursion + { + auto rootItem = _sharedObject->getRootItem(); + auto quickWindow = _sharedObject->getWindow(); + QObject* recurseTest = originalDestination; + while (recurseTest) { + Q_ASSERT(recurseTest != rootItem && recurseTest != quickWindow); + recurseTest = recurseTest->parent(); + } + } +#endif + + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: { + event->ignore(); + if (QCoreApplication::sendEvent(_sharedObject->getWindow(), event)) { + return event->isAccepted(); + } + break; + } + + case QEvent::Wheel: { + QWheelEvent* wheelEvent = static_cast(event); + QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos()); + QWheelEvent mappedEvent(transformedPos, wheelEvent->delta(), wheelEvent->buttons(), wheelEvent->modifiers(), + wheelEvent->orientation()); + mappedEvent.ignore(); + if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &mappedEvent)) { + return mappedEvent.isAccepted(); + } + break; + } + case QEvent::MouseMove: { + QMouseEvent* mouseEvent = static_cast(event); + QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos()); + QMouseEvent mappedEvent(mouseEvent->type(), transformedPos, mouseEvent->screenPos(), mouseEvent->button(), + mouseEvent->buttons(), mouseEvent->modifiers()); + if (event->type() == QEvent::MouseMove) { + // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install + // need to investigate into why this crash is happening. + //_qmlContext->setContextProperty("lastMousePosition", transformedPos); + } + mappedEvent.ignore(); + if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &mappedEvent)) { + return mappedEvent.isAccepted(); + } + break; + } + default: + break; + } + + return false; +} + +void OffscreenSurface::pause() { + _sharedObject->pause(); +} + +void OffscreenSurface::resume() { + _sharedObject->resume(); +} + +bool OffscreenSurface::isPaused() const { + return _sharedObject->isPaused(); +} + +void OffscreenSurface::setProxyWindow(QWindow* window) { + _sharedObject->setProxyWindow(window); +} + +QObject* OffscreenSurface::getEventHandler() { + return getWindow(); +} + +QQuickWindow* OffscreenSurface::getWindow() { + return _sharedObject->getWindow(); +} + +QSize OffscreenSurface::size() const { + return _sharedObject->getSize(); +} + +QQmlContext* OffscreenSurface::getSurfaceContext() { + return _sharedObject->getContext(); +} + +void OffscreenSurface::setMaxFps(uint8_t maxFps) { + _sharedObject->setMaxFps(maxFps); +} + +void OffscreenSurface::load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback) { + loadInternal(qmlSource, false, parent, [callback](QQmlContext* context, QQuickItem* newItem) { + QJSValue(callback).call(QJSValueList() << context->engine()->newQObject(newItem)); + }); +} + +void OffscreenSurface::load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback) { + loadInternal(qmlSource, createNewContext, nullptr, callback); +} + +void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback) { + load(qmlSource, true, callback); +} + +void OffscreenSurface::load(const QUrl& qmlSource, const QmlContextObjectCallback& callback) { + load(qmlSource, false, callback); +} + +void OffscreenSurface::load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback) { + return load(QUrl(qmlSourceFile), callback); +} + +void OffscreenSurface::loadInternal(const QUrl& qmlSource, + bool createNewContext, + QQuickItem* parent, + const QmlContextObjectCallback& callback) { + if (QThread::currentThread() != thread()) { + qFatal("Called load on a non-surface thread"); + } + // Synchronous loading may take a while; restart the deadlock timer + QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection); + + if (!getRootItem()) { + _sharedObject->create(this); + } + + QUrl finalQmlSource = qmlSource; + if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) { + finalQmlSource = getSurfaceContext()->resolvedUrl(qmlSource); + } + + if (!getRootItem()) { + _sharedObject->setObjectName(finalQmlSource.toString()); + } + + auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext); + auto qmlComponent = new QQmlComponent(getSurfaceContext()->engine(), finalQmlSource, QQmlComponent::PreferSynchronous); + if (qmlComponent->isLoading()) { + connect(qmlComponent, &QQmlComponent::statusChanged, this, + [=](QQmlComponent::Status) { finishQmlLoad(qmlComponent, targetContext, parent, callback); }); + return; + } + + finishQmlLoad(qmlComponent, targetContext, parent, callback); +} + +void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent, + QQmlContext* qmlContext, + QQuickItem* parent, + const QmlContextObjectCallback& callback) { + disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0); + if (qmlComponent->isError()) { + for (const auto& error : qmlComponent->errors()) { + qCWarning(qmlLogging) << error.url() << error.line() << error; + } + qmlComponent->deleteLater(); + return; + } + + QObject* newObject = qmlComponent->beginCreate(qmlContext); + if (qmlComponent->isError()) { + for (const auto& error : qmlComponent->errors()) { + qCWarning(qmlLogging) << error.url() << error.line() << error; + } + if (!getRootItem()) { + qFatal("Unable to finish loading QML root"); + } + qmlComponent->deleteLater(); + return; + } + + if (!newObject) { + if (!getRootItem()) { + qFatal("Could not load object as root item"); + return; + } + qCWarning(qmlLogging) << "Unable to load QML item"; + return; + } + + qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); + + // All quick items should be focusable + QQuickItem* newItem = qobject_cast(newObject); + if (newItem) { + // Make sure we make items focusable (critical for + // supporting keyboard shortcuts) + newItem->setFlag(QQuickItem::ItemIsFocusScope, true); + } + + bool rootCreated = getRootItem() != nullptr; + + // Make sure we will call callback for this codepath + // Call this before qmlComponent->completeCreate() otherwise ghost window appears + // If we already have a root, just set a couple of flags and the ancestry + if (rootCreated) { + callback(qmlContext, newItem); + if (!parent) { + parent = getRootItem(); + } + // Allow child windows to be destroyed from JS + QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership); + newObject->setParent(parent); + newItem->setParentItem(parent); + } else { + // The root item is ready. Associate it with the window. + _sharedObject->setRootItem(newItem); + } + + qmlComponent->completeCreate(); + qmlComponent->deleteLater(); + + onItemCreated(qmlContext, newItem); + connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant))); + + if (!rootCreated) { + onRootCreated(); + emit rootItemCreated(newItem); + // Call this callback after rootitem is set, otherwise VrMenu wont work + callback(qmlContext, newItem); + } +} + +QQmlContext* OffscreenSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) { + QQmlContext* targetContext = parent ? QQmlEngine::contextForObject(parent) : getSurfaceContext(); + if (!targetContext) { + targetContext = getSurfaceContext(); + } + + if (getRootItem() && forceNewContext) { + targetContext = new QQmlContext(targetContext, targetContext->engine()); + } + + return targetContext; +} diff --git a/libraries/qml/src/qml/OffscreenSurface.h b/libraries/qml/src/qml/OffscreenSurface.h new file mode 100644 index 0000000000..555f2ee6a4 --- /dev/null +++ b/libraries/qml/src/qml/OffscreenSurface.h @@ -0,0 +1,127 @@ +// +// Created by Bradley Austin Davis on 2015-04-04 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once +#ifndef hifi_qml_OffscreenSurface_h +#define hifi_qml_OffscreenSurface_h + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +class QWindow; +class QOpenGLContext; +class QQmlContext; +class QQmlEngine; +class QQmlComponent; +class QQuickWindow; +class QQuickItem; +class OffscreenQmlSharedObject; + +namespace hifi { namespace qml { + +namespace impl { +class SharedObject; +} + +using QmlContextObjectCallback = ::std::function; + +class OffscreenSurface : public QObject { + Q_OBJECT + +public: + static const QmlContextObjectCallback DEFAULT_CONTEXT_CALLBACK; + + using TextureAndFence = std::pair; + using MouseTranslator = std::function; + + static void setSharedContext(QOpenGLContext* context); + + OffscreenSurface(); + virtual ~OffscreenSurface(); + + QSize size() const; + virtual void resize(const QSize& size); + void clearCache(); + + void setMaxFps(uint8_t maxFps); + // Optional values for event handling + void setProxyWindow(QWindow* window); + void setMouseTranslator(const MouseTranslator& mouseTranslator) { _mouseTranslator = mouseTranslator; } + + void pause(); + void resume(); + bool isPaused() const; + + QQuickItem* getRootItem(); + QQuickWindow* getWindow(); + QObject* getEventHandler(); + QQmlContext* getSurfaceContext(); + + // Checks to see if a new texture is available. If one is, the function returns true and + // textureAndFence will be populated with the texture ID and a fence which will be signalled + // when the texture is safe to read. + // Returns false if no new texture is available + bool fetchTexture(TextureAndFence& textureAndFence); + + static std::function getDiscardLambda(); + static size_t getUsedTextureMemory(); + QPointF mapToVirtualScreen(const QPointF& originalPoint); + + // For use from QML/JS + Q_INVOKABLE void load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback); + + // For use from C++ + Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK); + Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK); + Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK); + Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK); + +public slots: + virtual void onFocusObjectChanged(QObject* newFocus) {} + +signals: + void rootContextCreated(QQmlContext* rootContext); + void rootItemCreated(QQuickItem* rootContext); + +protected: + bool eventFilter(QObject* originalDestination, QEvent* event) override; + bool filterEnabled(QObject* originalDestination, QEvent* event) const; + + virtual void initializeEngine(QQmlEngine* engine); + virtual void loadInternal(const QUrl& qmlSource, + bool createNewContext, + QQuickItem* parent, + const QmlContextObjectCallback& callback) final; + virtual void finishQmlLoad(QQmlComponent* qmlComponent, + QQmlContext* qmlContext, + QQuickItem* parent, + const QmlContextObjectCallback& onQmlLoadedCallback) final; + + virtual void onRootCreated() {} + virtual void onItemCreated(QQmlContext* context, QQuickItem* newItem) {} + virtual void onRootContextCreated(QQmlContext* qmlContext) {} + + virtual QQmlContext* contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext); +private: + MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } }; + friend class hifi::qml::impl::SharedObject; + impl::SharedObject* const _sharedObject; +}; + +}} // namespace hifi::qml + +#endif diff --git a/libraries/qml/src/qml/impl/Profiling.cpp b/libraries/qml/src/qml/impl/Profiling.cpp new file mode 100644 index 0000000000..488b45b082 --- /dev/null +++ b/libraries/qml/src/qml/impl/Profiling.cpp @@ -0,0 +1,12 @@ +// +// Created by Bradley Austin Davis on 2018-01-04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Profiling.h" + +Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml") +Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl") diff --git a/libraries/qml/src/qml/impl/Profiling.h b/libraries/qml/src/qml/impl/Profiling.h new file mode 100644 index 0000000000..c1f3decedc --- /dev/null +++ b/libraries/qml/src/qml/impl/Profiling.h @@ -0,0 +1,13 @@ +// +// Created by Bradley Austin Davis on 2018-01-04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once + +#include + +Q_DECLARE_LOGGING_CATEGORY(trace_render_qml) +Q_DECLARE_LOGGING_CATEGORY(trace_render_qml_gl) diff --git a/libraries/qml/src/qml/impl/RenderControl.cpp b/libraries/qml/src/qml/impl/RenderControl.cpp new file mode 100644 index 0000000000..885d719df5 --- /dev/null +++ b/libraries/qml/src/qml/impl/RenderControl.cpp @@ -0,0 +1,29 @@ +// +// Created by Bradley Austin Davis on 2018-01-04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "RenderControl.h" + +using namespace hifi::qml::impl; + +RenderControl::RenderControl(QObject* parent) : QQuickRenderControl(parent) { + +} + +void RenderControl::setRenderWindow(QWindow* renderWindow) { + _renderWindow = renderWindow; +} + +QWindow* RenderControl::renderWindow(QPoint* offset) { + if (nullptr == _renderWindow) { + return QQuickRenderControl::renderWindow(offset); + } + if (nullptr != offset) { + offset->rx() = offset->ry() = 0; + } + return _renderWindow; +} + diff --git a/libraries/qml/src/qml/impl/RenderControl.h b/libraries/qml/src/qml/impl/RenderControl.h new file mode 100644 index 0000000000..e63196b6ee --- /dev/null +++ b/libraries/qml/src/qml/impl/RenderControl.h @@ -0,0 +1,26 @@ +// +// Created by Bradley Austin Davis on 2018-01-04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once + +#include + +namespace hifi { namespace qml { namespace impl { + +class RenderControl : public QQuickRenderControl { +public: + RenderControl(QObject* parent = Q_NULLPTR); + void setRenderWindow(QWindow* renderWindow); + +protected: + QWindow* renderWindow(QPoint* offset) override; + +private: + QWindow* _renderWindow{ nullptr }; +}; + +}}} // namespace hifi::qml::impl diff --git a/libraries/qml/src/qml/impl/RenderEventHandler.cpp b/libraries/qml/src/qml/impl/RenderEventHandler.cpp new file mode 100644 index 0000000000..cce1b68da9 --- /dev/null +++ b/libraries/qml/src/qml/impl/RenderEventHandler.cpp @@ -0,0 +1,169 @@ +// +// Created by Bradley Austin Davis on 2018-01-04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "RenderEventHandler.h" + +#include +#include + +#include + +#include +#include "Profiling.h" +#include "SharedObject.h" +#include "TextureCache.h" +#include "RenderControl.h" +#include "../Logging.h" + +using namespace hifi::qml::impl; + +bool RenderEventHandler::event(QEvent* e) { + switch (static_cast(e->type())) { + case OffscreenEvent::Render: + onRender(); + return true; + + case OffscreenEvent::Initialize: + onInitalize(); + return true; + + case OffscreenEvent::Quit: + onQuit(); + return true; + + default: + break; + } + return QObject::event(e); +} + +RenderEventHandler::RenderEventHandler(SharedObject* shared, QThread* targetThread) + : _shared(shared) { + // Create the GL canvas in the same thread as the share canvas + if (!_canvas.create(SharedObject::getSharedContext())) { + qFatal("Unable to create new offscreen GL context"); + } + + moveToThread(targetThread); + _canvas.moveToThreadWithContext(targetThread); +} + +void RenderEventHandler::onInitalize() { + if (_shared->isQuit()) { + return; + } + + if (!_canvas.makeCurrent()) { + qFatal("Unable to make QML rendering context current on render thread"); + } + _shared->initializeRenderControl(_canvas.getContext()); +} + +void RenderEventHandler::resize() { + PROFILE_RANGE(render_qml_gl, __FUNCTION__); + auto targetSize = _shared->getSize(); + if (_currentSize != targetSize) { + auto& offscreenTextures = SharedObject::getTextureCache(); + // Release hold on the textures of the old size + if (_currentSize != QSize()) { + _shared->releaseTextureAndFence(); + offscreenTextures.releaseSize(_currentSize); + } + + _currentSize = targetSize; + + // Acquire the new texture size + if (_currentSize != QSize()) { + qCDebug(qmlLogging) << "Upating offscreen textures to " << _currentSize.width() << " x " << _currentSize.height(); + offscreenTextures.acquireSize(_currentSize); + if (_depthStencil) { + glDeleteRenderbuffers(1, &_depthStencil); + _depthStencil = 0; + } + glGenRenderbuffers(1, &_depthStencil); + glBindRenderbuffer(GL_RENDERBUFFER, _depthStencil); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _currentSize.width(), _currentSize.height()); + if (!_fbo) { + glGenFramebuffers(1, &_fbo); + } + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil); + glViewport(0, 0, _currentSize.width(), _currentSize.height()); + glScissor(0, 0, _currentSize.width(), _currentSize.height()); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + } + } +} + +void RenderEventHandler::onRender() { + if (_shared->isQuit()) { + return; + } + + if (_canvas.getContext() != QOpenGLContextWrapper::currentContext()) { + qFatal("QML rendering context not current on render thread"); + } + + PROFILE_RANGE(render_qml_gl, __FUNCTION__); + + if (!_shared->preRender()) { + return; + } + + resize(); + + { + PROFILE_RANGE(render_qml_gl, "render"); + GLuint texture = SharedObject::getTextureCache().acquireTexture(_currentSize); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0); + if (nsightActive()) { + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + } else { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + _shared->_quickWindow->setRenderTarget(_fbo, _currentSize); + _shared->_renderControl->render(); + } + _shared->_lastRenderTime = usecTimestampNow(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, texture); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + _shared->updateTextureAndFence({ texture, fence }); + // Fence will be used in another thread / context, so a flush is required + _shared->_quickWindow->resetOpenGLState(); + } +} + +void RenderEventHandler::onQuit() { + if (_canvas.getContext() != QOpenGLContextWrapper::currentContext()) { + qFatal("QML rendering context not current on render thread"); + } + + if (_depthStencil) { + glDeleteRenderbuffers(1, &_depthStencil); + _depthStencil = 0; + } + + if (_fbo) { + glDeleteFramebuffers(1, &_fbo); + _fbo = 0; + } + + _shared->shutdownRendering(_canvas, _currentSize); + // Release the reference to the shared object. This will allow it to + // be destroyed (should happen on it's own thread). + _shared->deleteLater(); + + deleteLater(); + + QThread::currentThread()->quit(); +} diff --git a/libraries/qml/src/qml/impl/RenderEventHandler.h b/libraries/qml/src/qml/impl/RenderEventHandler.h new file mode 100644 index 0000000000..d1e079cc85 --- /dev/null +++ b/libraries/qml/src/qml/impl/RenderEventHandler.h @@ -0,0 +1,56 @@ +// +// Created by Bradley Austin Davis on 2018-01-04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once + +#include +#include +#include + +#include +#include + +namespace hifi { namespace qml { namespace impl { + +class SharedObject; + +class OffscreenEvent : public QEvent { +public: + enum Type { + Initialize = QEvent::User + 1, + Render, + Quit + }; + + OffscreenEvent(Type type) : QEvent(static_cast(type)) {} +}; + +/* The render event handler lives on the QML rendering thread for a given surface + * (each surface has a dedicated rendering thread) and handles events of type + * OffscreenEvent to do one time initialization or destruction, and to actually + * perform the render. + */ +class RenderEventHandler : public QObject { +public: + RenderEventHandler(SharedObject* shared, QThread* targetThread); + +private: + bool event(QEvent* e) override; + void onInitalize(); + void resize(); + void onRender(); + void onQuit(); + + SharedObject* const _shared; + OffscreenGLCanvas _canvas; + QSize _currentSize; + + uint32_t _fbo{ 0 }; + uint32_t _depthStencil{ 0 }; +}; + +}}} // namespace hifi::qml::impl diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp new file mode 100644 index 0000000000..f2af5bd036 --- /dev/null +++ b/libraries/qml/src/qml/impl/SharedObject.cpp @@ -0,0 +1,448 @@ +// +// Created by Bradley Austin Davis on 2018-01-04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "SharedObject.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "../OffscreenSurface.h" +#include "../Logging.h" + +#include "Profiling.h" +#include "RenderControl.h" +#include "RenderEventHandler.h" +#include "TextureCache.h" + +// Time between receiving a request to render the offscreen UI actually triggering +// the render. Could possibly be increased depending on the framerate we expect to +// achieve. +// This has the effect of capping the framerate at 200 +static const int MIN_TIMER_MS = 5; + +using namespace hifi::qml; +using namespace hifi::qml::impl; + +TextureCache offscreenTextures; + +TextureCache& SharedObject::getTextureCache() { + return offscreenTextures; +} + +#define OFFSCREEN_QML_SHARED_CONTEXT_PROPERTY "com.highfidelity.qml.gl.sharedContext" +void SharedObject::setSharedContext(QOpenGLContext* sharedContext) { + qApp->setProperty(OFFSCREEN_QML_SHARED_CONTEXT_PROPERTY, QVariant::fromValue(sharedContext)); + if (QOpenGLContextWrapper::currentContext() != sharedContext) { + qFatal("The shared context must be the current context when setting"); + } +} + +QOpenGLContext* SharedObject::getSharedContext() { + return static_cast(qApp->property(OFFSCREEN_QML_SHARED_CONTEXT_PROPERTY).value()); +} + +SharedObject::SharedObject() { + // Create render control + _renderControl = new RenderControl(); + + // Create a QQuickWindow that is associated with our render control. + // This window never gets created or shown, meaning that it will never get an underlying native (platform) window. + // NOTE: Must be created on the main thread so that OffscreenQmlSurface can send it events + // NOTE: Must be created on the rendering thread or it will refuse to render, + // so we wait until after its ctor to move object/context to this thread. + QQuickWindow::setDefaultAlphaBuffer(true); + _quickWindow = new QQuickWindow(_renderControl); + _quickWindow->setColor(QColor(255, 255, 255, 0)); + _quickWindow->setClearBeforeRendering(true); + + QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &SharedObject::onAboutToQuit); +} + +SharedObject::~SharedObject() { + if (_quickWindow) { + _quickWindow->destroy(); + _quickWindow = nullptr; + } + + if (_renderControl) { + _renderControl->deleteLater(); + _renderControl = nullptr; + } + + if (_renderThread) { + _renderThread->quit(); + _renderThread->deleteLater(); + } + + if (_rootItem) { + _rootItem->deleteLater(); + _rootItem = nullptr; + } + + releaseEngine(_qmlContext->engine()); +} + +void SharedObject::create(OffscreenSurface* surface) { + if (_rootItem) { + qFatal("QML surface root item already set"); + } + + QObject::connect(_quickWindow, &QQuickWindow::focusObjectChanged, surface, &OffscreenSurface::onFocusObjectChanged); + + // Create a QML engine. + auto qmlEngine = acquireEngine(surface); + _qmlContext = new QQmlContext(qmlEngine->rootContext(), qmlEngine); + surface->onRootContextCreated(_qmlContext); + emit surface->rootContextCreated(_qmlContext); + + if (!qmlEngine->incubationController()) { + qmlEngine->setIncubationController(_quickWindow->incubationController()); + } + _qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(_quickWindow)); +} + +void SharedObject::setRootItem(QQuickItem* rootItem) { + _rootItem = rootItem; + _rootItem->setSize(_quickWindow->size()); + + // Create the render thread + _renderThread = new QThread(); + _renderThread->setObjectName(objectName()); + _renderThread->start(); + + // Create event handler for the render thread + _renderObject = new RenderEventHandler(this, _renderThread); + QCoreApplication::postEvent(this, new OffscreenEvent(OffscreenEvent::Initialize)); + + QObject::connect(_renderControl, &QQuickRenderControl::renderRequested, this, &SharedObject::requestRender); + QObject::connect(_renderControl, &QQuickRenderControl::sceneChanged, this, &SharedObject::requestRenderSync); +} + +void SharedObject::destroy() { + if (_quit) { + return; + } + + if (!_rootItem) { + deleteLater(); + return; + } + + + _paused = true; + if (_renderTimer) { + QObject::disconnect(_renderTimer); + _renderTimer->deleteLater(); + } + + QObject::disconnect(_renderControl); + QObject::disconnect(qApp); + + QMutexLocker lock(&_mutex); + _quit = true; + QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Quit)); +} + + +#define SINGLE_QML_ENGINE 0 + + +#if SINGLE_QML_ENGINE +static QQmlEngine* globalEngine{ nullptr }; +static size_t globalEngineRefCount{ 0 }; +#endif + +QQmlEngine* SharedObject::acquireEngine(OffscreenSurface* surface) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); + + QQmlEngine* result = nullptr; + if (QThread::currentThread() != qApp->thread()) { + qCWarning(qmlLogging) << "Cannot acquire QML engine on any thread but the main thread"; + } + +#if SINGLE_QML_ENGINE + if (!globalEngine) { + Q_ASSERT(0 == globalEngineRefCount); + globalEngine = new QQmlEngine(); + surface->initializeQmlEngine(result); + ++globalEngineRefCount; + } + result = globalEngine; +#else + result = new QQmlEngine(); + surface->initializeEngine(result); +#endif + + return result; +} + +void SharedObject::releaseEngine(QQmlEngine* engine) { + Q_ASSERT(QThread::currentThread() == qApp->thread()); +#if SINGLE_QML_ENGINE + Q_ASSERT(0 != globalEngineRefCount); + if (0 == --globalEngineRefCount) { + globalEngine->deleteLater(); + globalEngine = nullptr; + } +#else + engine->deleteLater(); +#endif +} + +bool SharedObject::event(QEvent* e) { + switch (static_cast(e->type())) { + case OffscreenEvent::Initialize: + onInitialize(); + return true; + + case OffscreenEvent::Render: + onRender(); + return true; + + default: + break; + } + return QObject::event(e); +} + +// Called by the render event handler, from the render thread +void SharedObject::initializeRenderControl(QOpenGLContext* context) { + if (context->shareContext() != getSharedContext()) { + qFatal("QML rendering context has no share context"); + } + + if (!nsightActive()) { + _renderControl->initialize(context); + } +} + +void SharedObject::releaseTextureAndFence() { + QMutexLocker lock(&_mutex); + // If the most recent texture was unused, we can directly recycle it + if (_latestTextureAndFence.first) { + offscreenTextures.releaseTexture(_latestTextureAndFence); + _latestTextureAndFence = TextureAndFence{ 0, 0 }; + } +} + +void SharedObject::setRenderTarget(uint32_t fbo, const QSize& size) { + _quickWindow->setRenderTarget(fbo, size); +} + +QSize SharedObject::getSize() const { + QMutexLocker locker(&_mutex); + return _size; +} + +void SharedObject::setSize(const QSize& size) { + if (getSize() == size) { + return; + } + + { + QMutexLocker locker(&_mutex); + _size = size; + } + + qCDebug(qmlLogging) << "Offscreen UI resizing to " << size.width() << "x" << size.height(); + _quickWindow->setGeometry(QRect(QPoint(), size)); + _quickWindow->contentItem()->setSize(size); + + if (_rootItem) { + _qmlContext->setContextProperty("surfaceSize", size); + _rootItem->setSize(size); + } + + requestRenderSync(); +} + +bool SharedObject::preRender() { + QMutexLocker lock(&_mutex); + if (_paused) { + if (_syncRequested) { + wake(); + } + return false; + } + + if (_syncRequested) { + bool syncResult = true; + if (!nsightActive()) { + PROFILE_RANGE(render_qml_gl, "sync") + syncResult = _renderControl->sync(); + } + wake(); + if (!syncResult) { + return false; + } + _syncRequested = false; + } + + return true; +} + +void SharedObject::shutdownRendering(OffscreenGLCanvas& canvas, const QSize& size) { + QMutexLocker locker(&_mutex); + if (size != QSize(0, 0)) { + offscreenTextures.releaseSize(size); + } + _renderControl->invalidate(); + canvas.doneCurrent(); + wake(); +} + +bool SharedObject::isQuit() { + QMutexLocker locker(&_mutex); + return _quit; +} + +void SharedObject::requestRender() { + // Don't queue multiple renders + if (_renderRequested) { + return; + } + _renderRequested = true; +} + +void SharedObject::requestRenderSync() { + if (_quit) { + return; + } + + { + QMutexLocker lock(&_mutex); + _syncRequested = true; + } + + requestRender(); +} + +bool SharedObject::fetchTexture(TextureAndFence& textureAndFence) { + QMutexLocker locker(&_mutex); + if (0 == _latestTextureAndFence.first) { + return false; + } + textureAndFence = { 0, 0 }; + std::swap(textureAndFence, _latestTextureAndFence); + return true; +} + +void SharedObject::setProxyWindow(QWindow* window) { + _proxyWindow = window; + _renderControl->setRenderWindow(window); +} + +void SharedObject::wait() { + _cond.wait(&_mutex); +} + +void SharedObject::wake() { + _cond.wakeOne(); +} + +void SharedObject::onInitialize() { + // Associate root item with the window. + _rootItem->setParentItem(_quickWindow->contentItem()); + _renderControl->prepareThread(_renderThread); + + // Set up the render thread + QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Initialize)); + + requestRender(); + + // Set up timer to trigger renders + _renderTimer = new QTimer(this); + QObject::connect(_renderTimer, &QTimer::timeout, this, &SharedObject::onTimer); + + _renderTimer->setTimerType(Qt::PreciseTimer); + _renderTimer->setInterval(MIN_TIMER_MS); // 5ms, Qt::PreciseTimer required + _renderTimer->start(); +} + +void SharedObject::onRender() { + PROFILE_RANGE(render_qml, __FUNCTION__); + if (_quit) { + return; + } + + QMutexLocker lock(&_mutex); + if (_syncRequested) { + lock.unlock(); + _renderControl->polishItems(); + lock.relock(); + QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Render)); + // sync and render request, main and render threads must be synchronized + wait(); + } else { + QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Render)); + } + _renderRequested = false; +} + +void SharedObject::onTimer() { + offscreenTextures.report(); + if (!_renderRequested) { + return; + } + + { + QMutexLocker locker(&_mutex); + // Don't queue more than one frame at a time + if (0 != _latestTextureAndFence.first) { + return; + } + } + + { + auto minRenderInterval = USECS_PER_SECOND / _maxFps; + auto lastInterval = usecTimestampNow() - _lastRenderTime; + // Don't exceed the framerate limit + if (lastInterval < minRenderInterval) { + return; + } + } + + QCoreApplication::postEvent(this, new OffscreenEvent(OffscreenEvent::Render)); +} + +void SharedObject::onAboutToQuit() { + destroy(); +} + +void SharedObject::updateTextureAndFence(const TextureAndFence& newTextureAndFence) { + QMutexLocker locker(&_mutex); + // If the most recent texture was unused, we can directly recycle it + if (_latestTextureAndFence.first) { + offscreenTextures.releaseTexture(_latestTextureAndFence); + _latestTextureAndFence = { 0, 0 }; + } + + _latestTextureAndFence = newTextureAndFence; +} + +void SharedObject::pause() { + _paused = true; +} + +void SharedObject::resume() { + _paused = false; + requestRender(); +} + +bool SharedObject::isPaused() const { + return _paused; +} diff --git a/libraries/qml/src/qml/impl/SharedObject.h b/libraries/qml/src/qml/impl/SharedObject.h new file mode 100644 index 0000000000..76dde611fc --- /dev/null +++ b/libraries/qml/src/qml/impl/SharedObject.h @@ -0,0 +1,119 @@ +// +// Created by Bradley Austin Davis on 2018-01-04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "TextureCache.h" + + +class QWindow; +class QTimer; +class QQuickWindow; +class QQuickItem; +class QOpenGLContext; +class QQmlEngine; +class QQmlContext; +class OffscreenGLCanvas; + +namespace hifi { namespace qml { + +class OffscreenSurface; + +namespace impl { + +class RenderControl; +class RenderEventHandler; + +class SharedObject : public QObject { + Q_OBJECT + + friend class RenderEventHandler; + +public: + static void setSharedContext(QOpenGLContext* context); + static QOpenGLContext* getSharedContext(); + static TextureCache& getTextureCache(); + + SharedObject(); + virtual ~SharedObject(); + + void create(OffscreenSurface* surface); + void setRootItem(QQuickItem* rootItem); + void destroy(); + bool isQuit(); + + QSize getSize() const; + void setSize(const QSize& size); + void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; } + + QQuickWindow* getWindow() { return _quickWindow; } + QQuickItem* getRootItem() { return _rootItem; } + QQmlContext* getContext() { return _qmlContext; } + void setProxyWindow(QWindow* window); + + void pause(); + void resume(); + bool isPaused() const; + bool fetchTexture(TextureAndFence& textureAndFence); + + +private: + bool event(QEvent* e) override; + + bool preRender(); + void shutdownRendering(OffscreenGLCanvas& canvas, const QSize& size); + // Called by the render event handler, from the render thread + void initializeRenderControl(QOpenGLContext* context); + void releaseTextureAndFence(); + void setRenderTarget(uint32_t fbo, const QSize& size); + + QQmlEngine* acquireEngine(OffscreenSurface* surface); + void releaseEngine(QQmlEngine* engine); + + void requestRender(); + void requestRenderSync(); + void wait(); + void wake(); + void onInitialize(); + void onRender(); + void onTimer(); + void onAboutToQuit(); + void updateTextureAndFence(const TextureAndFence& newTextureAndFence); + + // Texture management + TextureAndFence _latestTextureAndFence{ 0, 0 }; + RenderControl* _renderControl{ nullptr }; + RenderEventHandler* _renderObject{ nullptr }; + QQuickWindow* _quickWindow{ nullptr }; + QWindow* _proxyWindow{ nullptr }; + QQuickItem* _item{ nullptr }; + QQuickItem* _rootItem{ nullptr }; + QQmlContext* _qmlContext{ nullptr }; + QTimer* _renderTimer{ nullptr }; + QThread* _renderThread{ nullptr }; + QWaitCondition _cond; + mutable QMutex _mutex; + + uint64_t _lastRenderTime{ 0 }; + QSize _size{ 100, 100 }; + uint8_t _maxFps{ 60 }; + + bool _renderRequested{ false }; + bool _syncRequested{ false }; + bool _quit{ false }; + bool _paused{ false }; +}; + +} // namespace impl +}} // namespace hifi::qml diff --git a/libraries/qml/src/qml/impl/TextureCache.cpp b/libraries/qml/src/qml/impl/TextureCache.cpp new file mode 100644 index 0000000000..c649a36594 --- /dev/null +++ b/libraries/qml/src/qml/impl/TextureCache.cpp @@ -0,0 +1,152 @@ +#include "TextureCache.h" + +#include + +#include + +#include +#include + +#include "Profiling.h" + +using namespace hifi::qml::impl; + +#if defined(Q_OS_ANDROID) +#define USE_GLES 1 +#endif + +uint64_t uvec2ToUint64(const QSize& size) { + uint64_t result = size.width(); + result <<= 32; + result |= size.height(); + return result; +} + +void TextureCache::acquireSize(const QSize& size) { + auto sizeKey = uvec2ToUint64(size); + Lock lock(_mutex); + auto& textureSet = _textures[sizeKey]; + ++textureSet.clientCount; +} + +void TextureCache::releaseSize(const QSize& size) { + auto sizeKey = uvec2ToUint64(size); + ValueList texturesToDelete; + { + Lock lock(_mutex); + assert(_textures.count(sizeKey)); + auto& textureSet = _textures[sizeKey]; + if (0 == --textureSet.clientCount) { + texturesToDelete.swap(textureSet.returnedTextures); + _textures.erase(sizeKey); + } + } + for (const auto& textureAndFence : texturesToDelete) { + destroy(textureAndFence); + } +} + +uint32_t TextureCache::acquireTexture(const QSize& size) { + Lock lock(_mutex); + recycle(); + + + ++_activeTextureCount; + auto sizeKey = uvec2ToUint64(size); + assert(_textures.count(sizeKey)); + auto& textureSet = _textures[sizeKey]; + if (!textureSet.returnedTextures.empty()) { + auto textureAndFence = textureSet.returnedTextures.front(); + textureSet.returnedTextures.pop_front(); + glWaitSync((GLsync)textureAndFence.second, 0, GL_TIMEOUT_IGNORED); + glDeleteSync((GLsync)textureAndFence.second); + return textureAndFence.first; + } + return createTexture(size); +} + +void TextureCache::releaseTexture(const Value& textureAndFence) { + --_activeTextureCount; + Lock lock(_mutex); + _returnedTextures.push_back(textureAndFence); +} + +void TextureCache::report() { + if (randFloat() < 0.01f) { + PROFILE_COUNTER(render_qml_gl, "offscreenTextures", + { + { "total", QVariant::fromValue(_allTextureCount.load()) }, + { "active", QVariant::fromValue(_activeTextureCount.load()) }, + }); + PROFILE_COUNTER(render_qml_gl, "offscreenTextureMemory", { { "value", QVariant::fromValue(_totalTextureUsage) } }); + } +} + +size_t TextureCache::getUsedTextureMemory() { + return _totalTextureUsage; +} + +size_t TextureCache::getMemoryForSize(const QSize& size) { + // Base size + mips + return static_cast(((size.width() * size.height()) << 2) * 1.33f); +} + +void TextureCache::destroyTexture(uint32_t texture) { + --_allTextureCount; + auto size = _textureSizes[texture]; + assert(getMemoryForSize(size) <= _totalTextureUsage); + _totalTextureUsage -= getMemoryForSize(size); + _textureSizes.erase(texture); + // FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context. + glDeleteTextures(1, &texture); +} + +void TextureCache::destroy(const Value& textureAndFence) { + const auto& fence = textureAndFence.second; + // FIXME prevents crash on shutdown, but we should migrate to a global functions object owned by the shared context. + glWaitSync((GLsync)fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync((GLsync)fence); + destroyTexture(textureAndFence.first); +} + +uint32_t TextureCache::createTexture(const QSize& size) { + // Need a new texture + uint32_t newTexture; + glGenTextures(1, &newTexture); + ++_allTextureCount; + _textureSizes[newTexture] = size; + _totalTextureUsage += getMemoryForSize(size); + glBindTexture(GL_TEXTURE_2D, newTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); +#if !defined(USE_GLES) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f); +#endif + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size.width(), size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + return newTexture; +} + +void TextureCache::recycle() { + // First handle any global returns + ValueList returnedTextures; + returnedTextures.swap(_returnedTextures); + + for (auto textureAndFence : returnedTextures) { + GLuint texture = textureAndFence.first; + QSize size = _textureSizes[texture]; + auto sizeKey = uvec2ToUint64(size); + // Textures can be returned after all surfaces of the given size have been destroyed, + // in which case we just destroy the texture + if (!_textures.count(sizeKey)) { + destroy(textureAndFence); + continue; + } + _textures[sizeKey].returnedTextures.push_back(textureAndFence); + } +} diff --git a/libraries/qml/src/qml/impl/TextureCache.h b/libraries/qml/src/qml/impl/TextureCache.h new file mode 100644 index 0000000000..572e1cadea --- /dev/null +++ b/libraries/qml/src/qml/impl/TextureCache.h @@ -0,0 +1,75 @@ +// +// Created by Bradley Austin Davis on 2018-01-04 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once +#ifndef hifi_QmlTextureCache_h +#define hifi_QmlTextureCache_h + +#include +#include +#include +#include +#include +#include + +#include + +namespace hifi { namespace qml { namespace impl { + +class TextureAndFence : public std::pair { + using Parent = std::pair; +public: + TextureAndFence() : Parent(0, 0) {} + TextureAndFence(uint32_t texture, void* sync) : Parent(texture, sync) {}; +}; + + +class TextureCache { +public: + using Value = TextureAndFence; + using ValueList = std::list; + using Size = uint64_t; + + struct TextureSet { + Size textureSize; + // The number of surfaces with this size + size_t clientCount{ 0 }; + ValueList returnedTextures; + }; + + + void releaseSize(const QSize& size); + void acquireSize(const QSize& size); + uint32_t acquireTexture(const QSize& size); + void releaseTexture(const Value& textureAndFence); + + // For debugging + void report(); + size_t getUsedTextureMemory(); +private: + static size_t getMemoryForSize(const QSize& size); + + uint32_t createTexture(const QSize& size); + void destroyTexture(uint32_t texture); + + void destroy(const Value& textureAndFence); + void recycle(); + + using Mutex = std::mutex; + using Lock = std::unique_lock; + std::atomic _allTextureCount; + std::atomic _activeTextureCount; + std::unordered_map _textures; + std::unordered_map _textureSizes; + Mutex _mutex; + std::list _returnedTextures; + size_t _totalTextureUsage{ 0 }; +}; + +}}} // namespace hifi::qml::impl + +#endif diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index c9ac0524ad..69a8587581 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -187,14 +187,14 @@ void Deck::processFrames() { void Deck::removeClip(const ClipConstPointer& clip) { Locker lock(_mutex); - std::remove_if(_clips.begin(), _clips.end(), [&](const Clip::ConstPointer& testClip)->bool { + _clips.remove_if([&](const Clip::ConstPointer& testClip)->bool { return (clip == testClip); }); } void Deck::removeClip(const QString& clipName) { Locker lock(_mutex); - std::remove_if(_clips.begin(), _clips.end(), [&](const Clip::ConstPointer& clip)->bool { + _clips.remove_if([&](const Clip::ConstPointer& clip)->bool { return (clip->getName() == clipName); }); } diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 6be3057c93..57e3572012 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME render-utils) AUTOSCRIBE_SHADER_LIB(gpu graphics render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") -setup_hifi_library(Widgets OpenGL Network Qml Quick Script) +setup_hifi_library(Gui Network Qml Quick Script) link_hifi_libraries(shared ktx gpu graphics model-networking render animation fbx image procedural) include_hifi_library_headers(networking) include_hifi_library_headers(octree) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 83753131c8..015f5678c8 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -263,7 +263,7 @@ void AmbientOcclusionEffect::configure(const Config& config) { const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { if (!_occlusionPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(ssao_makeOcclusion_frag)); + auto ps = ssao_makeOcclusion_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -288,7 +288,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { const gpu::PipelinePointer& AmbientOcclusionEffect::getHBlurPipeline() { if (!_hBlurPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(ssao_makeHorizontalBlur_frag)); + auto ps = ssao_makeHorizontalBlur_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -311,7 +311,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getHBlurPipeline() { const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() { if (!_vBlurPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(ssao_makeVerticalBlur_frag)); + auto ps = ssao_makeVerticalBlur_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -458,7 +458,7 @@ void DebugAmbientOcclusion::configure(const Config& config) { const gpu::PipelinePointer& DebugAmbientOcclusion::getDebugPipeline() { if (!_debugPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(ssao_debugOcclusion_frag)); + auto ps = ssao_debugOcclusion_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 4b5b06ab0b..5174126243 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -9,8 +9,6 @@ #include -#include "animdebugdraw_vert.h" -#include "animdebugdraw_frag.h" #include #include "AbstractViewStateInterface.h" #include "RenderUtilsLogging.h" @@ -19,6 +17,9 @@ #include "AnimDebugDraw.h" +#include "animdebugdraw_vert.h" +#include "animdebugdraw_frag.h" + class AnimDebugDrawData { public: @@ -101,8 +102,8 @@ AnimDebugDraw::AnimDebugDraw() : state->setBlendFunction(false, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - auto vertShader = gpu::Shader::createVertex(std::string(animdebugdraw_vert)); - auto fragShader = gpu::Shader::createPixel(std::string(animdebugdraw_frag)); + auto vertShader = animdebugdraw_vert::getShader(); + auto fragShader = animdebugdraw_frag::getShader(); auto program = gpu::Shader::createProgram(vertShader, fragShader); _pipeline = gpu::Pipeline::create(program, state); diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 70c2e3b5ce..bdd8f19a5c 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -57,8 +57,8 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline(RenderArgs* ar } if (!_antialiasingPipeline) { - auto vs = gpu::Shader::createVertex(std::string(fxaa_vert)); - auto ps = gpu::Shader::createPixel(std::string(fxaa_frag)); + auto vs = fxaa_vert::getShader(); + auto ps = fxaa_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -82,8 +82,8 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline(RenderArgs* ar const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { if (!_blendPipeline) { - auto vs = gpu::Shader::createVertex(std::string(fxaa_vert)); - auto ps = gpu::Shader::createPixel(std::string(fxaa_blend_frag)); + auto vs = fxaa_vert::getShader(); + auto ps = fxaa_blend_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 9d9367a6d5..89a83a651a 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -61,7 +61,7 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons if (!_pipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(BloomThreshold_frag)); + auto ps = BloomThreshold_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -113,7 +113,7 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In if (!_pipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(BloomApply_frag)); + auto ps = BloomApply_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index fe03ead4e1..24cffe2fb8 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -369,8 +369,7 @@ bool DebugDeferredBuffer::pipelineNeedsUpdate(Mode mode, std::string customFile) const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::string customFile) { if (pipelineNeedsUpdate(mode, customFile)) { - static const std::string VERTEX_SHADER { debug_deferred_buffer_vert }; - static const std::string FRAGMENT_SHADER { debug_deferred_buffer_frag }; + static const std::string FRAGMENT_SHADER { debug_deferred_buffer_frag::getSource() }; static const std::string SOURCE_PLACEHOLDER { "//SOURCE_PLACEHOLDER" }; static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER); Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, @@ -380,7 +379,7 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::str bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), getShaderSourceCode(mode, customFile)); - static const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); + const auto vs = debug_deferred_buffer_vert::getShader(); const auto ps = gpu::Shader::createPixel(bakedFragmentShader); const auto program = gpu::Shader::createProgram(vs, ps); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index c7e6ff1dcf..2e77d702c6 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -83,7 +83,7 @@ enum DeferredShader_BufferSlot { LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, }; -static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations); +static void loadLightProgram(const gpu::ShaderPointer& vertShader, const gpu::ShaderPointer& fragShader, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations); void DeferredLightingEffect::init() { _directionalAmbientSphereLightLocations = std::make_shared(); @@ -95,14 +95,14 @@ void DeferredLightingEffect::init() { _localLightLocations = std::make_shared(); _localLightOutlineLocations = std::make_shared(); - loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations); + loadLightProgram(deferred_light_vert::getShader(), directional_ambient_light_frag::getShader(), false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations); + loadLightProgram(deferred_light_vert::getShader(), directional_skybox_light_frag::getShader(), false, _directionalSkyboxLight, _directionalSkyboxLightLocations); - loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_frag, false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_frag, false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations); + loadLightProgram(deferred_light_vert::getShader(), directional_ambient_light_shadow_frag::getShader(), false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations); + loadLightProgram(deferred_light_vert::getShader(), directional_skybox_light_shadow_frag::getShader(), false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations); - loadLightProgram(deferred_light_vert, local_lights_shading_frag, true, _localLight, _localLightLocations); - loadLightProgram(deferred_light_vert, local_lights_drawOutline_frag, true, _localLightOutline, _localLightOutlineLocations); + loadLightProgram(deferred_light_vert::getShader(), local_lights_shading_frag::getShader(), true, _localLight, _localLightLocations); + loadLightProgram(deferred_light_vert::getShader(), local_lights_drawOutline_frag::getShader(), true, _localLightOutline, _localLightOutlineLocations); } void DeferredLightingEffect::setupKeyLightBatch(const RenderArgs* args, gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) { @@ -171,12 +171,8 @@ void DeferredLightingEffect::unsetLocalLightsBatch(gpu::Batch& batch, int cluste } } -static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* fragSource, LightLocationsPtr& locations) { - auto VS = gpu::Shader::createVertex(std::string(vertSource)); - auto PS = gpu::Shader::createPixel(std::string(fragSource)); - - gpu::ShaderPointer program = gpu::Shader::createProgram(VS, PS); - +static gpu::ShaderPointer makeLightProgram(const gpu::ShaderPointer& vertShader, const gpu::ShaderPointer& fragShader, LightLocationsPtr& locations) { + gpu::ShaderPointer program = gpu::Shader::createProgram(vertShader, fragShader); gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("colorMap"), DEFERRED_BUFFER_COLOR_UNIT)); slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), DEFERRED_BUFFER_NORMAL_UNIT)); @@ -224,9 +220,9 @@ static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* f return program; } -static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) { +static void loadLightProgram(const gpu::ShaderPointer& vertShader, const gpu::ShaderPointer& fragShader, bool lightVolume, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) { - gpu::ShaderPointer program = makeLightProgram(vertSource, fragSource, locations); + gpu::ShaderPointer program = makeLightProgram(vertShader, fragShader, locations); auto state = std::make_shared(); state->setColorWriteMask(true, true, true, false); @@ -761,8 +757,6 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { auto textureCache = DependencyManager::get(); { PROFILE_RANGE(render, "Process Default Skybox"); - auto textureCache = DependencyManager::get(); - QFileSelector fileSelector; fileSelector.setExtraSelectors(FileUtils::getFileSelectors()); auto skyboxUrl = fileSelector.select(PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.ktx"); diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index 8cb2d41723..c0db1597ca 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -133,7 +133,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu RenderArgs* args = renderContext->args; if (!_hazePipeline) { - gpu::ShaderPointer ps = gpu::Shader::createPixel(std::string(Haze_frag)); + gpu::ShaderPointer ps = Haze_frag::getShader(); gpu::ShaderPointer vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 23473e74f2..a082a681c9 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1946,8 +1946,8 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const static std::once_flag once; std::call_once(once, [&] { auto state = std::make_shared(); - auto VS = gpu::Shader::createVertex(std::string(glowLine_vert)); - auto PS = gpu::Shader::createPixel(std::string(glowLine_frag)); + auto VS = glowLine_vert::getShader(); + auto PS = glowLine_frag::getShader(); auto program = gpu::Shader::createProgram(VS, PS); state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(true, false, gpu::LESS_EQUAL); @@ -2003,8 +2003,8 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { static std::once_flag once; std::call_once(once, [&]() { - auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)); - auto ps = gpu::Shader::createPixel(std::string(standardDrawTexture_frag)); + auto vs = standardTransformPNTC_vert::getShader(); + auto ps = standardDrawTexture_frag::getShader(); auto program = gpu::Shader::createProgram(vs, ps); auto state = std::make_shared(); @@ -2039,8 +2039,8 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered) { if (!_gridPipeline) { - auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)); - auto ps = gpu::Shader::createPixel(std::string(grid_frag)); + auto vs = standardTransformPNTC_vert::getShader(); + auto ps = grid_frag::getShader(); auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram((*program)); _gridSlot = program->getUniformBuffers().findLocation("gridBuffer"); @@ -2123,12 +2123,9 @@ inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw(); } -static void buildWebShader(const std::string& vertShaderText, const std::string& fragShaderText, bool blendEnable, +static void buildWebShader(const gpu::ShaderPointer& vertShader, const gpu::ShaderPointer& fragShader, bool blendEnable, gpu::ShaderPointer& shaderPointerOut, gpu::PipelinePointer& pipelinePointerOut) { - auto VS = gpu::Shader::createVertex(vertShaderText); - auto PS = gpu::Shader::createPixel(fragShaderText); - - shaderPointerOut = gpu::Shader::createProgram(VS, PS); + shaderPointerOut = gpu::Shader::createProgram(vertShader, fragShader); gpu::Shader::BindingSet slotBindings; gpu::Shader::makeProgram(*shaderPointerOut, slotBindings); @@ -2151,8 +2148,8 @@ void GeometryCache::bindWebBrowserProgram(gpu::Batch& batch, bool transparent) { gpu::PipelinePointer GeometryCache::getWebBrowserProgram(bool transparent) { static std::once_flag once; std::call_once(once, [&]() { - buildWebShader(simple_vert, simple_opaque_web_browser_frag, false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipelineNoAA); - buildWebShader(simple_vert, simple_transparent_web_browser_frag, true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipelineNoAA); + buildWebShader(simple_vert::getShader(), simple_opaque_web_browser_frag::getShader(), false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipelineNoAA); + buildWebShader(simple_vert::getShader(), simple_transparent_web_browser_frag::getShader(), true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipelineNoAA); }); return transparent ? _simpleTransparentWebBrowserPipelineNoAA : _simpleOpaqueWebBrowserPipelineNoAA; @@ -2181,9 +2178,9 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp if (!fading) { static std::once_flag once; std::call_once(once, [&]() { - auto VS = gpu::Shader::createVertex(std::string(simple_vert)); - auto PS = gpu::Shader::createPixel(std::string(simple_textured_frag)); - auto PSUnlit = gpu::Shader::createPixel(std::string(simple_textured_unlit_frag)); + auto VS = simple_vert::getShader(); + auto PS = simple_textured_frag::getShader(); + auto PSUnlit = simple_textured_unlit_frag::getShader(); _simpleShader = gpu::Shader::createProgram(VS, PS); _unlitShader = gpu::Shader::createProgram(VS, PSUnlit); @@ -2196,9 +2193,9 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp } else { static std::once_flag once; std::call_once(once, [&]() { - auto VS = gpu::Shader::createVertex(std::string(simple_fade_vert)); - auto PS = gpu::Shader::createPixel(std::string(simple_textured_fade_frag)); - auto PSUnlit = gpu::Shader::createPixel(std::string(simple_textured_unlit_fade_frag)); + auto VS = simple_fade_vert::getShader(); + auto PS = simple_textured_fade_frag::getShader(); + auto PSUnlit = simple_textured_unlit_fade_frag::getShader(); _simpleFadeShader = gpu::Shader::createProgram(VS, PS); _unlitFadeShader = gpu::Shader::createProgram(VS, PSUnlit); diff --git a/libraries/render-utils/src/Haze.slh b/libraries/render-utils/src/Haze.slh index 15e484e055..10b493da2c 100644 --- a/libraries/render-utils/src/Haze.slh +++ b/libraries/render-utils/src/Haze.slh @@ -26,7 +26,7 @@ struct HazeParams { vec3 colorModulationFactor; int hazeMode; - mat4 zoneTransform; + mat4 transform; float backgroundBlend; float hazeRangeFactor; diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index fee1f4a568..9501a74d52 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -130,8 +130,8 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c fillState->setColorWriteMask(false, false, false, false); fillState->setCullMode(gpu::State::CULL_FRONT); - auto vs = gpu::Shader::createVertex(std::string(Highlight_aabox_vert)); - auto ps = gpu::Shader::createPixel(std::string(nop_frag)); + auto vs = Highlight_aabox_vert::getShader(); + auto ps = nop_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -313,7 +313,7 @@ const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightSt state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(Highlight_frag)); + auto ps = Highlight_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -325,7 +325,7 @@ const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightSt _pipeline = gpu::Pipeline::create(program, state); - ps = gpu::Shader::createPixel(std::string(Highlight_filled_frag)); + ps = Highlight_filled_frag::getShader(); program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram(*program, slotBindings); _pipelineFilled = gpu::Pipeline::create(program, state); @@ -385,8 +385,7 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons } void DebugHighlight::initializePipelines() { - static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert }; - static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag }; + static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag::getSource() }; static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" }; static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER); Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, @@ -396,7 +395,7 @@ void DebugHighlight::initializePipelines() { state->setDepthTest(gpu::State::DepthTest(false, false)); state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); - const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); + const auto vs = debug_deferred_buffer_vert::getShader(); // Depth shader { @@ -553,14 +552,14 @@ const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const #include "model_shadow_frag.h" void DrawHighlightTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { - auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); + auto modelVertex = model_shadow_vert::getShader(); + auto modelPixel = model_shadow_frag::getShader(); gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutSkinned(), modelProgram, state); - auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); + auto skinVertex = skin_model_shadow_vert::getShader(); gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, modelPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withSkinned(), diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 89d3f4faee..3f52760eff 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -5,9 +5,6 @@ // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - <@func declareSkyboxMap()@> // declareSkyboxMap @@ -15,7 +12,6 @@ uniform samplerCube skyboxMap; vec4 evalSkyboxLight(vec3 direction, float lod) { // textureQueryLevels is not available until #430, so we require explicit lod - // float mipmapLevel = lod * textureQueryLevels(skyboxMap); float filterLod = textureQueryLod(skyboxMap, direction).x; // Keep texture filtering LOD as limit to prevent aliasing on specular reflection lod = max(lod, filterLod); @@ -28,15 +24,13 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { vec3 fresnelSchlickAmbient(vec3 fresnelColor, float ndotd, float gloss) { float f = pow(1.0 - ndotd, 5.0); return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; -// return fresnelColor + (vec3(1.0) - fresnelColor) * f; } <@if supportAmbientMap@> <$declareSkyboxMap()$> <@endif@> -vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { - vec3 lightDir = -reflect(surface.eyeDir, surface.normal); +vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface, vec3 lightDir) { vec3 specularLight; <@if supportIfAmbientMapElseAmbientSphere@> if (getLightHasAmbientMap(ambient)) @@ -80,14 +74,21 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie <@endif@> ) { - // Fresnel + // Rotate surface normal and eye direction + vec3 ambientSpaceSurfaceNormal = (ambient.transform * vec4(surface.normal, 0.0)).xyz; + vec3 ambientSpaceSurfaceEyeDir = (ambient.transform * vec4(surface.eyeDir, 0.0)).xyz; +<@if supportScattering@> + vec3 ambientSpaceLowNormal = (ambient.transform * vec4(lowNormalCurvature.xyz, 0.0)).xyz; +<@endif@> + vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface.ndotv, 1.0-surface.roughness); - // Diffuse from ambient - diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.normal).xyz; + diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * + sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), ambientSpaceSurfaceNormal).xyz; // Specular highlight from ambient - specular = evalAmbientSpecularIrradiance(ambient, surface) * ambientFresnel; + vec3 ambientSpaceLightDir = -reflect(ambientSpaceSurfaceEyeDir, ambientSpaceSurfaceNormal); + specular = evalAmbientSpecularIrradiance(ambient, surface, ambientSpaceLightDir) * ambientFresnel; <@if supportScattering@> if (scattering * isScatteringEnabled() > 0.0) { @@ -98,7 +99,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie obscurance = min(obscurance, ambientOcclusion); // Diffuse from ambient - diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lowNormalCurvature.xyz).xyz; + diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), ambientSpaceLowNormal).xyz; // Scattering ambient specular is the same as non scattering for now // TODO: we should use the same specular answer as for direct lighting diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index d6ac7fd2e2..ea02edb601 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -605,8 +605,8 @@ void DebugLightClusters::configure(const Config& config) { const gpu::PipelinePointer DebugLightClusters::getDrawClusterGridPipeline() { if (!_drawClusterGrid) { - auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawGrid_vert)); - auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawGrid_frag)); + auto vs = lightClusters_drawGrid_vert::getShader(); + auto ps = lightClusters_drawGrid_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -635,7 +635,7 @@ const gpu::PipelinePointer DebugLightClusters::getDrawClusterFromDepthPipeline() if (!_drawClusterFromDepth) { // auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawGrid_vert)); auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawClusterFromDepth_frag)); + auto ps = lightClusters_drawClusterFromDepth_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -665,7 +665,7 @@ const gpu::PipelinePointer DebugLightClusters::getDrawClusterContentPipeline() { if (!_drawClusterContent) { // auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawClusterContent_vert)); auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawClusterContent_frag)); + auto ps = lightClusters_drawClusterContent_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index eacd5b5b66..ad7409b731 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -121,16 +121,16 @@ void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* a void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args); void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest) { - auto vertex = gpu::Shader::createVertex(std::string(overlay3D_vert)); - auto vertexModel = gpu::Shader::createVertex(std::string(model_vert)); - auto pixel = gpu::Shader::createPixel(std::string(overlay3D_frag)); - auto pixelTranslucent = gpu::Shader::createPixel(std::string(overlay3D_translucent_frag)); - auto pixelUnlit = gpu::Shader::createPixel(std::string(overlay3D_unlit_frag)); - auto pixelTranslucentUnlit = gpu::Shader::createPixel(std::string(overlay3D_translucent_unlit_frag)); - auto pixelModel = gpu::Shader::createPixel(std::string(overlay3D_model_frag)); - auto pixelModelTranslucent = gpu::Shader::createPixel(std::string(overlay3D_model_translucent_frag)); - auto pixelModelUnlit = gpu::Shader::createPixel(std::string(overlay3D_model_unlit_frag)); - auto pixelModelTranslucentUnlit = gpu::Shader::createPixel(std::string(overlay3D_model_translucent_unlit_frag)); + auto vertex = overlay3D_vert::getShader(); + auto vertexModel = model_vert::getShader(); + auto pixel = overlay3D_frag::getShader(); + auto pixelTranslucent = overlay3D_translucent_frag::getShader(); + auto pixelUnlit = overlay3D_unlit_frag::getShader(); + auto pixelTranslucentUnlit = overlay3D_translucent_unlit_frag::getShader(); + auto pixelModel = overlay3D_model_frag::getShader(); + auto pixelModelTranslucent = overlay3D_model_translucent_frag::getShader(); + auto pixelModelUnlit = overlay3D_model_unlit_frag::getShader(); + auto pixelModelTranslucentUnlit = overlay3D_model_translucent_unlit_frag::getShader(); auto opaqueProgram = gpu::Shader::createProgram(vertex, pixel); auto translucentProgram = gpu::Shader::createProgram(vertex, pixelTranslucent); @@ -187,66 +187,66 @@ void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest) { void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { // Vertex shaders - auto simpleVertex = gpu::Shader::createVertex(std::string(simple_vert)); - auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); - auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); - auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); - auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); - auto modelTranslucentVertex = gpu::Shader::createVertex(std::string(model_translucent_vert)); - auto modelTranslucentNormalMapVertex = gpu::Shader::createVertex(std::string(model_translucent_normal_map_vert)); - auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); - auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); - auto skinModelShadowVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); - auto modelLightmapFadeVertex = gpu::Shader::createVertex(std::string(model_lightmap_fade_vert)); - auto modelLightmapNormalMapFadeVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_fade_vert)); - auto skinModelFadeVertex = gpu::Shader::createVertex(std::string(skin_model_fade_vert)); - auto skinModelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_fade_vert)); + auto simpleVertex = simple_vert::getShader(); + auto modelVertex = model_vert::getShader(); + auto modelNormalMapVertex = model_normal_map_vert::getShader(); + auto modelLightmapVertex = model_lightmap_vert::getShader(); + auto modelLightmapNormalMapVertex = model_lightmap_normal_map_vert::getShader(); + auto modelTranslucentVertex = model_translucent_vert::getShader(); + auto modelTranslucentNormalMapVertex = model_translucent_normal_map_vert::getShader(); + auto modelShadowVertex = model_shadow_vert::getShader(); + auto skinModelVertex = skin_model_vert::getShader(); + auto skinModelNormalMapVertex = skin_model_normal_map_vert::getShader(); + auto skinModelShadowVertex = skin_model_shadow_vert::getShader(); + auto modelLightmapFadeVertex = model_lightmap_fade_vert::getShader(); + auto modelLightmapNormalMapFadeVertex = model_lightmap_normal_map_fade_vert::getShader(); + auto skinModelFadeVertex = skin_model_fade_vert::getShader(); + auto skinModelNormalMapFadeVertex = skin_model_normal_map_fade_vert::getShader(); auto skinModelTranslucentVertex = skinModelFadeVertex; // We use the same because it ouputs world position per vertex auto skinModelNormalMapTranslucentVertex = skinModelNormalMapFadeVertex; // We use the same because it ouputs world position per vertex - auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_fade_vert)); - auto modelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(model_normal_map_fade_vert)); - auto simpleFadeVertex = gpu::Shader::createVertex(std::string(simple_fade_vert)); - auto modelShadowFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert)); - auto skinModelShadowFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert)); + auto modelFadeVertex = model_fade_vert::getShader(); + auto modelNormalMapFadeVertex = model_normal_map_fade_vert::getShader(); + auto simpleFadeVertex = simple_fade_vert::getShader(); + auto modelShadowFadeVertex = model_shadow_fade_vert::getShader(); + auto skinModelShadowFadeVertex = skin_model_shadow_fade_vert::getShader(); // Pixel shaders - auto simplePixel = gpu::Shader::createPixel(std::string(simple_textured_frag)); - auto simpleUnlitPixel = gpu::Shader::createPixel(std::string(simple_textured_unlit_frag)); - auto simpleTranslucentPixel = gpu::Shader::createPixel(std::string(simple_transparent_textured_frag)); - auto simpleTranslucentUnlitPixel = gpu::Shader::createPixel(std::string(simple_transparent_textured_unlit_frag)); - auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); - auto modelUnlitPixel = gpu::Shader::createPixel(std::string(model_unlit_frag)); - auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(model_normal_map_frag)); - auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); - auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); - auto modelTranslucentPixel = gpu::Shader::createPixel(std::string(model_translucent_frag)); - auto modelTranslucentNormalMapPixel = gpu::Shader::createPixel(std::string(model_translucent_normal_map_frag)); - auto modelTranslucentUnlitPixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_frag)); - auto modelShadowPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); - auto modelLightmapPixel = gpu::Shader::createPixel(std::string(model_lightmap_frag)); - auto modelLightmapNormalMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag)); - auto modelLightmapSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag)); - auto modelLightmapNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)); - auto modelLightmapFadePixel = gpu::Shader::createPixel(std::string(model_lightmap_fade_frag)); - auto modelLightmapNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_map_fade_frag)); - auto modelLightmapSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_fade_frag)); - auto modelLightmapNormalSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_fade_frag)); + auto simplePixel = simple_textured_frag::getShader(); + auto simpleUnlitPixel = simple_textured_unlit_frag::getShader(); + auto simpleTranslucentPixel = simple_transparent_textured_frag::getShader(); + auto simpleTranslucentUnlitPixel = simple_transparent_textured_unlit_frag::getShader(); + auto modelPixel = model_frag::getShader(); + auto modelUnlitPixel = model_unlit_frag::getShader(); + auto modelNormalMapPixel = model_normal_map_frag::getShader(); + auto modelSpecularMapPixel = model_specular_map_frag::getShader(); + auto modelNormalSpecularMapPixel = model_normal_specular_map_frag::getShader(); + auto modelTranslucentPixel = model_translucent_frag::getShader(); + auto modelTranslucentNormalMapPixel = model_translucent_normal_map_frag::getShader(); + auto modelTranslucentUnlitPixel = model_translucent_unlit_frag::getShader(); + auto modelShadowPixel = model_shadow_frag::getShader(); + auto modelLightmapPixel = model_lightmap_frag::getShader(); + auto modelLightmapNormalMapPixel = model_lightmap_normal_map_frag::getShader(); + auto modelLightmapSpecularMapPixel = model_lightmap_specular_map_frag::getShader(); + auto modelLightmapNormalSpecularMapPixel = model_lightmap_normal_specular_map_frag::getShader(); + auto modelLightmapFadePixel = model_lightmap_fade_frag::getShader(); + auto modelLightmapNormalMapFadePixel = model_lightmap_normal_map_fade_frag::getShader(); + auto modelLightmapSpecularMapFadePixel = model_lightmap_specular_map_fade_frag::getShader(); + auto modelLightmapNormalSpecularMapFadePixel = model_lightmap_normal_specular_map_fade_frag::getShader(); - auto modelFadePixel = gpu::Shader::createPixel(std::string(model_fade_frag)); - auto modelUnlitFadePixel = gpu::Shader::createPixel(std::string(model_unlit_fade_frag)); - auto modelNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_map_fade_frag)); - auto modelSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_specular_map_fade_frag)); - auto modelNormalSpecularMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_fade_frag)); - auto modelShadowFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag)); - auto modelTranslucentFadePixel = gpu::Shader::createPixel(std::string(model_translucent_fade_frag)); - auto modelTranslucentNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_translucent_normal_map_fade_frag)); - auto modelTranslucentUnlitFadePixel = gpu::Shader::createPixel(std::string(model_translucent_unlit_fade_frag)); - auto simpleFadePixel = gpu::Shader::createPixel(std::string(simple_textured_fade_frag)); - auto simpleUnlitFadePixel = gpu::Shader::createPixel(std::string(simple_textured_unlit_fade_frag)); - auto simpleTranslucentFadePixel = gpu::Shader::createPixel(std::string(simple_transparent_textured_fade_frag)); - auto simpleTranslucentUnlitFadePixel = gpu::Shader::createPixel(std::string(simple_transparent_textured_unlit_fade_frag)); + auto modelFadePixel = model_fade_frag::getShader(); + auto modelUnlitFadePixel = model_unlit_fade_frag::getShader(); + auto modelNormalMapFadePixel = model_normal_map_fade_frag::getShader(); + auto modelSpecularMapFadePixel = model_specular_map_fade_frag::getShader(); + auto modelNormalSpecularMapFadePixel = model_normal_specular_map_fade_frag::getShader(); + auto modelShadowFadePixel = model_shadow_fade_frag::getShader(); + auto modelTranslucentFadePixel = model_translucent_fade_frag::getShader(); + auto modelTranslucentNormalMapFadePixel = model_translucent_normal_map_fade_frag::getShader(); + auto modelTranslucentUnlitFadePixel = model_translucent_unlit_fade_frag::getShader(); + auto simpleFadePixel = simple_textured_fade_frag::getShader(); + auto simpleUnlitFadePixel = simple_textured_unlit_fade_frag::getShader(); + auto simpleTranslucentFadePixel = simple_transparent_textured_fade_frag::getShader(); + auto simpleTranslucentUnlitFadePixel = simple_transparent_textured_unlit_fade_frag::getShader(); using Key = render::ShapeKey; auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, _4, _5); @@ -448,19 +448,19 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { // Vertex shaders - auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); - auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); - auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); - auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); - auto skinModelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_fade_vert)); + auto modelVertex = model_vert::getShader(); + auto modelNormalMapVertex = model_normal_map_vert::getShader(); + auto skinModelVertex = skin_model_vert::getShader(); + auto skinModelNormalMapVertex = skin_model_normal_map_vert::getShader(); + auto skinModelNormalMapFadeVertex = skin_model_normal_map_fade_vert::getShader(); // Pixel shaders - auto modelPixel = gpu::Shader::createPixel(std::string(forward_model_frag)); - auto modelUnlitPixel = gpu::Shader::createPixel(std::string(forward_model_unlit_frag)); - auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_map_frag)); - auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_specular_map_frag)); - auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_specular_map_frag)); - auto modelNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_map_fade_frag)); + auto modelPixel = forward_model_frag::getShader(); + auto modelUnlitPixel = forward_model_unlit_frag::getShader(); + auto modelNormalMapPixel = forward_model_normal_map_frag::getShader(); + auto modelSpecularMapPixel = forward_model_specular_map_frag::getShader(); + auto modelNormalSpecularMapPixel = forward_model_normal_specular_map_frag::getShader(); + auto modelNormalMapFadePixel = model_normal_map_fade_frag::getShader(); using Key = render::ShapeKey; auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, _4, _5); @@ -590,29 +590,29 @@ void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderAr } void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state) { - auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); + auto modelVertex = model_shadow_vert::getShader(); + auto modelPixel = model_shadow_frag::getShader(); gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutSkinned().withoutFade(), modelProgram, state); - auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); - auto skinPixel = gpu::Shader::createPixel(std::string(skin_model_shadow_frag)); + auto skinVertex = skin_model_shadow_vert::getShader(); + auto skinPixel = skin_model_shadow_frag::getShader(); gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, skinPixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withSkinned().withoutFade(), skinProgram, state); - auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert)); - auto modelFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag)); + auto modelFadeVertex = model_shadow_fade_vert::getShader(); + auto modelFadePixel = model_shadow_fade_frag::getShader(); gpu::ShaderPointer modelFadeProgram = gpu::Shader::createProgram(modelFadeVertex, modelFadePixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutSkinned().withFade(), modelFadeProgram, state); - auto skinFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert)); - auto skinFadePixel = gpu::Shader::createPixel(std::string(skin_model_shadow_fade_frag)); + auto skinFadeVertex = skin_model_shadow_fade_vert::getShader(); + auto skinFadePixel = skin_model_shadow_fade_frag::getShader(); gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(skinFadeVertex, skinFadePixel); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withSkinned().withFade(), diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index de8de9abcf..f9af157e7c 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -60,7 +60,7 @@ gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() { gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() { if (!_paintStencilPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(stencil_drawMask_frag)); + auto ps = stencil_drawMask_frag::getShader(); auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::makeProgram((*program)); diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index 2183f95565..ff415accc3 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -308,7 +308,7 @@ void diffuseProfileGPU(gpu::TexturePointer& profileMap, RenderArgs* args) { gpu::PipelinePointer makePipeline; { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(subsurfaceScattering_makeProfile_frag)); + auto ps = subsurfaceScattering_makeProfile_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -344,7 +344,7 @@ void diffuseScatterGPU(const gpu::TexturePointer& profileMap, gpu::TexturePointe gpu::PipelinePointer makePipeline; { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(subsurfaceScattering_makeLUT_frag)); + auto ps = subsurfaceScattering_makeLUT_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -382,7 +382,7 @@ void computeSpecularBeckmannGPU(gpu::TexturePointer& beckmannMap, RenderArgs* ar gpu::PipelinePointer makePipeline; { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(subsurfaceScattering_makeSpecularBeckmann_frag)); + auto ps = subsurfaceScattering_makeSpecularBeckmann_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -457,7 +457,7 @@ void DebugSubsurfaceScattering::configure(const Config& config) { gpu::PipelinePointer DebugSubsurfaceScattering::getScatteringPipeline() { if (!_scatteringPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(subsurfaceScattering_drawScattering_frag)); + auto ps = subsurfaceScattering_drawScattering_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index c4eea7ce7f..afed9ee8fd 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -212,7 +212,7 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con const gpu::PipelinePointer& LinearDepthPass::getLinearDepthPipeline() { if (!_linearDepthPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_makeLinearDepth_frag)); + auto ps = surfaceGeometry_makeLinearDepth_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -239,7 +239,7 @@ const gpu::PipelinePointer& LinearDepthPass::getLinearDepthPipeline() { const gpu::PipelinePointer& LinearDepthPass::getDownsamplePipeline() { if (!_downsamplePipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_downsampleDepthNormal_frag)); + auto ps = surfaceGeometry_downsampleDepthNormal_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -540,7 +540,7 @@ void SurfaceGeometryPass::run(const render::RenderContextPointer& renderContext, const gpu::PipelinePointer& SurfaceGeometryPass::getCurvaturePipeline() { if (!_curvaturePipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_makeCurvature_frag)); + auto ps = surfaceGeometry_makeCurvature_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp index 72d692c5b2..e1abefd681 100644 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -28,7 +28,7 @@ ToneMappingEffect::ToneMappingEffect() { } void ToneMappingEffect::init() { - auto blitPS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(toneMapping_frag))); + auto blitPS = toneMapping_frag::getShader(); auto blitVS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); auto blitProgram = gpu::ShaderPointer(gpu::Shader::createProgram(blitVS, blitPS)); diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 11e5b8d2f5..1d358ff895 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -78,7 +78,7 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { if (!_keyLightPipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(zone_drawKeyLight_frag)); + auto ps = zone_drawKeyLight_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -99,7 +99,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { const gpu::PipelinePointer& DebugZoneLighting::getAmbientPipeline() { if (!_ambientPipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(zone_drawAmbient_frag)); + auto ps = zone_drawAmbient_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -120,7 +120,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getAmbientPipeline() { const gpu::PipelinePointer& DebugZoneLighting::getBackgroundPipeline() { if (!_backgroundPipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(zone_drawSkybox_frag)); + auto ps = zone_drawSkybox_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/model_translucent_fade.slf b/libraries/render-utils/src/model_translucent_fade.slf index 316dae7aad..e767d0ffa5 100644 --- a/libraries/render-utils/src/model_translucent_fade.slf +++ b/libraries/render-utils/src/model_translucent_fade.slf @@ -1,25 +1,18 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// // model_translucent_fade.frag -// fragment shader -// // Created by Olivier Prat on 06/05/17. // Copyright 2017 High Fidelity, Inc. -// // Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// +// See the accompanying file LICENSE <@include graphics/Material.slh@> - <@include DeferredGlobalLight.slh@> <$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include LightLocal.slh@> - <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index b6868a2cd4..f9508b343f 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -223,9 +223,9 @@ void Font::setupGPU() { // Setup render pipeline { - auto vertexShader = gpu::Shader::createVertex(std::string(sdf_text3D_vert)); - auto pixelShader = gpu::Shader::createPixel(std::string(sdf_text3D_frag)); - auto pixelShaderTransparent = gpu::Shader::createPixel(std::string(sdf_text3D_transparent_frag)); + auto vertexShader = sdf_text3D_vert::getShader(); + auto pixelShader = sdf_text3D_frag::getShader(); + auto pixelShaderTransparent = sdf_text3D_transparent_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader); gpu::ShaderPointer programTransparent = gpu::Shader::createProgram(vertexShader, pixelShaderTransparent); diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 2be6f8fad2..0625179a6d 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -210,7 +210,7 @@ BlurGaussian::BlurGaussian(bool generateOutputFramebuffer, unsigned int downsamp gpu::PipelinePointer BlurGaussian::getBlurVPipeline() { if (!_blurVPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(blurGaussianV_frag)); + auto ps = blurGaussianV_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -232,7 +232,7 @@ gpu::PipelinePointer BlurGaussian::getBlurVPipeline() { gpu::PipelinePointer BlurGaussian::getBlurHPipeline() { if (!_blurHPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(blurGaussianH_frag)); + auto ps = blurGaussianH_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -324,7 +324,7 @@ BlurGaussianDepthAware::BlurGaussianDepthAware(bool generateOutputFramebuffer, c gpu::PipelinePointer BlurGaussianDepthAware::getBlurVPipeline() { if (!_blurVPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(blurGaussianDepthAwareV_frag)); + auto ps = blurGaussianDepthAwareV_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -347,7 +347,7 @@ gpu::PipelinePointer BlurGaussianDepthAware::getBlurVPipeline() { gpu::PipelinePointer BlurGaussianDepthAware::getBlurHPipeline() { if (!_blurHPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(blurGaussianDepthAwareH_frag)); + auto ps = blurGaussianDepthAwareH_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 08d6340e43..6a35623fa0 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -34,8 +34,8 @@ using namespace render; const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { if (!_drawCellBoundsPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawCellBounds_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawCellBounds_frag)); + auto vs = drawCellBounds_vert::getShader(); + auto ps = drawCellBounds_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -59,7 +59,7 @@ const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() { const gpu::PipelinePointer DrawSceneOctree::getDrawLODReticlePipeline() { if (!_drawLODReticlePipeline) { auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); - auto ps = gpu::Shader::createPixel(std::string(drawLODReticle_frag)); + auto ps = drawLODReticle_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -162,8 +162,8 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS const gpu::PipelinePointer DrawItemSelection::getDrawItemBoundPipeline() { if (!_drawItemBoundPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag)); + auto vs = drawItemBounds_vert::getShader(); + auto ps = drawItemBounds_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 148e104453..a11e9b1a88 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -35,8 +35,8 @@ void DrawStatusConfig::dirtyHelper() { const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() { if (!_drawItemBoundsPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag)); + auto vs = drawItemBounds_vert::getShader(); + auto ps = drawItemBounds_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -63,8 +63,8 @@ const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() { const gpu::PipelinePointer DrawStatus::getDrawItemStatusPipeline() { if (!_drawItemStatusPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawItemStatus_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawItemStatus_frag)); + auto vs = drawItemStatus_vert::getShader(); + auto ps = drawItemStatus_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 629cc55ccb..88d38d1c66 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -22,8 +22,8 @@ #include #include -#include -#include +#include "drawItemBounds_vert.h" +#include "drawItemBounds_frag.h" using namespace render; @@ -155,8 +155,8 @@ void DrawLight::run(const RenderContextPointer& renderContext, const ItemBounds& const gpu::PipelinePointer DrawBounds::getPipeline() { if (!_boundsPipeline) { - auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert)); - auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag)); + auto vs = drawItemBounds_vert::getShader(); + auto ps = drawItemBounds_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render/src/render/drawItemBounds.slf b/libraries/render/src/render/drawItemBounds.slf index e01d1607bd..84c47d0933 100644 --- a/libraries/render/src/render/drawItemBounds.slf +++ b/libraries/render/src/render/drawItemBounds.slf @@ -15,7 +15,6 @@ in vec4 varColor; in vec2 varTexcoord; out vec4 outFragColor; - void main(void) { float var = step(fract(varTexcoord.x * varTexcoord.y * 1.0), 0.5); diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 478e4c4765..31436bbf8b 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,4 +1,5 @@ set(TARGET_NAME script-engine) +# FIXME Move undo scripting interface to application and remove Widgets setup_hifi_library(Gui Network Script ScriptTools WebSockets Widgets) target_zlib() diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index a8fe14e23e..713501aa77 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME shared) # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) -setup_hifi_library(Gui Network Script Widgets) +setup_hifi_library(Gui Network Script) if (WIN32) target_link_libraries(${TARGET_NAME} Wbemuuid.lib) diff --git a/libraries/shared/src/BitVectorHelpers.h b/libraries/shared/src/BitVectorHelpers.h index 9f5af0c380..a619fe3999 100644 --- a/libraries/shared/src/BitVectorHelpers.h +++ b/libraries/shared/src/BitVectorHelpers.h @@ -12,6 +12,8 @@ #ifndef hifi_BitVectorHelpers_h #define hifi_BitVectorHelpers_h +#include "NumericalConstants.h" + int calcBitVectorSize(int numBits) { return ((numBits - 1) >> 3) + 1; } diff --git a/libraries/shared/src/QVariantGLM.cpp b/libraries/shared/src/QVariantGLM.cpp index b5b840c2ca..1c73315d94 100644 --- a/libraries/shared/src/QVariantGLM.cpp +++ b/libraries/shared/src/QVariantGLM.cpp @@ -66,7 +66,7 @@ void qListtoRgbColor(const QVariant& q, rgbColor& returnValue) { glm::vec3 qMapToGlmVec3(const QVariant& q) { QVariantMap qMap = q.toMap(); - if (qMap.contains("x") && qMap.contains("y") && qMap.contains("y")) { + if (qMap.contains("x") && qMap.contains("y") && qMap.contains("z")) { return glm::vec3( qMap["x"].toFloat(), qMap["y"].toFloat(), diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 786f58804f..9f688f9def 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -85,7 +85,7 @@ public: protected: bool _isBalanced{ false }; - TriangleOctreeCell _triangleOctree; std::vector _triangles; + TriangleOctreeCell _triangleOctree; AABox _bounds; }; diff --git a/libraries/shared/src/shared/AbstractLoggerInterface.h b/libraries/shared/src/shared/AbstractLoggerInterface.h index c202496103..f48496324b 100644 --- a/libraries/shared/src/shared/AbstractLoggerInterface.h +++ b/libraries/shared/src/shared/AbstractLoggerInterface.h @@ -50,8 +50,8 @@ signals: private: bool _extraDebugging{ false }; - bool _debugPrint{ false }; - bool _infoPrint{ false }; + bool _debugPrint{ true }; + bool _infoPrint{ true }; bool _criticalPrint{ true }; bool _warningPrint{ true }; bool _suppressPrint{ true }; diff --git a/libraries/shared/src/shared/MiniPromises.h b/libraries/shared/src/shared/MiniPromises.h index 5983f135b7..30b57ad7b8 100644 --- a/libraries/shared/src/shared/MiniPromises.h +++ b/libraries/shared/src/shared/MiniPromises.h @@ -103,7 +103,7 @@ public: return self(); } Promise fail(ErrorFunction errorOnly) { - return fail([this, errorOnly](QString error, QVariantMap result) { + return fail([errorOnly](QString error, QVariantMap result) { errorOnly(error); }); } @@ -122,7 +122,7 @@ public: } Promise then(SuccessFunction successOnly) { - return then([this, successOnly](QString error, QVariantMap result) { + return then([successOnly](QString error, QVariantMap result) { successOnly(result); }); } diff --git a/libraries/trackers/src/trackers/EyeTracker.cpp b/libraries/trackers/src/trackers/EyeTracker.cpp index e641abc630..a64b945c55 100644 --- a/libraries/trackers/src/trackers/EyeTracker.cpp +++ b/libraries/trackers/src/trackers/EyeTracker.cpp @@ -9,7 +9,6 @@ #include "EyeTracker.h" #include -#include #include #include diff --git a/libraries/ui-plugins/CMakeLists.txt b/libraries/ui-plugins/CMakeLists.txt index 8da0815082..8f4123c1d4 100644 --- a/libraries/ui-plugins/CMakeLists.txt +++ b/libraries/ui-plugins/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME ui-plugins) -setup_hifi_library(OpenGL) +setup_hifi_library(Gui) link_hifi_libraries(shared plugins ui) include_hifi_library_headers(gpu) \ No newline at end of file diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 2dd23f5134..cbafc00f82 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME ui) setup_hifi_library(OpenGL Multimedia Network Qml Quick Script WebChannel WebSockets XmlPatterns ${PLATFORM_QT_COMPONENTS}) -link_hifi_libraries(shared networking gl audio audio-client plugins pointers) +link_hifi_libraries(shared networking qml gl audio audio-client plugins pointers) include_hifi_library_headers(controllers) # Required for some low level GL interaction in the OffscreenQMLSurface diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 221f5013bf..0e3c15b965 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -127,13 +127,17 @@ void OffscreenUi::removeModalDialog(QObject* modal) { } } -void OffscreenUi::create() { - OffscreenQmlSurface::create(); - auto myContext = getSurfaceContext(); +void OffscreenUi::onRootContextCreated(QQmlContext* qmlContext) { + OffscreenQmlSurface::onRootContextCreated(qmlContext); + qmlContext->setContextProperty("OffscreenUi", this); + qmlContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags()); + qmlContext->setContextProperty("fileDialogHelper", new FileDialogHelper()); +#ifdef DEBUG + qmlContext->setContextProperty("DebugQML", QVariant(true)); +#else + qmlContext->setContextProperty("DebugQML", QVariant(false)); +#endif - myContext->setContextProperty("OffscreenUi", this); - myContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags()); - myContext->setContextProperty("fileDialogHelper", new FileDialogHelper()); } void OffscreenUi::show(const QUrl& url, const QString& name, std::function f) { @@ -656,12 +660,6 @@ void OffscreenUi::createDesktop(const QUrl& url) { return; } -#ifdef DEBUG - getSurfaceContext()->setContextProperty("DebugQML", QVariant(true)); -#else - getSurfaceContext()->setContextProperty("DebugQML", QVariant(false)); -#endif - load(url, [=](QQmlContext* context, QObject* newObject) { Q_UNUSED(context) _desktop = static_cast(newObject); diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index ab3a209820..e507333840 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -57,7 +57,6 @@ class OffscreenUi : public OffscreenQmlSurface, public Dependency { friend class VrMenu; public: OffscreenUi(); - virtual void create() override; void createDesktop(const QUrl& url); void show(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QObject*) {}); void hide(const QString& name); @@ -253,6 +252,9 @@ private slots: void hoverEndEvent(const PointerEvent& event); void handlePointerEvent(const PointerEvent& event); +protected: + void onRootContextCreated(QQmlContext* qmlContext) override; + private: QString fileDialog(const QVariantMap& properties); ModalDialogListener *fileDialogAsync(const QVariantMap &properties); diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 0825d238bc..02c9707d95 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -37,7 +37,6 @@ static const uvec2 MIN_QML_WINDOW_SIZE { 120, 80 }; QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) { const auto argumentCount = context->argumentCount(); - QString title; QVariantMap properties; if (argumentCount > 1) { if (!context->argument(0).isUndefined()) { diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp index 4e61eba28f..b600fc7b29 100644 --- a/libraries/ui/src/ui/Menu.cpp +++ b/libraries/ui/src/ui/Menu.cpp @@ -300,7 +300,6 @@ QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) { } foreach (QAction* menuAction, menuActions) { - QString actionText = menuAction->text(); if (menuName == menuAction->text()) { return menuAction; } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 6ff0a7d416..ea34f3de76 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -7,13 +7,13 @@ // #include "OffscreenQmlSurface.h" -#include - #include #include #include +#include +#include #include #include #include @@ -41,11 +41,11 @@ #include #include #include +#include #include #include #include -#include #include #include "ImageProvider.h" @@ -57,16 +57,12 @@ #include "ToolbarScriptingInterface.h" #include "Logging.h" -Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml") -Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl") -Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") - +namespace hifi { namespace qml { namespace offscreen { class OffscreenQmlWhitelist : public Dependency, private ReadWriteLockable { SINGLETON_DEPENDENCY public: - void addWhitelistContextHandler(const std::initializer_list& urls, const QmlContextCallback& callback) { withWriteLock([&] { for (auto url : urls) { @@ -90,241 +86,32 @@ public: } private: - QHash> _callbacks; }; QSharedPointer getQmlWhitelist() { static std::once_flag once; - std::call_once(once, [&] { - DependencyManager::set(); - }); + std::call_once(once, [&] { DependencyManager::set(); }); return DependencyManager::get(); } - -void OffscreenQmlSurface::addWhitelistContextHandler(const std::initializer_list& urls, const QmlContextCallback& callback) { - getQmlWhitelist()->addWhitelistContextHandler(urls, callback); -} - - -QmlContextObjectCallback OffscreenQmlSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QQuickItem*) {}; - -struct TextureSet { - // The number of surfaces with this size - size_t count { 0 }; - std::list returnedTextures; -}; - -uint64_t uvec2ToUint64(const uvec2& v) { - uint64_t result = v.x; - result <<= 32; - result |= v.y; - return result; -} - // Class to handle changing QML audio output device using another thread class AudioHandler : public QObject, QRunnable { Q_OBJECT public: - AudioHandler(QSharedPointer surface, const QString& deviceName, QObject* parent = nullptr) : QObject(parent) { - _newTargetDevice = deviceName; - _surface = surface; - setAutoDelete(true); - if (deviceName.size() > 0) { - QThreadPool::globalInstance()->start(this); - } - } - virtual ~AudioHandler() { - qDebug() << "Audio Handler Destroyed"; - } - void run() override { - if (!_surface.isNull() && _surface->getRootItem() && !_surface->getCleaned()) { - for (auto player : _surface->getRootItem()->findChildren()) { - auto mediaState = player->state(); - QMediaService *svc = player->service(); - if (nullptr == svc) { - return; - } - QAudioOutputSelectorControl *out = qobject_cast - (svc->requestControl(QAudioOutputSelectorControl_iid)); - if (nullptr == out) { - return; - } - QString deviceOuput; - auto outputs = out->availableOutputs(); - for (int i = 0; i < outputs.size(); i++) { - QString output = outputs[i]; - QString description = out->outputDescription(output); - if (description == _newTargetDevice) { - deviceOuput = output; - break; - } - } - out->setActiveOutput(deviceOuput); - svc->releaseControl(out); - // if multimedia was paused, it will start playing automatically after changing audio device - // this will reset it back to a paused state - if (mediaState == QMediaPlayer::State::PausedState) { - player->pause(); - } - else if (mediaState == QMediaPlayer::State::StoppedState) { - player->stop(); - } - } - } - qDebug() << "QML Audio changed to " << _newTargetDevice; - } + AudioHandler(OffscreenQmlSurface* surface, const QString& deviceName, QObject* parent = nullptr); + + virtual ~AudioHandler() { qDebug() << "Audio Handler Destroyed"; } + + void run() override; private: QString _newTargetDevice; QSharedPointer _surface; + std::vector _players; }; -class OffscreenTextures { -public: - GLuint getNextTexture(const uvec2& size) { - assert(QThread::currentThread() == qApp->thread()); - - recycle(); - - ++_activeTextureCount; - auto sizeKey = uvec2ToUint64(size); - assert(_textures.count(sizeKey)); - auto& textureSet = _textures[sizeKey]; - if (!textureSet.returnedTextures.empty()) { - auto textureAndFence = textureSet.returnedTextures.front(); - textureSet.returnedTextures.pop_front(); - waitOnFence(static_cast(textureAndFence.second)); - return textureAndFence.first; - } - - return createTexture(size); - } - - void releaseSize(const uvec2& size) { - assert(QThread::currentThread() == qApp->thread()); - auto sizeKey = uvec2ToUint64(size); - assert(_textures.count(sizeKey)); - auto& textureSet = _textures[sizeKey]; - if (0 == --textureSet.count) { - for (const auto& textureAndFence : textureSet.returnedTextures) { - destroy(textureAndFence); - } - _textures.erase(sizeKey); - } - } - - void acquireSize(const uvec2& size) { - assert(QThread::currentThread() == qApp->thread()); - auto sizeKey = uvec2ToUint64(size); - auto& textureSet = _textures[sizeKey]; - ++textureSet.count; - } - - // May be called on any thread - void releaseTexture(const OffscreenQmlSurface::TextureAndFence & textureAndFence) { - --_activeTextureCount; - Lock lock(_mutex); - _returnedTextures.push_back(textureAndFence); - } - - void report() { - if (randFloat() < 0.01f) { - PROFILE_COUNTER(render_qml_gl, "offscreenTextures", { - { "total", QVariant::fromValue(_allTextureCount.load()) }, - { "active", QVariant::fromValue(_activeTextureCount.load()) }, - }); - PROFILE_COUNTER(render_qml_gl, "offscreenTextureMemory", { - { "value", QVariant::fromValue(_totalTextureUsage) } - }); - } - } - - size_t getUsedTextureMemory() { return _totalTextureUsage; } -private: - static void waitOnFence(GLsync fence) { - glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(fence); - } - - static size_t getMemoryForSize(const uvec2& size) { - // Base size + mips - return static_cast(((size.x * size.y) << 2) * 1.33f); - } - - void destroyTexture(GLuint texture) { - --_allTextureCount; - auto size = _textureSizes[texture]; - assert(getMemoryForSize(size) <= _totalTextureUsage); - _totalTextureUsage -= getMemoryForSize(size); - _textureSizes.erase(texture); - glDeleteTextures(1, &texture); - } - - void destroy(const OffscreenQmlSurface::TextureAndFence& textureAndFence) { - waitOnFence(static_cast(textureAndFence.second)); - destroyTexture(textureAndFence.first); - } - - GLuint createTexture(const uvec2& size) { - // Need a new texture - uint32_t newTexture; - glGenTextures(1, &newTexture); - ++_allTextureCount; - _textureSizes[newTexture] = size; - _totalTextureUsage += getMemoryForSize(size); - glBindTexture(GL_TEXTURE_2D, newTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); -#if !defined(USE_GLES) - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f); -#endif - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - return newTexture; - } - - void recycle() { - assert(QThread::currentThread() == qApp->thread()); - // First handle any global returns - std::list returnedTextures; - { - Lock lock(_mutex); - returnedTextures.swap(_returnedTextures); - } - - for (auto textureAndFence : returnedTextures) { - GLuint texture = textureAndFence.first; - uvec2 size = _textureSizes[texture]; - auto sizeKey = uvec2ToUint64(size); - // Textures can be returned after all surfaces of the given size have been destroyed, - // in which case we just destroy the texture - if (!_textures.count(sizeKey)) { - destroy(textureAndFence); - continue; - } - _textures[sizeKey].returnedTextures.push_back(textureAndFence); - } - } - - using Mutex = std::mutex; - using Lock = std::unique_lock; - std::atomic _allTextureCount; - std::atomic _activeTextureCount; - std::unordered_map _textures; - std::unordered_map _textureSizes; - Mutex _mutex; - std::list _returnedTextures; - size_t _totalTextureUsage { 0 }; -} offscreenTextures; - class UrlHandler : public QObject { Q_OBJECT public: @@ -339,57 +126,22 @@ public: } }; -// Time between receiving a request to render the offscreen UI actually triggering -// the render. Could possibly be increased depending on the framerate we expect to -// achieve. -// This has the effect of capping the framerate at 200 -static const int MIN_TIMER_MS = 5; - -class QMyQuickRenderControl : public QQuickRenderControl { -protected: - QWindow* renderWindow(QPoint* offset) Q_DECL_OVERRIDE{ - if (nullptr == _renderWindow) { - return QQuickRenderControl::renderWindow(offset); - } - if (nullptr != offset) { - offset->rx() = offset->ry() = 0; - } - return _renderWindow; - } - -private: - QWindow* _renderWindow{ nullptr }; - friend class OffscreenQmlRenderThread; - friend class OffscreenQmlSurface; -}; - -size_t OffscreenQmlSurface::getUsedTextureMemory() { - return offscreenTextures.getUsedTextureMemory(); -} - -class QmlNetworkAccessManager : public NetworkAccessManager { -public: - friend class QmlNetworkAccessManagerFactory; -protected: - QmlNetworkAccessManager(QObject* parent) : NetworkAccessManager(parent) { } -}; - class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory { public: - QNetworkAccessManager* create(QObject* parent) override; + class QmlNetworkAccessManager : public NetworkAccessManager { + public: + QmlNetworkAccessManager(QObject* parent) + : NetworkAccessManager(parent){}; + }; + QNetworkAccessManager* create(QObject* parent) override { return new QmlNetworkAccessManager(parent); } }; -QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { - return new QmlNetworkAccessManager(parent); -} - QString getEventBridgeJavascript() { // FIXME: Refactor with similar code in RenderableWebEntityItem QString javaScriptToInject; QFile webChannelFile(":qtwebchannel/qwebchannel.js"); QFile createGlobalEventBridgeFile(PathUtils::resourcesPath() + "/html/createGlobalEventBridge.js"); - if (webChannelFile.open(QFile::ReadOnly | QFile::Text) && - createGlobalEventBridgeFile.open(QFile::ReadOnly | QFile::Text)) { + if (webChannelFile.open(QFile::ReadOnly | QFile::Text) && createGlobalEventBridgeFile.open(QFile::ReadOnly | QFile::Text)) { QString webChannelStr = QTextStream(&webChannelFile).readAll(); QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll(); javaScriptToInject = webChannelStr + createGlobalEventBridgeStr; @@ -404,28 +156,82 @@ class EventBridgeWrapper : public QObject { Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT); public: - EventBridgeWrapper(QObject* eventBridge, QObject* parent = nullptr) : QObject(parent), _eventBridge(eventBridge) { - } + EventBridgeWrapper(QObject* eventBridge, QObject* parent = nullptr) + : QObject(parent) + , _eventBridge(eventBridge) {} - QObject* getEventBridge() { - return _eventBridge; - } + QObject* getEventBridge() { return _eventBridge; } private: QObject* _eventBridge; }; +}}} // namespace hifi::qml::offscreen +using namespace hifi::qml::offscreen; -#define SINGLE_QML_ENGINE 0 +AudioHandler::AudioHandler(OffscreenQmlSurface* surface, const QString& deviceName, QObject* parent) + : QObject(parent) { + setAutoDelete(true); + _newTargetDevice = deviceName; + auto rootItem = surface->getRootItem(); + if (rootItem) { + for (auto player : rootItem->findChildren()) { + _players.push_back(player); + } + } -#if SINGLE_QML_ENGINE -static QQmlEngine* globalEngine{ nullptr }; -static size_t globalEngineRefCount{ 0 }; -#endif + if (!_newTargetDevice.isEmpty() && !_players.empty()) { + QThreadPool::globalInstance()->start(this); + } else { + deleteLater(); + } +} -void initializeQmlEngine(QQmlEngine* engine, QQuickWindow* window) { - new QQmlFileSelector(engine, window); +void AudioHandler::run() { + for (auto player : _players) { + auto mediaState = player->state(); + QMediaService* svc = player->service(); + if (nullptr == svc) { + continue; + } + QAudioOutputSelectorControl* out = + qobject_cast(svc->requestControl(QAudioOutputSelectorControl_iid)); + if (nullptr == out) { + continue; + } + QString deviceOuput; + auto outputs = out->availableOutputs(); + for (int i = 0; i < outputs.size(); i++) { + QString output = outputs[i]; + QString description = out->outputDescription(output); + if (description == _newTargetDevice) { + deviceOuput = output; + break; + } + } + out->setActiveOutput(deviceOuput); + svc->releaseControl(out); + // if multimedia was paused, it will start playing automatically after changing audio device + // this will reset it back to a paused state + if (mediaState == QMediaPlayer::State::PausedState) { + player->pause(); + } else if (mediaState == QMediaPlayer::State::StoppedState) { + player->stop(); + } + } + qDebug() << "QML Audio changed to " << _newTargetDevice; +} + +void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) { + Parent::initializeEngine(engine); + + static std::once_flag once; + std::call_once(once, [] { + qRegisterMetaType(); + qRegisterMetaType(); + qmlRegisterType("Hifi", 1, 0, "SoundEffect"); + }); // register the pixmap image provider (used only for security image, for now) engine->addImageProvider(ImageProvider::PROVIDER_NAME, new ImageProvider()); @@ -438,13 +244,10 @@ void initializeQmlEngine(QQmlEngine* engine, QQuickWindow* window) { qDebug() << path; } - if (!engine->incubationController()) { - engine->setIncubationController(window->incubationController()); - } auto rootContext = engine->rootContext(); rootContext->setContextProperty("GL", ::getGLContextData()); rootContext->setContextProperty("urlHandler", new UrlHandler()); - rootContext->setContextProperty("resourceDirectoryUrl", QUrl(PathUtils::resourcesUrl())); + rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath())); rootContext->setContextProperty("pathToFonts", "../../"); rootContext->setContextProperty("ApplicationInterface", qApp); auto javaScriptToInject = getEventBridgeJavascript(); @@ -456,414 +259,27 @@ void initializeQmlEngine(QQmlEngine* engine, QQuickWindow* window) { rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext)); #endif rootContext->setContextProperty("Paths", DependencyManager::get().data()); - static std::once_flag once; - std::call_once(once, [&] { - qRegisterMetaType(); - qRegisterMetaType(); - }); rootContext->setContextProperty("Tablet", DependencyManager::get().data()); rootContext->setContextProperty("Toolbars", DependencyManager::get().data()); - TabletProxy* tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + TabletProxy* tablet = + DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); engine->setObjectOwnership(tablet, QQmlEngine::CppOwnership); } -QQmlEngine* acquireEngine(QQuickWindow* window) { - Q_ASSERT(QThread::currentThread() == qApp->thread()); - - QQmlEngine* result = nullptr; - if (QThread::currentThread() != qApp->thread()) { - qCWarning(uiLogging) << "Cannot acquire QML engine on any thread but the main thread"; - } - static std::once_flag once; - std::call_once(once, [] { - qmlRegisterType("Hifi", 1, 0, "SoundEffect"); - }); - - -#if SINGLE_QML_ENGINE - if (!globalEngine) { - Q_ASSERT(0 == globalEngineRefCount); - globalEngine = new QQmlEngine(); - initializeQmlEngine(result, window); - ++globalEngineRefCount; - } - result = globalEngine; -#else - result = new QQmlEngine(); - initializeQmlEngine(result, window); -#endif - - return result; +void OffscreenQmlSurface::addWhitelistContextHandler(const std::initializer_list& urls, + const QmlContextCallback& callback) { + getQmlWhitelist()->addWhitelistContextHandler(urls, callback); } -void releaseEngine(QQmlEngine* engine) { - Q_ASSERT(QThread::currentThread() == qApp->thread()); -#if SINGLE_QML_ENGINE - Q_ASSERT(0 != globalEngineRefCount); - if (0 == --globalEngineRefCount) { - globalEngine->deleteLater(); - globalEngine = nullptr; - } -#else - engine->deleteLater(); -#endif -} - -#define OFFSCREEN_QML_SHARED_CONTEXT_PROPERTY "com.highfidelity.qml.gl.sharedContext" -void OffscreenQmlSurface::setSharedContext(QOpenGLContext* sharedContext) { - qApp->setProperty(OFFSCREEN_QML_SHARED_CONTEXT_PROPERTY, QVariant::fromValue(sharedContext)); -} - -QOpenGLContext* OffscreenQmlSurface::getSharedContext() { - return static_cast(qApp->property(OFFSCREEN_QML_SHARED_CONTEXT_PROPERTY).value()); -} - -void OffscreenQmlSurface::cleanup() { - _isCleaned = true; -#if !defined(DISABLE_QML) - _canvas->makeCurrent(); - - _renderControl->invalidate(); - delete _renderControl; // and invalidate - - if (_depthStencil) { - glDeleteRenderbuffers(1, &_depthStencil); - _depthStencil = 0; - } - if (_fbo) { - glDeleteFramebuffers(1, &_fbo); - _fbo = 0; - } - - offscreenTextures.releaseSize(_size); - - _canvas->doneCurrent(); -#endif -} - -void OffscreenQmlSurface::render() { -#if !defined(DISABLE_QML) - - if (nsightActive()) { - return; - } - if (_paused) { - return; - } - - PROFILE_RANGE(render_qml_gl, __FUNCTION__) - _canvas->makeCurrent(); - - _renderControl->sync(); - _quickWindow->setRenderTarget(_fbo, QSize(_size.x, _size.y)); - - GLuint texture = offscreenTextures.getNextTexture(_size); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); - glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - _renderControl->render(); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBindTexture(GL_TEXTURE_2D, texture); - glGenerateMipmap(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); - - - { - // If the most recent texture was unused, we can directly recycle it - auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - // Fence will be used in another thread / context, so a flush is required - glFlush(); - - { - Lock lock(_latestTextureAndFenceMutex); - if (_latestTextureAndFence.first) { - offscreenTextures.releaseTexture(_latestTextureAndFence); - _latestTextureAndFence = { 0, 0 }; - } - _latestTextureAndFence = { texture, fence}; - } - } - - _quickWindow->resetOpenGLState(); - _lastRenderTime = usecTimestampNow(); - _canvas->doneCurrent(); -#endif -} - -bool OffscreenQmlSurface::fetchTexture(TextureAndFence& textureAndFence) { - textureAndFence = { 0, 0 }; - - // Lock free early check - if (0 == _latestTextureAndFence.first) { - return false; - } - - // Ensure writes to the latest texture are complete before before returning it for reading - { - Lock lock(_latestTextureAndFenceMutex); - // Double check inside the lock - if (0 == _latestTextureAndFence.first) { - return false; - } - textureAndFence = _latestTextureAndFence; - _latestTextureAndFence = { 0, 0 }; - } - return true; -} - -std::function OffscreenQmlSurface::getDiscardLambda() { - return [](uint32_t texture, void* fence) { - offscreenTextures.releaseTexture({ texture, static_cast(fence) }); - }; -} - -bool OffscreenQmlSurface::allowNewFrame(uint8_t fps) { - // If we already have a pending texture, don't render another one - // i.e. don't render faster than the consumer context, since it wastes - // GPU cycles on producing output that will never be seen - if (0 != _latestTextureAndFence.first) { - return false; - } - - auto minRenderInterval = USECS_PER_SECOND / fps; - auto lastInterval = usecTimestampNow() - _lastRenderTime; - return (lastInterval > minRenderInterval); -} - -OffscreenQmlSurface::OffscreenQmlSurface() { -} - -OffscreenQmlSurface::~OffscreenQmlSurface() { - QObject::disconnect(&_updateTimer); - disconnectAudioOutputTimer(); - QObject::disconnect(qApp); - - cleanup(); - auto engine = _qmlContext->engine(); -#if !defined(DISABLE_QML) - _canvas->deleteLater(); -#endif - _rootItem->deleteLater(); - _quickWindow->deleteLater(); - releaseEngine(engine); -} - -void OffscreenQmlSurface::onAboutToQuit() { - _paused = true; - QObject::disconnect(&_updateTimer); - disconnectAudioOutputTimer(); - -} - -void OffscreenQmlSurface::disconnectAudioOutputTimer() { -#if !defined(Q_OS_ANDROID) - if (_audioOutputUpdateTimer.isActive()) { - _audioOutputUpdateTimer.stop(); - } - QObject::disconnect(&_audioOutputUpdateTimer); -#endif -} - -void OffscreenQmlSurface::create() { - qCDebug(uiLogging) << "Building QML surface"; - -#if !defined(DISABLE_QML) - _renderControl = new QMyQuickRenderControl(); - connect(_renderControl, &QQuickRenderControl::renderRequested, this, [this] { _render = true; }); - connect(_renderControl, &QQuickRenderControl::sceneChanged, this, [this] { _render = _polish = true; }); - - QQuickWindow::setDefaultAlphaBuffer(true); - - // Create a QQuickWindow that is associated with our render control. - // This window never gets created or shown, meaning that it will never get an underlying native (platform) window. - // NOTE: Must be created on the main thread so that OffscreenQmlSurface can send it events - // NOTE: Must be created on the rendering thread or it will refuse to render, - // so we wait until after its ctor to move object/context to this thread. - _quickWindow = new QQuickWindow(_renderControl); - _quickWindow->setColor(QColor(255, 255, 255, 0)); - _quickWindow->setClearBeforeRendering(false); - - _renderControl->_renderWindow = _proxyWindow; - _canvas = new OffscreenGLCanvas(); - if (!_canvas->create(getSharedContext())) { - qFatal("Failed to create OffscreenGLCanvas"); - return; - }; - - // acquireEngine interrogates the GL context, so we need to have the context current here - if (!_canvas->makeCurrent()) { - qFatal("Failed to make context current for QML Renderer"); - return; - } -#else - _quickWindow = new QQuickWindow(); -#endif - - - connect(_quickWindow, &QQuickWindow::focusObjectChanged, this, &OffscreenQmlSurface::onFocusObjectChanged); - - - // Create a QML engine. - auto qmlEngine = acquireEngine(_quickWindow); - - _qmlContext = new QQmlContext(qmlEngine->rootContext()); - _qmlContext->setBaseUrl(QUrl{ PathUtils::qmlBaseUrl() }); - _qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); - _qmlContext->setContextProperty("eventBridge", this); - _qmlContext->setContextProperty("webEntity", this); - _qmlContext->setContextProperty("QmlSurface", this); - +void OffscreenQmlSurface::onRootContextCreated(QQmlContext* qmlContext) { + OffscreenSurface::onRootContextCreated(qmlContext); + qmlContext->setBaseUrl(PathUtils::qmlBaseUrl()); + qmlContext->setContextProperty("eventBridge", this); + qmlContext->setContextProperty("webEntity", this); + qmlContext->setContextProperty("QmlSurface", this); // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper // Find a way to flag older scripts using this mechanism and wanr that this is deprecated - _qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, _qmlContext)); - -#if !defined(DISABLE_QML) - _renderControl->initialize(_canvas->getContext()); -#endif - -#if !defined(Q_OS_ANDROID) - // Connect with the audio client and listen for audio device changes - auto audioIO = DependencyManager::get(); - connect(audioIO.data(), &AudioClient::deviceChanged, this, [&](QAudio::Mode mode, const QAudioDeviceInfo& device) { - if (mode == QAudio::Mode::AudioOutput) { - QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, Q_ARG(QString, device.deviceName())); - } - }); - - // Setup the update of the QML media components with the current audio output device - QObject::connect(&_audioOutputUpdateTimer, &QTimer::timeout, this, [this]() { - if (_currentAudioOutputDevice.size() > 0) { - new AudioHandler(sharedFromThis(), _currentAudioOutputDevice); - } - }); - int waitForAudioQmlMs = 200; - _audioOutputUpdateTimer.setInterval(waitForAudioQmlMs); - _audioOutputUpdateTimer.setSingleShot(true); -#endif - - // When Quick says there is a need to render, we will not render immediately. Instead, - // a timer with a small interval is used to get better performance. - QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); - QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit); - _updateTimer.setTimerType(Qt::PreciseTimer); - _updateTimer.setInterval(MIN_TIMER_MS); // 5ms, Qt::PreciseTimer required - _updateTimer.start(); -} - -void OffscreenQmlSurface::changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate) { -#if !defined(Q_OS_ANDROID) - _currentAudioOutputDevice = deviceName; - if (_rootItem != nullptr && !isHtmlUpdate) { - QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); - } - emit audioOutputDeviceChanged(deviceName); -#endif -} - -void OffscreenQmlSurface::forceHtmlAudioOutputDeviceUpdate() { -#if !defined(Q_OS_ANDROID) - if (_currentAudioOutputDevice.size() > 0) { - QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, - Q_ARG(QString, _currentAudioOutputDevice), Q_ARG(bool, true)); - } -#endif -} - -void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { -#if !defined(Q_OS_ANDROID) - if (QThread::currentThread() != qApp->thread()) { - QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); - } else { - if (_audioOutputUpdateTimer.isActive()) { - _audioOutputUpdateTimer.stop(); - } - _audioOutputUpdateTimer.start(); - } -#endif -} - -static uvec2 clampSize(const uvec2& size, uint32_t maxDimension) { - return glm::clamp(size, glm::uvec2(1), glm::uvec2(maxDimension)); -} - -static QSize clampSize(const QSize& qsize, uint32_t maxDimension) { - return fromGlm(clampSize(toGlm(qsize), maxDimension)); -} - -void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { - - if (!_quickWindow) { - return; - } - - const uint32_t MAX_OFFSCREEN_DIMENSION = 4096; - const QSize newSize = clampSize(newSize_, MAX_OFFSCREEN_DIMENSION); - if (!forceResize && newSize == _quickWindow->geometry().size()) { - return; - } - - _qmlContext->setContextProperty("surfaceSize", newSize); - - if (_rootItem) { - _rootItem->setSize(newSize); - } - - // Update our members - _quickWindow->setGeometry(QRect(QPoint(), newSize)); - _quickWindow->contentItem()->setSize(newSize); - - // Qt bug in 5.4 forces this check of pixel ratio, - // even though we're rendering offscreen. - uvec2 newOffscreenSize = toGlm(newSize); - if (newOffscreenSize == _size) { - return; - } - -#if !defined(DISABLE_QML) - qCDebug(uiLogging) << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height(); - gl::withSavedContext([&] { - _canvas->makeCurrent(); - - // Release hold on the textures of the old size - if (uvec2() != _size) { - { - Lock lock(_latestTextureAndFenceMutex); - // If the most recent texture was unused, we can directly recycle it - if (_latestTextureAndFence.first) { - offscreenTextures.releaseTexture(_latestTextureAndFence); - _latestTextureAndFence = { 0, 0 }; - } - } - offscreenTextures.releaseSize(_size); - } - - _size = newOffscreenSize; - - // Acquire the new texture size - if (uvec2() != _size) { - offscreenTextures.acquireSize(_size); - if (_depthStencil) { - glDeleteRenderbuffers(1, &_depthStencil); - _depthStencil = 0; - } - glGenRenderbuffers(1, &_depthStencil); - glBindRenderbuffer(GL_RENDERBUFFER, _depthStencil); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _size.x, _size.y); - if (!_fbo) { - glGenFramebuffers(1, &_fbo); - } - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); - glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - } - - _canvas->doneCurrent(); - }); -#endif -} - -QQuickItem* OffscreenQmlSurface::getRootItem() { - return _rootItem; + qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, qmlContext)); } QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) { @@ -872,13 +288,7 @@ QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, QQuickIte // If we have whitelisted content, we must load a new context forceNewContext |= !callbacks.empty(); - QQmlContext* targetContext = parent ? QQmlEngine::contextForObject(parent) : _qmlContext; - if (!targetContext) { - targetContext = _qmlContext; - } - if (_rootItem && forceNewContext) { - targetContext = new QQmlContext(targetContext); - } + QQmlContext* targetContext = Parent::contextForUrl(qmlSource, parent, forceNewContext); for (const auto& callback : callbacks) { callback(targetContext); @@ -887,171 +297,48 @@ QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, QQuickIte return targetContext; } -void OffscreenQmlSurface::load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback) { - loadInternal(qmlSource, false, parent, [callback](QQmlContext* context, QQuickItem* newItem) { - QJSValue(callback).call(QJSValueList() << context->engine()->newQObject(newItem)); - }); -} - -void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& onQmlLoadedCallback) { - loadInternal(qmlSource, createNewContext, nullptr, onQmlLoadedCallback); -} - -void OffscreenQmlSurface::loadInternal(const QUrl& qmlSource, bool createNewContext, QQuickItem* parent, const QmlContextObjectCallback& onQmlLoadedCallback) { - if (QThread::currentThread() != thread()) { - qCWarning(uiLogging) << "Called load on a non-surface thread"; - } - // Synchronous loading may take a while; restart the deadlock timer - QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection); - - QUrl finalQmlSource = qmlSource; - if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) { - finalQmlSource = _qmlContext->resolvedUrl(qmlSource); - } - - auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext); - auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous); - if (qmlComponent->isLoading()) { - connect(qmlComponent, &QQmlComponent::statusChanged, this, [=](QQmlComponent::Status) { - finishQmlLoad(qmlComponent, targetContext, parent, onQmlLoadedCallback); - }); - return; - } - - finishQmlLoad(qmlComponent, targetContext, parent, onQmlLoadedCallback); -} - -void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& onQmlLoadedCallback) { - load(qmlSource, true, onQmlLoadedCallback); -} - -void OffscreenQmlSurface::load(const QUrl& qmlSource, const QmlContextObjectCallback& onQmlLoadedCallback) { - load(qmlSource, false, onQmlLoadedCallback); -} - -void OffscreenQmlSurface::load(const QString& qmlSourceFile, const QmlContextObjectCallback& onQmlLoadedCallback) { - return load(QUrl(qmlSourceFile), onQmlLoadedCallback); -} - -void OffscreenQmlSurface::clearCache() { - _qmlContext->engine()->clearComponentCache(); -} - - -void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, QQuickItem* parent, const QmlContextObjectCallback& callback) { - disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0); - if (qmlComponent->isError()) { - for (const auto& error : qmlComponent->errors()) { - qCWarning(uiLogging) << error.url() << error.line() << error; - } - qmlComponent->deleteLater(); - return; - } - - QObject* newObject = qmlComponent->beginCreate(qmlContext); - if (qmlComponent->isError()) { - for (const auto& error : qmlComponent->errors()) { - qCWarning(uiLogging) << error.url() << error.line() << error; - } - if (!_rootItem) { - qFatal("Unable to finish loading QML root"); - } - qmlComponent->deleteLater(); - return; - } - - if (!newObject) { - if (!_rootItem) { - qFatal("Could not load object as root item"); - return; - } - qCWarning(uiLogging) << "Unable to load QML item"; - return; - } - +void OffscreenQmlSurface::onItemCreated(QQmlContext* qmlContext, QQuickItem* newItem) { QObject* eventBridge = qmlContext->contextProperty("eventBridge").value(); - if (qmlContext != _qmlContext && eventBridge && eventBridge != this) { + if (qmlContext != getSurfaceContext() && eventBridge && eventBridge != this) { // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper // Find a way to flag older scripts using this mechanism and wanr that this is deprecated qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(eventBridge, qmlContext)); } - qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); - - // All quick items should be focusable - QQuickItem* newItem = qobject_cast(newObject); - if (newItem) { - // Make sure we make items focusable (critical for - // supporting keyboard shortcuts) - newItem->setFlag(QQuickItem::ItemIsFocusScope, true); - } - - - // Make sure we will call callback for this codepath - // Call this before qmlComponent->completeCreate() otherwise ghost window appears - // If we already have a root, just set a couple of flags and the ancestry - if (_rootItem) { - callback(qmlContext, newItem); - - if (!parent) { - parent = _rootItem; - } - // Allow child windows to be destroyed from JS - QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership); - newObject->setParent(parent); - newItem->setParentItem(parent); - } - - qmlComponent->completeCreate(); - qmlComponent->deleteLater(); - - if (_rootItem) { - QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); - return; - } - connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant))); +} - // The root item is ready. Associate it with the window. - _rootItem = newItem; - _rootItem->setParentItem(_quickWindow->contentItem()); - _rootItem->setSize(_quickWindow->renderTargetSize()); +void OffscreenQmlSurface::onRootCreated() { + getSurfaceContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); - if (_rootItem->objectName() == "tabletRoot") { - _qmlContext->setContextProperty("tabletRoot", QVariant::fromValue(_rootItem)); + // Connect with the audio client and listen for audio device changes + auto audioIO = DependencyManager::get(); + connect(audioIO.data(), &AudioClient::deviceChanged, this, [&](QAudio::Mode mode, const QAudioDeviceInfo& device) { + if (mode == QAudio::Mode::AudioOutput) { + QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, Q_ARG(QString, device.deviceName())); + } + }); + +#if !defined(Q_OS_ANDROID) + // Setup the update of the QML media components with the current audio output device + QObject::connect(&_audioOutputUpdateTimer, &QTimer::timeout, this, [this]() { + if (_currentAudioOutputDevice.size() > 0) { + new AudioHandler(this, _currentAudioOutputDevice); + } + }); + int waitForAudioQmlMs = 200; + _audioOutputUpdateTimer.setInterval(waitForAudioQmlMs); + _audioOutputUpdateTimer.setSingleShot(true); +#endif + + if (getRootItem()->objectName() == "tabletRoot") { + getSurfaceContext()->setContextProperty("tabletRoot", QVariant::fromValue(getRootItem())); auto tabletScriptingInterface = DependencyManager::get(); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", this); QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"); - _qmlContext->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership); + getSurfaceContext()->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership); } QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); - // Call this callback after rootitem is set, otherwise VrMenu wont work - callback(qmlContext, newItem); -} - -void OffscreenQmlSurface::updateQuick() { - offscreenTextures.report(); - // If we're - // a) not set up - // b) already rendering a frame - // c) rendering too fast - // then skip this - if (!allowNewFrame(_maxFps)) { - return; - } - - if (_polish) { - PROFILE_RANGE(render_qml, "OffscreenQML polish") -#if !defined(DISABLE_QML) - _renderControl->polishItems(); -#endif - _polish = false; - } - - if (_render) { - render(); - _render = false; - } } QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject) { @@ -1063,106 +350,37 @@ QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObjec } vec2 offscreenPosition = toGlm(sourcePosition); offscreenPosition /= sourceSize; - offscreenPosition *= vec2(toGlm(_quickWindow->size())); + offscreenPosition *= vec2(toGlm(getWindow()->size())); return QPointF(offscreenPosition.x, offscreenPosition.y); } -QPointF OffscreenQmlSurface::mapToVirtualScreen(const QPointF& originalPoint) { - return _mouseTranslator(originalPoint); -} - /////////////////////////////////////////////////////// // // Event handling customization // -bool OffscreenQmlSurface::filterEnabled(QObject* originalDestination, QEvent* event) const { - if (_quickWindow == originalDestination) { - return false; - } - // Only intercept events while we're in an active state - if (_paused) { - return false; - } - return true; -} - bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* event) { if (!filterEnabled(originalDestination, event)) { return false; } -#ifdef DEBUG - // Don't intercept our own events, or we enter an infinite recursion - QObject* recurseTest = originalDestination; - while (recurseTest) { - Q_ASSERT(recurseTest != _rootItem && recurseTest != _quickWindow); - recurseTest = recurseTest->parent(); - } -#endif - switch (event->type()) { - case QEvent::Resize: { - QResizeEvent* resizeEvent = static_cast(event); - QWidget* widget = static_cast(originalDestination); - if (widget) { - this->resize(resizeEvent->size()); - } - break; + if (event->type() == QEvent::Resize) { + QResizeEvent* resizeEvent = static_cast(event); + QWidget* widget = static_cast(originalDestination); + if (widget) { + this->resize(resizeEvent->size()); } - - case QEvent::KeyPress: - case QEvent::KeyRelease: { - event->ignore(); - if (QCoreApplication::sendEvent(_quickWindow, event)) { - return event->isAccepted(); - } - break; - } - - case QEvent::Wheel: { - QWheelEvent* wheelEvent = static_cast(event); - QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos()); - QWheelEvent mappedEvent( - transformedPos, - wheelEvent->delta(), wheelEvent->buttons(), - wheelEvent->modifiers(), wheelEvent->orientation()); - mappedEvent.ignore(); - if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { - return mappedEvent.isAccepted(); - } - break; - } -#if defined(Q_OS_ANDROID) - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: { - QTouchEvent *originalEvent = static_cast(event); - QTouchEvent *fakeEvent = new QTouchEvent(*originalEvent); - auto newTouchPoints = fakeEvent->touchPoints(); - for (size_t i = 0; i < newTouchPoints.size(); ++i) { - const auto &originalPoint = originalEvent->touchPoints()[i]; - auto &newPoint = newTouchPoints[i]; - newPoint.setPos(originalPoint.pos()); - } - fakeEvent->setTouchPoints(newTouchPoints); - if (QCoreApplication::sendEvent(_quickWindow, fakeEvent)) { - qInfo() << __FUNCTION__ << "sent fake touch event:" << fakeEvent->type() - << "_quickWindow handled it... accepted:" << fakeEvent->isAccepted(); - return false; //event->isAccepted(); - } - break; - } -#endif - default: - break; } - return false; + return Parent::eventFilter(originalDestination, event); } unsigned int OffscreenQmlSurface::deviceIdByTouchPoint(qreal x, qreal y) { - auto mapped = _rootItem->mapFromGlobal(QPoint(x, y)); + if (!getRootItem()) { + return PointerEvent::INVALID_POINTER_ID; + } + auto mapped = getRootItem()->mapFromGlobal(QPoint(x, y)); for (auto pair : _activeTouchPoints) { if (mapped.x() == (int)pair.second.touchPoint.pos().x() && mapped.y() == (int)pair.second.touchPoint.pos().y()) { return pair.first; @@ -1207,7 +425,7 @@ void OffscreenQmlSurface::hoverEndEvent(const PointerEvent& event, class QTouchD bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QTouchDevice& device, bool release) { // Ignore mouse interaction if we're paused - if (_paused || !_quickWindow) { + if (!getRootItem() || isPaused()) { return false; } @@ -1226,7 +444,8 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT // - this was a hover end event and the mouse wasn't pressed // - this was a release event and we aren't still hovering auto touchPoint = _activeTouchPoints.find(event.getID()); - bool removeTouchPoint = release || (touchPoint != _activeTouchPoints.end() && !touchPoint->second.hovering && state == Qt::TouchPointReleased); + bool removeTouchPoint = + release || (touchPoint != _activeTouchPoints.end() && !touchPoint->second.hovering && state == Qt::TouchPointReleased); QEvent::Type touchType = QEvent::TouchUpdate; if (_activeTouchPoints.empty()) { // If the first active touch point is being created, send a begin @@ -1259,9 +478,9 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT touchPoints.push_back(entry.second.touchPoint); } - touchEvent.setWindow(_quickWindow); touchEvent.setDevice(&device); - touchEvent.setTarget(_rootItem); + touchEvent.setWindow(getWindow()); + touchEvent.setTarget(getRootItem()); touchEvent.setTouchPoints(touchPoints); touchEvent.setTouchPointStates(touchPointStates); touchEvent.setTimestamp((ulong)QDateTime::currentMSecsSinceEpoch()); @@ -1285,25 +504,26 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT bool eventsAccepted = true; if (event.getType() == PointerEvent::Move) { - QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers()); + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, + event.getKeyboardModifiers()); // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install // need to investigate into why this crash is happening. //_qmlContext->setContextProperty("lastMousePosition", windowPoint); mouseEvent.ignore(); - if (QCoreApplication::sendEvent(_quickWindow, &mouseEvent)) { + if (QCoreApplication::sendEvent(getWindow(), &mouseEvent)) { eventSent = true; eventsAccepted &= mouseEvent.isAccepted(); } } if (touchType == QEvent::TouchBegin) { - _touchBeginAccepted = QCoreApplication::sendEvent(_quickWindow, &touchEvent); + _touchBeginAccepted = QCoreApplication::sendEvent(getWindow(), &touchEvent); if (_touchBeginAccepted) { eventSent = true; eventsAccepted &= touchEvent.isAccepted(); } } else if (_touchBeginAccepted) { - if (QCoreApplication::sendEvent(_quickWindow, &touchEvent)) { + if (QCoreApplication::sendEvent(getWindow(), &touchEvent)) { eventSent = true; eventsAccepted &= touchEvent.isAccepted(); } @@ -1316,49 +536,10 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT return eventSent && eventsAccepted; } -void OffscreenQmlSurface::pause() { - _paused = true; -} - -void OffscreenQmlSurface::resume() { - _paused = false; - _render = true; - - if (getRootItem()) { - getRootItem()->setProperty("eventBridge", QVariant::fromValue(this)); +void OffscreenQmlSurface::focusDestroyed(QObject* obj) { + if (_currentFocusItem) { + disconnect(_currentFocusItem, &QObject::destroyed, this, &OffscreenQmlSurface::focusDestroyed); } -} - -bool OffscreenQmlSurface::isPaused() const { - return _paused; -} - -void OffscreenQmlSurface::setProxyWindow(QWindow* window) { - _proxyWindow = window; -#if !defined(DISABLE_QML) - if (_renderControl) { - _renderControl->_renderWindow = window; - } -#endif -} - -QObject* OffscreenQmlSurface::getEventHandler() { - return getWindow(); -} - -QQuickWindow* OffscreenQmlSurface::getWindow() { - return _quickWindow; -} - -QSize OffscreenQmlSurface::size() const { - return _quickWindow->geometry().size(); -} - -QQmlContext* OffscreenQmlSurface::getSurfaceContext() { - return _qmlContext; -} - -void OffscreenQmlSurface::focusDestroyed(QObject *obj) { _currentFocusItem = nullptr; } @@ -1454,8 +635,7 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* targetOverrid } void OffscreenQmlSurface::lowerKeyboard() { - - QSignalBlocker blocker(_quickWindow); + QSignalBlocker blocker(getWindow()); if (_currentFocusItem) { _currentFocusItem->setFocus(false); @@ -1464,7 +644,8 @@ void OffscreenQmlSurface::lowerKeyboard() { } void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool numeric, bool passwordField) { - qCDebug(uiLogging) << "setKeyboardRaised: " << object << ", raised: " << raised << ", numeric: " << numeric << ", password: " << passwordField; + qCDebug(uiLogging) << "setKeyboardRaised: " << object << ", raised: " << raised << ", numeric: " << numeric + << ", password: " << passwordField; if (!object) { return; @@ -1488,7 +669,6 @@ void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool n numeric = numeric || QString(item->metaObject()->className()).left(7) == "SpinBox"; if (item->property("keyboardRaised").isValid()) { - // FIXME - HMD only: Possibly set value of "keyboardEnabled" per isHMDMode() for use in WebView.qml. if (item->property("punctuationMode").isValid()) { item->setProperty("punctuationMode", QVariant(numeric)); @@ -1544,11 +724,42 @@ void OffscreenQmlSurface::emitWebEvent(const QVariant& message) { void OffscreenQmlSurface::sendToQml(const QVariant& message) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "emitQmlEvent", Qt::QueuedConnection, Q_ARG(QVariant, message)); - } else if (_rootItem) { + } else if (getRootItem()) { // call fromScript method on qml root - QMetaObject::invokeMethod(_rootItem, "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); + QMetaObject::invokeMethod(getRootItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); } } +void OffscreenQmlSurface::changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate) { +#if !defined(Q_OS_ANDROID) + _currentAudioOutputDevice = deviceName; + if (getRootItem() && !isHtmlUpdate) { + QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); + } + emit audioOutputDeviceChanged(deviceName); +#endif +} + +void OffscreenQmlSurface::forceHtmlAudioOutputDeviceUpdate() { +#if !defined(Q_OS_ANDROID) + if (_currentAudioOutputDevice.size() > 0) { + QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, + Q_ARG(QString, _currentAudioOutputDevice), Q_ARG(bool, true)); + } +#endif +} + +void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { +#if !defined(Q_OS_ANDROID) + if (QThread::currentThread() != qApp->thread()) { + QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); + } else { + if (_audioOutputUpdateTimer.isActive()) { + _audioOutputUpdateTimer.stop(); + } + _audioOutputUpdateTimer.start(); + } +#endif +} #include "OffscreenQmlSurface.moc" diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 08c7ca6bf8..9fa86f12a3 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -9,200 +9,79 @@ #ifndef hifi_OffscreenQmlSurface_h #define hifi_OffscreenQmlSurface_h -#include -#include -#include -#include - -#include -#include -#include - -#include -#include +#include +#include #include #include "PointerEvent.h" -class QWindow; -class QMyQuickRenderControl; -class OffscreenGLCanvas; -class QOpenGLContext; -class QQmlEngine; -class QQmlContext; -class QQmlComponent; -class QQuickWindow; -class QQuickItem; -class QJSValue; - -// GPU resources are typically buffered for one copy being used by the renderer, -// one copy in flight, and one copy being used by the receiver -#define GPU_RESOURCE_BUFFER_SIZE 3 - using QmlContextCallback = std::function; -using QmlContextObjectCallback = std::function; -class OffscreenQmlSurface : public QObject, public QEnableSharedFromThis { +class OffscreenQmlSurface : public hifi::qml::OffscreenSurface { + using Parent = hifi::qml::OffscreenSurface; Q_OBJECT Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged) public: - static void setSharedContext(QOpenGLContext* context); - - static QmlContextObjectCallback DEFAULT_CONTEXT_CALLBACK; + static void addWhitelistContextHandler(const std::initializer_list& urls, const QmlContextCallback& callback); static void addWhitelistContextHandler(const QUrl& url, const QmlContextCallback& callback) { addWhitelistContextHandler({ { url } }, callback); }; - OffscreenQmlSurface(); - virtual ~OffscreenQmlSurface(); - - using MouseTranslator = std::function; - - virtual void create(); - void resize(const QSize& size, bool forceResize = false); - QSize size() const; - - // Usable from QML code as QmlSurface.load(url, parent, function(newItem){ ... }) - Q_INVOKABLE void load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback); - - // For C++ use - Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK); - Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK); - Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK); - Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextObjectCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK); - - void clearCache(); - void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; } - // Optional values for event handling - void setProxyWindow(QWindow* window); - void setMouseTranslator(MouseTranslator mouseTranslator) { - _mouseTranslator = mouseTranslator; - } - bool isFocusText() const { return _focusText; } - void pause(); - void resume(); - bool isPaused() const; bool getCleaned() { return _isCleaned; } - QQuickItem* getRootItem(); - QQuickWindow* getWindow(); - QObject* getEventHandler(); - QQmlContext* getSurfaceContext(); - - QPointF mapToVirtualScreen(const QPointF& originalPoint); bool eventFilter(QObject* originalDestination, QEvent* event) override; - void setKeyboardRaised(QObject* object, bool raised, bool numeric = false, bool passwordField = false); Q_INVOKABLE void synthesizeKeyPress(QString key, QObject* targetOverride = nullptr); Q_INVOKABLE void lowerKeyboard(); - - using TextureAndFence = std::pair; - // Checks to see if a new texture is available. If one is, the function returns true and - // textureAndFence will be populated with the texture ID and a fence which will be signalled - // when the texture is safe to read. - // Returns false if no new texture is available - bool fetchTexture(TextureAndFence& textureAndFence); - - static std::function getDiscardLambda(); - static size_t getUsedTextureMemory(); - PointerEvent::EventType choosePointerEventType(QEvent::Type type); - unsigned int deviceIdByTouchPoint(qreal x, qreal y); signals: void focusObjectChanged(QObject* newFocus); void focusTextChanged(bool focusText); - -public slots: - void onAboutToQuit(); - void focusDestroyed(QObject *obj); - - // audio output device -public slots: - void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false); - void forceHtmlAudioOutputDeviceUpdate(); - void forceQmlAudioOutputDeviceUpdate(); - -signals: void audioOutputDeviceChanged(const QString& deviceName); - // event bridge -public slots: - void emitScriptEvent(const QVariant& scriptMessage); - void emitWebEvent(const QVariant& webMessage); -signals: - void scriptEventReceived(const QVariant& message); + // web event bridge void webEventReceived(const QVariant& message); - + // script event bridge + void scriptEventReceived(const QVariant& message); // qml event bridge + void fromQml(const QVariant& message); + public slots: + void focusDestroyed(QObject *obj); + // script event bridge + void emitScriptEvent(const QVariant& scriptMessage); + // web event bridge + void emitWebEvent(const QVariant& webMessage); + // qml event bridge void sendToQml(const QVariant& message); -signals: - void fromQml(QVariant message); protected: - bool filterEnabled(QObject* originalDestination, QEvent* event) const; void setFocusText(bool newFocusText); + void initializeEngine(QQmlEngine* engine) override; + void onRootContextCreated(QQmlContext* qmlContext) override; private: - static QOpenGLContext* getSharedContext(); - - QQmlContext* contextForUrl(const QUrl& url, QQuickItem* parent, bool forceNewContext = false); - void loadInternal(const QUrl& qmlSource, bool createNewContext, QQuickItem* parent, const QmlContextObjectCallback& onQmlLoadedCallback); - void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, QQuickItem* parent, const QmlContextObjectCallback& onQmlLoadedCallback); + void onRootCreated() override; + void onItemCreated(QQmlContext* qmlContext, QQuickItem* newItem) override; + QQmlContext* contextForUrl(const QUrl& url, QQuickItem* parent, bool forceNewContext = false) override; QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject); - bool allowNewFrame(uint8_t fps); - void render(); - void cleanup(); - void disconnectAudioOutputTimer(); private slots: - void updateQuick(); - void onFocusObjectChanged(QObject* newFocus); + void onFocusObjectChanged(QObject* newFocus) override; public slots: void hoverBeginEvent(const PointerEvent& event, class QTouchDevice& device); void hoverEndEvent(const PointerEvent& event, class QTouchDevice& device); bool handlePointerEvent(const PointerEvent& event, class QTouchDevice& device, bool release = false); + void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false); + void forceHtmlAudioOutputDeviceUpdate(); + void forceQmlAudioOutputDeviceUpdate(); private: - using Mutex = std::mutex; - using Lock = std::unique_lock; - - QQuickWindow* _quickWindow { nullptr }; - QQmlContext* _qmlContext { nullptr }; - QQuickItem* _rootItem { nullptr }; - -#if !defined(DISABLE_QML) - QMyQuickRenderControl* _renderControl{ nullptr }; - OffscreenGLCanvas* _canvas { nullptr }; -#endif - - QTimer _updateTimer; - uint32_t _fbo { 0 }; - uint32_t _depthStencil { 0 }; - uint64_t _lastRenderTime { 0 }; - uvec2 _size; - -#if !defined(Q_OS_ANDROID) - QTimer _audioOutputUpdateTimer; - QString _currentAudioOutputDevice; -#endif - - // Texture management - Mutex _latestTextureAndFenceMutex; - TextureAndFence _latestTextureAndFence { 0, 0 }; - - bool _render { false }; - bool _polish { true }; - bool _paused { true }; bool _focusText { false }; bool _isCleaned{ false }; - uint8_t _maxFps { 60 }; - MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } }; - QWindow* _proxyWindow { nullptr }; - QQuickItem* _currentFocusItem { nullptr }; struct TouchState { @@ -214,6 +93,9 @@ private: bool _pressed; bool _touchBeginAccepted { false }; std::map _activeTouchPoints; + + QString _currentAudioOutputDevice; + QTimer _audioOutputUpdateTimer; }; #endif diff --git a/libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp b/libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp index 9b6b031dd9..7efa36624b 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurfaceCache.cpp @@ -44,7 +44,6 @@ void OffscreenQmlSurfaceCache::release(const QString& rootSource, const QSharedP QSharedPointer OffscreenQmlSurfaceCache::buildSurface(const QString& rootSource) { auto surface = QSharedPointer(new OffscreenQmlSurface()); - surface->create(); surface->load(rootSource); surface->resize(QSize(100, 100)); return surface; diff --git a/plugins/hifiCodec/src/HiFiCodec.cpp b/plugins/hifiCodec/src/HiFiCodec.cpp index f78bbae2c1..99bb411539 100644 --- a/plugins/hifiCodec/src/HiFiCodec.cpp +++ b/plugins/hifiCodec/src/HiFiCodec.cpp @@ -9,12 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "HiFiCodec.h" #include #include -#include "HiFiCodec.h" const char* HiFiCodec::NAME { "hifiAC" }; diff --git a/plugins/hifiKinect/CMakeLists.txt b/plugins/hifiKinect/CMakeLists.txt index a7088eb3b4..97f78b0c11 100644 --- a/plugins/hifiKinect/CMakeLists.txt +++ b/plugins/hifiKinect/CMakeLists.txt @@ -10,7 +10,7 @@ if (WIN32) find_package(KINECT) if (KINECT_FOUND) set(TARGET_NAME hifiKinect) - setup_hifi_plugin(Script Qml Widgets) + setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins input-plugins display-plugins) # need to setup appropriate externals... diff --git a/plugins/hifiLeapMotion/CMakeLists.txt b/plugins/hifiLeapMotion/CMakeLists.txt index 14f9bbaa17..3075107bb3 100644 --- a/plugins/hifiLeapMotion/CMakeLists.txt +++ b/plugins/hifiLeapMotion/CMakeLists.txt @@ -9,7 +9,7 @@ find_package(LEAPMOTION) if (LEAPMOTION_FOUND) set(TARGET_NAME hifiLeapMotion) - setup_hifi_plugin(Script Qml Widgets) + setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins input-plugins) target_leapmotion() endif() diff --git a/plugins/hifiNeuron/CMakeLists.txt b/plugins/hifiNeuron/CMakeLists.txt index a9ed8cca6e..7d2616e4d6 100644 --- a/plugins/hifiNeuron/CMakeLists.txt +++ b/plugins/hifiNeuron/CMakeLists.txt @@ -9,8 +9,8 @@ if (APPLE OR WIN32) set(TARGET_NAME hifiNeuron) - setup_hifi_plugin(Script Qml Widgets) - link_hifi_libraries(shared controllers ui plugins input-plugins) + setup_hifi_plugin(Qml) + link_hifi_libraries(shared controllers qml ui plugins input-plugins) target_neuron() endif() diff --git a/plugins/hifiSdl2/CMakeLists.txt b/plugins/hifiSdl2/CMakeLists.txt index 7e499e314a..8b2bb114a0 100644 --- a/plugins/hifiSdl2/CMakeLists.txt +++ b/plugins/hifiSdl2/CMakeLists.txt @@ -7,6 +7,6 @@ # set(TARGET_NAME hifiSdl2) -setup_hifi_plugin(Script Qml Widgets) +setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins input-plugins script-engine) target_sdl2() diff --git a/plugins/hifiSdl2/src/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp index a5376af24e..664e53d115 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.cpp +++ b/plugins/hifiSdl2/src/SDL2Manager.cpp @@ -9,14 +9,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "SDL2Manager.h" + +#include #include #include #include #include -#include "SDL2Manager.h" static_assert( (int)controller::A == (int)SDL_CONTROLLER_BUTTON_A && diff --git a/plugins/hifiSixense/CMakeLists.txt b/plugins/hifiSixense/CMakeLists.txt index 6e642fce29..e84a4587c4 100644 --- a/plugins/hifiSixense/CMakeLists.txt +++ b/plugins/hifiSixense/CMakeLists.txt @@ -12,7 +12,7 @@ # the msvcr100 runtime support, the plugin will not load. if (NOT ANDROID) set(TARGET_NAME hifiSixense) - setup_hifi_plugin(Script Qml Widgets) + setup_hifi_plugin(Qml) link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins) target_sixense() if (WIN32) diff --git a/plugins/hifiSpacemouse/CMakeLists.txt b/plugins/hifiSpacemouse/CMakeLists.txt index bcfb309a69..174d0822ec 100644 --- a/plugins/hifiSpacemouse/CMakeLists.txt +++ b/plugins/hifiSpacemouse/CMakeLists.txt @@ -10,7 +10,7 @@ if(WIN32) set(TARGET_NAME hifiSpacemouse) find_package(3DCONNEXIONCLIENT) if (3DCONNEXIONCLIENT_FOUND) - setup_hifi_plugin(Script Qml Widgets) + setup_hifi_plugin(Qml) link_hifi_libraries(shared networking controllers ui plugins input-plugins) target_include_directories(${TARGET_NAME} PUBLIC ${3DCONNEXIONCLIENT_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${3DCONNEXIONCLIENT_LIBRARIES}) diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 59986d103e..2a011a349a 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 gl gpu controllers ui + shared gl gpu gpu-gl controllers ui qml plugins ui-plugins display-plugins input-plugins audio-client networking render-utils ${PLATFORM_GL_BACKEND} diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index d03419d025..d8f2db75c9 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -41,7 +41,7 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { std::array handPoses; // Make controller poses available to the presentation thread ovr_for_each_hand([&](ovrHandType hand) { - static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked & ovrStatus_PositionTracked; + static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked; if (REQUIRED_HAND_STATUS != (trackingState.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) { return; } diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 3581009284..a6e90aa5a6 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -10,8 +10,8 @@ if (WIN32 AND (NOT USE_GLES)) # we're using static GLEW, so define GLEW_STATIC add_definitions(-DGLEW_STATIC) set(TARGET_NAME openvr) - setup_hifi_plugin(OpenGL Script Qml Widgets Multimedia) - link_hifi_libraries(shared gl networking controllers ui + setup_hifi_plugin(Gui Qml Multimedia) + link_hifi_libraries(shared gl qml networking controllers ui plugins display-plugins ui-plugins input-plugins script-engine audio-client render-utils graphics gpu render model-networking fbx ktx image procedural ${PLATFORM_GL_BACKEND}) diff --git a/plugins/pcmCodec/src/PCMCodecManager.cpp b/plugins/pcmCodec/src/PCMCodecManager.cpp index 7278edaf92..051f3973a8 100644 --- a/plugins/pcmCodec/src/PCMCodecManager.cpp +++ b/plugins/pcmCodec/src/PCMCodecManager.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index cef61bdc53..8cf5b72b9a 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -636,14 +636,9 @@ openLoginWindow(); break; case 'disableHmdPreview': - isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview"); - DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN"); - Menu.setIsOptionChecked("Disable Preview", true); - break; + break; // do nothing here, handled in marketplaces.js case 'maybeEnableHmdPreview': - DesktopPreviewProvider.setPreviewDisabledReason("USER"); - Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled); - break; + break; // do nothing here, handled in marketplaces.js case 'passphraseReset': onButtonClicked(); onButtonClicked(); @@ -731,11 +726,7 @@ // -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string // value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML. function onTabletScreenChanged(type, url) { - var onWalletScreenNow = (type === "QML" && url === WALLET_QML_SOURCE); - if (!onWalletScreenNow && onWalletScreen) { - DesktopPreviewProvider.setPreviewDisabledReason("USER"); - } - onWalletScreen = onWalletScreenNow; + onWalletScreen = (type === "QML" && url === WALLET_QML_SOURCE); wireEventBridge(onWalletScreen); // Change button to active when window is first openend, false otherwise. if (button) { diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 32bf7316a9..b72a38f986 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -208,7 +208,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); - var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, ["position"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES); var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -369,6 +369,14 @@ Script.include("/~/system/libraries/Xform.js"); } }; + this.targetIsNull = function() { + var properties = Entities.getEntityProperties(this.grabbedThingID); + if (Object.keys(properties).length === 0 && this.distanceHolding) { + return true; + } + return false; + } + this.isReady = function (controllerData) { if (HMD.active) { if (this.notPointingAtEntity(controllerData)) { @@ -391,7 +399,7 @@ Script.include("/~/system/libraries/Xform.js"); this.run = function (controllerData) { if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || - this.notPointingAtEntity(controllerData)) { + this.notPointingAtEntity(controllerData) || this.targetIsNull()) { this.endNearGrabAction(); return makeRunningValues(false, [], []); } diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index 212cf12fda..fe8ddb394d 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -411,7 +411,7 @@ Script.include("/~/system/libraries/controllers.js"); var data = parseJSON(props.userData); if (data !== undefined && data.seat !== undefined) { var avatarUuid = Uuid.fromString(data.seat.user); - if (Uuid.isNull(avatarUuid) || !AvatarList.getAvatar(avatarUuid)) { + if (Uuid.isNull(avatarUuid) || !AvatarList.getAvatar(avatarUuid).sessionUUID) { return TARGET.SEAT; } else { return TARGET.INVALID; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 863c185cb4..4ef88fcd21 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1250,7 +1250,6 @@ var lastPosition = null; // Do some stuff regularly, like check for placement of various overlays Script.update.connect(function (deltaTime) { progressDialog.move(); - selectionDisplay.checkMove(); selectionDisplay.checkControllerMove(); var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1); var dPosition = Vec3.distance(Camera.position, lastPosition); diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index b8ba146757..24ec12a2c8 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1,9 +1,10 @@ // -// entitySelectionToolClass.js +// entitySelectionTool.js // examples // // Created by Brad hefta-Gaub on 10/1/14. // Modified by Daniela Fontes * @DanielaFifo and Tiago Andrade @TagoWill on 4/7/2017 +// Modified by David Back on 1/9/2018 // Copyright 2014 High Fidelity, Inc. // // This script implements a class useful for building tools for editing entities. @@ -18,15 +19,10 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; SPACE_LOCAL = "local"; SPACE_WORLD = "world"; +HIGHLIGHT_LIST_NAME = "editHandleHighlightList"; Script.include("./controllers.js"); -function objectTranslationPlanePoint(position, dimensions) { - var newPosition = { x: position.x, y: position.y, z: position.z }; - newPosition.y -= dimensions.y / 2.0; - return newPosition; -} - SelectionManager = (function() { var that = {}; @@ -53,10 +49,6 @@ SelectionManager = (function() { print("ERROR: entitySelectionTool.handleEntitySelectionToolUpdates - got malformed message: " + message); } - // if (message === 'callUpdate') { - // that._update(); - // } - if (messageParsed.method === "selectEntity") { if (wantDebug) { print("setting selection to " + messageParsed.entityID); @@ -67,6 +59,22 @@ SelectionManager = (function() { subscribeToUpdateMessages(); + var COLOR_ORANGE_HIGHLIGHT = { red: 255, green: 99, blue: 9 } + var editHandleOutlineStyle = { + outlineUnoccludedColor: COLOR_ORANGE_HIGHLIGHT, + outlineOccludedColor: COLOR_ORANGE_HIGHLIGHT, + fillUnoccludedColor: COLOR_ORANGE_HIGHLIGHT, + fillOccludedColor: COLOR_ORANGE_HIGHLIGHT, + outlineUnoccludedAlpha: 1, + outlineOccludedAlpha: 0, + fillUnoccludedAlpha: 0, + fillOccludedAlpha: 0, + outlineWidth: 3, + isOutlineSmooth: true + }; + //disabling this for now as it is causing rendering issues with the other handle overlays + //Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, editHandleOutlineStyle); + that.savedProperties = {}; that.selections = []; var listeners = []; @@ -103,6 +111,7 @@ SelectionManager = (function() { for (var i = 0; i < entityIDs.length; i++) { var entityID = entityIDs[i]; that.selections.push(entityID); + Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } that._update(true); @@ -119,8 +128,10 @@ SelectionManager = (function() { } if (idx === -1) { that.selections.push(entityID); + Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } else if (toggleSelection) { that.selections.splice(idx, 1); + Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } } @@ -131,6 +142,7 @@ SelectionManager = (function() { var idx = that.selections.indexOf(entityID); if (idx >= 0) { that.selections.splice(idx, 1); + Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } that._update(true); }; @@ -147,6 +159,7 @@ SelectionManager = (function() { that.localPosition = null; that.worldDimensions = null; that.worldPosition = null; + that.worldRotation = null; } else if (that.selections.length === 1) { properties = Entities.getEntityProperties(that.selections[0]); that.localDimensions = properties.dimensions; @@ -156,6 +169,7 @@ SelectionManager = (function() { that.worldDimensions = properties.boundingBox.dimensions; that.worldPosition = properties.boundingBox.center; + that.worldRotation = properties.boundingBox.rotation; SelectionDisplay.setSpaceMode(SPACE_LOCAL); } else { @@ -203,13 +217,12 @@ SelectionManager = (function() { print("ERROR: entitySelectionTool.update got exception: " + JSON.stringify(e)); } } - }; return that; })(); -// Normalize degrees to be in the range (-180, 180] +// Normalize degrees to be in the range (-180, 180) function normalizeDegrees(degrees) { degrees = ((degrees + 180) % 360) - 180; if (degrees <= -180) { @@ -219,36 +232,83 @@ function normalizeDegrees(degrees) { return degrees; } -// FUNCTION: getRelativeCenterPosition -// Return the enter position of an entity relative to it's registrationPoint -// A registration point of (0.5, 0.5, 0.5) will have an offset of (0, 0, 0) -// A registration point of (1.0, 1.0, 1.0) will have an offset of (-dimensions.x / 2, -dimensions.y / 2, -dimensions.z / 2) -function getRelativeCenterPosition(dimensions, registrationPoint) { - return { - x: -dimensions.x * (registrationPoint.x - 0.5), - y: -dimensions.y * (registrationPoint.y - 0.5), - z: -dimensions.z * (registrationPoint.z - 0.5) - }; -} - // SELECTION DISPLAY DEFINITION SelectionDisplay = (function() { var that = {}; - var MINIMUM_DIMENSION = 0.001; + var NEGATE_VECTOR = -1; - var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.0075; + var COLOR_GREEN = { red:31, green:198, blue:166 }; + var COLOR_BLUE = { red:0, green:147, blue:197 }; + var COLOR_RED = { red:226, green:51, blue:77 }; + var COLOR_HOVER = { red:227, green:227, blue:227 }; + var COLOR_ROTATE_CURRENT_RING = { red: 255, green: 99, blue: 9 }; + var COLOR_SCALE_EDGE = { red:87, green:87, blue:87 }; + var COLOR_SCALE_CUBE = { red:106, green:106, blue:106 }; + var COLOR_SCALE_CUBE_SELECTED = { red:18, green:18, blue:18 }; + + var TRANSLATE_ARROW_CYLINDER_OFFSET = 0.1; + var TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE = 0.005; + var TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE = 7.5; + var TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE = 0.025; + var TRANSLATE_ARROW_CONE_OFFSET_CYLINDER_DIMENSION_MULTIPLE = 0.83; + + var ROTATE_RING_CAMERA_DISTANCE_MULTIPLE = 0.15; + var ROTATE_CTRL_SNAP_ANGLE = 22.5; + var ROTATE_DEFAULT_SNAP_ANGLE = 1; + var ROTATE_DEFAULT_TICK_MARKS_ANGLE = 5; + var ROTATE_RING_IDLE_INNER_RADIUS = 0.95; + var ROTATE_RING_SELECTED_INNER_RADIUS = 0.9; // These are multipliers for sizing the rotation degrees display while rotating an entity - var ROTATION_DISPLAY_DISTANCE_MULTIPLIER = 1.2; - var ROTATION_DISPLAY_SIZE_X_MULTIPLIER = 0.6; - var ROTATION_DISPLAY_SIZE_Y_MULTIPLIER = 0.18; - var ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.14; + var ROTATE_DISPLAY_DISTANCE_MULTIPLIER = 2; + var ROTATE_DISPLAY_SIZE_X_MULTIPLIER = 0.2; + var ROTATE_DISPLAY_SIZE_Y_MULTIPLIER = 0.09; + var ROTATE_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.07; - var ROTATE_ARROW_WEST_NORTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-north.svg"; - var ROTATE_ARROW_WEST_SOUTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-south.svg"; + var STRETCH_SPHERE_OFFSET = 0.06; + var STRETCH_SPHERE_CAMERA_DISTANCE_MULTIPLE = 0.01; + var STRETCH_MINIMUM_DIMENSION = 0.001; + var STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE = 2; + var STRETCH_PANEL_WIDTH = 0.01; - var showExtendedStretchHandles = false; + var SCALE_CUBE_OFFSET = 0.5; + var SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE = 0.015; + var SCALE_MINIMUM_DIMENSION = 0.02; + + var CLONER_OFFSET = { x:0.9, y:-0.9, z:0.9 }; + + var CTRL_KEY_CODE = 16777249; + + var TRANSLATE_DIRECTION = { + X : 0, + Y : 1, + Z : 2 + } + + var STRETCH_DIRECTION = { + X : 0, + Y : 1, + Z : 2, + ALL : 3 + } + + var SCALE_DIRECTION = { + LBN : 0, + RBN : 1, + LBF : 2, + RBF : 3, + LTN : 4, + RTN : 5, + LTF : 6, + RTF : 7 + } + + var ROTATE_DIRECTION = { + PITCH : 0, + YAW : 1, + ROLL : 2 + } var spaceMode = SPACE_LOCAL; var overlayNames = []; @@ -259,185 +319,96 @@ SelectionDisplay = (function() { getControllerWorldLocation(Controller.Standard.RightHand, true) ]; - var handleHoverColor = { - red: 224, - green: 67, - blue: 36 - }; - var handleHoverAlpha = 1.0; - - var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool - var innerRadius; - var outerRadius; - var yawHandleRotation; - var pitchHandleRotation; - var rollHandleRotation; - var yawCenter; - var pitchCenter; - var rollCenter; - var rotZero; + var rotationZero; var rotationNormal; + var rotationDegreesPosition; + var worldRotationX; + var worldRotationY; + var worldRotationZ; - var handleColor = { - red: 255, - green: 255, - blue: 255 - }; - var handleAlpha = 0.7; - - var highlightedHandleColor = { - red: 183, - green: 64, - blue: 44 - }; - var highlightedHandleAlpha = 0.9; - - var previousHandle = false; + var previousHandle = null; + var previousHandleHelper = null; var previousHandleColor; - var previousHandleAlpha; - var grabberSizeCorner = 0.025; // These get resized by updateHandleSizes(). - var grabberSizeEdge = 0.015; - var grabberSizeFace = 0.025; - var grabberAlpha = 1; - var grabberColorCorner = { - red: 120, - green: 120, - blue: 120 - }; - var grabberColorEdge = { - red: 0, - green: 0, - blue: 0 - }; - var grabberColorFace = { - red: 120, - green: 120, - blue: 120 - }; - var grabberColorCloner = { - red: 0, - green: 155, - blue: 0 - }; - var grabberLineWidth = 0.5; - var grabberSolid = true; - var grabberMoveUpPosition = Vec3.ZERO; + var ctrlPressed = false; - var lightOverlayColor = { - red: 255, - green: 153, - blue: 0 - }; - - var grabberPropertiesCorner = { - position: Vec3.ZERO, - size: grabberSizeCorner, - color: grabberColorCorner, - alpha: 1, - solid: grabberSolid, + var handlePropertiesTranslateArrowCones = { + shape: "Cone", + solid: true, visible: false, - dashed: false, - drawInFront: true, - borderSize: 1.4 + ignoreRayIntersection: false, + drawInFront: true }; - - var grabberPropertiesEdge = { - position: Vec3.ZERO, - size: grabberSizeEdge, - color: grabberColorEdge, - alpha: 1, - solid: grabberSolid, + var handlePropertiesTranslateArrowCylinders = { + shape: "Cylinder", + solid: true, visible: false, - dashed: false, - drawInFront: true, - borderSize: 1.4 + ignoreRayIntersection: false, + drawInFront: true }; + var handleTranslateXCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); + var handleTranslateXCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders); + Overlays.editOverlay(handleTranslateXCone, { color : COLOR_RED }); + Overlays.editOverlay(handleTranslateXCylinder, { color : COLOR_RED }); + var handleTranslateYCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); + var handleTranslateYCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders); + Overlays.editOverlay(handleTranslateYCone, { color : COLOR_GREEN }); + Overlays.editOverlay(handleTranslateYCylinder, { color : COLOR_GREEN }); + var handleTranslateZCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones); + var handleTranslateZCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders); + Overlays.editOverlay(handleTranslateZCone, { color : COLOR_BLUE }); + Overlays.editOverlay(handleTranslateZCylinder, { color : COLOR_BLUE }); - var grabberPropertiesFace = { - position: Vec3.ZERO, - size: grabberSizeFace, - color: grabberColorFace, + var handlePropertiesRotateRings = { alpha: 1, - solid: grabberSolid, + solid: true, + startAt: 0, + endAt: 360, + innerRadius: ROTATE_RING_IDLE_INNER_RADIUS, + majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE, + majorTickMarksLength: 0.1, visible: false, - dashed: false, - drawInFront: true, - borderSize: 1.4 + ignoreRayIntersection: false, + drawInFront: true }; + var handleRotatePitchRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); + Overlays.editOverlay(handleRotatePitchRing, { + color : COLOR_RED, + majorTickMarksColor: COLOR_RED, + }); + var handleRotateYawRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); + Overlays.editOverlay(handleRotateYawRing, { + color : COLOR_GREEN, + majorTickMarksColor: COLOR_GREEN, + }); + var handleRotateRollRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings); + Overlays.editOverlay(handleRotateRollRing, { + color : COLOR_BLUE, + majorTickMarksColor: COLOR_BLUE, + }); - var grabberPropertiesCloner = { - position: Vec3.ZERO, - size: grabberSizeCorner, - color: grabberColorCloner, + var handleRotateCurrentRing = Overlays.addOverlay("circle3d", { alpha: 1, - solid: grabberSolid, + color: COLOR_ROTATE_CURRENT_RING, + solid: true, + innerRadius: 0.9, visible: false, - dashed: false, - drawInFront: true, - borderSize: 1.4 - }; - - var spotLightLineProperties = { - color: lightOverlayColor - }; - - var highlightBox = Overlays.addOverlay("cube", { - position: Vec3.ZERO, - size: 1, - color: { - red: 90, - green: 90, - blue: 90 - }, - alpha: 1, - solid: false, - visible: false, - dashed: true, - ignoreRayIntersection: true, // this never ray intersects + ignoreRayIntersection: true, drawInFront: true }); - var selectionBox = Overlays.addOverlay("cube", { - position: Vec3.ZERO, - size: 1, - color: { - red: 255, - green: 0, - blue: 0 - }, - alpha: 1, - solid: false, - visible: false, - dashed: false - }); - - var selectionBoxes = []; - var rotationDegreesDisplay = Overlays.addOverlay("text3d", { - position: Vec3.ZERO, text: "", - color: { - red: 0, - green: 0, - blue: 0 - }, - backgroundColor: { - red: 255, - green: 255, - blue: 255 - }, + color: { red: 0, green: 0, blue: 0 }, + backgroundColor: { red: 255, green: 255, blue: 255 }, alpha: 0.7, backgroundAlpha: 0.7, visible: false, isFacingAvatar: true, drawInFront: true, ignoreRayIntersection: true, - dimensions: { - x: 0, - y: 0 - }, + dimensions: { x: 0, y: 0 }, lineHeight: 0.0, topMargin: 0, rightMargin: 0, @@ -445,461 +416,183 @@ SelectionDisplay = (function() { leftMargin: 0 }); - var grabberMoveUp = Overlays.addOverlay("image3d", { - url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg", - position: Vec3.ZERO, - color: handleColor, - alpha: handleAlpha, + var handlePropertiesStretchSpheres = { + shape: "Sphere", + solid: true, visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: true, + ignoreRayIntersection: false, drawInFront: true - }); - - // var normalLine = Overlays.addOverlay("line3d", { - // visible: true, - // start: { x: 0, y: 0, z: 0 }, - // end: { x: 0, y: 0, z: 0 }, - // color: { red: 255, green: 255, blue: 0 }, - // ignoreRayIntersection: true, - // }); - - var grabberLBN = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberRBN = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberLBF = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberRBF = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberLTN = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberRTN = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberLTF = Overlays.addOverlay("cube", grabberPropertiesCorner); - var grabberRTF = Overlays.addOverlay("cube", grabberPropertiesCorner); - - var grabberTOP = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberBOTTOM = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberLEFT = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberRIGHT = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberNEAR = Overlays.addOverlay("cube", grabberPropertiesFace); - var grabberFAR = Overlays.addOverlay("cube", grabberPropertiesFace); - - var grabberEdgeTR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeTL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeTF = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeTN = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeBR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeBL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeBF = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeBN = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeNR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeNL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeFR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberEdgeFL = Overlays.addOverlay("cube", grabberPropertiesEdge); - - var grabberSpotLightCircle = Overlays.addOverlay("circle3d", { - color: lightOverlayColor, - isSolid: false, - visible: false - }); - var grabberSpotLightLineT = Overlays.addOverlay("line3d", spotLightLineProperties); - var grabberSpotLightLineB = Overlays.addOverlay("line3d", spotLightLineProperties); - var grabberSpotLightLineL = Overlays.addOverlay("line3d", spotLightLineProperties); - var grabberSpotLightLineR = Overlays.addOverlay("line3d", spotLightLineProperties); - - var grabberSpotLightCenter = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightRadius = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightT = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberSpotLightB = Overlays.addOverlay("cube", grabberPropertiesEdge); - - var spotLightGrabberHandles = [ - grabberSpotLightCircle, grabberSpotLightCenter, grabberSpotLightRadius, - grabberSpotLightLineT, grabberSpotLightLineB, grabberSpotLightLineL, grabberSpotLightLineR, - grabberSpotLightT, grabberSpotLightB, grabberSpotLightL, grabberSpotLightR - ]; - - var grabberPointLightCircleX = Overlays.addOverlay("circle3d", { - rotation: Quat.fromPitchYawRollDegrees(0, 90, 0), - color: lightOverlayColor, - isSolid: false, - visible: false - }); - var grabberPointLightCircleY = Overlays.addOverlay("circle3d", { - rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), - color: lightOverlayColor, - isSolid: false, - visible: false - }); - var grabberPointLightCircleZ = Overlays.addOverlay("circle3d", { - rotation: Quat.fromPitchYawRollDegrees(0, 0, 0), - color: lightOverlayColor, - isSolid: false, - visible: false - }); - var grabberPointLightT = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightB = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightR = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightF = Overlays.addOverlay("cube", grabberPropertiesEdge); - var grabberPointLightN = Overlays.addOverlay("cube", grabberPropertiesEdge); - - var pointLightGrabberHandles = [ - grabberPointLightCircleX, grabberPointLightCircleY, grabberPointLightCircleZ, - grabberPointLightT, grabberPointLightB, grabberPointLightL, - grabberPointLightR, grabberPointLightF, grabberPointLightN - ]; - - var grabberCloner = Overlays.addOverlay("cube", grabberPropertiesCloner); - - var stretchHandles = [ - grabberLBN, - grabberRBN, - grabberLBF, - grabberRBF, - grabberLTN, - grabberRTN, - grabberLTF, - grabberRTF, - grabberTOP, - grabberBOTTOM, - grabberLEFT, - grabberRIGHT, - grabberNEAR, - grabberFAR, - grabberEdgeTR, - grabberEdgeTL, - grabberEdgeTF, - grabberEdgeTN, - grabberEdgeBR, - grabberEdgeBL, - grabberEdgeBF, - grabberEdgeBN, - grabberEdgeNR, - grabberEdgeNL, - grabberEdgeFR, - grabberEdgeFL, - - grabberSpotLightLineT, - grabberSpotLightLineB, - grabberSpotLightLineL, - grabberSpotLightLineR, - - grabberSpotLightCenter, - grabberSpotLightRadius, - grabberSpotLightL, - grabberSpotLightR, - grabberSpotLightT, - grabberSpotLightB, - - grabberPointLightT, - grabberPointLightB, - grabberPointLightL, - grabberPointLightR, - grabberPointLightF, - grabberPointLightN, - - grabberCloner - ]; - - - var baseOverlayAngles = { - x: 0, - y: 0, - z: 0 }; - var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles); - var baseOfEntityProjectionOverlay = Overlays.addOverlay("rectangle3d", { - position: { - x: 1, - y: 0, - z: 0 - }, - color: { - red: 51, - green: 152, - blue: 203 - }, + var handleStretchXSphere = Overlays.addOverlay("shape", handlePropertiesStretchSpheres); + Overlays.editOverlay(handleStretchXSphere, { color : COLOR_RED }); + var handleStretchYSphere = Overlays.addOverlay("shape", handlePropertiesStretchSpheres); + Overlays.editOverlay(handleStretchYSphere, { color : COLOR_GREEN }); + var handleStretchZSphere = Overlays.addOverlay("shape", handlePropertiesStretchSpheres); + Overlays.editOverlay(handleStretchZSphere, { color : COLOR_BLUE }); + + var handlePropertiesStretchPanel = { + shape: "Quad", alpha: 0.5, solid: true, visible: false, - width: 300, - height: 200, - rotation: baseOverlayRotation, - ignoreRayIntersection: true // always ignore this - }); + ignoreRayIntersection: true, + drawInFront: true, + } + var handleStretchXPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); + Overlays.editOverlay(handleStretchXPanel, { color : COLOR_RED }); + var handleStretchYPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); + Overlays.editOverlay(handleStretchYPanel, { color : COLOR_GREEN }); + var handleStretchZPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); + Overlays.editOverlay(handleStretchZPanel, { color : COLOR_BLUE }); - var yawOverlayAngles = { - x: 90, - y: 0, - z: 0 - }; - var yawOverlayRotation = Quat.fromVec3Degrees(yawOverlayAngles); - var pitchOverlayAngles = { - x: 0, - y: 90, - z: 0 - }; - var pitchOverlayRotation = Quat.fromVec3Degrees(pitchOverlayAngles); - var rollOverlayAngles = { - x: 0, - y: 180, - z: 0 - }; - var rollOverlayRotation = Quat.fromVec3Degrees(rollOverlayAngles); - - var xRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 255, - green: 0, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this - }); - var yRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 0, - green: 255, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this - }); - var zRailOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 0, - green: 0, - blue: 255 - }, - ignoreRayIntersection: true // always ignore this - }); - - var rotateZeroOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 255, - green: 0, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this - }); - - var rotateCurrentOverlay = Overlays.addOverlay("line3d", { - visible: false, - start: Vec3.ZERO, - end: Vec3.ZERO, - color: { - red: 0, - green: 0, - blue: 255 - }, - ignoreRayIntersection: true // always ignore this - }); - - - var rotateOverlayInner = Overlays.addOverlay("circle3d", { - position: Vec3.ZERO, - size: 1, - color: { - red: 51, - green: 152, - blue: 203 - }, - alpha: 0.2, + var handlePropertiesScaleCubes = { + size: 0.025, + color: COLOR_SCALE_CUBE, solid: true, visible: false, - rotation: yawOverlayRotation, - hasTickMarks: true, - majorTickMarksAngle: innerSnapAngle, - minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, - minorTickMarksLength: 0, - majorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - minorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this - }); + ignoreRayIntersection: false, + drawInFront: true, + borderSize: 1.4 + }; + var handleScaleLBNCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // (-x, -y, -z) + var handleScaleRBNCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // (-x, -y, z) + var handleScaleLBFCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // ( x, -y, -z) + var handleScaleRBFCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // ( x, -y, z) + var handleScaleLTNCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // (-x, y, -z) + var handleScaleRTNCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // (-x, y, z) + var handleScaleLTFCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // ( x, y, -z) + var handleScaleRTFCube = Overlays.addOverlay("cube", handlePropertiesScaleCubes); // ( x, y, z) - var rotateOverlayOuter = Overlays.addOverlay("circle3d", { - position: Vec3.ZERO, - size: 1, - color: { - red: 51, - green: 152, - blue: 203 - }, - alpha: 0.2, + var handlePropertiesScaleEdge = { + color: COLOR_SCALE_EDGE, + visible: false, + ignoreRayIntersection: true, + drawInFront: true, + lineWidth: 0.2 + } + var handleScaleTREdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleTLEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleTFEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleTNEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleBREdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleBLEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleBFEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleBNEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleNREdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleNLEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleFREdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + var handleScaleFLEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge); + + var handleCloner = Overlays.addOverlay("cube", { + size: 0.05, + color: COLOR_GREEN, solid: true, visible: false, - rotation: yawOverlayRotation, - - hasTickMarks: true, - majorTickMarksAngle: 45.0, - minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, - minorTickMarksLength: 0.1, - majorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - minorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - ignoreRayIntersection: true // always ignore this + ignoreRayIntersection: false, + drawInFront: true, + borderSize: 1.4 }); - var rotateOverlayCurrent = Overlays.addOverlay("circle3d", { - position: Vec3.ZERO, + // setting to 0 alpha for now to keep this hidden vs using visible false + // because its used as the translate xz tool handle overlay + var selectionBox = Overlays.addOverlay("cube", { size: 1, - color: { - red: 224, - green: 67, - blue: 36 - }, - alpha: 0.8, - solid: true, + color: COLOR_RED, + alpha: 0, + solid: false, visible: false, - rotation: yawOverlayRotation, - ignoreRayIntersection: true, // always ignore this - hasTickMarks: true, - majorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - }, - minorTickMarksColor: { - red: 0, - green: 0, - blue: 0 - } - }); - - var yawHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: Vec3.ZERO, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true - }); - - - var pitchHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: Vec3.ZERO, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true - }); - - - var rollHandle = Overlays.addOverlay("image3d", { - url: ROTATE_ARROW_WEST_NORTH_URL, - position: Vec3.ZERO, - color: handleColor, - alpha: handleAlpha, - visible: false, - size: 0.1, - scale: 0.1, - isFacingAvatar: false, - drawInFront: true + dashed: false }); var allOverlays = [ - highlightBox, - selectionBox, - grabberMoveUp, - yawHandle, - pitchHandle, - rollHandle, - rotateOverlayInner, - rotateOverlayOuter, - rotateOverlayCurrent, - rotateZeroOverlay, - rotateCurrentOverlay, + handleTranslateXCone, + handleTranslateXCylinder, + handleTranslateYCone, + handleTranslateYCylinder, + handleTranslateZCone, + handleTranslateZCylinder, + handleRotatePitchRing, + handleRotateYawRing, + handleRotateRollRing, + handleRotateCurrentRing, rotationDegreesDisplay, - xRailOverlay, - yRailOverlay, - zRailOverlay, - baseOfEntityProjectionOverlay, - grabberSpotLightCircle, - grabberPointLightCircleX, - grabberPointLightCircleY, - grabberPointLightCircleZ + handleStretchXSphere, + handleStretchYSphere, + handleStretchZSphere, + handleStretchXPanel, + handleStretchYPanel, + handleStretchZPanel, + handleScaleLBNCube, + handleScaleRBNCube, + handleScaleLBFCube, + handleScaleRBFCube, + handleScaleLTNCube, + handleScaleRTNCube, + handleScaleLTFCube, + handleScaleRTFCube, + handleScaleTREdge, + handleScaleTLEdge, + handleScaleTFEdge, + handleScaleTNEdge, + handleScaleBREdge, + handleScaleBLEdge, + handleScaleBFEdge, + handleScaleBNEdge, + handleScaleNREdge, + handleScaleNLEdge, + handleScaleFREdge, + handleScaleFLEdge, + handleCloner, + selectionBox + ]; - ].concat(stretchHandles); + overlayNames[handleTranslateXCone] = "handleTranslateXCone"; + overlayNames[handleTranslateXCylinder] = "handleTranslateXCylinder"; + overlayNames[handleTranslateYCone] = "handleTranslateYCone"; + overlayNames[handleTranslateYCylinder] = "handleTranslateYCylinder"; + overlayNames[handleTranslateZCone] = "handleTranslateZCone"; + overlayNames[handleTranslateZCylinder] = "handleTranslateZCylinder"; - overlayNames[highlightBox] = "highlightBox"; + overlayNames[handleRotatePitchRing] = "handleRotatePitchRing"; + overlayNames[handleRotateYawRing] = "handleRotateYawRing"; + overlayNames[handleRotateRollRing] = "handleRotateRollRing"; + overlayNames[handleRotateCurrentRing] = "handleRotateCurrentRing"; + overlayNames[rotationDegreesDisplay] = "rotationDegreesDisplay"; + + overlayNames[handleStretchXSphere] = "handleStretchXSphere"; + overlayNames[handleStretchYSphere] = "handleStretchYSphere"; + overlayNames[handleStretchZSphere] = "handleStretchZSphere"; + overlayNames[handleStretchXPanel] = "handleStretchXPanel"; + overlayNames[handleStretchYPanel] = "handleStretchYPanel"; + overlayNames[handleStretchZPanel] = "handleStretchZPanel"; + + overlayNames[handleScaleLBNCube] = "handleScaleLBNCube"; + overlayNames[handleScaleRBNCube] = "handleScaleRBNCube"; + overlayNames[handleScaleLBFCube] = "handleScaleLBFCube"; + overlayNames[handleScaleRBFCube] = "handleScaleRBFCube"; + overlayNames[handleScaleLTNCube] = "handleScaleLTNCube"; + overlayNames[handleScaleRTNCube] = "handleScaleRTNCube"; + overlayNames[handleScaleLTFCube] = "handleScaleLTFCube"; + overlayNames[handleScaleRTFCube] = "handleScaleRTFCube"; + + overlayNames[handleScaleTREdge] = "handleScaleTREdge"; + overlayNames[handleScaleTLEdge] = "handleScaleTLEdge"; + overlayNames[handleScaleTFEdge] = "handleScaleTFEdge"; + overlayNames[handleScaleTNEdge] = "handleScaleTNEdge"; + overlayNames[handleScaleBREdge] = "handleScaleBREdge"; + overlayNames[handleScaleBLEdge] = "handleScaleBLEdge"; + overlayNames[handleScaleBFEdge] = "handleScaleBFEdge"; + overlayNames[handleScaleBNEdge] = "handleScaleBNEdge"; + overlayNames[handleScaleNREdge] = "handleScaleNREdge"; + overlayNames[handleScaleNLEdge] = "handleScaleNLEdge"; + overlayNames[handleScaleFREdge] = "handleScaleFREdge"; + overlayNames[handleScaleFLEdge] = "handleScaleFLEdge"; + + overlayNames[handleCloner] = "handleCloner"; overlayNames[selectionBox] = "selectionBox"; - overlayNames[baseOfEntityProjectionOverlay] = "baseOfEntityProjectionOverlay"; - overlayNames[grabberMoveUp] = "grabberMoveUp"; - overlayNames[grabberLBN] = "grabberLBN"; - overlayNames[grabberLBF] = "grabberLBF"; - overlayNames[grabberRBN] = "grabberRBN"; - overlayNames[grabberRBF] = "grabberRBF"; - overlayNames[grabberLTN] = "grabberLTN"; - overlayNames[grabberLTF] = "grabberLTF"; - overlayNames[grabberRTN] = "grabberRTN"; - overlayNames[grabberRTF] = "grabberRTF"; - overlayNames[grabberTOP] = "grabberTOP"; - overlayNames[grabberBOTTOM] = "grabberBOTTOM"; - overlayNames[grabberLEFT] = "grabberLEFT"; - overlayNames[grabberRIGHT] = "grabberRIGHT"; - overlayNames[grabberNEAR] = "grabberNEAR"; - overlayNames[grabberFAR] = "grabberFAR"; - - overlayNames[grabberEdgeTR] = "grabberEdgeTR"; - overlayNames[grabberEdgeTL] = "grabberEdgeTL"; - overlayNames[grabberEdgeTF] = "grabberEdgeTF"; - overlayNames[grabberEdgeTN] = "grabberEdgeTN"; - overlayNames[grabberEdgeBR] = "grabberEdgeBR"; - overlayNames[grabberEdgeBL] = "grabberEdgeBL"; - overlayNames[grabberEdgeBF] = "grabberEdgeBF"; - overlayNames[grabberEdgeBN] = "grabberEdgeBN"; - overlayNames[grabberEdgeNR] = "grabberEdgeNR"; - overlayNames[grabberEdgeNL] = "grabberEdgeNL"; - overlayNames[grabberEdgeFR] = "grabberEdgeFR"; - overlayNames[grabberEdgeFL] = "grabberEdgeFL"; - - overlayNames[yawHandle] = "yawHandle"; - overlayNames[pitchHandle] = "pitchHandle"; - overlayNames[rollHandle] = "rollHandle"; - - overlayNames[rotateOverlayInner] = "rotateOverlayInner"; - overlayNames[rotateOverlayOuter] = "rotateOverlayOuter"; - overlayNames[rotateOverlayCurrent] = "rotateOverlayCurrent"; - - overlayNames[rotateZeroOverlay] = "rotateZeroOverlay"; - overlayNames[rotateCurrentOverlay] = "rotateCurrentOverlay"; - overlayNames[grabberCloner] = "grabberCloner"; var activeTool = null; - var grabberTools = {}; + var handleTools = {}; // We get mouseMoveEvents from the handControllers, via handControllerPointer. // But we dont' get mousePressEvents. @@ -932,6 +625,282 @@ SelectionDisplay = (function() { that.triggerMapping.from(Controller.Standard.RT).peek().to(makeTriggerHandler(Controller.Standard.RightHand)); that.triggerMapping.from(Controller.Standard.LT).peek().to(makeTriggerHandler(Controller.Standard.LeftHand)); + // FUNCTION DEF(s): Intersection Check Helpers + function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) { + var wantDebug = false; + if ((queryRay === undefined) || (queryRay === null)) { + if (wantDebug) { + print("testRayIntersect - EARLY EXIT -> queryRay is undefined OR null!"); + } + return null; + } + + var intersectObj = Overlays.findRayIntersection(queryRay, true, overlayIncludes, overlayExcludes); + + if (wantDebug) { + if (!overlayIncludes) { + print("testRayIntersect - no overlayIncludes provided."); + } + if (!overlayExcludes) { + print("testRayIntersect - no overlayExcludes provided."); + } + print("testRayIntersect - Hit: " + intersectObj.intersects); + print(" intersectObj.overlayID:" + intersectObj.overlayID + "[" + overlayNames[intersectObj.overlayID] + "]"); + print(" OverlayName: " + overlayNames[intersectObj.overlayID]); + print(" intersectObj.distance:" + intersectObj.distance); + print(" intersectObj.face:" + intersectObj.face); + Vec3.print(" intersectObj.intersection:", intersectObj.intersection); + } + + return intersectObj; + } + + // FUNCTION: MOUSE PRESS EVENT + that.mousePressEvent = function (event) { + var wantDebug = false; + if (wantDebug) { + print("=============== eST::MousePressEvent BEG ======================="); + } + if (!event.isLeftButton && !that.triggered) { + // EARLY EXIT-(if another mouse button than left is pressed ignore it) + return false; + } + + var pickRay = generalComputePickRay(event.x, event.y); + // TODO_Case6491: Move this out to setup just to make it once + var interactiveOverlays = [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]; + for (var key in handleTools) { + if (handleTools.hasOwnProperty(key)) { + interactiveOverlays.push(key); + } + } + + // Start with unknown mode, in case no tool can handle this. + activeTool = null; + + var results = testRayIntersect(pickRay, interactiveOverlays); + if (results.intersects) { + var hitOverlayID = results.overlayID; + if ((hitOverlayID === HMD.tabletID) || (hitOverlayID === HMD.tabletScreenID) || + (hitOverlayID === HMD.homeButtonID)) { + // EARLY EXIT-(mouse clicks on the tablet should override the edit affordances) + return false; + } + + entityIconOverlayManager.setIconsSelectable(SelectionManager.selections, true); + + var hitTool = handleTools[ hitOverlayID ]; + if (hitTool) { + activeTool = hitTool; + if (activeTool.onBegin) { + activeTool.onBegin(event, pickRay, results); + } else { + print("ERROR: entitySelectionTool.mousePressEvent - ActiveTool(" + activeTool.mode + ") missing onBegin"); + } + } else { + print("ERROR: entitySelectionTool.mousePressEvent - Hit unexpected object, check interactiveOverlays"); + }// End_if (hitTool) + }// End_If(results.intersects) + + if (wantDebug) { + print(" DisplayMode: " + getMode()); + print("=============== eST::MousePressEvent END ======================="); + } + + // If mode is known then we successfully handled this; + // otherwise, we're missing a tool. + return activeTool; + }; + + that.resetPreviousHandleColor = function() { + if (previousHandle != null) { + Overlays.editOverlay(previousHandle, { color: previousHandleColor }); + previousHandle = null; + } + if (previousHandleHelper != null) { + Overlays.editOverlay(previousHandleHelper, { color: previousHandleColor }); + previousHandleHelper = null; + } + }; + + that.getHandleHelper = function(overlay) { + if (overlay === handleTranslateXCone) { + return handleTranslateXCylinder; + } else if (overlay === handleTranslateXCylinder) { + return handleTranslateXCone; + } else if (overlay === handleTranslateYCone) { + return handleTranslateYCylinder; + } else if (overlay === handleTranslateYCylinder) { + return handleTranslateYCone; + } else if (overlay === handleTranslateZCone) { + return handleTranslateZCylinder; + } else if (overlay === handleTranslateZCylinder) { + return handleTranslateZCone; + } + }; + + // FUNCTION: MOUSE MOVE EVENT + that.mouseMoveEvent = function(event) { + var wantDebug = false; + if (wantDebug) { + print("=============== eST::MouseMoveEvent BEG ======================="); + } + if (activeTool) { + if (wantDebug) { + print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); + } + activeTool.onMove(event); + + if (wantDebug) { + print(" Trigger SelectionManager::update"); + } + SelectionManager._update(); + + if (wantDebug) { + print("=============== eST::MouseMoveEvent END ======================="); + } + // EARLY EXIT--(Move handled via active tool) + return true; + } + + // if no tool is active, then just look for handles to highlight... + var pickRay = generalComputePickRay(event.x, event.y); + var result = Overlays.findRayIntersection(pickRay); + var pickedColor; + var highlightNeeded = false; + + if (result.intersects) { + switch (result.overlayID) { + case handleTranslateXCone: + case handleTranslateXCylinder: + case handleRotatePitchRing: + case handleStretchXSphere: + pickedColor = COLOR_RED; + highlightNeeded = true; + break; + case handleTranslateYCone: + case handleTranslateYCylinder: + case handleRotateYawRing: + case handleStretchYSphere: + pickedColor = COLOR_GREEN; + highlightNeeded = true; + break; + case handleTranslateZCone: + case handleTranslateZCylinder: + case handleRotateRollRing: + case handleStretchZSphere: + pickedColor = COLOR_BLUE; + highlightNeeded = true; + break; + case handleScaleLBNCube: + case handleScaleRBNCube: + case handleScaleLBFCube: + case handleScaleRBFCube: + case handleScaleLTNCube: + case handleScaleRTNCube: + case handleScaleLTFCube: + case handleScaleRTFCube: + pickedColor = COLOR_SCALE_CUBE; + highlightNeeded = true; + break; + default: + that.resetPreviousHandleColor(); + break; + } + + if (highlightNeeded) { + that.resetPreviousHandleColor(); + Overlays.editOverlay(result.overlayID, { color: COLOR_HOVER }); + previousHandle = result.overlayID; + previousHandleHelper = that.getHandleHelper(result.overlayID); + if (previousHandleHelper != null) { + Overlays.editOverlay(previousHandleHelper, { color: COLOR_HOVER }); + } + previousHandleColor = pickedColor; + } + + } else { + that.resetPreviousHandleColor(); + } + + if (wantDebug) { + print("=============== eST::MouseMoveEvent END ======================="); + } + return false; + }; + + // FUNCTION: MOUSE RELEASE EVENT + that.mouseReleaseEvent = function(event) { + var wantDebug = false; + if (wantDebug) { + print("=============== eST::MouseReleaseEvent BEG ======================="); + } + var showHandles = false; + if (activeTool) { + if (activeTool.onEnd) { + if (wantDebug) { + print(" Triggering ActiveTool(" + activeTool.mode + ")'s onEnd"); + } + activeTool.onEnd(event); + } else if (wantDebug) { + print(" ActiveTool(" + activeTool.mode + ")'s missing onEnd"); + } + } + + showHandles = activeTool; // base on prior tool value + activeTool = null; + + // if something is selected, then reset the "original" properties for any potential next click+move operation + if (SelectionManager.hasSelection()) { + if (showHandles) { + if (wantDebug) { + print(" Triggering that.select"); + } + that.select(SelectionManager.selections[0], event); + } + } + + if (wantDebug) { + print("=============== eST::MouseReleaseEvent END ======================="); + } + }; + + // Control key remains active only while key is held down + that.keyReleaseEvent = function(key) { + if (key.key === CTRL_KEY_CODE) { + ctrlPressed = false; + that.updateActiveRotateRing(); + } + } + + // Triggers notification on specific key driven events + that.keyPressEvent = function(key) { + if (key.key === CTRL_KEY_CODE) { + ctrlPressed = true; + that.updateActiveRotateRing(); + } + } + + // NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these: + // Controller.mousePressEvent.connect(that.mousePressEvent); + // Controller.mouseMoveEvent.connect(that.mouseMoveEvent); + Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); + Controller.keyPressEvent.connect(that.keyPressEvent); + Controller.keyReleaseEvent.connect(that.keyReleaseEvent); + + that.checkControllerMove = function() { + if (SelectionManager.hasSelection()) { + var controllerPose = getControllerWorldLocation(activeHand, true); + var hand = (activeHand === Controller.Standard.LeftHand) ? 0 : 1; + if (controllerPose.valid && lastControllerPoses[hand].valid) { + if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) || + !Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) { + that.mouseMoveEvent({}); + } + } + lastControllerPoses[hand] = controllerPose; + } + }; function controllerComputePickRay() { var controllerPose = getControllerWorldLocation(activeHand, true); @@ -942,35 +911,15 @@ SelectionDisplay = (function() { return {origin: controllerPosition, direction: controllerDirection}; } } + function generalComputePickRay(x, y) { return controllerComputePickRay() || Camera.computePickRay(x, y); } - function addGrabberTool(overlay, tool) { - grabberTools[overlay] = tool; - return tool; - } - // @param: toolHandle: The overlayID associated with the tool - // that correlates to the tool you wish to query. - // @note: If toolHandle is null or undefined then activeTool - // will be checked against those values as opposed to - // the tool registered under toolHandle. Null & Undefined - // are treated as separate values. - // @return: bool - Indicates if the activeTool is that queried. - function isActiveTool(toolHandle) { - if (!toolHandle) { - // Allow isActiveTool(null) and similar to return true if there's - // no active tool - return (activeTool === toolHandle); - } - - if (!grabberTools.hasOwnProperty(toolHandle)) { - print("WARNING: entitySelectionTool.isActiveTool - Encountered unknown grabberToolHandle: " + toolHandle + ". Tools should be egistered via addGrabberTool."); - // EARLY EXIT - return false; - } - - return (activeTool === grabberTools[ toolHandle ]); + function getDistanceToCamera(position) { + var cameraPosition = Camera.getPosition(); + var toCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position)); + return toCameraDistance; } // @return string - The mode of the currently active tool; @@ -979,30 +928,10 @@ SelectionDisplay = (function() { return (activeTool ? activeTool.mode : "UNKNOWN"); } - that.cleanup = function() { for (var i = 0; i < allOverlays.length; i++) { Overlays.deleteOverlay(allOverlays[i]); } - for (var j = 0; j < selectionBoxes.length; j++) { - Overlays.deleteOverlay(selectionBoxes[j]); - } - }; - - that.highlightSelectable = function(entityID) { - var properties = Entities.getEntityProperties(entityID); - Overlays.editOverlay(highlightBox, { - visible: true, - position: properties.boundingBox.center, - dimensions: properties.dimensions, - rotation: properties.rotation - }); - }; - - that.unhighlightSelectable = function(entityID) { - Overlays.editOverlay(highlightBox, { - visible: false - }); }; that.select = function(entityID, event) { @@ -1020,262 +949,11 @@ SelectionDisplay = (function() { print(" event.y:" + event.y); Vec3.print(" current position:", properties.position); } - - } - Overlays.editOverlay(highlightBox, { - visible: false - }); - that.updateHandles(); }; - // Function: Calculate New Bound Extremes - // uses dot product to discover new top and bottom on the new referential (max and min) - that.calculateNewBoundExtremes = function(boundPointList, referenceVector) { - - if (boundPointList.length < 2) { - return [null, null]; - } - - var refMax = boundPointList[0]; - var refMin = boundPointList[1]; - - var dotMax = Vec3.dot(boundPointList[0], referenceVector); - var dotMin = Vec3.dot(boundPointList[1], referenceVector); - - if (dotMin > dotMax) { - dotMax = dotMin; - dotMin = Vec3.dot(boundPointList[0], referenceVector); - refMax = boundPointList[1]; - refMin = boundPointList[0]; - } - - for (var i = 2; i < boundPointList.length ; i++) { - var dotAux = Vec3.dot(boundPointList[i], referenceVector); - if (dotAux > dotMax) { - dotMax = dotAux; - refMax = boundPointList[i]; - } else if (dotAux < dotMin) { - dotMin = dotAux; - refMin = boundPointList[i]; - } - } - return [refMin, refMax]; - } - - // Function: Project Bounding Box Points - // Projects all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) onto - // one of the basis of the new avatar referencial - // dimensions - dimensions of the AABB (axis aligned bounding box) on the standard basis - // [1, 0, 0], [0, 1, 0], [0, 0, 1] - // v - projection vector - // rotateHandleOffset - offset for the rotation handle gizmo position - that.projectBoundingBoxPoints = function(dimensions, v, rotateHandleOffset) { - var projT_v = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), v); - projT_v = Vec3.multiply(projT_v, v); - - var projB_v = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), v); - projB_v = Vec3.multiply(projB_v, v); - - var projL_v = Vec3.dot(Vec3.multiply((dimensions.x / 2) + rotateHandleOffset, Vec3.UNIT_X), v); - projL_v = Vec3.multiply(projL_v, v); - - var projR_v = Vec3.dot(Vec3.multiply(-1.0 * (dimensions.x / 2) - 1.0 * rotateHandleOffset, Vec3.UNIT_X), v); - projR_v = Vec3.multiply(projR_v, v); - - var projN_v = Vec3.dot(Vec3.multiply((dimensions.z / 2) + rotateHandleOffset, Vec3.FRONT), v); - projN_v = Vec3.multiply(projN_v, v); - - var projF_v = Vec3.dot(Vec3.multiply(-1.0 * (dimensions.z / 2) - 1.0 * rotateHandleOffset, Vec3.FRONT), v); - projF_v = Vec3.multiply(projF_v, v); - - var projList = [projT_v, projB_v, projL_v, projR_v, projN_v, projF_v]; - - return that.calculateNewBoundExtremes(projList, v); - }; - - // FUNCTION: UPDATE ROTATION HANDLES - that.updateRotationHandles = function() { - var diagonal = (Vec3.length(SelectionManager.worldDimensions) / 2) * 1.1; - var halfDimensions = Vec3.multiply(SelectionManager.worldDimensions, 0.5); - var innerActive = false; - var innerAlpha = 0.2; - var outerAlpha = 0.2; - if (innerActive) { - innerAlpha = 0.5; - } else { - outerAlpha = 0.5; - } - // prev 0.05 - var rotateHandleOffset = 0.05; - - var boundsCenter, objectCenter; - - var dimensions, rotation; - if (spaceMode === SPACE_LOCAL) { - rotation = SelectionManager.localRotation; - } else { - rotation = SelectionManager.worldRotation; - } - objectCenter = SelectionManager.worldPosition; - dimensions = SelectionManager.worldDimensions; - var position = objectCenter; - - boundsCenter = objectCenter; - - var yawCorner; - var pitchCorner; - var rollCorner; - - var cameraPosition = Camera.getPosition(); - var look = Vec3.normalize(Vec3.subtract(cameraPosition, objectCenter)); - - // place yaw, pitch and roll rotations on the avatar referential - - var avatarReferential = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({ - x: 0, - y: 180, - z: 0 - })); - var upVector = Quat.getUp(avatarReferential); - var rightVector = Vec3.multiply(-1, Quat.getRight(avatarReferential)); - var frontVector = Quat.getFront(avatarReferential); - - // project all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) - // onto the new avatar referential - - // UP - var projUP = that.projectBoundingBoxPoints(dimensions, upVector, rotateHandleOffset); - // RIGHT - var projRIGHT = that.projectBoundingBoxPoints(dimensions, rightVector, rotateHandleOffset); - // FRONT - var projFRONT = that.projectBoundingBoxPoints(dimensions, frontVector, rotateHandleOffset); - - // YAW - yawCenter = Vec3.sum(boundsCenter, projUP[0]); - yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[0], projRIGHT[1]), projFRONT[1])); - - yawHandleRotation = Quat.lookAt( - yawCorner, - Vec3.sum(yawCorner, upVector), - Vec3.subtract(yawCenter,yawCorner)); - yawHandleRotation = Quat.multiply(Quat.angleAxis(45, upVector), yawHandleRotation); - - // PTCH - pitchCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[0]), projFRONT[1])); - pitchCenter = Vec3.sum(boundsCenter, projRIGHT[0]); - - pitchHandleRotation = Quat.lookAt( - pitchCorner, - Vec3.sum(pitchCorner, rightVector), - Vec3.subtract(pitchCenter,pitchCorner)); - pitchHandleRotation = Quat.multiply(Quat.angleAxis(45, rightVector), pitchHandleRotation); - - // ROLL - rollCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[1]), projFRONT[0])); - rollCenter = Vec3.sum(boundsCenter, projFRONT[0]); - - rollHandleRotation = Quat.lookAt( - rollCorner, - Vec3.sum(rollCorner, frontVector), - Vec3.subtract(rollCenter,rollCorner)); - rollHandleRotation = Quat.multiply(Quat.angleAxis(45, frontVector), rollHandleRotation); - - - var rotateHandlesVisible = true; - var rotationOverlaysVisible = false; - // note: Commented out as these are currently unused here; however, - // leaving them around as they document intent of state as it - // relates to modes that may be useful later. - // var translateHandlesVisible = true; - // var selectionBoxVisible = true; - var isPointLight = false; - if (SelectionManager.selections.length === 1) { - var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - isPointLight = (properties.type === "Light") && !properties.isSpotlight; - } - - if (isActiveTool(yawHandle) || isActiveTool(pitchHandle) || - isActiveTool(rollHandle) || isActiveTool(selectionBox) || isActiveTool(grabberCloner)) { - rotationOverlaysVisible = true; - rotateHandlesVisible = false; - // translateHandlesVisible = false; - // selectionBoxVisible = false; - } else if (isActiveTool(grabberMoveUp) || isPointLight) { - rotateHandlesVisible = false; - } else if (activeTool) { - // every other mode is a stretch mode... - rotateHandlesVisible = false; - // translateHandlesVisible = false; - } - - Overlays.editOverlay(rotateZeroOverlay, { - visible: rotationOverlaysVisible - }); - Overlays.editOverlay(rotateCurrentOverlay, { - visible: rotationOverlaysVisible - }); - - Overlays.editOverlay(yawHandle, { - visible: rotateHandlesVisible, - position: yawCorner, - rotation: yawHandleRotation - }); - Overlays.editOverlay(pitchHandle, { - visible: rotateHandlesVisible, - position: pitchCorner, - rotation: pitchHandleRotation - }); - Overlays.editOverlay(rollHandle, { - visible: rotateHandlesVisible, - position: rollCorner, - rotation: rollHandleRotation - }); - - - }; - - // FUNCTION: UPDATE HANDLE SIZES - that.updateHandleSizes = function() { - if (SelectionManager.hasSelection()) { - var diff = Vec3.subtract(SelectionManager.worldPosition, Camera.getPosition()); - var grabberSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 5; - var dimensions = SelectionManager.worldDimensions; - var avgDimension = (dimensions.x + dimensions.y + dimensions.z) / 3; - grabberSize = Math.min(grabberSize, avgDimension / 10); - - for (var i = 0; i < stretchHandles.length; i++) { - Overlays.editOverlay(stretchHandles[i], { - size: grabberSize - }); - } - var handleSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 7; - handleSize = Math.min(handleSize, avgDimension / 3); - - Overlays.editOverlay(yawHandle, { - scale: handleSize - }); - Overlays.editOverlay(pitchHandle, { - scale: handleSize - }); - Overlays.editOverlay(rollHandle, { - scale: handleSize - }); - var upDiff = Vec3.multiply(( - Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 3), - Quat.getUp(MyAvatar.orientation) - ); - var pos = Vec3.sum(grabberMoveUpPosition, upDiff); - Overlays.editOverlay(grabberMoveUp, { - position: pos, - scale: handleSize / 1.25 - }); - } - }; - Script.update.connect(that.updateHandleSizes); - // FUNCTION: SET SPACE MODE that.setSpaceMode = function(newSpaceMode) { var wantDebug = false; @@ -1297,32 +975,33 @@ SelectionDisplay = (function() { } }; - // FUNCTION: TOGGLE SPACE MODE - that.toggleSpaceMode = function() { - var wantDebug = false; - if (wantDebug) { - print("========> ToggleSpaceMode called. ========="); - } - if ((spaceMode === SPACE_WORLD) && (SelectionManager.selections.length > 1)) { - if (wantDebug) { - print("Local space editing is not available with multiple selections"); - } - return; - } - if (wantDebug) { - print("PreToggle: " + spaceMode); - } - spaceMode = (spaceMode === SPACE_LOCAL) ? SPACE_WORLD : SPACE_LOCAL; - that.updateHandles(); - if (wantDebug) { - print("PostToggle: " + spaceMode); - print("======== ToggleSpaceMode called. <========="); - } - }; + function addHandleTool(overlay, tool) { + handleTools[overlay] = tool; + return tool; + } - // FUNCTION: UNSELECT ALL - // TODO?: Needs implementation - that.unselectAll = function() {}; + // @param: toolHandle: The overlayID associated with the tool + // that correlates to the tool you wish to query. + // @note: If toolHandle is null or undefined then activeTool + // will be checked against those values as opposed to + // the tool registered under toolHandle. Null & Undefined + // are treated as separate values. + // @return: bool - Indicates if the activeTool is that queried. + function isActiveTool(toolHandle) { + if (!toolHandle) { + // Allow isActiveTool(null) and similar to return true if there's + // no active tool + return (activeTool === toolHandle); + } + + if (!handleTools.hasOwnProperty(toolHandle)) { + print("WARNING: entitySelectionTool.isActiveTool - Encountered unknown grabberToolHandle: " + toolHandle + ". Tools should be registered via addHandleTool."); + // EARLY EXIT + return false; + } + + return (activeTool === handleTools[ toolHandle ]); + } // FUNCTION: UPDATE HANDLES that.updateHandles = function() { @@ -1333,709 +1012,451 @@ SelectionDisplay = (function() { print(" SpaceMode: " + spaceMode); print(" DisplayMode: " + getMode()); } + if (SelectionManager.selections.length === 0) { that.setOverlaysVisible(false); return; } - // print(" Triggering updateRotationHandles"); - that.updateRotationHandles(); + if (SelectionManager.hasSelection()) { + var position = SelectionManager.worldPosition; + var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; + var dimensions = spaceMode === SPACE_LOCAL ? SelectionManager.localDimensions : SelectionManager.worldDimensions; + var rotationInverse = Quat.inverse(rotation); + var toCameraDistance = getDistanceToCamera(position); - var rotation, dimensions, position, registrationPoint; + var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -90); + rotationX = Quat.multiply(rotation, localRotationX); + worldRotationX = rotationX; + var localRotationY = Quat.fromPitchYawRollDegrees(0, 90, 0); + rotationY = Quat.multiply(rotation, localRotationY); + worldRotationY = rotationY; + var localRotationZ = Quat.fromPitchYawRollDegrees(90, 0, 0); + rotationZ = Quat.multiply(rotation, localRotationZ); + worldRotationZ = rotationZ; - if (spaceMode === SPACE_LOCAL) { - rotation = SelectionManager.localRotation; - dimensions = SelectionManager.localDimensions; - position = SelectionManager.localPosition; - registrationPoint = SelectionManager.localRegistrationPoint; - } else { - rotation = Quat.IDENTITY; - dimensions = SelectionManager.worldDimensions; - position = SelectionManager.worldPosition; - registrationPoint = SelectionManager.worldRegistrationPoint; - } - - var registrationPointDimensions = { - x: dimensions.x * registrationPoint.x, - y: dimensions.y * registrationPoint.y, - z: dimensions.z * registrationPoint.z - }; - - // Center of entity, relative to registration point - var center = getRelativeCenterPosition(dimensions, registrationPoint); - - // Distances in world coordinates relative to the registration point - var left = -registrationPointDimensions.x; - var right = dimensions.x - registrationPointDimensions.x; - var bottom = -registrationPointDimensions.y; - var top = dimensions.y - registrationPointDimensions.y; - var near = -registrationPointDimensions.z; - var far = dimensions.z - registrationPointDimensions.z; - var front = far; - - var worldTop = SelectionManager.worldDimensions.y / 2; - - var LBN = { - x: left, - y: bottom, - z: near - }; - var RBN = { - x: right, - y: bottom, - z: near - }; - var LBF = { - x: left, - y: bottom, - z: far - }; - var RBF = { - x: right, - y: bottom, - z: far - }; - var LTN = { - x: left, - y: top, - z: near - }; - var RTN = { - x: right, - y: top, - z: near - }; - var LTF = { - x: left, - y: top, - z: far - }; - var RTF = { - x: right, - y: top, - z: far - }; - - var TOP = { - x: center.x, - y: top, - z: center.z - }; - var BOTTOM = { - x: center.x, - y: bottom, - z: center.z - }; - var LEFT = { - x: left, - y: center.y, - z: center.z - }; - var RIGHT = { - x: right, - y: center.y, - z: center.z - }; - var NEAR = { - x: center.x, - y: center.y, - z: near - }; - var FAR = { - x: center.x, - y: center.y, - z: far - }; - - var EdgeTR = { - x: right, - y: top, - z: center.z - }; - var EdgeTL = { - x: left, - y: top, - z: center.z - }; - var EdgeTF = { - x: center.x, - y: top, - z: front - }; - var EdgeTN = { - x: center.x, - y: top, - z: near - }; - var EdgeBR = { - x: right, - y: bottom, - z: center.z - }; - var EdgeBL = { - x: left, - y: bottom, - z: center.z - }; - var EdgeBF = { - x: center.x, - y: bottom, - z: front - }; - var EdgeBN = { - x: center.x, - y: bottom, - z: near - }; - var EdgeNR = { - x: right, - y: center.y, - z: near - }; - var EdgeNL = { - x: left, - y: center.y, - z: near - }; - var EdgeFR = { - x: right, - y: center.y, - z: front - }; - var EdgeFL = { - x: left, - y: center.y, - z: front - }; - - LBN = Vec3.multiplyQbyV(rotation, LBN); - RBN = Vec3.multiplyQbyV(rotation, RBN); - LBF = Vec3.multiplyQbyV(rotation, LBF); - RBF = Vec3.multiplyQbyV(rotation, RBF); - LTN = Vec3.multiplyQbyV(rotation, LTN); - RTN = Vec3.multiplyQbyV(rotation, RTN); - LTF = Vec3.multiplyQbyV(rotation, LTF); - RTF = Vec3.multiplyQbyV(rotation, RTF); - - TOP = Vec3.multiplyQbyV(rotation, TOP); - BOTTOM = Vec3.multiplyQbyV(rotation, BOTTOM); - LEFT = Vec3.multiplyQbyV(rotation, LEFT); - RIGHT = Vec3.multiplyQbyV(rotation, RIGHT); - NEAR = Vec3.multiplyQbyV(rotation, NEAR); - FAR = Vec3.multiplyQbyV(rotation, FAR); - - EdgeTR = Vec3.multiplyQbyV(rotation, EdgeTR); - EdgeTL = Vec3.multiplyQbyV(rotation, EdgeTL); - EdgeTF = Vec3.multiplyQbyV(rotation, EdgeTF); - EdgeTN = Vec3.multiplyQbyV(rotation, EdgeTN); - EdgeBR = Vec3.multiplyQbyV(rotation, EdgeBR); - EdgeBL = Vec3.multiplyQbyV(rotation, EdgeBL); - EdgeBF = Vec3.multiplyQbyV(rotation, EdgeBF); - EdgeBN = Vec3.multiplyQbyV(rotation, EdgeBN); - EdgeNR = Vec3.multiplyQbyV(rotation, EdgeNR); - EdgeNL = Vec3.multiplyQbyV(rotation, EdgeNL); - EdgeFR = Vec3.multiplyQbyV(rotation, EdgeFR); - EdgeFL = Vec3.multiplyQbyV(rotation, EdgeFL); - - LBN = Vec3.sum(position, LBN); - RBN = Vec3.sum(position, RBN); - LBF = Vec3.sum(position, LBF); - RBF = Vec3.sum(position, RBF); - LTN = Vec3.sum(position, LTN); - RTN = Vec3.sum(position, RTN); - LTF = Vec3.sum(position, LTF); - RTF = Vec3.sum(position, RTF); - - TOP = Vec3.sum(position, TOP); - BOTTOM = Vec3.sum(position, BOTTOM); - LEFT = Vec3.sum(position, LEFT); - RIGHT = Vec3.sum(position, RIGHT); - NEAR = Vec3.sum(position, NEAR); - FAR = Vec3.sum(position, FAR); - - EdgeTR = Vec3.sum(position, EdgeTR); - EdgeTL = Vec3.sum(position, EdgeTL); - EdgeTF = Vec3.sum(position, EdgeTF); - EdgeTN = Vec3.sum(position, EdgeTN); - EdgeBR = Vec3.sum(position, EdgeBR); - EdgeBL = Vec3.sum(position, EdgeBL); - EdgeBF = Vec3.sum(position, EdgeBF); - EdgeBN = Vec3.sum(position, EdgeBN); - EdgeNR = Vec3.sum(position, EdgeNR); - EdgeNL = Vec3.sum(position, EdgeNL); - EdgeFR = Vec3.sum(position, EdgeFR); - EdgeFL = Vec3.sum(position, EdgeFL); - - var inModeRotate = (isActiveTool(yawHandle) || isActiveTool(pitchHandle) || isActiveTool(rollHandle)); - var inModeTranslate = (isActiveTool(selectionBox) || isActiveTool(grabberCloner) || isActiveTool(grabberMoveUp)); - var stretchHandlesVisible = !(inModeRotate || inModeTranslate) && (spaceMode === SPACE_LOCAL); - var extendedStretchHandlesVisible = (stretchHandlesVisible && showExtendedStretchHandles); - var cloneHandleVisible = !(inModeRotate || inModeTranslate); - if (wantDebug) { - print(" Set Non-Light Grabbers Visible - Norm: " + stretchHandlesVisible + " Ext: " + extendedStretchHandlesVisible); - } - var isSingleSelection = (SelectionManager.selections.length === 1); - - if (isSingleSelection) { - var properties = Entities.getEntityProperties(SelectionManager.selections[0]); - var isLightSelection = (properties.type === "Light"); - if (isLightSelection) { - if (wantDebug) { - print(" Light Selection revoking Non-Light Grabbers Visibility!"); - } - stretchHandlesVisible = false; - extendedStretchHandlesVisible = false; - cloneHandleVisible = false; - if (properties.isSpotlight) { - that.setPointLightHandlesVisible(false); - - var distance = (properties.dimensions.z / 2) * Math.sin(properties.cutoff * (Math.PI / 180)); - var showEdgeSpotGrabbers = !(inModeTranslate || inModeRotate); - Overlays.editOverlay(grabberSpotLightCenter, { - position: position, - visible: false - }); - Overlays.editOverlay(grabberSpotLightRadius, { - position: NEAR, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - - Overlays.editOverlay(grabberSpotLightL, { - position: EdgeNL, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - Overlays.editOverlay(grabberSpotLightR, { - position: EdgeNR, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - Overlays.editOverlay(grabberSpotLightT, { - position: EdgeTN, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - Overlays.editOverlay(grabberSpotLightB, { - position: EdgeBN, - rotation: rotation, - visible: showEdgeSpotGrabbers - }); - Overlays.editOverlay(grabberSpotLightCircle, { - position: NEAR, - dimensions: { - x: distance, - y: distance, - z: 1 - }, - rotation: rotation, - visible: true - }); - - Overlays.editOverlay(grabberSpotLightLineT, { - start: position, - end: EdgeTN, - visible: true - }); - Overlays.editOverlay(grabberSpotLightLineB, { - start: position, - end: EdgeBN, - visible: true - }); - Overlays.editOverlay(grabberSpotLightLineR, { - start: position, - end: EdgeNR, - visible: true - }); - Overlays.editOverlay(grabberSpotLightLineL, { - start: position, - end: EdgeNL, - visible: true - }); - - } else { // ..it's a PointLight - that.setSpotLightHandlesVisible(false); - - var showEdgePointGrabbers = !inModeTranslate; - Overlays.editOverlay(grabberPointLightT, { - position: TOP, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightB, { - position: BOTTOM, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightL, { - position: LEFT, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightR, { - position: RIGHT, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightF, { - position: FAR, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightN, { - position: NEAR, - rotation: rotation, - visible: showEdgePointGrabbers - }); - Overlays.editOverlay(grabberPointLightCircleX, { - position: position, - rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)), - dimensions: { - x: properties.dimensions.z / 2.0, - y: properties.dimensions.z / 2.0, - z: 1 - }, - visible: true - }); - Overlays.editOverlay(grabberPointLightCircleY, { - position: position, - rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)), - dimensions: { - x: properties.dimensions.z / 2.0, - y: properties.dimensions.z / 2.0, - z: 1 - }, - visible: true - }); - Overlays.editOverlay(grabberPointLightCircleZ, { - position: position, - rotation: rotation, - dimensions: { - x: properties.dimensions.z / 2.0, - y: properties.dimensions.z / 2.0, - z: 1 - }, - visible: true - }); - } - } else { // ..it's not a light at all - that.setSpotLightHandlesVisible(false); - that.setPointLightHandlesVisible(false); + // in HMD we clamp the overlays to the bounding box for now so lasers can hit them + var maxHandleDimension = 0; + if (HMD.active) { + maxHandleDimension = Math.max(dimensions.x, dimensions.y, dimensions.z); } - }// end of isSingleSelection - - Overlays.editOverlay(grabberLBN, { - visible: stretchHandlesVisible, - rotation: rotation, - position: LBN - }); - Overlays.editOverlay(grabberRBN, { - visible: stretchHandlesVisible, - rotation: rotation, - position: RBN - }); - Overlays.editOverlay(grabberLBF, { - visible: stretchHandlesVisible, - rotation: rotation, - position: LBF - }); - Overlays.editOverlay(grabberRBF, { - visible: stretchHandlesVisible, - rotation: rotation, - position: RBF - }); - - Overlays.editOverlay(grabberLTN, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: LTN - }); - Overlays.editOverlay(grabberRTN, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: RTN - }); - Overlays.editOverlay(grabberLTF, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: LTF - }); - Overlays.editOverlay(grabberRTF, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: RTF - }); - - Overlays.editOverlay(grabberTOP, { - visible: stretchHandlesVisible, - rotation: rotation, - position: TOP - }); - Overlays.editOverlay(grabberBOTTOM, { - visible: stretchHandlesVisible, - rotation: rotation, - position: BOTTOM - }); - Overlays.editOverlay(grabberLEFT, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: LEFT - }); - Overlays.editOverlay(grabberRIGHT, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: RIGHT - }); - Overlays.editOverlay(grabberNEAR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: NEAR - }); - Overlays.editOverlay(grabberFAR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: FAR - }); - - Overlays.editOverlay(grabberCloner, { - visible: cloneHandleVisible, - rotation: rotation, - position: EdgeTR - }); - - var selectionBoxPosition = Vec3.multiplyQbyV(rotation, center); - selectionBoxPosition = Vec3.sum(position, selectionBoxPosition); - Overlays.editOverlay(selectionBox, { - position: selectionBoxPosition, - dimensions: dimensions, - rotation: rotation, - visible: !inModeRotate - }); - - // Create more selection box overlays if we don't have enough - var overlaysNeeded = SelectionManager.selections.length - selectionBoxes.length; - for (var i = 0; i < overlaysNeeded; i++) { - selectionBoxes.push( - Overlays.addOverlay("cube", { - position: { - x: 0, - y: 0, - z: 0 - }, - size: 1, - color: { - red: 255, - green: 153, - blue: 0 - }, - alpha: 1, - solid: false, - visible: false, - dashed: false, - ignoreRayIntersection: true - })); - } - - i = 0; - // Only show individual selections boxes if there is more than 1 selection - if (SelectionManager.selections.length > 1) { - for (; i < SelectionManager.selections.length; i++) { - var props = Entities.getEntityProperties(SelectionManager.selections[i]); - - // Adjust overlay position to take registrationPoint into account - // centeredRP = registrationPoint with range [-0.5, 0.5] - var centeredRP = Vec3.subtract(props.registrationPoint, { - x: 0.5, - y: 0.5, - z: 0.5 - }); - var offset = vec3Mult(props.dimensions, centeredRP); - offset = Vec3.multiply(-1, offset); - offset = Vec3.multiplyQbyV(props.rotation, offset); - var curBoxPosition = Vec3.sum(props.position, offset); - - var color = {red: 255, green: 128, blue: 0}; - if (i >= SelectionManager.selections.length - 1) { - color = {red: 255, green: 255, blue: 64}; - } - - Overlays.editOverlay(selectionBoxes[i], { - position: curBoxPosition, - color: color, - rotation: props.rotation, - dimensions: props.dimensions, - visible: true + // UPDATE ROTATION RINGS + // rotateDimension is used as the base dimension for all overlays + var rotateDimension = Math.max(maxHandleDimension, toCameraDistance * ROTATE_RING_CAMERA_DISTANCE_MULTIPLE); + var rotateDimensions = { x:rotateDimension, y:rotateDimension, z:rotateDimension }; + if (!isActiveTool(handleRotatePitchRing)) { + Overlays.editOverlay(handleRotatePitchRing, { + position: position, + rotation: rotationY, + dimensions: rotateDimensions, + majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE }); } - } - // Hide any remaining selection boxes - for (; i < selectionBoxes.length; i++) { - Overlays.editOverlay(selectionBoxes[i], { - visible: false + if (!isActiveTool(handleRotateYawRing)) { + Overlays.editOverlay(handleRotateYawRing, { + position: position, + rotation: rotationZ, + dimensions: rotateDimensions, + majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE + }); + } + if (!isActiveTool(handleRotateRollRing)) { + Overlays.editOverlay(handleRotateRollRing, { + position: position, + rotation: rotationX, + dimensions: rotateDimensions, + majorTickMarksAngle: ROTATE_DEFAULT_TICK_MARKS_ANGLE + }); + } + Overlays.editOverlay(handleRotateCurrentRing, { dimensions: rotateDimensions }); + that.updateActiveRotateRing(); + + // UPDATE TRANSLATION ARROWS + var arrowCylinderDimension = rotateDimension * TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE / + ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; + var arrowCylinderDimensions = { + x:arrowCylinderDimension, + y:arrowCylinderDimension * TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE, + z:arrowCylinderDimension + }; + var arrowConeDimension = rotateDimension * TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE / + ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; + var arrowConeDimensions = { x:arrowConeDimension, y:arrowConeDimension, z:arrowConeDimension }; + var arrowCylinderOffset = rotateDimension * TRANSLATE_ARROW_CYLINDER_OFFSET / ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; + var arrowConeOffset = arrowCylinderDimensions.y * TRANSLATE_ARROW_CONE_OFFSET_CYLINDER_DIMENSION_MULTIPLE; + var cylinderXPosition = { x:arrowCylinderOffset, y:0, z:0 }; + cylinderXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderXPosition)); + Overlays.editOverlay(handleTranslateXCylinder, { + position: cylinderXPosition, + rotation: rotationX, + dimensions: arrowCylinderDimensions + }); + var cylinderXOffset = Vec3.subtract(cylinderXPosition, position); + var coneXPosition = Vec3.sum(cylinderXPosition, Vec3.multiply(Vec3.normalize(cylinderXOffset), arrowConeOffset)); + Overlays.editOverlay(handleTranslateXCone, { + position: coneXPosition, + rotation: rotationX, + dimensions: arrowConeDimensions + }); + var cylinderYPosition = { x:0, y:arrowCylinderOffset, z:0 }; + cylinderYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderYPosition)); + Overlays.editOverlay(handleTranslateYCylinder, { + position: cylinderYPosition, + rotation: rotationY, + dimensions: arrowCylinderDimensions + }); + var cylinderYOffset = Vec3.subtract(cylinderYPosition, position); + var coneYPosition = Vec3.sum(cylinderYPosition, Vec3.multiply(Vec3.normalize(cylinderYOffset), arrowConeOffset)); + Overlays.editOverlay(handleTranslateYCone, { + position: coneYPosition, + rotation: rotationY, + dimensions: arrowConeDimensions + }); + var cylinderZPosition = { x:0, y:0, z:arrowCylinderOffset }; + cylinderZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderZPosition)); + Overlays.editOverlay(handleTranslateZCylinder, { + position: cylinderZPosition, + rotation: rotationZ, + dimensions: arrowCylinderDimensions + }); + var cylinderZOffset = Vec3.subtract(cylinderZPosition, position); + var coneZPosition = Vec3.sum(cylinderZPosition, Vec3.multiply(Vec3.normalize(cylinderZOffset), arrowConeOffset)); + Overlays.editOverlay(handleTranslateZCone, { + position: coneZPosition, + rotation: rotationZ, + dimensions: arrowConeDimensions + }); + + // UPDATE SCALE CUBES + var scaleCubeOffsetX = SCALE_CUBE_OFFSET * dimensions.x; + var scaleCubeOffsetY = SCALE_CUBE_OFFSET * dimensions.y; + var scaleCubeOffsetZ = SCALE_CUBE_OFFSET * dimensions.z; + var scaleCubeDimension = rotateDimension * SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE / + ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; + var scaleCubeDimensions = { x:scaleCubeDimension, y:scaleCubeDimension, z:scaleCubeDimension }; + var scaleCubeRotation = spaceMode === SPACE_LOCAL ? rotation : Quat.IDENTITY; + var scaleLBNCubePosition = { x:-scaleCubeOffsetX, y:-scaleCubeOffsetY, z:-scaleCubeOffsetZ }; + scaleLBNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLBNCubePosition)); + Overlays.editOverlay(handleScaleLBNCube, { + position: scaleLBNCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleRBNCubePosition = { x:-scaleCubeOffsetX, y:-scaleCubeOffsetY, z:scaleCubeOffsetZ }; + scaleRBNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRBNCubePosition)); + Overlays.editOverlay(handleScaleRBNCube, { + position: scaleRBNCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleLBFCubePosition = { x:scaleCubeOffsetX, y:-scaleCubeOffsetY, z:-scaleCubeOffsetZ }; + scaleLBFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLBFCubePosition)); + Overlays.editOverlay(handleScaleLBFCube, { + position: scaleLBFCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleRBFCubePosition = { x:scaleCubeOffsetX, y:-scaleCubeOffsetY, z:scaleCubeOffsetZ }; + scaleRBFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRBFCubePosition)); + Overlays.editOverlay(handleScaleRBFCube, { + position: scaleRBFCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleLTNCubePosition = { x:-scaleCubeOffsetX, y:scaleCubeOffsetY, z:-scaleCubeOffsetZ }; + scaleLTNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLTNCubePosition)); + Overlays.editOverlay(handleScaleLTNCube, { + position: scaleLTNCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleRTNCubePosition = { x:-scaleCubeOffsetX, y:scaleCubeOffsetY, z:scaleCubeOffsetZ }; + scaleRTNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRTNCubePosition)); + Overlays.editOverlay(handleScaleRTNCube, { + position: scaleRTNCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleLTFCubePosition = { x:scaleCubeOffsetX, y:scaleCubeOffsetY, z:-scaleCubeOffsetZ }; + scaleLTFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLTFCubePosition)); + Overlays.editOverlay(handleScaleLTFCube, { + position: scaleLTFCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + var scaleRTFCubePosition = { x:scaleCubeOffsetX, y:scaleCubeOffsetY, z:scaleCubeOffsetZ }; + scaleRTFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRTFCubePosition)); + Overlays.editOverlay(handleScaleRTFCube, { + position: scaleRTFCubePosition, + rotation: scaleCubeRotation, + dimensions: scaleCubeDimensions + }); + + // UPDATE SCALE EDGES + Overlays.editOverlay(handleScaleTREdge, { start: scaleRTNCubePosition, end: scaleRTFCubePosition }); + Overlays.editOverlay(handleScaleTLEdge, { start: scaleLTNCubePosition, end: scaleLTFCubePosition }); + Overlays.editOverlay(handleScaleTFEdge, { start: scaleLTFCubePosition, end: scaleRTFCubePosition }); + Overlays.editOverlay(handleScaleTNEdge, { start: scaleLTNCubePosition, end: scaleRTNCubePosition }); + Overlays.editOverlay(handleScaleBREdge, { start: scaleRBNCubePosition, end: scaleRBFCubePosition }); + Overlays.editOverlay(handleScaleBLEdge, { start: scaleLBNCubePosition, end: scaleLBFCubePosition }); + Overlays.editOverlay(handleScaleBFEdge, { start: scaleLBFCubePosition, end: scaleRBFCubePosition }); + Overlays.editOverlay(handleScaleBNEdge, { start: scaleLBNCubePosition, end: scaleRBNCubePosition }); + Overlays.editOverlay(handleScaleNREdge, { start: scaleRTNCubePosition, end: scaleRBNCubePosition }); + Overlays.editOverlay(handleScaleNLEdge, { start: scaleLTNCubePosition, end: scaleLBNCubePosition }); + Overlays.editOverlay(handleScaleFREdge, { start: scaleRTFCubePosition, end: scaleRBFCubePosition }); + Overlays.editOverlay(handleScaleFLEdge, { start: scaleLTFCubePosition, end: scaleLBFCubePosition }); + + // UPDATE STRETCH SPHERES + var stretchSphereDimension = rotateDimension * STRETCH_SPHERE_CAMERA_DISTANCE_MULTIPLE / + ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; + var stretchSphereDimensions = { x:stretchSphereDimension, y:stretchSphereDimension, z:stretchSphereDimension }; + var stretchSphereOffset = rotateDimension * STRETCH_SPHERE_OFFSET / ROTATE_RING_CAMERA_DISTANCE_MULTIPLE; + var stretchXPosition = { x:stretchSphereOffset, y:0, z:0 }; + stretchXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchXPosition)); + Overlays.editOverlay(handleStretchXSphere, { + position: stretchXPosition, + dimensions: stretchSphereDimensions + }); + var stretchYPosition = { x:0, y:stretchSphereOffset, z:0 }; + stretchYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchYPosition)); + Overlays.editOverlay(handleStretchYSphere, { + position: stretchYPosition, + dimensions: stretchSphereDimensions + }); + var stretchZPosition = { x:0, y:0, z:stretchSphereOffset }; + stretchZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchZPosition)); + Overlays.editOverlay(handleStretchZSphere, { + position: stretchZPosition, + dimensions: stretchSphereDimensions + }); + + // UPDATE STRETCH HIGHLIGHT PANELS + var scaleLTFCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleLTFCubePosition); + var scaleRBFCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleRBFCubePosition); + var stretchPanelXDimensions = Vec3.subtract(scaleLTFCubePositionRotated, scaleRBFCubePositionRotated); + var tempY = Math.abs(stretchPanelXDimensions.y); + stretchPanelXDimensions.x = STRETCH_PANEL_WIDTH; + stretchPanelXDimensions.y = Math.abs(stretchPanelXDimensions.z); + stretchPanelXDimensions.z = tempY; + var stretchPanelXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:dimensions.x / 2, y:0, z:0 })); + Overlays.editOverlay(handleStretchXPanel, { + position: stretchPanelXPosition, + rotation: rotationZ, + dimensions: stretchPanelXDimensions + }); + var scaleLTNCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleLTNCubePosition); + var scaleRTFCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleRTFCubePosition); + var stretchPanelYDimensions = Vec3.subtract(scaleLTNCubePositionRotated, scaleRTFCubePositionRotated); + var tempX = Math.abs(stretchPanelYDimensions.x); + stretchPanelYDimensions.x = Math.abs(stretchPanelYDimensions.z); + stretchPanelYDimensions.y = STRETCH_PANEL_WIDTH; + stretchPanelYDimensions.z = tempX; + var stretchPanelYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:dimensions.y / 2, z:0 })); + Overlays.editOverlay(handleStretchYPanel, { + position: stretchPanelYPosition, + rotation: rotationY, + dimensions: stretchPanelYDimensions + }); + var scaleRTFCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleRTFCubePosition); + var scaleRBNCubePositionRotated = Vec3.multiplyQbyV(rotationInverse, scaleRBNCubePosition); + var stretchPanelZDimensions = Vec3.subtract(scaleRTFCubePositionRotated, scaleRBNCubePositionRotated); + var tempX = Math.abs(stretchPanelZDimensions.x); + stretchPanelZDimensions.x = Math.abs(stretchPanelZDimensions.y); + stretchPanelZDimensions.y = tempX; + stretchPanelZDimensions.z = STRETCH_PANEL_WIDTH; + var stretchPanelZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:dimensions.z / 2 })); + Overlays.editOverlay(handleStretchZPanel, { + position: stretchPanelZPosition, + rotation: rotationX, + dimensions: stretchPanelZDimensions + }); + + // UPDATE SELECTION BOX (CURRENTLY INVISIBLE WITH 0 ALPHA FOR TRANSLATE XZ TOOL) + var inModeRotate = isActiveTool(handleRotatePitchRing) || + isActiveTool(handleRotateYawRing) || + isActiveTool(handleRotateRollRing); + Overlays.editOverlay(selectionBox, { + position: position, + rotation: rotation, + dimensions: dimensions, + visible: !inModeRotate + }); + + // UPDATE CLONER (CURRENTLY HIDDEN FOR NOW) + var handleClonerOffset = { + x:CLONER_OFFSET.x * dimensions.x, + y:CLONER_OFFSET.y * dimensions.y, + z:CLONER_OFFSET.z * dimensions.z + }; + var handleClonerPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, handleClonerOffset)); + Overlays.editOverlay(handleCloner, { + position: handleClonerPos, + rotation: rotation, + dimensions: scaleCubeDimensions }); } - Overlays.editOverlay(grabberEdgeTR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeTR - }); - Overlays.editOverlay(grabberEdgeTL, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeTL - }); - Overlays.editOverlay(grabberEdgeTF, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeTF - }); - Overlays.editOverlay(grabberEdgeTN, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeTN - }); - Overlays.editOverlay(grabberEdgeBR, { - visible: stretchHandlesVisible, - rotation: rotation, - position: EdgeBR - }); - Overlays.editOverlay(grabberEdgeBL, { - visible: stretchHandlesVisible, - rotation: rotation, - position: EdgeBL - }); - Overlays.editOverlay(grabberEdgeBF, { - visible: stretchHandlesVisible, - rotation: rotation, - position: EdgeBF - }); - Overlays.editOverlay(grabberEdgeBN, { - visible: stretchHandlesVisible, - rotation: rotation, - position: EdgeBN - }); - Overlays.editOverlay(grabberEdgeNR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeNR - }); - Overlays.editOverlay(grabberEdgeNL, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeNL - }); - Overlays.editOverlay(grabberEdgeFR, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeFR - }); - Overlays.editOverlay(grabberEdgeFL, { - visible: extendedStretchHandlesVisible, - rotation: rotation, - position: EdgeFL - }); + that.setHandleTranslateXVisible(!activeTool || isActiveTool(handleTranslateXCone) || + isActiveTool(handleTranslateXCylinder)); + that.setHandleTranslateYVisible(!activeTool || isActiveTool(handleTranslateYCone) || + isActiveTool(handleTranslateYCylinder)); + that.setHandleTranslateZVisible(!activeTool || isActiveTool(handleTranslateZCone) || + isActiveTool(handleTranslateZCylinder)); + that.setHandleRotatePitchVisible(!activeTool || isActiveTool(handleRotatePitchRing)); + that.setHandleRotateYawVisible(!activeTool || isActiveTool(handleRotateYawRing)); + that.setHandleRotateRollVisible(!activeTool || isActiveTool(handleRotateRollRing)); - var grabberMoveUpOffset = 0.1; - var upVec = Quat.getUp(MyAvatar.orientation); - grabberMoveUpPosition = { - x: position.x + (grabberMoveUpOffset + worldTop) * upVec.x , - y: position.y+ (grabberMoveUpOffset + worldTop) * upVec.y, - z: position.z + (grabberMoveUpOffset + worldTop) * upVec.z - }; - Overlays.editOverlay(grabberMoveUp, { - visible: (!activeTool) || isActiveTool(grabberMoveUp) - }); + var showScaleStretch = !activeTool && SelectionManager.selections.length === 1; + that.setHandleStretchXVisible(showScaleStretch || isActiveTool(handleStretchXSphere)); + that.setHandleStretchYVisible(showScaleStretch || isActiveTool(handleStretchYSphere)); + that.setHandleStretchZVisible(showScaleStretch || isActiveTool(handleStretchZSphere)); + that.setHandleScaleCubeVisible(showScaleStretch || isActiveTool(handleScaleLBNCube) || + isActiveTool(handleScaleRBNCube) || isActiveTool(handleScaleLBFCube) || + isActiveTool(handleScaleRBFCube) || isActiveTool(handleScaleLTNCube) || + isActiveTool(handleScaleRTNCube) || isActiveTool(handleScaleLTFCube) || + isActiveTool(handleScaleRTFCube) || isActiveTool(handleStretchXSphere) || + isActiveTool(handleStretchYSphere) || isActiveTool(handleStretchZSphere)); + that.setHandleScaleEdgeVisible(!isActiveTool(handleRotatePitchRing) && !isActiveTool(handleRotateYawRing) && + !isActiveTool(handleRotateRollRing)); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { - visible: !inModeRotate, - solid: true, - position: { - x: SelectionManager.worldPosition.x, - y: grid.getOrigin().y, - z: SelectionManager.worldPosition.z - }, - dimensions: { - x: SelectionManager.worldDimensions.x, - y: SelectionManager.worldDimensions.z - }, - rotation: Quat.fromPitchYawRollDegrees(90, 0, 0) - }); + //keep cloner always hidden for now since you can hold Alt to clone while + //translating an entity - we may bring cloner back for HMD only later + //that.setHandleClonerVisible(!activeTool || isActiveTool(handleCloner)); if (wantDebug) { print("====== Update Handles <======="); } - }; + Script.update.connect(that.updateHandles); - function helperSetOverlaysVisibility(handleArray, isVisible) { - var numHandles = handleArray.length; - var visibilityUpdate = { visible: isVisible }; - for (var handleIndex = 0; handleIndex < numHandles; ++handleIndex) { - Overlays.editOverlay(handleArray[ handleIndex ], visibilityUpdate); + // FUNCTION: UPDATE ACTIVE ROTATE RING + that.updateActiveRotateRing = function() { + var activeRotateRing = null; + if (isActiveTool(handleRotatePitchRing)) { + activeRotateRing = handleRotatePitchRing; + } else if (isActiveTool(handleRotateYawRing)) { + activeRotateRing = handleRotateYawRing; + } else if (isActiveTool(handleRotateRollRing)) { + activeRotateRing = handleRotateRollRing; } - } + if (activeRotateRing != null) { + var tickMarksAngle = ctrlPressed ? ROTATE_CTRL_SNAP_ANGLE : ROTATE_DEFAULT_TICK_MARKS_ANGLE; + Overlays.editOverlay(activeRotateRing, { majorTickMarksAngle: tickMarksAngle }); + } + }; // FUNCTION: SET OVERLAYS VISIBLE that.setOverlaysVisible = function(isVisible) { - helperSetOverlaysVisibility(allOverlays, isVisible); - helperSetOverlaysVisibility(selectionBoxes, isVisible); - }; - - // FUNCTION: SET ROTATION HANDLES VISIBLE - that.setRotationHandlesVisible = function(isVisible) { - var visibilityUpdate = { visible: isVisible }; - Overlays.editOverlay(yawHandle, visibilityUpdate); - Overlays.editOverlay(pitchHandle, visibilityUpdate); - Overlays.editOverlay(rollHandle, visibilityUpdate); - }; - - // FUNCTION: SET STRETCH HANDLES VISIBLE - that.setStretchHandlesVisible = function(isVisible) { - helperSetOverlaysVisibility(stretchHandles, isVisible); - }; - - // FUNCTION: SET GRABBER MOVE UP VISIBLE - that.setGrabberMoveUpVisible = function(isVisible) { - Overlays.editOverlay(grabberMoveUp, { visible: isVisible }); - }; - - // FUNCTION: SET GRABBER TOOLS UP VISIBLE - that.setGrabberToolsVisible = function(isVisible) { - var visibilityUpdate = { visible: isVisible }; - for (var toolKey in grabberTools) { - if (!grabberTools.hasOwnProperty(toolKey)) { - // EARLY ITERATION EXIT--(On to the next one) - continue; - } - - Overlays.editOverlay(toolKey, visibilityUpdate); + for (var i = 0; i < allOverlays.length; i++) { + Overlays.editOverlay(allOverlays[i], { visible: isVisible }); } }; - // FUNCTION: SET POINT LIGHT HANDLES VISIBLE - that.setPointLightHandlesVisible = function(isVisible) { - helperSetOverlaysVisibility(pointLightGrabberHandles, isVisible); + // FUNCTION: SET HANDLE TRANSLATE VISIBLE + that.setHandleTranslateVisible = function(isVisible) { + that.setHandleTranslateXVisible(isVisible); + that.setHandleTranslateYVisible(isVisible); + that.setHandleTranslateZVisible(isVisible); }; - // FUNCTION: SET SPOT LIGHT HANDLES VISIBLE - that.setSpotLightHandlesVisible = function(isVisible) { - helperSetOverlaysVisibility(spotLightGrabberHandles, isVisible); + that.setHandleTranslateXVisible = function(isVisible) { + Overlays.editOverlay(handleTranslateXCone, { visible: isVisible }); + Overlays.editOverlay(handleTranslateXCylinder, { visible: isVisible }); }; - // FUNCTION: UNSELECT - // TODO?: Needs implementation - that.unselect = function(entityID) {}; + that.setHandleTranslateYVisible = function(isVisible) { + Overlays.editOverlay(handleTranslateYCone, { visible: isVisible }); + Overlays.editOverlay(handleTranslateYCylinder, { visible: isVisible }); + }; + that.setHandleTranslateZVisible = function(isVisible) { + Overlays.editOverlay(handleTranslateZCone, { visible: isVisible }); + Overlays.editOverlay(handleTranslateZCylinder, { visible: isVisible }); + }; + + // FUNCTION: SET HANDLE ROTATE VISIBLE + that.setHandleRotateVisible = function(isVisible) { + that.setHandleRotatePitchVisible(isVisible); + that.setHandleRotateYawVisible(isVisible); + that.setHandleRotateRollVisible(isVisible); + }; + + that.setHandleRotatePitchVisible = function(isVisible) { + Overlays.editOverlay(handleRotatePitchRing, { visible: isVisible }); + }; + + that.setHandleRotateYawVisible = function(isVisible) { + Overlays.editOverlay(handleRotateYawRing, { visible: isVisible }); + }; + + that.setHandleRotateRollVisible = function(isVisible) { + Overlays.editOverlay(handleRotateRollRing, { visible: isVisible }); + }; + + // FUNCTION: SET HANDLE STRETCH VISIBLE + that.setHandleStretchVisible = function(isVisible) { + that.setHandleStretchXVisible(isVisible); + that.setHandleStretchYVisible(isVisible); + that.setHandleStretchZVisible(isVisible); + }; + + that.setHandleStretchXVisible = function(isVisible) { + Overlays.editOverlay(handleStretchXSphere, { visible: isVisible }); + }; + + that.setHandleStretchYVisible = function(isVisible) { + Overlays.editOverlay(handleStretchYSphere, { visible: isVisible }); + }; + + that.setHandleStretchZVisible = function(isVisible) { + Overlays.editOverlay(handleStretchZSphere, { visible: isVisible }); + }; + + // FUNCTION: SET HANDLE SCALE VISIBLE + that.setHandleScaleVisible = function(isVisible) { + that.setHandleScaleCubeVisible(isVisible); + that.setHandleScaleEdgeVisible(isVisible); + }; + + that.setHandleScaleCubeVisible = function(isVisible) { + Overlays.editOverlay(handleScaleLBNCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleRBNCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleLBFCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleRBFCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleLTNCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleRTNCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleLTFCube, { visible: isVisible }); + Overlays.editOverlay(handleScaleRTFCube, { visible: isVisible }); + }; + + that.setHandleScaleEdgeVisible = function(isVisible) { + Overlays.editOverlay(handleScaleTREdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleTLEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleTFEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleTNEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleBREdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleBLEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleBFEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleBNEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleNREdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleNLEdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleFREdge, { visible: isVisible }); + Overlays.editOverlay(handleScaleFLEdge, { visible: isVisible }); + }; + + // FUNCTION: SET HANDLE CLONER VISIBLE + that.setHandleClonerVisible = function(isVisible) { + Overlays.editOverlay(handleCloner, { visible: isVisible }); + }; + + // TOOL DEFINITION: TRANSLATE XZ TOOL var initialXZPick = null; var isConstrained = false; var constrainMajorOnly = false; var startPosition = null; var duplicatedEntityIDs = null; - - // TOOL DEFINITION: TRANSLATE XZ TOOL - var translateXZTool = addGrabberTool(selectionBox,{ + var translateXZTool = addHandleTool(selectionBox, { mode: 'TRANSLATE_XZ', pickPlanePosition: { x: 0, y: 0, z: 0 }, greatestDimension: 0.0, @@ -2051,14 +1472,20 @@ SelectionDisplay = (function() { } SelectionManager.saveProperties(); - that.setRotationHandlesVisible(false); - that.setStretchHandlesVisible(false); - that.setGrabberMoveUpVisible(false); + that.resetPreviousHandleColor(); + + that.setHandleTranslateVisible(false); + that.setHandleRotateVisible(false); + that.setHandleScaleCubeVisible(false); + that.setHandleStretchVisible(false); + that.setHandleClonerVisible(false); startPosition = SelectionManager.worldPosition; translateXZTool.pickPlanePosition = pickResult.intersection; - translateXZTool.greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x, SelectionManager.worldDimensions.y), SelectionManager.worldDimensions.z); + translateXZTool.greatestDimension = Math.max(Math.max(SelectionManager.worldDimensions.x, + SelectionManager.worldDimensions.y), + SelectionManager.worldDimensions.z); translateXZTool.startingDistance = Vec3.distance(pickRay.origin, SelectionManager.position); translateXZTool.startingElevation = translateXZTool.elevation(pickRay.origin, translateXZTool.pickPlanePosition); if (wantDebug) { @@ -2099,13 +1526,6 @@ SelectionDisplay = (function() { }, onEnd: function(event, reason) { pushCommandForSelections(duplicatedEntityIDs); - - Overlays.editOverlay(xRailOverlay, { - visible: false - }); - Overlays.editOverlay(zRailOverlay, { - visible: false - }); }, elevation: function(origin, intersection) { return (origin.y - intersection.y) / Vec3.distance(origin, intersection); @@ -2169,49 +1589,10 @@ SelectionDisplay = (function() { vector.x = 0; } if (!isConstrained) { - Overlays.editOverlay(xRailOverlay, { - visible: true - }); - var xStart = Vec3.sum(startPosition, { - x: -10000, - y: 0, - z: 0 - }); - var xEnd = Vec3.sum(startPosition, { - x: 10000, - y: 0, - z: 0 - }); - var zStart = Vec3.sum(startPosition, { - x: 0, - y: 0, - z: -10000 - }); - var zEnd = Vec3.sum(startPosition, { - x: 0, - y: 0, - z: 10000 - }); - Overlays.editOverlay(xRailOverlay, { - start: xStart, - end: xEnd, - visible: true - }); - Overlays.editOverlay(zRailOverlay, { - start: zStart, - end: zEnd, - visible: true - }); isConstrained = true; } } else { if (isConstrained) { - Overlays.editOverlay(xRailOverlay, { - visible: false - }); - Overlays.editOverlay(zRailOverlay, { - visible: false - }); isConstrained = false; } } @@ -2222,7 +1603,6 @@ SelectionDisplay = (function() { grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly), cornerPosition); - for (var i = 0; i < SelectionManager.selections.length; i++) { var properties = SelectionManager.savedProperties[SelectionManager.selections[i]]; if (!properties) { @@ -2248,106 +1628,102 @@ SelectionDisplay = (function() { SelectionManager._update(); } }); - - // GRABBER TOOL: GRABBER MOVE UP - var lastXYPick = null; - var upDownPickNormal = null; - addGrabberTool(grabberMoveUp, { - mode: "TRANSLATE_UP_DOWN", - onBegin: function(event, pickRay, pickResult) { - upDownPickNormal = Quat.getForward(lastCameraOrientation); - lastXYPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal); - SelectionManager.saveProperties(); - that.setGrabberMoveUpVisible(true); - that.setStretchHandlesVisible(false); - that.setRotationHandlesVisible(false); - - // Duplicate entities if alt is pressed. This will make a - // copy of the selected entities and move the _original_ entities, not - // the new ones. - if (event.isAlt) { - duplicatedEntityIDs = []; - for (var otherEntityID in SelectionManager.savedProperties) { - var properties = SelectionManager.savedProperties[otherEntityID]; - if (!properties.locked) { - var entityID = Entities.addEntity(properties); - duplicatedEntityIDs.push({ - entityID: entityID, - properties: properties - }); - } + // TOOL DEFINITION: HANDLE TRANSLATE TOOL + function addHandleTranslateTool(overlay, mode, direction) { + var pickNormal = null; + var lastPick = null; + var projectionVector = null; + addHandleTool(overlay, { + mode: mode, + onBegin: function(event, pickRay, pickResult) { + if (direction === TRANSLATE_DIRECTION.X) { + pickNormal = { x:0, y:0, z:1 }; + } else if (direction === TRANSLATE_DIRECTION.Y) { + pickNormal = { x:1, y:0, z:0 }; + } else if (direction === TRANSLATE_DIRECTION.Z) { + pickNormal = { x:0, y:1, z:0 }; } - } else { - duplicatedEntityIDs = null; + + var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; + pickNormal = Vec3.multiplyQbyV(rotation, pickNormal); + + lastPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); + + SelectionManager.saveProperties(); + that.resetPreviousHandleColor(); + + that.setHandleTranslateXVisible(direction === TRANSLATE_DIRECTION.X); + that.setHandleTranslateYVisible(direction === TRANSLATE_DIRECTION.Y); + that.setHandleTranslateZVisible(direction === TRANSLATE_DIRECTION.Z); + that.setHandleRotateVisible(false); + that.setHandleStretchVisible(false); + that.setHandleScaleCubeVisible(false); + that.setHandleClonerVisible(false); + + // Duplicate entities if alt is pressed. This will make a + // copy of the selected entities and move the _original_ entities, not + // the new ones. + if (event.isAlt) { + duplicatedEntityIDs = []; + for (var otherEntityID in SelectionManager.savedProperties) { + var properties = SelectionManager.savedProperties[otherEntityID]; + if (!properties.locked) { + var entityID = Entities.addEntity(properties); + duplicatedEntityIDs.push({ + entityID: entityID, + properties: properties + }); + } + } + } else { + duplicatedEntityIDs = null; + } + }, + onEnd: function(event, reason) { + pushCommandForSelections(duplicatedEntityIDs); + }, + onMove: function(event) { + pickRay = generalComputePickRay(event.x, event.y); + + // translate mode left/right based on view toward entity + var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal); + var vector = Vec3.subtract(newIntersection, lastPick); + + if (direction === TRANSLATE_DIRECTION.X) { + projectionVector = { x:1, y:0, z:0 }; + } else if (direction === TRANSLATE_DIRECTION.Y) { + projectionVector = { x:0, y:1, z:0 }; + } else if (direction === TRANSLATE_DIRECTION.Z) { + projectionVector = { x:0, y:0, z:1 }; + } + + var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; + projectionVector = Vec3.multiplyQbyV(rotation, projectionVector); + + var dotVector = Vec3.dot(vector, projectionVector); + vector = Vec3.multiply(dotVector, projectionVector); + vector = grid.snapToGrid(vector); + + var wantDebug = false; + if (wantDebug) { + print("translateUpDown... "); + print(" event.y:" + event.y); + Vec3.print(" newIntersection:", newIntersection); + Vec3.print(" vector:", vector); + } + + for (var i = 0; i < SelectionManager.selections.length; i++) { + var id = SelectionManager.selections[i]; + var properties = SelectionManager.savedProperties[id]; + var newPosition = Vec3.sum(properties.position, vector); + Entities.editEntity(id, { position: newPosition }); + } + + SelectionManager._update(); } - }, - onEnd: function(event, reason) { - pushCommandForSelections(duplicatedEntityIDs); - }, - onMove: function(event) { - pickRay = generalComputePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal); - - var vector = Vec3.subtract(newIntersection, lastXYPick); - - // project vector onto avatar up vector - // we want the avatar referential not the camera. - var avatarUpVector = Quat.getUp(MyAvatar.orientation); - var dotVectorUp = Vec3.dot(vector, avatarUpVector); - vector = Vec3.multiply(dotVectorUp, avatarUpVector); - - - vector = grid.snapToGrid(vector); - - - - var wantDebug = false; - if (wantDebug) { - print("translateUpDown... "); - print(" event.y:" + event.y); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - // Vec3.print(" newPosition:", newPosition); - } - for (var i = 0; i < SelectionManager.selections.length; i++) { - var id = SelectionManager.selections[i]; - var properties = SelectionManager.savedProperties[id]; - - var original = properties.position; - var newPosition = Vec3.sum(properties.position, vector); - - Entities.editEntity(id, { - position: newPosition - }); - } - - SelectionManager._update(); - } - }); - - // GRABBER TOOL: GRABBER CLONER - addGrabberTool(grabberCloner, { - mode: "CLONE", - onBegin: function(event, pickRay, pickResult) { - var doClone = true; - translateXZTool.onBegin(event,pickRay,pickResult,doClone); - }, - elevation: function (event) { - translateXZTool.elevation(event); - }, - - onEnd: function (event) { - translateXZTool.onEnd(event); - }, - - onMove: function (event) { - translateXZTool.onMove(event); - } - }); - + }); + } // FUNCTION: VEC 3 MULT var vec3Mult = function(v1, v2) { @@ -2358,36 +1734,24 @@ SelectionDisplay = (function() { }; }; - // FUNCTION: MAKE STRETCH TOOL - // stretchMode - name of mode - // direction - direction to stretch in - // pivot - point to use as a pivot - // offset - the position of the overlay tool relative to the selections center position - // @return: tool obj - var makeStretchTool = function(stretchMode, direction, pivot, offset, customOnMove) { - // directionFor3DStretch - direction and pivot for 3D stretch - // distanceFor3DStretch - distance from the intersection point and the handController - // used to increase the scale taking into account the distance to the object - // DISTANCE_INFLUENCE_THRESHOLD - constant that holds the minimum distance where the - // distance to the object will influence the stretch/resize/scale - var directionFor3DStretch = getDirectionsFor3DStretch(stretchMode); + // TOOL DEFINITION: HANDLE STRETCH TOOL + function makeStretchTool(stretchMode, directionEnum, directionVec, pivot, offset, stretchPanel, scaleHandle) { + var directionFor3DStretch = directionVec; var distanceFor3DStretch = 0; var DISTANCE_INFLUENCE_THRESHOLD = 1.2; - var signs = { - x: direction.x < 0 ? -1 : (direction.x > 0 ? 1 : 0), - y: direction.y < 0 ? -1 : (direction.y > 0 ? 1 : 0), - z: direction.z < 0 ? -1 : (direction.z > 0 ? 1 : 0) + x: directionVec.x < 0 ? -1 : (directionVec.x > 0 ? 1 : 0), + y: directionVec.y < 0 ? -1 : (directionVec.y > 0 ? 1 : 0), + z: directionVec.z < 0 ? -1 : (directionVec.z > 0 ? 1 : 0) }; var mask = { - x: Math.abs(direction.x) > 0 ? 1 : 0, - y: Math.abs(direction.y) > 0 ? 1 : 0, - z: Math.abs(direction.z) > 0 ? 1 : 0 + x: Math.abs(directionVec.x) > 0 ? 1 : 0, + y: Math.abs(directionVec.y) > 0 ? 1 : 0, + z: Math.abs(directionVec.z) > 0 ? 1 : 0 }; - var numDimensions = mask.x + mask.y + mask.z; var planeNormal = null; @@ -2467,11 +1831,6 @@ SelectionDisplay = (function() { z: 0 }); end = Vec3.sum(end, properties.position); - Overlays.editOverlay(xRailOverlay, { - start: start, - end: end, - visible: true - }); } if ((numDimensions === 1) && mask.y) { start = Vec3.multiplyQbyV(rotation, { @@ -2486,11 +1845,6 @@ SelectionDisplay = (function() { z: 0 }); end = Vec3.sum(end, properties.position); - Overlays.editOverlay(yRailOverlay, { - start: start, - end: end, - visible: true - }); } if ((numDimensions === 1) && mask.z) { start = Vec3.multiplyQbyV(rotation, { @@ -2505,11 +1859,6 @@ SelectionDisplay = (function() { z: 10000 }); end = Vec3.sum(end, properties.position); - Overlays.editOverlay(zRailOverlay, { - start: start, - end: end, - visible: true - }); } if (numDimensions === 1) { if (mask.x === 1) { @@ -2569,27 +1918,39 @@ SelectionDisplay = (function() { planeNormal3D); distanceFor3DStretch = Vec3.length(Vec3.subtract(pickRayPosition3D, pickRay.origin)); } + + that.setHandleTranslateVisible(false); + that.setHandleRotateVisible(false); + that.setHandleScaleCubeVisible(true); + that.setHandleStretchXVisible(directionEnum === STRETCH_DIRECTION.X); + that.setHandleStretchYVisible(directionEnum === STRETCH_DIRECTION.Y); + that.setHandleStretchZVisible(directionEnum === STRETCH_DIRECTION.Z); + that.setHandleClonerVisible(false); SelectionManager.saveProperties(); + that.resetPreviousHandleColor(); + + if (stretchPanel != null) { + Overlays.editOverlay(stretchPanel, { visible: true }); + } + if (scaleHandle != null) { + Overlays.editOverlay(scaleHandle, { color: COLOR_SCALE_CUBE_SELECTED }); + } }; - var onEnd = function(event, reason) { - Overlays.editOverlay(xRailOverlay, { - visible: false - }); - Overlays.editOverlay(yRailOverlay, { - visible: false - }); - Overlays.editOverlay(zRailOverlay, { - visible: false - }); - + var onEnd = function(event, reason) { + if (stretchPanel != null) { + Overlays.editOverlay(stretchPanel, { visible: false }); + } + if (scaleHandle != null) { + Overlays.editOverlay(scaleHandle, { color: COLOR_SCALE_CUBE }); + } pushCommandForSelections(); }; var onMove = function(event) { - var proportional = (spaceMode === SPACE_WORLD) || event.isShifted || isActiveTool(grabberSpotLightRadius); - + var proportional = (spaceMode === SPACE_WORLD) || event.isShifted || directionEnum === STRETCH_DIRECTION.ALL; + var position, dimensions, rotation; if (spaceMode === SPACE_LOCAL) { position = SelectionManager.localPosition; @@ -2603,7 +1964,6 @@ SelectionDisplay = (function() { var localDeltaPivot = deltaPivot; var localSigns = signs; - var pickRay = generalComputePickRay(event.x, event.y); // Are we using handControllers or Mouse - only relevant for 3D tools @@ -2612,91 +1972,79 @@ SelectionDisplay = (function() { if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() && controllerPose.valid && that.triggered && directionFor3DStretch) { localDeltaPivot = deltaPivot3D; - newPick = pickRay.origin; - vector = Vec3.subtract(newPick, lastPick3D); - vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); - if (distanceFor3DStretch > DISTANCE_INFLUENCE_THRESHOLD) { // Range of Motion vector = Vec3.multiply(distanceFor3DStretch , vector); } - localSigns = directionFor3DStretch; - } else { - newPick = rayPlaneIntersection(pickRay, - pickRayPosition, - planeNormal); + newPick = rayPlaneIntersection(pickRay, pickRayPosition, planeNormal); vector = Vec3.subtract(newPick, lastPick); - vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); - vector = vec3Mult(mask, vector); - } - if (customOnMove) { - var change = Vec3.multiply(-1, vec3Mult(localSigns, vector)); - customOnMove(vector, change); - } else { - vector = grid.snapToSpacing(vector); - - var changeInDimensions = Vec3.multiply(-1, vec3Mult(localSigns, vector)); - var newDimensions; - if (proportional) { - var absX = Math.abs(changeInDimensions.x); - var absY = Math.abs(changeInDimensions.y); - var absZ = Math.abs(changeInDimensions.z); - var pctChange = 0; - if (absX > absY && absX > absZ) { - pctChange = changeInDimensions.x / initialProperties.dimensions.x; - pctChange = changeInDimensions.x / initialDimensions.x; - } else if (absY > absZ) { - pctChange = changeInDimensions.y / initialProperties.dimensions.y; - pctChange = changeInDimensions.y / initialDimensions.y; - } else { - pctChange = changeInDimensions.z / initialProperties.dimensions.z; - pctChange = changeInDimensions.z / initialDimensions.z; - } - pctChange += 1.0; - newDimensions = Vec3.multiply(pctChange, initialDimensions); - } else { - newDimensions = Vec3.sum(initialDimensions, changeInDimensions); - } - - newDimensions.x = Math.max(newDimensions.x, MINIMUM_DIMENSION); - newDimensions.y = Math.max(newDimensions.y, MINIMUM_DIMENSION); - newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION); - - var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(localDeltaPivot, changeInDimensions)); - var newPosition = Vec3.sum(initialPosition, changeInPosition); - - for (var i = 0; i < SelectionManager.selections.length; i++) { - Entities.editEntity(SelectionManager.selections[i], { - position: newPosition, - dimensions: newDimensions - }); - } - - - var wantDebug = false; - if (wantDebug) { - print(stretchMode); - // Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - // Vec3.print(" oldPOS:", oldPOS); - // Vec3.print(" newPOS:", newPOS); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } + vector = grid.snapToSpacing(vector); + + var changeInDimensions = Vec3.multiply(NEGATE_VECTOR, vec3Mult(localSigns, vector)); + if (directionEnum === STRETCH_DIRECTION.ALL) { + var toCameraDistance = getDistanceToCamera(position); + var dimensionsMultiple = toCameraDistance * STRETCH_DIRECTION_ALL_CAMERA_DISTANCE_MULTIPLE; + changeInDimensions = Vec3.multiply(changeInDimensions, dimensionsMultiple); } + var newDimensions; + if (proportional) { + var absoluteX = Math.abs(changeInDimensions.x); + var absoluteY = Math.abs(changeInDimensions.y); + var absoluteZ = Math.abs(changeInDimensions.z); + var percentChange = 0; + if (absoluteX > absoluteY && absoluteX > absoluteZ) { + percentChange = changeInDimensions.x / initialProperties.dimensions.x; + percentChange = changeInDimensions.x / initialDimensions.x; + } else if (absoluteY > absoluteZ) { + percentChange = changeInDimensions.y / initialProperties.dimensions.y; + percentChange = changeInDimensions.y / initialDimensions.y; + } else { + percentChange = changeInDimensions.z / initialProperties.dimensions.z; + percentChange = changeInDimensions.z / initialDimensions.z; + } + percentChange += 1.0; + newDimensions = Vec3.multiply(percentChange, initialDimensions); + } else { + newDimensions = Vec3.sum(initialDimensions, changeInDimensions); + } + + newDimensions.x = Math.max(newDimensions.x, STRETCH_MINIMUM_DIMENSION); + newDimensions.y = Math.max(newDimensions.y, STRETCH_MINIMUM_DIMENSION); + newDimensions.z = Math.max(newDimensions.z, STRETCH_MINIMUM_DIMENSION); + + var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(localDeltaPivot, changeInDimensions)); + if (directionEnum === STRETCH_DIRECTION.ALL) { + changeInPosition = { x:0, y:0, z:0 }; + } + var newPosition = Vec3.sum(initialPosition, changeInPosition); + + for (var i = 0; i < SelectionManager.selections.length; i++) { + Entities.editEntity(SelectionManager.selections[i], { + position: newPosition, + dimensions: newDimensions + }); + } + + var wantDebug = false; + if (wantDebug) { + print(stretchMode); + Vec3.print(" vector:", vector); + Vec3.print(" changeInDimensions:", changeInDimensions); + Vec3.print(" newDimensions:", newDimensions); + Vec3.print(" changeInPosition:", changeInPosition); + Vec3.print(" newPosition:", newPosition); + } + SelectionManager._update(); };// End of onMove def @@ -2706,600 +2054,85 @@ SelectionDisplay = (function() { onMove: onMove, onEnd: onEnd }; - }; - - // Direction for the stretch tool when using hand controller - var directionsFor3DGrab = { - LBN: { - x: 1, - y: 1, - z: 1 - }, - RBN: { - x: -1, - y: 1, - z: 1 - }, - LBF: { - x: 1, - y: 1, - z: -1 - }, - RBF: { - x: -1, - y: 1, - z: -1 - }, - LTN: { - x: 1, - y: -1, - z: 1 - }, - RTN: { - x: -1, - y: -1, - z: 1 - }, - LTF: { - x: 1, - y: -1, - z: -1 - }, - RTF: { - x: -1, - y: -1, - z: -1 - } - }; - - // FUNCTION: GET DIRECTION FOR 3D STRETCH - // Returns a vector with directions for the stretch tool in 3D using hand controllers - function getDirectionsFor3DStretch(mode) { - if (mode === "STRETCH_LBN") { - return directionsFor3DGrab.LBN; - } else if (mode === "STRETCH_RBN") { - return directionsFor3DGrab.RBN; - } else if (mode === "STRETCH_LBF") { - return directionsFor3DGrab.LBF; - } else if (mode === "STRETCH_RBF") { - return directionsFor3DGrab.RBF; - } else if (mode === "STRETCH_LTN") { - return directionsFor3DGrab.LTN; - } else if (mode === "STRETCH_RTN") { - return directionsFor3DGrab.RTN; - } else if (mode === "STRETCH_LTF") { - return directionsFor3DGrab.LTF; - } else if (mode === "STRETCH_RTF") { - return directionsFor3DGrab.RTF; - } else { - return null; - } - } - - - // FUNCTION: ADD STRETCH TOOL - function addStretchTool(overlay, mode, pivot, direction, offset, handleMove) { - if (!pivot) { - pivot = direction; - } - var tool = makeStretchTool(mode, direction, pivot, offset, handleMove); - - return addGrabberTool(overlay, tool); } - // FUNCTION: CUTOFF STRETCH FUNC - function cutoffStretchFunc(vector, change) { - vector = change; - var wantDebug = false; - if (wantDebug) { - Vec3.print("Radius stretch: ", vector); + function addHandleStretchTool(overlay, mode, directionEnum) { + var directionVector, offset, stretchPanel; + if (directionEnum === STRETCH_DIRECTION.X) { + stretchPanel = handleStretchXPanel; + directionVector = { x:-1, y:0, z:0 }; + } else if (directionEnum === STRETCH_DIRECTION.Y) { + stretchPanel = handleStretchYPanel; + directionVector = { x:0, y:-1, z:0 }; + } else if (directionEnum === STRETCH_DIRECTION.Z) { + stretchPanel = handleStretchZPanel + directionVector = { x:0, y:0, z:-1 }; } - var length = vector.x + vector.y + vector.z; - var props = SelectionManager.savedProperties[SelectionManager.selections[0]]; - - var radius = props.dimensions.z / 2; - var originalCutoff = props.cutoff; - - var originalSize = radius * Math.tan(originalCutoff * (Math.PI / 180)); - var newSize = originalSize + length; - var cutoff = Math.atan2(newSize, radius) * 180 / Math.PI; - - Entities.editEntity(SelectionManager.selections[0], { - cutoff: cutoff - }); - - SelectionManager._update(); + offset = Vec3.multiply(directionVector, NEGATE_VECTOR); + var tool = makeStretchTool(mode, directionEnum, directionVector, directionVector, offset, stretchPanel, null); + return addHandleTool(overlay, tool); } - // FUNCTION: RADIUS STRETCH FUNC - function radiusStretchFunc(vector, change) { - var props = SelectionManager.savedProperties[SelectionManager.selections[0]]; - - // Find the axis being adjusted - var size; - if (Math.abs(change.x) > 0) { - size = props.dimensions.x + change.x; - } else if (Math.abs(change.y) > 0) { - size = props.dimensions.y + change.y; - } else if (Math.abs(change.z) > 0) { - size = props.dimensions.z + change.z; + // TOOL DEFINITION: HANDLE SCALE TOOL + function addHandleScaleTool(overlay, mode, directionEnum) { + var directionVector, offset, selectedHandle; + if (directionEnum === SCALE_DIRECTION.LBN) { + directionVector = { x:1, y:1, z:1 }; + selectedHandle = handleScaleLBNCube; + } else if (directionEnum === SCALE_DIRECTION.RBN) { + directionVector = { x:1, y:1, z:-1 }; + selectedHandle = handleScaleRBNCube; + } else if (directionEnum === SCALE_DIRECTION.LBF) { + directionVector = { x:-1, y:1, z:1 }; + selectedHandle = handleScaleLBFCube; + } else if (directionEnum === SCALE_DIRECTION.RBF) { + directionVector = { x:-1, y:1, z:-1 }; + selectedHandle = handleScaleRBFCube; + } else if (directionEnum === SCALE_DIRECTION.LTN) { + directionVector = { x:1, y:-1, z:1 }; + selectedHandle = handleScaleLTNCube; + } else if (directionEnum === SCALE_DIRECTION.RTN) { + directionVector = { x:1, y:-1, z:-1 }; + selectedHandle = handleScaleRTNCube; + } else if (directionEnum === SCALE_DIRECTION.LTF) { + directionVector = { x:-1, y:-1, z:1 }; + selectedHandle = handleScaleLTFCube; + } else if (directionEnum === SCALE_DIRECTION.RTF) { + directionVector = { x:-1, y:-1, z:-1 }; + selectedHandle = handleScaleRTFCube; } - - var newDimensions = { - x: size, - y: size, - z: size - }; - - Entities.editEntity(SelectionManager.selections[0], { - dimensions: newDimensions - }); - - SelectionManager._update(); + offset = Vec3.multiply(directionVector, NEGATE_VECTOR); + var tool = makeStretchTool(mode, STRETCH_DIRECTION.ALL, directionVector, + directionVector, offset, null, selectedHandle); + return addHandleTool(overlay, tool); } - // STRETCH TOOL DEF SECTION - addStretchTool(grabberNEAR, "STRETCH_NEAR", { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: 0, - z: -1 - }); - addStretchTool(grabberFAR, "STRETCH_FAR", { - x: 0, - y: 0, - z: -1 - }, { - x: 0, - y: 0, - z: -1 - }, { - x: 0, - y: 0, - z: 1 - }); - addStretchTool(grabberTOP, "STRETCH_TOP", { - x: 0, - y: -1, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }); - addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { - x: 0, - y: 1, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }); - addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { - x: -1, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }); - addStretchTool(grabberLEFT, "STRETCH_LEFT", { - x: 1, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }); - - addStretchTool(grabberSpotLightRadius, "STRETCH_RADIUS", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: 0, - z: -1 - }); - addStretchTool(grabberSpotLightT, "STRETCH_CUTOFF_T", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }, cutoffStretchFunc); - addStretchTool(grabberSpotLightB, "STRETCH_CUTOFF_B", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }, cutoffStretchFunc); - addStretchTool(grabberSpotLightL, "STRETCH_CUTOFF_L", { - x: 0, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }, cutoffStretchFunc); - addStretchTool(grabberSpotLightR, "STRETCH_CUTOFF_R", { - x: 0, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }, cutoffStretchFunc); - - addStretchTool(grabberPointLightT, "STRETCH_RADIUS_T", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: -1, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightB, "STRETCH_RADIUS_B", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 1, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightL, "STRETCH_RADIUS_L", { - x: 0, - y: 0, - z: 0 - }, { - x: 1, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightR, "STRETCH_RADIUS_R", { - x: 0, - y: 0, - z: 0 - }, { - x: -1, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightF, "STRETCH_RADIUS_F", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: -1 - }, { - x: 0, - y: 0, - z: 1 - }, radiusStretchFunc); - addStretchTool(grabberPointLightN, "STRETCH_RADIUS_N", { - x: 0, - y: 0, - z: 0 - }, { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: 0, - z: -1 - }, radiusStretchFunc); - - addStretchTool(grabberLBN, "STRETCH_LBN", null, { - x: 1, - y: 0, - z: 1 - }, { - x: -1, - y: -1, - z: -1 - }); - addStretchTool(grabberRBN, "STRETCH_RBN", null, { - x: -1, - y: 0, - z: 1 - }, { - x: 1, - y: -1, - z: -1 - }); - addStretchTool(grabberLBF, "STRETCH_LBF", null, { - x: 1, - y: 0, - z: -1 - }, { - x: -1, - y: -1, - z: 1 - }); - addStretchTool(grabberRBF, "STRETCH_RBF", null, { - x: -1, - y: 0, - z: -1 - }, { - x: 1, - y: -1, - z: 1 - }); - addStretchTool(grabberLTN, "STRETCH_LTN", null, { - x: 1, - y: 0, - z: 1 - }, { - x: -1, - y: 1, - z: -1 - }); - addStretchTool(grabberRTN, "STRETCH_RTN", null, { - x: -1, - y: 0, - z: 1 - }, { - x: 1, - y: 1, - z: -1 - }); - addStretchTool(grabberLTF, "STRETCH_LTF", null, { - x: 1, - y: 0, - z: -1 - }, { - x: -1, - y: 1, - z: 1 - }); - addStretchTool(grabberRTF, "STRETCH_RTF", null, { - x: -1, - y: 0, - z: -1 - }, { - x: 1, - y: 1, - z: 1 - }); - - addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, { - x: 1, - y: 1, - z: 0 - }, { - x: 1, - y: 1, - z: 0 - }); - addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, { - x: -1, - y: 1, - z: 0 - }, { - x: -1, - y: 1, - z: 0 - }); - addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, { - x: 0, - y: 1, - z: -1 - }, { - x: 0, - y: 1, - z: -1 - }); - addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, { - x: 0, - y: 1, - z: 1 - }, { - x: 0, - y: 1, - z: 1 - }); - addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, { - x: -1, - y: 0, - z: 0 - }, { - x: 1, - y: -1, - z: 0 - }); - addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, { - x: 1, - y: 0, - z: 0 - }, { - x: -1, - y: -1, - z: 0 - }); - addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, { - x: 0, - y: 0, - z: -1 - }, { - x: 0, - y: -1, - z: -1 - }); - addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, { - x: 0, - y: 0, - z: 1 - }, { - x: 0, - y: -1, - z: 1 - }); - addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, { - x: -1, - y: 0, - z: 1 - }, { - x: 1, - y: 0, - z: -1 - }); - addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, { - x: 1, - y: 0, - z: 1 - }, { - x: -1, - y: 0, - z: -1 - }); - addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, { - x: -1, - y: 0, - z: -1 - }, { - x: 1, - y: 0, - z: 1 - }); - addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, { - x: 1, - y: 0, - z: -1 - }, { - x: -1, - y: 0, - z: 1 - }); - // FUNCTION: UPDATE ROTATION DEGREES OVERLAY - function updateRotationDegreesOverlay(angleFromZero, handleRotation, centerPosition) { - var wantDebug = false; - if (wantDebug) { - print("---> updateRotationDegreesOverlay ---"); - print(" AngleFromZero: " + angleFromZero); - print(" HandleRotation - X: " + handleRotation.x + " Y: " + handleRotation.y + " Z: " + handleRotation.z); - print(" CenterPos - " + centerPosition.x + " Y: " + centerPosition.y + " Z: " + centerPosition.z); - } - + function updateRotationDegreesOverlay(angleFromZero, position) { var angle = angleFromZero * (Math.PI / 180); - var position = { - x: Math.cos(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - y: Math.sin(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - z: 0 - }; - if (wantDebug) { - print(" Angle: " + angle); - print(" InitialPos: " + position.x + ", " + position.y + ", " + position.z); - } - - position = Vec3.multiplyQbyV(handleRotation, position); - position = Vec3.sum(centerPosition, position); + var toCameraDistance = getDistanceToCamera(position); var overlayProps = { position: position, dimensions: { - x: innerRadius * ROTATION_DISPLAY_SIZE_X_MULTIPLIER, - y: innerRadius * ROTATION_DISPLAY_SIZE_Y_MULTIPLIER + x: toCameraDistance * ROTATE_DISPLAY_SIZE_X_MULTIPLIER, + y: toCameraDistance * ROTATE_DISPLAY_SIZE_Y_MULTIPLIER }, - lineHeight: innerRadius * ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER, + lineHeight: toCameraDistance * ROTATE_DISPLAY_LINE_HEIGHT_MULTIPLIER, text: normalizeDegrees(-angleFromZero) + "°" }; - if (wantDebug) { - print(" TranslatedPos: " + position.x + ", " + position.y + ", " + position.z); - print(" OverlayDim - X: " + overlayProps.dimensions.x + " Y: " + overlayProps.dimensions.y + " Z: " + overlayProps.dimensions.z); - print(" OverlayLineHeight: " + overlayProps.lineHeight); - print(" OverlayText: " + overlayProps.text); - } - Overlays.editOverlay(rotationDegreesDisplay, overlayProps); - if (wantDebug) { - print("<--- updateRotationDegreesOverlay ---"); - } } // FUNCTION DEF: updateSelectionsRotation - // Helper func used by rotation grabber tools - function updateSelectionsRotation(rotationChange) { + // Helper func used by rotation handle tools + function updateSelectionsRotation(rotationChange, initialPosition) { if (!rotationChange) { print("ERROR: entitySelectionTool.updateSelectionsRotation - Invalid arg specified!!"); // EARLY EXIT return; } - + // Entities should only reposition if we are rotating multiple selections around // the selections center point. Otherwise, the rotation will be around the entities // registration point which does not need repositioning. @@ -3322,585 +2155,211 @@ SelectionDisplay = (function() { } } - function helperRotationHandleOnBegin(event, pickRay, rotAroundAxis, rotCenter, handleRotation) { - var wantDebug = false; - if (wantDebug) { - print("================== " + getMode() + "(rotation helper onBegin) -> ======================="); - } + // TOOL DEFINITION: HANDLE ROTATION TOOL + function addHandleRotateTool(overlay, mode, direction) { + var selectedHandle = null; + var worldRotation = null; + var rotationCenter = null; + var initialRotation = null; + addHandleTool(overlay, { + mode: mode, + onBegin: function(event, pickRay, pickResult) { + var wantDebug = false; + if (wantDebug) { + print("================== " + getMode() + "(addHandleRotateTool onBegin) -> ======================="); + } - SelectionManager.saveProperties(); - that.setRotationHandlesVisible(false); - that.setStretchHandlesVisible(false); - that.setGrabberMoveUpVisible(false); + SelectionManager.saveProperties(); + that.resetPreviousHandleColor(); + + that.setHandleTranslateVisible(false); + that.setHandleRotatePitchVisible(direction === ROTATE_DIRECTION.PITCH); + that.setHandleRotateYawVisible(direction === ROTATE_DIRECTION.YAW); + that.setHandleRotateRollVisible(direction === ROTATE_DIRECTION.ROLL); + that.setHandleStretchVisible(false); + that.setHandleScaleCubeVisible(false); + that.setHandleClonerVisible(false); - initialPosition = SelectionManager.worldPosition; - rotationNormal = { x: 0, y: 0, z: 0 }; - rotationNormal[rotAroundAxis] = 1; - //get the correct axis according to the avatar referencial - var avatarReferential = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({ - x: 0, - y: 0, - z: 0 - })); - rotationNormal = Vec3.multiplyQbyV(avatarReferential, rotationNormal); + if (direction === ROTATE_DIRECTION.PITCH) { + rotationNormal = { x: 1, y: 0, z: 0 }; + worldRotation = worldRotationY; + selectedHandle = handleRotatePitchRing; + } else if (direction === ROTATE_DIRECTION.YAW) { + rotationNormal = { x: 0, y: 1, z: 0 }; + worldRotation = worldRotationZ; + selectedHandle = handleRotateYawRing; + } else if (direction === ROTATE_DIRECTION.ROLL) { + rotationNormal = { x: 0, y: 0, z: 1 }; + worldRotation = worldRotationX; + selectedHandle = handleRotateRollRing; + } - // Size the overlays to the current selection size - var diagonal = (Vec3.length(SelectionManager.worldDimensions) / 2) * 1.1; - var halfDimensions = Vec3.multiply(SelectionManager.worldDimensions, 0.5); - innerRadius = diagonal; - outerRadius = diagonal * 1.15; - var innerAlpha = 0.2; - var outerAlpha = 0.2; - Overlays.editOverlay(rotateOverlayInner, { - visible: true, - rotation: handleRotation, - position: rotCenter, - size: innerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: innerAlpha - }); + Overlays.editOverlay(selectedHandle, { + hasTickMarks: true, + solid: false, + innerRadius: ROTATE_RING_SELECTED_INNER_RADIUS + }); - Overlays.editOverlay(rotateOverlayOuter, { - visible: true, - rotation: handleRotation, - position: rotCenter, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha - }); + initialRotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation; + rotationNormal = Vec3.multiplyQbyV(initialRotation, rotationNormal); - Overlays.editOverlay(rotateOverlayCurrent, { - visible: true, - rotation: handleRotation, - position: rotCenter, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9 - }); + rotationCenter = SelectionManager.worldPosition; - Overlays.editOverlay(rotationDegreesDisplay, { - visible: true - }); - - updateRotationDegreesOverlay(0, handleRotation, rotCenter); - - // editOverlays may not have committed rotation changes. - // Compute zero position based on where the overlay will be eventually. - var result = rayPlaneIntersection(pickRay, rotCenter, rotationNormal); - // In case of a parallel ray, this will be null, which will cause early-out - // in the onMove helper. - rotZero = result; - - if (wantDebug) { - print("================== " + getMode() + "(rotation helper onBegin) <- ======================="); - } - }// End_Function(helperRotationHandleOnBegin) - - function helperRotationHandleOnMove(event, rotAroundAxis, rotCenter, handleRotation) { - - if (!rotZero) { - print("ERROR: entitySelectionTool.handleRotationHandleOnMove - Invalid RotationZero Specified (missed rotation target plane?)"); - - // EARLY EXIT - return; - } - - var wantDebug = false; - if (wantDebug) { - print("================== "+ getMode() + "(rotation helper onMove) -> ======================="); - Vec3.print(" rotZero: ", rotZero); - } - var pickRay = generalComputePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { - visible: false - }); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { - visible: false - }); - - var result = rayPlaneIntersection(pickRay, rotCenter, rotationNormal); - if (result) { - var centerToZero = Vec3.subtract(rotZero, rotCenter); - var centerToIntersect = Vec3.subtract(result, rotCenter); - if (wantDebug) { - Vec3.print(" RotationNormal: ", rotationNormal); - Vec3.print(" rotZero: ", rotZero); - Vec3.print(" rotCenter: ", rotCenter); - Vec3.print(" intersect: ", result); - Vec3.print(" centerToZero: ", centerToZero); - Vec3.print(" centerToIntersect: ", centerToIntersect); - } - // Note: orientedAngle which wants normalized centerToZero and centerToIntersect - // handles that internally, so it's to pass unnormalized vectors here. - var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); - - var distanceFromCenter = Vec3.length(centerToIntersect); - var snapToInner = distanceFromCenter < innerRadius; - var snapAngle = snapToInner ? innerSnapAngle : 1.0; - angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - - - var rotChange = Quat.angleAxis(angleFromZero, rotationNormal); - updateSelectionsRotation(rotChange); - //present angle in avatar referencial - angleFromZero = -angleFromZero; - updateRotationDegreesOverlay(angleFromZero, handleRotation, rotCenter); - - // update the rotation display accordingly... - var startAtCurrent = 0; - var endAtCurrent = angleFromZero; - var startAtRemainder = angleFromZero; - var endAtRemainder = 360; - if (angleFromZero < 0) { - startAtCurrent = 360 + angleFromZero; - endAtCurrent = 360; - startAtRemainder = 0; - endAtRemainder = startAtCurrent; - } - if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { + Overlays.editOverlay(rotationDegreesDisplay, { visible: true }); + Overlays.editOverlay(handleRotateCurrentRing, { + position: rotationCenter, + rotation: worldRotation, startAt: 0, - endAt: 360 + endAt: 0, + visible: true }); - Overlays.editOverlay(rotateOverlayInner, { - startAt: startAtRemainder, - endAt: endAtRemainder + + // editOverlays may not have committed rotation changes. + // Compute zero position based on where the overlay will be eventually. + var result = rayPlaneIntersection(pickRay, rotationCenter, rotationNormal); + // In case of a parallel ray, this will be null, which will cause early-out + // in the onMove helper. + rotationZero = result; + + var rotationCenterToZero = Vec3.subtract(rotationZero, rotationCenter); + var rotationCenterToZeroLength = Vec3.length(rotationCenterToZero); + rotationDegreesPosition = Vec3.sum(rotationCenter, Vec3.multiply(Vec3.normalize(rotationCenterToZero), + rotationCenterToZeroLength * ROTATE_DISPLAY_DISTANCE_MULTIPLIER)); + updateRotationDegreesOverlay(0, rotationDegreesPosition); + + if (wantDebug) { + print("================== " + getMode() + "(addHandleRotateTool onBegin) <- ======================="); + } + }, + onEnd: function(event, reason) { + var wantDebug = false; + if (wantDebug) { + print("================== " + getMode() + "(addHandleRotateTool onEnd) -> ======================="); + } + Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); + Overlays.editOverlay(selectedHandle, { + hasTickMarks: false, + solid: true, + innerRadius: ROTATE_RING_IDLE_INNER_RADIUS }); - Overlays.editOverlay(rotateOverlayCurrent, { - startAt: startAtCurrent, - endAt: endAtCurrent, - size: innerRadius, - majorTickMarksAngle: innerSnapAngle, - minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, - minorTickMarksLength: 0 - }); - } else { - Overlays.editOverlay(rotateOverlayInner, { - startAt: 0, - endAt: 360 - }); - Overlays.editOverlay(rotateOverlayOuter, { - startAt: startAtRemainder, - endAt: endAtRemainder - }); - Overlays.editOverlay(rotateOverlayCurrent, { - startAt: startAtCurrent, - endAt: endAtCurrent, - size: outerRadius, - majorTickMarksAngle: 45.0, - minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, - minorTickMarksLength: 0.1 - }); - } - }// End_If(results.intersects) + Overlays.editOverlay(handleRotateCurrentRing, { visible: false }); + pushCommandForSelections(); + if (wantDebug) { + print("================== " + getMode() + "(addHandleRotateTool onEnd) <- ======================="); + } + }, + onMove: function(event) { + if (!rotationZero) { + print("ERROR: entitySelectionTool.addHandleRotateTool.onMove - " + + "Invalid RotationZero Specified (missed rotation target plane?)"); - if (wantDebug) { - print("================== "+ getMode() + "(rotation helper onMove) <- ======================="); - } - }// End_Function(helperRotationHandleOnMove) + // EARLY EXIT + return; + } + + var wantDebug = false; + if (wantDebug) { + print("================== "+ getMode() + "(addHandleRotateTool onMove) -> ======================="); + Vec3.print(" rotationZero: ", rotationZero); + } - function helperRotationHandleOnEnd() { - var wantDebug = false; - if (wantDebug) { - print("================== " + getMode() + "(onEnd) -> ======================="); - } - Overlays.editOverlay(rotateOverlayInner, { - visible: false - }); - Overlays.editOverlay(rotateOverlayOuter, { - visible: false - }); - Overlays.editOverlay(rotateOverlayCurrent, { - visible: false - }); - Overlays.editOverlay(rotationDegreesDisplay, { - visible: false - }); + var pickRay = generalComputePickRay(event.x, event.y); + var result = rayPlaneIntersection(pickRay, rotationCenter, rotationNormal); + if (result) { + var centerToZero = Vec3.subtract(rotationZero, rotationCenter); + var centerToIntersect = Vec3.subtract(result, rotationCenter); - pushCommandForSelections(); + if (wantDebug) { + Vec3.print(" RotationNormal: ", rotationNormal); + Vec3.print(" rotationZero: ", rotationZero); + Vec3.print(" rotationCenter: ", rotationCenter); + Vec3.print(" intersect: ", result); + Vec3.print(" centerToZero: ", centerToZero); + Vec3.print(" centerToIntersect: ", centerToIntersect); + } - if (wantDebug) { - print("================== " + getMode() + "(onEnd) <- ======================="); - } - }// End_Function(helperRotationHandleOnEnd) + // Note: orientedAngle which wants normalized centerToZero and centerToIntersect + // handles that internally, so it's to pass unnormalized vectors here. + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + var snapAngle = ctrlPressed ? ROTATE_CTRL_SNAP_ANGLE : ROTATE_DEFAULT_SNAP_ANGLE; + angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; + var rotationChange = Quat.angleAxis(angleFromZero, rotationNormal); + updateSelectionsRotation(rotationChange, rotationCenter); + updateRotationDegreesOverlay(-angleFromZero, rotationDegreesPosition); + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + } + Overlays.editOverlay(handleRotateCurrentRing, { + startAt: startAtCurrent, + endAt: endAtCurrent + }); - // YAW GRABBER TOOL DEFINITION - var initialPosition = SelectionManager.worldPosition; - addGrabberTool(yawHandle, { - mode: "ROTATE_YAW", - onBegin: function(event, pickRay, pickResult) { - helperRotationHandleOnBegin(event, pickRay, "y", yawCenter, yawHandleRotation); - }, - onEnd: function(event, reason) { - helperRotationHandleOnEnd(); - }, - onMove: function(event) { - helperRotationHandleOnMove(event, "y", yawCenter, yawHandleRotation); - } - }); + // not sure why but this seems to be needed to fix an reverse rotation for yaw ring only + if (direction === ROTATE_DIRECTION.YAW) { + if (spaceMode === SPACE_LOCAL) { + Overlays.editOverlay(handleRotateCurrentRing, { rotation: worldRotationZ }); + } else { + Overlays.editOverlay(handleRotateCurrentRing, { + rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0) + }); + } + } + } - - // PITCH GRABBER TOOL DEFINITION - addGrabberTool(pitchHandle, { - mode: "ROTATE_PITCH", - onBegin: function(event, pickRay, pickResult) { - helperRotationHandleOnBegin(event, pickRay, "x", pitchCenter, pitchHandleRotation); - }, - onEnd: function(event, reason) { - helperRotationHandleOnEnd(); - }, - onMove: function (event) { - helperRotationHandleOnMove(event, "x", pitchCenter, pitchHandleRotation); - } - }); - - - // ROLL GRABBER TOOL DEFINITION - addGrabberTool(rollHandle, { - mode: "ROTATE_ROLL", - onBegin: function(event, pickRay, pickResult) { - helperRotationHandleOnBegin(event, pickRay, "z", rollCenter, rollHandleRotation); - }, - onEnd: function (event, reason) { - helperRotationHandleOnEnd(); - }, - onMove: function(event) { - helperRotationHandleOnMove(event, "z", rollCenter, rollHandleRotation); - } - }); - - // FUNCTION: CHECK MOVE - that.checkMove = function() { - if (SelectionManager.hasSelection()) { - - // FIXME - this cause problems with editing in the entity properties window - // SelectionManager._update(); - - if (!Vec3.equal(Camera.getPosition(), lastCameraPosition) || - !Quat.equal(Camera.getOrientation(), lastCameraOrientation)) { - - that.updateRotationHandles(); - } - } - }; - - that.checkControllerMove = function() { - if (SelectionManager.hasSelection()) { - var controllerPose = getControllerWorldLocation(activeHand, true); - var hand = (activeHand === Controller.Standard.LeftHand) ? 0 : 1; - if (controllerPose.valid && lastControllerPoses[hand].valid) { - if (!Vec3.equal(controllerPose.position, lastControllerPoses[hand].position) || - !Vec3.equal(controllerPose.rotation, lastControllerPoses[hand].rotation)) { - that.mouseMoveEvent({}); + if (wantDebug) { + print("================== "+ getMode() + "(addHandleRotateTool onMove) <- ======================="); } } - lastControllerPoses[hand] = controllerPose; - } - }; - - - // FUNCTION DEF(s): Intersection Check Helpers - function testRayIntersect(queryRay, overlayIncludes, overlayExcludes) { - var wantDebug = false; - if ((queryRay === undefined) || (queryRay === null)) { - if (wantDebug) { - print("testRayIntersect - EARLY EXIT -> queryRay is undefined OR null!"); - } - return null; - } - - var intersectObj = Overlays.findRayIntersection(queryRay, true, overlayIncludes, overlayExcludes); - - if (wantDebug) { - if (!overlayIncludes) { - print("testRayIntersect - no overlayIncludes provided."); - } - if (!overlayExcludes) { - print("testRayIntersect - no overlayExcludes provided."); - } - print("testRayIntersect - Hit: " + intersectObj.intersects); - print(" intersectObj.overlayID:" + intersectObj.overlayID + "[" + overlayNames[intersectObj.overlayID] + "]"); - print(" OverlayName: " + overlayNames[intersectObj.overlayID]); - print(" intersectObj.distance:" + intersectObj.distance); - print(" intersectObj.face:" + intersectObj.face); - Vec3.print(" intersectObj.intersection:", intersectObj.intersection); - } - - return intersectObj; + }); } - // FUNCTION: MOUSE PRESS EVENT - that.mousePressEvent = function (event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MousePressEvent BEG ======================="); - } - if (!event.isLeftButton && !that.triggered) { - // EARLY EXIT-(if another mouse button than left is pressed ignore it) - return false; + // TOOL DEFINITION: HANDLE CLONER + addHandleTool(handleCloner, { + mode: "CLONE", + onBegin: function(event, pickRay, pickResult) { + var doClone = true; + translateXZTool.onBegin(event,pickRay,pickResult,doClone); + }, + elevation: function (event) { + translateXZTool.elevation(event); + }, + + onEnd: function (event) { + translateXZTool.onEnd(event); + }, + + onMove: function (event) { + translateXZTool.onMove(event); } + }); - var pickRay = generalComputePickRay(event.x, event.y); - // TODO_Case6491: Move this out to setup just to make it once - var interactiveOverlays = [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, selectionBox]; - for (var key in grabberTools) { - if (grabberTools.hasOwnProperty(key)) { - interactiveOverlays.push(key); - } - } + addHandleTranslateTool(handleTranslateXCone, "TRANSLATE_X", TRANSLATE_DIRECTION.X); + addHandleTranslateTool(handleTranslateXCylinder, "TRANSLATE_X", TRANSLATE_DIRECTION.X); + addHandleTranslateTool(handleTranslateYCone, "TRANSLATE_Y", TRANSLATE_DIRECTION.Y); + addHandleTranslateTool(handleTranslateYCylinder, "TRANSLATE_Y", TRANSLATE_DIRECTION.Y); + addHandleTranslateTool(handleTranslateZCone, "TRANSLATE_Z", TRANSLATE_DIRECTION.Z); + addHandleTranslateTool(handleTranslateZCylinder, "TRANSLATE_Z", TRANSLATE_DIRECTION.Z); - // Start with unknown mode, in case no tool can handle this. - activeTool = null; + addHandleRotateTool(handleRotatePitchRing, "ROTATE_PITCH", ROTATE_DIRECTION.PITCH); + addHandleRotateTool(handleRotateYawRing, "ROTATE_YAW", ROTATE_DIRECTION.YAW); + addHandleRotateTool(handleRotateRollRing, "ROTATE_ROLL", ROTATE_DIRECTION.ROLL); - var results = testRayIntersect(pickRay, interactiveOverlays); - if (results.intersects) { - var hitOverlayID = results.overlayID; - if ((hitOverlayID === HMD.tabletID) || (hitOverlayID === HMD.tabletScreenID) || (hitOverlayID === HMD.homeButtonID)) { - // EARLY EXIT-(mouse clicks on the tablet should override the edit affordances) - return false; - } - - entityIconOverlayManager.setIconsSelectable(SelectionManager.selections, true); - - var hitTool = grabberTools[ hitOverlayID ]; - if (hitTool) { - activeTool = hitTool; - if (activeTool.onBegin) { - activeTool.onBegin(event, pickRay, results); - } else { - print("ERROR: entitySelectionTool.mousePressEvent - ActiveTool(" + activeTool.mode + ") missing onBegin"); - } - } else { - print("ERROR: entitySelectionTool.mousePressEvent - Hit unexpected object, check interactiveOverlays"); - }// End_if (hitTool) - }// End_If(results.intersects) - - if (wantDebug) { - print(" DisplayMode: " + getMode()); - print("=============== eST::MousePressEvent END ======================="); - } - - // If mode is known then we successfully handled this; - // otherwise, we're missing a tool. - return activeTool; - }; - - // FUNCTION: MOUSE MOVE EVENT - that.mouseMoveEvent = function(event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MouseMoveEvent BEG ======================="); - } - if (activeTool) { - if (wantDebug) { - print(" Trigger ActiveTool(" + activeTool.mode + ")'s onMove"); - } - activeTool.onMove(event); - - if (wantDebug) { - print(" Trigger SelectionManager::update"); - } - SelectionManager._update(); - - if (wantDebug) { - print("=============== eST::MouseMoveEvent END ======================="); - } - // EARLY EXIT--(Move handled via active tool) - return true; - } - - // if no tool is active, then just look for handles to highlight... - var pickRay = generalComputePickRay(event.x, event.y); - var result = Overlays.findRayIntersection(pickRay); - var pickedColor; - var pickedAlpha; - var highlightNeeded = false; - - if (result.intersects) { - switch (result.overlayID) { - case yawHandle: - case pitchHandle: - case rollHandle: - pickedColor = handleColor; - pickedAlpha = handleAlpha; - highlightNeeded = true; - break; - - case grabberMoveUp: - pickedColor = handleColor; - pickedAlpha = handleAlpha; - highlightNeeded = true; - break; - - case grabberLBN: - case grabberLBF: - case grabberRBN: - case grabberRBF: - case grabberLTN: - case grabberLTF: - case grabberRTN: - case grabberRTF: - pickedColor = grabberColorCorner; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - case grabberTOP: - case grabberBOTTOM: - case grabberLEFT: - case grabberRIGHT: - case grabberNEAR: - case grabberFAR: - pickedColor = grabberColorFace; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - case grabberEdgeTR: - case grabberEdgeTL: - case grabberEdgeTF: - case grabberEdgeTN: - case grabberEdgeBR: - case grabberEdgeBL: - case grabberEdgeBF: - case grabberEdgeBN: - case grabberEdgeNR: - case grabberEdgeNL: - case grabberEdgeFR: - case grabberEdgeFL: - case grabberSpotLightRadius: - case grabberSpotLightT: - case grabberSpotLightB: - case grabberSpotLightL: - case grabberSpotLightR: - case grabberPointLightT: - case grabberPointLightB: - case grabberPointLightR: - case grabberPointLightL: - case grabberPointLightN: - case grabberPointLightF: - pickedColor = grabberColorEdge; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - case grabberCloner: - pickedColor = grabberColorCloner; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - default: - if (previousHandle) { - Overlays.editOverlay(previousHandle, { - color: previousHandleColor, - alpha: previousHandleAlpha - }); - previousHandle = false; - } - break; - } - - if (highlightNeeded) { - if (previousHandle) { - Overlays.editOverlay(previousHandle, { - color: previousHandleColor, - alpha: previousHandleAlpha - }); - previousHandle = false; - } - Overlays.editOverlay(result.overlayID, { - color: highlightedHandleColor, - alpha: highlightedHandleAlpha - }); - previousHandle = result.overlayID; - previousHandleColor = pickedColor; - previousHandleAlpha = pickedAlpha; - } - - } else { - if (previousHandle) { - Overlays.editOverlay(previousHandle, { - color: previousHandleColor, - alpha: previousHandleAlpha - }); - previousHandle = false; - } - } - - if (wantDebug) { - print("=============== eST::MouseMoveEvent END ======================="); - } - return false; - }; - - // FUNCTION: MOUSE RELEASE EVENT - that.mouseReleaseEvent = function(event) { - var wantDebug = false; - if (wantDebug) { - print("=============== eST::MouseReleaseEvent BEG ======================="); - } - var showHandles = false; - if (activeTool) { - if (activeTool.onEnd) { - if (wantDebug) { - print(" Triggering ActiveTool(" + activeTool.mode + ")'s onEnd"); - } - activeTool.onEnd(event); - } else if (wantDebug) { - print(" ActiveTool(" + activeTool.mode + ")'s missing onEnd"); - } - } - - // hide our rotation overlays..., and show our handles - if (isActiveTool(yawHandle) || isActiveTool(pitchHandle) || isActiveTool(rollHandle)) { - if (wantDebug) { - print(" Triggering hide of RotateOverlays"); - } - Overlays.editOverlay(rotateOverlayInner, { - visible: false - }); - Overlays.editOverlay(rotateOverlayOuter, { - visible: false - }); - Overlays.editOverlay(rotateOverlayCurrent, { - visible: false - }); - - } - - showHandles = activeTool; // base on prior tool value - activeTool = null; - - // if something is selected, then reset the "original" properties for any potential next click+move operation - if (SelectionManager.hasSelection()) { - if (showHandles) { - if (wantDebug) { - print(" Triggering that.select"); - } - that.select(SelectionManager.selections[0], event); - } - } - - if (wantDebug) { - print("=============== eST::MouseReleaseEvent END ======================="); - } - }; - - // NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these: - // Controller.mousePressEvent.connect(that.mousePressEvent); - // Controller.mouseMoveEvent.connect(that.mouseMoveEvent); - Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent); + addHandleStretchTool(handleStretchXSphere, "STRETCH_X", STRETCH_DIRECTION.X); + addHandleStretchTool(handleStretchYSphere, "STRETCH_Y", STRETCH_DIRECTION.Y); + addHandleStretchTool(handleStretchZSphere, "STRETCH_Z", STRETCH_DIRECTION.Z); + addHandleScaleTool(handleScaleLBNCube, "SCALE_LBN", SCALE_DIRECTION.LBN); + addHandleScaleTool(handleScaleRBNCube, "SCALE_RBN", SCALE_DIRECTION.RBN); + addHandleScaleTool(handleScaleLBFCube, "SCALE_LBF", SCALE_DIRECTION.LBF); + addHandleScaleTool(handleScaleRBFCube, "SCALE_RBF", SCALE_DIRECTION.RBF); + addHandleScaleTool(handleScaleLTNCube, "SCALE_LTN", SCALE_DIRECTION.LTN); + addHandleScaleTool(handleScaleRTNCube, "SCALE_RTN", SCALE_DIRECTION.RTN); + addHandleScaleTool(handleScaleLTFCube, "SCALE_LTF", SCALE_DIRECTION.LTF); + addHandleScaleTool(handleScaleRTFCube, "SCALE_RTF", SCALE_DIRECTION.RTF); return that; - }()); diff --git a/scripts/system/libraries/pointersUtils.js b/scripts/system/libraries/pointersUtils.js index 2af563f8d4..53959b91f8 100644 --- a/scripts/system/libraries/pointersUtils.js +++ b/scripts/system/libraries/pointersUtils.js @@ -30,7 +30,6 @@ Pointer = function(hudLayer, pickType, pointerData) { ignoreRayIntersection: true, // always ignore this drawInFront: !hudLayer, // Even when burried inside of something, show it. drawHUDLayer: hudLayer, - parentID: MyAvatar.SELF_ID }; this.halfEnd = { type: "sphere", @@ -53,7 +52,6 @@ Pointer = function(hudLayer, pickType, pointerData) { ignoreRayIntersection: true, // always ignore this drawInFront: !hudLayer, // Even when burried inside of something, show it. drawHUDLayer: hudLayer, - parentID: MyAvatar.SELF_ID }; this.fullEnd = { type: "sphere", @@ -76,7 +74,6 @@ Pointer = function(hudLayer, pickType, pointerData) { ignoreRayIntersection: true, // always ignore this drawInFront: !hudLayer, // Even when burried inside of something, show it. drawHUDLayer: hudLayer, - parentID: MyAvatar.SELF_ID }; this.renderStates = [ diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index facb932eb0..fd1275a251 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -112,11 +112,23 @@ var selectionDisplay = null; // for gridTool.js to ignore var referrerURL; // Used for updating Purchases QML var filterText; // Used for updating Purchases QML + + var onWalletScreen = false; function onScreenChanged(type, url) { onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; - onWalletScreen = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1; + var onWalletScreenNow = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1; onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1); + + if (!onWalletScreenNow && onWalletScreen) { // exiting wallet screen + if (isHmdPreviewDisabledBySecurity) { + DesktopPreviewProvider.setPreviewDisabledReason("USER"); + Menu.setIsOptionChecked("Disable Preview", false); + isHmdPreviewDisabledBySecurity = false; + } + } + + onWalletScreen = onWalletScreenNow; wireEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen); if (url === MARKETPLACE_PURCHASES_QML_PATH) { @@ -151,6 +163,7 @@ var selectionDisplay = null; // for gridTool.js to ignore var certificateId = itemCertificateId || (Entities.getEntityProperties(currentEntityWithContextOverlay, ['certificateID']).certificateID); tablet.sendToQml({ method: 'inspectionCertificate_setCertificateId', + entityId: currentEntityWithContextOverlay, certificateId: certificateId }); } @@ -480,7 +493,7 @@ var selectionDisplay = null; // for gridTool.js to ignore // Description: // -Called when a message is received from Checkout.qml. The "message" argument is what is sent from the Checkout QML // in the format "{method, params}", like json-rpc. - var isHmdPreviewDisabled = true; + var isHmdPreviewDisabledBySecurity = false; function fromQml(message) { switch (message.method) { case 'purchases_openWallet': @@ -548,11 +561,19 @@ var selectionDisplay = null; // for gridTool.js to ignore openLoginWindow(); break; case 'disableHmdPreview': - isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview"); - Menu.setIsOptionChecked("Disable Preview", true); + var isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview"); + if (!isHmdPreviewDisabled) { + DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN"); + Menu.setIsOptionChecked("Disable Preview", true); + isHmdPreviewDisabledBySecurity = true; + } break; case 'maybeEnableHmdPreview': - Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled); + if (isHmdPreviewDisabledBySecurity) { + DesktopPreviewProvider.setPreviewDisabledReason("USER"); + Menu.setIsOptionChecked("Disable Preview", false); + isHmdPreviewDisabledBySecurity = false; + } break; case 'purchases_openGoTo': tablet.loadQMLSource("hifi/tablet/TabletAddressDialog.qml"); @@ -563,6 +584,9 @@ var selectionDisplay = null; // for gridTool.js to ignore case 'inspectionCertificate_closeClicked': tablet.gotoHomeScreen(); break; + case 'inspectionCertificate_requestOwnershipVerification': + ContextOverlay.requestOwnershipVerification(message.entity); + break; case 'inspectionCertificate_showInMarketplaceClicked': tablet.gotoWebScreen(message.marketplaceUrl, MARKETPLACES_INJECT_SCRIPT_URL); break; diff --git a/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index 2092279159..7bc1349091 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME gpu-test) AUTOSCRIBE_SHADER_LIB(gpu graphics render-utils) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL Script Widgets) +setup_hifi_project(Quick Gui Script) setup_memory_debugger() set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") link_hifi_libraries( diff --git a/tests/qml/CMakeLists.txt b/tests/qml/CMakeLists.txt new file mode 100644 index 0000000000..f56b8c4533 --- /dev/null +++ b/tests/qml/CMakeLists.txt @@ -0,0 +1,13 @@ +set(TARGET_NAME qml-test) +setup_hifi_project(Quick Qml Gui OpenGL) +setup_memory_debugger() +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") +link_hifi_libraries(shared networking gl qml) + +if (WIN32) + set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/qml\"") + + package_libraries_for_deployment() +endif() + +target_nsight() diff --git a/tests/qml/qml/main.qml b/tests/qml/qml/main.qml new file mode 100644 index 0000000000..3b37543393 --- /dev/null +++ b/tests/qml/qml/main.qml @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Controls 1.2 +import "qml/UI.js" as UI +import "qml" + +Item { + width: 640 + height: 480 + + Rectangle { + width: 5 + height: 5 + color: "red" + ColorAnimation on color { loops: Animation.Infinite; from: "red"; to: "yellow"; duration: 1000 } + } + + TabView { + id: tabView + + anchors.fill: parent + anchors.margins: UI.margin + tabPosition: UI.tabPosition + + Layout.minimumWidth: 360 + Layout.minimumHeight: 360 + Layout.preferredWidth: 480 + Layout.preferredHeight: 640 + + Tab { + title: "Buttons" + ButtonPage { + enabled: enabler.checked + } + } + Tab { + title: "Progress" + ProgressPage { + enabled: enabler.checked + } + } + Tab { + title: "Input" + InputPage { + enabled: enabler.checked + } + } + } +} diff --git a/tests/qml/qml/qml/+android/UI.js b/tests/qml/qml/qml/+android/UI.js new file mode 100644 index 0000000000..be7ddfa967 --- /dev/null +++ b/tests/qml/qml/qml/+android/UI.js @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +.pragma library + +var margin = 0 +var tabPosition = Qt.BottomEdge +var label = "Gallery" diff --git a/tests/qml/qml/qml/+ios/UI.js b/tests/qml/qml/qml/+ios/UI.js new file mode 100644 index 0000000000..be7ddfa967 --- /dev/null +++ b/tests/qml/qml/qml/+ios/UI.js @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +.pragma library + +var margin = 0 +var tabPosition = Qt.BottomEdge +var label = "Gallery" diff --git a/tests/qml/qml/qml/+osx/UI.js b/tests/qml/qml/qml/+osx/UI.js new file mode 100644 index 0000000000..28b13b64aa --- /dev/null +++ b/tests/qml/qml/qml/+osx/UI.js @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +.pragma library + +var margin = 12 +var tabPosition = Qt.TopEdge +var label = "" diff --git a/tests/qml/qml/qml/ButtonPage.qml b/tests/qml/qml/qml/ButtonPage.qml new file mode 100644 index 0000000000..8eb9c1a730 --- /dev/null +++ b/tests/qml/qml/qml/ButtonPage.qml @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.2 + +ScrollView { + id: page + implicitWidth: 640 + implicitHeight: 200 + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + Item { + id: content + + width: Math.max(page.viewport.width, grid.implicitWidth + 2 * grid.rowSpacing) + height: Math.max(page.viewport.height, grid.implicitHeight + 2 * grid.columnSpacing) + + GridLayout { + id: grid + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: grid.rowSpacing + anchors.rightMargin: grid.rowSpacing + anchors.topMargin: grid.columnSpacing + + columns: page.width < page.height ? 1 : 2 + + GroupBox { + title: "Button" + Layout.fillWidth: true + Layout.columnSpan: grid.columns + RowLayout { + anchors.fill: parent + Button { text: "OK"; isDefault: true } + Button { text: "Cancel" } + Item { Layout.fillWidth: true } + Button { + text: "Attach" + menu: Menu { + MenuItem { text: "Image" } + MenuItem { text: "Document" } + } + } + } + } + + GroupBox { + title: "CheckBox" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + CheckBox { text: "E-mail"; checked: true } + CheckBox { text: "Calendar"; checked: true } + CheckBox { text: "Contacts" } + } + } + + GroupBox { + title: "RadioButton" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + ExclusiveGroup { id: radioGroup } + RadioButton { text: "Portrait"; exclusiveGroup: radioGroup } + RadioButton { text: "Landscape"; exclusiveGroup: radioGroup } + RadioButton { text: "Automatic"; exclusiveGroup: radioGroup; checked: true } + } + } + + GroupBox { + title: "Switch" + Layout.fillWidth: true + Layout.columnSpan: grid.columns + ColumnLayout { + anchors.fill: parent + RowLayout { + Label { text: "Wi-Fi"; Layout.fillWidth: true } + Switch { checked: true } + } + RowLayout { + Label { text: "Bluetooth"; Layout.fillWidth: true } + Switch { checked: false } + } + } + } + } + } +} diff --git a/tests/qml/qml/qml/InputPage.qml b/tests/qml/qml/qml/InputPage.qml new file mode 100644 index 0000000000..78ef0a64d6 --- /dev/null +++ b/tests/qml/qml/qml/InputPage.qml @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.2 + +ScrollView { + id: page + implicitWidth: 640 + implicitHeight: 400 + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + Item { + id: content + + width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing) + height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing) + + ColumnLayout { + id: column + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: column.spacing + + GroupBox { + title: "TextField" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + TextField { placeholderText: "..."; Layout.fillWidth: true; z: 1 } + TextField { placeholderText: "Password"; echoMode: TextInput.Password; Layout.fillWidth: true } + } + } + + GroupBox { + title: "ComboBox" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + ComboBox { + model: Qt.fontFamilies() + Layout.fillWidth: true + } + ComboBox { + editable: true + model: ListModel { + id: listModel + ListElement { text: "Apple" } + ListElement { text: "Banana" } + ListElement { text: "Coconut" } + ListElement { text: "Orange" } + } + onAccepted: { + if (find(currentText) === -1) { + listModel.append({text: editText}) + currentIndex = find(editText) + } + } + Layout.fillWidth: true + } + } + } + + GroupBox { + title: "SpinBox" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + SpinBox { value: 99; Layout.fillWidth: true; z: 1 } + SpinBox { decimals: 2; Layout.fillWidth: true } + } + } + } + } +} diff --git a/tests/qml/qml/qml/ProgressPage.qml b/tests/qml/qml/qml/ProgressPage.qml new file mode 100644 index 0000000000..5123d101ff --- /dev/null +++ b/tests/qml/qml/qml/ProgressPage.qml @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.2 + +ScrollView { + id: page + implicitWidth: 640 + implicitHeight: 400 + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + Item { + id: content + + width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing) + height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing) + + ColumnLayout { + id: column + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: column.spacing + + GroupBox { + title: "ProgressBar" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + ProgressBar { indeterminate: true; Layout.fillWidth: true } + ProgressBar { value: slider.value; Layout.fillWidth: true } + } + } + + GroupBox { + title: "Slider" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + Slider { id: slider; value: 0.5; Layout.fillWidth: true } + } + } + + GroupBox { + title: "BusyIndicator" + Layout.fillWidth: true + BusyIndicator { running: true } + } + } + } +} diff --git a/tests/qml/qml/qml/UI.js b/tests/qml/qml/qml/UI.js new file mode 100644 index 0000000000..3899ced027 --- /dev/null +++ b/tests/qml/qml/qml/UI.js @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +.pragma library + +var margin = 2 +var tabPosition = Qt.TopEdge +var label = "" diff --git a/tests/qml/src/main.cpp b/tests/qml/src/main.cpp new file mode 100644 index 0000000000..219efa5996 --- /dev/null +++ b/tests/qml/src/main.cpp @@ -0,0 +1,198 @@ +// +// main.cpp +// tests/gpu-test/src +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +class OffscreenQmlSurface : public hifi::qml::OffscreenSurface { + +}; + +class TestWindow : public QWindow { + +public: + TestWindow(); + + +private: + using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence; + QOpenGLContext _glContext; + OffscreenGLCanvas _sharedContext; + OffscreenQmlSurface _offscreenQml; + QOpenGLFunctions_4_5_Core _glf; + uint32_t _currentTexture{ 0 }; + GLsync _readFence{ 0 }; + std::function _discardLamdba; + QSize _size; + GLuint _fbo{ 0 }; + const QSize _qmlSize{ 640, 480 }; + bool _aboutToQuit{ false }; + void initGl(); + void resizeWindow(const QSize& size); + void draw(); + void resizeEvent(QResizeEvent* ev) override; +}; + +TestWindow::TestWindow() { + setSurfaceType(QSurface::OpenGLSurface); + + QSurfaceFormat format; + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setVersion(4, 5); + format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); + format.setOption(QSurfaceFormat::DebugContext); + QSurfaceFormat::setDefaultFormat(format); + setFormat(format); + + show(); + + resize(QSize(800, 600)); + + auto timer = new QTimer(this); + timer->setTimerType(Qt::PreciseTimer); + timer->setInterval(5); + connect(timer, &QTimer::timeout, [&] { draw(); }); + timer->start(); + + connect(qApp, &QCoreApplication::aboutToQuit, [this, timer] { + timer->stop(); + _aboutToQuit = true; + }); + +} + +void TestWindow::initGl() { + _glContext.setFormat(format()); + if (!_glContext.create() || !_glContext.makeCurrent(this)) { + qFatal("Unable to intialize Window GL context"); + } + + _glf.initializeOpenGLFunctions(); + _glf.glCreateFramebuffers(1, &_fbo); + + if (!_sharedContext.create(&_glContext) || !_sharedContext.makeCurrent()) { + qFatal("Unable to intialize Shared GL context"); + } + hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext()); + _discardLamdba = _offscreenQml.getDiscardLambda(); + _offscreenQml.resize({ 640, 480 }); + _offscreenQml.load(QUrl::fromLocalFile("C:/Users/bdavi/Git/hifi/tests/qml/qml/main.qml")); +} + +void TestWindow::resizeWindow(const QSize& size) { + _size = size; +} + +void TestWindow::draw() { + if (_aboutToQuit) { + return; + } + + // Attempting to draw before we're visible and have a valid size will + // produce GL errors. + if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) { + return; + } + + static std::once_flag once; + std::call_once(once, [&] { initGl(); }); + + if (!_glContext.makeCurrent(this)) { + return; + } + + _glf.glClearColor(1, 0, 0, 1); + _glf.glClear(GL_COLOR_BUFFER_BIT); + + TextureAndFence newTextureAndFence; + if (_offscreenQml.fetchTexture(newTextureAndFence)) { + if (_currentTexture) { + _discardLamdba(_currentTexture, _readFence); + _readFence = 0; + } + + _currentTexture = newTextureAndFence.first; + _glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED); + _glf.glNamedFramebufferTexture(_fbo, GL_COLOR_ATTACHMENT0, _currentTexture, 0); + } + + auto diff = _size - _qmlSize; + diff /= 2; + auto qmlExtent = diff + _qmlSize; + + if (_currentTexture) { + _glf.glBlitNamedFramebuffer(_fbo, 0, + 0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1, + diff.width(), diff.height(), qmlExtent.width() - 1, qmlExtent.height() - 2, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + + if (_readFence) { + _glf.glDeleteSync(_readFence); + } + _readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + _glf.glFlush(); + + _glContext.swapBuffers(this); +} + +void TestWindow::resizeEvent(QResizeEvent* ev) { + resizeWindow(ev->size()); +} + +void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { + if (!message.isEmpty()) { +#ifdef Q_OS_WIN + OutputDebugStringA(message.toLocal8Bit().constData()); + OutputDebugStringA("\n"); +#endif + } +} + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); + qInstallMessageHandler(messageHandler); + TestWindow window; + app.exec(); + return 0; +} + diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 5314f7a45b..57ae7dace7 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -8,17 +8,17 @@ endif() setup_memory_debugger() # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) +setup_hifi_project(Quick Gui) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries link_hifi_libraries( shared networking animation - ktx image octree gl gpu + ktx image octree gl gpu gpu-gl render render-utils graphics fbx model-networking entities entities-renderer audio avatars script-engine - physics procedural midi ui + physics procedural midi qml ui ${PLATFORM_GL_BACKEND} ) diff --git a/tests/render-texture-load/CMakeLists.txt b/tests/render-texture-load/CMakeLists.txt index dbfb7d33db..2ed905a3ef 100644 --- a/tests/render-texture-load/CMakeLists.txt +++ b/tests/render-texture-load/CMakeLists.txt @@ -8,7 +8,7 @@ endif() setup_memory_debugger() # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) +setup_hifi_project(Quick Gui) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries diff --git a/tests/render-utils/CMakeLists.txt b/tests/render-utils/CMakeLists.txt index e3b1523a96..be75c53f2e 100644 --- a/tests/render-utils/CMakeLists.txt +++ b/tests/render-utils/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME render-utils-test) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) +setup_hifi_project(Quick Gui) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") setup_memory_debugger() diff --git a/tests/shaders/CMakeLists.txt b/tests/shaders/CMakeLists.txt index 76d2aa100c..44394db6a0 100644 --- a/tests/shaders/CMakeLists.txt +++ b/tests/shaders/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME shaders-test) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) +setup_hifi_project(Quick Gui) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") setup_memory_debugger() diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index 3f48e37a76..585bc432a9 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -101,6 +101,7 @@ public: show(); makeCurrent(); + gl::initModuleGl(); gpu::Context::init(); makeCurrent(); resize(QSize(800, 600)); @@ -135,7 +136,7 @@ const std::string PIXEL_SHADER_DEFINES{ R"GLSL( #define GPU_TRANSFORM_STEREO_SPLIT_SCREEN )GLSL" }; -void testShaderBuild(const char* vs_src, const char * fs_src) { +void testShaderBuild(const std::string& vs_src, const std::string& fs_src) { std::string error; std::vector binary; GLuint vs, fs; @@ -160,54 +161,54 @@ void QTestWindow::draw() { static std::once_flag once; std::call_once(once, [&]{ - testShaderBuild(sdf_text3D_vert, sdf_text3D_frag); + testShaderBuild(sdf_text3D_vert::getSource(), sdf_text3D_frag::getSource()); - testShaderBuild(DrawTransformUnitQuad_vert, DrawTexture_frag); - testShaderBuild(DrawTexcoordRectTransformUnitQuad_vert, DrawTexture_frag); - testShaderBuild(DrawViewportQuadTransformTexcoord_vert, DrawTexture_frag); - testShaderBuild(DrawTransformUnitQuad_vert, DrawTextureOpaque_frag); - testShaderBuild(DrawTransformUnitQuad_vert, DrawColoredTexture_frag); + testShaderBuild(DrawTransformUnitQuad_vert::getSource(), DrawTexture_frag::getSource()); + testShaderBuild(DrawTexcoordRectTransformUnitQuad_vert::getSource(), DrawTexture_frag::getSource()); + testShaderBuild(DrawViewportQuadTransformTexcoord_vert::getSource(), DrawTexture_frag::getSource()); + testShaderBuild(DrawTransformUnitQuad_vert::getSource(), DrawTextureOpaque_frag::getSource()); + testShaderBuild(DrawTransformUnitQuad_vert::getSource(), DrawColoredTexture_frag::getSource()); - testShaderBuild(skybox_vert, skybox_frag); - testShaderBuild(simple_vert, simple_frag); - testShaderBuild(simple_vert, simple_textured_frag); - testShaderBuild(simple_vert, simple_textured_unlit_frag); - testShaderBuild(deferred_light_vert, directional_ambient_light_frag); - testShaderBuild(deferred_light_vert, directional_skybox_light_frag); - testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag); - testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag); + testShaderBuild(skybox_vert::getSource(), skybox_frag::getSource()); + testShaderBuild(simple_vert::getSource(), simple_frag::getSource()); + testShaderBuild(simple_vert::getSource(), simple_textured_frag::getSource()); + testShaderBuild(simple_vert::getSource(), simple_textured_unlit_frag::getSource()); + testShaderBuild(deferred_light_vert::getSource(), directional_ambient_light_frag::getSource()); + testShaderBuild(deferred_light_vert::getSource(), directional_skybox_light_frag::getSource()); + testShaderBuild(standardTransformPNTC_vert::getSource(), standardDrawTexture_frag::getSource()); + testShaderBuild(standardTransformPNTC_vert::getSource(), DrawTextureOpaque_frag::getSource()); - testShaderBuild(model_vert, model_frag); - testShaderBuild(model_normal_map_vert, model_normal_map_frag); - testShaderBuild(model_vert, model_specular_map_frag); - testShaderBuild(model_normal_map_vert, model_normal_specular_map_frag); - testShaderBuild(model_vert, model_translucent_frag); - testShaderBuild(model_normal_map_vert, model_translucent_frag); - testShaderBuild(model_lightmap_vert, model_lightmap_frag); - testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_map_frag); - testShaderBuild(model_lightmap_vert, model_lightmap_specular_map_frag); - testShaderBuild(model_lightmap_normal_map_vert, model_lightmap_normal_specular_map_frag); + testShaderBuild(model_vert::getSource(), model_frag::getSource()); + testShaderBuild(model_normal_map_vert::getSource(), model_normal_map_frag::getSource()); + testShaderBuild(model_vert::getSource(), model_specular_map_frag::getSource()); + testShaderBuild(model_normal_map_vert::getSource(), model_normal_specular_map_frag::getSource()); + testShaderBuild(model_vert::getSource(), model_translucent_frag::getSource()); + testShaderBuild(model_normal_map_vert::getSource(), model_translucent_frag::getSource()); + testShaderBuild(model_lightmap_vert::getSource(), model_lightmap_frag::getSource()); + testShaderBuild(model_lightmap_normal_map_vert::getSource(), model_lightmap_normal_map_frag::getSource()); + testShaderBuild(model_lightmap_vert::getSource(), model_lightmap_specular_map_frag::getSource()); + testShaderBuild(model_lightmap_normal_map_vert::getSource(), model_lightmap_normal_specular_map_frag::getSource()); - testShaderBuild(skin_model_vert, model_frag); - testShaderBuild(skin_model_normal_map_vert, model_normal_map_frag); - testShaderBuild(skin_model_vert, model_specular_map_frag); - testShaderBuild(skin_model_normal_map_vert, model_normal_specular_map_frag); - testShaderBuild(skin_model_vert, model_translucent_frag); - testShaderBuild(skin_model_normal_map_vert, model_translucent_frag); + testShaderBuild(skin_model_vert::getSource(), model_frag::getSource()); + testShaderBuild(skin_model_normal_map_vert::getSource(), model_normal_map_frag::getSource()); + testShaderBuild(skin_model_vert::getSource(), model_specular_map_frag::getSource()); + testShaderBuild(skin_model_normal_map_vert::getSource(), model_normal_specular_map_frag::getSource()); + testShaderBuild(skin_model_vert::getSource(), model_translucent_frag::getSource()); + testShaderBuild(skin_model_normal_map_vert::getSource(), model_translucent_frag::getSource()); - testShaderBuild(model_shadow_vert, model_shadow_frag); - testShaderBuild(textured_particle_vert, textured_particle_frag); + testShaderBuild(model_shadow_vert::getSource(), model_shadow_frag::getSource()); + testShaderBuild(textured_particle_vert::getSource(), textured_particle_frag::getSource()); /* FIXME: Bring back the ssao shader tests - testShaderBuild(gaussian_blur_vertical_vert, gaussian_blur_frag); - testShaderBuild(gaussian_blur_horizontal_vert, gaussian_blur_frag); - testShaderBuild(ambient_occlusion_vert, ambient_occlusion_frag); - testShaderBuild(ambient_occlusion_vert, occlusion_blend_frag); + testShaderBuild(gaussian_blur_vert::getSource()ical_vert::getSource(), gaussian_blur_frag::getSource()); + testShaderBuild(gaussian_blur_horizontal_vert::getSource(), gaussian_blur_frag::getSource()); + testShaderBuild(ambient_occlusion_vert::getSource(), ambient_occlusion_frag::getSource()); + testShaderBuild(ambient_occlusion_vert::getSource(), occlusion_blend_frag::getSource()); */ - testShaderBuild(overlay3D_vert, overlay3D_frag); + testShaderBuild(overlay3D_vert::getSource(), overlay3D_frag::getSource()); - testShaderBuild(paintStroke_vert,paintStroke_frag); - testShaderBuild(polyvox_vert, polyvox_frag); + testShaderBuild(paintStroke_vert::getSource(),paintStroke_frag::getSource()); + testShaderBuild(polyvox_vert::getSource(), polyvox_frag::getSource()); }); _context.swapBuffers(this); diff --git a/tests/shared/src/BitVectorHelperTests.cpp b/tests/shared/src/BitVectorHelperTests.cpp index 070e90eec7..618a86cad1 100644 --- a/tests/shared/src/BitVectorHelperTests.cpp +++ b/tests/shared/src/BitVectorHelperTests.cpp @@ -20,8 +20,6 @@ QTEST_MAIN(BitVectorHelperTests) -const int BITS_IN_BYTE = 8; - void BitVectorHelperTests::sizeTest() { std::vector sizes = {0, 6, 7, 8, 30, 31, 32, 33, 87, 88, 89, 90, 90, 91, 92, 93}; for (auto& size : sizes) { diff --git a/tests/shared/src/FileCacheTests.cpp b/tests/shared/src/FileCacheTests.cpp index 3f1c5e1a01..b7c2103817 100644 --- a/tests/shared/src/FileCacheTests.cpp +++ b/tests/shared/src/FileCacheTests.cpp @@ -33,7 +33,7 @@ size_t FileCacheTests::getCacheDirectorySize() const { return result; } -FileCachePointer makeFileCache(QString& location) { +FileCachePointer makeFileCache(QString location) { auto result = std::make_shared(location.toStdString(), "tmp"); result->initialize(); result->setMaxSize(MAX_UNUSED_SIZE); @@ -53,7 +53,7 @@ void FileCacheTests::testUnusedFiles() { auto file = cache->writeFile(TEST_DATA.data(), FileCache::Metadata(key, TEST_DATA.size())); QVERIFY(file->_locked); inUseFiles.push_back(file); - + QThread::msleep(10); } QCOMPARE(cache->getNumCachedFiles(), (size_t)0); @@ -100,13 +100,13 @@ void FileCacheTests::testUnusedFiles() { inUseFiles.push_back(file); if (i == 94) { - // Each access touches the file, so we need to sleep here to ensure that the the last 5 files + // Each access touches the file, so we need to sleep here to ensure that the the last 5 files // have later times for cache ejection priority, otherwise the test runs too fast to reliably - // differentiate + // differentiate QThread::msleep(1000); } } - + QCOMPARE(cache->getNumCachedFiles(), (size_t)0); QCOMPARE(cache->getNumTotalFiles(), (size_t)10); inUseFiles.clear(); @@ -119,7 +119,7 @@ size_t FileCacheTests::getFreeSpace() const { return QStorageInfo(_testDir.path()).bytesFree(); } -// FIXME if something else is changing the amount of free space on the target drive concurrently with this test +// FIXME if something else is changing the amount of free space on the target drive concurrently with this test // running, then it may fail void FileCacheTests::testFreeSpacePreservation() { QCOMPARE(getCacheDirectorySize(), MAX_UNUSED_SIZE); diff --git a/tests/shared/src/GeometryUtilTests.cpp b/tests/shared/src/GeometryUtilTests.cpp index ccb3dc8a0e..eb9be4987f 100644 --- a/tests/shared/src/GeometryUtilTests.cpp +++ b/tests/shared/src/GeometryUtilTests.cpp @@ -31,20 +31,20 @@ static void testSphereVsCone(const glm::vec3 coneNormal, const glm::vec3 coneBiN glm::vec3 coneCenter = glm::vec3(0.0f, 0.0f, 0.0f); glm::vec3 sphereCenter = coneCenter + coneEdge * sphereDistance; float result = coneSphereAngle(coneCenter, u, sphereCenter, sphereRadius); - QCOMPARE(isnan(result), false); + QCOMPARE(glm::isnan(result), false); QCOMPARE(result < coneAngle, true); // push sphere outward from edge so it is tangent to the cone. glm::vec3 sphereOffset = glm::angleAxis(PI / 2.0f, w) * coneEdge; sphereCenter += sphereOffset * sphereRadius; result = coneSphereAngle(coneCenter, u, sphereCenter, sphereRadius); - QCOMPARE(isnan(result), false); + QCOMPARE(glm::isnan(result), false); QCOMPARE_WITH_ABS_ERROR(result, coneAngle, 0.001f); // push sphere outward from edge a bit further, so it is outside of the cone. sphereCenter += 0.1f * sphereOffset; result = coneSphereAngle(coneCenter, u, sphereCenter, sphereRadius); - QCOMPARE(isnan(result), false); + QCOMPARE(glm::isnan(result), false); QCOMPARE(result > coneAngle, true); } diff --git a/tests/shared/src/PathUtilsTests.cpp b/tests/shared/src/PathUtilsTests.cpp index 1c445908f7..3aec4fa5f9 100644 --- a/tests/shared/src/PathUtilsTests.cpp +++ b/tests/shared/src/PathUtilsTests.cpp @@ -16,12 +16,8 @@ QTEST_MAIN(PathUtilsTests) void PathUtilsTests::testPathUtils() { - QString result = PathUtils::qmlBasePath(); -#if DEV_BUILD - QVERIFY(result.startsWith("file:///")); -#else + QString result = PathUtils::qmlBaseUrl(); QVERIFY(result.startsWith("qrc:///")); -#endif QVERIFY(result.endsWith("/")); } diff --git a/tools/ac-client/CMakeLists.txt b/tools/ac-client/CMakeLists.txt index 24eeadba9c..ad16187dcb 100644 --- a/tools/ac-client/CMakeLists.txt +++ b/tools/ac-client/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME ac-client) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) setup_memory_debugger() link_hifi_libraries(shared networking) diff --git a/tools/ac-client/src/ACClientApp.h b/tools/ac-client/src/ACClientApp.h index e295b17654..d43e78eaeb 100644 --- a/tools/ac-client/src/ACClientApp.h +++ b/tools/ac-client/src/ACClientApp.h @@ -13,7 +13,7 @@ #ifndef hifi_ACClientApp_h #define hifi_ACClientApp_h -#include +#include #include #include #include diff --git a/tools/atp-client/CMakeLists.txt b/tools/atp-client/CMakeLists.txt index 4cee30bcc3..19c70597f7 100644 --- a/tools/atp-client/CMakeLists.txt +++ b/tools/atp-client/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME atp-client) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) setup_memory_debugger() link_hifi_libraries(shared networking) diff --git a/tools/atp-client/src/ATPClientApp.h b/tools/atp-client/src/ATPClientApp.h index 42760ceca9..7ab4ec4a7a 100644 --- a/tools/atp-client/src/ATPClientApp.h +++ b/tools/atp-client/src/ATPClientApp.h @@ -13,7 +13,7 @@ #ifndef hifi_ATPClientApp_h #define hifi_ATPClientApp_h -#include +#include #include #include #include diff --git a/tools/ice-client/CMakeLists.txt b/tools/ice-client/CMakeLists.txt index ae42d79f7e..64ec6b131d 100644 --- a/tools/ice-client/CMakeLists.txt +++ b/tools/ice-client/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME ice-client) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) setup_memory_debugger() link_hifi_libraries(shared networking) diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h index de6b6abb14..642b9bc36b 100644 --- a/tools/ice-client/src/ICEClientApp.h +++ b/tools/ice-client/src/ICEClientApp.h @@ -13,7 +13,7 @@ #ifndef hifi_ICEClientApp_h #define hifi_ICEClientApp_h -#include +#include #include #include #include diff --git a/tools/scribe/CMakeLists.txt b/tools/scribe/CMakeLists.txt index e62a3467e0..9c71aeec9c 100755 --- a/tools/scribe/CMakeLists.txt +++ b/tools/scribe/CMakeLists.txt @@ -1,3 +1,8 @@ set(TARGET_NAME scribe) -setup_hifi_project() +# don't use the setup_hifi_project macro as we don't want Qt or GLM dependencies +file(GLOB TARGET_SRCS src/*) +add_executable(${TARGET_NAME} ${TARGET_SRCS}) +if (WIN32) + set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF") +endif() diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp index 810f6c0f45..c8c540c362 100755 --- a/tools/scribe/src/main.cpp +++ b/tools/scribe/src/main.cpp @@ -41,9 +41,23 @@ int main (int argc, char** argv) { GRAB_VAR_VALUE, GRAB_INCLUDE_PATH, GRAB_TARGET_NAME, + GRAB_SHADER_TYPE, EXIT, } mode = READY; + enum Type { + VERTEX = 0, + FRAGMENT, + GEOMETRY, + } type = VERTEX; + static const char* shaderTypeString[] = { + "VERTEX", "PIXEL", "GEOMETRY" + }; + static const char* shaderCreateString[] = { + "Vertex", "Pixel", "Geometry" + }; + std::string shaderStage{ "vert" }; + for (int ii = 1; (mode != EXIT) && (ii < argc); ii++) { inputs.push_back(argv[ii]); @@ -66,6 +80,8 @@ int main (int argc, char** argv) { } else if (inputs.back() == "-c++") { makeCPlusPlus = true; mode = READY; + } else if (inputs.back() == "-T") { + mode = GRAB_SHADER_TYPE; } else { // just grabbed the source filename, stop parameter parsing srcFilename = inputs.back(); @@ -106,6 +122,24 @@ int main (int argc, char** argv) { } break; + case GRAB_SHADER_TYPE: + { + if (inputs.back() == "frag") { + shaderStage = inputs.back(); + type = FRAGMENT; + } else if (inputs.back() == "geom") { + shaderStage = inputs.back(); + type = GEOMETRY; + } else if (inputs.back() == "vert") { + shaderStage = inputs.back(); + type = VERTEX; + } else { + cerr << "Unrecognized shader type. Supported is vert, frag or geom" << endl; + } + mode = READY; + } + break; + case EXIT: { // THis shouldn't happen } @@ -123,11 +157,13 @@ int main (int argc, char** argv) { cerr << " varname and varvalue must be made of alpha numerical characters with no spaces." << endl; cerr << " -listVars : Will list the vars name and value in the standard output." << endl; cerr << " -showParseTree : Draw the tree obtained while parsing the source" << endl; - cerr << " -c++ : Generate a c++ header file containing the output file stream stored as a char[] variable" << endl; + cerr << " -c++ : Generate a c++ source file containing the output file stream stored as a char[] variable" << endl; + cerr << " -T vert/frag/geom : define the type of the shader. Defaults to VERTEX if not specified." << endl; + cerr << " This is necessary if the -c++ option is used." << endl; return 0; } - // Define targetName: if none, get destFilenmae, if none get srcFilename + // Define targetName: if none, get destFilename, if none get srcFilename if (targetName.empty()) { if (destFilename.empty()) { targetName = srcFilename; @@ -163,7 +199,7 @@ int main (int argc, char** argv) { srcStream.open(srcFilename, std::fstream::in); if (!srcStream.is_open()) { cerr << "Failed to open source file <" << srcFilename << ">" << endl; - return 0; + return 1; } auto scribe = std::make_shared(srcFilename, config); @@ -173,7 +209,7 @@ int main (int argc, char** argv) { int numErrors = scribe->scribe(destStringStream, srcStream, vars); if (numErrors) { cerr << "Scribe " << srcFilename << "> failed: " << numErrors << " errors." << endl; - return 0; + return 1; }; @@ -186,7 +222,6 @@ int main (int argc, char** argv) { scribe->displayTree(cerr, level); } - std::ostringstream targetStringStream; if (makeCPlusPlus) { // Because there is a maximum size for literal strings declared in source we need to partition the // full source string stream into pages that seems to be around that value... @@ -208,35 +243,108 @@ int main (int argc, char** argv) { pageSize += lineSize; } - targetStringStream << "// File generated by Scribe " << vars["_SCRIBE_DATE"] << std::endl; - targetStringStream << "#ifndef scribe_" << targetName << "_h" << std::endl; - targetStringStream << "#define scribe_" << targetName << "_h" << std::endl << std::endl; + std::stringstream headerStringStream; + std::stringstream sourceStringStream; + std::string headerFileName = destFilename + ".h"; + std::string sourceFileName = destFilename + ".cpp"; - targetStringStream << "const char " << targetName << "[] = \n"; + sourceStringStream << "// File generated by Scribe " << vars["_SCRIBE_DATE"] << std::endl; + // Write header file + headerStringStream << "#ifndef " << targetName << "_h" << std::endl; + headerStringStream << "#define " << targetName << "_h\n" << std::endl; + headerStringStream << "#include \n" << std::endl; + headerStringStream << "class " << targetName << " {" << std::endl; + headerStringStream << "public:" << std::endl; + headerStringStream << "\tstatic gpu::Shader::Type getType() { return gpu::Shader::" << shaderTypeString[type] << "; }" << std::endl; + headerStringStream << "\tstatic const std::string& getSource() { return _source; }" << std::endl; + headerStringStream << "\tstatic gpu::ShaderPointer getShader();" << std::endl; + headerStringStream << "private:" << std::endl; + headerStringStream << "\tstatic const std::string _source;" << std::endl; + headerStringStream << "\tstatic gpu::ShaderPointer _shader;" << std::endl; + headerStringStream << "};\n" << std::endl; + headerStringStream << "#endif // " << targetName << "_h" << std::endl; + + bool mustOutputHeader = destFilename.empty(); + // Compare with existing file + { + std::fstream headerFile; + headerFile.open(headerFileName, std::fstream::in); + if (headerFile.is_open()) { + // Skip first line + std::string line; + std::stringstream previousHeaderStringStream; + std::getline(headerFile, line); + + previousHeaderStringStream << headerFile.rdbuf(); + mustOutputHeader = mustOutputHeader || previousHeaderStringStream.str() != headerStringStream.str(); + } else { + mustOutputHeader = true; + } + } + + if (mustOutputHeader) { + if (!destFilename.empty()) { + // File content has changed so write it + std::fstream headerFile; + headerFile.open(headerFileName, std::fstream::out); + if (headerFile.is_open()) { + // First line contains the date of modification + headerFile << sourceStringStream.str(); + headerFile << headerStringStream.str(); + } else { + cerr << "Scribe output file <" << headerFileName << "> failed to open." << endl; + return 1; + } + } else { + cerr << sourceStringStream.str(); + cerr << headerStringStream.str(); + } + } + + // Write source file + sourceStringStream << "#include \"" << targetName << ".h\"\n" << std::endl; + sourceStringStream << "gpu::ShaderPointer " << targetName << "::_shader;" << std::endl; + sourceStringStream << "const std::string " << targetName << "::_source = std::string()"; // Write the pages content for (auto page : pages) { - targetStringStream << "R\"SCRIBE(\n" << page->str() << "\n)SCRIBE\"\n"; + sourceStringStream << "+ std::string(R\"SCRIBE(\n" << page->str() << "\n)SCRIBE\")\n"; } - targetStringStream << ";\n" << std::endl << std::endl; + sourceStringStream << ";\n" << std::endl << std::endl; - targetStringStream << "#endif" << std::endl; - } else { - targetStringStream << destStringStream.str(); - } + sourceStringStream << "gpu::ShaderPointer " << targetName << "::getShader() {" << std::endl; + sourceStringStream << "\tif (_shader==nullptr) {" << std::endl; + sourceStringStream << "\t\t_shader = gpu::Shader::create" << shaderCreateString[type] << "(std::string(_source));" << std::endl; + sourceStringStream << "\t}" << std::endl; + sourceStringStream << "\treturn _shader;" << std::endl; + sourceStringStream << "}\n" << std::endl; - // Destination stream - if (!destFilename.empty()) { - std::fstream destFileStream; - destFileStream.open(destFilename, std::fstream::out); - if (!destFileStream.is_open()) { - cerr << "Scribe output file " << destFilename << "> failed to open." << endl; - return 0; + // Destination stream + if (!destFilename.empty()) { + std::fstream sourceFile; + sourceFile.open(sourceFileName, std::fstream::out); + if (!sourceFile.is_open()) { + cerr << "Scribe output file <" << sourceFileName << "> failed to open." << endl; + return 1; + } + sourceFile << sourceStringStream.str(); + } else { + cerr << sourceStringStream.str(); } - - destFileStream << targetStringStream.str(); } else { - cerr << targetStringStream.str(); + // Destination stream + if (!destFilename.empty()) { + std::fstream destFileStream; + destFileStream.open(destFilename, std::fstream::out); + if (!destFileStream.is_open()) { + cerr << "Scribe output file <" << destFilename << "> failed to open." << endl; + return 1; + } + + destFileStream << destStringStream.str(); + } else { + cerr << destStringStream.str(); + } } return 0; diff --git a/tools/skeleton-dump/CMakeLists.txt b/tools/skeleton-dump/CMakeLists.txt index 1c30e322d6..d0a6475c59 100644 --- a/tools/skeleton-dump/CMakeLists.txt +++ b/tools/skeleton-dump/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME skeleton-dump) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) setup_memory_debugger() link_hifi_libraries(shared fbx graphics gpu gl animation) diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.h b/tools/skeleton-dump/src/SkeletonDumpApp.h index 40df98eb65..272f7563fa 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.h +++ b/tools/skeleton-dump/src/SkeletonDumpApp.h @@ -12,7 +12,7 @@ #ifndef hifi_SkeletonDumpApp_h #define hifi_SkeletonDumpApp_h -#include +#include class SkeletonDumpApp : public QCoreApplication { Q_OBJECT diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt index 599561bd2d..5ba8b7d971 100644 --- a/tools/vhacd-util/CMakeLists.txt +++ b/tools/vhacd-util/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME vhacd-util) -setup_hifi_project(Core Widgets) +setup_hifi_project(Core) link_hifi_libraries(shared fbx graphics gpu gl) add_dependency_external_projects(vhacd) diff --git a/tools/vhacd-util/src/VHACDUtilApp.h b/tools/vhacd-util/src/VHACDUtilApp.h index ebb8164634..0d75275802 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.h +++ b/tools/vhacd-util/src/VHACDUtilApp.h @@ -13,7 +13,7 @@ #ifndef hifi_VHACDUtilApp_h #define hifi_VHACDUtilApp_h -#include +#include #include