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/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/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 21d44a4145..b4a37519a6 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()); @@ -7461,6 +7470,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()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 518aa4fe2c..54785933c9 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_LOOK_AT || mode == CAMERA_MODE_SELFIE) { - updateHeadLookAt(deltaTime); + if (!_pointAtActive || !_isPointTargetValid) { + updateHeadLookAt(deltaTime); + } else { + resetHeadLookAt(); + } } else if (_headLookAtActive){ resetHeadLookAt(); _headLookAtActive = false; @@ -3514,6 +3523,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; @@ -3536,9 +3551,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) { @@ -3623,8 +3636,12 @@ void MyAvatar::updateOrientation(float deltaTime) { if (frontBackDot < 0.0f) { ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight); cameraVector = (ajustedYawVector * _lookAtPitch) * Vectors::FRONT; - if (frontBackDot < -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE))) { - _shouldTurnToFaceCamera = true; + if (!isRotatingWhileSeated) { + if (frontBackDot < -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE))) { + _shouldTurnToFaceCamera = true; + } + } else { + setWorldOrientation(previousOrientation); } } else if (frontBackDot > glm::sin(glm::radians(REORIENT_ANGLE))) { _shouldTurnToFaceCamera = false; @@ -6105,6 +6122,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; @@ -6114,13 +6135,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; } @@ -6131,10 +6157,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++) { @@ -6660,10 +6689,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); } } @@ -6679,39 +6740,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; @@ -6735,3 +6767,30 @@ void MyAvatar::setHeadLookAt(const glm::vec3& lookAtTarget) { _scriptHeadControlTimer = 0.0f; _lookAtScriptTarget = lookAtTarget; } + +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 7b63e8e86b..6b1344aad2 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 @@ -2653,6 +2663,8 @@ private: bool _shouldTurnToFaceCamera { false }; bool _scriptControlsHeadLookAt { false }; float _scriptHeadControlTimer { 0.0f }; + bool _pointAtActive { false }; + bool _isPointTargetValid { true }; Setting::Handle _realWorldFieldOfView; Setting::Handle _useAdvancedMovementControls; @@ -2681,6 +2693,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/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/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/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/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/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/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;