diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 1ef375b562..a1a2b7c1b5 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,7 @@ AssignmentClient::~AssignmentClient() { } void AssignmentClient::aboutToQuit() { + crash::annotations::setShutdownState(true); stopAssignmentClient(); } @@ -173,6 +175,7 @@ void AssignmentClient::sendStatusPacketToACM() { void AssignmentClient::sendAssignmentRequest() { if (!_currentAssignment && !_isAssigned) { + crash::annotations::setShutdownState(false); auto nodeList = DependencyManager::get(); @@ -289,6 +292,8 @@ void AssignmentClient::handleAuthenticationRequest() { } void AssignmentClient::assignmentCompleted() { + crash::annotations::setShutdownState(true); + // we expect that to be here the previous assignment has completely cleaned up assert(_currentAssignment.isNull()); diff --git a/assignment-client/src/avatars/MixerAvatar.cpp b/assignment-client/src/avatars/MixerAvatar.cpp index 97d28fa246..ac633d9388 100644 --- a/assignment-client/src/avatars/MixerAvatar.cpp +++ b/assignment-client/src/avatars/MixerAvatar.cpp @@ -38,7 +38,7 @@ MixerAvatar::MixerAvatar() { _pendingEvent = false; _verifyState = verificationFailed; _needsIdentityUpdate = true; - qCDebug(avatars) << "Dynamic verification TIMED-OUT for " << getDisplayName() << getSessionUUID(); + qCDebug(avatars) << "Dynamic verification TIMED-OUT for" << getDisplayName() << getSessionUUID(); } else { qCDebug(avatars) << "Ignoring timeout of avatar challenge"; } @@ -287,7 +287,7 @@ void MixerAvatar::processCertifyEvents() { << ":" << _dynamicMarketResponse; } } else { - qCDebug(avatars) << "Get owner status failed for " << getDisplayName() << _marketplaceIdFromURL << + qCDebug(avatars) << "Get owner status failed for" << getDisplayName() << _marketplaceIdFromURL << "message:" << responseJson["message"].toString(); _verifyState = error; } @@ -356,7 +356,7 @@ void MixerAvatar::processChallengeResponse(ReceivedMessage& response) { _verifyState = challengeResult ? verificationSucceeded : verificationFailed; _needsIdentityUpdate = true; if (_verifyState == verificationFailed) { - qCDebug(avatars) << "Dynamic verification FAILED for " << getDisplayName() << getSessionUUID(); + qCDebug(avatars) << "Dynamic verification FAILED for" << getDisplayName() << getSessionUUID(); } else { qCDebug(avatars) << "Dynamic verification SUCCEEDED for" << getDisplayName() << getSessionUUID(); } diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index 04687e2c84..108786a651 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -20,7 +20,7 @@ macro(SETUP_HIFI_LIBRARY) foreach(SRC ${AVX_SRCS}) if (WIN32) set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS /arch:AVX) - elseif (APPLE OR UNIX) + elseif (APPLE OR (UNIX AND NOT ANDROID)) set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS -mavx) endif() endforeach() @@ -30,7 +30,7 @@ macro(SETUP_HIFI_LIBRARY) foreach(SRC ${AVX2_SRCS}) if (WIN32) set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS /arch:AVX2) - elseif (APPLE OR UNIX) + elseif (APPLE OR (UNIX AND NOT ANDROID)) set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS "-mavx2 -mfma") endif() endforeach() @@ -44,7 +44,7 @@ macro(SETUP_HIFI_LIBRARY) if (COMPILER_SUPPORTS_AVX512) set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS /arch:AVX512) endif() - elseif (APPLE OR UNIX) + elseif (APPLE OR (UNIX AND NOT ANDROID)) check_cxx_compiler_flag("-mavx512f" COMPILER_SUPPORTS_AVX512) if (COMPILER_SUPPORTS_AVX512) set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS -mavx512f) diff --git a/domain-server/resources/web/js/base-settings.js b/domain-server/resources/web/js/base-settings.js index 295013878c..d3f5baa2f9 100644 --- a/domain-server/resources/web/js/base-settings.js +++ b/domain-server/resources/web/js/base-settings.js @@ -250,14 +250,14 @@ $(document).ready(function(){ // set focus to the first input in the new row $target.closest('table').find('tr.inputs input:first').focus(); - } + } else { + var tableRows = sibling.parent(); + var tableBody = tableRows.parent(); - var tableRows = sibling.parent(); - var tableBody = tableRows.parent(); - - // if theres no more siblings, we should jump to a new row - if (sibling.next().length == 0 && tableRows.nextAll().length == 1) { - tableBody.find("." + Settings.ADD_ROW_BUTTON_CLASS).click(); + // if theres no more siblings, we should jump to a new row + if (sibling.next().length == 0 && tableRows.nextAll().length == 1) { + tableBody.find("." + Settings.ADD_ROW_BUTTON_CLASS).click(); + } } } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index de2382e647..d9c0a0a6b3 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -185,6 +186,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : qDebug() << "[VERSION] BUILD_GLOBAL_SERVICES:" << BuildInfo::BUILD_GLOBAL_SERVICES; qDebug() << "[VERSION] We will be using this name to find ICE servers:" << _iceServerAddr; + connect(this, &QCoreApplication::aboutToQuit, this, &DomainServer::aboutToQuit); // make sure we have a fresh AccountManager instance // (need this since domain-server can restart itself and maintain static variables) @@ -432,6 +434,10 @@ DomainServer::~DomainServer() { DependencyManager::destroy(); } +void DomainServer::aboutToQuit() { + crash::annotations::setShutdownState(true); +} + void DomainServer::queuedQuit(QString quitMessage, int exitCode) { if (!quitMessage.isEmpty()) { qWarning() << qPrintable(quitMessage); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 5e8eee53fe..c725688b67 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -136,6 +136,8 @@ private slots: void tokenGrantFinished(); void profileRequestFinished(); + void aboutToQuit(); + signals: void iceServerChanged(); void userConnected(); diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp index 7aea9cc3d4..ba12349347 100644 --- a/domain-server/src/main.cpp +++ b/domain-server/src/main.cpp @@ -15,9 +15,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include #include #include -#include #include "DomainServer.h" @@ -32,6 +33,7 @@ int main(int argc, char* argv[]) { // use a do-while to handle domain-server restart do { + crash::annotations::setShutdownState(false); DomainServer domainServer(argc, argv); currentExitCode = domainServer.exec(); } while (currentExitCode == DomainServer::EXIT_CODE_REBOOT); @@ -39,4 +41,3 @@ int main(int argc, char* argv[]) { qInfo() << "Quitting."; return currentExitCode; } - diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 4bcd7aaf0f..bcd3f269e8 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -70,12 +70,16 @@ file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h") GroupSources("src") list(APPEND INTERFACE_SRCS ${RESOURCES_RCC}) +# grab the Objective-C sources on OS X +if (APPLE) + file(GLOB_RECURSE INTERFACE_OBJCPP_SRCS "src/*.m" "src/*.mm") + list(APPEND INTERFACE_SRCS ${INTERFACE_OBJCPP_SRCS}) +endif () + # Add SpeechRecognizer if on Windows or OS X, otherwise remove if (WIN32) # Use .cpp and .h files as is. elseif (APPLE) - file(GLOB INTERFACE_OBJCPP_SRCS "src/SpeechRecognizer.mm") - set(INTERFACE_SRCS ${INTERFACE_SRCS} ${INTERFACE_OBJCPP_SRCS}) get_filename_component(SPEECHRECOGNIZER_CPP "src/SpeechRecognizer.cpp" ABSOLUTE) list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP}) else () diff --git a/interface/resources/avatar/animations/emote_clap01_all.fbx b/interface/resources/avatar/animations/emote_clap01_all.fbx index 3076b3e34f..61cec1c19c 100644 Binary files a/interface/resources/avatar/animations/emote_clap01_all.fbx and b/interface/resources/avatar/animations/emote_clap01_all.fbx differ diff --git a/interface/resources/avatar/animations/emote_clap02_all.fbx b/interface/resources/avatar/animations/emote_clap02_all.fbx index 7474b49763..ba4db90363 100644 Binary files a/interface/resources/avatar/animations/emote_clap02_all.fbx and b/interface/resources/avatar/animations/emote_clap02_all.fbx differ diff --git a/interface/resources/avatar/animations/emote_clap03_all.fbx b/interface/resources/avatar/animations/emote_clap03_all.fbx index 317c03c197..e04f656610 100644 Binary files a/interface/resources/avatar/animations/emote_clap03_all.fbx and b/interface/resources/avatar/animations/emote_clap03_all.fbx differ diff --git a/interface/resources/avatar/animations/emote_disagree_annoyedheadshake.fbx b/interface/resources/avatar/animations/emote_disagree_annoyedheadshake.fbx index 01c9e5f79e..976c72ba85 100644 Binary files a/interface/resources/avatar/animations/emote_disagree_annoyedheadshake.fbx and b/interface/resources/avatar/animations/emote_disagree_annoyedheadshake.fbx differ diff --git a/interface/resources/avatar/animations/emote_disagree_drophead.fbx b/interface/resources/avatar/animations/emote_disagree_drophead.fbx index e82c2fb269..68899e6e90 100644 Binary files a/interface/resources/avatar/animations/emote_disagree_drophead.fbx and b/interface/resources/avatar/animations/emote_disagree_drophead.fbx differ diff --git a/interface/resources/avatar/animations/emote_disagree_thoughtfulheadshake.fbx b/interface/resources/avatar/animations/emote_disagree_thoughtfulheadshake.fbx index 214b5a3cd8..17691b8dc7 100644 Binary files a/interface/resources/avatar/animations/emote_disagree_thoughtfulheadshake.fbx and b/interface/resources/avatar/animations/emote_disagree_thoughtfulheadshake.fbx differ diff --git a/interface/resources/avatar/animations/emote_point01_aimoffsets.fbx b/interface/resources/avatar/animations/emote_point01_aimoffsets.fbx new file mode 100644 index 0000000000..84524168e7 Binary files /dev/null and b/interface/resources/avatar/animations/emote_point01_aimoffsets.fbx differ diff --git a/interface/resources/avatar/animations/sitting_emote_agree_acknowledge.fbx b/interface/resources/avatar/animations/sitting_emote_agree_acknowledge.fbx index d377e6bbe7..368228ff4e 100644 Binary files a/interface/resources/avatar/animations/sitting_emote_agree_acknowledge.fbx and b/interface/resources/avatar/animations/sitting_emote_agree_acknowledge.fbx differ diff --git a/interface/resources/avatar/animations/sitting_emote_agree_headnod.fbx b/interface/resources/avatar/animations/sitting_emote_agree_headnod.fbx index 1bc297ba60..ad72d31881 100644 Binary files a/interface/resources/avatar/animations/sitting_emote_agree_headnod.fbx and b/interface/resources/avatar/animations/sitting_emote_agree_headnod.fbx differ diff --git a/interface/resources/avatar/animations/sitting_emote_agree_headnodyes.fbx b/interface/resources/avatar/animations/sitting_emote_agree_headnodyes.fbx index c69cbbe805..2b24011b99 100644 Binary files a/interface/resources/avatar/animations/sitting_emote_agree_headnodyes.fbx and b/interface/resources/avatar/animations/sitting_emote_agree_headnodyes.fbx differ diff --git a/interface/resources/avatar/animations/sitting_emote_agree_longheadnod.fbx b/interface/resources/avatar/animations/sitting_emote_agree_longheadnod.fbx index aea11137cc..43b160f96a 100644 Binary files a/interface/resources/avatar/animations/sitting_emote_agree_longheadnod.fbx and b/interface/resources/avatar/animations/sitting_emote_agree_longheadnod.fbx differ diff --git a/interface/resources/avatar/animations/sitting_emote_clap02_all.fbx b/interface/resources/avatar/animations/sitting_emote_clap02_all.fbx index 8d0e3d4b44..685af598a0 100644 Binary files a/interface/resources/avatar/animations/sitting_emote_clap02_all.fbx and b/interface/resources/avatar/animations/sitting_emote_clap02_all.fbx differ diff --git a/interface/resources/avatar/animations/sitting_emote_clap03_all.fbx b/interface/resources/avatar/animations/sitting_emote_clap03_all.fbx index 39e55d45f0..c7ae67149e 100644 Binary files a/interface/resources/avatar/animations/sitting_emote_clap03_all.fbx and b/interface/resources/avatar/animations/sitting_emote_clap03_all.fbx differ diff --git a/interface/resources/avatar/animations/sitting_emote_clap_all.fbx b/interface/resources/avatar/animations/sitting_emote_clap_all.fbx index 8121f2907b..09b09e0101 100644 Binary files a/interface/resources/avatar/animations/sitting_emote_clap_all.fbx and b/interface/resources/avatar/animations/sitting_emote_clap_all.fbx differ diff --git a/interface/resources/avatar/animations/sitting_emote_point_aimoffsets.fbx b/interface/resources/avatar/animations/sitting_emote_point_aimoffsets.fbx new file mode 100644 index 0000000000..eb31091a94 Binary files /dev/null and b/interface/resources/avatar/animations/sitting_emote_point_aimoffsets.fbx differ diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index 436f8b5949..fa3a020b23 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -1544,103 +1544,292 @@ "type": "randomSwitchStateMachine" }, { - "children": [ + "children": [ { - "children": [ + "children": [ + { + "children": [ + { + "children": [ + ], + "data": { + "endFrame": 21, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_all.fbx" + }, + "id": "seatedReactionPointIntro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 100, + "loopFlag": true, + "startFrame": 21, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_all.fbx" + }, + "id": "seatedReactionPointLoop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 134, + "loopFlag": false, + "mirrorFlag": false, + "startFrame": 100, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_all.fbx" + }, + "id": "seatedReactionPointOutro", + "type": "clip" + } + ], + "data": { + "currentState": "seatedReactionPointIntro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "seatedReactionPointIntro", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionPointLoop", + "var": "seatedReactionPointIntroOnDone" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionPointLoop", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionPointOutro", + "var": "reactionPointDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "seatedReactionPointOutro", + "interpDuration": 18, + "interpTarget": 18, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "seatedReactionPointLoop", + "var": "reactionPointEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" + }, + "id": "seatedReactionPoint", + "type": "randomSwitchStateMachine" + } ], "data": { - "endFrame": 21, - "loopFlag": false, - "startFrame": 1, - "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_emote_point_all.fbx" + "alpha": 0, + "alphaVar": "seatedPointBlendAlpha", + "blendType": "addAbsolute" }, - "id": "seatedReactionPointIntro", - "type": "clip" + "id": "seatedReactionPointBase", + "type": "blendLinear" }, { "children": [ + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 11, + "loopFlag": true, + "startFrame": 11, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 30, + "loopFlag": true, + "startFrame": 30, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 50, + "loopFlag": true, + "startFrame": 50, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointUp", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 70, + "loopFlag": true, + "startFrame": 70, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointDown", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 90, + "loopFlag": true, + "startFrame": 90, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointUpLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 110, + "loopFlag": true, + "startFrame": 110, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointUpRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 130, + "loopFlag": true, + "startFrame": 130, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointDownLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 150, + "loopFlag": true, + "startFrame": 150, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointDownRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 3, + "loopFlag": true, + "startFrame": 3, + "timeScale": 1, + "url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx" + }, + "id": "seatedPointCenter", + "type": "clip" + } ], "data": { - "endFrame": 100, - "loopFlag": true, - "startFrame": 21, - "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_emote_point_all.fbx" + "alpha": [ + 0, + 0, + 0 + ], + "alphaVar": "pointAroundAlpha", + "centerId": "seatedPointCenter", + "downId": "seatedPointDown", + "downLeftId": "seatedPointDownLeft", + "downRightId": "seatedPointDownRight", + "leftId": "seatedPointLeft", + "rightId": "seatedPointRight", + "upId": "seatedPointUp", + "upLeftId": "seatedPointUpLeft", + "upRightId": "seatedPointUpRight" }, - "id": "seatedReactionPointLoop", - "type": "clip" - }, - { - "children": [ - ], - "data": { - "endFrame": 134, - "loopFlag": false, - "mirrorFlag": false, - "startFrame": 100, - "timeScale": 1, - "url": "qrc:///avatar/animations/sitting_emote_point_all.fbx" - }, - "id": "seatedReactionPointOutro", - "type": "clip" + "id": "seatedPointAround", + "type": "blendDirectional" } ], "data": { - "currentState": "seatedReactionPointIntro", - "randomSwitchTimeMax": 10, - "randomSwitchTimeMin": 1, - "states": [ - { - "easingType": "easeInOutQuad", - "id": "seatedReactionPointIntro", - "interpDuration": 18, - "interpTarget": 18, - "interpType": "evaluateBoth", - "priority": 1, - "resume": false, - "transitions": [ - { - "randomSwitchState": "seatedReactionPointLoop", - "var": "seatedReactionPointIntroOnDone" - } - ] - }, - { - "easingType": "easeInOutQuad", - "id": "seatedReactionPointLoop", - "interpDuration": 18, - "interpTarget": 18, - "interpType": "evaluateBoth", - "priority": 0, - "resume": false, - "transitions": [ - { - "randomSwitchState": "seatedReactionPointOutro", - "var": "reactionPointDisabled" - } - ] - }, - { - "easingType": "easeInOutQuad", - "id": "seatedReactionPointOutro", - "interpDuration": 18, - "interpTarget": 18, - "interpType": "evaluateBoth", - "priority": 0, - "resume": false, - "transitions": [ - { - "randomSwitchState": "seatedReactionPointLoop", - "var": "reactionPointEnabled" - } - ] - } - ], - "triggerRandomSwitch": "" + "alpha": 0, + "alphaVar": "pointBlendAlpha", + "blendType": "addAbsolute" }, "id": "seatedReactionPoint", - "type": "randomSwitchStateMachine" + "type": "blendLinear" } ], "data": { @@ -2802,7 +2991,7 @@ "children": [ ], "data": { - "endFrame": 92, + "endFrame": 84, "loopFlag": false, "startFrame": 1, "timeScale": 1, @@ -2815,7 +3004,7 @@ "children": [ ], "data": { - "endFrame": 158, + "endFrame": 100, "loopFlag": false, "startFrame": 1, "timeScale": 1, @@ -3531,97 +3720,275 @@ "children": [ { "children": [ + { + "children": [ + ], + "data": { + "endFrame": 21, + "loopFlag": false, + "startFrame": 1, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_all.fbx" + }, + "id": "reactionPointIntro", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 100, + "loopFlag": true, + "startFrame": 21, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_all.fbx" + }, + "id": "reactionPointLoop", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "endFrame": 134, + "loopFlag": false, + "startFrame": 100, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_all.fbx" + }, + "id": "reactionPointOutro", + "type": "clip" + } ], "data": { - "endFrame": 21, - "loopFlag": false, - "startFrame": 1, - "timeScale": 1, - "url": "qrc:///avatar/animations/emote_point01_all.fbx" + "currentState": "reactionPointIntro", + "randomSwitchTimeMax": 10, + "randomSwitchTimeMin": 1, + "states": [ + { + "easingType": "easeInOutQuad", + "id": "reactionPointIntro", + "interpDuration": 1, + "interpTarget": 1, + "interpType": "evaluateBoth", + "priority": 1, + "resume": false, + "transitions": [ + { + "randomSwitchState": "reactionPointLoop", + "var": "reactionPointIntroOnDone" + } + ] + }, + { + "id": "reactionPointLoop", + "interpDuration": 1, + "interpTarget": 1, + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "reactionPointOutro", + "var": "reactionPointDisabled" + } + ] + }, + { + "easingType": "easeInOutQuad", + "id": "reactionPointOutro", + "interpDuration": 6, + "interpTarget": 6, + "interpType": "evaluateBoth", + "priority": 0, + "resume": false, + "transitions": [ + { + "randomSwitchState": "reactionPointLoop", + "var": "reactionPointEnabled" + } + ] + } + ], + "triggerRandomSwitch": "" }, - "id": "reactionPointIntro", - "type": "clip" + "id": "reactionPoint", + "type": "randomSwitchStateMachine" }, { "children": [ + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 11, + "loopFlag": true, + "startFrame": 11, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 30, + "loopFlag": true, + "startFrame": 30, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 50, + "loopFlag": true, + "startFrame": 50, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointUp", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 70, + "loopFlag": true, + "startFrame": 70, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointDown", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 90, + "loopFlag": true, + "startFrame": 90, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointUpLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 110, + "loopFlag": true, + "startFrame": 110, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointUpRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 130, + "loopFlag": true, + "startFrame": 130, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointDownLeft", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 150, + "loopFlag": true, + "startFrame": 150, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointDownRight", + "type": "clip" + }, + { + "children": [ + ], + "data": { + "baseFrame": 1, + "baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx", + "blendType": "addAbsolute", + "endFrame": 3, + "loopFlag": true, + "startFrame": 3, + "timeScale": 1, + "url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx" + }, + "id": "idlePointCenter", + "type": "clip" + } ], "data": { - "endFrame": 100, - "loopFlag": true, - "startFrame": 21, - "timeScale": 1, - "url": "qrc:///avatar/animations/emote_point01_all.fbx" + "alpha": [ + 0, + 0, + 0 + ], + "alphaVar": "pointAroundAlpha", + "centerId": "idlePointCenter", + "downId": "idlePointDown", + "downLeftId": "idlePointDownLeft", + "downRightId": "idlePointDownRight", + "leftId": "idlePointLeft", + "rightId": "idlePointRight", + "upId": "idlePointUp", + "upLeftId": "idlePointUpLeft", + "upRightId": "idlePointUpRight" }, - "id": "reactionPointLoop", - "type": "clip" - }, - { - "children": [ - ], - "data": { - "endFrame": 134, - "loopFlag": false, - "startFrame": 100, - "timeScale": 1, - "url": "qrc:///avatar/animations/emote_point01_all.fbx" - }, - "id": "reactionPointOutro", - "type": "clip" + "id": "idlePointAround", + "type": "blendDirectional" } ], "data": { - "currentState": "reactionPointIntro", - "randomSwitchTimeMax": 10, - "randomSwitchTimeMin": 1, - "states": [ - { - "easingType": "easeInOutQuad", - "id": "reactionPointIntro", - "interpDuration": 1, - "interpTarget": 1, - "interpType": "evaluateBoth", - "priority": 1, - "resume": false, - "transitions": [ - { - "randomSwitchState": "reactionPointLoop", - "var": "reactionPointIntroOnDone" - } - ] - }, - { - "id": "reactionPointLoop", - "interpDuration": 1, - "interpTarget": 1, - "priority": 0, - "resume": false, - "transitions": [ - { - "randomSwitchState": "reactionPointOutro", - "var": "reactionPointDisabled" - } - ] - }, - { - "easingType": "easeInOutQuad", - "id": "reactionPointOutro", - "interpDuration": 6, - "interpTarget": 6, - "interpType": "evaluateBoth", - "priority": 0, - "resume": false, - "transitions": [ - { - "randomSwitchState": "reactionPointLoop", - "var": "reactionPointEnabled" - } - ] - } - ], - "triggerRandomSwitch": "" + "alpha": 0, + "alphaVar": "pointBlendAlpha", + "blendType": "addAbsolute" }, "id": "reactionPoint", - "type": "randomSwitchStateMachine" + "type": "blendLinear" } ], "data": { @@ -4610,9 +4977,9 @@ { "easingType": "easeInOutQuad", "id": "idle", - "interpDuration": 10, - "interpTarget": 10, - "interpType": "snapshotPrev", + "interpDuration": 20, + "interpTarget": 20, + "interpType": "evaluateBoth", "transitions": [ { "state": "WALKFWD", @@ -5683,16 +6050,16 @@ "upLeftId": "lookUpLeft", "upRightId": "lookUpRight" }, - "id": "lookAround", + "id": "lookAroundBlend", "type": "blendDirectional" } ], "data": { "alpha": 0, - "alphaVar": "additiveBlendAlpha", + "alphaVar": "lookBlendAlpha", "blendType": "addAbsolute" }, - "id": "additiveBlend", + "id": "lookAround", "type": "blendLinear" } ], diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index ff02142891..eb07c9a6dd 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -3,8 +3,10 @@ "channels": [ { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.Q", "when": ["!Application.CameraSelfie"," !Application.CameraLookAt", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" }, { "comment" : "Mouse turn need to be small continuous increments", @@ -122,18 +124,18 @@ }, { "from": { "makeAxis" : [ - ["Keyboard.Q"], - ["Keyboard.E"] - ] + ["Keyboard.A"], + ["Keyboard.D"] + ] }, "when": ["Application.CameraLookAt", "!Keyboard.Control"], "to": "Actions.Yaw" }, { "from": { "makeAxis" : [ - ["Keyboard.E"], - ["Keyboard.Q"] - ] + ["Keyboard.A"], + ["Keyboard.D"] + ] }, "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.Yaw" @@ -198,7 +200,7 @@ "to": "Actions.DeltaPitch", "filters": [ - { "type": "scale", "scale": 0.3 } + { "type": "scale", "scale": 0.2 } ] }, @@ -207,7 +209,7 @@ "to": "Actions.DeltaPitch", "filters": [ - { "type": "scale", "scale": 0.3 } + { "type": "scale", "scale": 0.2 } ] }, @@ -215,10 +217,6 @@ { "from": "Keyboard.S", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.S", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.W", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.A", "when": "Application.CameraLookAt", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.D", "when": "Application.CameraLookAt", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.A", "when": "Application.CameraSelfie", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.D", "when": "Application.CameraSelfie", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" }, { "from": "Keyboard.C", "when": "!Keyboard.Control", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, diff --git a/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml b/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml index 050515da37..e600924b42 100644 --- a/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml +++ b/interface/resources/qml/hifi/tablet/+webengine/BlocksWebView.qml @@ -1,6 +1,6 @@ import QtQuick 2.0 import QtWebEngine 1.5 -import "../../controls" as Controls +import "../../../controls" as Controls Controls.TabletWebView { profile: WebEngineProfile { httpUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"} diff --git a/interface/src/AppNapDisabler.h b/interface/src/AppNapDisabler.h new file mode 100644 index 0000000000..b3cfc3038d --- /dev/null +++ b/interface/src/AppNapDisabler.h @@ -0,0 +1,24 @@ +// +// AppNapDisabler.h +// interface/src +// +// Copyright 2019 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_AppNapDisabler_h +#define hifi_AppNapDisabler_h + +#import + +class AppNapDisabler { +public: + AppNapDisabler(); + ~AppNapDisabler(); +private: + id _activity; +}; + +#endif // hifi_AppNapDisabler_h diff --git a/interface/src/AppNapDisabler.mm b/interface/src/AppNapDisabler.mm new file mode 100644 index 0000000000..0c760fad5f --- /dev/null +++ b/interface/src/AppNapDisabler.mm @@ -0,0 +1,28 @@ +// +// AppNapDisabler.mm +// interface/src +// +// Copyright 2019 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 +#ifdef Q_OS_MAC + +#include "AppNapDisabler.h" + +#import + +AppNapDisabler::AppNapDisabler() { + _activity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityUserInitiated reason:@"Audio is in use"]; + [_activity retain]; +} + +AppNapDisabler::~AppNapDisabler() { + [[NSProcessInfo processInfo] endActivity:_activity]; + [_activity release]; +} + +#endif // Q_OS_MAC diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7c08f7d978..4766353eba 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -106,8 +106,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -265,6 +265,12 @@ extern "C" { #include "AndroidHelper.h" #endif +#if defined(Q_OS_MAC) +// On Mac OS, disable App Nap to prevent audio glitches while running in the background +#include "AppNapDisabler.h" +static AppNapDisabler appNapDisabler; // disabled, while in scope +#endif + #include "graphics/RenderEventHandler.h" Q_LOGGING_CATEGORY(trace_app_input_mouse, "trace.app.input.mouse") @@ -886,6 +892,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -2906,6 +2913,7 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); @@ -3431,6 +3439,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { // Caches surfaceContext->setContextProperty("AnimationCache", DependencyManager::get().data()); surfaceContext->setContextProperty("TextureCache", DependencyManager::get().data()); + surfaceContext->setContextProperty("MaterialCache", DependencyManager::get().data()); surfaceContext->setContextProperty("ModelCache", DependencyManager::get().data()); surfaceContext->setContextProperty("SoundCache", DependencyManager::get().data()); @@ -7458,6 +7467,7 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine // Caches scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("MaterialCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); @@ -8491,6 +8501,8 @@ void Application::toggleLogDialog() { Qt::WindowFlags flags = _logDialog->windowFlags() | Qt::Tool; _logDialog->setWindowFlags(flags); } +#else + Q_UNUSED(keepOnTop) #endif } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4f6bd616b4..090694b639 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -99,10 +99,16 @@ static const QString USER_RECENTER_MODEL_FORCE_STAND = QStringLiteral("ForceStan static const QString USER_RECENTER_MODEL_AUTO = QStringLiteral("Auto"); static const QString USER_RECENTER_MODEL_DISABLE_HMD_LEAN = QStringLiteral("DisableHMDLean"); -const QString HEAD_BLENDING_NAME = "lookAroundAlpha"; -const QString HEAD_ALPHA_NAME = "additiveBlendAlpha"; +const QString HEAD_BLEND_DIRECTIONAL_ALPHA_NAME = "lookAroundAlpha"; +const QString HEAD_BLEND_LINEAR_ALPHA_NAME = "lookBlendAlpha"; const float HEAD_ALPHA_BLENDING = 1.0f; +const QString POINT_REACTION_NAME = "point"; +const QString POINT_BLEND_DIRECTIONAL_ALPHA_NAME = "pointAroundAlpha"; +const QString POINT_BLEND_LINEAR_ALPHA_NAME = "pointBlendAlpha"; +const QString POINT_REF_JOINT_NAME = "RightShoulder"; +const float POINT_ALPHA_BLENDING = 1.0f; + MyAvatar::SitStandModelType stringToUserRecenterModel(const QString& str) { if (str == USER_RECENTER_MODEL_FORCE_SIT) { return MyAvatar::ForceSit; @@ -948,13 +954,16 @@ void MyAvatar::simulate(float deltaTime, bool inView) { qCDebug(interfaceapp) << "MyAvatar::simulate headPosition is NaN"; headPosition = glm::vec3(0.0f); } - head->setPosition(headPosition); head->setScale(getModelScale()); head->simulate(deltaTime); CameraMode mode = qApp->getCamera().getMode(); if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { - updateHeadLookAt(deltaTime); + if (!_pointAtActive || !_isPointTargetValid) { + updateHeadLookAt(deltaTime); + } else { + resetHeadLookAt(); + } } else if (_headLookAtActive){ resetHeadLookAt(); _headLookAtActive = false; @@ -2709,7 +2718,11 @@ void MyAvatar::updateMotors() { if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { if (_characterController.getState() == CharacterController::State::Hover || _characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) { - motorRotation = getMyHead()->getHeadOrientation(); + if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT || qApp->getCamera().getMode() == CAMERA_MODE_SELFIE) { + motorRotation = getLookAtRotation(); + } else { + motorRotation = getMyHead()->getHeadOrientation(); + } } else { // non-hovering = walking: follow camera twist about vertical but not lift // we decompose camera's rotation and store the twist part in motorRotation @@ -3512,6 +3525,12 @@ void MyAvatar::updateOrientation(float deltaTime) { float timeScale = deltaTime * FPS; bool faceForward = false; + bool isMovingFwdBwd = getDriveKey(TRANSLATE_Z) != 0.0f; + bool isMovingSideways = getDriveKey(TRANSLATE_X) != 0.0f; + bool isCameraYawing = getDriveKey(DELTA_YAW) + getDriveKey(STEP_YAW) + getDriveKey(YAW) != 0.0f; + bool isRotatingWhileSeated = !isCameraYawing && isMovingSideways && _characterController.getSeated(); + glm::quat previousOrientation = getWorldOrientation(); + if (!computeLookAt) { setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); _lookAtCameraTarget = eyesPosition + getWorldOrientation() * Vectors::FRONT; @@ -3534,9 +3553,7 @@ void MyAvatar::updateOrientation(float deltaTime) { _lookAtPitch = _previousLookAtPitch; } } - bool isMovingFwdBwd = getDriveKey(TRANSLATE_Z) != 0.0f; - bool isMovingSideways = getDriveKey(TRANSLATE_X) != 0.0f; - bool isRotatingWhileSeated = isMovingSideways && _characterController.getSeated(); + faceForward = isMovingFwdBwd || (isMovingSideways && !isRotatingWhileSeated); // Blend the avatar orientation with the camera look at if moving forward. if (faceForward || _shouldTurnToFaceCamera) { @@ -3636,9 +3653,13 @@ void MyAvatar::updateOrientation(float deltaTime) { ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight); cameraVector = (ajustedYawVector * _lookAtPitch) * Vectors::FRONT; } - if (frontBackDot < triggerAngle) { - _shouldTurnToFaceCamera = true; - _firstPersonSteadyHeadTimer = 0.0f; + if (!isRotatingWhileSeated) { + if (frontBackDot < triggerAngle) { + _shouldTurnToFaceCamera = true; + _firstPersonSteadyHeadTimer = 0.0f; + } + } else { + setWorldOrientation(previousOrientation); } } else if (frontBackDot > glm::sin(glm::radians(reorientAngle))) { _shouldTurnToFaceCamera = false; @@ -3650,8 +3671,7 @@ void MyAvatar::updateOrientation(float deltaTime) { glm::vec3 targetPoint = eyesPosition + glm::normalize(cameraVector); const float LOOKAT_MIX_ALPHA = 0.25f; - - if (getDriveKey(TRANSLATE_Y) == 0.0f) { + if (!isFlying() || !hasDriveInput()) { // Approximate the head's look at vector to the camera look at vector with some delay. float mixAlpha = LOOKAT_MIX_ALPHA * timeScale; if (mixAlpha > 1.0f) { @@ -6137,6 +6157,10 @@ bool MyAvatar::beginReaction(QString reactionName) { if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) { std::lock_guard guard(_reactionLock); _reactionEnabledRefCounts[reactionIndex]++; + if (reactionName == POINT_REACTION_NAME) { + _pointAtActive = true; + _isPointTargetValid = true; + } return true; } return false; @@ -6146,13 +6170,18 @@ bool MyAvatar::endReaction(QString reactionName) { int reactionIndex = beginEndReactionNameToIndex(reactionName); if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) { std::lock_guard guard(_reactionLock); + bool wasReactionActive = true; if (_reactionEnabledRefCounts[reactionIndex] > 0) { _reactionEnabledRefCounts[reactionIndex]--; - return true; + wasReactionActive = true; } else { _reactionEnabledRefCounts[reactionIndex] = 0; - return false; + wasReactionActive = false; } + if (reactionName == POINT_REACTION_NAME) { + _pointAtActive = _reactionEnabledRefCounts[reactionIndex] > 0; + } + return wasReactionActive; } return false; } @@ -6163,10 +6192,13 @@ void MyAvatar::updateRigControllerParameters(Rig::ControllerParameters& params) for (int i = 0; i < TRIGGER_REACTION_NAMES.size(); i++) { params.reactionTriggers[i] = _reactionTriggers[i]; } - + int pointReactionIndex = beginEndReactionNameToIndex("point"); for (int i = 0; i < BEGIN_END_REACTION_NAMES.size(); i++) { // copy current state into params. params.reactionEnabledFlags[i] = _reactionEnabledRefCounts[i] > 0; + if (params.reactionEnabledFlags[i] && i == pointReactionIndex) { + params.reactionEnabledFlags[i] = _isPointTargetValid; + } } for (int i = 0; i < TRIGGER_REACTION_NAMES.size(); i++) { @@ -6692,10 +6724,42 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera) getHead()->setLookAtPosition(lookAtSpot); } +glm::vec3 MyAvatar::aimToBlendValues(const glm::vec3& aimVector, const glm::quat& frameOrientation) { + // This method computes the values for the directional blending animation node + + glm::vec3 uVector = glm::normalize(frameOrientation * Vectors::UNIT_X); + glm::vec3 vVector = glm::normalize(frameOrientation * Vectors::UNIT_Y); + + glm::vec3 aimDirection; + if (glm::length(aimVector) > EPSILON) { + aimDirection = glm::normalize(aimVector); + } else { + // aim vector is zero + return glm::vec3(); + } + + float xDot = glm::dot(uVector, aimDirection); + float yDot = glm::dot(vVector, aimDirection); + + // Make sure dot products are in range to avoid acosf returning NaN + xDot = glm::min(glm::max(xDot, -1.0f), 1.0f); + yDot = glm::min(glm::max(yDot, -1.0f), 1.0f); + + float xAngle = acosf(xDot); + float yAngle = acosf(yDot); + + // xBlend and yBlend are the values from -1.0 to 1.0 that set the directional blending. + // We compute them using the angles (0 to PI/2) => (1.0 to 0.0) and (PI/2 to PI) => (0.0 to -1.0) + float xBlend = -(xAngle - 0.5f * PI) / (0.5f * PI); + float yBlend = -(yAngle - 0.5f * PI) / (0.5f * PI); + glm::vec3 blendValues = glm::vec3(xBlend, yBlend, 0.0f); + return blendValues; +} + void MyAvatar::resetHeadLookAt() { if (_skeletonModelLoaded) { - _skeletonModel->getRig().setDirectionalBlending(HEAD_BLENDING_NAME, glm::vec3(), - HEAD_ALPHA_NAME, HEAD_ALPHA_BLENDING); + _skeletonModel->getRig().setDirectionalBlending(HEAD_BLEND_DIRECTIONAL_ALPHA_NAME, glm::vec3(), + HEAD_BLEND_LINEAR_ALPHA_NAME, HEAD_ALPHA_BLENDING); } } @@ -6711,39 +6775,10 @@ void MyAvatar::resetLookAtRotation(const glm::vec3& avatarPosition, const glm::q void MyAvatar::updateHeadLookAt(float deltaTime) { if (_skeletonModelLoaded) { glm::vec3 lookAtTarget = _scriptControlsHeadLookAt ? _lookAtScriptTarget : _lookAtCameraTarget; - glm::vec3 avatarXVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_X); - glm::vec3 avatarYVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_Y); - glm::vec3 avatarZVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_Z); - glm::vec3 headToTargetVector = lookAtTarget - getDefaultEyePosition(); - if (glm::length(headToTargetVector) > EPSILON) { - headToTargetVector = glm::normalize(headToTargetVector); - } else { - // The target point is the avatar head - return; - } - - float xDot = glm::dot(avatarXVector, headToTargetVector); - float yDot = glm::dot(avatarYVector, headToTargetVector); - float zDot = glm::dot(avatarZVector, headToTargetVector); - // Force the head to look at one of the sides when the look at point is behind the avatar - if (zDot > 0.0f && xDot != 0.0f) { - //xDot /= fabsf(xDot); - } - - // Make sure dot products are in range to avoid acosf returning NaN - xDot = glm::min(glm::max(xDot, -1.0f), 1.0f); - yDot = glm::min(glm::max(yDot, -1.0f), 1.0f); - - float xAngle = acosf(xDot); - float yAngle = acosf(yDot); - - // xBlend and yBlend are the values from -1.0 to 1.0 that set the directional blending. - // We compute them using the angles (0 to PI/2) => (1.0 to 0.0) and (PI/2 to PI) => (0.0 to -1.0) - float xBlend = -(xAngle - 0.5f * PI) / (0.5f * PI); - float yBlend = -(yAngle - 0.5f * PI) / (0.5f * PI); - glm::vec3 lookAtBlend = glm::vec3(xBlend, yBlend, 0.0f); - _skeletonModel->getRig().setDirectionalBlending(HEAD_BLENDING_NAME, lookAtBlend, - HEAD_ALPHA_NAME, HEAD_ALPHA_BLENDING); + glm::vec3 aimVector = lookAtTarget - getDefaultEyePosition(); + glm::vec3 lookAtBlend = MyAvatar::aimToBlendValues(aimVector, getWorldOrientation()); + _skeletonModel->getRig().setDirectionalBlending(HEAD_BLEND_DIRECTIONAL_ALPHA_NAME, lookAtBlend, + HEAD_BLEND_LINEAR_ALPHA_NAME, HEAD_ALPHA_BLENDING); if (_scriptControlsHeadLookAt) { _scriptHeadControlTimer += deltaTime; @@ -6774,3 +6809,30 @@ glm::vec3 MyAvatar::getLookAtPivotPoint() { return yAxisEyePosition; } +bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) { + if (QThread::currentThread() != thread()) { + bool result = false; + BLOCKING_INVOKE_METHOD(this, "setPointAt", Q_RETURN_ARG(bool, result), + Q_ARG(const glm::vec3&, pointAtTarget)); + return result; + } + if (_skeletonModelLoaded && _pointAtActive) { + glm::vec3 aimVector = pointAtTarget - getJointPosition(POINT_REF_JOINT_NAME); + _isPointTargetValid = glm::dot(aimVector, getWorldOrientation() * Vectors::FRONT) > 0.0f; + if (_isPointTargetValid) { + glm::vec3 pointAtBlend = MyAvatar::aimToBlendValues(aimVector, getWorldOrientation()); + _skeletonModel->getRig().setDirectionalBlending(POINT_BLEND_DIRECTIONAL_ALPHA_NAME, pointAtBlend, + POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING); + } + return _isPointTargetValid; + } + return false; +} + +void MyAvatar::resetPointAt() { + if (_skeletonModelLoaded) { + _skeletonModel->getRig().setDirectionalBlending(POINT_BLEND_DIRECTIONAL_ALPHA_NAME, glm::vec3(), + POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING); + } +} + diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e099ab0dbe..3dcbc2634d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1765,6 +1765,16 @@ public: */ Q_INVOKABLE glm::vec3 getHeadLookAt() { return _lookAtCameraTarget; } + /**jsdoc + * Aims the pointing directional blending towards the provided target point. + * The "point" reaction should be triggered before using this method. + * MyAvatar.beginReaction("point") + * Returns true if the target point lays in front of the avatar. + * @function MyAvatar.setPointAt + * @param {Vec3} pointAtTarget - The target point in world coordinates. + */ + Q_INVOKABLE bool setPointAt(const glm::vec3& pointAtTarget); + glm::quat getLookAtRotation() { return _lookAtYaw * _lookAtPitch; } /**jsdoc @@ -2655,6 +2665,8 @@ private: bool _scriptControlsHeadLookAt { false }; float _scriptHeadControlTimer { 0.0f }; float _firstPersonSteadyHeadTimer { 0.0f }; + bool _pointAtActive { false }; + bool _isPointTargetValid { true }; Setting::Handle _realWorldFieldOfView; Setting::Handle _useAdvancedMovementControls; @@ -2683,6 +2695,8 @@ private: void updateHeadLookAt(float deltaTime); void resetHeadLookAt(); void resetLookAtRotation(const glm::vec3& avatarPosition, const glm::quat& avatarOrientation); + void resetPointAt(); + static glm::vec3 aimToBlendValues(const glm::vec3& aimVector, const glm::quat& frameOrientation); // Avatar Preferences QUrl _fullAvatarURLFromPreferences; diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index eae4a61552..8cc45a3bf5 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -282,9 +282,9 @@ void AudioDeviceList::onDevicesChanged(const QList& devices if (deviceInfo.isDefault()) { if (deviceInfo.getMode() == QAudio::AudioInput) { - device.display = "Default microphone (recommended)"; + device.display = "Computer's default microphone (recommended)"; } else { - device.display = "Default audio (recommended)"; + device.display = "Computer's default audio (recommended)"; } } else { device.display = device.info.deviceName() diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 51ff537d51..98431e1dca 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -359,7 +359,7 @@ protected: A, B }; - NetworkAnimState() : clipNodeEnum(NetworkAnimState::None) {} + NetworkAnimState() : clipNodeEnum(NetworkAnimState::None), fps(30.0f), loop(false), firstFrame(0.0f), lastFrame(0.0f), blendTime(FLT_MAX) {} NetworkAnimState(ClipNodeEnum clipNodeEnumIn, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) : clipNodeEnum(clipNodeEnumIn), url(urlIn), fps(fpsIn), loop(loopIn), firstFrame(firstFrameIn), lastFrame(lastFrameIn) {} diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 5e7a49c015..5e1f285a6c 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -60,25 +60,13 @@ const int AudioClient::MIN_BUFFER_FRAMES = 1; const int AudioClient::MAX_BUFFER_FRAMES = 20; -static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100; - #if defined(Q_OS_ANDROID) static const int CHECK_INPUT_READS_MSECS = 2000; static const int MIN_READS_TO_CONSIDER_INPUT_ALIVE = 10; #endif -static const auto DEFAULT_POSITION_GETTER = []{ return Vectors::ZERO; }; -static const auto DEFAULT_ORIENTATION_GETTER = [] { return Quaternions::IDENTITY; }; - -static const int DEFAULT_BUFFER_FRAMES = 1; - -// OUTPUT_CHANNEL_COUNT is audio pipeline output format, which is always 2 channel. -// _outputFormat.channelCount() is device output format, which may be 1 or multichannel. -static const int OUTPUT_CHANNEL_COUNT = 2; - -static const bool DEFAULT_STARVE_DETECTION_ENABLED = true; -static const int STARVE_DETECTION_THRESHOLD = 3; -static const int STARVE_DETECTION_PERIOD = 10 * 1000; // 10 Seconds +const AudioClient::AudioPositionGetter AudioClient::DEFAULT_POSITION_GETTER = []{ return Vectors::ZERO; }; +const AudioClient::AudioOrientationGetter AudioClient::DEFAULT_ORIENTATION_GETTER = [] { return Quaternions::IDENTITY; }; Setting::Handle dynamicJitterBufferEnabled("dynamicJitterBuffersEnabled", InboundAudioStream::DEFAULT_DYNAMIC_JITTER_BUFFER_ENABLED); @@ -272,55 +260,7 @@ static inline float convertToFloat(int16_t sample) { return (float)sample * (1 / 32768.0f); } -AudioClient::AudioClient() : - AbstractAudioInterface(), - _gate(this), - _audioInput(NULL), - _dummyAudioInput(NULL), - _desiredInputFormat(), - _inputFormat(), - _numInputCallbackBytes(0), - _audioOutput(NULL), - _desiredOutputFormat(), - _outputFormat(), - _outputFrameSize(0), - _numOutputCallbackBytes(0), - _loopbackAudioOutput(NULL), - _loopbackOutputDevice(NULL), - _inputRingBuffer(0), - _localInjectorsStream(0, 1), - _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), - _isStereoInput(false), - _outputStarveDetectionStartTimeMsec(0), - _outputStarveDetectionCount(0), - _outputBufferSizeFrames("audioOutputBufferFrames", DEFAULT_BUFFER_FRAMES), - _sessionOutputBufferSizeFrames(_outputBufferSizeFrames.get()), - _outputStarveDetectionEnabled("audioOutputStarveDetectionEnabled", DEFAULT_STARVE_DETECTION_ENABLED), - _lastRawInputLoudness(0.0f), - _lastSmoothedRawInputLoudness(0.0f), - _lastInputLoudness(0.0f), - _timeSinceLastClip(-1.0f), - _muted(false), - _shouldEchoLocally(false), - _shouldEchoToServer(false), - _isNoiseGateEnabled(true), - _isAECEnabled(true), - _reverb(false), - _reverbOptions(&_scriptReverbOptions), - _inputToNetworkResampler(NULL), - _networkToOutputResampler(NULL), - _localToOutputResampler(NULL), - _loopbackResampler(NULL), - _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), - _outgoingAvatarAudioSequenceNumber(0), - _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), - _stats(&_receivedAudioStream), - _positionGetter(DEFAULT_POSITION_GETTER), -#if defined(Q_OS_ANDROID) - _checkInputTimer(this), - _isHeadsetPluggedIn(false), -#endif - _orientationGetter(DEFAULT_ORIENTATION_GETTER) { +AudioClient::AudioClient() { // avoid putting a lock in the device callback assert(_localSamplesAvailable.is_lock_free()); @@ -554,7 +494,9 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic)); //Use the received manufacturer id to get the device's real name waveInGetDevCaps(wic.wMid, &wic, sizeof(wic)); +#if !defined(NDEBUG) qCDebug(audioclient) << "input device:" << wic.szPname; +#endif deviceName = wic.szPname; } else { WAVEOUTCAPS woc; @@ -562,7 +504,9 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(woc)); //Use the received manufacturer id to get the device's real name waveOutGetDevCaps(woc.wMid, &woc, sizeof(woc)); +#if !defined(NDEBUG) qCDebug(audioclient) << "output device:" << woc.szPname; +#endif deviceName = woc.szPname; } } else { @@ -592,10 +536,10 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { break; } } - +#if !defined(NDEBUG) qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input") << " [" << deviceName << "] [" << foundDevice.deviceName() << "]"; - +#endif return foundDevice; #endif diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index a13943f22e..28a4f23968 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -80,6 +80,9 @@ class QIODevice; class Transform; class NLPacket; +#define DEFAULT_STARVE_DETECTION_ENABLED true +#define DEFAULT_BUFFER_FRAMES 1 + class AudioClient : public AbstractAudioInterface, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -292,6 +295,16 @@ protected: virtual void customDeleter() override; private: + static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES{ 100 }; + // OUTPUT_CHANNEL_COUNT is audio pipeline output format, which is always 2 channel. + // _outputFormat.channelCount() is device output format, which may be 1 or multichannel. + static const int OUTPUT_CHANNEL_COUNT{ 2 }; + static const int STARVE_DETECTION_THRESHOLD{ 3 }; + static const int STARVE_DETECTION_PERIOD{ 10 * 1000 }; // 10 Seconds + + static const AudioPositionGetter DEFAULT_POSITION_GETTER; + static const AudioOrientationGetter DEFAULT_ORIENTATION_GETTER; + friend class CheckDevicesThread; friend class LocalInjectorsThread; @@ -307,9 +320,9 @@ private: float gainForSource(float distance, float volume); #ifdef Q_OS_ANDROID - QTimer _checkInputTimer; + QTimer _checkInputTimer{ this }; long _inputReadsSinceLastCheck = 0l; - bool _isHeadsetPluggedIn; + bool _isHeadsetPluggedIn { false }; #endif class Gate { @@ -336,68 +349,68 @@ private: bool _isSimulatingJitter{ false }; }; - Gate _gate; + Gate _gate{ this }; Mutex _injectorsMutex; - QAudioInput* _audioInput; - QTimer* _dummyAudioInput; + QAudioInput* _audioInput{ nullptr }; + QTimer* _dummyAudioInput{ nullptr }; QAudioFormat _desiredInputFormat; QAudioFormat _inputFormat; - QIODevice* _inputDevice; - int _numInputCallbackBytes; - QAudioOutput* _audioOutput; + QIODevice* _inputDevice{ nullptr }; + int _numInputCallbackBytes{ 0 }; + QAudioOutput* _audioOutput{ nullptr }; std::atomic _audioOutputInitialized { false }; QAudioFormat _desiredOutputFormat; QAudioFormat _outputFormat; - int _outputFrameSize; - int _numOutputCallbackBytes; - QAudioOutput* _loopbackAudioOutput; - QIODevice* _loopbackOutputDevice; - AudioRingBuffer _inputRingBuffer; - LocalInjectorsStream _localInjectorsStream; + int _outputFrameSize{ 0 }; + int _numOutputCallbackBytes{ 0 }; + QAudioOutput* _loopbackAudioOutput{ nullptr }; + QIODevice* _loopbackOutputDevice{ nullptr }; + AudioRingBuffer _inputRingBuffer{ 0 }; + LocalInjectorsStream _localInjectorsStream{ 0 , 1 }; // In order to use _localInjectorsStream as a lock-free pipe, // use it with a single producer/consumer, and track available samples and injectors std::atomic _localSamplesAvailable { 0 }; std::atomic _localInjectorsAvailable { false }; - MixedProcessedAudioStream _receivedAudioStream; - bool _isStereoInput; + MixedProcessedAudioStream _receivedAudioStream{ RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES }; + bool _isStereoInput{ false }; std::atomic _enablePeakValues { false }; - quint64 _outputStarveDetectionStartTimeMsec; - int _outputStarveDetectionCount; + quint64 _outputStarveDetectionStartTimeMsec{ 0 }; + int _outputStarveDetectionCount { 0 }; - Setting::Handle _outputBufferSizeFrames; - int _sessionOutputBufferSizeFrames; - Setting::Handle _outputStarveDetectionEnabled; + Setting::Handle _outputBufferSizeFrames{"audioOutputBufferFrames", DEFAULT_BUFFER_FRAMES}; + int _sessionOutputBufferSizeFrames{ _outputBufferSizeFrames.get() }; + Setting::Handle _outputStarveDetectionEnabled{ "audioOutputStarveDetectionEnabled", DEFAULT_STARVE_DETECTION_ENABLED}; StDev _stdev; QElapsedTimer _timeSinceLastReceived; - float _lastRawInputLoudness; // before mute/gate - float _lastSmoothedRawInputLoudness; - float _lastInputLoudness; // after mute/gate - float _timeSinceLastClip; + float _lastRawInputLoudness{ 0.0f }; // before mute/gate + float _lastSmoothedRawInputLoudness{ 0.0f }; + float _lastInputLoudness{ 0.0f }; // after mute/gate + float _timeSinceLastClip{ -1.0f }; int _totalInputAudioSamples; - bool _muted; - bool _shouldEchoLocally; - bool _shouldEchoToServer; - bool _isNoiseGateEnabled; + bool _muted{ false }; + bool _shouldEchoLocally{ false }; + bool _shouldEchoToServer{ false }; + bool _isNoiseGateEnabled{ true }; bool _warnWhenMuted; - bool _isAECEnabled; + bool _isAECEnabled{ true }; - bool _reverb; + bool _reverb{ false }; AudioEffectOptions _scriptReverbOptions; AudioEffectOptions _zoneReverbOptions; - AudioEffectOptions* _reverbOptions; + AudioEffectOptions* _reverbOptions{ &_scriptReverbOptions }; AudioReverb _sourceReverb { AudioConstants::SAMPLE_RATE }; AudioReverb _listenerReverb { AudioConstants::SAMPLE_RATE }; AudioReverb _localReverb { AudioConstants::SAMPLE_RATE }; // possible streams needed for resample - AudioSRC* _inputToNetworkResampler; - AudioSRC* _networkToOutputResampler; - AudioSRC* _localToOutputResampler; - AudioSRC* _loopbackResampler; + AudioSRC* _inputToNetworkResampler{ nullptr }; + AudioSRC* _networkToOutputResampler{ nullptr }; + AudioSRC* _localToOutputResampler{ nullptr }; + AudioSRC* _loopbackResampler{ nullptr }; // for network audio (used by network audio thread) int16_t _networkScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; @@ -416,7 +429,7 @@ private: int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; float* _localOutputMixBuffer { NULL }; Mutex _localAudioMutex; - AudioLimiter _audioLimiter; + AudioLimiter _audioLimiter{ AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT }; // Adds Reverb void configureReverb(); @@ -445,17 +458,17 @@ private: int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const; int calculateNumberOfFrameSamples(int numBytes) const; - quint16 _outgoingAvatarAudioSequenceNumber; + quint16 _outgoingAvatarAudioSequenceNumber{ 0 }; - AudioOutputIODevice _audioOutputIODevice; + AudioOutputIODevice _audioOutputIODevice{ _localInjectorsStream, _receivedAudioStream, this }; - AudioIOStats _stats; + AudioIOStats _stats{ &_receivedAudioStream }; AudioGate* _audioGate { nullptr }; bool _audioGateOpen { true }; - AudioPositionGetter _positionGetter; - AudioOrientationGetter _orientationGetter; + AudioPositionGetter _positionGetter{ DEFAULT_POSITION_GETTER }; + AudioOrientationGetter _orientationGetter{ DEFAULT_ORIENTATION_GETTER }; glm::vec3 avatarBoundingBoxCorner; glm::vec3 avatarBoundingBoxScale; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b0f3934278..75a7693de8 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -133,7 +133,21 @@ void Avatar::setShowNamesAboveHeads(bool show) { showNamesAboveHeads = show; } +static const char* avatarTransitStatusToStringMap[] = { + "IDLE", + "STARTED", + "PRE_TRANSIT", + "START_TRANSIT", + "TRANSITING", + "END_TRANSIT", + "POST_TRANSIT", + "ENDED", + "ABORT_TRANSIT" +}; + AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { + AvatarTransit::Status previousStatus = _status; + float oneFrameDistance = _isActive ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition); if (oneFrameDistance > (config._minTriggerDistance * _scale)) { if (oneFrameDistance < (config._maxTriggerDistance * _scale)) { @@ -150,6 +164,10 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av reset(); _status = Status::ENDED; } + + if (previousStatus != _status) { + qDebug(avatars_renderer) << "AvatarTransit " << avatarTransitStatusToStringMap[(int)previousStatus] << "->" << avatarTransitStatusToStringMap[_status]; + } return _status; } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 2697d30de4..d11f6dd6f4 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -108,15 +108,6 @@ protected: virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; } virtual void setRenderLayer(RenderLayer value) { _renderLayer = value; } virtual void setPrimitiveMode(PrimitiveMode value) { _primitiveMode = value; } - - template - T withReadLockResult(const std::function& f) { - T result; - withReadLock([&] { - result = f(); - }); - return result; - } signals: void requestRenderUpdate(); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp index 7f61ca78f6..1cff06d919 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp @@ -21,8 +21,10 @@ const uint32_t MAX_RANGE_QUERY_DEPTH = 1; static bool timeElapsed = true; #else const uint32_t MAX_RANGE_QUERY_DEPTH = 10000; +#if !defined(USE_GLES) static bool timeElapsed = false; #endif +#endif #if defined(USE_GLES) static bool hasTimerExtension() { diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp index 36b37083cb..99bcb34547 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp @@ -17,34 +17,6 @@ namespace gpu { namespace gles { - -// returns the FOV from the projection matrix -static inline vec4 extractFov( const glm::mat4& m) { - static const std::array CLIPS{ { - { 1, 0, 0, 1 }, - { -1, 0, 0, 1 }, - { 0, 1, 0, 1 }, - { 0, -1, 0, 1 } - } }; - - glm::mat4 mt = glm::transpose(m); - vec4 v, result; - // Left - v = mt * CLIPS[0]; - result.x = -atanf(v.z / v.x); - // Right - v = mt * CLIPS[1]; - result.y = atanf(v.z / v.x); - // Down - v = mt * CLIPS[2]; - result.z = -atanf(v.z / v.y); - // Up - v = mt * CLIPS[3]; - result.w = atanf(v.z / v.y); - return result; -} - - class GLESFramebuffer : public gl::GLFramebuffer { using Parent = gl::GLFramebuffer; static GLuint allocate() { diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 428c08e9f6..1295fc5722 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -50,6 +50,8 @@ namespace scriptable { * @property {string} emissiveMap * @property {string} albedoMap * @property {string} opacityMap + * @property {string} opacityMapMode + * @property {number|string} opacityCutoff * @property {string} metallicMap * @property {string} specularMap * @property {string} roughnessMap @@ -84,6 +86,8 @@ namespace scriptable { QString emissiveMap; QString albedoMap; QString opacityMap; + QString opacityMapMode; + float opacityCutoff; QString metallicMap; QString specularMap; QString roughnessMap; @@ -94,7 +98,6 @@ namespace scriptable { QString lightMap; QString scatteringMap; std::array texCoordTransforms; - bool defaultFallthrough; std::unordered_map propertyFallthroughs; // not actually exposed to script diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 45f8461a1a..ca9634e365 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -420,6 +420,18 @@ namespace scriptable { obj.setProperty("opacityMap", material.opacityMap); } + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT | graphics::MaterialKey::OPACITY_MASK_MAP_BIT)) { + obj.setProperty("opacityMapMode", FALLTHROUGH); + } else if (material.key.getOpacityMapMode() != graphics::Material::DEFAULT_OPACITY_MAP_MODE) { + obj.setProperty("opacityMapMode", material.opacityMapMode); + } + + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT)) { + obj.setProperty("opacityCutoff", FALLTHROUGH); + } else if (material.key.isOpacityCutoff()) { + obj.setProperty("opacityCutoff", material.opacityCutoff); + } + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) { obj.setProperty("occlusionMap", FALLTHROUGH); } else if (!material.occlusionMap.isEmpty()) { diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index e9cc7930ae..bc610108ec 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -26,6 +26,7 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const roughness = material.roughness; metallic = material.metallic; scattering = material.scattering; + opacityCutoff = material.opacityCutoff; unlit = material.unlit; emissive = material.emissive; albedo = material.albedo; @@ -41,6 +42,8 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const occlusionMap = material.occlusionMap; lightMap = material.lightMap; scatteringMap = material.scatteringMap; + opacityMapMode = material.opacityMapMode; + defaultFallthrough = material.defaultFallthrough; propertyFallthroughs = material.propertyFallthroughs; @@ -55,9 +58,12 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint name = material->getName().c_str(); model = material->getModel().c_str(); opacity = material->getOpacity(); + + opacityMapMode = QString(graphics::MaterialKey::getOpacityMapModeName(material->getOpacityMapMode()).c_str()); roughness = material->getRoughness(); metallic = material->getMetallic(); scattering = material->getScattering(); + opacityCutoff = material->getOpacityCutoff(); unlit = material->isUnlit(); emissive = material->getEmissive(); albedo = material->getAlbedo(); diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index 6be8ec5f68..dffc52e29f 100755 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -14,6 +14,8 @@ #include +#include "GraphicsLogging.h" + using namespace graphics; using namespace gpu; @@ -22,7 +24,26 @@ const float Material::DEFAULT_OPACITY { 1.0f }; const float Material::DEFAULT_ALBEDO { 0.5f }; const float Material::DEFAULT_METALLIC { 0.0f }; const float Material::DEFAULT_ROUGHNESS { 1.0f }; -const float Material::DEFAULT_SCATTERING { 0.0f }; +const float Material::DEFAULT_SCATTERING{ 0.0f }; +const MaterialKey::OpacityMapMode Material::DEFAULT_OPACITY_MAP_MODE{ MaterialKey::OPACITY_MAP_OPAQUE }; +const float Material::DEFAULT_OPACITY_CUTOFF { 0.5f }; + + +std::string MaterialKey::getOpacityMapModeName(OpacityMapMode mode) { + const std::string names[3] = { "OPACITY_MAP_OPAQUE", "OPACITY_MAP_MASK", "OPACITY_MAP_BLEND" }; + return names[mode]; +} + + +bool MaterialKey::getOpacityMapModeFromName(const std::string& modeName, MaterialKey::OpacityMapMode& mode) { + for (int i = OPACITY_MAP_OPAQUE; i <= OPACITY_MAP_BLEND; i++) { + mode = (MaterialKey::OpacityMapMode) i; + if (modeName == getOpacityMapModeName(mode)) { + return true; + } + } + return false; +} Material::Material() { for (int i = 0; i < NUM_TOTAL_FLAGS; i++) { @@ -40,6 +61,7 @@ Material::Material(const Material& material) : _roughness(material._roughness), _metallic(material._metallic), _scattering(material._scattering), + _opacityCutoff(material._opacityCutoff), _texcoordTransforms(material._texcoordTransforms), _lightmapParams(material._lightmapParams), _materialParams(material._materialParams), @@ -50,7 +72,7 @@ Material::Material(const Material& material) : } Material& Material::operator=(const Material& material) { - QMutexLocker locker(&_textureMapsMutex); + std::lock_guard locker(_textureMapsMutex); _name = material._name; _model = material._model; @@ -61,6 +83,7 @@ Material& Material::operator=(const Material& material) { _roughness = material._roughness; _metallic = material._metallic; _scattering = material._scattering; + _opacityCutoff = material._opacityCutoff; _texcoordTransforms = material._texcoordTransforms; _lightmapParams = material._lightmapParams; _materialParams = material._materialParams; @@ -109,8 +132,22 @@ void Material::setScattering(float scattering) { _scattering = scattering; } +void Material::setOpacityCutoff(float opacityCutoff) { + opacityCutoff = glm::clamp(opacityCutoff, 0.0f, 1.0f); + _key.setOpacityCutoff(opacityCutoff != DEFAULT_OPACITY_CUTOFF); + _opacityCutoff = opacityCutoff; +} + +void Material::setOpacityMapMode(MaterialKey::OpacityMapMode opacityMapMode) { + _key.setOpacityMapMode(opacityMapMode); +} + +MaterialKey::OpacityMapMode Material::getOpacityMapMode() const { + return _key.getOpacityMapMode(); +} + void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) { - QMutexLocker locker(&_textureMapsMutex); + std::lock_guard locker(_textureMapsMutex); if (textureMap) { _key.setMapChannel(channel, true); @@ -139,7 +176,14 @@ void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textur } -void Material::resetOpacityMap() const { +bool Material::resetOpacityMap() const { + // If OpacityMapMode explicit then nothing need to change here. + if (_key.isOpacityMapMode()) { + return false; + } + + // Else, the legacy behavior is to interpret the albedo texture assigned to tune the opacity map mode value + auto previous = _key.getOpacityMapMode(); // Clear the previous flags _key.setOpacityMaskMap(false); _key.setTranslucentMap(false); @@ -163,10 +207,16 @@ void Material::resetOpacityMap() const { } } } + auto newious = _key.getOpacityMapMode(); + if (previous != newious) { + //opacity change detected for this material + return true; + } + return false; } const TextureMapPointer Material::getTextureMap(MapChannel channel) const { - QMutexLocker locker(&_textureMapsMutex); + std::lock_guard locker(_textureMapsMutex); auto result = _textureMaps.find(channel); if (result != _textureMaps.end()) { diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 25601c5743..25ff711c0c 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -11,8 +11,7 @@ #ifndef hifi_model_Material_h #define hifi_model_Material_h -#include - +#include #include #include #include @@ -44,6 +43,8 @@ public: OPACITY_VAL_BIT, OPACITY_MASK_MAP_BIT, // Opacity Map and Opacity MASK map are mutually exclusive OPACITY_TRANSLUCENT_MAP_BIT, + OPACITY_MAP_MODE_BIT, // Opacity map mode bit is set if the value has set explicitely and not deduced from the textures assigned + OPACITY_CUTOFF_VAL_BIT, SCATTERING_VAL_BIT, // THe map bits must be in the same sequence as the enum names for the map channels @@ -73,6 +74,15 @@ public: NUM_MAP_CHANNELS, }; + enum OpacityMapMode { + OPACITY_MAP_OPAQUE = 0, + OPACITY_MAP_MASK, + OPACITY_MAP_BLEND, + }; + static std::string getOpacityMapModeName(OpacityMapMode mode); + // find the enum value from a string, return true if match found + static bool getOpacityMapModeFromName(const std::string& modeName, OpacityMapMode& mode); + // The signature is the Flags Flags _flags; @@ -94,6 +104,27 @@ public: Builder& withGlossy() { _flags.set(GLOSSY_VAL_BIT); return (*this); } Builder& withTranslucentFactor() { _flags.set(OPACITY_VAL_BIT); return (*this); } + Builder& withTranslucentMap() { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT); return (*this); } + Builder& withMaskMap() { _flags.set(OPACITY_MASK_MAP_BIT); return (*this); } + Builder& withOpacityMapMode(OpacityMapMode mode) { + switch (mode) { + case OPACITY_MAP_OPAQUE: + _flags.reset(OPACITY_TRANSLUCENT_MAP_BIT); + _flags.reset(OPACITY_MASK_MAP_BIT); + break; + case OPACITY_MAP_MASK: + _flags.reset(OPACITY_TRANSLUCENT_MAP_BIT); + _flags.set(OPACITY_MASK_MAP_BIT); + break; + case OPACITY_MAP_BLEND: + _flags.set(OPACITY_TRANSLUCENT_MAP_BIT); + _flags.reset(OPACITY_MASK_MAP_BIT); + break; + }; + _flags.set(OPACITY_MAP_MODE_BIT); // Intentionally set the mode! + return (*this); + } + Builder& withOpacityCutoff() { _flags.set(OPACITY_CUTOFF_VAL_BIT); return (*this); } Builder& withScattering() { _flags.set(SCATTERING_VAL_BIT); return (*this); } @@ -102,9 +133,6 @@ public: Builder& withMetallicMap() { _flags.set(METALLIC_MAP_BIT); return (*this); } Builder& withRoughnessMap() { _flags.set(ROUGHNESS_MAP_BIT); return (*this); } - Builder& withTranslucentMap() { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT); return (*this); } - Builder& withMaskMap() { _flags.set(OPACITY_MASK_MAP_BIT); return (*this); } - Builder& withNormalMap() { _flags.set(NORMAL_MAP_BIT); return (*this); } Builder& withOcclusionMap() { _flags.set(OCCLUSION_MAP_BIT); return (*this); } Builder& withLightMap() { _flags.set(LIGHT_MAP_BIT); return (*this); } @@ -151,6 +179,9 @@ public: void setOpacityMaskMap(bool value) { _flags.set(OPACITY_MASK_MAP_BIT, value); } bool isOpacityMaskMap() const { return _flags[OPACITY_MASK_MAP_BIT]; } + void setOpacityCutoff(bool value) { _flags.set(OPACITY_CUTOFF_VAL_BIT, value); } + bool isOpacityCutoff() const { return _flags[OPACITY_CUTOFF_VAL_BIT]; } + void setNormalMap(bool value) { _flags.set(NORMAL_MAP_BIT, value); } bool isNormalMap() const { return _flags[NORMAL_MAP_BIT]; } @@ -171,6 +202,26 @@ public: // Translucency and Opacity Heuristics are combining several flags: + void setOpacityMapMode(OpacityMapMode mode) { + switch (mode) { + case OPACITY_MAP_OPAQUE: + _flags.reset(OPACITY_TRANSLUCENT_MAP_BIT); + _flags.reset(OPACITY_MASK_MAP_BIT); + break; + case OPACITY_MAP_MASK: + _flags.reset(OPACITY_TRANSLUCENT_MAP_BIT); + _flags.set(OPACITY_MASK_MAP_BIT); + break; + case OPACITY_MAP_BLEND: + _flags.set(OPACITY_TRANSLUCENT_MAP_BIT); + _flags.reset(OPACITY_MASK_MAP_BIT); + break; + }; + _flags.set(OPACITY_MAP_MODE_BIT); // Intentionally set the mode! + } + bool isOpacityMapMode() const { return _flags[OPACITY_MAP_MODE_BIT]; } + OpacityMapMode getOpacityMapMode() const { return (isOpacityMaskMap() ? OPACITY_MAP_MASK : (isTranslucentMap() ? OPACITY_MAP_BLEND : OPACITY_MAP_OPAQUE)); } + bool isTranslucent() const { return isTranslucentFactor() || isTranslucentMap(); } bool isOpaque() const { return !isTranslucent(); } bool isSurfaceOpaque() const { return isOpaque() && !isOpacityMaskMap(); } @@ -229,6 +280,12 @@ public: Builder& withoutMaskMap() { _value.reset(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); } Builder& withMaskMap() { _value.set(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); } + Builder& withoutOpacityMapMode() { _value.reset(MaterialKey::OPACITY_MAP_MODE_BIT); _mask.set(MaterialKey::OPACITY_MAP_MODE_BIT); return (*this); } + Builder& withOpacityMapMode() { _value.set(MaterialKey::OPACITY_MAP_MODE_BIT); _mask.set(MaterialKey::OPACITY_MAP_MODE_BIT); return (*this); } + + Builder& withoutOpacityCutoff() { _value.reset(MaterialKey::OPACITY_CUTOFF_VAL_BIT); _mask.set(MaterialKey::OPACITY_CUTOFF_VAL_BIT); return (*this); } + Builder& withOpacityCutoff() { _value.set(MaterialKey::OPACITY_CUTOFF_VAL_BIT); _mask.set(MaterialKey::OPACITY_CUTOFF_VAL_BIT); return (*this); } + Builder& withoutNormalMap() { _value.reset(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); } Builder& withNormalMap() { _value.set(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); } @@ -283,6 +340,14 @@ public: void setOpacity(float opacity); float getOpacity() const { return _opacity; } + static const MaterialKey::OpacityMapMode DEFAULT_OPACITY_MAP_MODE; + void setOpacityMapMode(MaterialKey::OpacityMapMode opacityMapMode); + MaterialKey::OpacityMapMode getOpacityMapMode() const; + + static const float DEFAULT_OPACITY_CUTOFF; + void setOpacityCutoff(float opacityCutoff); + float getOpacityCutoff() const { return _opacityCutoff; } + void setUnlit(bool value); bool isUnlit() const { return _key.isUnlit(); } @@ -310,7 +375,8 @@ public: // Albedo maps cannot have opacity detected until they are loaded // This method allows const changing of the key/schemaBuffer without touching the map - void resetOpacityMap() const; + // return true if the opacity changed, flase otherwise + bool resetOpacityMap() const; // conversion from legacy material properties to PBR equivalent static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; } @@ -357,6 +423,7 @@ private: float _roughness { DEFAULT_ROUGHNESS }; float _metallic { DEFAULT_METALLIC }; float _scattering { DEFAULT_SCATTERING }; + float _opacityCutoff { DEFAULT_OPACITY_CUTOFF }; std::array _texcoordTransforms; glm::vec2 _lightmapParams { 0.0, 1.0 }; glm::vec2 _materialParams { 0.0, 1.0 }; @@ -365,7 +432,7 @@ private: bool _defaultFallthrough { false }; std::unordered_map _propertyFallthroughs { NUM_TOTAL_FLAGS }; - mutable QMutex _textureMapsMutex { QMutex::Recursive }; + mutable std::recursive_mutex _textureMapsMutex; }; typedef std::shared_ptr MaterialPointer; @@ -425,18 +492,8 @@ public: float _metallic { Material::DEFAULT_METALLIC }; // Not Metallic float _scattering { Material::DEFAULT_SCATTERING }; // Scattering info -#if defined(__clang__) - __attribute__((unused)) -#endif - glm::vec2 _spare { 0.0f }; // Padding - + float _opacityCutoff { Material::DEFAULT_OPACITY_CUTOFF }; // Opacity cutoff applyed when using opacityMap as Mask uint32_t _key { 0 }; // a copy of the materialKey -#if defined(__clang__) - __attribute__((unused)) -#endif - glm::vec3 _spare2 { 0.0f }; - - // for alignment beauty, Material size == Mat4x4 // Texture Coord Transform Array glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS]; diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh index dfd4a8eec4..328ff4a3af 100644 --- a/libraries/graphics/src/graphics/Material.slh +++ b/libraries/graphics/src/graphics/Material.slh @@ -49,8 +49,7 @@ struct TexMapArray { struct Material { vec4 _emissiveOpacity; vec4 _albedoRoughness; - vec4 _metallicScatteringSpare2; - vec4 _keySpare3; + vec4 _metallicScatteringOpacityCutoffKey; }; LAYOUT_STD140(binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer { @@ -72,10 +71,11 @@ vec3 getMaterialAlbedo(Material m) { return m._albedoRoughness.rgb; } float getMaterialRoughness(Material m) { return m._albedoRoughness.a; } float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); } -float getMaterialMetallic(Material m) { return m._metallicScatteringSpare2.x; } -float getMaterialScattering(Material m) { return m._metallicScatteringSpare2.y; } +float getMaterialMetallic(Material m) { return m._metallicScatteringOpacityCutoffKey.x; } +float getMaterialScattering(Material m) { return m._metallicScatteringOpacityCutoffKey.y; } +float getMaterialOpacityCutoff(Material m) { return m._metallicScatteringOpacityCutoffKey.z; } -BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._keySpare3.x); } +BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._metallicScatteringOpacityCutoffKey.w); } const BITFIELD EMISSIVE_VAL_BIT = 0x00000001; const BITFIELD UNLIT_VAL_BIT = 0x00000002; @@ -85,16 +85,18 @@ const BITFIELD GLOSSY_VAL_BIT = 0x00000010; const BITFIELD OPACITY_VAL_BIT = 0x00000020; const BITFIELD OPACITY_MASK_MAP_BIT = 0x00000040; const BITFIELD OPACITY_TRANSLUCENT_MAP_BIT = 0x00000080; -const BITFIELD SCATTERING_VAL_BIT = 0x00000100; +const BITFIELD OPACITY_MAP_MODE_BIT = 0x00000100; +const BITFIELD OPACITY_CUTOFF_VAL_BIT = 0x00000200; +const BITFIELD SCATTERING_VAL_BIT = 0x00000400; -const BITFIELD EMISSIVE_MAP_BIT = 0x00000200; -const BITFIELD ALBEDO_MAP_BIT = 0x00000400; -const BITFIELD METALLIC_MAP_BIT = 0x00000800; -const BITFIELD ROUGHNESS_MAP_BIT = 0x00001000; -const BITFIELD NORMAL_MAP_BIT = 0x00002000; -const BITFIELD OCCLUSION_MAP_BIT = 0x00004000; -const BITFIELD LIGHTMAP_MAP_BIT = 0x00008000; -const BITFIELD SCATTERING_MAP_BIT = 0x00010000; +const BITFIELD EMISSIVE_MAP_BIT = 0x00000800; +const BITFIELD ALBEDO_MAP_BIT = 0x00001000; +const BITFIELD METALLIC_MAP_BIT = 0x00002000; +const BITFIELD ROUGHNESS_MAP_BIT = 0x00004000; +const BITFIELD NORMAL_MAP_BIT = 0x00008000; +const BITFIELD OCCLUSION_MAP_BIT = 0x00010000; +const BITFIELD LIGHTMAP_MAP_BIT = 0x00020000; +const BITFIELD SCATTERING_MAP_BIT = 0x00040000; <@endif@> diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh index 278057b01a..2a291e5d57 100644 --- a/libraries/graphics/src/graphics/MaterialTextures.slh +++ b/libraries/graphics/src/graphics/MaterialTextures.slh @@ -214,14 +214,22 @@ vec3 fetchLightMap(vec2 uv) { } <@endfunc@> -<@func evalMaterialOpacity(fetchedOpacity, materialOpacity, matKey, opacity)@> +<@func evalMaterialOpacityMask(fetchedOpacity, materialOpacityCutoff, opacity)@> { - const float OPACITY_MASK_THRESHOLD = 0.5; - <$opacity$> = mix(1.0, - mix(<$fetchedOpacity$>, - step(OPACITY_MASK_THRESHOLD, <$fetchedOpacity$>), - float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)), - float((<$matKey$> & (OPACITY_TRANSLUCENT_MAP_BIT | OPACITY_MASK_MAP_BIT)) != 0)) * <$materialOpacity$>; + // This path only valid for opaque or texel opaque material + <$opacity$> = step(<$materialOpacityCutoff$>, <$fetchedOpacity$>); +} +<@endfunc@> + + +<@func evalMaterialOpacity(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@> +{ + // This path only valid for transparent material + // Assert that float((<$matKey$> & (OPACITY_TRANSLUCENT_MAP_BIT | OPACITY_MASK_MAP_BIT)) != 0)) == 1.0 + <$opacity$> = mix(<$fetchedOpacity$>, + step(<$materialOpacityCutoff$>, <$fetchedOpacity$>), + float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)) + * <$materialOpacity$>; } <@endfunc@> diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp index 087e1ca049..db4783d249 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.cpp +++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp @@ -139,6 +139,14 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * @property {string} opacityMap - The URL of the opacity texture image. Set the value the same as the albedoMap * value for transparency. * "hifi_pbr" model only. + * @property {number|string} opacityMapMode - The mode defining the interpretation of the opacity map. Values can be: + * "OPACITY_MAP_OPAQUE" for ignoring the opacity map information. + * "OPACITY_MAP_MASK" for using the opacity map as a mask, where only the texel greater than opacityCutoff are visible and rendered opaque. + * "OPACITY_MAP_BLEND" for using the opacity map for alpha blending the material surface with the background. + * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * @property {number|string} opacityCutoff - The opacity cutoff threshold used to determine the opaque texels of the Opacity map + * when opacityMapMode is "OPACITY_MAP_MASK", range 0.01.0. + * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. * @property {string} roughnessMap - The URL of the roughness texture image. You can use this or glossMap, but not * both. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. @@ -258,6 +266,24 @@ std::pair> NetworkMaterialResource } else if (value.isDouble()) { material->setMetallic(value.toDouble()); } + } else if (key == "opacityMapMode") { + auto value = materialJSON.value(key); + auto valueString = (value.isString() ? value.toString() : ""); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OPACITY_MAP_MODE_BIT); + } else { + graphics::MaterialKey::OpacityMapMode mode; + if (graphics::MaterialKey::getOpacityMapModeFromName(valueString.toStdString(), mode)) { + material->setOpacityMapMode(mode); + } + } + } else if (key == "opacityCutoff") { + auto value = materialJSON.value(key); + if (value.isString() && value.toString() == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OPACITY_CUTOFF_VAL_BIT); + } else if (value.isDouble()) { + material->setOpacityCutoff(value.toDouble()); + } } else if (key == "scattering") { auto value = materialJSON.value(key); if (value.isString() && value.toString() == FALLTHROUGH) { @@ -748,13 +774,14 @@ bool NetworkMaterial::isMissingTexture() { return false; } -void NetworkMaterial::checkResetOpacityMap() { +bool NetworkMaterial::checkResetOpacityMap() { // If material textures are loaded, check the material translucency // FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap. // However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail. // Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now const auto& albedoTexture = _textures[NetworkMaterial::MapChannel::ALBEDO_MAP]; if (albedoTexture.texture) { - resetOpacityMap(); + return resetOpacityMap(); } + return false; } diff --git a/libraries/material-networking/src/material-networking/MaterialCache.h b/libraries/material-networking/src/material-networking/MaterialCache.h index 18aa5e5994..aa103adb1e 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.h +++ b/libraries/material-networking/src/material-networking/MaterialCache.h @@ -34,7 +34,7 @@ public: void setLightMap(const QUrl& url); bool isMissingTexture(); - void checkResetOpacityMap(); + bool checkResetOpacityMap(); class Texture { public: diff --git a/libraries/material-networking/src/material-networking/MaterialCacheScriptingInterface.cpp b/libraries/material-networking/src/material-networking/MaterialCacheScriptingInterface.cpp new file mode 100644 index 0000000000..193d9b96ee --- /dev/null +++ b/libraries/material-networking/src/material-networking/MaterialCacheScriptingInterface.cpp @@ -0,0 +1,17 @@ +// +// MaterialCacheScriptingInterface.cpp +// libraries/mmodel-networking/src/model-networking +// +// Created by Sam Gateau on 17 September 2019. +// Copyright 2019 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 "MaterialCacheScriptingInterface.h" + +MaterialCacheScriptingInterface::MaterialCacheScriptingInterface() : + ScriptableResourceCache::ScriptableResourceCache(DependencyManager::get()) +{ } + diff --git a/libraries/material-networking/src/material-networking/MaterialCacheScriptingInterface.h b/libraries/material-networking/src/material-networking/MaterialCacheScriptingInterface.h new file mode 100644 index 0000000000..c619966a2a --- /dev/null +++ b/libraries/material-networking/src/material-networking/MaterialCacheScriptingInterface.h @@ -0,0 +1,51 @@ +// +// MaterialCacheScriptingInterface.h +// libraries/material-networking/src/material-networking +// +// Created by Sam Gateau on 17 September 2019. +// Copyright 2019 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_MaterialCacheScriptingInterface_h +#define hifi_MaterialCacheScriptingInterface_h + +#include + +#include + +#include "MaterialCache.h" + +class MaterialCacheScriptingInterface : public ScriptableResourceCache, public Dependency { + Q_OBJECT + + // Properties are copied over from ResourceCache (see ResourceCache.h for reason). + + /**jsdoc + * The TextureCache API manages texture cache resources. + * + * @namespace TextureCache + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {number} numTotal - Total number of total resources. Read-only. + * @property {number} numCached - Total number of cached resource. Read-only. + * @property {number} sizeTotal - Size in bytes of all resources. Read-only. + * @property {number} sizeCached - Size in bytes of all cached resources. Read-only. + * + * @borrows ResourceCache.getResourceList as getResourceList + * @borrows ResourceCache.updateTotalSize as updateTotalSize + * @borrows ResourceCache.prefetch as prefetch + * @borrows ResourceCache.dirty as dirty + */ + +public: + MaterialCacheScriptingInterface(); +}; + +#endif // hifi_MaterialCacheScriptingInterface_h diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 1ed1c65358..1fcfcfcc70 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -472,7 +472,10 @@ bool Geometry::areTexturesLoaded() const { return false; } - material->checkResetOpacityMap(); + bool changed = material->checkResetOpacityMap(); + if (changed) { + qCWarning(modelnetworking) << "Material list: opacity change detected for material " << material->getName().c_str(); + } } for (auto& materialMapping : _materialMapping) { @@ -483,7 +486,10 @@ bool Geometry::areTexturesLoaded() const { return false; } - materialPair.second->checkResetOpacityMap(); + bool changed = materialPair.second->checkResetOpacityMap(); + if (changed) { + qCWarning(modelnetworking) << "Mapping list: opacity change detected for material " << materialPair.first.c_str(); + } } } } diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 88e6b14ec7..88a4f5bf32 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -148,6 +148,8 @@ void ResourceCacheSharedItems::clear() { ScriptableResourceCache::ScriptableResourceCache(QSharedPointer resourceCache) { _resourceCache = resourceCache; + connect(&(*_resourceCache), &ResourceCache::dirty, + this, &ScriptableResourceCache::dirty, Qt::DirectConnection); } QVariantList ScriptableResourceCache::getResourceList() { @@ -323,7 +325,11 @@ QVariantList ResourceCache::getResourceList() { BLOCKING_INVOKE_METHOD(this, "getResourceList", Q_RETURN_ARG(QVariantList, list)); } else { - auto resources = _resources.uniqueKeys(); + QList resources; + { + QReadLocker locker(&_resourcesLock); + resources = _resources.uniqueKeys(); + } list.reserve(resources.size()); for (auto& resource : resources) { list << resource; @@ -510,7 +516,7 @@ void ResourceCache::updateTotalSize(const qint64& deltaSize) { emit dirty(); } - + QList> ResourceCache::getLoadingRequests() { return DependencyManager::get()->getLoadingRequests(); } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index e1f3098658..4213d92fc0 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -317,6 +317,13 @@ class ScriptableResourceCache : public QObject { Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty) Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty) + /**jsdoc + * @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource managers). Read-only. + * @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource managers). Read-only. + */ + Q_PROPERTY(size_t numGlobalQueriesPending READ getNumGlobalQueriesPending NOTIFY dirty) + Q_PROPERTY(size_t numGlobalQueriesLoading READ getNumGlobalQueriesLoading NOTIFY dirty) + public: ScriptableResourceCache(QSharedPointer resourceCache); @@ -390,6 +397,9 @@ private: size_t getSizeTotalResources() const { return _resourceCache->getSizeTotalResources(); } size_t getNumCachedResources() const { return _resourceCache->getNumCachedResources(); } size_t getSizeCachedResources() const { return _resourceCache->getSizeCachedResources(); } + + size_t getNumGlobalQueriesPending() const { return ResourceCache::getPendingRequestCount(); } + size_t getNumGlobalQueriesLoading() const { return ResourceCache::getLoadingRequestCount(); } }; /// Base class for resources. diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 872ee4dd4c..338b95163f 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -269,7 +269,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in bool wasDuplicate = false; if (sequenceNumber > _lastReceivedSequenceNumber) { - // Update largest recieved sequence number + // Update largest received sequence number _lastReceivedSequenceNumber = sequenceNumber; } else { // Otherwise, it could be a resend, try and remove it from the loss list @@ -312,9 +312,7 @@ void Connection::processControl(ControlPacketPointer controlPacket) { // We're already in a state where we've received a handshake ack, so we are likely in a state // where the other end expired our connection. Let's reset. -#ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "Got HandshakeRequest from" << _destination << ", stopping SendQueue"; -#endif + qCDebug(networking) << "Got HandshakeRequest from" << _destination << "while active, stopping SendQueue"; _hasReceivedHandshakeACK = false; stopSendQueue(); } @@ -333,7 +331,7 @@ void Connection::processACK(ControlPacketPointer controlPacket) { // validate that this isn't a BS ACK if (ack > getSendQueue().getCurrentSequenceNumber()) { // in UDT they specifically break the connection here - do we want to do anything? - Q_ASSERT_X(false, "Connection::processACK", "ACK recieved higher than largest sent sequence number"); + Q_ASSERT_X(false, "Connection::processACK", "ACK received higher than largest sent sequence number"); return; } diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 47edb021c8..00d5beb5ab 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -73,6 +73,7 @@ public: void setMaxBandwidth(int maxBandwidth); void sendHandshakeRequest(); + bool hasReceivedHandshake() const { return _hasReceivedHandshake; } void recordSentUnreliablePackets(int wireSize, int payloadSize); void recordReceivedUnreliablePackets(int wireSize, int payloadSize); diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 20cb30dbd8..2d99f3649b 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -239,15 +239,24 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc int pending = _udpSocket.bytesToWrite(); if (bytesWritten < 0 || pending) { int wsaError = 0; + static std::atomic previousWsaError (0); #ifdef WIN32 wsaError = WSAGetLastError(); #endif - qCDebug(networking) << "udt::writeDatagram (" << _udpSocket.state() << sockAddr << ") error - " << wsaError << _udpSocket.error() << "(" << _udpSocket.errorString() << ")" + QString errorString; + QDebug(&errorString) << "udt::writeDatagram (" << _udpSocket.state() << sockAddr << ") error - " + << wsaError << _udpSocket.error() << "(" << _udpSocket.errorString() << ")" << (pending ? "pending bytes:" : "pending:") << pending; + + if (previousWsaError.exchange(wsaError) != wsaError) { + qCDebug(networking).noquote() << errorString; #ifdef DEBUG_EVENT_QUEUE - int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread()); - qCDebug(networking) << "Networking queue size - " << nodeListQueueSize << "writing datagram to" << sockAddr; -#endif // DEBUG_EVENT_QUEUE + int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread()); + qCDebug(networking) << "Networking queue size - " << nodeListQueueSize << "writing datagram to" << sockAddr; +#endif // DEBUG_EVENT_QUEUE + } else { + HIFI_FCDEBUG(networking(), errorString.toLatin1().constData()); + } } return bytesWritten; @@ -525,16 +534,25 @@ std::vector Socket::getConnectionSockAddrs() { void Socket::handleSocketError(QAbstractSocket::SocketError socketError) { int wsaError = 0; + static std::atomic previousWsaError(0); #ifdef WIN32 wsaError = WSAGetLastError(); #endif int pending = _udpSocket.bytesToWrite(); - qCDebug(networking) << "udt::Socket (" << _udpSocket.state() << ") error - " << wsaError << socketError << "(" << _udpSocket.errorString() << ")" - << (pending ? "pending bytes:" : "pending:") << pending; + QString errorString; + QDebug(&errorString) << "udt::Socket (" << _udpSocket.state() << ") error - " << wsaError << socketError << + "(" << _udpSocket.errorString() << ")" << (pending ? "pending bytes:" : "pending:") + << pending; + + if (previousWsaError.exchange(wsaError) != wsaError) { + qCDebug(networking).noquote() << errorString; #ifdef DEBUG_EVENT_QUEUE - int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread()); - qCDebug(networking) << "Networking queue size - " << nodeListQueueSize; -#endif // DEBUG_EVENT_QUEUE + int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread()); + qCDebug(networking) << "Networking queue size - " << nodeListQueueSize; +#endif // DEBUG_EVENT_QUEUE + } else { + HIFI_FCDEBUG(networking(), errorString.toLatin1().constData()); + } } void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) { @@ -548,12 +566,14 @@ void Socket::handleRemoteAddressChange(HifiSockAddr previousAddress, HifiSockAdd Lock connectionsLock(_connectionsHashMutex); const auto connectionIter = _connectionsHash.find(previousAddress); - if (connectionIter != _connectionsHash.end()) { + // Don't move classes that are unused so far. + if (connectionIter != _connectionsHash.end() && connectionIter->second->hasReceivedHandshake()) { auto connection = move(connectionIter->second); _connectionsHash.erase(connectionIter); connection->setDestinationAddress(currentAddress); _connectionsHash[currentAddress] = move(connection); connectionsLock.unlock(); + qCDebug(networking) << "Moved Connection class from" << previousAddress << "to" << currentAddress; Lock sequenceNumbersLock(_unreliableSequenceNumbersMutex); const auto sequenceNumbersIter = _unreliableSequenceNumbers.find(previousAddress); diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 43ced16119..df8c3fa32e 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -75,7 +75,8 @@ void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) { if (motionState) { removeOwnershipData(motionState); _entitiesToRemoveFromPhysics.insert(entity); - } else if (entity->isDead() && entity->getElement()) { + } + if (entity->isDead() && entity->getElement()) { _deadEntities.insert(entity); } } diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index a7290a4d7d..d9fc8117bd 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -214,6 +214,8 @@ bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event) fakeMouseEventType = QEvent::MouseButtonRelease; fakeMouseButtons = Qt::NoButton; break; + default: + Q_UNREACHABLE(); } // Same case as OffscreenUi.cpp::eventFilter: touch events are always being accepted so we now use mouse events and consider one touch, touchPoints()[0]. QMouseEvent fakeMouseEvent(fakeMouseEventType, originalEvent->touchPoints()[0].pos(), fakeMouseButton, fakeMouseButtons, Qt::NoModifier); diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index d0ebe167f9..518e43a8ec 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -461,6 +461,13 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial wasSet = true; } break; + case graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT: + if (materialKey.isOpacityCutoff()) { + schema._opacityCutoff = material->getOpacityCutoff(); + schemaKey.setOpacityCutoff(true); + wasSet = true; + } + break; case graphics::MaterialKey::SCATTERING_VAL_BIT: if (materialKey.isScattering()) { schema._scattering = material->getScattering(); @@ -752,7 +759,7 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: // For shadows, we only need opacity mask information auto key = multiMaterial.getMaterialKey(); - if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE || key.isOpacityMaskMap()) { + if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE || (key.isOpacityMaskMap() || key.isTranslucentMap())) { auto& schemaBuffer = multiMaterial.getSchemaBuffer(); batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer); if (enableTextures) { diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index 3e4711dac8..bacc6b0ab1 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -103,14 +103,14 @@ void main(void) { <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$> <@if HIFI_USE_TRANSLUCENT@> + float cutoff = getMaterialOpacityCutoff(mat); float opacity = getMaterialOpacity(mat) * _color.a; - <@else@> - float opacity = 1.0; - <@endif@> - <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; - <@if HIFI_USE_TRANSLUCENT@> + <$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>; <$discardInvisible(opacity)$>; <@else@> + float cutoff = getMaterialOpacityCutoff(mat); + float opacity = 1.0; + <$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity)$>; <$discardTransparent(opacity)$>; <@endif@> @@ -155,15 +155,15 @@ void main(void) { <@endif@> <@if HIFI_USE_TRANSLUCENT@> + float cutoff = getMaterialOpacityCutoff(mat); float opacity = getMaterialOpacity(mat) * _color.a; + <$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>; + <$discardInvisible(opacity)$>; <@else@> + float cutoff = getMaterialOpacityCutoff(mat); float opacity = 1.0; - <@endif@> - <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; - <@if HIFI_USE_TRANSLUCENT@> - <$discardInvisible(opacity)$>; - <@else@> - <$discardTransparent(opacity)$>; + <$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity)$>; + <$discardTransparent(opacity)$>; <@endif@> vec3 albedo = getMaterialAlbedo(mat); @@ -217,13 +217,13 @@ void main(void) { _fragColor0 = color; <@else@> _fragColor0 = vec4(evalLightmappedColor( - cam._viewInverse, - 1.0, - DEFAULT_OCCLUSION, - fragNormalWS, - albedo, - lightmap), - opacity); + cam._viewInverse, + 1.0, + DEFAULT_OCCLUSION, + fragNormalWS, + albedo, + lightmap), + opacity); <@endif@> <@else@> <@if not HIFI_USE_LIGHTMAP@> @@ -241,13 +241,13 @@ void main(void) { opacity); <@else@> _fragColor0 = vec4(evalLightmappedColor( - cam._viewInverse, - 1.0, - DEFAULT_OCCLUSION, - fragNormalWS, - albedo, - lightmap), - opacity); + cam._viewInverse, + 1.0, + DEFAULT_OCCLUSION, + fragNormalWS, + albedo, + lightmap), + opacity); <@endif@> <@endif@> <@else@> @@ -315,13 +315,13 @@ void main(void) { opacity); <@else@> _fragColor0 = vec4(evalLightmappedColor( - cam._viewInverse, - 1.0, - DEFAULT_OCCLUSION, - fragNormalWS, - albedo, - lightmap), - opacity); + cam._viewInverse, + 1.0, + DEFAULT_OCCLUSION, + fragNormalWS, + albedo, + lightmap), + opacity); <@endif@> <@endif@> <@endif@> diff --git a/libraries/render/src/render/EngineStats.cpp b/libraries/render/src/render/EngineStats.cpp index ae1467ac0f..6fbd3e6f9c 100644 --- a/libraries/render/src/render/EngineStats.cpp +++ b/libraries/render/src/render/EngineStats.cpp @@ -63,4 +63,6 @@ void EngineStats::run(const RenderContextPointer& renderContext) { config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines; config->frameSetInputFormatCount = _gpuStats._ISNumFormatChanges; + + // These new stat values are notified with the "newStats" signal triggered by the timer } diff --git a/libraries/render/src/render/EngineStats.h b/libraries/render/src/render/EngineStats.h index 3ccbd40715..9f8be748f7 100644 --- a/libraries/render/src/render/EngineStats.h +++ b/libraries/render/src/render/EngineStats.h @@ -24,42 +24,42 @@ namespace render { class EngineStatsConfig : public Job::Config{ Q_OBJECT - Q_PROPERTY(quint32 bufferCPUCount MEMBER bufferCPUCount NOTIFY dirty) - Q_PROPERTY(quint32 bufferGPUCount MEMBER bufferGPUCount NOTIFY dirty) - Q_PROPERTY(qint64 bufferCPUMemSize MEMBER bufferCPUMemSize NOTIFY dirty) - Q_PROPERTY(qint64 bufferGPUMemSize MEMBER bufferGPUMemSize NOTIFY dirty) + Q_PROPERTY(quint32 bufferCPUCount MEMBER bufferCPUCount NOTIFY newStats) + Q_PROPERTY(quint32 bufferGPUCount MEMBER bufferGPUCount NOTIFY newStats) + Q_PROPERTY(qint64 bufferCPUMemSize MEMBER bufferCPUMemSize NOTIFY newStats) + Q_PROPERTY(qint64 bufferGPUMemSize MEMBER bufferGPUMemSize NOTIFY newStats) - Q_PROPERTY(quint32 textureCPUCount MEMBER textureCPUCount NOTIFY dirty) - Q_PROPERTY(quint32 textureGPUCount MEMBER textureGPUCount NOTIFY dirty) - Q_PROPERTY(quint32 textureResidentGPUCount MEMBER textureResidentGPUCount NOTIFY dirty) - Q_PROPERTY(quint32 textureFramebufferGPUCount MEMBER textureFramebufferGPUCount NOTIFY dirty) - Q_PROPERTY(quint32 textureResourceGPUCount MEMBER textureResourceGPUCount NOTIFY dirty) - Q_PROPERTY(quint32 textureExternalGPUCount MEMBER textureExternalGPUCount NOTIFY dirty) + Q_PROPERTY(quint32 textureCPUCount MEMBER textureCPUCount NOTIFY newStats) + Q_PROPERTY(quint32 textureGPUCount MEMBER textureGPUCount NOTIFY newStats) + Q_PROPERTY(quint32 textureResidentGPUCount MEMBER textureResidentGPUCount NOTIFY newStats) + Q_PROPERTY(quint32 textureFramebufferGPUCount MEMBER textureFramebufferGPUCount NOTIFY newStats) + Q_PROPERTY(quint32 textureResourceGPUCount MEMBER textureResourceGPUCount NOTIFY newStats) + Q_PROPERTY(quint32 textureExternalGPUCount MEMBER textureExternalGPUCount NOTIFY newStats) - Q_PROPERTY(qint64 textureCPUMemSize MEMBER textureCPUMemSize NOTIFY dirty) - Q_PROPERTY(qint64 textureGPUMemSize MEMBER textureGPUMemSize NOTIFY dirty) - Q_PROPERTY(qint64 textureResidentGPUMemSize MEMBER textureResidentGPUMemSize NOTIFY dirty) - Q_PROPERTY(qint64 textureFramebufferGPUMemSize MEMBER textureFramebufferGPUMemSize NOTIFY dirty) - Q_PROPERTY(qint64 textureResourceGPUMemSize MEMBER textureResourceGPUMemSize NOTIFY dirty) - Q_PROPERTY(qint64 textureExternalGPUMemSize MEMBER textureExternalGPUMemSize NOTIFY dirty) + Q_PROPERTY(qint64 textureCPUMemSize MEMBER textureCPUMemSize NOTIFY newStats) + Q_PROPERTY(qint64 textureGPUMemSize MEMBER textureGPUMemSize NOTIFY newStats) + Q_PROPERTY(qint64 textureResidentGPUMemSize MEMBER textureResidentGPUMemSize NOTIFY newStats) + Q_PROPERTY(qint64 textureFramebufferGPUMemSize MEMBER textureFramebufferGPUMemSize NOTIFY newStats) + Q_PROPERTY(qint64 textureResourceGPUMemSize MEMBER textureResourceGPUMemSize NOTIFY newStats) + Q_PROPERTY(qint64 textureExternalGPUMemSize MEMBER textureExternalGPUMemSize NOTIFY newStats) - Q_PROPERTY(quint32 texturePendingGPUTransferCount MEMBER texturePendingGPUTransferCount NOTIFY dirty) - Q_PROPERTY(qint64 texturePendingGPUTransferSize MEMBER texturePendingGPUTransferSize NOTIFY dirty) - Q_PROPERTY(qint64 textureResourcePopulatedGPUMemSize MEMBER textureResourcePopulatedGPUMemSize NOTIFY dirty) + Q_PROPERTY(quint32 texturePendingGPUTransferCount MEMBER texturePendingGPUTransferCount NOTIFY newStats) + Q_PROPERTY(qint64 texturePendingGPUTransferSize MEMBER texturePendingGPUTransferSize NOTIFY newStats) + Q_PROPERTY(qint64 textureResourcePopulatedGPUMemSize MEMBER textureResourcePopulatedGPUMemSize NOTIFY newStats) - Q_PROPERTY(quint32 frameAPIDrawcallCount MEMBER frameAPIDrawcallCount NOTIFY dirty) - Q_PROPERTY(quint32 frameDrawcallCount MEMBER frameDrawcallCount NOTIFY dirty) - Q_PROPERTY(quint32 frameDrawcallRate MEMBER frameDrawcallRate NOTIFY dirty) + Q_PROPERTY(quint32 frameAPIDrawcallCount MEMBER frameAPIDrawcallCount NOTIFY newStats) + Q_PROPERTY(quint32 frameDrawcallCount MEMBER frameDrawcallCount NOTIFY newStats) + Q_PROPERTY(quint32 frameDrawcallRate MEMBER frameDrawcallRate NOTIFY newStats) - Q_PROPERTY(quint32 frameTriangleCount MEMBER frameTriangleCount NOTIFY dirty) - Q_PROPERTY(quint32 frameTriangleRate MEMBER frameTriangleRate NOTIFY dirty) + Q_PROPERTY(quint32 frameTriangleCount MEMBER frameTriangleCount NOTIFY newStats) + Q_PROPERTY(quint32 frameTriangleRate MEMBER frameTriangleRate NOTIFY newStats) - Q_PROPERTY(quint32 frameTextureCount MEMBER frameTextureCount NOTIFY dirty) - Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty) - Q_PROPERTY(quint64 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY dirty) + Q_PROPERTY(quint32 frameTextureCount MEMBER frameTextureCount NOTIFY newStats) + Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY newStats) + Q_PROPERTY(quint64 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY newStats) - Q_PROPERTY(quint32 frameSetPipelineCount MEMBER frameSetPipelineCount NOTIFY dirty) - Q_PROPERTY(quint32 frameSetInputFormatCount MEMBER frameSetInputFormatCount NOTIFY dirty) + Q_PROPERTY(quint32 frameSetPipelineCount MEMBER frameSetPipelineCount NOTIFY newStats) + Q_PROPERTY(quint32 frameSetInputFormatCount MEMBER frameSetInputFormatCount NOTIFY newStats) public: @@ -101,13 +101,6 @@ namespace render { quint32 frameSetPipelineCount{ 0 }; quint32 frameSetInputFormatCount{ 0 }; - - - - void emitDirty() { emit dirty(); } - - signals: - void dirty(); }; class EngineStats { diff --git a/libraries/shaders/src/shaders/Shaders.cpp b/libraries/shaders/src/shaders/Shaders.cpp index ef67842f84..9964b1a1ed 100644 --- a/libraries/shaders/src/shaders/Shaders.cpp +++ b/libraries/shaders/src/shaders/Shaders.cpp @@ -35,7 +35,7 @@ namespace shader { const Dialect DEFAULT_DIALECT = Dialect::glsl310es; const std::vector& allDialects() { - static const std::vector ALL_DIALECTS{ { Dialect::glsl310es } }; + static const std::vector ALL_DIALECTS{ Dialect::glsl310es }; return ALL_DIALECTS; } diff --git a/libraries/shared/src/CrashAnnotations.cpp b/libraries/shared/src/CrashAnnotations.cpp new file mode 100644 index 0000000000..074b467244 --- /dev/null +++ b/libraries/shared/src/CrashAnnotations.cpp @@ -0,0 +1,27 @@ +// +// CrashAnnotations.cpp +// libraries/shared/src +// +// Created by Clement Brisset on 9/26/19. +// Copyright 2019 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 "SharedUtil.h" + +// Global variables specifically tuned to work with ptrace module for crash collection +// Do not move/rename unless you update the ptrace module with it. +bool crash_annotation_isShuttingDown = false; + +namespace crash { +namespace annotations { + +void setShutdownState(bool isShuttingDown) { + crash_annotation_isShuttingDown = isShuttingDown; +} + + +}; +}; diff --git a/libraries/shared/src/CrashAnnotations.h b/libraries/shared/src/CrashAnnotations.h new file mode 100644 index 0000000000..697d821335 --- /dev/null +++ b/libraries/shared/src/CrashAnnotations.h @@ -0,0 +1,23 @@ +// +// CrashAnnotations.h +// libraries/shared/src +// +// Created by Clement Brisset on 9/26/19. +// Copyright 2019 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_CrashAnnotations_h +#define hifi_CrashAnnotations_h + +namespace crash { +namespace annotations { + +void setShutdownState(bool isShuttingDown); + +}; +}; + +#endif // hifi_CrashAnnotations_h diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 6deae695cd..cfb4bb6398 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -14,6 +14,8 @@ #include +#include + #include #include #include @@ -364,4 +366,30 @@ inline int fastLrintf(float x) { #endif } +// returns the FOV from the projection matrix +inline glm::vec4 extractFov( const glm::mat4& m) { + static const std::array CLIPS{ { + { 1, 0, 0, 1 }, + { -1, 0, 0, 1 }, + { 0, 1, 0, 1 }, + { 0, -1, 0, 1 } + } }; + + glm::mat4 mt = glm::transpose(m); + glm::vec4 v, result; + // Left + v = mt * CLIPS[0]; + result.x = -atanf(v.z / v.x); + // Right + v = mt * CLIPS[1]; + result.y = atanf(v.z / v.x); + // Down + v = mt * CLIPS[2]; + result.z = -atanf(v.z / v.y); + // Up + v = mt * CLIPS[3]; + result.w = atanf(v.z / v.y); + return result; +} + #endif // hifi_GLMHelpers_h diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index bae5eda29e..58596d6d92 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -279,6 +279,7 @@ public: * @param {string} [injectedJavaScriptUrl=""] - The URL of JavaScript to inject into the web page. * @param {boolean} [loadOtherBase=false] - If true, the web page or app is displayed in a frame with "back" * and "close" buttons. + *

Deprecated: This parameter is deprecated and will be removed.

*/ Q_INVOKABLE void gotoWebScreen(const QString& url); Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase = false); diff --git a/scripts/developer/utilities/cache/cash.js b/scripts/developer/utilities/cache/cash.js new file mode 100644 index 0000000000..14ecf873e3 --- /dev/null +++ b/scripts/developer/utilities/cache/cash.js @@ -0,0 +1,59 @@ +"use strict"; +var Page = Script.require('../lib/skit/Page.js'); + +function openView() { + //window.closed.connect(function() { Script.stop(); }); + + + var pages = new Pages(Script.resolvePath(".")); + function fromQml(message) { + console.log(JSON.stringify(message)) + if (message.method == "inspectResource") { + pages.open("openResourceInspector") + pages.sendTo("openResourceInspector", message) + return; + } + if (pages.open(message.method)) { + return; + } + } + + function openCashWindow(window) { + var onMousePressEvent = function (e) { + }; + Controller.mousePressEvent.connect(onMousePressEvent); + + var onMouseReleaseEvent = function () { + }; + Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); + + var onMouseMoveEvent = function (e) { + }; + Controller.mouseMoveEvent.connect(onMouseMoveEvent); + } + + function closeCashWindow() { + Controller.mousePressEvent.disconnect(onMousePressEvent); + Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent); + Controller.mouseMoveEvent.disconnect(onMouseMoveEvent); + pages.clear(); + } + + + pages.addPage('Cash', 'Cash', "cash.qml", 300, 500, fromQml, openCashWindow, closeCashWindow); + pages.addPage('openModelCacheInspector', 'Model Cache Inspector', "cash/ModelCacheInspector.qml", 300, 500, fromQml); + pages.addPage('openMaterialCacheInspector', 'Material Cache Inspector', "cash/MaterialCacheInspector.qml", 300, 500, fromQml); + pages.addPage('openTextureCacheInspector', 'Texture Cache Inspector', "cash/TextureCacheInspector.qml", 300, 500, fromQml); + pages.addPage('openAnimationCacheInspector', 'Animation Cache Inspector', "cash/AnimationCacheInspector.qml", 300, 500); + pages.addPage('openSoundCacheInspector', 'Sound Cache Inspector', "cash/SoundCacheInspector.qml", 300, 500); + pages.addPage('openResourceInspector', 'Resource Inspector', "cash/ResourceInspector.qml", 300, 500); + + + pages.open('Cash'); + + + return pages; +} + + +openView(); diff --git a/scripts/developer/utilities/cache/cash.qml b/scripts/developer/utilities/cache/cash.qml new file mode 100644 index 0000000000..159ce95c5f --- /dev/null +++ b/scripts/developer/utilities/cache/cash.qml @@ -0,0 +1,167 @@ +// +// cash.qml +// +// Created by Sam Gateau on 17/9/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 + +import controlsUit 1.0 as HifiControls + +import "../lib/prop" as Prop +import "cash" +import "../lib/plotperf" + +Rectangle { + anchors.fill: parent + id: root; + + Prop.Global { id: global;} + color: global.color + + ScrollView { + id: scrollView + anchors.fill: parent + contentWidth: parent.width + clip: true + + Column { + id: column + width: parent.width + + Prop.PropFolderPanel { + label: "Resource Queries Inspector" + isUnfold: true + panelFrameData: Component { + Column { + PlotPerf { + title: "Global Queries" + height: 80 + valueScale: 1 + valueUnit: "" + plots: [ + { + object: ModelCache, + prop: "numGlobalQueriesPending", + label: "Pending", + color: "#1AC567" + }, + { + object: ModelCache, + prop: "numGlobalQueriesLoading", + label: "Loading", + color: "#FEC567" + }, + { + object: ModelCache, + prop: "numLoading", + label: "Model Loading", + color: "#C5FE67" + } + ] + } + } + } + } + + Prop.PropFolderPanel { + label: "Cache Inspectors" + isUnfold: true + panelFrameData: Component { + Column { + Prop.PropButton { + text: "Model" + onClicked: { + sendToScript({method: "openModelCacheInspector"}); + } + width:column.width + } + Prop.PropButton { + text: "Material" + onClicked: { + sendToScript({method: "openMaterialCacheInspector"}); + } + width:column.width + } + Prop.PropButton { + text: "Texture" + onClicked: { + sendToScript({method: "openTextureCacheInspector"}); + } + width:column.width + } + Prop.PropButton { + text: "Animation" + onClicked: { + sendToScript({method: "openAnimationCacheInspector"}); + } + width:column.width + } + Prop.PropButton { + text: "Sound" + onClicked: { + sendToScript({method: "openSoundCacheInspector"}); + } + width:column.width + } + } + } + } + Prop.PropFolderPanel { + label: "Stats" + isUnfold: true + panelFrameData: Component { Column { + PlotPerf { + title: "Resources" + height: 200 + valueScale: 1 + valueUnit: "" + plots: [ + { + object: TextureCache, + prop: "numTotal", + label: "Textures", + color: "#1AC567" + }, + { + object: TextureCache, + prop: "numCached", + label: "Textures Cached", + color: "#FEC567" + }, + { + object: ModelCache, + prop: "numTotal", + label: "Models", + color: "#FED959" + }, + { + object: ModelCache, + prop: "numCached", + label: "Models Cached", + color: "#FEFE59" + }, + { + object: MaterialCache, + prop: "numTotal", + label: "Materials", + color: "#00B4EF" + }, + { + object: MaterialCache, + prop: "numCached", + label: "Materials Cached", + color: "#FFB4EF" + } + ] + }} + } + } + } + } +} \ No newline at end of file diff --git a/scripts/developer/utilities/cache/cash/AnimationCacheInspector.qml b/scripts/developer/utilities/cache/cash/AnimationCacheInspector.qml new file mode 100644 index 0000000000..4ded44c2b1 --- /dev/null +++ b/scripts/developer/utilities/cache/cash/AnimationCacheInspector.qml @@ -0,0 +1,21 @@ +// +// AnimationCacheInspector.qml +// +// Created by Sam Gateau on 2019-09-17 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import QtQuick.Controls 2.5 +import QtQuick.Layouts 1.3 + +import "../../lib/prop" as Prop + +ResourceCacheInspector { + id: root; + anchors.fill: parent.fill + cache: AnimationCache + cacheResourceName: "Animation" +} diff --git a/scripts/developer/utilities/cache/cash/MaterialCacheInspector.qml b/scripts/developer/utilities/cache/cash/MaterialCacheInspector.qml new file mode 100644 index 0000000000..160c47c946 --- /dev/null +++ b/scripts/developer/utilities/cache/cash/MaterialCacheInspector.qml @@ -0,0 +1,21 @@ +// +// MaterialCacheInspector.qml +// +// Created by Sam Gateau on 2019-09-17 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import QtQuick.Controls 2.5 +import QtQuick.Layouts 1.3 + +import "../../lib/prop" as Prop + +ResourceCacheInspector { + id: root; + anchors.fill: parent.fill + cache: MaterialCache + cacheResourceName: "Material" +} diff --git a/scripts/developer/utilities/cache/cash/ModelCacheInspector.qml b/scripts/developer/utilities/cache/cash/ModelCacheInspector.qml new file mode 100644 index 0000000000..017942dfc9 --- /dev/null +++ b/scripts/developer/utilities/cache/cash/ModelCacheInspector.qml @@ -0,0 +1,21 @@ +// +// ModelCacheInspector.qml +// +// Created by Sam Gateau on 2019-09-17 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import QtQuick.Controls 2.5 +import QtQuick.Layouts 1.3 + +import "../../lib/prop" as Prop + +ResourceCacheInspector { + id: root; + anchors.fill: parent.fill + cache: ModelCache + cacheResourceName: "Model" +} diff --git a/scripts/developer/utilities/cache/cash/ResourceCacheInspector.qml b/scripts/developer/utilities/cache/cash/ResourceCacheInspector.qml new file mode 100644 index 0000000000..2fa0af8cbc --- /dev/null +++ b/scripts/developer/utilities/cache/cash/ResourceCacheInspector.qml @@ -0,0 +1,367 @@ +// +// ResourceCacheInspector.qml +// +// Created by Sam Gateau on 2019-09-17 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.12 +import QtQuick.Controls 2.5 +import QtQuick.Layouts 1.3 +import QtQml.Models 2.12 + +import "../../lib/skit/qml" as Skit +import "../../lib/prop" as Prop + +Item { + id: root; + Prop.Global { id: global } + + anchors.fill: parent.fill + property var cache: {} + property string cacheResourceName: "" + + function fromScript(message) { + switch (message.method) { + case "resetItemList": + resetItemListFromCache() + break; + } + } + + function requestResourceDetails(resourceURL) { + sendToScript({method: "inspectResource", params: {url: resourceURL, semantic: cacheResourceName}}); + } + + Component.onCompleted: { + resetItemListFromCache(); + } + + function fetchItemsList() { + var theList; + if (cache !== undefined) { + theList = cache.getResourceList(); + } else { + theList = ["ResourceCacheInspector.cache is undefined"]; + } + var theListString = new Array(theList.length) + for (var i in theList) { + theListString[i] = (theList[i].toString()) + } + return theListString; + } + + function resetItemListFromCache() { + resetItemList(fetchItemsList()) + } + + property var needFreshList : false + + function updateItemListFromCache() { + needFreshList = true + } + + + Timer { + interval: 2000; running: true; repeat: true + onTriggered: pullFreshValues() + } + + function pullFreshValues() { + if (needFreshList) { + updateItemList(fetchItemsList()) + needFreshList = false + } + } + + property alias resourceItemsModel: visualModel.model + property var currentItemsList: new Array(); + + function packItemEntry(item, identifier) { + var entry = { "identifier": identifier, "name": "", "scheme": "", "host": "", "pathDir": "", "url": item} + if (item.length > 0) { + // Detect scheme: + var schemePos = item.search(":") + entry.scheme = item.substring(0, schemePos) + if (schemePos < 0) schemePos = 0 + else schemePos += 1 + + // path pos is probably after schemePos + var pathPos = schemePos + + // try to detect //userinfo@host:port + var token = item.substr(schemePos, 2); + if (token.search("//") == 0) { + pathPos += 2 + } + item = item.substring(pathPos, item.length) + // item is now everything after scheme:[//] + var splitted = item.split('/') + + // odd ball, the rest of the url has no other'/' ? + // in theory this means it s just the host info ? + // we are assuming that path ALWAYS starts with a slash + entry.host = splitted[0] + + if (splitted.length > 1) { + entry.name = splitted[splitted.length - 1] + + // if splitted is longer than 2 then there should be a path dir + if (splitted.length > 2) { + for (var i = 1; i < splitted.length - 1; i++) { + entry.pathDir += '/' + splitted[i] + } + } + } + } + return entry + } + + + function resetItemList(itemList) { + currentItemsList = [] + resourceItemsModel.clear() + for (var i in itemList) { + var item = itemList[i] + currentItemsList.push(item) + resourceItemsModel.append(packItemEntry(item, currentItemsList.length -1)) + } + // At the end of it, force an update + visualModel.forceUpdate() + } + + function updateItemList(newItemList) { + resetItemList(newItemList) + } + + property var itemFields: ['identifier', 'name', 'scheme', 'host', 'pathDir', 'url'] + + + Column { + id: header + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + Item { + anchors.left: parent.left + anchors.right: parent.right + height: totalCount.height + id: headerTop + + Prop.PropButton { + id: refreshButton + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + text: "Refresh" + color: needFreshList ? global.colorOrangeAccent : global.fontColor + onPressed: { pullFreshValues() } + } + + GridLayout { + anchors.left: refreshButton.right + anchors.right: parent.right + id: headerCountLane + columns: 3 + + Item { + Layout.fillWidth: true + Prop.PropScalar { + id: itemCount + label: "Count" + object: root.cache + property: "numTotal" + integral: true + readOnly: true + } + } + Item { + Layout.fillWidth: true + Prop.PropScalar { + id: totalCount + label: "Count" + object: root.cache + property: "numTotal" + integral: true + readOnly: true + onSourceValueVarChanged: { updateItemListFromCache() } + } + } + Item { + Layout.fillWidth: true + Prop.PropScalar { + id: cachedCount + label: "Cached" + object: root.cache + property: "numCached" + integral: true + readOnly: true + onSourceValueVarChanged: { updateItemListFromCache() } + } + } + } + } + Item { + anchors.left: parent.left + anchors.right: parent.right + height: orderSelector.height + + Prop.PropText { + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + width: 50 + id: orderSelectorLabel + text: "Sort by" + horizontalAlignment: Text.AlignHCenter + } + Prop.PropComboBox { + anchors.left: orderSelectorLabel.right + width: 80 + id: orderSelector + model: itemFields + currentIndex: 1 + + property var isSchemeVisible: (currentIndex == 2 || filterFieldSelector.currentIndex == 2) + property var isHostVisible: (currentIndex == 3 || filterFieldSelector.currentIndex == 3) + property var isPathDirVisible: (currentIndex == 4 || filterFieldSelector.currentIndex == 4) + property var isURLVisible: (currentIndex == 5 || filterFieldSelector.currentIndex == 5) + } + + Prop.PropTextField { + anchors.left: orderSelector.right + anchors.right: filterFieldSelector.left + id: nameFilter + placeholderText: qsTr("Filter by " + itemFields[filterFieldSelector.currentIndex] + "...") + Layout.fillWidth: true + } + Prop.PropComboBox { + anchors.right: parent.right + id: filterFieldSelector + model: itemFields + currentIndex: 1 + width: 80 + opacity: (nameFilter.text.length > 0) ? 1.0 : 0.5 + } + } + } + + Component { + id: resouceItemDelegate + MouseArea { + id: dragArea + property bool held: false + anchors { left: parent.left; right: parent.right } + height: item.height + onPressed: {held = true} + onReleased: {held = false} + onDoubleClicked: { requestResourceDetails(model.url) } + Rectangle { + id: item + width: parent.width + height: global.slimHeight + color: dragArea.held ? global.colorBackHighlight : (index % 2 ? global.colorBackShadow : global.colorBack) + Row { + id: itemRow + anchors.verticalCenter : parent.verticalCenter + Prop.PropText { + id: itemIdentifier + text: model.identifier + width: 30 + } + Prop.PropSplitter { + visible: orderSelector.isSchemeVisible + size:8 + } + Prop.PropLabel { + visible: orderSelector.isSchemeVisible + text: model.scheme + width: 30 + } + Prop.PropSplitter { + visible: orderSelector.isHostVisible + size:8 + } + Prop.PropLabel { + visible: orderSelector.isHostVisible + text: model.host + width: 150 + } + Prop.PropSplitter { + visible: orderSelector.isPathDirVisible + size:8 + } + Prop.PropLabel { + visible: orderSelector.isPathDirVisible + text: model.pathDir + } + Prop.PropSplitter { + size:8 + } + Prop.PropLabel { + visible: !orderSelector.isURLVisible + text: model.name + } + Prop.PropLabel { + visible: orderSelector.isURLVisible + text: model.url + } + } + } + + } + } + + Skit.SortFilterModel { + id: visualModel + model: ListModel {} + + property int sortOrder: orderSelector.currentIndex + + property var lessThanArray: [ + function(left, right) { return left.index < right.index }, + function(left, right) { return left.name < right.name }, + function(left, right) { return left.scheme < right.scheme }, + function(left, right) { return left.host < right.host }, + function(left, right) { return left.pathDir < right.pathDir }, + function(left, right) { return left.url < right.url } + ]; + lessThan: lessThanArray[sortOrder] + + property int filterField: filterFieldSelector.currentIndex + onFilterFieldChanged: { refreshFilter() } + property var textFilter: nameFilter.text + onTextFilterChanged: { refreshFilter() } + + function filterToken(itemWord, token) { return (itemWord.search(token) > -1) } + property var acceptItemArray: [ + function(item) { return true }, + function(item) { return filterToken(item.identifier.toString(), textFilter) }, + function(item) { return filterToken(item.name, textFilter) }, + function(item) { return filterToken(item.scheme, textFilter) }, + function(item) { return filterToken(item.host, textFilter) }, + function(item) { return filterToken(item.pathDir, textFilter) }, + function(item) { return filterToken(item.url, textFilter) } + ] + + function refreshFilter() { + //console.log("refreshFilter! token = " + textFilter + " field = " + filterField) + acceptItem = acceptItemArray[(textFilter.length != 0) * + (1 + filterField)] + } + + delegate: resouceItemDelegate + } + + ListView { + anchors.top: header.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + clip: true + + id: listView + model: visualModel + } +} diff --git a/scripts/developer/utilities/cache/cash/ResourceInspector.qml b/scripts/developer/utilities/cache/cash/ResourceInspector.qml new file mode 100644 index 0000000000..099eb735d0 --- /dev/null +++ b/scripts/developer/utilities/cache/cash/ResourceInspector.qml @@ -0,0 +1,58 @@ +// +// ResourceInspector.qml +// +// Created by Sam Gateau on 2019-09-24 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.12 +import QtQuick.Controls 2.5 +import QtQuick.Layouts 1.3 +import QtQml.Models 2.12 + +import "../../lib/prop" as Prop + +Item { + id: root; + Prop.Global { id: global } + + anchors.fill: parent.fill + property var cache: {} + property string cacheResourceName: "" + + function fromScript(message) { + switch (message.method) { + case "inspectResource": + inspectResource(message.params.url, message.params.semantic) + break; + } + } + + function inspectResource(url, semantic) { + console.log("inspectResource :" + url + " as " + semantic) + info.text = "url: " + url + "\nsemantic: " + semantic + "\n"; + + if (semantic == "Texture") { + var res = TextureCache.prefetch(url, 0) + info.text += JSON.stringify(res); + } else if (semantic == "Model") { + var res = ModelCache.prefetch(url) + info.text += JSON.stringify(res); + } + } + + TextEdit { + id: info + anchors.fill: parent + text: "Click an object to get material JSON" + width: root.width + font.pointSize: 10 + color: "#FFFFFF" + readOnly: true + selectByMouse: true + wrapMode: Text.WordWrap + } +} + diff --git a/scripts/developer/utilities/cache/cash/SoundCacheInspector.qml b/scripts/developer/utilities/cache/cash/SoundCacheInspector.qml new file mode 100644 index 0000000000..26b043469e --- /dev/null +++ b/scripts/developer/utilities/cache/cash/SoundCacheInspector.qml @@ -0,0 +1,21 @@ +// +// SoundCacheInspector.qml +// +// Created by Sam Gateau on 2019-09-17 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import QtQuick.Controls 2.5 +import QtQuick.Layouts 1.3 + +import "../../lib/prop" as Prop + +ResourceCacheInspector { + id: root; + anchors.fill: parent.fill + cache: SoundCache + cacheResourceName: "Sound" +} diff --git a/scripts/developer/utilities/cache/cash/TextureCacheInspector.qml b/scripts/developer/utilities/cache/cash/TextureCacheInspector.qml new file mode 100644 index 0000000000..9bfd663a4e --- /dev/null +++ b/scripts/developer/utilities/cache/cash/TextureCacheInspector.qml @@ -0,0 +1,21 @@ +// +// TextureCacheInspector.qml +// +// Created by Sam Gateau on 2019-09-17 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import QtQuick.Controls 2.5 +import QtQuick.Layouts 1.3 + +import "../../lib/prop" as Prop + +ResourceCacheInspector { + id: root; + anchors.fill: parent.fill + cache: TextureCache + cacheResourceName: "Texture" +} diff --git a/scripts/developer/utilities/cache/cash/qmldir b/scripts/developer/utilities/cache/cash/qmldir new file mode 100644 index 0000000000..8793a6b8f5 --- /dev/null +++ b/scripts/developer/utilities/cache/cash/qmldir @@ -0,0 +1,6 @@ +ResourceCacheInspector 1.0 ResourceCacheInspector.qml +TextureCacheInspector 1.0 TextureCacheInspector.qml +MaterialCacheInspector 1.0 MaterialCacheInspector.qml +ModelCacheInspector 1.0 ModelCacheInspector.qml +AnimationCacheInspector 1.0 AnimationCacheInspector.qml +SoundCacheInspector 1.0 SoundCacheInspector.qml \ No newline at end of file diff --git a/scripts/developer/utilities/lib/jet/qml/TaskListView.qml b/scripts/developer/utilities/lib/jet/qml/TaskListView.qml index e2576fe783..a935163bd9 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskListView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskListView.qml @@ -91,12 +91,12 @@ Rectangle { } } - Original.ScrollView { + ListView { anchors.fill: parent - ListView { - id: theView - model: jobsModel - delegate: objRecursiveDelegate - } + + id: theView + model: jobsModel + delegate: objRecursiveDelegate } + } \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/PropEnum.qml b/scripts/developer/utilities/lib/prop/PropEnum.qml index 2268b21e34..97c385281d 100644 --- a/scripts/developer/utilities/lib/prop/PropEnum.qml +++ b/scripts/developer/utilities/lib/prop/PropEnum.qml @@ -16,6 +16,7 @@ PropItem { id: root property alias enums : valueCombo.model + property alias currentIndex : valueCombo.currentIndex PropComboBox { id: valueCombo diff --git a/scripts/developer/utilities/lib/prop/PropItem.qml b/scripts/developer/utilities/lib/prop/PropItem.qml index 6d2f4c11ad..3779e7f4a9 100644 --- a/scripts/developer/utilities/lib/prop/PropItem.qml +++ b/scripts/developer/utilities/lib/prop/PropItem.qml @@ -25,19 +25,16 @@ Item { // function defaultGet() { var v = root.object[root.property]; return v; } function defaultSet(value) { root.object[root.property] = value; } - // function defaultSetReadOnly(value) { log ( "read only " + property + ", NOT setting to " + value); } - // function defaultSetReadOnly(value) {} - // property var valueVarSetter: (root.readOnly ? defaultSetReadOnly : defaultSet) - property var valueVarSetter: defaultSet + function defaultSetReadOnly(value) {} + + property var valueVarSetter: (readOnly ? defaultSetReadOnly : defaultSet) property var valueVarGetter: defaultGet // PropItem is stretching horizontally accross its parent // Fixed height + height: global.lineHeight anchors.left: parent.left anchors.right: parent.right - height: global.lineHeight - anchors.leftMargin: global.horizontalMargin - anchors.rightMargin: global.horizontalMargin // LabelControl And SplitterControl are on the left side of the PropItem property bool showLabel: true diff --git a/scripts/developer/utilities/lib/prop/PropScalar.qml b/scripts/developer/utilities/lib/prop/PropScalar.qml index ce89342997..3776f5c4bc 100644 --- a/scripts/developer/utilities/lib/prop/PropScalar.qml +++ b/scripts/developer/utilities/lib/prop/PropScalar.qml @@ -32,9 +32,7 @@ PropItem { property var sourceValueVar: root.valueVarGetter() function applyValueVarFromWidgets(value) { - if (!root.readOnly) { - root.valueVarSetter(value) - } + root.valueVarSetter(value) } PropLabel { @@ -58,6 +56,7 @@ PropItem { MouseArea{ id: mousearea + enabled: !root.readOnly anchors.fill: parent onDoubleClicked: { sliderControl.visible = !sliderControl.visible } } diff --git a/scripts/developer/utilities/lib/prop/qmldir b/scripts/developer/utilities/lib/prop/qmldir index e09785846d..99e721fb33 100644 --- a/scripts/developer/utilities/lib/prop/qmldir +++ b/scripts/developer/utilities/lib/prop/qmldir @@ -1,8 +1,10 @@ Module Prop Global 1.0 style/Global.qml PropText 1.0 style/PiText.qml +PropTextField 1.0 style/PiTextField.qml PropLabel 1.0 style/PiLabel.qml PropSplitter 1.0 style/PiSplitter.qml +PropButton 1.0 style/PiButton.qml PropComboBox 1.0 style/PiComboBox.qml PropCanvasIcon 1.0 style/PiCanvasIcon.qml PropCheckBox 1.0 style/PiCheckBox.qml diff --git a/scripts/developer/utilities/lib/prop/style/PiButton.qml b/scripts/developer/utilities/lib/prop/style/PiButton.qml new file mode 100644 index 0000000000..5469431d81 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/style/PiButton.qml @@ -0,0 +1,35 @@ +// +// Prop/style/PiButton.qml +// +// Created by Sam Gateau on 17/09/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.6 +import QtQuick.Controls 2.1 + +Button { + Global { id: global } + id: control + text: "" + spacing: 0 + property alias color: theContentItem.color + + contentItem: PiText { + id: theContentItem + text: control.text + horizontalAlignment: Text.AlignHCenter + color: global.fontColor + } + + background: Rectangle { + color: control.down ? global.colorBackHighlight : global.colorBackShadow + opacity: enabled ? 1 : 0.3 + border.color: control.down ? global.colorBorderHighight : (control.hovered ? global.colorBorderHighight : global.colorBorderLight) + border.width: global.valueBorderWidth + radius: global.valueBorderRadius + } +} \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/style/PiComboBox.qml b/scripts/developer/utilities/lib/prop/style/PiComboBox.qml index 92164fefc5..9430228eba 100644 --- a/scripts/developer/utilities/lib/prop/style/PiComboBox.qml +++ b/scripts/developer/utilities/lib/prop/style/PiComboBox.qml @@ -16,7 +16,9 @@ ComboBox { id: valueCombo height: global.slimHeight - + width: 120 + implicitHeight: global.slimHeight + // look flat: true @@ -51,8 +53,6 @@ ComboBox { } background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 color: global.colorBack border.color: valueCombo.popup.visible ? global.colorBorderLighter : global.colorBorderLight border.width: global.valueBorderWidth diff --git a/scripts/developer/utilities/lib/prop/style/PiTextField.qml b/scripts/developer/utilities/lib/prop/style/PiTextField.qml new file mode 100644 index 0000000000..8dfb9d88ee --- /dev/null +++ b/scripts/developer/utilities/lib/prop/style/PiTextField.qml @@ -0,0 +1,31 @@ +// +// Prop/style/PiTextField.qml +// +// Created by Sam Gateau on 9/24/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +TextField { + id: control + Global { id: global } + implicitHeight: global.slimHeight + implicitWidth: 200 + + placeholderText: qsTr("Enter description") + + color: global.fontColor + font.pixelSize: global.fontSize + font.family: global.fontFamily + font.weight: global.fontWeight + + background: Rectangle { + color: (control.text.length > 0) ? global.colorBackHighlight : global.colorBackShadow + border.color: (control.text.length > 0) ? global.colorBorderHighight : "transparent" + } +} diff --git a/scripts/developer/utilities/render/luci/Page.js b/scripts/developer/utilities/lib/skit/Page.js similarity index 69% rename from scripts/developer/utilities/render/luci/Page.js rename to scripts/developer/utilities/lib/skit/Page.js index 06c9704abf..189c26044a 100644 --- a/scripts/developer/utilities/render/luci/Page.js +++ b/scripts/developer/utilities/lib/skit/Page.js @@ -10,11 +10,12 @@ "use strict"; (function() { -function Page(title, qmlurl, width, height, onViewCreated, onViewClosed) { +function Page(title, qmlurl, width, height, onFromQml, onViewCreated, onViewClosed) { this.title = title; - this.qml = qmlurl; + this.qmlurl = qmlurl; this.width = width; this.height = height; + this.onFromQml = onFromQml; this.onViewCreated = onViewCreated; this.onViewClosed = onViewClosed; @@ -30,6 +31,9 @@ Page.prototype.killView = function () { //this.window.closed.disconnect(function () { // this.killView(); //}); + if (this.onFromQml) { + this.window.fromQml.disconnect(this.onFromQml) + } this.window.close(); this.window = false; } @@ -39,12 +43,15 @@ Page.prototype.createView = function () { var that = this; if (!this.window) { print("Page: New window for page:" + this.title); - this.window = Desktop.createWindow(Script.resolvePath(this.qml), { + this.window = Desktop.createWindow(this.qmlurl, { title: this.title, presentationMode: Desktop.PresentationMode.NATIVE, size: {x: this.width, y: this.height} }); this.onViewCreated(this.window); + if (this.onFromQml) { + this.window.fromQml.connect(this.onFromQml) + } this.window.closed.connect(function () { that.killView(); that.onViewClosed(); @@ -53,11 +60,13 @@ Page.prototype.createView = function () { }; -Pages = function () { +Pages = function (relativePath) { + print(relativePath) + this._relativePath = relativePath this._pages = {}; }; -Pages.prototype.addPage = function (command, title, qmlurl, width, height, onViewCreated, onViewClosed) { +Pages.prototype.addPage = function (command, title, qmlurl, width, height, onFromQml, onViewCreated, onViewClosed) { if (onViewCreated === undefined) { // Workaround for bad linter onViewCreated = function(window) {}; @@ -66,7 +75,7 @@ Pages.prototype.addPage = function (command, title, qmlurl, width, height, onVie // Workaround for bad linter onViewClosed = function() {}; } - this._pages[command] = new Page(title, qmlurl, width, height, onViewCreated, onViewClosed); + this._pages[command] = new Page(title, Script.resolvePath(this._relativePath + qmlurl), width, height, onFromQml, onViewCreated, onViewClosed); }; Pages.prototype.open = function (command) { @@ -87,4 +96,12 @@ Pages.prototype.clear = function () { this._pages = {}; }; +Pages.prototype.sendTo = function (command, message) { + if (!this._pages[command]) { + print("Pages: unknown command = " + command); + return; + } + this._pages[command].window.sendToQml(message); +}; + }()); diff --git a/scripts/developer/utilities/lib/skit/qml/SortFilterModel.qml b/scripts/developer/utilities/lib/skit/qml/SortFilterModel.qml new file mode 100644 index 0000000000..08ad8d1dbd --- /dev/null +++ b/scripts/developer/utilities/lib/skit/qml/SortFilterModel.qml @@ -0,0 +1,93 @@ +import QtQuick 2.9 +import QtQml.Models 2.3 + +DelegateModel { + id: delegateModel + + property var lessThan: function(left, right) { return true; } + property var acceptItem: function(item) { return true; } + + function insertPosition(lessThanFunctor, item) { + var lower = 0 + var upper = visibleItems.count + while (lower < upper) { + var middle = Math.floor(lower + (upper - lower) / 2) + var result = lessThanFunctor(item.model, visibleItems.get(middle).model); + if (result) { + upper = middle + } else { + lower = middle + 1 + } + } + return lower + } + + function sortAndFilter(lessThanFunctor, acceptItemFunctor) { + while (unsortedItems.count > 0) { + var item = unsortedItems.get(0) + + if (acceptItemFunctor(item.model)) { + var index = insertPosition(lessThanFunctor, item) + + item.groups = ["items","visible"] + visibleItems.move(item.visibleIndex, index) + } else { + item.groups = ["items"] + } + } + } + + // Private bool to track when items changed and view is dirty + property bool itemsDirty: true + function update() { + console.log("SortFilterMode: update and sort and filter items !!" + items.count); + if (items.count > 0) { + items.setGroups(0, items.count, ["items","unsorted"]); + } + + sortAndFilter(lessThan, acceptItem) + itemsDirty = false; + itemsUpdated() + } + + signal itemsUpdated() + + function updateOnItemsChanged() { + itemsDirty = true; + if (isAutoUpdateOnChanged) { + update() + } + } + + property bool isAutoUpdateOnChanged: false + function setAutoUpdateOnChanged(enabled) { + isAutoUpdateOnChanged = enabled + if (enabled) { + update() + } + } + + function forceUpdate() { + update(); + } + items.onChanged: updateOnItemsChanged() + onLessThanChanged: update() + onAcceptItemChanged: update() + + groups: [ + DelegateModelGroup { + id: visibleItems + + name: "visible" + includeByDefault: false + }, + DelegateModelGroup { + id: unsortedItems + + name: "unsorted" + includeByDefault: false + } + ] + + filterOnGroup: "visible" +} \ No newline at end of file diff --git a/scripts/developer/utilities/lib/skit/qml/qmldir b/scripts/developer/utilities/lib/skit/qml/qmldir new file mode 100644 index 0000000000..14d11d998a --- /dev/null +++ b/scripts/developer/utilities/lib/skit/qml/qmldir @@ -0,0 +1 @@ +SortFilterModel 1.0 SortFilterModel.qml \ No newline at end of file diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index e2e5523ccd..3b832bdfb4 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -1,14 +1,13 @@ var MaterialInspector = Script.require('./materialInspector.js'); -var Page = Script.require('./luci/Page.js'); + +var Page = Script.require('../lib/skit/Page.js'); function openView() { - //window.closed.connect(function() { Script.stop(); }); + var pages = new Pages(Script.resolvePath(".")); - - var pages = new Pages(); function fromQml(message) { if (pages.open(message.method)) { return; @@ -17,12 +16,6 @@ function openView() { var luciWindow function openLuciWindow(window) { - if (luciWindow !== undefined) { - activeWindow.fromQml.disconnect(fromQml); - } - if (window !== undefined) { - window.fromQml.connect(fromQml); - } luciWindow = window; @@ -57,9 +50,6 @@ function openView() { } function closeLuciWindow() { - if (luciWindow !== undefined) { - activeWindow.fromQml.disconnect(fromQml); - } luciWindow = {}; Controller.mousePressEvent.disconnect(onMousePressEvent); @@ -68,10 +58,10 @@ function openView() { pages.clear(); } - pages.addPage('Luci', 'Luci', '../luci.qml', 300, 420, openLuciWindow, closeLuciWindow); - pages.addPage('openEngineInspectorView', 'Render Engine Inspector', '../engineInspector.qml', 300, 400); - pages.addPage('openEngineLODView', 'Render LOD', '../lod.qml', 300, 400); - pages.addPage('openMaterialInspectorView', 'Material Inspector', '../materialInspector.qml', 300, 400, MaterialInspector.setWindow, MaterialInspector.setWindow); + pages.addPage('Luci', 'Luci', 'luci.qml', 300, 420, fromQml, openLuciWindow, closeLuciWindow); + pages.addPage('openEngineInspectorView', 'Render Engine Inspector', 'engineInspector.qml', 300, 400); + pages.addPage('openEngineLODView', 'Render LOD', 'lod.qml', 300, 400); + pages.addPage('openMaterialInspectorView', 'Material Inspector', 'materialInspector.qml', 300, 400, null, MaterialInspector.setWindow, MaterialInspector.setWindow); pages.open('Luci'); diff --git a/scripts/developer/utilities/render/materialInspector.js b/scripts/developer/utilities/render/materialInspector.js index 98d9f769fb..8a7d5ad7dd 100644 --- a/scripts/developer/utilities/render/materialInspector.js +++ b/scripts/developer/utilities/render/materialInspector.js @@ -128,7 +128,7 @@ function fromQml(message) { var SELECT_LIST = "luci_materialInspector_SelectionList"; Selection.enableListHighlight(SELECT_LIST, { - outlineUnoccludedColor: { red: 255, green: 255, blue: 255 } + outlineUnoccludedColor: { red: 125, green: 255, blue: 225 } }); function setSelectedObject(id, type) { Selection.clearSelectedItemsList(SELECT_LIST); diff --git a/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js b/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js index 189608fafd..8633fe8870 100644 --- a/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js +++ b/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js @@ -203,10 +203,20 @@ function maybeDeleteRemoteIndicatorTimeout() { } } - var reactionsBegun = []; var pointReticle = null; var mouseMoveEventsConnected = false; +var targetPointInterpolateConnected = false; +var pointAtTarget = Vec3.ZERO; +var isReticleVisible = true; + +function targetPointInterpolate() { + if (reticlePosition) { + pointAtTarget = Vec3.mix(pointAtTarget, reticlePosition, POINT_AT_MIX_ALPHA); + isReticleVisible = MyAvatar.setPointAt(pointAtTarget); + } +} + function beginReactionWrapper(reaction) { maybeDeleteRemoteIndicatorTimeout(); @@ -227,14 +237,18 @@ function beginReactionWrapper(reaction) { break; case ("point"): deleteOldReticles(); + pointAtTarget = MyAvatar.getHeadLookAt(); if (!mouseMoveEventsConnected) { Controller.mouseMoveEvent.connect(mouseMoveEvent); mouseMoveEventsConnected = true; } + if (!targetPointInterpolateConnected) { + Script.update.connect(targetPointInterpolate); + targetPointInterpolateConnected = true; + } } } - // Checks to see if there are any reticle entities already to delete function deleteOldReticles() { MyAvatar.getAvatarEntitiesVariant() @@ -250,6 +264,8 @@ function deleteOldReticles() { var MAX_INTERSECTION_DISTANCE_M = 50; var reticleUpdateRateLimiterTimer = false; var RETICLE_UPDATE_RATE_LIMITER_TIMER_MS = 75; +var POINT_AT_MIX_ALPHA = 0.15; +var reticlePosition = Vec3.ZERO; function mouseMoveEvent(event) { if (!reticleUpdateRateLimiterTimer) { reticleUpdateRateLimiterTimer = Script.setTimeout(function() { @@ -261,11 +277,10 @@ function mouseMoveEvent(event) { var pickRay = Camera.computePickRay(event.x, event.y); - var avatarIntersectionData = AvatarManager.findRayIntersection(pickRay); + var avatarIntersectionData = AvatarManager.findRayIntersection(pickRay, [], [MyAvatar.sessionUUID], false); var entityIntersectionData = Entities.findRayIntersection(pickRay, true); var avatarIntersectionDistanceM = avatarIntersectionData.intersects && avatarIntersectionData.distance < MAX_INTERSECTION_DISTANCE_M ? avatarIntersectionData.distance : null; var entityIntersectionDistanceM = entityIntersectionData.intersects && entityIntersectionData.distance < MAX_INTERSECTION_DISTANCE_M ? entityIntersectionData.distance : null; - var reticlePosition; if (avatarIntersectionDistanceM && entityIntersectionDistanceM) { if (avatarIntersectionDistanceM < entityIntersectionDistanceM) { @@ -283,7 +298,7 @@ function mouseMoveEvent(event) { } if (pointReticle && reticlePosition) { - Entities.editEntity(pointReticle, { position: reticlePosition }); + Entities.editEntity(pointReticle, { position: reticlePosition, visible: isReticleVisible }); } else if (reticlePosition) { pointReticle = Entities.addEntity({ type: "Box", @@ -349,6 +364,10 @@ function endReactionWrapper(reaction) { Controller.mouseMoveEvent.disconnect(mouseMoveEvent); mouseMoveEventsConnected = false; } + if (targetPointInterpolateConnected) { + Script.update.disconnect(targetPointInterpolate); + targetPointInterpolateConnected = false; + } maybeClearReticleUpdateLimiterTimeout(); deleteOldReticles(); break; @@ -757,7 +776,6 @@ function toggleEmojiApp() { emojiAPI.registerAvimojiQMLWindow(emojiAppWindow); } - // #endregion // ************************************* // END EMOJI_MAIN diff --git a/tools/ci-scripts/notarize b/tools/ci-scripts/notarize new file mode 100755 index 0000000000..aa4e84dda8 --- /dev/null +++ b/tools/ci-scripts/notarize @@ -0,0 +1,222 @@ +#!/bin/bash +set -e + +# Sign, Notarize, and Staple a notarization ticket to the app. + +function usage { + echo "usage: $0 datt bundle_path [entitlements]" + exit -1 +} + +apple_id="" +developer_id_key_label="Mac Developer ID Application: High Fidelity Inc" +k_password_label="com.highfidelity.mac-signing-appleid" +k_password_service="AC_PASSWORD" + +# Check for supported Mac OS Version (10.14 or greater) +if [ ! -x /usr/bin/sw_vers ]; then + echo "Can't find sw_vers, is this Mac OS?" + exit -1 +fi +set +e +/usr/bin/sw_vers -productVersion | \ + awk -F'.' '{ if ($1 == 10 && $2 >= 14) { exit 0 } else { exit 1 } }' +is_supported=$? +set -e +if [[ $is_supported -eq 1 ]]; then + echo "Must use Mac OS 10.14 or greater" + exit -1 +fi + +# Check for Xcode +if [ ! -x /usr/bin/xcode-select ]; then + echo "Xcode is not installed" + exit -1 +fi + +function set_apple_id { + if [[ "$apple_id" != "" ]]; then return; fi # already set + + apple_id=$(security find-generic-password \ + -l $k_password_label -s $k_password_service | \ + grep acct | awk -F'=' '{ print $2 }' | tr -d '"') + if [ "$apple_id" == "" ]; then + echo "couldn't find Apple ID in keychain" + exit -1 + fi +} + +function ensure_developer_id_key_is_present { + set +e + security find-key -l "$developer_id_key_label" > /dev/null 2>&1 + success=$? + set -e + if [[ $success == 1 ]]; then + echo "The Developer ID Key is not in your Keychain: $developer_id_key_label" + exit -1 + fi +} + +function sign { + bundle_path="$1" + entitlements="$2" + + ensure_developer_id_key_is_present + if [ "$entitlements" != "" ]; then + codesign --force \ + --deep \ + --sign "Developer ID Application: High Fidelity Inc" \ + --timestamp \ + --options runtime \ + --entitlements "$entitlements" \ + "$bundle_path" + else + codesign --force \ + --deep \ + --sign "Developer ID Application: High Fidelity Inc" \ + --timestamp \ + --options runtime \ + "$bundle_path" + fi +} + +# The `notarize` function sets `result_uuid` on return. +result_uuid="" + +function notarize { + local bundle_path=$1 + + local bundle_id=$(/usr/libexec/PlistBuddy -c Print:CFBundleIdentifier \ + "$bundle_path"/Contents/Info.plist) + if [ "$bundle_id" == "" ]; then + echo "couldn't find bundle id in bundle's Info.plist: $bundle_path" + exit -1 + fi + + set_apple_id + + if [ -e "$bundle_path.zip" ]; then + echo "zip already exists, moving it out of the way" + mv "$bundle_path.zip"{,.$(uuidgen)} + fi + + local local_bundle_path=$(basename "$bundle_path") + (cd "$bundle_path/.."; \ + zip -r --symlinks "$local_bundle_path.zip" "$local_bundle_path" \ + >/dev/null 2>&1) + + # `result_uuid` is intentionally global + result_uuid=$(xcrun altool --notarize-app \ + --primary-bundle-id $bundle_id \ + --username $apple_id --password "@keychain:$k_password_service" \ + --file "$bundle_path.zip" 2>&1 | tee /dev/tty | \ + awk -F'=' '{ if (/^RequestUUID/) print $2 }' | sed 's/^ //') +} + +function is_notarization_complete { + set_apple_id + + set +e + local status_code=$(xcrun altool --notarization-info $result_uuid \ + --username $apple_id --password "@keychain:$k_password_service" 2>&1 | \ + awk '{ if (/Status Code/) print $3 }') + set -e + # Status Code is only set when a job is complete. + if [[ "$status_code" -ne "" ]]; then + return 1 + fi + return $status_code +} + +command=$1 + +case $command in + # Utility to add your Apple ID to the keychain + add_password) + echo -n "Apple ID: " + read appleid_name + if [ "$appleid_name" == "" ]; then + echo "Apple ID was empty!" + exit -1 + fi + security add-generic-password \ + -a "$appleid_name" \ + -s "$k_password_service" \ + -l "$k_password_label" \ + -j "Apple ID used for High Fidelity's code signing" \ + -w + ;; + + # Sign a bundle with the Developer ID Application cert + sign) + bundle_path=$2 + if [ "$bundle_path" == "" ]; then + echo "usage: $0 $command bundle_path" + exit -1 + fi + if [ ! -d "$bundle_path" ]; then + echo "bundle_path doesn't exist" + exit -1 + fi + + sign "$bundle_path" "$3" + ;; + + # Submit a bundle to Apple for notarization + notarize) + bundle_path=$2 + if [ "$bundle_path" == "" ]; then + echo "usage: $0 $command bundle_path" + exit -1 + fi + if [ ! -d "$bundle_path" ]; then + echo "bundle_path doesn't exist" + exit -1 + fi + + notarize "$bundle_path" + ;; + + # Check the notarization logs + logs) + result_uuid=$2 + if [ "$result_uuid" == "" ]; then + echo "usage: $0 $command result_uuid" + exit -1 + fi + set_apple_id + + xcrun altool --notarization-info $result_uuid \ + --username $apple_id --password "@keychain:$k_password_service" + ;; + + # Do all the things. + datt) + bundle_path=$2 + if [ "$bundle_path" == "" ]; then + echo "usage: $0 bundle_path [entitlements]" + exit -1 + fi + if [ ! -d "$bundle_path" ]; then + echo "bundle_path doesn't exist" + exit -1 + fi + + sign "$bundle_path" "$3" + notarize "$bundle_path" + + # TODO: is_notarization_complete needs to recognize notarization + # while :; do + # echo "sleeping..." + # sleep 27 + # is_notarization_complete + # [[ $? == 1 ]] && break + # done + # xcrun stapler staple "$bundle_path" + ;; + + *) + usage + ;; + +esac diff --git a/tools/gpu-frame-player/src/RenderThread.cpp b/tools/gpu-frame-player/src/RenderThread.cpp index ff0d7630e5..31e154d38c 100644 --- a/tools/gpu-frame-player/src/RenderThread.cpp +++ b/tools/gpu-frame-player/src/RenderThread.cpp @@ -51,6 +51,12 @@ void RenderThread::initialize(QWindow* window) { _backend = _gpuContext->getBackend(); _context.doneCurrent(); _context.moveToThread(_thread); + + if (!_presentPipeline) { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + _presentPipeline = gpu::Pipeline::create(program, state); + } #else auto size = window->size(); _extent = vk::Extent2D{ (uint32_t)size.width(), (uint32_t)size.height() }; @@ -169,15 +175,28 @@ void RenderThread::renderFrame(gpu::FramePointer& frame) { } #ifdef USE_GL + static gpu::BatchPointer batch = nullptr; + if (!batch) { + batch = std::make_shared(); + batch->setPipeline(_presentPipeline); + batch->setFramebuffer(nullptr); + batch->setResourceTexture(0, frame->framebuffer->getRenderBuffer(0)); + batch->setViewportTransform(ivec4(uvec2(0), ivec2(windowSize.width(), windowSize.height()))); + batch->draw(gpu::TRIANGLE_STRIP, 4); + } + glDisable(GL_FRAMEBUFFER_SRGB); + _gpuContext->executeBatch(*batch); + + // Keep this raw gl code here for reference //glDisable(GL_FRAMEBUFFER_SRGB); //glClear(GL_COLOR_BUFFER_BIT); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + /* glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBlitFramebuffer( 0, 0, fboSize.x, fboSize.y, 0, 0, windowSize.width(), windowSize.height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); - +*/ (void)CHECK_GL_ERROR(); _context.swapBuffers(); _context.doneCurrent(); diff --git a/tools/gpu-frame-player/src/RenderThread.h b/tools/gpu-frame-player/src/RenderThread.h index 09eef56623..b090e1737f 100644 --- a/tools/gpu-frame-player/src/RenderThread.h +++ b/tools/gpu-frame-player/src/RenderThread.h @@ -57,7 +57,7 @@ public: uint32_t _externalTexture{ 0 }; void move(const glm::vec3& v); glm::mat4 _correction; - + gpu::PipelinePointer _presentPipeline; void resize(const QSize& newSize); void setup() override; diff --git a/unpublishedScripts/marketplace/blocks/blocksApp.js b/unpublishedScripts/marketplace/blocks/blocksApp.js index 813a77c74a..9c10ba8197 100644 --- a/unpublishedScripts/marketplace/blocks/blocksApp.js +++ b/unpublishedScripts/marketplace/blocks/blocksApp.js @@ -35,7 +35,7 @@ function onClicked() { if (!shown) { - tablet.gotoWebScreen(APP_URL, "", true); + tablet.gotoWebScreen(APP_URL, ""); } else { tablet.gotoHomeScreen(); }