From 9e88a6ed8d6b11abfcf975eb68dbc5736fe03989 Mon Sep 17 00:00:00 2001 From: Liv Date: Fri, 25 Aug 2017 17:48:35 -0700 Subject: [PATCH 01/50] Initial attempt at changing first run behavior --- interface/src/Application.cpp | 50 +++-------------------------------- 1 file changed, 3 insertions(+), 47 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dce95e6f8f..e8bf8ca260 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2812,26 +2812,18 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { handControllerType = Oculus; } - // Check tutorial content versioning - bool hasTutorialContent = contentVersion >= MIN_CONTENT_VERSION.at(handControllerType); - // Check HMD use (may be technically available without being in use) bool hasHMD = PluginUtils::isHMDAvailable(); bool isUsingHMD = _displayPlugin->isHmd(); bool isUsingHMDAndHandControllers = hasHMD && hasHandControllers && isUsingHMD; - Setting::Handle tutorialComplete{ "tutorialComplete", false }; Setting::Handle firstRun{ Settings::firstRun, true }; const QString HIFI_SKIP_TUTORIAL_COMMAND_LINE_KEY = "--skipTutorial"; // Skips tutorial/help behavior, and does NOT clear firstRun setting. bool skipTutorial = arguments().contains(HIFI_SKIP_TUTORIAL_COMMAND_LINE_KEY); - bool isTutorialComplete = tutorialComplete.get(); - bool shouldGoToTutorial = isUsingHMDAndHandControllers && hasTutorialContent && !isTutorialComplete && !skipTutorial; qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers; - qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent << - ", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial; // when --url in command line, teleport to location const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; @@ -2841,39 +2833,15 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { addressLookupString = arguments().value(urlIndex + 1); } - const QString TUTORIAL_PATH = "/tutorial_begin"; - - static const QString SENT_TO_TUTORIAL = "tutorial"; static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location"; static const QString SENT_TO_ENTRY = "entry"; static const QString SENT_TO_SANDBOX = "sandbox"; QString sentTo; - if (shouldGoToTutorial) { - if (sandboxIsRunning) { - qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(TUTORIAL_PATH); - sentTo = SENT_TO_TUTORIAL; - } else { - qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; - if (firstRun.get()) { - showHelp(); - } - if (addressLookupString.isEmpty()) { - DependencyManager::get()->goToEntry(); - sentTo = SENT_TO_ENTRY; - } else { - DependencyManager::get()->loadSettings(addressLookupString); - sentTo = SENT_TO_PREVIOUS_LOCATION; - } - } - } else { - // If this is a first run we short-circuit the address passed in - if (firstRun.get() && !skipTutorial) { + if (firstRun.get()) { showHelp(); - if (isUsingHMDAndHandControllers) { if (sandboxIsRunning) { qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; DependencyManager::get()->goToLocalSandbox(); @@ -2883,16 +2851,12 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { DependencyManager::get()->goToEntry(); sentTo = SENT_TO_ENTRY; } - } else { - DependencyManager::get()->goToEntry(); - sentTo = SENT_TO_ENTRY; - } + } else { qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); DependencyManager::get()->loadSettings(addressLookupString); sentTo = SENT_TO_PREVIOUS_LOCATION; } - } UserActivityLogger::getInstance().logAction("startup_sent_to", { { "sent_to", sentTo }, @@ -2901,18 +2865,10 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { { "has_hand_controllers", hasHandControllers }, { "is_using_hmd", isUsingHMD }, { "is_using_hmd_and_hand_controllers", isUsingHMDAndHandControllers }, - { "content_version", contentVersion }, - { "is_tutorial_complete", isTutorialComplete }, - { "has_tutorial_content", hasTutorialContent }, - { "should_go_to_tutorial", shouldGoToTutorial } + { "content_version", contentVersion } }); _connectionMonitor.init(); - - // After all of the constructor is completed, then set firstRun to false. - if (!skipTutorial) { - firstRun.set(false); - } } bool Application::importJSONFromURL(const QString& urlString) { From 7ee2ac736efff731da57859c6dd61e3040cea321 Mon Sep 17 00:00:00 2001 From: Liv Date: Tue, 29 Aug 2017 14:55:59 -0700 Subject: [PATCH 02/50] Remove unused skipTutorial code --- interface/src/Application.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e8bf8ca260..80752ae7df 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2819,10 +2819,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { Setting::Handle firstRun{ Settings::firstRun, true }; - const QString HIFI_SKIP_TUTORIAL_COMMAND_LINE_KEY = "--skipTutorial"; - // Skips tutorial/help behavior, and does NOT clear firstRun setting. - bool skipTutorial = arguments().contains(HIFI_SKIP_TUTORIAL_COMMAND_LINE_KEY); - qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers; // when --url in command line, teleport to location From 758fc5388234af4a409ef935d29822327543a046 Mon Sep 17 00:00:00 2001 From: Liv Date: Wed, 30 Aug 2017 09:23:33 -0700 Subject: [PATCH 03/50] Remove unneeded handcontrollertype code from handle first run checks --- interface/src/Application.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 80752ae7df..3fafd8a58b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2803,13 +2803,8 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { // Get controller availability bool hasHandControllers = false; - HandControllerType handControllerType = Vive; - if (PluginUtils::isViveControllerAvailable()) { + if (PluginUtils::isViveControllerAvailable() || PluginUtils::isOculusTouchControllerAvailable()) { hasHandControllers = true; - handControllerType = Vive; - } else if (PluginUtils::isOculusTouchControllerAvailable()) { - hasHandControllers = true; - handControllerType = Oculus; } // Check HMD use (may be technically available without being in use) From 930d59c53e642b8c33b4ec81e8edcc80a4b9d16c Mon Sep 17 00:00:00 2001 From: Liv Date: Tue, 5 Sep 2017 17:50:25 -0700 Subject: [PATCH 04/50] add back first run to false and fix formatting --- interface/src/Application.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3fafd8a58b..748dc287ef 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2833,15 +2833,16 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { // If this is a first run we short-circuit the address passed in if (firstRun.get()) { showHelp(); - if (sandboxIsRunning) { - qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(); - sentTo = SENT_TO_SANDBOX; - } else { - qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; - DependencyManager::get()->goToEntry(); - sentTo = SENT_TO_ENTRY; - } + if (sandboxIsRunning) { + qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; + DependencyManager::get()->goToLocalSandbox(); + sentTo = SENT_TO_SANDBOX; + } else { + qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; + DependencyManager::get()->goToEntry(); + sentTo = SENT_TO_ENTRY; + } + firstRun.set(false); } else { qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); From 832e4aa5ebb1c007a1a9543d13671fc032148081 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 8 Sep 2017 09:51:10 +0200 Subject: [PATCH 05/50] Introducong tablet audio events --- .../resources/qml/hifi/tablet/TabletRoot.qml | 3 ++- interface/resources/sounds/Button01.wav | Bin 0 -> 53646 bytes interface/resources/sounds/Button02.wav | Bin 0 -> 111602 bytes interface/resources/sounds/Button03.wav | Bin 0 -> 80394 bytes interface/resources/sounds/Button04.wav | Bin 0 -> 22432 bytes interface/resources/sounds/Button05.wav | Bin 0 -> 31348 bytes interface/resources/sounds/Button06.wav | Bin 0 -> 26898 bytes interface/resources/sounds/Button07.wav | Bin 0 -> 26898 bytes interface/resources/sounds/Collapse.wav | Bin 0 -> 156186 bytes interface/resources/sounds/Expand.wav | Bin 0 -> 89310 bytes interface/resources/sounds/Tab01.wav | Bin 0 -> 31348 bytes interface/resources/sounds/Tab02.wav | Bin 0 -> 80390 bytes interface/resources/sounds/Tab03.wav | Bin 0 -> 67018 bytes .../ui/src/ui/TabletScriptingInterface.cpp | 17 +++++++++++++++++ libraries/ui/src/ui/TabletScriptingInterface.h | 8 +++++++- 15 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 interface/resources/sounds/Button01.wav create mode 100644 interface/resources/sounds/Button02.wav create mode 100644 interface/resources/sounds/Button03.wav create mode 100644 interface/resources/sounds/Button04.wav create mode 100644 interface/resources/sounds/Button05.wav create mode 100644 interface/resources/sounds/Button06.wav create mode 100644 interface/resources/sounds/Button07.wav create mode 100644 interface/resources/sounds/Collapse.wav create mode 100644 interface/resources/sounds/Expand.wav create mode 100644 interface/resources/sounds/Tab01.wav create mode 100644 interface/resources/sounds/Tab02.wav create mode 100644 interface/resources/sounds/Tab03.wav diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 5084377273..36ea16d882 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -1,6 +1,7 @@ import QtQuick 2.0 import Hifi 1.0 import QtQuick.Controls 1.4 + import "../../dialogs" import "../../controls" @@ -145,7 +146,7 @@ Item { SoundEffect { id: buttonClickSound volume: 0.1 - source: "../../../sounds/Gamemaster-Audio-button-click.wav" + source: "" } function playButtonClickSound() { diff --git a/interface/resources/sounds/Button01.wav b/interface/resources/sounds/Button01.wav new file mode 100644 index 0000000000000000000000000000000000000000..e1c8111c6f21ef0ef009ed245bff6ea3fedbb148 GIT binary patch literal 53646 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 literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/Button02.wav b/interface/resources/sounds/Button02.wav new file mode 100644 index 0000000000000000000000000000000000000000..f4bac99e38d6dda8906c094b9d02dddcef2bd862 GIT binary patch literal 111602 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 IyipbZ54QRGo&W#< literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/Button03.wav b/interface/resources/sounds/Button03.wav new file mode 100644 index 0000000000000000000000000000000000000000..1eb28b7daf5fb54d62ba9704918bd7f25e57e073 GIT binary patch literal 80394 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 literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/Button04.wav b/interface/resources/sounds/Button04.wav new file mode 100644 index 0000000000000000000000000000000000000000..cd76d0174cf7bfd5589dc26ba7f58793778a5456 GIT binary patch literal 22432 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 V%C#f9hyK$KF6-l-WzU}2zX7KrVm1H( literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/Button06.wav b/interface/resources/sounds/Button06.wav new file mode 100644 index 0000000000000000000000000000000000000000..30a097ce456369195b2791ebe83fe4d20d0a0c57 GIT binary patch literal 26898 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 literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/Button07.wav b/interface/resources/sounds/Button07.wav new file mode 100644 index 0000000000000000000000000000000000000000..5d285ffa07052e264fd5083edbdc26907f84e3f5 GIT binary patch literal 26898 zcmeHP3vg6d8UF9x%>$^krBD?gEK8?==4S71Hv8&kF=Vr3HLpq1LMhX5ckkU~+kGUv z8xl~*P9IYdN6T2IC_42?P?_p5OvjF>)Bwe?I<~b{Y8hLUQf)0&D#>Q|-rqTA_a--+ zg^;RaTkm%m?%s3G|NZ~@|NlJp1kx1@1QtdCHa6bU9EoR4O8|fY7Vi2n0KCqE5v~CM zQOF1fn?Z*T9XfRA(4j+z4jnpl=+L3#Z2_iOt^_y-luHx>Ek9c<1~7n8Q^-59hk$V9 zF^`itiCN6z1Ww>Mj?*W3&tZ;gBYlZnB~(H+RKo&IA@5aCMKuterM!fj6v9Px5uK{7 z#10};Y9q0i)RCf&<4T_DxClSdBcn`ps=CQLsV6q7dMlt}$~#1#YQHK=Y$oz*A@#IV zkwf*#!eSx*AoddwS>n$(@lE_Y{vF@IH|X;MUZC~F7Si`BxC*X=>)^fcUU)yeAFhY% z;TpJxaua=||LgcVeZB|Y11-=3kH8}kU;+%oGVC$-7~5y)GaNGiz)$v#SE1tu1_$t1NXYdR@j*sJK@H4m+m*Su@sO*+^%XjDR&iCee zbKXhsq&aKOu9;Xf(Ldfl{@2uWg?WlHCk5-LVovl1uIbJbdan5+oc+zmvaELv`9%YUa z^O^0i9ah0AI@W*SKkzkt4gZLL#AA32zk*-ETk%%RDp@71q?Hr$33)s}p1(SOb$)qn zd9G%%X7cvz?b#C(Cnn+(@rf-HTPD6S@r{WM*$vsQ$*#%cx#PL7%U_p&fxo~?wvt_J zTx|Si#WyQPD@Q9=RIRA`aMg#a#wy1uKU(?G%0xw?;;Y858jl)|8eU;vVOOv#*g-Mm|+d9ffcZV&ZVE?PpPfHQGTPWR#q$ha=*MUzc2q}?#WzL zt}3@>a?9lD?CI=7*@v?Gviq_Zvlp}9oBZD7qq#?OOXMZ;A?1*gf)uP`SFw8ydku$; zhmChs+)?q9il0G zOl|!je2~VSfaKrr@%Q*c{2>nE5Z-_{;1T7B(x@~lKazhWua;NKd-Hqq%ksc z#`}$NW87#mnv4ex2Ms2J$*_~%$zEVCF#SwF^9H;D55NO7he^JZwSmN&HDt4&p)F1-n3C1jfXg*dy!_w#HCnNE(ucdkps&;)b}vZm=6p zv!~e%n<44Qeu4P{vyxfK9EGD0hdA5_H^O;5kH_&iejC4yyKond;0Q)BikopW1~7m% zSc8jj5x%Irs63)Pq6{fRid%6jvMkHT<>T^Rd9S=f-XRalgYv!dz4BrCuxwO}3Ym8# zw;#j@(E$#49-fCGW{8<&CK-v9STavuU|(QgW?yE>9QzjgEtdFvF}s*O%p9h1&qE%* z3*UvUu$9guvR?cee~pjgqd1DA_%-|*?!i6ydHg(n8b6JF*oQ6Hf-7+)zNx&aJgz*h zd{OzLvQ^niZC;_QP_9$1Q{Jb%PvI3_xlOrE`Lgn5<(zU(X~HJTc{N-O!!Qh|;1qBS z$B=pQ9p*dC6U-CLbIfxLS$oEqG3EesfVr2sm${X>mH7zs5oQ7=;5ZzI2jM}GKmr%I z=vqf|;7NQEe}F&0FX5N)llVytV;EbpmB#-=_#v#qD!iavP|hf4lxLM^m0u~pQhurY zQu(d&TZQBYgAC2z!7)}ULrqIV6JDbXKr9_pmwch zRx=-9KEPBn)ig(+hNs~W9HKHwNJ2Zbg9R*f{*%3ttWQti6Ldc9!+m%^-j5l~po9`` z#Etj~`~)^)Bf8OzcC@1nZRkJ;HedsO96yd*a0~7K06sv+{1iS#*S;mN1Z-e~4(Nb8 z;10M8?t&le#^y2R;j*g-^k!AO}v_-A|upTRL4!^3!(&M&gQ?#A8tIs6>nhxg$j=L5pK6qeF;um)XnuK-LAaZ<2lQFZdT~`-}J@zJM>_DLjSG;dA(`rVzWH$LH}hp2nB( zC43oQru!UO_sAYh_EB;^ApB&nCc3VLYiT^m-b==@43^O+i2>Pv7sF!OZxJk_CmwRn zA?F44TvB@8Ap1EfM3-8~y9zq6IiU6>Q28$P)S?_Mu}>{;1zLUr)s9lU6>b&tv9DCG zYLEKVLgm--&V@FfD&ERy)Ep~)&IMJcnqTFfrI?FTMX4?1eZDBwtzy1B?_{WB*5aK! zp8o~@6S=5kCQ5z!Zba!AOW)@MGJmz}t9pJaeQNt?`L(*$yCQAfe4&*s_gRMy9XfRA z(4j+z4jnpl=+L1I&|pJq2vD%t-wB!c$7=WRR*rXicpgI1NM>{4KNS66qftzTBvawd8?))ZJ`~zdm7t1i`I4bb_d&=ntR)V{`TO8mY&|$4j*5TnycH~ zTrI&X;%W){yq2Ql);DkJ@eyzKc60?d1lt>XI=XywrM}lvA~1s>*wNnWCo!0w0W$@r zI?NIyUiPWeWwsD)rb6UXZ&!1mxvQCYv$bO*89A}4OiMY5mf(h#ONh`WuchD_!buX% zq(g^}cORg)vu)G0%jwQDAcXIfGNzE!7fm#n&;R&mW>ZvbFmJ4HKq#MzQXCyulmokEBBpyqoJtGZfft(yXiL{4QTg)cfC39zkxv;KoYU?yL zB~y~AzShBot#;nzbk|xPyuIF9Lk<>B3tw;H9UO1<@Gg(ds`}jQTWB(wyeTo_>GB7P z>QZ8)!Q7w840tS-;o;%h;kw#ns?TC|yWJMvX0h40lo;XC+Y=dKgiEAX6-ZPbzmyKA zq63*|GGQXTLMS5|gPp;TCE-XykYs5^dE-6x$Xe2&xd5g+vABhk6FXe)G z!3F;92_YU0)AM3k4lz8XcVH+Lqrnox7AYphr9>uevDR8GQ!yf8qJH-T6X}eQ2une~ zZzMiY8;y#dP@QNK>mm{tafWQ16t-JAw>@O%tZva|t#gLLA&1RtnI%3Q+9I_-86G0K zgMMEmBDw_0CUW+OL*(2JSA=u$f{lwfB_}!J@VvcFrPPScqU=aT`=SXUrs+eh7Nep^ z5G1$5>Z;>x4k^M3PP>&0h4~N{aYU>_DB_j`w_8jOe6Es?xyFsq5Db^H{(G=Dupn9LGiTG%FuUdxQSX``l46pBuVO|aLyMc(b; z_=wHPNj6u63k#t-&gK$q1>ZV(l~*HnY3|{EDWN%H6~D&S6~D%*RJ7NnNz%zkW>`o` zjeSxgGv~R_S!*?|*%(cT$>H=`Rl`h*OREa^3yD5S^jTCvwW3tSYavTfEIPZPQ2)P( zp0nQNiijaQXN%ZvT%F766P#p%OKz111EiGsx0d6%6N-OhT> zEs0J}bVVYCC7K*w3dU%}%CROdbAA!)1h=bR1XxTXrXOMF&f!^MJ&@jOE{WRl8}+gbEsFx zbV%BsGdcWi9&sooOnupy`v69)_qTbr39%u`XXo7xs$r^Tn!b2Y_P2S`VId~@tQNc7 zO78y(1cl1!?rObij<%^Fk&)s9Npe4=ebo>Ys)Zpjn(P+h12L(K#He`bSibRc(&QNG zvpRx_jH*#XO&iNgH}wmNgcPGax-(KrO3skiHqRnRXM|LyCmNT?rS_H1<)S;HaVd~Y z#f6N|=5{;%spNo4r0UmnnbtfVCenNh4J*&zr2f&$78<8T)wUT{YugK!O*MDZJfIh| zvqTGR8tzhA8qx0P4#`)z)4g>2ED|C1Z}@ax9+~OPw-OH3F&dk-0}{iYOf(bI)~R2i z8e`&z*HT75u_;nWyp6&(iF`CQ zl1WH=7h8%kDUfPzIA}wmw#y&zE%aKZ?rv0>|1PGJ<-Z7B$-JPuYhKXZH81Gyniq6; c%?rA_=7r0-YoT|Y`qLjNAvTzY&Fg&dFFe$&E&u=k literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/Collapse.wav b/interface/resources/sounds/Collapse.wav new file mode 100644 index 0000000000000000000000000000000000000000..7a169d506c3a03d4df9e458338f6f2a5edbf333d GIT binary patch literal 156186 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 literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/Tab02.wav b/interface/resources/sounds/Tab02.wav new file mode 100644 index 0000000000000000000000000000000000000000..04ad9b909f441bf6e98ddcf123dd62267f436590 GIT binary patch literal 80390 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 ZIFz{t7F^{1*-x}3GBR~m>P3b0e*j4u2oV4P literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/Tab03.wav b/interface/resources/sounds/Tab03.wav new file mode 100644 index 0000000000000000000000000000000000000000..db7545a365e33007580e08dc3f173b3a9a524296 GIT binary patch literal 67018 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 D@<6#Z literal 0 HcmV?d00001 diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index adff219e0f..6901666da0 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -22,12 +22,22 @@ #include "../InfoView.h" #include "ToolbarScriptingInterface.h" #include "Logging.h" +#include "SoundCache.h" + +#include "SettingHandle.h" // FIXME move to global app properties const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; +Setting::Handle tabletSoundsButtonClick("TabletSounds/buttonClick", "../../../sounds/Button06.wav"); +Setting::Handle tabletSoundsButtonHover("TabletSounds/buttonHover", "../../../sounds/Button04.wav"); +Setting::Handle tabletSoundsTabletOpen("TabletSounds/tabletOpen", "../../../sounds/Button07.wav"); +Setting::Handle tabletSoundsTabletHandsIn("TabletSounds/tabletHandsIn", "../../../sounds/Tab01.wav"); +Setting::Handle tabletSoundsTabletHandsOut("TabletSounds/tabletHandsOut", "../../../sounds/Tab02.wav"); + TabletScriptingInterface::TabletScriptingInterface() { + } TabletScriptingInterface::~TabletScriptingInterface() { @@ -61,6 +71,13 @@ TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) { return tabletProxy; } +void TabletScriptingInterface::playSound(QString soundId) +{ + QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); + SharedSoundPointer _snapshotSound = DependencyManager::get()-> + getSound(QUrl::fromLocalFile(inf.absoluteFilePath())); +} + void TabletScriptingInterface::setToolbarMode(bool toolbarMode) { Q_ASSERT(QThread::currentThread() == qApp->thread()); _toolbarMode = toolbarMode; diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 822bae839e..78c616b412 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -34,6 +34,10 @@ class TabletButtonProxy; class QmlWindowClass; class OffscreenQmlSurface; +namespace Tablet +{ + enum AudioEvents { ButtonClick, ButtonHover, TabletOpen, TabletHandsIn, TabletHandsOut }; +} /**jsdoc * @namespace Tablet */ @@ -41,7 +45,7 @@ class TabletScriptingInterface : public QObject, public Dependency { Q_OBJECT public: TabletScriptingInterface(); - ~TabletScriptingInterface(); + virtual ~TabletScriptingInterface(); void setToolbarScriptingInterface(ToolbarScriptingInterface* toolbarScriptingInterface) { _toolbarScriptingInterface = toolbarScriptingInterface; } @@ -53,6 +57,7 @@ public: */ Q_INVOKABLE TabletProxy* getTablet(const QString& tabletId); + Q_INVOKABLE void playSound(QString soundId); void setToolbarMode(bool toolbarMode); void setQmlTabletRoot(QString tabletId, OffscreenQmlSurface* offscreenQmlSurface); @@ -308,6 +313,7 @@ protected: }; Q_DECLARE_METATYPE(TabletButtonProxy*); +Q_DECLARE_METATYPE(Tablet::AudioEvents); /**jsdoc * @typedef TabletButtonProxy.ButtonProperties From 7c1827d67232932afeab9230fa445e3ab98b7a40 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 14 Sep 2017 20:21:11 +0200 Subject: [PATCH 06/50] use AduioEvent instead of string --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 3 +-- libraries/ui/src/ui/TabletScriptingInterface.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 6901666da0..80ea2f61f7 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -71,8 +71,7 @@ TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) { return tabletProxy; } -void TabletScriptingInterface::playSound(QString soundId) -{ +void TabletScriptingInterface::playSound(Tablet::AudioEvents aevent) { QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); SharedSoundPointer _snapshotSound = DependencyManager::get()-> getSound(QUrl::fromLocalFile(inf.absoluteFilePath())); diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 78c616b412..ce54a5586d 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -57,7 +57,7 @@ public: */ Q_INVOKABLE TabletProxy* getTablet(const QString& tabletId); - Q_INVOKABLE void playSound(QString soundId); + Q_INVOKABLE void playSound(Tablet::AudioEvents aevent); void setToolbarMode(bool toolbarMode); void setQmlTabletRoot(QString tabletId, OffscreenQmlSurface* offscreenQmlSurface); From e26118254fb5e70fe305c196215fe26e48c9aa0e Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 18 Sep 2017 19:40:06 +0200 Subject: [PATCH 07/50] Implemented Audio injector in Tablet scripting interface. Added missed sound events --- .../resources/qml/controls-uit/CheckBox.qml | 11 ++++ .../qml/controls-uit/RadioButton.qml | 11 ++++ .../qml/hifi/tablet/TabletButton.qml | 5 +- .../qml/hifi/tablet/TabletMenuView.qml | 10 +++- .../resources/qml/hifi/tablet/TabletRoot.qml | 1 + interface/src/Application.cpp | 3 ++ interface/src/ui/overlays/Web3DOverlay.cpp | 19 +++++++ .../ui/src/ui/TabletScriptingInterface.cpp | 50 +++++++++++++------ .../ui/src/ui/TabletScriptingInterface.h | 19 ++++--- 9 files changed, 105 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index b279b7ca8d..6eaa9f8923 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -14,6 +14,8 @@ import QtQuick.Controls.Styles 1.4 import "../styles-uit" +import TabletScriptingInterface 1.0 + Original.CheckBox { id: checkBox @@ -28,6 +30,15 @@ Original.CheckBox { readonly property int checkRadius: 2 activeFocusOnPress: true + onClicked: { + tabletInterface.playSound(TabletEnums.ButtonClick) + } + +// TODO: doesnt works for QQC1. check with QQC2 +// onHovered: { +// tabletInterface.playSound(TabletEnums.ButtonHover) +// } + style: CheckBoxStyle { indicator: Rectangle { id: box diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml index ab11ec68b1..1b36641418 100644 --- a/interface/resources/qml/controls-uit/RadioButton.qml +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -15,6 +15,8 @@ import QtQuick.Controls.Styles 1.4 import "../styles-uit" import "../controls-uit" as HifiControls +import TabletScriptingInterface 1.0 + Original.RadioButton { id: radioButton HifiConstants { id: hifi } @@ -27,6 +29,15 @@ Original.RadioButton { readonly property int checkSize: 10 readonly property int checkRadius: 2 + onClicked: { + tabletInterface.playSound(TabletEnums.ButtonClick) + } + +// TODO: doesnt works for QQC1. check with QQC2 +// onHovered: { +// tabletInterface.playSound(TabletEnums.ButtonHover) +// } + style: RadioButtonStyle { indicator: Rectangle { id: box diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 58091d9fab..ce99eff8a3 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -1,5 +1,6 @@ import QtQuick 2.0 import QtGraphicalEffects 1.0 +import TabletScriptingInterface 1.0 Item { id: tabletButton @@ -130,11 +131,13 @@ Item { } tabletButton.clicked(); if (tabletRoot) { - tabletRoot.playButtonClickSound(); + tabletInterface.playSound(TabletEnums.ButtonClick) } } onEntered: { tabletButton.isEntered = true; + tabletInterface.playSound(TabletEnums.ButtonHover) + if (tabletButton.isActive) { tabletButton.state = "hover active state"; } else { diff --git a/interface/resources/qml/hifi/tablet/TabletMenuView.qml b/interface/resources/qml/hifi/tablet/TabletMenuView.qml index 92e7f59524..cdfa501bd3 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuView.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuView.qml @@ -11,9 +11,11 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 +import TabletScriptingInterface 1.0 import "../../styles-uit" import "." + FocusScope { id: root implicitHeight: background.height @@ -74,10 +76,14 @@ FocusScope { MouseArea { anchors.fill: parent hoverEnabled: true - onEntered: listView.currentIndex = index + onEntered: { + tabletInterface.playSound(TabletEnums.ButtonHover) + listView.currentIndex = index + } + onClicked: { + tabletInterface.playSound(TabletEnums.ButtonClick) root.selected(item) - tabletRoot.playButtonClickSound(); } } } diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 36ea16d882..f36f3912ec 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -1,6 +1,7 @@ import QtQuick 2.0 import Hifi 1.0 import QtQuick.Controls 1.4 +import TabletScriptingInterface 1.0 import "../../dialogs" import "../../controls" diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8a2cd5b4c5..40503fa4a4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1828,6 +1828,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _rayPickManager.setPrecisionPicking(rayPickID, value); }); + // Preload Tablet sounds + DependencyManager::get()->preloadSounds(); + qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID()); } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index eb8451e5c3..97996fd59c 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -167,6 +167,10 @@ void Web3DOverlay::buildWebSurface() { if (!self) { return; } + auto tabletScreenID = DependencyManager::get()->getCurrentTabletScreenID(); + if (overlayID == tabletScreenID) { //play only on Tablet border crossing + DependencyManager::get()->playSound(TabletScriptingInterface::TabletHandsOut); + } if (overlayID == selfOverlayID && (self->_pressed || (!self->_activeTouchPoints.empty() && self->_touchBeginAccepted))) { PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), event.getButton(), event.getButtons(), event.getKeyboardModifiers()); @@ -174,6 +178,19 @@ void Web3DOverlay::buildWebSurface() { } }); + QObject::connect(overlays, &Overlays::hoverEnterOverlay, this, [=](OverlayID overlayID, const PointerEvent& event) { + Q_UNUSED(event) + auto self = weakSelf.lock(); + if (!self) { + return; + } + + auto tabletScreenID = DependencyManager::get()->getCurrentTabletScreenID(); + if (overlayID == tabletScreenID) { //play only on Tablet border crossing + DependencyManager::get()->playSound(TabletScriptingInterface::TabletHandsIn); + } + }); + QObject::connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); } @@ -245,6 +262,8 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get()->getMyAvatar().get()); _webSurface->getSurfaceContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Tablet", DependencyManager::get().data()); + // Tablet inteference with Tablet.qml. Need to avoid this in QML space + _webSurface->getSurfaceContext()->setContextProperty("tabletInterface", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Assets", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get().data()); diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 367f58bc0f..358e3c3ec6 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -22,7 +22,8 @@ #include "../InfoView.h" #include "ToolbarScriptingInterface.h" #include "Logging.h" -#include "SoundCache.h" + +#include #include "SettingHandle.h" @@ -30,17 +31,18 @@ const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; -Setting::Handle tabletSoundsButtonClick("TabletSounds/buttonClick", "../../../sounds/Button06.wav"); -Setting::Handle tabletSoundsButtonHover("TabletSounds/buttonHover", "../../../sounds/Button04.wav"); -Setting::Handle tabletSoundsTabletOpen("TabletSounds/tabletOpen", "../../../sounds/Button07.wav"); -Setting::Handle tabletSoundsTabletHandsIn("TabletSounds/tabletHandsIn", "../../../sounds/Tab01.wav"); -Setting::Handle tabletSoundsTabletHandsOut("TabletSounds/tabletHandsOut", "../../../sounds/Tab02.wav"); +static Setting::Handle tabletSoundsButtonClick("TabletSounds", QStringList { "/sounds/Button06.wav", + "/sounds/Button04.wav", + "/sounds/Button07.wav", + "/sounds/Tab01.wav", + "/sounds/Tab02.wav" }); TabletScriptingInterface::TabletScriptingInterface() { - + qmlRegisterType("TabletScriptingInterface", 1, 0, "TabletEnums"); } TabletScriptingInterface::~TabletScriptingInterface() { + tabletSoundsButtonClick.set(tabletSoundsButtonClick.get()); } ToolbarProxy* TabletScriptingInterface::getSystemToolbarProxy() { @@ -71,10 +73,27 @@ TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) { return tabletProxy; } -void TabletScriptingInterface::playSound(Tablet::AudioEvents aevent) { - QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); - SharedSoundPointer _snapshotSound = DependencyManager::get()-> - getSound(QUrl::fromLocalFile(inf.absoluteFilePath())); +void TabletScriptingInterface::preloadSounds() { + //preload audio events + const QStringList &audioSettings = tabletSoundsButtonClick.get(); + for (int i = 0; i < TabletAudioEvents::Last; i++) { + QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + audioSettings.at(i)); + SharedSoundPointer sound = DependencyManager::get()-> + getSound(QUrl::fromLocalFile(inf.absoluteFilePath())); + _audioEvents.insert(static_cast(i), sound); + } +} + +void TabletScriptingInterface::playSound(TabletAudioEvents aEvent) { + SharedSoundPointer sound = _audioEvents[aEvent]; + if (sound) { + AudioInjectorOptions options; + options.stereo = sound->isStereo(); + options.ambisonic = sound->isAmbisonic(); + options.localOnly = options.localOnly || sound->isAmbisonic(); // force localOnly when Ambisonic + + AudioInjectorPointer injector = AudioInjector::playSoundAndDelete(sound->getByteArray(), options); + } } void TabletScriptingInterface::setToolbarMode(bool toolbarMode) { @@ -337,9 +356,12 @@ void TabletProxy::emitWebEvent(const QVariant& msg) { } void TabletProxy::onTabletShown() { - if (_tabletShown && _showRunningScripts) { - _showRunningScripts = false; - pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + if (_tabletShown) { + static_cast(parent())->playSound(TabletScriptingInterface::TabletOpen); + if (_showRunningScripts) { + _showRunningScripts = false; + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } } } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index c06bd9bf2e..edbbb58dd8 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -23,7 +23,7 @@ #include #include #include - +#include "SoundCache.h" #include class ToolbarProxy; @@ -34,19 +34,22 @@ class TabletButtonProxy; class QmlWindowClass; class OffscreenQmlSurface; -namespace Tablet -{ - enum AudioEvents { ButtonClick, ButtonHover, TabletOpen, TabletHandsIn, TabletHandsOut }; -} /**jsdoc * @namespace Tablet */ + class TabletScriptingInterface : public QObject, public Dependency { Q_OBJECT + + public: + enum TabletAudioEvents { ButtonClick, ButtonHover, TabletOpen, TabletHandsIn, TabletHandsOut, Last}; + Q_ENUM(TabletAudioEvents) + TabletScriptingInterface(); virtual ~TabletScriptingInterface(); + void setToolbarScriptingInterface(ToolbarScriptingInterface* toolbarScriptingInterface) { _toolbarScriptingInterface = toolbarScriptingInterface; } /**jsdoc @@ -57,7 +60,9 @@ public: */ Q_INVOKABLE TabletProxy* getTablet(const QString& tabletId); - Q_INVOKABLE void playSound(Tablet::AudioEvents aevent); + void preloadSounds(); + Q_INVOKABLE void playSound(TabletAudioEvents aEvent); + void setToolbarMode(bool toolbarMode); void setQmlTabletRoot(QString tabletId, OffscreenQmlSurface* offscreenQmlSurface); @@ -81,6 +86,7 @@ private: void processTabletEvents(QObject* object, const QKeyEvent* event); ToolbarProxy* getSystemToolbarProxy(); + QMap _audioEvents; protected: std::map _tabletProxies; ToolbarScriptingInterface* _toolbarScriptingInterface { nullptr }; @@ -313,7 +319,6 @@ protected: }; Q_DECLARE_METATYPE(TabletButtonProxy*); -Q_DECLARE_METATYPE(Tablet::AudioEvents); /**jsdoc * @typedef TabletButtonProxy.ButtonProperties From b5f0c4d940ea38a99b91406056f408e5e324934c Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 25 Sep 2017 16:46:36 +0200 Subject: [PATCH 08/50] Added settings controls sounds --- interface/resources/qml/controls-uit/Button.qml | 11 +++++++++++ interface/resources/qml/controls-uit/GlyphButton.qml | 11 +++++++++++ .../qml/dialogs/preferences/ButtonPreference.qml | 12 +++++++++++- .../qml/dialogs/preferences/CheckBoxPreference.qml | 11 +++++++++++ interface/src/Application.cpp | 2 ++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index 59f8a63238..20174b13c2 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -11,6 +11,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 +import TabletScriptingInterface 1.0 import "../styles-uit" @@ -23,6 +24,16 @@ Original.Button { HifiConstants { id: hifi } + onHoveredChanged: { + if (hovered) { + tabletInterface.playSound(TabletEnums.ButtonHover) + } + } + + onClicked: { + tabletInterface.playSound(TabletEnums.ButtonClick) + } + style: ButtonStyle { background: Rectangle { diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index ac353b5a52..24ef4afdff 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -12,6 +12,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 +import TabletScriptingInterface 1.0 import "../styles-uit" @@ -24,6 +25,16 @@ Original.Button { width: 120 height: 28 + onHoveredChanged: { + if (hovered) { + tabletInterface.playSound(TabletEnums.ButtonHover) + } + } + + onClicked: { + tabletInterface.playSound(TabletEnums.ButtonClick) + } + style: ButtonStyle { background: Rectangle { diff --git a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml index 06332bd1be..bb959f1bee 100644 --- a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml +++ b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml @@ -9,6 +9,7 @@ // import QtQuick 2.5 +import TabletScriptingInterface 1.0 import "../../controls-uit" @@ -22,7 +23,16 @@ Preference { Button { id: button - onClicked: preference.trigger() + onHoveredChanged: { + if (hovered) { + tabletInterface.playSound(TabletEnums.ButtonHover) + } + } + + onClicked: { + preference.trigger() + tabletInterface.playSound(TabletEnums.ButtonClick) + } width: 180 anchors.bottom: parent.bottom } diff --git a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml index f8f992735c..7c336d7829 100644 --- a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml @@ -9,6 +9,7 @@ // import QtQuick 2.5 +import TabletScriptingInterface 1.0 import "../../controls-uit" @@ -38,6 +39,16 @@ Preference { CheckBox { id: checkBox + onHoveredChanged: { + if (hovered) { + tabletInterface.playSound(TabletEnums.ButtonHover) + } + } + + onClicked: { + tabletInterface.playSound(TabletEnums.ButtonClick) + } + anchors { top: spacer.bottom left: parent.left diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ce615a4739..b9289be47c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2310,6 +2310,8 @@ void Application::initializeUi() { surfaceContext->setContextProperty("Account", AccountScriptingInterface::getInstance()); surfaceContext->setContextProperty("Tablet", DependencyManager::get().data()); + // Tablet inteference with Tablet.qml. Need to avoid this in QML space + surfaceContext->setContextProperty("tabletInterface", DependencyManager::get().data()); surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface); surfaceContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); surfaceContext->setContextProperty("FaceTracker", DependencyManager::get().data()); From 1a2d38e31f681a5bf0bf24dde9a1c1be1bb942f2 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 25 Sep 2017 17:11:44 +0200 Subject: [PATCH 09/50] Removed sounds for tablet in and out, added sounds for stylus instead. Added logging for testing semi-pressed laser and keyboard sounds --- interface/resources/qml/controls-uit/Key.qml | 8 ++++++++ .../resources/qml/hifi/tablet/TabletRoot.qml | 3 +-- interface/src/ui/overlays/Web3DOverlay.cpp | 17 ----------------- .../ui/src/ui/TabletScriptingInterface.cpp | 1 + .../controllerModules/tabletStylusInput.js | 3 +++ 5 files changed, 13 insertions(+), 19 deletions(-) diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml index 0c888d1a0a..b6227fd28e 100644 --- a/interface/resources/qml/controls-uit/Key.qml +++ b/interface/resources/qml/controls-uit/Key.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import TabletScriptingInterface 1.0 Item { id: keyItem @@ -32,8 +33,15 @@ Item { } } + onHoveredChanged: { + if (hovered) { + tabletInterface.playSound(TabletEnums.ButtonHover) + } + } + onClicked: { mouse.accepted = true; + tabletInterface.playSound(TabletEnums.ButtonClick) webEntity.synthesizeKeyPress(glyph); webEntity.synthesizeKeyPress(glyph, mirrorText); diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index f36f3912ec..ba9d06eee3 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -1,7 +1,6 @@ import QtQuick 2.0 import Hifi 1.0 import QtQuick.Controls 1.4 -import TabletScriptingInterface 1.0 import "../../dialogs" import "../../controls" @@ -147,7 +146,7 @@ Item { SoundEffect { id: buttonClickSound volume: 0.1 - source: "" + source: "../../../sounds/Gamemaster-Audio-button-click.wav" } function playButtonClickSound() { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index ed86b3c5cf..df34b93ac3 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -167,10 +167,6 @@ void Web3DOverlay::buildWebSurface() { if (!self) { return; } - auto tabletScreenID = DependencyManager::get()->getCurrentTabletScreenID(); - if (overlayID == tabletScreenID) { //play only on Tablet border crossing - DependencyManager::get()->playSound(TabletScriptingInterface::TabletHandsOut); - } if (overlayID == selfOverlayID && (self->_pressed || (!self->_activeTouchPoints.empty() && self->_touchBeginAccepted))) { PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), event.getButton(), event.getButtons(), event.getKeyboardModifiers()); @@ -178,19 +174,6 @@ void Web3DOverlay::buildWebSurface() { } }); - QObject::connect(overlays, &Overlays::hoverEnterOverlay, this, [=](OverlayID overlayID, const PointerEvent& event) { - Q_UNUSED(event) - auto self = weakSelf.lock(); - if (!self) { - return; - } - - auto tabletScreenID = DependencyManager::get()->getCurrentTabletScreenID(); - if (overlayID == tabletScreenID) { //play only on Tablet border crossing - DependencyManager::get()->playSound(TabletScriptingInterface::TabletHandsIn); - } - }); - QObject::connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 358e3c3ec6..d1763a8318 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -93,6 +93,7 @@ void TabletScriptingInterface::playSound(TabletAudioEvents aEvent) { options.localOnly = options.localOnly || sound->isAmbisonic(); // force localOnly when Ambisonic AudioInjectorPointer injector = AudioInjector::playSoundAndDelete(sound->getByteArray(), options); + qDebug() << "playing sound for event" << aEvent; } } diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 9d01ceef65..51ac9f69e6 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -157,6 +157,8 @@ Script.include("/~/system/libraries/controllers.js"); return; } + tabletInterface.playSound(3);//HandsIn + var X_ROT_NEG_90 = { x: -0.70710678, y: 0, z: 0, w: 0.70710678 }; var modelOrientation = Quat.multiply(this.stylusTip.orientation, X_ROT_NEG_90); var modelPositionOffset = Vec3.multiplyQbyV(modelOrientation, { x: 0, y: 0, z: MyAvatar.sensorToWorldScale * -WEB_STYLUS_LENGTH / 2 }); @@ -184,6 +186,7 @@ Script.include("/~/system/libraries/controllers.js"); if (!this.stylus) { return; } + tabletInterface.playSound(4);//HandsOut Overlays.deleteOverlay(this.stylus); this.stylus = null; }; From b190fac4f17668ff93f651cc7aa8a2a903b1f284 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 25 Sep 2017 19:45:51 +0200 Subject: [PATCH 10/50] Fix stylus --- interface/src/Application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b9289be47c..55f4f5f46b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6008,6 +6008,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue, wrapperFromScriptValue); qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue, wrapperFromScriptValue); + // Tablet inteference with Tablet.qml. Need to avoid this in QML space + scriptEngine->registerGlobalObject("tabletInterface", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Tablet", DependencyManager::get().data()); auto toolbarScriptingInterface = DependencyManager::get().data(); From 083b52abafb2cf4b7ac06d5a97ded2995ce0f677 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 25 Sep 2017 21:15:05 +0200 Subject: [PATCH 11/50] CHeck containsMouse property for mouse area instead on hovered --- interface/resources/qml/controls-uit/Key.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml index b6227fd28e..26a78b08f5 100644 --- a/interface/resources/qml/controls-uit/Key.qml +++ b/interface/resources/qml/controls-uit/Key.qml @@ -33,8 +33,8 @@ Item { } } - onHoveredChanged: { - if (hovered) { + onContainsMouseChanged: { + if (containsMouse) { tabletInterface.playSound(TabletEnums.ButtonHover) } } From 074362b22868c622f5b27943f07385d2cdb04cce Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 25 Sep 2017 23:10:26 +0200 Subject: [PATCH 12/50] Remove logging --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index d1763a8318..358e3c3ec6 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -93,7 +93,6 @@ void TabletScriptingInterface::playSound(TabletAudioEvents aEvent) { options.localOnly = options.localOnly || sound->isAmbisonic(); // force localOnly when Ambisonic AudioInjectorPointer injector = AudioInjector::playSoundAndDelete(sound->getByteArray(), options); - qDebug() << "playing sound for event" << aEvent; } } From f1d5df59263daeb3b0e08817ecf4f89e4dd09dd4 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 12 Oct 2017 18:26:50 +0200 Subject: [PATCH 13/50] Restore lost-in-merge context property --- interface/src/ui/overlays/Web3DOverlay.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 2d154f30a2..363c85b395 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -247,6 +247,9 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); + // Tablet inteference with Tablet.qml. Need to avoid this in QML space + _webSurface->getSurfaceContext()->setContextProperty("tabletInterface", DependencyManager::get().data()); + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); // mark the TabletProxy object as cpp ownership. QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"); From b965049651b96d35ef5da15987c833c30c7909b5 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 12 Oct 2017 18:39:26 +0200 Subject: [PATCH 14/50] Do not produce sound and highlight on disabled items and menu separators --- interface/resources/qml/hifi/tablet/TabletMenuView.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/tablet/TabletMenuView.qml b/interface/resources/qml/hifi/tablet/TabletMenuView.qml index a8914b9763..f76d896cf2 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuView.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuView.qml @@ -73,6 +73,7 @@ FocusScope { onImplicitWidthChanged: listView !== null ? listView.recalcSize() : 0 MouseArea { + enabled: name !== "" && item.enabled anchors.fill: parent hoverEnabled: true onEntered: { From da734b628c73404c733ec275ef35819f4d48fc9e Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 12 Oct 2017 18:54:40 +0200 Subject: [PATCH 15/50] Remove sound on stylus show and hide --- .../system/controllers/controllerModules/tabletStylusInput.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 6111bf6d8e..29fa878cb1 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -157,8 +157,6 @@ Script.include("/~/system/libraries/controllers.js"); return; } - tabletInterface.playSound(3);//HandsIn - var X_ROT_NEG_90 = { x: -0.70710678, y: 0, z: 0, w: 0.70710678 }; var modelOrientation = Quat.multiply(this.stylusTip.orientation, X_ROT_NEG_90); var modelPositionOffset = Vec3.multiplyQbyV(modelOrientation, { x: 0, y: 0, z: MyAvatar.sensorToWorldScale * -WEB_STYLUS_LENGTH / 2 }); @@ -186,7 +184,6 @@ Script.include("/~/system/libraries/controllers.js"); if (!this.stylus) { return; } - tabletInterface.playSound(4);//HandsOut Overlays.deleteOverlay(this.stylus); this.stylus = null; }; From 35b146b59a0c402a0293b159cfd6ddcae87feb4c Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 12 Oct 2017 19:01:38 +0200 Subject: [PATCH 16/50] Added sounds in Audio menu --- .../resources/qml/controls-uit/CheckBoxQQC2.qml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml index 92bad04d01..3f165bfc09 100644 --- a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml +++ b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml @@ -13,6 +13,7 @@ import QtQuick.Controls 2.2 import "../styles-uit" import "../controls-uit" as HiFiControls +import TabletScriptingInterface 1.0 CheckBox { id: checkBox @@ -32,6 +33,17 @@ CheckBox { readonly property int checkSize: Math.max(boxSize - 8, 10) readonly property int checkRadius: isRound ? checkSize / 2 : 2 focusPolicy: Qt.ClickFocus + hoverEnabled: true + + onClicked: { + tabletInterface.playSound(TabletEnums.ButtonClick) + } + + onHoveredChanged: { + if (hovered) { + tabletInterface.playSound(TabletEnums.ButtonHover) + } + } indicator: Rectangle { id: box From 2d010980b000f934db80ef7985f83fad1df2e259 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 12 Oct 2017 19:15:09 +0200 Subject: [PATCH 17/50] Removed special sound for Mute and replace it with hover and click sounds --- interface/resources/qml/hifi/audio/MicBar.qml | 12 +++++++++++- scripts/system/dialTone.js | 6 ------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index e757d7cadf..81903111c0 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -14,6 +14,8 @@ import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 +import TabletScriptingInterface 1.0 + Rectangle { readonly property var level: Audio.inputLevel; @@ -57,8 +59,16 @@ Rectangle { hoverEnabled: true; scrollGestureEnabled: false; - onClicked: { Audio.muted = !Audio.muted; } + onClicked: { + Audio.muted = !Audio.muted; + tabletInterface.playSound(TabletEnums.ButtonClick) + } drag.target: dragTarget; + onContainsMouseChanged: { + if (containsMouse) { + tabletInterface.playSound(TabletEnums.ButtonHover) + } + } } QtObject { diff --git a/scripts/system/dialTone.js b/scripts/system/dialTone.js index 7b693aa2de..7c0a5b250d 100644 --- a/scripts/system/dialTone.js +++ b/scripts/system/dialTone.js @@ -33,10 +33,4 @@ Audio.disconnected.connect(function(){ Audio.playSound(disconnectSound, soundOptions); }); -Audio.mutedChanged.connect(function () { - if (Audio.muted) { - Audio.playSound(micMutedSound, soundOptions); - } -}); - }()); // END LOCAL_SCOPE From e109101accf73420c0bf6aaccdec99f43061906a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 13 Oct 2017 14:55:12 -0700 Subject: [PATCH 18/50] update profiling timers --- interface/src/Application.cpp | 39 +++++++++++-------- .../src/EntityTreeRenderer.cpp | 25 ++++++------ .../src/RenderableEntityItem.cpp | 2 + .../src/RenderableModelEntityItem.cpp | 37 +++++++++++------- libraries/entities/src/EntitySimulation.cpp | 16 ++++++-- libraries/entities/src/EntityTree.cpp | 5 ++- 6 files changed, 76 insertions(+), 48 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e5d5e696a1..9170b1eddd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2383,8 +2383,8 @@ void Application::initializeUi() { } void Application::updateCamera(RenderArgs& renderArgs) { - PROFILE_RANGE(render, "/updateCamera"); - PerformanceTimer perfTimer("CameraUpdates"); + PROFILE_RANGE(render, __FUNCTION__); + PerformanceTimer perfTimer("updateCamera"); glm::vec3 boomOffset; auto myAvatar = getMyAvatar(); @@ -2600,7 +2600,7 @@ void Application::resizeGL() { } void Application::handleSandboxStatus(QNetworkReply* reply) { - PROFILE_RANGE(render, "HandleSandboxStatus"); + PROFILE_RANGE(render, __FUNCTION__); bool sandboxIsRunning = SandboxUtils::readStatus(reply->readAll()); qDebug() << "HandleSandboxStatus" << sandboxIsRunning; @@ -4595,7 +4595,6 @@ void Application::updateDialogs(float deltaTime) const { static bool domainLoadingInProgress = false; void Application::update(float deltaTime) { - PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_renderFrameCount + 1); if (!_physicsEnabled) { @@ -4790,11 +4789,11 @@ void Application::update(float deltaTime) { QSharedPointer avatarManager = DependencyManager::get(); { - PROFILE_RANGE_EX(simulation_physics, "Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PROFILE_RANGE(simulation_physics, "Physics"); PerformanceTimer perfTimer("physics"); if (_physicsEnabled) { { - PROFILE_RANGE_EX(simulation_physics, "UpdateStates", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PROFILE_RANGE(simulation_physics, "PreStep"); PerformanceTimer perfTimer("updateStates)"); static VectorOfMotionStates motionStates; @@ -4828,14 +4827,14 @@ void Application::update(float deltaTime) { }); } { - PROFILE_RANGE_EX(simulation_physics, "StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PROFILE_RANGE(simulation_physics, "Step"); PerformanceTimer perfTimer("stepSimulation"); getEntities()->getTree()->withWriteLock([&] { _physicsEngine->stepSimulation(); }); } { - PROFILE_RANGE_EX(simulation_physics, "HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PROFILE_RANGE(simulation_physics, "PostStep"); PerformanceTimer perfTimer("harvestChanges"); if (_physicsEngine->hasOutgoingChanges()) { // grab the collision events BEFORE handleOutgoingChanges() because at this point @@ -4843,6 +4842,7 @@ void Application::update(float deltaTime) { auto& collisionEvents = _physicsEngine->getCollisionEvents(); getEntities()->getTree()->withWriteLock([&] { + PROFILE_RANGE(simulation_physics, "Harvest"); PerformanceTimer perfTimer("handleOutgoingChanges"); const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); @@ -4855,18 +4855,25 @@ void Application::update(float deltaTime) { if (!_aboutToQuit) { // handleCollisionEvents() AFTER handleOutgoinChanges() - PerformanceTimer perfTimer("entities"); - avatarManager->handleCollisionEvents(collisionEvents); - // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk - // deadlock.) - _entitySimulation->handleCollisionEvents(collisionEvents); + { + PROFILE_RANGE(simulation_physics, "CollisionEvents"); + PerformanceTimer perfTimer("entities"); + avatarManager->handleCollisionEvents(collisionEvents); + // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk + // deadlock.) + _entitySimulation->handleCollisionEvents(collisionEvents); + } + PROFILE_RANGE(simulation_physics, "UpdateEntities"); // NOTE: the getEntities()->update() call below will wait for lock // and will simulate entity motion (the EntityTree has been given an EntitySimulation). getEntities()->update(true); // update the models... } - myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); + { + PROFILE_RANGE(simulation_physics, "MyAvatar"); + myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); + } if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) { @@ -4885,13 +4892,13 @@ void Application::update(float deltaTime) { // AvatarManager update { { + PROFILE_RANGE(simulation, "OtherAvatars"); PerformanceTimer perfTimer("otherAvatars"); - PROFILE_RANGE_EX(simulation, "OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); avatarManager->updateOtherAvatars(deltaTime); } { - PROFILE_RANGE_EX(simulation, "MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PROFILE_RANGE(simulation, "MyAvatar"); PerformanceTimer perfTimer("MyAvatar"); qApp->updateMyAvatarLookAtPosition(); avatarManager->updateMyAvatar(deltaTime); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 85916baf60..4238eb4050 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -160,6 +160,8 @@ void EntityTreeRenderer::shutdown() { } void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction) { + PROFILE_RANGE_EX(simulation_physics, "Add", 0xffff00ff, (uint64_t)_entitiesToAdd.size()); + PerformanceTimer pt("add"); // Clear any expired entities // FIXME should be able to use std::remove_if, but it fails due to some // weird compilation error related to EntityItemID assignment operators @@ -203,6 +205,8 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r } void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction) { + PROFILE_RANGE_EX(simulation_physics, "Change", 0xffff00ff, (uint64_t)_changedEntities.size()); + PerformanceTimer pt("change"); std::unordered_set changedEntities; _changedEntitiesGuard.withWriteLock([&] { #if 0 @@ -223,6 +227,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene } if (!_renderablesToUpdate.empty()) { + PROFILE_RANGE_EX(simulation_physics, "UpdateRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size()); for (const auto& entry : _renderablesToUpdate) { const auto& renderable = entry.second; renderable->updateInScene(scene, transaction); @@ -232,6 +237,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene } void EntityTreeRenderer::update(bool simulate) { + PROFILE_RANGE(simulation_physics, "ETR::update"); PerformanceTimer perfTimer("ETRupdate"); if (_tree && !_shuttingDown) { EntityTreePointer tree = std::static_pointer_cast(_tree); @@ -239,22 +245,14 @@ void EntityTreeRenderer::update(bool simulate) { // Update the rendereable entities as needed { + PROFILE_RANGE(simulation_physics, "Scene"); PerformanceTimer sceneTimer("scene"); auto scene = _viewState->getMain3DScene(); if (scene) { render::Transaction transaction; - { - PerformanceTimer pt("add"); - addPendingEntities(scene, transaction); - } - { - PerformanceTimer pt("change"); - updateChangedEntities(scene, transaction); - } - { - PerformanceTimer pt("enqueue"); - scene->enqueueTransaction(transaction); - } + addPendingEntities(scene, transaction); + updateChangedEntities(scene, transaction); + scene->enqueueTransaction(transaction); } } @@ -336,7 +334,8 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVectorsetRotation(getRotation()); model->setTranslation(getPosition()); { - PerformanceTimer perfTimer("model->simulate"); model->simulate(0.0f); } _needsInitialSimulation = false; @@ -138,6 +138,7 @@ void RenderableModelEntityItem::autoResizeJointArrays() { } bool RenderableModelEntityItem::needsUpdateModelBounds() const { + PROFILE_RANGE(simulation_physics, __FUNCTION__); ModelPointer model = getModel(); if (!hasModel() || !model) { return false; @@ -182,6 +183,7 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const { } void RenderableModelEntityItem::updateModelBounds() { + PROFILE_RANGE(simulation_physics, "updateModelBounds"); if (needsUpdateModelBounds()) { doInitialModelSimulation(); _needsJointSimulation = false; @@ -1124,6 +1126,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin } void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { + PROFILE_RANGE(simulation_physics, __FUNCTION__); if (_hasModel != entity->hasModel()) { _hasModel = entity->hasModel(); } @@ -1213,12 +1216,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce model->setVisibleInScene(_visible, scene); } - //entity->doInitialModelSimulation(); - if (model->needsFixupInScene()) { - model->removeFromScene(scene, transaction); - render::Item::Status::Getters statusGetters; - makeStatusGetters(entity, statusGetters); - model->addToScene(scene, transaction, statusGetters); + { + PROFILE_RANGE(simulation_physics, "Fixup"); + if (model->needsFixupInScene()) { + model->removeFromScene(scene, transaction); + render::Item::Status::Getters statusGetters; + makeStatusGetters(entity, statusGetters); + model->addToScene(scene, transaction, statusGetters); + } } // When the individual mesh parts of a model finish fading, they will mark their Model as needing updating @@ -1227,16 +1232,20 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce model->updateRenderItems(); } - // make a copy of the animation properites - auto newAnimationProperties = entity->getAnimationProperties(); - if (newAnimationProperties != _renderAnimationProperties) { - withWriteLock([&] { - _renderAnimationProperties = newAnimationProperties; - _currentFrame = _renderAnimationProperties.getCurrentFrame(); - }); + { + PROFILE_RANGE(simulation_physics, "CheckAnimation"); + // make a copy of the animation properites + auto newAnimationProperties = entity->getAnimationProperties(); + if (newAnimationProperties != _renderAnimationProperties) { + withWriteLock([&] { + _renderAnimationProperties = newAnimationProperties; + _currentFrame = _renderAnimationProperties.getCurrentFrame(); + }); + } } if (_animating) { + PROFILE_RANGE(simulation_physics, "Animate"); if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); } diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 2e330fdcc5..f91d728d78 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -9,9 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - #include "EntitySimulation.h" + +#include +#include + #include "EntitiesLogging.h" #include "MovingEntitiesOperator.h" @@ -27,6 +29,7 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) { } void EntitySimulation::updateEntities() { + PROFILE_RANGE(simulation_physics, "ES::updateEntities"); QMutexLocker lock(&_mutex); quint64 now = usecTimestampNow(); @@ -35,8 +38,12 @@ void EntitySimulation::updateEntities() { callUpdateOnEntitiesThatNeedIt(now); moveSimpleKinematics(now); updateEntitiesInternal(now); - PerformanceTimer perfTimer("sortingEntities"); - sortEntitiesThatMoved(); + + { + PROFILE_RANGE(simulation_physics, "Sort"); + PerformanceTimer perfTimer("sortingEntities"); + sortEntitiesThatMoved(); + } } void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) { @@ -258,6 +265,7 @@ void EntitySimulation::clearEntities() { } void EntitySimulation::moveSimpleKinematics(const quint64& now) { + PROFILE_RANGE_EX(simulation_physics, "Kinematics", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size()); SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin(); while (itemItr != _simpleKinematicEntities.end()) { EntityItemPointer entity = *itemItr; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index d16aeaa6e1..4c7795dc75 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -15,8 +15,9 @@ #include -#include #include +#include +#include #include "EntitySimulation.h" #include "VariantMapToScriptValue.h" @@ -1370,6 +1371,7 @@ void EntityTree::entityChanged(EntityItemPointer entity) { void EntityTree::fixupNeedsParentFixups() { + PROFILE_RANGE(simulation_physics, "FixupParents"); MovingEntitiesOperator moveOperator; QWriteLocker locker(&_needsParentFixupLock); @@ -1459,6 +1461,7 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) { } void EntityTree::update(bool simulate) { + PROFILE_RANGE(simulation_physics, "ET::update"); fixupNeedsParentFixups(); if (simulate && _simulation) { withWriteLock([&] { From 23bb8608b14739ee776a9b0b8ef172a88475f885 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Oct 2017 11:52:03 -0700 Subject: [PATCH 19/50] minor optimizations in updateModelBounds() --- .../src/RenderableModelEntityItem.cpp | 66 +++++++++++++++---- .../src/RenderableModelEntityItem.h | 1 - libraries/entities/src/ModelEntityItem.cpp | 14 ++-- libraries/entities/src/ModelEntityItem.h | 5 +- 4 files changed, 60 insertions(+), 26 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a0c3cde28f..e2afc6eac2 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -124,10 +124,11 @@ void RenderableModelEntityItem::doInitialModelSimulation() { model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); model->setRotation(getRotation()); model->setTranslation(getPosition()); - { + + if (_needsInitialSimulation) { model->simulate(0.0f); + _needsInitialSimulation = false; } - _needsInitialSimulation = false; } void RenderableModelEntityItem::autoResizeJointArrays() { @@ -179,13 +180,59 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const { } } - return false; + return model->needsReload(); } void RenderableModelEntityItem::updateModelBounds() { PROFILE_RANGE(simulation_physics, "updateModelBounds"); - if (needsUpdateModelBounds()) { - doInitialModelSimulation(); + + if (!_dimensionsInitialized || !hasModel()) { + return; + } + + ModelPointer model = getModel(); + if (!model || !model->isLoaded()) { + return; + } + + /* adebug TODO: figure out if we need to DO anything when isAnimatingSomething() + if (isAnimatingSomething()) { + return true; + } + */ + + if (model->needsReload()) { + model->updateGeometry(); + } + + if (model->getScaleToFitDimensions() != getDimensions() || + model->getRegistrationPoint() != getRegistrationPoint()) { + // The machinery for updateModelBounds will give existing models the opportunity to fix their + // translation/rotation/scale/registration. The first two are straightforward, but the latter two + // have guards to make sure they don't happen after they've already been set. Here we reset those guards. + // This doesn't cause the entity values to change -- it just allows the model to match once it comes in. + model->setScaleToFit(false, getDimensions()); + model->setSnapModelToRegistrationPoint(false, getRegistrationPoint()); + + // now recalculate the bounds and registration + model->setScaleToFit(true, getDimensions()); + model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); + } + + bool success; + auto transform = getTransform(success); + if (success) { + if (model->getTranslation() != transform.getTranslation()) { + model->setTranslation(transform.getTranslation()); + } + if (model->getRotation() != transform.getRotation()) { + model->setRotation(transform.getRotation()); + } + } + + if (_needsInitialSimulation || _needsJointSimulation) { + model->simulate(0.0f); + _needsInitialSimulation = false; _needsJointSimulation = false; } } @@ -899,10 +946,6 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() { }); } -bool RenderableModelEntityItem::isAnimatingSomething() const { - return !getAnimationURL().isEmpty() && getAnimationIsPlaying() && getAnimationFPS() != 0.0f; -} - using namespace render; using namespace render::entities; @@ -1205,9 +1248,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } } - if (entity->needsUpdateModelBounds()) { - entity->updateModelBounds(); - } + entity->updateModelBounds(); if (model->isVisible() != _visible) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in @@ -1215,6 +1256,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // so most of the time we don't do anything in this function. model->setVisibleInScene(_visible, scene); } + // TODO? early exit here when not visible? { PROFILE_RANGE(simulation_physics, "Fixup"); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index d1424316e9..a50ca63382 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -108,7 +108,6 @@ public: private: bool needsUpdateModelBounds() const; - bool isAnimatingSomething() const; void autoResizeJointArrays(); void copyAnimationJointDataToModel(); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 6af4db154a..a5d259ea87 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -557,12 +557,6 @@ void ModelEntityItem::setAnimationLoop(bool loop) { }); } -bool ModelEntityItem::getAnimationLoop() const { - return resultWithReadLock([&] { - return _animationProperties.getLoop(); - }); -} - void ModelEntityItem::setAnimationHold(bool hold) { withWriteLock([&] { _animationProperties.setHold(hold); @@ -610,8 +604,10 @@ float ModelEntityItem::getAnimationCurrentFrame() const { }); } -float ModelEntityItem::getAnimationFPS() const { +bool ModelEntityItem::isAnimatingSomething() const { return resultWithReadLock([&] { - return _animationProperties.getFPS(); - }); + return !_animationProperties.getURL().isEmpty() && + _animationProperties.getRunning() && + (_animationProperties.getFPS() != 0.0f); + }); } diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 7efb493735..2c3ef3aa2d 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -90,7 +90,6 @@ public: bool getAnimationAllowTranslation() const { return _animationProperties.getAllowTranslation(); }; void setAnimationLoop(bool loop); - bool getAnimationLoop() const; void setAnimationHold(bool hold); bool getAnimationHold() const; @@ -101,10 +100,9 @@ public: void setAnimationLastFrame(float lastFrame); float getAnimationLastFrame() const; - bool getAnimationIsPlaying() const; float getAnimationCurrentFrame() const; - float getAnimationFPS() const; + bool isAnimatingSomething() const; static const QString DEFAULT_TEXTURES; const QString getTextures() const; @@ -123,7 +121,6 @@ public: QVector getJointRotationsSet() const; QVector getJointTranslations() const; QVector getJointTranslationsSet() const; - bool isAnimatingSomething() const; private: void setAnimationSettings(const QString& value); // only called for old bitstream format From 3f99e70af0698cd21ecb8d925a3b8821e53e977c Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Mon, 16 Oct 2017 14:20:31 -0700 Subject: [PATCH 20/50] C++ cleanup for pointers / picking --- interface/CMakeLists.txt | 1 + interface/src/raypick/JointRayPick.cpp | 1 - interface/src/raypick/JointRayPick.h | 2 +- interface/src/raypick/LaserPointer.cpp | 126 ++++++-------- interface/src/raypick/LaserPointer.h | 22 +-- interface/src/raypick/LaserPointerManager.cpp | 160 +++++++---------- interface/src/raypick/LaserPointerManager.h | 34 ++-- .../LaserPointerScriptingInterface.cpp | 19 +- .../raypick/LaserPointerScriptingInterface.h | 30 ++-- interface/src/raypick/MouseRayPick.h | 2 +- interface/src/raypick/RayPick.cpp | 62 ------- interface/src/raypick/RayPickManager.cpp | 162 ++++++++---------- interface/src/raypick/RayPickManager.h | 35 ++-- .../src/raypick/RayPickScriptingInterface.cpp | 34 +--- .../src/raypick/RayPickScriptingInterface.h | 29 ++-- libraries/pointers/CMakeLists.txt | 5 + .../pointers/src/pointers/PointerManager.cpp | 1 + .../pointers/src/pointers/PointerManager.h | 29 ++++ .../pointers/src/pointers/rays/RayPick.cpp | 75 ++++++++ .../pointers/src/pointers/rays}/RayPick.h | 103 ++++++----- .../src/pointers/rays}/StaticRayPick.cpp | 3 - .../src/pointers/rays}/StaticRayPick.h | 3 - 22 files changed, 454 insertions(+), 484 deletions(-) delete mode 100644 interface/src/raypick/RayPick.cpp create mode 100644 libraries/pointers/CMakeLists.txt create mode 100644 libraries/pointers/src/pointers/PointerManager.cpp create mode 100644 libraries/pointers/src/pointers/PointerManager.h create mode 100644 libraries/pointers/src/pointers/rays/RayPick.cpp rename {interface/src/raypick => libraries/pointers/src/pointers/rays}/RayPick.h (63%) rename {interface/src/raypick => libraries/pointers/src/pointers/rays}/StaticRayPick.cpp (92%) rename {interface/src/raypick => libraries/pointers/src/pointers/rays}/StaticRayPick.h (92%) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index b16ad58431..764e5ef201 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -198,6 +198,7 @@ endif() # link required hifi libraries link_hifi_libraries( shared octree ktx gpu gl gpu-gl procedural model render + pointers recording fbx networking model-networking entities avatars trackers audio audio-client animation script-engine physics render-utils entities-renderer avatars-renderer ui auto-updater midi diff --git a/interface/src/raypick/JointRayPick.cpp b/interface/src/raypick/JointRayPick.cpp index b4f0dde687..cf3f380ca0 100644 --- a/interface/src/raypick/JointRayPick.cpp +++ b/interface/src/raypick/JointRayPick.cpp @@ -10,7 +10,6 @@ // #include "JointRayPick.h" -#include "DependencyManager.h" #include "avatar/AvatarManager.h" JointRayPick::JointRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, const float maxDistance, const bool enabled) : diff --git a/interface/src/raypick/JointRayPick.h b/interface/src/raypick/JointRayPick.h index 3cd622fbbe..e3e5670e20 100644 --- a/interface/src/raypick/JointRayPick.h +++ b/interface/src/raypick/JointRayPick.h @@ -11,7 +11,7 @@ #ifndef hifi_JointRayPick_h #define hifi_JointRayPick_h -#include "RayPick.h" +#include class JointRayPick : public RayPick { diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 0e0f13cd6c..7fa5eaed5f 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -11,8 +11,8 @@ #include "LaserPointer.h" #include "Application.h" -#include "ui/overlays/Overlay.h" #include "avatar/AvatarManager.h" +#include "RayPickScriptingInterface.h" LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled) : @@ -21,9 +21,10 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende _defaultRenderStates(defaultRenderStates), _faceAvatar(faceAvatar), _centerEndY(centerEndY), - _lockEnd(lockEnd) + _lockEnd(lockEnd), + _rayPickUID(DependencyManager::get()->createRayPick(rayProps)) { - _rayPickUID = DependencyManager::get()->createRayPick(rayProps); + for (auto& state : _renderStates) { if (!enabled || state.first != _currentRenderState) { @@ -38,7 +39,7 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende } LaserPointer::~LaserPointer() { - DependencyManager::get()->removeRayPick(_rayPickUID); + qApp->getRayPickManager().removeRayPick(_rayPickUID); for (auto& renderState : _renderStates) { renderState.second.deleteOverlays(); @@ -49,43 +50,47 @@ LaserPointer::~LaserPointer() { } void LaserPointer::enable() { - QWriteLocker lock(getLock()); - DependencyManager::get()->enableRayPick(_rayPickUID); - _renderingEnabled = true; + qApp->getRayPickManager().enableRayPick(_rayPickUID); + withWriteLock([&] { + _renderingEnabled = true; + }); } void LaserPointer::disable() { - QWriteLocker lock(getLock()); - DependencyManager::get()->disableRayPick(_rayPickUID); + qApp->getRayPickManager().disableRayPick(_rayPickUID); _renderingEnabled = false; - if (!_currentRenderState.empty()) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - disableRenderState(_renderStates[_currentRenderState]); + withWriteLock([&] { + if (!_currentRenderState.empty()) { + if (_renderStates.find(_currentRenderState) != _renderStates.end()) { + disableRenderState(_renderStates[_currentRenderState]); + } + if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { + disableRenderState(_defaultRenderStates[_currentRenderState].second); + } } - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - disableRenderState(_defaultRenderStates[_currentRenderState].second); - } - } + }); } void LaserPointer::setRenderState(const std::string& state) { - QWriteLocker lock(getLock()); - if (!_currentRenderState.empty() && state != _currentRenderState) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - disableRenderState(_renderStates[_currentRenderState]); + withWriteLock([&] { + if (!_currentRenderState.empty() && state != _currentRenderState) { + if (_renderStates.find(_currentRenderState) != _renderStates.end()) { + disableRenderState(_renderStates[_currentRenderState]); + } + if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { + disableRenderState(_defaultRenderStates[_currentRenderState].second); + } } - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - disableRenderState(_defaultRenderStates[_currentRenderState].second); - } - } - _currentRenderState = state; + _currentRenderState = state; + }); } void LaserPointer::editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) { - QWriteLocker lock(getLock()); - updateRenderStateOverlay(_renderStates[state].getStartID(), startProps); - updateRenderStateOverlay(_renderStates[state].getPathID(), pathProps); - updateRenderStateOverlay(_renderStates[state].getEndID(), endProps); + withWriteLock([&] { + updateRenderStateOverlay(_renderStates[state].getStartID(), startProps); + updateRenderStateOverlay(_renderStates[state].getPathID(), pathProps); + updateRenderStateOverlay(_renderStates[state].getEndID(), endProps); + }); } void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) { @@ -97,7 +102,6 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& } const RayPickResult LaserPointer::getPrevRayPickResult() { - QReadLocker lock(getLock()); return DependencyManager::get()->getPrevRayPickResult(_rayPickUID); } @@ -193,65 +197,41 @@ void LaserPointer::disableRenderState(const RenderState& renderState) { void LaserPointer::update() { // This only needs to be a read lock because update won't change any of the properties that can be modified from scripts - QReadLocker lock(getLock()); - RayPickResult prevRayPickResult = DependencyManager::get()->getPrevRayPickResult(_rayPickUID); - if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && + withReadLock([&] { + RayPickResult prevRayPickResult = qApp->getRayPickManager().getPrevRayPickResult(_rayPickUID); + if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && (prevRayPickResult.type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) { - float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult.distance; - updateRenderState(_renderStates[_currentRenderState], prevRayPickResult.type, distance, prevRayPickResult.objectID, prevRayPickResult.searchRay, false); - disableRenderState(_defaultRenderStates[_currentRenderState].second); - } else if (_renderingEnabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - disableRenderState(_renderStates[_currentRenderState]); - updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), prevRayPickResult.searchRay, true); - } else if (!_currentRenderState.empty()) { - disableRenderState(_renderStates[_currentRenderState]); - disableRenderState(_defaultRenderStates[_currentRenderState].second); - } + float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult.distance; + updateRenderState(_renderStates[_currentRenderState], prevRayPickResult.type, distance, prevRayPickResult.objectID, prevRayPickResult.searchRay, false); + disableRenderState(_defaultRenderStates[_currentRenderState].second); + } else if (_renderingEnabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { + disableRenderState(_renderStates[_currentRenderState]); + updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), prevRayPickResult.searchRay, true); + } else if (!_currentRenderState.empty()) { + disableRenderState(_renderStates[_currentRenderState]); + disableRenderState(_defaultRenderStates[_currentRenderState].second); + } + }); } void LaserPointer::setPrecisionPicking(const bool precisionPicking) { - QWriteLocker lock(getLock()); - DependencyManager::get()->setPrecisionPicking(_rayPickUID, precisionPicking); + qApp->getRayPickManager().setPrecisionPicking(_rayPickUID, precisionPicking); } void LaserPointer::setLaserLength(const float laserLength) { - QWriteLocker lock(getLock()); _laserLength = laserLength; } void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) { - QWriteLocker lock(getLock()); _objectLockEnd = std::pair(objectID, isOverlay); } -void LaserPointer::setIgnoreEntities(const QScriptValue& ignoreEntities) { - QWriteLocker lock(getLock()); - DependencyManager::get()->setIgnoreEntities(_rayPickUID, ignoreEntities); +void LaserPointer::setIgnoreItems(const QVector& ignoreItems) const { + qApp->getRayPickManager().setIgnoreItems(_rayPickUID, ignoreItems); } -void LaserPointer::setIncludeEntities(const QScriptValue& includeEntities) { - QWriteLocker lock(getLock()); - DependencyManager::get()->setIncludeEntities(_rayPickUID, includeEntities); -} - -void LaserPointer::setIgnoreOverlays(const QScriptValue& ignoreOverlays) { - QWriteLocker lock(getLock()); - DependencyManager::get()->setIgnoreOverlays(_rayPickUID, ignoreOverlays); -} - -void LaserPointer::setIncludeOverlays(const QScriptValue& includeOverlays) { - QWriteLocker lock(getLock()); - DependencyManager::get()->setIncludeOverlays(_rayPickUID, includeOverlays); -} - -void LaserPointer::setIgnoreAvatars(const QScriptValue& ignoreAvatars) { - QWriteLocker lock(getLock()); - DependencyManager::get()->setIgnoreAvatars(_rayPickUID, ignoreAvatars); -} - -void LaserPointer::setIncludeAvatars(const QScriptValue& includeAvatars) { - QWriteLocker lock(getLock()); - DependencyManager::get()->setIncludeAvatars(_rayPickUID, includeAvatars); +void LaserPointer::setIncludeItems(const QVector& includeItems) const { + qApp->getRayPickManager().setIncludeItems(_rayPickUID, includeItems); } RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, const OverlayID& endID) : diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 01dfe01cfd..20ea824f50 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -12,10 +12,12 @@ #define hifi_LaserPointer_h #include -#include "glm/glm.hpp" +#include #include -#include "raypick/RayPickScriptingInterface.h" +#include + +#include "ui/overlays/Overlay.h" class RayPickResult; @@ -44,9 +46,10 @@ private: }; -class LaserPointer { +class LaserPointer : public ReadWriteLockable { public: + using Pointer = std::shared_ptr; typedef std::unordered_map RenderStateMap; typedef std::unordered_map> DefaultRenderStateMap; @@ -68,14 +71,8 @@ public: void setLaserLength(const float laserLength); void setLockEndUUID(QUuid objectID, const bool isOverlay); - void setIgnoreEntities(const QScriptValue& ignoreEntities); - void setIncludeEntities(const QScriptValue& includeEntities); - void setIgnoreOverlays(const QScriptValue& ignoreOverlays); - void setIncludeOverlays(const QScriptValue& includeOverlays); - void setIgnoreAvatars(const QScriptValue& ignoreAvatars); - void setIncludeAvatars(const QScriptValue& includeAvatars); - - QReadWriteLock* getLock() { return &_lock; } + void setIgnoreItems(const QVector& ignoreItems) const; + void setIncludeItems(const QVector& includeItems) const; void update(); @@ -90,8 +87,7 @@ private: bool _lockEnd; std::pair _objectLockEnd { std::pair(QUuid(), false)}; - QUuid _rayPickUID; - QReadWriteLock _lock; + const QUuid _rayPickUID; void updateRenderStateOverlay(const OverlayID& id, const QVariant& props); void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState); diff --git a/interface/src/raypick/LaserPointerManager.cpp b/interface/src/raypick/LaserPointerManager.cpp index 8615a96c3f..f65a8a1a84 100644 --- a/interface/src/raypick/LaserPointerManager.cpp +++ b/interface/src/raypick/LaserPointerManager.cpp @@ -12,138 +12,110 @@ QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates, const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled) { + QUuid result; std::shared_ptr laserPointer = std::make_shared(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, enabled); if (!laserPointer->getRayUID().isNull()) { - QWriteLocker containsLock(&_containsLock); - QUuid id = QUuid::createUuid(); - _laserPointers[id] = laserPointer; - return id; + result = QUuid::createUuid(); + withWriteLock([&] { _laserPointers[result] = laserPointer; }); } - return QUuid(); + return result; } -void LaserPointerManager::removeLaserPointer(const QUuid uid) { - QWriteLocker lock(&_containsLock); - _laserPointers.remove(uid); + +LaserPointer::Pointer LaserPointerManager::find(const QUuid& uid) const { + return resultWithReadLock([&] { + auto itr = _laserPointers.find(uid); + if (itr != _laserPointers.end()) { + return *itr; + } + return LaserPointer::Pointer(); + }); } -void LaserPointerManager::enableLaserPointer(const QUuid uid) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->enable(); + +void LaserPointerManager::removeLaserPointer(const QUuid& uid) { + withWriteLock([&] { + _laserPointers.remove(uid); + }); +} + +void LaserPointerManager::enableLaserPointer(const QUuid& uid) const { + auto laserPointer = find(uid); + if (laserPointer) { + laserPointer->enable(); } } -void LaserPointerManager::disableLaserPointer(const QUuid uid) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->disable(); +void LaserPointerManager::disableLaserPointer(const QUuid& uid) const { + auto laserPointer = find(uid); + if (laserPointer) { + laserPointer->disable(); } } -void LaserPointerManager::setRenderState(QUuid uid, const std::string& renderState) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setRenderState(renderState); +void LaserPointerManager::setRenderState(const QUuid& uid, const std::string& renderState) const { + auto laserPointer = find(uid); + if (laserPointer) { + laserPointer->setRenderState(renderState); } } -void LaserPointerManager::editRenderState(QUuid uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->editRenderState(state, startProps, pathProps, endProps); +void LaserPointerManager::editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const { + auto laserPointer = find(uid); + if (laserPointer) { + laserPointer->editRenderState(state, startProps, pathProps, endProps); } } -const RayPickResult LaserPointerManager::getPrevRayPickResult(const QUuid uid) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - return laserPointer.value()->getPrevRayPickResult(); +const RayPickResult LaserPointerManager::getPrevRayPickResult(const QUuid& uid) const { + auto laserPointer = find(uid); + if (laserPointer) { + return laserPointer->getPrevRayPickResult(); } return RayPickResult(); } void LaserPointerManager::update() { - QReadLocker lock(&_containsLock); - for (QUuid& uid : _laserPointers.keys()) { - auto laserPointer = _laserPointers.find(uid); - laserPointer.value()->update(); + auto cachedLaserPointers = resultWithReadLock>>([&] { + return _laserPointers.values(); + }); + + for (const auto& laserPointer : cachedLaserPointers) { + laserPointer->update(); } } -void LaserPointerManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setPrecisionPicking(precisionPicking); +void LaserPointerManager::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) const { + auto laserPointer = find(uid); + if (laserPointer) { + laserPointer->setPrecisionPicking(precisionPicking); } } -void LaserPointerManager::setLaserLength(QUuid uid, const float laserLength) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setLaserLength(laserLength); +void LaserPointerManager::setLaserLength(const QUuid& uid, const float laserLength) const { + auto laserPointer = find(uid); + if (laserPointer) { + laserPointer->setLaserLength(laserLength); } } -void LaserPointerManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setIgnoreEntities(ignoreEntities); +void LaserPointerManager::setIgnoreItems(const QUuid& uid, const QVector& ignoreEntities) const { + auto laserPointer = find(uid); + if (laserPointer) { + laserPointer->setIgnoreItems(ignoreEntities); } } -void LaserPointerManager::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setIncludeEntities(includeEntities); +void LaserPointerManager::setIncludeItems(const QUuid& uid, const QVector& includeEntities) const { + auto laserPointer = find(uid); + if (laserPointer) { + laserPointer->setIncludeItems(includeEntities); } } -void LaserPointerManager::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setIgnoreOverlays(ignoreOverlays); - } -} - -void LaserPointerManager::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setIncludeOverlays(includeOverlays); - } -} - -void LaserPointerManager::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setIgnoreAvatars(ignoreAvatars); - } -} - -void LaserPointerManager::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setIncludeAvatars(includeAvatars); - } -} - -void LaserPointerManager::setLockEndUUID(QUuid uid, QUuid objectID, const bool isOverlay) { - QReadLocker lock(&_containsLock); - auto laserPointer = _laserPointers.find(uid); - if (laserPointer != _laserPointers.end()) { - laserPointer.value()->setLockEndUUID(objectID, isOverlay); +void LaserPointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const { + auto laserPointer = find(uid); + if (laserPointer) { + laserPointer->setLockEndUUID(objectID, isOverlay); } } diff --git a/interface/src/raypick/LaserPointerManager.h b/interface/src/raypick/LaserPointerManager.h index b841877578..a4689cf16d 100644 --- a/interface/src/raypick/LaserPointerManager.h +++ b/interface/src/raypick/LaserPointerManager.h @@ -14,39 +14,37 @@ #include #include +#include + #include "LaserPointer.h" class RayPickResult; -class LaserPointerManager { + +class LaserPointerManager : protected ReadWriteLockable { public: QUuid createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates, const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled); - void removeLaserPointer(const QUuid uid); - void enableLaserPointer(const QUuid uid); - void disableLaserPointer(const QUuid uid); - void setRenderState(QUuid uid, const std::string& renderState); - void editRenderState(QUuid uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps); - const RayPickResult getPrevRayPickResult(const QUuid uid); + void removeLaserPointer(const QUuid& uid); + void enableLaserPointer(const QUuid& uid) const; + void disableLaserPointer(const QUuid& uid) const; + void setRenderState(const QUuid& uid, const std::string& renderState) const; + void editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const; + const RayPickResult getPrevRayPickResult(const QUuid& uid) const; - void setPrecisionPicking(QUuid uid, const bool precisionPicking); - void setLaserLength(QUuid uid, const float laserLength); - void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities); - void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities); - void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays); - void setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays); - void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars); - void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars); + void setPrecisionPicking(const QUuid& uid, const bool precisionPicking) const; + void setLaserLength(const QUuid& uid, const float laserLength) const; + void setIgnoreItems(const QUuid& uid, const QVector& ignoreEntities) const; + void setIncludeItems(const QUuid& uid, const QVector& includeEntities) const; - void setLockEndUUID(QUuid uid, QUuid objectID, const bool isOverlay); + void setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const; void update(); private: + LaserPointer::Pointer find(const QUuid& uid) const; QHash> _laserPointers; - QReadWriteLock _containsLock; - }; #endif // hifi_LaserPointerManager_h diff --git a/interface/src/raypick/LaserPointerScriptingInterface.cpp b/interface/src/raypick/LaserPointerScriptingInterface.cpp index d5e435f490..96839160fa 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.cpp +++ b/interface/src/raypick/LaserPointerScriptingInterface.cpp @@ -11,10 +11,19 @@ #include "LaserPointerScriptingInterface.h" -#include -#include "GLMHelpers.h" +#include -QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& properties) { +#include +#include + +void LaserPointerScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) const { + qApp->getLaserPointerManager().setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); +} +void LaserPointerScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) const { + qApp->getLaserPointerManager().setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); +} + +QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& properties) const { QVariantMap propertyMap = properties.toMap(); bool faceAvatar = false; @@ -69,7 +78,7 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, enabled); } -void LaserPointerScriptingInterface::editRenderState(QUuid uid, const QString& renderState, const QVariant& properties) { +void LaserPointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const { QVariantMap propMap = properties.toMap(); QVariant startProps; @@ -90,7 +99,7 @@ void LaserPointerScriptingInterface::editRenderState(QUuid uid, const QString& r qApp->getLaserPointerManager().editRenderState(uid, renderState.toStdString(), startProps, pathProps, endProps); } -const RenderState LaserPointerScriptingInterface::buildRenderState(const QVariantMap& propMap) { +RenderState LaserPointerScriptingInterface::buildRenderState(const QVariantMap& propMap) { QUuid startID; if (propMap["start"].isValid()) { QVariantMap startMap = propMap["start"].toMap(); diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h index 2f6da87b5f..19262e6e5d 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.h +++ b/interface/src/raypick/LaserPointerScriptingInterface.h @@ -22,27 +22,23 @@ class LaserPointerScriptingInterface : public QObject, public Dependency { SINGLETON_DEPENDENCY public slots: - Q_INVOKABLE QUuid createLaserPointer(const QVariant& properties); - Q_INVOKABLE void enableLaserPointer(QUuid uid) { qApp->getLaserPointerManager().enableLaserPointer(uid); } - Q_INVOKABLE void disableLaserPointer(QUuid uid) { qApp->getLaserPointerManager().disableLaserPointer(uid); } - Q_INVOKABLE void removeLaserPointer(QUuid uid) { qApp->getLaserPointerManager().removeLaserPointer(uid); } - Q_INVOKABLE void editRenderState(QUuid uid, const QString& renderState, const QVariant& properties); - Q_INVOKABLE void setRenderState(QUuid uid, const QString& renderState) { qApp->getLaserPointerManager().setRenderState(uid, renderState.toStdString()); } - Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid) { return qApp->getLaserPointerManager().getPrevRayPickResult(uid); } + Q_INVOKABLE QUuid createLaserPointer(const QVariant& properties) const; + Q_INVOKABLE void enableLaserPointer(const QUuid& uid) const { qApp->getLaserPointerManager().enableLaserPointer(uid); } + Q_INVOKABLE void disableLaserPointer(const QUuid& uid) const { qApp->getLaserPointerManager().disableLaserPointer(uid); } + Q_INVOKABLE void removeLaserPointer(const QUuid& uid) const { qApp->getLaserPointerManager().removeLaserPointer(uid); } + Q_INVOKABLE void editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const; + Q_INVOKABLE void setRenderState(const QUuid& uid, const QString& renderState) const { qApp->getLaserPointerManager().setRenderState(uid, renderState.toStdString()); } + Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid) const { return qApp->getLaserPointerManager().getPrevRayPickResult(uid); } - Q_INVOKABLE void setPrecisionPicking(QUuid uid, const bool precisionPicking) { qApp->getLaserPointerManager().setPrecisionPicking(uid, precisionPicking); } - Q_INVOKABLE void setLaserLength(QUuid uid, const float laserLength) { qApp->getLaserPointerManager().setLaserLength(uid, laserLength); } - Q_INVOKABLE void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { qApp->getLaserPointerManager().setIgnoreEntities(uid, ignoreEntities); } - Q_INVOKABLE void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { qApp->getLaserPointerManager().setIncludeEntities(uid, includeEntities); } - Q_INVOKABLE void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { qApp->getLaserPointerManager().setIgnoreOverlays(uid, ignoreOverlays); } - Q_INVOKABLE void setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) { qApp->getLaserPointerManager().setIncludeOverlays(uid, includeOverlays); } - Q_INVOKABLE void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) { qApp->getLaserPointerManager().setIgnoreAvatars(uid, ignoreAvatars); } - Q_INVOKABLE void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) { qApp->getLaserPointerManager().setIncludeAvatars(uid, includeAvatars); } + Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { qApp->getLaserPointerManager().setPrecisionPicking(uid, precisionPicking); } + Q_INVOKABLE void setLaserLength(const QUuid& uid, float laserLength) const { qApp->getLaserPointerManager().setLaserLength(uid, laserLength); } + Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities) const; + Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities) const; - Q_INVOKABLE void setLockEndUUID(QUuid uid, QUuid objectID, const bool isOverlay) { qApp->getLaserPointerManager().setLockEndUUID(uid, objectID, isOverlay); } + Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { qApp->getLaserPointerManager().setLockEndUUID(uid, objectID, isOverlay); } private: - const RenderState buildRenderState(const QVariantMap& propMap); + static RenderState buildRenderState(const QVariantMap& propMap); }; diff --git a/interface/src/raypick/MouseRayPick.h b/interface/src/raypick/MouseRayPick.h index 848a5de336..47f9404f3a 100644 --- a/interface/src/raypick/MouseRayPick.h +++ b/interface/src/raypick/MouseRayPick.h @@ -11,7 +11,7 @@ #ifndef hifi_MouseRayPick_h #define hifi_MouseRayPick_h -#include "RayPick.h" +#include class MouseRayPick : public RayPick { diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp deleted file mode 100644 index a5b1299210..0000000000 --- a/interface/src/raypick/RayPick.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// -// RayPick.cpp -// interface/src/raypick -// -// Created by Sam Gondelman 7/11/2017 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#include "RayPick.h" - -RayPick::RayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled) : - _filter(filter), - _maxDistance(maxDistance), - _enabled(enabled) -{ -} -void RayPick::enable() { - QWriteLocker lock(getLock()); - _enabled = true; -} - -void RayPick::disable() { - QWriteLocker lock(getLock()); - _enabled = false; -} - -const RayPickResult& RayPick::getPrevRayPickResult() { - QReadLocker lock(getLock()); - return _prevResult; -} - -void RayPick::setIgnoreEntities(const QScriptValue& ignoreEntities) { - QWriteLocker lock(getLock()); - _ignoreEntities = qVectorEntityItemIDFromScriptValue(ignoreEntities); -} - -void RayPick::setIncludeEntities(const QScriptValue& includeEntities) { - QWriteLocker lock(getLock()); - _includeEntities = qVectorEntityItemIDFromScriptValue(includeEntities); -} - -void RayPick::setIgnoreOverlays(const QScriptValue& ignoreOverlays) { - QWriteLocker lock(getLock()); - _ignoreOverlays = qVectorOverlayIDFromScriptValue(ignoreOverlays); -} - -void RayPick::setIncludeOverlays(const QScriptValue& includeOverlays) { - QWriteLocker lock(getLock()); - _includeOverlays = qVectorOverlayIDFromScriptValue(includeOverlays); -} - -void RayPick::setIgnoreAvatars(const QScriptValue& ignoreAvatars) { - QWriteLocker lock(getLock()); - _ignoreAvatars = qVectorEntityItemIDFromScriptValue(ignoreAvatars); -} - -void RayPick::setIncludeAvatars(const QScriptValue& includeAvatars) { - QWriteLocker lock(getLock()); - _includeAvatars = qVectorEntityItemIDFromScriptValue(includeAvatars); -} \ No newline at end of file diff --git a/interface/src/raypick/RayPickManager.cpp b/interface/src/raypick/RayPickManager.cpp index 1728ecd01a..f3425a2b28 100644 --- a/interface/src/raypick/RayPickManager.cpp +++ b/interface/src/raypick/RayPickManager.cpp @@ -10,6 +10,8 @@ // #include "RayPickManager.h" +#include + #include "Application.h" #include "EntityScriptingInterface.h" #include "ui/overlays/Overlays.h" @@ -18,7 +20,6 @@ #include "DependencyManager.h" #include "JointRayPick.h" -#include "StaticRayPick.h" #include "MouseRayPick.h" bool RayPickManager::checkAndCompareCachedResults(QPair& ray, RayPickCache& cache, RayPickResult& res, const RayPickFilter::Flags& mask) { @@ -43,20 +44,26 @@ void RayPickManager::cacheResult(const bool intersects, const RayPickResult& res } void RayPickManager::update() { - QReadLocker lock(&_containsLock); RayPickCache results; - for (auto& uid : _rayPicks.keys()) { - std::shared_ptr rayPick = _rayPicks[uid]; - QWriteLocker lock(rayPick->getLock()); + QHash cachedRayPicks; + withReadLock([&] { + cachedRayPicks = _rayPicks; + }); + + for (const auto& uid : cachedRayPicks.keys()) { + std::shared_ptr rayPick = cachedRayPicks[uid]; if (!rayPick->isEnabled() || rayPick->getFilter().doesPickNothing() || rayPick->getMaxDistance() < 0.0f) { continue; } - bool valid; - PickRay ray = rayPick->getPickRay(valid); + PickRay ray; - if (!valid) { - continue; + { + bool valid; + ray = rayPick->getPickRay(valid); + if (!valid) { + continue; + } } QPair rayKey = QPair(ray.origin, ray.direction); @@ -69,8 +76,8 @@ void RayPickManager::update() { bool nonCollidable = rayPick->getFilter().doesPickNonCollidable(); RayPickFilter::Flags entityMask = rayPick->getFilter().getEntityFlags(); if (!checkAndCompareCachedResults(rayKey, results, res, entityMask)) { - entityRes = DependencyManager::get()->findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCourse(), - rayPick->getIncludeEntites(), rayPick->getIgnoreEntites(), !invisible, !nonCollidable); + entityRes = DependencyManager::get()->findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCoarse(), + rayPick->getIncludeItemsAs(), rayPick->getIgnoreItemsAs(), !invisible, !nonCollidable); fromCache = false; } @@ -87,8 +94,8 @@ void RayPickManager::update() { bool nonCollidable = rayPick->getFilter().doesPickNonCollidable(); RayPickFilter::Flags overlayMask = rayPick->getFilter().getOverlayFlags(); if (!checkAndCompareCachedResults(rayKey, results, res, overlayMask)) { - overlayRes = qApp->getOverlays().findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCourse(), - rayPick->getIncludeOverlays(), rayPick->getIgnoreOverlays(), !invisible, !nonCollidable); + overlayRes = qApp->getOverlays().findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCoarse(), + rayPick->getIncludeItemsAs(), rayPick->getIgnoreItemsAs(), !invisible, !nonCollidable); fromCache = false; } @@ -101,7 +108,8 @@ void RayPickManager::update() { if (rayPick->getFilter().doesPickAvatars()) { RayPickFilter::Flags avatarMask = rayPick->getFilter().getAvatarFlags(); if (!checkAndCompareCachedResults(rayKey, results, res, avatarMask)) { - RayToAvatarIntersectionResult avatarRes = DependencyManager::get()->findRayIntersectionVector(ray, rayPick->getIncludeAvatars(), rayPick->getIgnoreAvatars()); + RayToAvatarIntersectionResult avatarRes = DependencyManager::get()->findRayIntersectionVector(ray, + rayPick->getIncludeItemsAs(), rayPick->getIgnoreItemsAs()); cacheResult(avatarRes.intersects, RayPickResult(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, ray), avatarMask, res, rayKey, results); } } @@ -123,109 +131,87 @@ void RayPickManager::update() { } } -QUuid RayPickManager::createRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, const float maxDistance, const bool enabled) { - QWriteLocker lock(&_containsLock); +QUuid RayPickManager::createRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, float maxDistance, bool enabled) { + auto newRayPick = std::make_shared(jointName, posOffset, dirOffset, filter, maxDistance, enabled); QUuid id = QUuid::createUuid(); - _rayPicks[id] = std::make_shared(jointName, posOffset, dirOffset, filter, maxDistance, enabled); + withWriteLock([&] { + _rayPicks[id] = newRayPick; + }); return id; } -QUuid RayPickManager::createRayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled) { - QWriteLocker lock(&_containsLock); +QUuid RayPickManager::createRayPick(const RayPickFilter& filter, float maxDistance, bool enabled) { QUuid id = QUuid::createUuid(); - _rayPicks[id] = std::make_shared(filter, maxDistance, enabled); + auto newRayPick = std::make_shared(filter, maxDistance, enabled); + withWriteLock([&] { + _rayPicks[id] = newRayPick; + }); return id; } -QUuid RayPickManager::createRayPick(const glm::vec3& position, const glm::vec3& direction, const RayPickFilter& filter, const float maxDistance, const bool enabled) { - QWriteLocker lock(&_containsLock); +QUuid RayPickManager::createRayPick(const glm::vec3& position, const glm::vec3& direction, const RayPickFilter& filter, float maxDistance, bool enabled) { QUuid id = QUuid::createUuid(); - _rayPicks[id] = std::make_shared(position, direction, filter, maxDistance, enabled); + auto newRayPick = std::make_shared(position, direction, filter, maxDistance, enabled); + withWriteLock([&] { + _rayPicks[id] = newRayPick; + }); return id; } -void RayPickManager::removeRayPick(const QUuid uid) { - QWriteLocker lock(&_containsLock); - _rayPicks.remove(uid); +void RayPickManager::removeRayPick(const QUuid& uid) { + withWriteLock([&] { + _rayPicks.remove(uid); + }); } -void RayPickManager::enableRayPick(const QUuid uid) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - rayPick.value()->enable(); +RayPick::Pointer RayPickManager::findRayPick(const QUuid& uid) const { + return resultWithReadLock([&] { + if (_rayPicks.contains(uid)) { + return _rayPicks[uid]; + } + return RayPick::Pointer(); + }); +} + +void RayPickManager::enableRayPick(const QUuid& uid) const { + auto rayPick = findRayPick(uid); + if (rayPick) { + rayPick->enable(); } } -void RayPickManager::disableRayPick(const QUuid uid) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - rayPick.value()->disable(); +void RayPickManager::disableRayPick(const QUuid& uid) const { + auto rayPick = findRayPick(uid); + if (rayPick) { + rayPick->disable(); } } -const RayPickResult RayPickManager::getPrevRayPickResult(const QUuid uid) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - return rayPick.value()->getPrevRayPickResult(); +RayPickResult RayPickManager::getPrevRayPickResult(const QUuid& uid) const { + auto rayPick = findRayPick(uid); + if (rayPick) { + return rayPick->getPrevRayPickResult(); } return RayPickResult(); } -void RayPickManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - rayPick.value()->setPrecisionPicking(precisionPicking); +void RayPickManager::setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { + auto rayPick = findRayPick(uid); + if (rayPick) { + rayPick->setPrecisionPicking(precisionPicking); } } -void RayPickManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - rayPick.value()->setIgnoreEntities(ignoreEntities); +void RayPickManager::setIgnoreItems(const QUuid& uid, const QVector& ignore) const { + auto rayPick = findRayPick(uid); + if (rayPick) { + rayPick->setIgnoreItems(ignore); } } -void RayPickManager::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - rayPick.value()->setIncludeEntities(includeEntities); +void RayPickManager::setIncludeItems(const QUuid& uid, const QVector& include) const { + auto rayPick = findRayPick(uid); + if (rayPick) { + rayPick->setIncludeItems(include); } } - -void RayPickManager::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - rayPick.value()->setIgnoreOverlays(ignoreOverlays); - } -} - -void RayPickManager::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - rayPick.value()->setIncludeOverlays(includeOverlays); - } -} - -void RayPickManager::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - rayPick.value()->setIgnoreAvatars(ignoreAvatars); - } -} - -void RayPickManager::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) { - QReadLocker containsLock(&_containsLock); - auto rayPick = _rayPicks.find(uid); - if (rayPick != _rayPicks.end()) { - rayPick.value()->setIncludeAvatars(includeAvatars); - } -} \ No newline at end of file diff --git a/interface/src/raypick/RayPickManager.h b/interface/src/raypick/RayPickManager.h index 974022eb4d..8c6c28ceab 100644 --- a/interface/src/raypick/RayPickManager.h +++ b/interface/src/raypick/RayPickManager.h @@ -11,19 +11,20 @@ #ifndef hifi_RayPickManager_h #define hifi_RayPickManager_h -#include "RayPick.h" #include -#include - -#include "RegisteredMetaTypes.h" - #include #include +#include + +#include +#include + + class RayPickResult; -class RayPickManager { +class RayPickManager : protected ReadWriteLockable { public: void update(); @@ -31,22 +32,18 @@ public: QUuid createRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const RayPickFilter& filter, const float maxDistance, const bool enabled); QUuid createRayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled); QUuid createRayPick(const glm::vec3& position, const glm::vec3& direction, const RayPickFilter& filter, const float maxDistance, const bool enabled); - void removeRayPick(const QUuid uid); - void enableRayPick(const QUuid uid); - void disableRayPick(const QUuid uid); - const RayPickResult getPrevRayPickResult(const QUuid uid); + void removeRayPick(const QUuid& uid); + void enableRayPick(const QUuid& uid) const; + void disableRayPick(const QUuid& uid) const; + RayPickResult getPrevRayPickResult(const QUuid& uid) const; - void setPrecisionPicking(QUuid uid, const bool precisionPicking); - void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities); - void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities); - void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays); - void setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays); - void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars); - void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars); + void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const; + void setIgnoreItems(const QUuid& uid, const QVector& ignore) const; + void setIncludeItems(const QUuid& uid, const QVector& include) const; private: - QHash> _rayPicks; - QReadWriteLock _containsLock; + RayPick::Pointer findRayPick(const QUuid& uid) const; + QHash _rayPicks; typedef QHash, std::unordered_map> RayPickCache; diff --git a/interface/src/raypick/RayPickScriptingInterface.cpp b/interface/src/raypick/RayPickScriptingInterface.cpp index cb2b3e4471..621ae9b738 100644 --- a/interface/src/raypick/RayPickScriptingInterface.cpp +++ b/interface/src/raypick/RayPickScriptingInterface.cpp @@ -66,46 +66,30 @@ QUuid RayPickScriptingInterface::createRayPick(const QVariant& properties) { return QUuid(); } -void RayPickScriptingInterface::enableRayPick(QUuid uid) { +void RayPickScriptingInterface::enableRayPick(const QUuid& uid) { qApp->getRayPickManager().enableRayPick(uid); } -void RayPickScriptingInterface::disableRayPick(QUuid uid) { +void RayPickScriptingInterface::disableRayPick(const QUuid& uid) { qApp->getRayPickManager().disableRayPick(uid); } -void RayPickScriptingInterface::removeRayPick(QUuid uid) { +void RayPickScriptingInterface::removeRayPick(const QUuid& uid) { qApp->getRayPickManager().removeRayPick(uid); } -RayPickResult RayPickScriptingInterface::getPrevRayPickResult(QUuid uid) { +RayPickResult RayPickScriptingInterface::getPrevRayPickResult(const QUuid& uid) { return qApp->getRayPickManager().getPrevRayPickResult(uid); } -void RayPickScriptingInterface::setPrecisionPicking(QUuid uid, const bool precisionPicking) { +void RayPickScriptingInterface::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) { qApp->getRayPickManager().setPrecisionPicking(uid, precisionPicking); } -void RayPickScriptingInterface::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { - qApp->getRayPickManager().setIgnoreEntities(uid, ignoreEntities); +void RayPickScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) { + qApp->getRayPickManager().setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); } -void RayPickScriptingInterface::setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { - qApp->getRayPickManager().setIncludeEntities(uid, includeEntities); -} - -void RayPickScriptingInterface::setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { - qApp->getRayPickManager().setIgnoreOverlays(uid, ignoreOverlays); -} - -void RayPickScriptingInterface::setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays) { - qApp->getRayPickManager().setIncludeOverlays(uid, includeOverlays); -} - -void RayPickScriptingInterface::setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars) { - qApp->getRayPickManager().setIgnoreAvatars(uid, ignoreAvatars); -} - -void RayPickScriptingInterface::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) { - qApp->getRayPickManager().setIncludeAvatars(uid, includeAvatars); +void RayPickScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) { + qApp->getRayPickManager().setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index f7ed2e6fa6..59c5804d72 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -13,10 +13,9 @@ #include -#include "RegisteredMetaTypes.h" -#include "DependencyManager.h" - -#include "RayPick.h" +#include +#include +#include class RayPickScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -38,25 +37,21 @@ class RayPickScriptingInterface : public QObject, public Dependency { public slots: Q_INVOKABLE QUuid createRayPick(const QVariant& properties); - Q_INVOKABLE void enableRayPick(QUuid uid); - Q_INVOKABLE void disableRayPick(QUuid uid); - Q_INVOKABLE void removeRayPick(QUuid uid); - Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid); + Q_INVOKABLE void enableRayPick(const QUuid& uid); + Q_INVOKABLE void disableRayPick(const QUuid& uid); + Q_INVOKABLE void removeRayPick(const QUuid& uid); + Q_INVOKABLE RayPickResult getPrevRayPickResult(const QUuid& uid); - Q_INVOKABLE void setPrecisionPicking(QUuid uid, const bool precisionPicking); - Q_INVOKABLE void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities); - Q_INVOKABLE void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities); - Q_INVOKABLE void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays); - Q_INVOKABLE void setIncludeOverlays(QUuid uid, const QScriptValue& includeOverlays); - Q_INVOKABLE void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars); - Q_INVOKABLE void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars); + Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, const bool precisionPicking); + Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities); + Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities); - unsigned int PICK_NOTHING() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_NOTHING); } + unsigned int PICK_NOTHING() { return 0; } unsigned int PICK_ENTITIES() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_ENTITIES); } unsigned int PICK_OVERLAYS() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_OVERLAYS); } unsigned int PICK_AVATARS() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_AVATARS); } unsigned int PICK_HUD() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_HUD); } - unsigned int PICK_COURSE() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_COURSE); } + unsigned int PICK_COARSE() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_COARSE); } unsigned int PICK_INCLUDE_INVISIBLE() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_INCLUDE_INVISIBLE); } unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_INCLUDE_NONCOLLIDABLE); } unsigned int PICK_ALL_INTERSECTIONS() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_ALL_INTERSECTIONS); } diff --git a/libraries/pointers/CMakeLists.txt b/libraries/pointers/CMakeLists.txt new file mode 100644 index 0000000000..504484574c --- /dev/null +++ b/libraries/pointers/CMakeLists.txt @@ -0,0 +1,5 @@ +set(TARGET_NAME pointers) +setup_hifi_library() +GroupSources(src) +link_hifi_libraries(shared) + diff --git a/libraries/pointers/src/pointers/PointerManager.cpp b/libraries/pointers/src/pointers/PointerManager.cpp new file mode 100644 index 0000000000..dddc84279b --- /dev/null +++ b/libraries/pointers/src/pointers/PointerManager.cpp @@ -0,0 +1 @@ +#include "PointerManager.h" diff --git a/libraries/pointers/src/pointers/PointerManager.h b/libraries/pointers/src/pointers/PointerManager.h new file mode 100644 index 0000000000..0a045997c2 --- /dev/null +++ b/libraries/pointers/src/pointers/PointerManager.h @@ -0,0 +1,29 @@ +// +// Created by Bradley Austin Davis on 2017/10/16 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_pointers_PointerManager_h +#define hifi_pointers_PointerManager_h + +#include +#include + +class PointerManager : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY +public: + +signals: + void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent); + void triggerContinue(const QUuid& id, const PointerEvent& pointerEvent); + void triggerEnd(const QUuid& id, const PointerEvent& pointerEvent); + + void hoverEnter(const QUuid& id, const PointerEvent& pointerEvent); + void hoverOver(const QUuid& id, const PointerEvent& pointerEvent); + void hoverLeave(const QUuid& id, const PointerEvent& pointerEvent); +}; + +#endif // hifi_RayPick_h diff --git a/libraries/pointers/src/pointers/rays/RayPick.cpp b/libraries/pointers/src/pointers/rays/RayPick.cpp new file mode 100644 index 0000000000..36756cdb79 --- /dev/null +++ b/libraries/pointers/src/pointers/rays/RayPick.cpp @@ -0,0 +1,75 @@ +// +// Created by Sam Gondelman 7/11/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "RayPick.h" + +const RayPickFilter RayPickFilter::NOTHING; + +RayPick::RayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled) : + _filter(filter), + _maxDistance(maxDistance), + _enabled(enabled) +{ +} + +void RayPick::enable(bool enabled) { + withWriteLock([&] { + _enabled = enabled; + }); +} + +RayPickFilter RayPick::getFilter() const { + return resultWithReadLock([&] { + return _filter; + }); +} + +float RayPick::getMaxDistance() const { + return _maxDistance; +} + +bool RayPick::isEnabled() const { + return _enabled; +} + +void RayPick::setPrecisionPicking(bool precisionPicking) { + withWriteLock([&]{ + _filter.setFlag(RayPickFilter::PICK_COARSE, !precisionPicking); + }); +} + +void RayPick::setRayPickResult(const RayPickResult& rayPickResult) { + withWriteLock([&] { + _prevResult = rayPickResult; + }); +} + +QVector RayPick::getIgnoreItems() const { + return _ignoreItems; +} + +QVector RayPick::getIncludeItems() const { + return _includeItems; +} + +RayPickResult RayPick::getPrevRayPickResult() const { + return resultWithReadLock([&] { + return _prevResult; + }); +} + +void RayPick::setIgnoreItems(const QVector& ignoreItems) { + withWriteLock([&] { + _ignoreItems = ignoreItems; + }); +} + +void RayPick::setIncludeItems(const QVector& includeItems) { + withWriteLock([&] { + _includeItems = includeItems; + }); +} diff --git a/interface/src/raypick/RayPick.h b/libraries/pointers/src/pointers/rays/RayPick.h similarity index 63% rename from interface/src/raypick/RayPick.h rename to libraries/pointers/src/pointers/rays/RayPick.h index 6dacc084b4..3691ac9985 100644 --- a/interface/src/raypick/RayPick.h +++ b/libraries/pointers/src/pointers/rays/RayPick.h @@ -1,7 +1,4 @@ // -// RayPick.h -// interface/src/raypick -// // Created by Sam Gondelman 7/11/2017 // Copyright 2017 High Fidelity, Inc. // @@ -12,22 +9,22 @@ #define hifi_RayPick_h #include -#include "RegisteredMetaTypes.h" +#include -#include "EntityItemID.h" -#include "ui/overlays/Overlay.h" -#include +#include + +#include +#include class RayPickFilter { public: enum FlagBit { - PICK_NOTHING = 0, - PICK_ENTITIES, + PICK_ENTITIES = 0, PICK_OVERLAYS, PICK_AVATARS, PICK_HUD, - PICK_COURSE, // if not set, does precise intersection, otherwise, doesn't + PICK_COARSE, // if not set, does precise intersection, otherwise, doesn't PICK_INCLUDE_INVISIBLE, // if not set, will not intersect invisible elements, otherwise, intersects both visible and invisible elements PICK_INCLUDE_NONCOLLIDABLE, // if not set, will not intersect noncollidable elements, otherwise, intersects both collidable and noncollidable elements @@ -42,7 +39,7 @@ public: // The key is the Flags Flags _flags; - RayPickFilter() : _flags(getBitMask(PICK_NOTHING)) {} + RayPickFilter() {} RayPickFilter(const Flags& flags) : _flags(flags) {} bool operator== (const RayPickFilter& rhs) const { return _flags == rhs._flags; } @@ -50,13 +47,13 @@ public: void setFlag(FlagBit flag, bool value) { _flags[flag] = value; } - bool doesPickNothing() const { return _flags[PICK_NOTHING]; } + bool doesPickNothing() const { return _flags == NOTHING._flags; } bool doesPickEntities() const { return _flags[PICK_ENTITIES]; } bool doesPickOverlays() const { return _flags[PICK_OVERLAYS]; } bool doesPickAvatars() const { return _flags[PICK_AVATARS]; } bool doesPickHUD() const { return _flags[PICK_HUD]; } - bool doesPickCourse() const { return _flags[PICK_COURSE]; } + bool doesPickCoarse() const { return _flags[PICK_COARSE]; } bool doesPickInvisible() const { return _flags[PICK_INCLUDE_INVISIBLE]; } bool doesPickNonCollidable() const { return _flags[PICK_INCLUDE_NONCOLLIDABLE]; } @@ -71,8 +68,8 @@ public: if (doesPickNonCollidable()) { toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE); } - if (doesPickCourse()) { - toReturn |= getBitMask(PICK_COURSE); + if (doesPickCoarse()) { + toReturn |= getBitMask(PICK_COARSE); } return Flags(toReturn); } @@ -84,66 +81,84 @@ public: if (doesPickNonCollidable()) { toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE); } - if (doesPickCourse()) { - toReturn |= getBitMask(PICK_COURSE); + if (doesPickCoarse()) { + toReturn |= getBitMask(PICK_COARSE); } return Flags(toReturn); } Flags getAvatarFlags() const { return Flags(getBitMask(PICK_AVATARS)); } Flags getHUDFlags() const { return Flags(getBitMask(PICK_HUD)); } - static unsigned int getBitMask(FlagBit bit) { return 1 << bit; } + static constexpr unsigned int getBitMask(FlagBit bit) { return 1 << bit; } + static const RayPickFilter NOTHING; }; -class RayPick { +class RayPick : protected ReadWriteLockable { public: + using Pointer = std::shared_ptr; + RayPick(const RayPickFilter& filter, const float maxDistance, const bool enabled); virtual const PickRay getPickRay(bool& valid) const = 0; - void enable(); - void disable(); + void enable(bool enabled = true); + void disable() { enable(false); } - const RayPickFilter& getFilter() { return _filter; } - float getMaxDistance() { return _maxDistance; } - bool isEnabled() { return _enabled; } - const RayPickResult& getPrevRayPickResult(); + RayPickFilter getFilter() const; + float getMaxDistance() const; + bool isEnabled() const; + RayPickResult getPrevRayPickResult() const; - void setPrecisionPicking(bool precisionPicking) { _filter.setFlag(RayPickFilter::PICK_COURSE, !precisionPicking); } + void setPrecisionPicking(bool precisionPicking); - void setRayPickResult(const RayPickResult& rayPickResult) { _prevResult = rayPickResult; } + void setRayPickResult(const RayPickResult& rayPickResult); - const QVector& getIgnoreEntites() { return _ignoreEntities; } - const QVector& getIncludeEntites() { return _includeEntities; } - const QVector& getIgnoreOverlays() { return _ignoreOverlays; } - const QVector& getIncludeOverlays() { return _includeOverlays; } - const QVector& getIgnoreAvatars() { return _ignoreAvatars; } - const QVector& getIncludeAvatars() { return _includeAvatars; } + QVector getIgnoreItems() const; + QVector getIncludeItems() const; + + template + QVector getIgnoreItemsAs() const { + QVector result; + withReadLock([&] { + for (const auto& uid : _ignoreItems) { + result.push_back(uid); + } + }); + return result; + } + + template + QVector getIncludeItemsAs() const { + QVector result; + withReadLock([&] { + for (const auto& uid : _includeItems) { + result.push_back(uid); + } + }); + return result; + } + + void setIgnoreItems(const QVector& items); + void setIncludeItems(const QVector& items); +#if 0 void setIgnoreEntities(const QScriptValue& ignoreEntities); void setIncludeEntities(const QScriptValue& includeEntities); void setIgnoreOverlays(const QScriptValue& ignoreOverlays); void setIncludeOverlays(const QScriptValue& includeOverlays); void setIgnoreAvatars(const QScriptValue& ignoreAvatars); void setIncludeAvatars(const QScriptValue& includeAvatars); - - QReadWriteLock* getLock() { return &_lock; } +#endif private: RayPickFilter _filter; - float _maxDistance; + const float _maxDistance; bool _enabled; RayPickResult _prevResult; - QVector _ignoreEntities; - QVector _includeEntities; - QVector _ignoreOverlays; - QVector _includeOverlays; - QVector _ignoreAvatars; - QVector _includeAvatars; - - QReadWriteLock _lock; + QVector _ignoreItems; + QVector _includeItems; }; #endif // hifi_RayPick_h diff --git a/interface/src/raypick/StaticRayPick.cpp b/libraries/pointers/src/pointers/rays/StaticRayPick.cpp similarity index 92% rename from interface/src/raypick/StaticRayPick.cpp rename to libraries/pointers/src/pointers/rays/StaticRayPick.cpp index 89bcddb3df..e507341021 100644 --- a/interface/src/raypick/StaticRayPick.cpp +++ b/libraries/pointers/src/pointers/rays/StaticRayPick.cpp @@ -1,7 +1,4 @@ // -// StaticRayPick.cpp -// interface/src/raypick -// // Created by Sam Gondelman 7/11/2017 // Copyright 2017 High Fidelity, Inc. // diff --git a/interface/src/raypick/StaticRayPick.h b/libraries/pointers/src/pointers/rays/StaticRayPick.h similarity index 92% rename from interface/src/raypick/StaticRayPick.h rename to libraries/pointers/src/pointers/rays/StaticRayPick.h index fc09ee6a27..de5ec234a5 100644 --- a/interface/src/raypick/StaticRayPick.h +++ b/libraries/pointers/src/pointers/rays/StaticRayPick.h @@ -1,7 +1,4 @@ // -// StaticRayPick.h -// interface/src/raypick -// // Created by Sam Gondelman 7/11/2017 // Copyright 2017 High Fidelity, Inc. // From 429905de51d1f11ff9074f4797922b8c47f4b033 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 16 Oct 2017 15:52:17 -0700 Subject: [PATCH 21/50] update scripts to use ignoreItems, fix caching bug --- interface/src/raypick/LaserPointer.cpp | 10 +++-- interface/src/raypick/RayPickManager.cpp | 37 ++++++++++--------- interface/src/raypick/RayPickManager.h | 25 +++++++++++-- .../src/raypick/RayPickScriptingInterface.h | 2 +- .../pointers/src/pointers/PointerManager.h | 2 +- .../pointers/src/pointers/rays/RayPick.h | 8 ---- .../controllers/controllerDispatcher.js | 12 +++--- .../controllerModules/inEditMode.js | 9 ++++- .../controllerModules/overlayLaserInput.js | 7 +++- .../controllers/controllerModules/teleport.js | 8 ++-- scripts/system/controllers/grab.js | 2 +- 11 files changed, 76 insertions(+), 46 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 7fa5eaed5f..a2f7c7a521 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -58,8 +58,8 @@ void LaserPointer::enable() { void LaserPointer::disable() { qApp->getRayPickManager().disableRayPick(_rayPickUID); - _renderingEnabled = false; withWriteLock([&] { + _renderingEnabled = false; if (!_currentRenderState.empty()) { if (_renderStates.find(_currentRenderState) != _renderStates.end()) { disableRenderState(_renderStates[_currentRenderState]); @@ -219,11 +219,15 @@ void LaserPointer::setPrecisionPicking(const bool precisionPicking) { } void LaserPointer::setLaserLength(const float laserLength) { - _laserLength = laserLength; + withWriteLock([&] { + _laserLength = laserLength; + }); } void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) { - _objectLockEnd = std::pair(objectID, isOverlay); + withWriteLock([&] { + _objectLockEnd = std::pair(objectID, isOverlay); + }); } void LaserPointer::setIgnoreItems(const QVector& ignoreItems) const { diff --git a/interface/src/raypick/RayPickManager.cpp b/interface/src/raypick/RayPickManager.cpp index f3425a2b28..beb0075428 100644 --- a/interface/src/raypick/RayPickManager.cpp +++ b/interface/src/raypick/RayPickManager.cpp @@ -22,24 +22,24 @@ #include "JointRayPick.h" #include "MouseRayPick.h" -bool RayPickManager::checkAndCompareCachedResults(QPair& ray, RayPickCache& cache, RayPickResult& res, const RayPickFilter::Flags& mask) { - if (cache.contains(ray) && cache[ray].find(mask) != cache[ray].end()) { - if (cache[ray][mask].distance < res.distance) { - res = cache[ray][mask]; +bool RayPickManager::checkAndCompareCachedResults(QPair& ray, RayPickCache& cache, RayPickResult& res, const RayCacheKey& key) { + if (cache.contains(ray) && cache[ray].find(key) != cache[ray].end()) { + if (cache[ray][key].distance < res.distance) { + res = cache[ray][key]; } return true; } return false; } -void RayPickManager::cacheResult(const bool intersects, const RayPickResult& resTemp, const RayPickFilter::Flags& mask, RayPickResult& res, QPair& ray, RayPickCache& cache) { +void RayPickManager::cacheResult(const bool intersects, const RayPickResult& resTemp, const RayCacheKey& key, RayPickResult& res, QPair& ray, RayPickCache& cache) { if (intersects) { - cache[ray][mask] = resTemp; + cache[ray][key] = resTemp; if (resTemp.distance < res.distance) { res = resTemp; } } else { - cache[ray][mask] = RayPickResult(res.searchRay); + cache[ray][key] = RayPickResult(res.searchRay); } } @@ -74,8 +74,8 @@ void RayPickManager::update() { bool fromCache = true; bool invisible = rayPick->getFilter().doesPickInvisible(); bool nonCollidable = rayPick->getFilter().doesPickNonCollidable(); - RayPickFilter::Flags entityMask = rayPick->getFilter().getEntityFlags(); - if (!checkAndCompareCachedResults(rayKey, results, res, entityMask)) { + RayCacheKey entityKey = { rayPick->getFilter().getEntityFlags(), rayPick->getIncludeItems(), rayPick->getIgnoreItems() }; + if (!checkAndCompareCachedResults(rayKey, results, res, entityKey)) { entityRes = DependencyManager::get()->findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCoarse(), rayPick->getIncludeItemsAs(), rayPick->getIgnoreItemsAs(), !invisible, !nonCollidable); fromCache = false; @@ -83,7 +83,7 @@ void RayPickManager::update() { if (!fromCache) { cacheResult(entityRes.intersects, RayPickResult(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, ray, entityRes.surfaceNormal), - entityMask, res, rayKey, results); + entityKey, res, rayKey, results); } } @@ -92,8 +92,8 @@ void RayPickManager::update() { bool fromCache = true; bool invisible = rayPick->getFilter().doesPickInvisible(); bool nonCollidable = rayPick->getFilter().doesPickNonCollidable(); - RayPickFilter::Flags overlayMask = rayPick->getFilter().getOverlayFlags(); - if (!checkAndCompareCachedResults(rayKey, results, res, overlayMask)) { + RayCacheKey overlayKey = { rayPick->getFilter().getOverlayFlags(), rayPick->getIncludeItems(), rayPick->getIgnoreItems() }; + if (!checkAndCompareCachedResults(rayKey, results, res, overlayKey)) { overlayRes = qApp->getOverlays().findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCoarse(), rayPick->getIncludeItemsAs(), rayPick->getIgnoreItemsAs(), !invisible, !nonCollidable); fromCache = false; @@ -101,25 +101,26 @@ void RayPickManager::update() { if (!fromCache) { cacheResult(overlayRes.intersects, RayPickResult(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, ray, overlayRes.surfaceNormal), - overlayMask, res, rayKey, results); + overlayKey, res, rayKey, results); } } if (rayPick->getFilter().doesPickAvatars()) { - RayPickFilter::Flags avatarMask = rayPick->getFilter().getAvatarFlags(); - if (!checkAndCompareCachedResults(rayKey, results, res, avatarMask)) { + RayCacheKey avatarKey = { rayPick->getFilter().getAvatarFlags(), rayPick->getIncludeItems(), rayPick->getIgnoreItems() }; + if (!checkAndCompareCachedResults(rayKey, results, res, avatarKey)) { RayToAvatarIntersectionResult avatarRes = DependencyManager::get()->findRayIntersectionVector(ray, rayPick->getIncludeItemsAs(), rayPick->getIgnoreItemsAs()); - cacheResult(avatarRes.intersects, RayPickResult(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, ray), avatarMask, res, rayKey, results); + cacheResult(avatarRes.intersects, RayPickResult(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, ray), avatarKey, res, rayKey, results); } } // Can't intersect with HUD in desktop mode if (rayPick->getFilter().doesPickHUD() && DependencyManager::get()->isHMDMode()) { RayPickFilter::Flags hudMask = rayPick->getFilter().getHUDFlags(); - if (!checkAndCompareCachedResults(rayKey, results, res, hudMask)) { + RayCacheKey hudKey = { rayPick->getFilter().getHUDFlags() }; + if (!checkAndCompareCachedResults(rayKey, results, res, hudKey)) { glm::vec3 hudRes = DependencyManager::get()->calculateRayUICollisionPoint(ray.origin, ray.direction); - cacheResult(true, RayPickResult(IntersectionType::HUD, 0, glm::distance(ray.origin, hudRes), hudRes, ray), hudMask, res, rayKey, results); + cacheResult(true, RayPickResult(IntersectionType::HUD, 0, glm::distance(ray.origin, hudRes), hudRes, ray), hudKey, res, rayKey, results); } } diff --git a/interface/src/raypick/RayPickManager.h b/interface/src/raypick/RayPickManager.h index 8c6c28ceab..fd2c6f4a6b 100644 --- a/interface/src/raypick/RayPickManager.h +++ b/interface/src/raypick/RayPickManager.h @@ -24,6 +24,25 @@ class RayPickResult; +typedef struct RayCacheKey { + RayPickFilter::Flags mask; + QVector include; + QVector ignore; + + bool operator==(const RayCacheKey& other) const { + return (mask == other.mask && include == other.include && ignore == other.ignore); + } +} RayCacheKey; + +namespace std { + template <> + struct hash { + size_t operator()(const RayCacheKey& k) const { + return ((hash()(k.mask) ^ (qHash(k.include) << 1)) >> 1) ^ (qHash(k.ignore) << 1); + } + }; +} + class RayPickManager : protected ReadWriteLockable { public: @@ -45,11 +64,11 @@ private: RayPick::Pointer findRayPick(const QUuid& uid) const; QHash _rayPicks; - typedef QHash, std::unordered_map> RayPickCache; + typedef QHash, std::unordered_map> RayPickCache; // Returns true if this ray exists in the cache, and if it does, update res if the cached result is closer - bool checkAndCompareCachedResults(QPair& ray, RayPickCache& cache, RayPickResult& res, const RayPickFilter::Flags& mask); - void cacheResult(const bool intersects, const RayPickResult& resTemp, const RayPickFilter::Flags& mask, RayPickResult& res, QPair& ray, RayPickCache& cache); + bool checkAndCompareCachedResults(QPair& ray, RayPickCache& cache, RayPickResult& res, const RayCacheKey& key); + void cacheResult(const bool intersects, const RayPickResult& resTemp, const RayCacheKey& key, RayPickResult& res, QPair& ray, RayPickCache& cache); }; #endif // hifi_RayPickManager_h \ No newline at end of file diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index 59c5804d72..099103e4c5 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -24,7 +24,7 @@ class RayPickScriptingInterface : public QObject, public Dependency { Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT) Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT) Q_PROPERTY(unsigned int PICK_HUD READ PICK_HUD CONSTANT) - Q_PROPERTY(unsigned int PICK_COURSE READ PICK_COURSE CONSTANT) + Q_PROPERTY(unsigned int PICK_COARSE READ PICK_COARSE CONSTANT) Q_PROPERTY(unsigned int PICK_INCLUDE_INVISIBLE READ PICK_INCLUDE_INVISIBLE CONSTANT) Q_PROPERTY(unsigned int PICK_INCLUDE_NONCOLLIDABLE READ PICK_INCLUDE_NONCOLLIDABLE CONSTANT) Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ PICK_ALL_INTERSECTIONS CONSTANT) diff --git a/libraries/pointers/src/pointers/PointerManager.h b/libraries/pointers/src/pointers/PointerManager.h index 0a045997c2..df21ab7cf2 100644 --- a/libraries/pointers/src/pointers/PointerManager.h +++ b/libraries/pointers/src/pointers/PointerManager.h @@ -26,4 +26,4 @@ signals: void hoverLeave(const QUuid& id, const PointerEvent& pointerEvent); }; -#endif // hifi_RayPick_h +#endif // hifi_pointers_PointerManager_h diff --git a/libraries/pointers/src/pointers/rays/RayPick.h b/libraries/pointers/src/pointers/rays/RayPick.h index 3691ac9985..5a53891dc6 100644 --- a/libraries/pointers/src/pointers/rays/RayPick.h +++ b/libraries/pointers/src/pointers/rays/RayPick.h @@ -142,14 +142,6 @@ public: void setIgnoreItems(const QVector& items); void setIncludeItems(const QVector& items); -#if 0 - void setIgnoreEntities(const QScriptValue& ignoreEntities); - void setIncludeEntities(const QScriptValue& includeEntities); - void setIgnoreOverlays(const QScriptValue& ignoreOverlays); - void setIncludeOverlays(const QScriptValue& includeOverlays); - void setIgnoreAvatars(const QScriptValue& ignoreAvatars); - void setIncludeAvatars(const QScriptValue& includeAvatars); -#endif private: RayPickFilter _filter; diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 9295f50518..2deef5f9fa 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -141,8 +141,11 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.setIgnoreTablet = function() { - RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]); - RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]); + if (HMD.tabletID !== this.tabletID) { + this.tabletID = HMD.tabletID; + RayPick.setIgnoreItems(_this.leftControllerRayPick, _this.blacklist.concat([HMD.tabletID])); + RayPick.setIgnoreItems(_this.rightControllerRayPick, _this.blacklist.concat([HMD.tabletID])); + } }; this.update = function () { @@ -367,9 +370,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.setBlacklist = function() { - RayPick.setIgnoreEntities(_this.leftControllerRayPick, this.blacklist); - RayPick.setIgnoreEntities(_this.rightControllerRayPick, this.blacklist); - + RayPick.setIgnoreItems(_this.leftControllerRayPick, this.blacklist.concat(HMD.tabletID)); + RayPick.setIgnoreItems(_this.rightControllerRayPick, this.blacklist.concat(HMD.tabletID)); }; var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index cbe64b1870..5049185827 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -134,6 +134,13 @@ Script.include("/~/system/libraries/utils.js"); LaserPointers.enableLaserPointer(this.laserPointer); LaserPointers.setRenderState(this.laserPointer, this.mode); + + if (HMD.tabletID !== this.tabletID || HMD.tabletButtonID !== this.tabletButtonID || HMD.tabletScreenID !== this.tabletScreenID) { + this.tabletID = HMD.tabletID; + this.tabletButtonID = HMD.tabletButtonID; + this.tabletScreenID = HMD.tabletScreenID; + LaserPointers.setIgnoreItems(this.laserPointer, [HMD.tabletID, HMD.tabletButtonID, HMD.tabletScreenID]); + } }; this.pointingAtTablet = function(objectID) { @@ -244,7 +251,7 @@ Script.include("/~/system/libraries/utils.js"); defaultRenderStates: defaultRenderStates }); - LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID, HMD.tabletButtonID, HMD.tabletScreenID]); + LaserPointers.setIgnoreItems(this.laserPointer, [HMD.tabletID, HMD.tabletButtonID, HMD.tabletScreenID]); } var leftHandInEditMode = new InEditMode(LEFT_HAND); diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 7dace85ec4..7c86a01c5d 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -184,6 +184,11 @@ Script.include("/~/system/libraries/controllers.js"); LaserPointers.enableLaserPointer(this.laserPointer); LaserPointers.setRenderState(this.laserPointer, this.mode); + + if (HMD.tabletID !== this.tabletID) { + this.tabletID = HMD.tabletID; + LaserPointers.setIgnoreItems(this.laserPointer, [HMD.tabletID]); + } }; this.processControllerTriggers = function(controllerData) { @@ -378,7 +383,7 @@ Script.include("/~/system/libraries/controllers.js"); defaultRenderStates: defaultRenderStates }); - LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID]); + LaserPointers.setIgnoreItems(this.laserPointer, [HMD.tabletID]); } var leftOverlayLaserInput = new OverlayLaserInput(LEFT_HAND); diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index d2717a1348..86ad9c56ca 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -347,10 +347,10 @@ Script.include("/~/system/libraries/controllers.js"); }; this.setIgnoreEntities = function(entitiesToIgnore) { - LaserPointers.setIgnoreEntities(this.teleportRayHandVisible, entitiesToIgnore); - LaserPointers.setIgnoreEntities(this.teleportRayHandInvisible, entitiesToIgnore); - LaserPointers.setIgnoreEntities(this.teleportRayHeadVisible, entitiesToIgnore); - LaserPointers.setIgnoreEntities(this.teleportRayHeadInvisible, entitiesToIgnore); + LaserPointers.setIgnoreItems(this.teleportRayHandVisible, entitiesToIgnore); + LaserPointers.setIgnoreItems(this.teleportRayHandInvisible, entitiesToIgnore); + LaserPointers.setIgnoreItems(this.teleportRayHeadVisible, entitiesToIgnore); + LaserPointers.setIgnoreItems(this.teleportRayHeadInvisible, entitiesToIgnore); }; } diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 2f046cbce3..a1846e7ad7 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -263,7 +263,7 @@ function Grabber() { filter: RayPick.PICK_OVERLAYS, enabled: true }); - RayPick.setIncludeOverlays(this.mouseRayOverlays, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]); + RayPick.setIncludeItems(this.mouseRayOverlays, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]); var renderStates = [{name: "grabbed", end: beacon}]; this.mouseRayEntities = LaserPointers.createLaserPointer({ joint: "Mouse", From fb1c075a1272c5a57ab9fd80fcf051d1a2702160 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Oct 2017 15:55:25 -0700 Subject: [PATCH 22/50] fix animating entities --- .../src/RenderableModelEntityItem.cpp | 32 +++++++++---------- libraries/render-utils/src/Model.cpp | 10 ++++-- libraries/render-utils/src/Model.h | 1 + 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e2afc6eac2..1e75b3bdf1 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -153,7 +153,7 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const { return true; } - if (isMovingRelativeToParent() || isAnimatingSomething()) { + if (isAnimatingSomething()) { return true; } @@ -195,14 +195,10 @@ void RenderableModelEntityItem::updateModelBounds() { return; } - /* adebug TODO: figure out if we need to DO anything when isAnimatingSomething() - if (isAnimatingSomething()) { - return true; - } - */ - + bool updateRenderItems = false; if (model->needsReload()) { model->updateGeometry(); + updateRenderItems = true; } if (model->getScaleToFitDimensions() != getDimensions() || @@ -217,23 +213,28 @@ void RenderableModelEntityItem::updateModelBounds() { // now recalculate the bounds and registration model->setScaleToFit(true, getDimensions()); model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); + updateRenderItems = true; } bool success; auto transform = getTransform(success); - if (success) { - if (model->getTranslation() != transform.getTranslation()) { - model->setTranslation(transform.getTranslation()); - } - if (model->getRotation() != transform.getRotation()) { - model->setRotation(transform.getRotation()); - } + if (success && (model->getTranslation() != transform.getTranslation() || + model->getRotation() != transform.getRotation())) { + model->setTransformNoUpdateRenderItems(transform); + updateRenderItems = true; } - if (_needsInitialSimulation || _needsJointSimulation) { + if (_needsInitialSimulation || _needsJointSimulation || isAnimatingSomething()) { + // NOTE: on isAnimatingSomething() we need to call Model::simulate() which calls Rig::updateRig() + // TODO: there is opportunity to further optimize the isAnimatingSomething() case. model->simulate(0.0f); _needsInitialSimulation = false; _needsJointSimulation = false; + updateRenderItems = true; + } + + if (updateRenderItems) { + model->updateRenderItems(); } } @@ -929,7 +930,6 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() { return; } - // relay any inbound joint changes from scripts/animation/network to the model/rig _jointDataLock.withWriteLock([&] { for (int index = 0; index < _localJointData.size(); ++index) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 4573b5aa78..fcda330f4a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -121,8 +121,6 @@ bool Model::needsFixupInScene() const { return (_needsFixupInScene || !_addedToScene) && !_needsReload && isLoaded(); } -// TODO?: should we combine translation and rotation into single method to avoid double-work? -// (figure out where we call these) void Model::setTranslation(const glm::vec3& translation) { _translation = translation; updateRenderItems(); @@ -133,6 +131,14 @@ void Model::setRotation(const glm::quat& rotation) { updateRenderItems(); } +// temporary HACK: set transform while avoiding implicit calls to updateRenderItems() +// TODO: make setRotation() and friends set flag to be used later to decide to updateRenderItems() +void Model::setTransformNoUpdateRenderItems(const Transform& transform) { + _translation = transform.getTranslation(); + _rotation = transform.getRotation(); + // DO NOT call updateRenderItems() here! +} + Transform Model::getTransform() const { if (_spatiallyNestableOverride) { bool success; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 0207a4871a..8bce976b4e 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -206,6 +206,7 @@ public: void setTranslation(const glm::vec3& translation); void setRotation(const glm::quat& rotation); + void setTransformNoUpdateRenderItems(const Transform& transform); // temporary HACK const glm::vec3& getTranslation() const { return _translation; } const glm::quat& getRotation() const { return _rotation; } From 102a1cb36beee5faa15ee4230fa8cafcc683010b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Oct 2017 16:02:11 -0700 Subject: [PATCH 23/50] ifdef out detailed profiling --- .../src/RenderableModelEntityItem.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 1e75b3bdf1..f36525b527 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -34,6 +34,9 @@ #include "EntityTreeRenderer.h" #include "EntitiesRendererLogging.h" +// uncomment WANT_DETAILED_PROFILING to profile the interesting contexts +//#define WANT_DETAILED_PROFILING + static CollisionRenderMeshCache collisionMeshCache; void ModelEntityWrapper::setModel(const ModelPointer& model) { @@ -107,7 +110,9 @@ QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextu } void RenderableModelEntityItem::doInitialModelSimulation() { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_physics, __FUNCTION__); +#endif ModelPointer model = getModel(); if (!model) { return; @@ -139,7 +144,9 @@ void RenderableModelEntityItem::autoResizeJointArrays() { } bool RenderableModelEntityItem::needsUpdateModelBounds() const { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_physics, __FUNCTION__); +#endif ModelPointer model = getModel(); if (!hasModel() || !model) { return false; @@ -184,7 +191,9 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const { } void RenderableModelEntityItem::updateModelBounds() { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_physics, "updateModelBounds"); +#endif if (!_dimensionsInitialized || !hasModel()) { return; @@ -1169,7 +1178,9 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin } void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_physics, __FUNCTION__); +#endif if (_hasModel != entity->hasModel()) { _hasModel = entity->hasModel(); } @@ -1259,7 +1270,9 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // TODO? early exit here when not visible? { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_physics, "Fixup"); +#endif if (model->needsFixupInScene()) { model->removeFromScene(scene, transaction); render::Item::Status::Getters statusGetters; @@ -1275,7 +1288,9 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_physics, "CheckAnimation"); +#endif // make a copy of the animation properites auto newAnimationProperties = entity->getAnimationProperties(); if (newAnimationProperties != _renderAnimationProperties) { @@ -1287,7 +1302,9 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } if (_animating) { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_physics, "Animate"); +#endif if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); } @@ -1298,7 +1315,9 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items void ModelEntityRenderer::doRender(RenderArgs* args) { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(render_detail, "MetaModelRender"); +#endif PerformanceTimer perfTimer("RMEIrender"); ModelPointer model; From 1f09c10fb15efb5c89f7bf04978eb1174b924fcf Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Oct 2017 16:43:16 -0700 Subject: [PATCH 24/50] comment out more profiling stuff --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 73c900f2b5..ff581f8e8d 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -26,6 +26,9 @@ #include "RenderableWebEntityItem.h" #include "RenderableZoneEntityItem.h" +// uncomment WANT_DETAILED_PROFILING to see some of the interesting profiling contexts +//#define WANT_DETAILED_PROFILING + using namespace render; using namespace render::entities; @@ -271,7 +274,9 @@ void EntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& tra } void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& transaction) { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_physics, __FUNCTION__); +#endif if (!isValidRenderItem()) { return; } @@ -331,7 +336,9 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity } void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_physics, __FUNCTION__); +#endif withWriteLock([&] { auto transparent = isTransparent(); if (_prevIsTransparent && !transparent) { From 339edc67c8efca481754b0147616be527e47ed91 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Oct 2017 17:27:53 -0700 Subject: [PATCH 25/50] ifdef out more per-frame-per-entity profiling --- libraries/animation/src/Rig.cpp | 16 ++++++++++++++-- .../src/RenderableModelEntityItem.cpp | 6 +++++- libraries/render-utils/src/Model.cpp | 9 +++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0897c26a12..5609baf548 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -32,6 +32,9 @@ #include "AnimUtil.h" #include "IKTarget.h" +// uncomment WANT_DETAILED_PROFILING for profiling that would otherwise impact performance +//#define WANT_DETAILED_PROFILING + static int nextRigId = 1; static std::map rigRegistry; static std::mutex rigRegistryMutex; @@ -999,14 +1002,17 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh } void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, const glm::mat4& rigToWorldTransform) { - +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE_EX(simulation_animation_detail, __FUNCTION__, 0xffff00ff, 0); PerformanceTimer perfTimer("updateAnimations"); +#endif setModelOffset(rootTransform); if (_animNode && _enabledAnimations) { +#ifdef WANT_DETAILED_PROFILING PerformanceTimer perfTimer("handleTriggers"); +#endif updateAnimationStateHandlers(); _animVars.setRigToGeometryTransform(_rigToGeometryTransform); @@ -1658,7 +1664,9 @@ bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const } void Rig::applyOverridePoses() { +#ifdef WANT_DETAILED_PROFILING PerformanceTimer perfTimer("override"); +#endif if (_numOverrides == 0 || !_animSkeleton) { return; } @@ -1675,7 +1683,9 @@ void Rig::applyOverridePoses() { } void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) { +#ifdef WANT_DETAILED_PROFILING PerformanceTimer perfTimer("buildAbsolute"); +#endif if (!_animSkeleton) { return; } @@ -1730,8 +1740,10 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { } void Rig::copyJointsFromJointData(const QVector& jointDataVec) { - PerformanceTimer perfTimer("copyJoints"); +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_animation_detail, "copyJoints"); + PerformanceTimer perfTimer("copyJoints"); +#endif if (!_animSkeleton) { return; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index f36525b527..3b7b180f7a 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -352,7 +352,9 @@ bool RenderableModelEntityItem::isReadyToComputeShape() const { // we have both URLs AND both geometries AND they are both fully loaded. if (_needsInitialSimulation) { // the _model's offset will be wrong until _needsInitialSimulation is false +#ifdef WANT_DETAILED_PROFILING PerformanceTimer perfTimer("_model->simulate"); +#endif const_cast(this)->doInitialModelSimulation(); } return true; @@ -898,7 +900,9 @@ void RenderableModelEntityItem::setJointTranslationsSet(const QVector& tra } void RenderableModelEntityItem::locationChanged(bool tellPhysics) { +#ifdef WANT_DETAILED_PROFILING PerformanceTimer pertTimer("locationChanged"); +#endif EntityItem::locationChanged(tellPhysics); auto model = getModel(); if (model && model->isLoaded()) { @@ -1317,8 +1321,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce void ModelEntityRenderer::doRender(RenderArgs* args) { #ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(render_detail, "MetaModelRender"); -#endif PerformanceTimer perfTimer("RMEIrender"); +#endif ModelPointer model; withReadLock([&]{ diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index fcda330f4a..616b1554b5 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -32,6 +32,9 @@ #include "RenderUtilsLogging.h" #include +// uncomment WANT_DETAILED_PROFILING to enable profiling that causes measureable performance impact +//#define WANT_DETAILED_PROFILING + using namespace std; int nakedModelPointerTypeId = qRegisterMetaType(); @@ -963,7 +966,9 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe } void Blender::run() { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); +#endif QVector vertices, normals; if (_model) { int offset = 0; @@ -1084,8 +1089,10 @@ void Model::snapToRegistrationPoint() { } void Model::simulate(float deltaTime, bool fullUpdate) { +#ifdef WANT_DETAILED_PROFILING PROFILE_RANGE(simulation_detail, __FUNCTION__); PerformanceTimer perfTimer("Model::simulate"); +#endif fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); @@ -1123,7 +1130,9 @@ void Model::computeMeshPartLocalBounds() { // virtual void Model::updateClusterMatrices() { +#ifdef WANT_DETAILED_PROFILING PerformanceTimer perfTimer("Model::updateClusterMatrices"); +#endif if (!_needsUpdateClusterMatrices || !isLoaded()) { return; From 2e69d2b1ad3576120aa917e462da5968fc59616d Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Tue, 17 Oct 2017 11:13:02 -0700 Subject: [PATCH 26/50] missed one scripting interface reference --- interface/src/raypick/LaserPointer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index a2f7c7a521..320c2a6809 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -102,7 +102,7 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& } const RayPickResult LaserPointer::getPrevRayPickResult() { - return DependencyManager::get()->getPrevRayPickResult(_rayPickUID); + return qApp->getRayPickManager().getPrevRayPickResult(_rayPickUID); } void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState) { @@ -262,4 +262,4 @@ void RenderState::deleteOverlays() { if (!_endID.isNull()) { qApp->getOverlays().deleteOverlay(_endID); } -} \ No newline at end of file +} From ba4f41b59a2780826a75ea54880b398c9437fdce Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 17 Oct 2017 12:03:59 -0700 Subject: [PATCH 27/50] fix warnings --- interface/src/raypick/RayPickManager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/raypick/RayPickManager.cpp b/interface/src/raypick/RayPickManager.cpp index beb0075428..15489ce93d 100644 --- a/interface/src/raypick/RayPickManager.cpp +++ b/interface/src/raypick/RayPickManager.cpp @@ -116,8 +116,7 @@ void RayPickManager::update() { // Can't intersect with HUD in desktop mode if (rayPick->getFilter().doesPickHUD() && DependencyManager::get()->isHMDMode()) { - RayPickFilter::Flags hudMask = rayPick->getFilter().getHUDFlags(); - RayCacheKey hudKey = { rayPick->getFilter().getHUDFlags() }; + RayCacheKey hudKey = { rayPick->getFilter().getHUDFlags(), QVector(), QVector() }; if (!checkAndCompareCachedResults(rayKey, results, res, hudKey)) { glm::vec3 hudRes = DependencyManager::get()->calculateRayUICollisionPoint(ray.origin, ray.direction); cacheResult(true, RayPickResult(IntersectionType::HUD, 0, glm::distance(ray.origin, hudRes), hudRes, ray), hudKey, res, rayKey, results); From 50929b780d962eb35e76b2ad98b939036c9cff44 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 17 Oct 2017 12:59:41 -0700 Subject: [PATCH 28/50] Add constructor to suppress warning --- libraries/pointers/src/pointers/PointerManager.cpp | 5 +++++ libraries/pointers/src/pointers/PointerManager.h | 1 + 2 files changed, 6 insertions(+) diff --git a/libraries/pointers/src/pointers/PointerManager.cpp b/libraries/pointers/src/pointers/PointerManager.cpp index dddc84279b..63bd983420 100644 --- a/libraries/pointers/src/pointers/PointerManager.cpp +++ b/libraries/pointers/src/pointers/PointerManager.cpp @@ -1 +1,6 @@ #include "PointerManager.h" + +PointerManager::PointerManager() { + +} + diff --git a/libraries/pointers/src/pointers/PointerManager.h b/libraries/pointers/src/pointers/PointerManager.h index df21ab7cf2..16f854bff5 100644 --- a/libraries/pointers/src/pointers/PointerManager.h +++ b/libraries/pointers/src/pointers/PointerManager.h @@ -15,6 +15,7 @@ class PointerManager : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY public: + PointerManager(); signals: void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent); From 315baeae1f690bfedffa75f9bdc1ab8e721de1ce Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 17 Oct 2017 13:22:40 -0700 Subject: [PATCH 29/50] threading fix --- libraries/pointers/src/pointers/rays/RayPick.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/pointers/src/pointers/rays/RayPick.cpp b/libraries/pointers/src/pointers/rays/RayPick.cpp index 36756cdb79..bc3a05cd7a 100644 --- a/libraries/pointers/src/pointers/rays/RayPick.cpp +++ b/libraries/pointers/src/pointers/rays/RayPick.cpp @@ -32,8 +32,10 @@ float RayPick::getMaxDistance() const { return _maxDistance; } -bool RayPick::isEnabled() const { - return _enabled; +bool RayPick::isEnabled() const { + return resultWithReadLock([&] { + return _enabled; + }); } void RayPick::setPrecisionPicking(bool precisionPicking) { @@ -49,11 +51,15 @@ void RayPick::setRayPickResult(const RayPickResult& rayPickResult) { } QVector RayPick::getIgnoreItems() const { - return _ignoreItems; + return resultWithReadLock>([&] { + return _ignoreItems; + }); } QVector RayPick::getIncludeItems() const { - return _includeItems; + return resultWithReadLock>([&] { + return _includeItems; + }); } RayPickResult RayPick::getPrevRayPickResult() const { From edd256c6004732eed5abe05bb35a8ebb1455843e Mon Sep 17 00:00:00 2001 From: Nissim Date: Thu, 19 Oct 2017 13:36:32 -0700 Subject: [PATCH 30/50] Fixed crash on invalid HazeMode. --- libraries/entities/src/EntityItemProperties.cpp | 14 ++++++++++++-- libraries/entities/src/ZoneEntityItem.cpp | 10 +++++++++- libraries/entities/src/ZoneEntityItem.h | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7ad2f22144..cfe9bda1c0 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -228,11 +228,21 @@ const std::array COMPONENT_MODES = { { } }; QString EntityItemProperties::getHazeModeAsString() const { - return COMPONENT_MODES[_hazeMode].second; + // return "inherit" if _hazeMode is not valid + if (_hazeMode >= 0 && _hazeMode < COMPONENT_MODE_ITEM_COUNT) { + return COMPONENT_MODES[_hazeMode].second; + } else { + return COMPONENT_MODES[0].second; + } } QString EntityItemProperties::getHazeModeString(uint32_t mode) { - return COMPONENT_MODES[mode].second; + // return "inherit" if mode is not valid + if (mode >= 0 && mode < COMPONENT_MODE_ITEM_COUNT) { + return COMPONENT_MODES[mode].second; + } else { + return COMPONENT_MODES[0].second; + } } void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) { diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 077024c3ab..540a31f6c4 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -44,6 +44,7 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(en _compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL; _backgroundMode = BACKGROUND_MODE_INHERIT; + _hazeMode = (uint32_t)COMPONENT_MODE_INHERIT; } EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { @@ -320,8 +321,15 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { }); } +#pragma optimize("", off) void ZoneEntityItem::setHazeMode(const uint32_t value) { - _hazeMode = value; + if (_hazeMode >= 0 && _hazeMode < COMPONENT_MODE_ITEM_COUNT) { + _hazeMode = value; + } + else { + _hazeMode = 0; + } + _hazePropertiesChanged = true; } diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 066bd5518f..877ca1303c 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -146,7 +146,7 @@ protected: BackgroundMode _backgroundMode = BACKGROUND_MODE_INHERIT; - uint8_t _hazeMode{ (uint8_t)COMPONENT_MODE_INHERIT }; + uint32_t _hazeMode{ (uint32_t)COMPONENT_MODE_INHERIT }; float _hazeRange{ HazePropertyGroup::DEFAULT_HAZE_RANGE }; xColor _hazeColor{ HazePropertyGroup::DEFAULT_HAZE_COLOR }; From 9af87d46f2e361afdc209738cfccac0b62798236 Mon Sep 17 00:00:00 2001 From: Nissim Date: Thu, 19 Oct 2017 13:51:33 -0700 Subject: [PATCH 31/50] Removed optimize pragma. --- libraries/entities/src/ZoneEntityItem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 540a31f6c4..d1b397d1f8 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -321,7 +321,6 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { }); } -#pragma optimize("", off) void ZoneEntityItem::setHazeMode(const uint32_t value) { if (_hazeMode >= 0 && _hazeMode < COMPONENT_MODE_ITEM_COUNT) { _hazeMode = value; From e314ee432e9ca5393d0237a09c22fcd1f87f4520 Mon Sep 17 00:00:00 2001 From: Nissim Date: Thu, 19 Oct 2017 14:20:47 -0700 Subject: [PATCH 32/50] Fixed gcc warning. --- libraries/entities/src/EntityItemProperties.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 80a1d0827e..adcfa412cf 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -229,7 +229,7 @@ const std::array COMPONENT_MODES = { { QString EntityItemProperties::getHazeModeAsString() const { // return "inherit" if _hazeMode is not valid - if (_hazeMode >= 0 && _hazeMode < COMPONENT_MODE_ITEM_COUNT) { + if (_hazeMode < COMPONENT_MODE_ITEM_COUNT) { return COMPONENT_MODES[_hazeMode].second; } else { return COMPONENT_MODES[0].second; @@ -238,7 +238,7 @@ QString EntityItemProperties::getHazeModeAsString() const { QString EntityItemProperties::getHazeModeString(uint32_t mode) { // return "inherit" if mode is not valid - if (mode >= 0 && mode < COMPONENT_MODE_ITEM_COUNT) { + if (mode < COMPONENT_MODE_ITEM_COUNT) { return COMPONENT_MODES[mode].second; } else { return COMPONENT_MODES[0].second; From c79bf3bc042b8e45ba7af8f393a14851f715416b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Oct 2017 14:31:50 -0700 Subject: [PATCH 33/50] cleanup WANT_DETAILED_PROFILING --- BUILD.md | 2 +- libraries/animation/src/Rig.cpp | 27 ++++-------- .../src/RenderableEntityItem.cpp | 10 +---- .../src/RenderableModelEntityItem.cpp | 44 +++++-------------- libraries/render-utils/src/Model.cpp | 16 ++----- libraries/shared/src/PerfStat.h | 7 +++ libraries/shared/src/Profile.h | 10 +++++ 7 files changed, 42 insertions(+), 74 deletions(-) diff --git a/BUILD.md b/BUILD.md index 4d321146c3..feed677828 100644 --- a/BUILD.md +++ b/BUILD.md @@ -25,7 +25,7 @@ The above dependencies will be downloaded, built, linked and included automatica These are not placed in your normal build tree when doing an out of source build so that they do not need to be re-downloaded and re-compiled every time the CMake build folder is cleared. Should you want to force a re-download and re-compile of a specific external, you can simply remove that directory from the appropriate subfolder in `build/ext`. Should you want to force a re-download and re-compile of all externals, just remove the `build/ext` folder. -If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE_LOCAL_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project. +If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE\_LOCAL\_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project. ### OS Specific Build Guides diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 5609baf548..0f2bce5ca4 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -32,8 +32,6 @@ #include "AnimUtil.h" #include "IKTarget.h" -// uncomment WANT_DETAILED_PROFILING for profiling that would otherwise impact performance -//#define WANT_DETAILED_PROFILING static int nextRigId = 1; static std::map rigRegistry; @@ -1002,17 +1000,13 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh } void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, const glm::mat4& rigToWorldTransform) { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE_EX(simulation_animation_detail, __FUNCTION__, 0xffff00ff, 0); - PerformanceTimer perfTimer("updateAnimations"); -#endif + DETAILED_PROFILE_RANGE_EX(simulation_animation_detail, __FUNCTION__, 0xffff00ff, 0); + DETAILED_PERFORMANCE_TIMER("updateAnimations"); setModelOffset(rootTransform); if (_animNode && _enabledAnimations) { -#ifdef WANT_DETAILED_PROFILING - PerformanceTimer perfTimer("handleTriggers"); -#endif + DETAILED_PERFORMANCE_TIMER("handleTriggers"); updateAnimationStateHandlers(); _animVars.setRigToGeometryTransform(_rigToGeometryTransform); @@ -1664,9 +1658,7 @@ bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const } void Rig::applyOverridePoses() { -#ifdef WANT_DETAILED_PROFILING - PerformanceTimer perfTimer("override"); -#endif + DETAILED_PERFORMANCE_TIMER("override"); if (_numOverrides == 0 || !_animSkeleton) { return; } @@ -1683,9 +1675,7 @@ void Rig::applyOverridePoses() { } void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) { -#ifdef WANT_DETAILED_PROFILING - PerformanceTimer perfTimer("buildAbsolute"); -#endif + DETAILED_PERFORMANCE_TIMER("buildAbsolute"); if (!_animSkeleton) { return; } @@ -1740,10 +1730,9 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { } void Rig::copyJointsFromJointData(const QVector& jointDataVec) { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_animation_detail, "copyJoints"); - PerformanceTimer perfTimer("copyJoints"); -#endif + DETAILED_PROFILE_RANGE(simulation_animation_detail, "copyJoints"); + DETAILED_PERFORMANCE_TIMER("copyJoints"); + if (!_animSkeleton) { return; } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index ff581f8e8d..07c5694a2a 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -26,8 +26,6 @@ #include "RenderableWebEntityItem.h" #include "RenderableZoneEntityItem.h" -// uncomment WANT_DETAILED_PROFILING to see some of the interesting profiling contexts -//#define WANT_DETAILED_PROFILING using namespace render; using namespace render::entities; @@ -274,9 +272,7 @@ void EntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& tra } void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& transaction) { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_physics, __FUNCTION__); -#endif + DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__); if (!isValidRenderItem()) { return; } @@ -336,9 +332,7 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity } void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_physics, __FUNCTION__); -#endif + DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__); withWriteLock([&] { auto transparent = isTransparent(); if (_prevIsTransparent && !transparent) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 3b7b180f7a..e9fbc43420 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -34,8 +34,6 @@ #include "EntityTreeRenderer.h" #include "EntitiesRendererLogging.h" -// uncomment WANT_DETAILED_PROFILING to profile the interesting contexts -//#define WANT_DETAILED_PROFILING static CollisionRenderMeshCache collisionMeshCache; @@ -110,9 +108,7 @@ QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextu } void RenderableModelEntityItem::doInitialModelSimulation() { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_physics, __FUNCTION__); -#endif + DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__); ModelPointer model = getModel(); if (!model) { return; @@ -144,9 +140,7 @@ void RenderableModelEntityItem::autoResizeJointArrays() { } bool RenderableModelEntityItem::needsUpdateModelBounds() const { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_physics, __FUNCTION__); -#endif + DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__); ModelPointer model = getModel(); if (!hasModel() || !model) { return false; @@ -191,9 +185,7 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const { } void RenderableModelEntityItem::updateModelBounds() { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_physics, "updateModelBounds"); -#endif + DETAILED_PROFILE_RANGE(simulation_physics, "updateModelBounds"); if (!_dimensionsInitialized || !hasModel()) { return; @@ -352,9 +344,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() const { // we have both URLs AND both geometries AND they are both fully loaded. if (_needsInitialSimulation) { // the _model's offset will be wrong until _needsInitialSimulation is false -#ifdef WANT_DETAILED_PROFILING - PerformanceTimer perfTimer("_model->simulate"); -#endif + DETAILED_PERFORMANCE_TIMER("_model->simulate"); const_cast(this)->doInitialModelSimulation(); } return true; @@ -900,9 +890,7 @@ void RenderableModelEntityItem::setJointTranslationsSet(const QVector& tra } void RenderableModelEntityItem::locationChanged(bool tellPhysics) { -#ifdef WANT_DETAILED_PROFILING - PerformanceTimer pertTimer("locationChanged"); -#endif + DETAILED_PERFORMANCE_TIMER("locationChanged"); EntityItem::locationChanged(tellPhysics); auto model = getModel(); if (model && model->isLoaded()) { @@ -1182,9 +1170,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin } void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_physics, __FUNCTION__); -#endif + DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__); if (_hasModel != entity->hasModel()) { _hasModel = entity->hasModel(); } @@ -1274,9 +1260,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // TODO? early exit here when not visible? { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_physics, "Fixup"); -#endif + DETAILED_PROFILE_RANGE(simulation_physics, "Fixup"); if (model->needsFixupInScene()) { model->removeFromScene(scene, transaction); render::Item::Status::Getters statusGetters; @@ -1292,9 +1276,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_physics, "CheckAnimation"); -#endif + DETAILED_PROFILE_RANGE(simulation_physics, "CheckAnimation"); // make a copy of the animation properites auto newAnimationProperties = entity->getAnimationProperties(); if (newAnimationProperties != _renderAnimationProperties) { @@ -1306,9 +1288,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } if (_animating) { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_physics, "Animate"); -#endif + DETAILED_PROFILE_RANGE(simulation_physics, "Animate"); if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); } @@ -1319,10 +1299,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items void ModelEntityRenderer::doRender(RenderArgs* args) { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(render_detail, "MetaModelRender"); - PerformanceTimer perfTimer("RMEIrender"); -#endif + DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender"); + DETAILED_PERFORMANCE_TIMER("RMEIrender"); ModelPointer model; withReadLock([&]{ diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 616b1554b5..964a1961d6 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -32,9 +32,6 @@ #include "RenderUtilsLogging.h" #include -// uncomment WANT_DETAILED_PROFILING to enable profiling that causes measureable performance impact -//#define WANT_DETAILED_PROFILING - using namespace std; int nakedModelPointerTypeId = qRegisterMetaType(); @@ -966,9 +963,7 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe } void Blender::run() { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); -#endif + DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); QVector vertices, normals; if (_model) { int offset = 0; @@ -1089,10 +1084,7 @@ void Model::snapToRegistrationPoint() { } void Model::simulate(float deltaTime, bool fullUpdate) { -#ifdef WANT_DETAILED_PROFILING - PROFILE_RANGE(simulation_detail, __FUNCTION__); - PerformanceTimer perfTimer("Model::simulate"); -#endif + DETAILED_PROFILE_RANGE(simulation_detail, __FUNCTION__); fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); @@ -1130,9 +1122,7 @@ void Model::computeMeshPartLocalBounds() { // virtual void Model::updateClusterMatrices() { -#ifdef WANT_DETAILED_PROFILING - PerformanceTimer perfTimer("Model::updateClusterMatrices"); -#endif + DETAILED_PERFORMANCE_TIMER("Model::updateClusterMatrices"); if (!_needsUpdateClusterMatrices || !isLoaded()) { return; diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h index 785920779e..b09cb38808 100644 --- a/libraries/shared/src/PerfStat.h +++ b/libraries/shared/src/PerfStat.h @@ -97,5 +97,12 @@ private: static QMap _records; }; +// uncomment WANT_DETAILED_PERFORMANCE_TIMERS definition to enable performance timers in high-frequency contexts +//#define WANT_DETAILED_PERFORMANCE_TIMERS +#ifdef WANT_DETAILED_PERFORMANCE_TIMERS + #define DETAILED_PERFORMANCE_TIMER(name) PerformanceTimer detailedPerformanceTimer(name); +#else // WANT_DETAILED_PERFORMANCE_TIMERS + #define DETAILED_PERFORMANCE_TIMER(name) ; // no-op +#endif // WANT_DETAILED_PERFORMANCE_TIMERS #endif // hifi_PerfStat_h diff --git a/libraries/shared/src/Profile.h b/libraries/shared/src/Profile.h index 5de4e8f41a..fc6a2a52cb 100644 --- a/libraries/shared/src/Profile.h +++ b/libraries/shared/src/Profile.h @@ -108,4 +108,14 @@ inline void metadata(const QString& metadataType, const QVariantMap& args) { #define SAMPLE_PROFILE_COUNTER(chance, category, name, ...) if (randFloat() <= chance) { PROFILE_COUNTER(category, name, ##__VA_ARGS__); } #define SAMPLE_PROFILE_INSTANT(chance, category, name, ...) if (randFloat() <= chance) { PROFILE_INSTANT(category, name, ##__VA_ARGS__); } +// uncomment WANT_DETAILED_PROFILING definition to enable profiling in high-frequency contexts +//#define WANT_DETAILED_PROFILING +#ifdef WANT_DETAILED_PROFILING +#define DETAILED_PROFILE_RANGE(category, name) Duration profileRangeThis(trace_##category(), name); +#define DETAILED_PROFILE_RANGE_EX(category, name, argbColor, payload, ...) Duration profileRangeThis(trace_##category(), name, argbColor, (uint64_t)payload, ##__VA_ARGS__); +#else // WANT_DETAILED_PROFILING +#define DETAILED_PROFILE_RANGE(category, name) ; // no-op +#define DETAILED_PROFILE_RANGE_EX(category, name, argbColor, payload, ...) ; // no-op +#endif // WANT_DETAILED_PROFILING + #endif From d01ecb71dfc607b6e32d87df3c0065fdd89bc63c Mon Sep 17 00:00:00 2001 From: Nissim Date: Thu, 19 Oct 2017 14:41:02 -0700 Subject: [PATCH 34/50] Fixed gcc warning. --- libraries/entities/src/ZoneEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index d1b397d1f8..822c0341e6 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -322,7 +322,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { } void ZoneEntityItem::setHazeMode(const uint32_t value) { - if (_hazeMode >= 0 && _hazeMode < COMPONENT_MODE_ITEM_COUNT) { + if (value < COMPONENT_MODE_ITEM_COUNT) { _hazeMode = value; } else { From 0f519da76e4a13d962e07ae77138527185bc64ec Mon Sep 17 00:00:00 2001 From: Nissim Date: Thu, 19 Oct 2017 17:16:36 -0700 Subject: [PATCH 35/50] Additional protection for mode. --- libraries/model/src/model/Stage.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 75a94f0cea..189aa9a184 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -13,6 +13,7 @@ #include #include #include +#include using namespace model; @@ -256,8 +257,12 @@ void SunSkyStage::setSkybox(const SkyboxPointer& skybox) { invalidate(); } -// Haze -void SunSkyStage::setHazeMode(uint32_t mode) { - _hazeMode = mode; +void SunSkyStage::setHazeMode(uint32_t hazeMode) { + if (hazeMode < COMPONENT_MODE_ITEM_COUNT) { + _hazeMode = hazeMode; + } else { + _hazeMode = 0; + } + invalidate(); } From fa349a694c7c19f826f216f38d46661157cf4a8b Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 20 Oct 2017 15:12:09 +0200 Subject: [PATCH 36/50] Code style fixes --- interface/resources/qml/controls-uit/Button.qml | 4 ++-- interface/resources/qml/controls-uit/CheckBox.qml | 4 ++-- interface/resources/qml/controls-uit/CheckBoxQQC2.qml | 4 ++-- interface/resources/qml/controls-uit/GlyphButton.qml | 4 ++-- interface/resources/qml/controls-uit/Key.qml | 4 ++-- interface/resources/qml/controls-uit/RadioButton.qml | 4 ++-- .../resources/qml/dialogs/preferences/ButtonPreference.qml | 4 ++-- .../qml/dialogs/preferences/CheckBoxPreference.qml | 4 ++-- interface/resources/qml/hifi/audio/MicBar.qml | 4 ++-- interface/resources/qml/hifi/tablet/TabletButton.qml | 4 ++-- interface/resources/qml/hifi/tablet/TabletMenuView.qml | 6 +++--- libraries/ui/src/ui/TabletScriptingInterface.cpp | 2 +- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index 75a696eebd..c068fdcfaf 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -29,12 +29,12 @@ Original.Button { onHoveredChanged: { if (hovered) { - tabletInterface.playSound(TabletEnums.ButtonHover) + tabletInterface.playSound(TabletEnums.ButtonHover); } } onClicked: { - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); } style: ButtonStyle { diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 6eaa9f8923..22b25671c3 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -31,12 +31,12 @@ Original.CheckBox { activeFocusOnPress: true onClicked: { - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); } // TODO: doesnt works for QQC1. check with QQC2 // onHovered: { -// tabletInterface.playSound(TabletEnums.ButtonHover) +// tabletInterface.playSound(TabletEnums.ButtonHover); // } style: CheckBoxStyle { diff --git a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml index 3f165bfc09..32d69cf339 100644 --- a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml +++ b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml @@ -36,12 +36,12 @@ CheckBox { hoverEnabled: true onClicked: { - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); } onHoveredChanged: { if (hovered) { - tabletInterface.playSound(TabletEnums.ButtonHover) + tabletInterface.playSound(TabletEnums.ButtonHover); } } diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index 24ef4afdff..bc7bc636fe 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -27,12 +27,12 @@ Original.Button { onHoveredChanged: { if (hovered) { - tabletInterface.playSound(TabletEnums.ButtonHover) + tabletInterface.playSound(TabletEnums.ButtonHover); } } onClicked: { - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); } style: ButtonStyle { diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml index 26a78b08f5..eda9c03fa2 100644 --- a/interface/resources/qml/controls-uit/Key.qml +++ b/interface/resources/qml/controls-uit/Key.qml @@ -35,13 +35,13 @@ Item { onContainsMouseChanged: { if (containsMouse) { - tabletInterface.playSound(TabletEnums.ButtonHover) + tabletInterface.playSound(TabletEnums.ButtonHover); } } onClicked: { mouse.accepted = true; - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); webEntity.synthesizeKeyPress(glyph); webEntity.synthesizeKeyPress(glyph, mirrorText); diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml index 1b36641418..65d36d2dcb 100644 --- a/interface/resources/qml/controls-uit/RadioButton.qml +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -30,12 +30,12 @@ Original.RadioButton { readonly property int checkRadius: 2 onClicked: { - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); } // TODO: doesnt works for QQC1. check with QQC2 // onHovered: { -// tabletInterface.playSound(TabletEnums.ButtonHover) +// tabletInterface.playSound(TabletEnums.ButtonHover); // } style: RadioButtonStyle { diff --git a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml index bb959f1bee..907d8ffd8d 100644 --- a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml +++ b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml @@ -25,13 +25,13 @@ Preference { id: button onHoveredChanged: { if (hovered) { - tabletInterface.playSound(TabletEnums.ButtonHover) + tabletInterface.playSound(TabletEnums.ButtonHover); } } onClicked: { preference.trigger() - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); } width: 180 anchors.bottom: parent.bottom diff --git a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml index 7c336d7829..8904896ab7 100644 --- a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml @@ -41,12 +41,12 @@ Preference { id: checkBox onHoveredChanged: { if (hovered) { - tabletInterface.playSound(TabletEnums.ButtonHover) + tabletInterface.playSound(TabletEnums.ButtonHover); } } onClicked: { - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); } anchors { diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 81903111c0..b6699d6ceb 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -61,12 +61,12 @@ Rectangle { scrollGestureEnabled: false; onClicked: { Audio.muted = !Audio.muted; - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); } drag.target: dragTarget; onContainsMouseChanged: { if (containsMouse) { - tabletInterface.playSound(TabletEnums.ButtonHover) + tabletInterface.playSound(TabletEnums.ButtonHover); } } } diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 0a8773db54..169c7acec1 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -131,12 +131,12 @@ Item { } tabletButton.clicked(); if (tabletRoot) { - tabletInterface.playSound(TabletEnums.ButtonClick) + tabletInterface.playSound(TabletEnums.ButtonClick); } } onEntered: { tabletButton.isEntered = true; - tabletInterface.playSound(TabletEnums.ButtonHover) + tabletInterface.playSound(TabletEnums.ButtonHover); if (tabletButton.isActive) { tabletButton.state = "hover active state"; diff --git a/interface/resources/qml/hifi/tablet/TabletMenuView.qml b/interface/resources/qml/hifi/tablet/TabletMenuView.qml index f76d896cf2..4a4a6b7f87 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuView.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuView.qml @@ -77,13 +77,13 @@ FocusScope { anchors.fill: parent hoverEnabled: true onEntered: { - tabletInterface.playSound(TabletEnums.ButtonHover) + tabletInterface.playSound(TabletEnums.ButtonHover); listView.currentIndex = index } onClicked: { - tabletInterface.playSound(TabletEnums.ButtonClick) - root.selected(item) + tabletInterface.playSound(TabletEnums.ButtonClick); + root.selected(item); } } } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 9c2ae9eba9..b9da230715 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -92,7 +92,7 @@ void TabletScriptingInterface::playSound(TabletAudioEvents aEvent) { AudioInjectorOptions options; options.stereo = sound->isStereo(); options.ambisonic = sound->isAmbisonic(); - options.localOnly = options.localOnly || sound->isAmbisonic(); // force localOnly when Ambisonic + options.localOnly = true; AudioInjectorPointer injector = AudioInjector::playSoundAndDelete(sound->getByteArray(), options); } From 908d601a430b9da36cf0b1202750e5eec9e4b2d8 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 20 Oct 2017 20:00:11 +0200 Subject: [PATCH 37/50] Code style fixes #1 --- .../resources/qml/dialogs/preferences/ButtonPreference.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml index 907d8ffd8d..3a5c850031 100644 --- a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml +++ b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml @@ -30,7 +30,7 @@ Preference { } onClicked: { - preference.trigger() + preference.trigger(); tabletInterface.playSound(TabletEnums.ButtonClick); } width: 180 From ef0ff96a4432fa223a03b0e89073943b4d422f67 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 20 Oct 2017 11:46:28 -0700 Subject: [PATCH 38/50] fix the rendering of wireframe for model entities with bad model URLs --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e9d395a857..94e88ae29c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -905,7 +905,7 @@ using namespace render; using namespace render::entities; ItemKey ModelEntityRenderer::getKey() { - return ItemKey::Builder().withTypeMeta(); + return ItemKey::Builder().withTypeMeta().withTypeShape(); } uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { @@ -1255,7 +1255,8 @@ void ModelEntityRenderer::doRender(RenderArgs* args) { model = _model; }); - if (_model && _model->didVisualGeometryRequestFail()) { + // If we don't have a model, or the model doesn't have visual geometry, render our bounding box as green wireframe + if (!model || (model && model->didVisualGeometryRequestFail())) { static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; batch.setModelTransform(_modelTransform); // we want to include the scale as well From b0ca1353e4e055f8110749f15ac5e93e2d3f8bdb Mon Sep 17 00:00:00 2001 From: Nissim Date: Fri, 20 Oct 2017 13:32:27 -0700 Subject: [PATCH 39/50] Implemented Haze mode default. --- libraries/entities/src/ZoneEntityItem.cpp | 1 - libraries/entities/src/ZoneEntityItem.h | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 822c0341e6..3b5bcb2450 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -44,7 +44,6 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(en _compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL; _backgroundMode = BACKGROUND_MODE_INHERIT; - _hazeMode = (uint32_t)COMPONENT_MODE_INHERIT; } EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 877ca1303c..ddbb2ed914 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -138,6 +138,8 @@ public: static const bool DEFAULT_GHOSTING_ALLOWED; static const QString DEFAULT_FILTER_URL; + static const uint32_t DEFAULT_HAZE_MODE{ (uint32_t)COMPONENT_MODE_INHERIT }; + protected: KeyLightPropertyGroup _keyLightProperties; @@ -146,7 +148,7 @@ protected: BackgroundMode _backgroundMode = BACKGROUND_MODE_INHERIT; - uint32_t _hazeMode{ (uint32_t)COMPONENT_MODE_INHERIT }; + uint32_t _hazeMode{ DEFAULT_HAZE_MODE }; float _hazeRange{ HazePropertyGroup::DEFAULT_HAZE_RANGE }; xColor _hazeColor{ HazePropertyGroup::DEFAULT_HAZE_COLOR }; From 00c99d3d02dd84322eca21bc01121e9ff0015e78 Mon Sep 17 00:00:00 2001 From: Nissim Date: Fri, 20 Oct 2017 13:53:53 -0700 Subject: [PATCH 40/50] Corrected wrong else format. --- libraries/entities/src/ZoneEntityItem.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 3b5bcb2450..4b1bf53bcc 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -323,8 +323,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { void ZoneEntityItem::setHazeMode(const uint32_t value) { if (value < COMPONENT_MODE_ITEM_COUNT) { _hazeMode = value; - } - else { + } else { _hazeMode = 0; } From 5d0deaf9462201848a86f3f8763e0dae20e4cec4 Mon Sep 17 00:00:00 2001 From: Nissim Date: Fri, 20 Oct 2017 14:27:27 -0700 Subject: [PATCH 41/50] Code review fixes. --- libraries/entities/src/EntityItemProperties.cpp | 4 ++-- libraries/entities/src/ZoneEntityItem.cpp | 5 +---- libraries/model/src/model/Stage.cpp | 5 +---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index adcfa412cf..51ed66bb23 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -232,7 +232,7 @@ QString EntityItemProperties::getHazeModeAsString() const { if (_hazeMode < COMPONENT_MODE_ITEM_COUNT) { return COMPONENT_MODES[_hazeMode].second; } else { - return COMPONENT_MODES[0].second; + return COMPONENT_MODES[COMPONENT_MODE_INHERIT].second; } } @@ -241,7 +241,7 @@ QString EntityItemProperties::getHazeModeString(uint32_t mode) { if (mode < COMPONENT_MODE_ITEM_COUNT) { return COMPONENT_MODES[mode].second; } else { - return COMPONENT_MODES[0].second; + return COMPONENT_MODES[COMPONENT_MODE_INHERIT].second; } } diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 4b1bf53bcc..588c1f9386 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -323,11 +323,8 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { void ZoneEntityItem::setHazeMode(const uint32_t value) { if (value < COMPONENT_MODE_ITEM_COUNT) { _hazeMode = value; - } else { - _hazeMode = 0; + _hazePropertiesChanged = true; } - - _hazePropertiesChanged = true; } uint32_t ZoneEntityItem::getHazeMode() const { diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 189aa9a184..cd2312122c 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -260,9 +260,6 @@ void SunSkyStage::setSkybox(const SkyboxPointer& skybox) { void SunSkyStage::setHazeMode(uint32_t hazeMode) { if (hazeMode < COMPONENT_MODE_ITEM_COUNT) { _hazeMode = hazeMode; - } else { - _hazeMode = 0; + invalidate(); } - - invalidate(); } From 88b8d7bc46316b50abedafa0ee44810b70a50083 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 20 Oct 2017 16:56:42 -0700 Subject: [PATCH 42/50] fix image3d overlay isFacingAvatar --- interface/src/ui/overlays/Image3DOverlay.cpp | 2 -- interface/src/ui/overlays/Text3DOverlay.cpp | 4 ---- interface/src/ui/overlays/Text3DOverlay.h | 3 --- 3 files changed, 9 deletions(-) diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index d0024d9710..313d71fc40 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -51,13 +51,11 @@ void Image3DOverlay::update(float deltatime) { _texture = DependencyManager::get()->getTexture(_url); _textureIsLoaded = false; } -#if OVERLAY_PANELS if (usecTimestampNow() > _transformExpiry) { Transform transform = getTransform(); applyTransformTo(transform); setTransform(transform); } -#endif Parent::update(deltatime); } diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 2e55a9a471..d8188cf6dc 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -257,7 +257,3 @@ bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal); } -Transform Text3DOverlay::evalRenderTransform() { - return Parent::evalRenderTransform(); -} - diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index 6ed31f37b3..daa5fdc804 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -65,9 +65,6 @@ public: virtual Text3DOverlay* createClone() const override; -protected: - Transform evalRenderTransform() override; - private: TextRenderer3D* _textRenderer = nullptr; From bc772a2f9aa1ac244fd51872c7245bb8e3b6d8ed Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Sat, 21 Oct 2017 19:42:53 +0100 Subject: [PATCH 43/50] WL 21508 - Improve upon Skybox Changer --- .../resources/qml/hifi/SkyboxChanger.qml | 253 +++++++++--------- .../qml/hifi/SkyboxSelectionModel.qml | 50 ++++ 2 files changed, 177 insertions(+), 126 deletions(-) create mode 100644 interface/resources/qml/hifi/SkyboxSelectionModel.qml diff --git a/interface/resources/qml/hifi/SkyboxChanger.qml b/interface/resources/qml/hifi/SkyboxChanger.qml index a4798ba959..2aa0460e15 100644 --- a/interface/resources/qml/hifi/SkyboxChanger.qml +++ b/interface/resources/qml/hifi/SkyboxChanger.qml @@ -1,6 +1,6 @@ // -// skyboxchanger.qml -// +// SkyboxChanger.qml +// qml/hifi // // Created by Cain Kilgore on 9th August 2017 // Copyright 2017 High Fidelity, Inc. @@ -9,33 +9,97 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick.Layouts 1.3 +import QtQuick 2.5 +import "../styles-uit" +import "../controls-uit" as HifiControls -Rectangle { +Item { id: root; + HifiConstants { id: hifi; } + + property var defaultThumbnails: []; + property var defaultFulls: []; + + SkyboxSelectionModel { + id: skyboxModel; + } + + function getSkyboxes() { + var xmlhttp = new XMLHttpRequest(); + var url = "http://mpassets.highfidelity.com/5fbdbeef-1cf8-4954-811d-3d4acbba4dc9-v1/skyboxes.json"; + xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState === XMLHttpRequest.DONE && xmlhttp.status === 200) { + sortSkyboxes(xmlhttp.responseText); + } + } + xmlhttp.open("GET", url, true); + xmlhttp.send(); + } + + function sortSkyboxes(response) { + var arr = JSON.parse(response); + for (var i = 0; i < arr.length; i++) { + defaultThumbnails.push(arr[i].thumb); + defaultFulls.push(arr[i].full); + } + setDefaultSkyboxes(); + } + + function setDefaultSkyboxes() { + for (var i = 0; i < skyboxModel.count; i++) { + skyboxModel.setProperty(i, "thumbnailPath", defaultThumbnails[i]); + skyboxModel.setProperty(i, "fullSkyboxPath", defaultFulls[i]); + } + } + + function shuffle(array) { + var tmp, current, top = array.length; + if (top) { + while (--top) { + current = Math.floor(Math.random() * (top + 1)); + tmp = array[current]; + array[current] = array[top]; + array[top] = tmp; + } + } + return array; + } - color: hifi.colors.baseGray; - + function chooseRandom() { + for (var a = [], i=0; i < defaultFulls.length; ++i) { + a[i] = i; + } + + a = shuffle(a); + + for (var i = 0; i < skyboxModel.count; i++) { + skyboxModel.setProperty(i, "thumbnailPath", defaultThumbnails[a[i]]); + skyboxModel.setProperty(i, "fullSkyboxPath", defaultFulls[a[i]]); + } + } + + + Component.onCompleted: { + getSkyboxes(); + } + Item { id: titleBarContainer; - // Size width: parent.width; - height: 50; - // Anchors + height: childrenRect.height; anchors.left: parent.left; anchors.top: parent.top; - - RalewaySemiBold { + anchors.right: parent.right; + anchors.topMargin: 20; + RalewayBold { id: titleBarText; text: "Skybox Changer"; - // Text size size: hifi.fontSizes.overlayTitle; - // Anchors - anchors.fill: parent; - anchors.leftMargin: 16; - // Style + anchors.top: parent.top; + anchors.left: parent.left; + anchors.leftMargin: 40 + height: paintedHeight; color: hifi.colors.lightGrayText; - // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; } @@ -43,131 +107,68 @@ Rectangle { id: titleBarDesc; text: "Click an image to choose a new Skybox."; wrapMode: Text.Wrap - // Text size size: 14; - // Anchors - anchors.fill: parent; - anchors.top: titleBarText.bottom - anchors.leftMargin: 16; - anchors.rightMargin: 16; - // Style + anchors.top: titleBarText.bottom; + anchors.left: parent.left; + anchors.leftMargin: 40 + height: paintedHeight; color: hifi.colors.lightGrayText; - // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; - } + } + HifiControls.Button { + id: randomButton + text: "Randomize" + color: hifi.buttons.blue + colorScheme: root.colorScheme + width: 100 + anchors.right: parent.right + anchors.top: parent.top + anchors.topMargin: 5 + anchors.rightMargin: 40 + onClicked: { + chooseRandom() + } + } } - // This RowLayout could be a GridLayout instead for further expandability. - // As this SkyboxChanger task only required 6 images, implementing GridLayout wasn't necessary. - // In the future if this is to be expanded to add more Skyboxes, it might be worth changing this. - RowLayout { - id: row1 + GridView { + id: gridView + interactive: false + clip: true anchors.top: titleBarContainer.bottom - anchors.left: parent.left - anchors.leftMargin: 30 - Layout.fillWidth: true - anchors.topMargin: 30 - spacing: 10 - Image { - width: 200; height: 200 - fillMode: Image.Stretch - source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" - clip: true - id: preview1 - MouseArea { + anchors.topMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + width: 400 + height: parent.height + currentIndex: -1 + + cellWidth: 200 + cellHeight: 200 + model: skyboxModel + delegate: Item { + width: gridView.cellWidth + height: gridView.cellHeight + Item { anchors.fill: parent - onClicked: { - sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg'}); + Image { + source: thumbnailPath + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + fillMode: Image.Stretch + sourceSize.width: parent.width + sourceSize.height: parent.height + mipmap: true } } - Layout.fillWidth: true - } - Image { - width: 200; height: 200 - fillMode: Image.Stretch - source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_2.jpg" - clip: true - id: preview2 MouseArea { anchors.fill: parent onClicked: { - sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/2.png'}); - } - } - } - } - RowLayout { - id: row2 - anchors.top: row1.bottom - anchors.topMargin: 10 - anchors.left: parent.left - Layout.fillWidth: true - anchors.leftMargin: 30 - spacing: 10 - Image { - width: 200; height: 200 - fillMode: Image.Stretch - source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_3.jpg" - clip: true - id: preview3 - MouseArea { - anchors.fill: parent - onClicked: { - sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/3.jpg'}); - } - } - } - Image { - width: 200; height: 200 - fillMode: Image.Stretch - source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_4.jpg" - clip: true - id: preview4 - MouseArea { - anchors.fill: parent - onClicked: { - sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/4.jpg'}); - } - } - } - } - RowLayout { - id: row3 - anchors.top: row2.bottom - anchors.topMargin: 10 - anchors.left: parent.left - Layout.fillWidth: true - anchors.leftMargin: 30 - spacing: 10 - Image { - width: 200; height: 200 - fillMode: Image.Stretch - source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_5.jpg" - clip: true - id: preview5 - MouseArea { - anchors.fill: parent - onClicked: { - sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/5.png'}); - } - } - } - Image { - width: 200; height: 200 - fillMode: Image.Stretch - source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_6.jpg" - clip: true - id: preview6 - MouseArea { - anchors.fill: parent - onClicked: { - sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/6.jpg'}); + sendToScript({method: 'changeSkybox', url: fullSkyboxPath}); } } } } signal sendToScript(var message); - -} +} \ No newline at end of file diff --git a/interface/resources/qml/hifi/SkyboxSelectionModel.qml b/interface/resources/qml/hifi/SkyboxSelectionModel.qml new file mode 100644 index 0000000000..c88ef05432 --- /dev/null +++ b/interface/resources/qml/hifi/SkyboxSelectionModel.qml @@ -0,0 +1,50 @@ +// +// SkyboxSelectionModel.qml +// qml/hifi +// +// Created by Cain Kilgore on 21st October 2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +ListModel { + id: root; + ListElement{ + thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" + fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" + skyboxEnumValue: 1; + } + ListElement{ + thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" + fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" + skyboxEnumValue: 2; + } + ListElement{ + thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" + fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" + skyboxEnumValue: 3; + } + ListElement{ + thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" + fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" + skyboxEnumValue: 4; + } + ListElement{ + thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" + fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" + skyboxEnumValue: 5; + } + ListElement{ + thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" + fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" + skyboxEnumValue: 6; + } + + function getImagePathFromImageID(imageID) { + return (imageID ? root.get(imageID - 1).fullSkyboxPath : ""); + } +} \ No newline at end of file From cf7b050680e4be7161bc761f1ae322de042e5776 Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 21 Oct 2017 20:47:56 +0200 Subject: [PATCH 44/50] Sound cues for GoTo interface --- interface/resources/qml/hifi/Card.qml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 678245910b..fc7b8c6200 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -14,6 +14,8 @@ import Hifi 1.0 import QtQuick 2.5 import QtGraphicalEffects 1.0 +import TabletScriptingInterface 1.0 + import "toolbars" import "../styles-uit" @@ -243,9 +245,15 @@ Item { MouseArea { anchors.fill: parent; acceptedButtons: Qt.LeftButton; - onClicked: goFunction("hifi://" + hifiUrl); + onClicked: { + tabletInterface.playSound(TabletEnums.ButtonClick); + goFunction("hifi://" + hifiUrl); + } hoverEnabled: true; - onEntered: hoverThunk(); + onEntered: { + tabletInterface.playSound(TabletEnums.ButtonHover); + hoverThunk(); + } onExited: unhoverThunk(); } StateImage { @@ -261,6 +269,7 @@ Item { } } function go() { + tabletInterface.playSound(TabletEnums.ButtonClick); goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId)); } MouseArea { From 934c9479af368595279ff23cbfcbc81e937ae25b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 21 Oct 2017 20:27:35 -0700 Subject: [PATCH 45/50] implement support for Entities.callEntityServerMethod() --- .../src/scripts/EntityScriptServer.cpp | 28 ++++++++++++++++ .../src/scripts/EntityScriptServer.h | 3 ++ .../entities/src/EntityScriptingInterface.cpp | 5 +++ .../entities/src/EntityScriptingInterface.h | 33 +++++++++++++++---- .../networking/src/EntityScriptClient.cpp | 30 +++++++++++++++++ libraries/networking/src/EntityScriptClient.h | 2 ++ libraries/networking/src/udt/PacketHeaders.h | 1 + 7 files changed, 95 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 2bfcdcdf8c..2e74e889a6 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -85,6 +85,7 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket"); packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket"); packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket"); + packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket"); static const int LOG_INTERVAL = MSECS_PER_SECOND / 10; auto timer = new QTimer(this); @@ -231,6 +232,33 @@ void EntityScriptServer::pushLogs() { } } +void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointer receivedMessage, SharedNodePointer senderNode) { + + if (_entitiesScriptEngine && _entityViewer.getTree() && !_shuttingDown) { + auto entityID = QUuid::fromRfc4122(receivedMessage->read(NUM_BYTES_RFC4122_UUID)); + + quint16 methodLength; + receivedMessage->readPrimitive(&methodLength); + auto methodData = receivedMessage->read(methodLength); + auto method = QString::fromUtf8(methodData); + + quint16 paramCount; + receivedMessage->readPrimitive(¶mCount); + + QStringList params; + for (int param = 0; param < paramCount; param++) { + quint16 paramLength; + receivedMessage->readPrimitive(¶mLength); + auto paramData = receivedMessage->read(paramLength); + auto paramString = QString::fromUtf8(paramData); + params << paramString; + } + + _entitiesScriptEngine->callEntityScriptMethod(entityID, method, params); + } +} + + void EntityScriptServer::run() { // make sure we request our script once the agent connects to the domain auto nodeList = DependencyManager::get(); diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h index e6bd12460b..f9c5e921f0 100644 --- a/assignment-client/src/scripts/EntityScriptServer.h +++ b/assignment-client/src/scripts/EntityScriptServer.h @@ -54,6 +54,9 @@ private slots: void pushLogs(); + void handleEntityScriptCallMethodPacket(QSharedPointer message, SharedNodePointer senderNode); + + private: void negotiateAudioFormat(); void selectAudioFormat(const QString& selectedCodecName); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f5117dddc0..39f19323e3 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -566,6 +566,11 @@ void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, } } +void EntityScriptingInterface::callEntityServerMethod(QUuid id, const QString& method, const QStringList& params) { + PROFILE_RANGE(script_entities, __FUNCTION__); + DependencyManager::get()->callEntityServerMethod(id, method, params); +} + QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const { PROFILE_RANGE(script_entities, __FUNCTION__); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 989d3dd89d..ee5011e99d 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -188,11 +188,11 @@ public slots: */ Q_INVOKABLE void deleteEntity(QUuid entityID); - /// Allows a script to call a method on an entity's script. The method will execute in the entity script - /// engine. If the entity does not have an entity script or the method does not exist, this call will have - /// no effect. /**jsdoc - * Call a method on an entity. If it is running an entity script (specified by the `script` property) + * Call a method on an entity. Allows a script to call a method on an entity's script. + * The method will execute in the entity script engine. If the entity does not have an + * entity script or the method does not exist, this call will have no effect. + * If it is running an entity script (specified by the `script` property) * and it exposes a property with the specified name `method`, it will be called * using `params` as the list of arguments. * @@ -203,10 +203,29 @@ public slots: */ Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); - /// finds the closest model to the center point, within the radius - /// will return a EntityItemID.isKnownID = false if no models are in the radius - /// this function will not find any models in script engine contexts which don't have access to models /**jsdoc + * Call a server method on an entity. Allows a client entity script to call a method on an + * entity's server script. The method will execute in the entity server script engine. If + * the entity does not have an entity server script or the method does not exist, this call will + * have no effect. If the entity is running an entity script (specified by the `serverScripts` property) + * and it exposes a property with the specified name `method`, it will be called using `params` as + * the list of arguments. + * + * @function Entities.callEntityServerMethod + * @param {EntityID} entityID The ID of the entity to call the method on. + * @param {string} method The name of the method to call. + * @param {string[]} params The list of parameters to call the specified method with. + */ + Q_INVOKABLE void callEntityServerMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); + + /**jsdoc + * finds the closest model to the center point, within the radius + * will return a EntityItemID.isKnownID = false if no models are in the radius + * this function will not find any models in script engine contexts which don't have access to models + * @function Entities.findClosestEntity + * @param {vec3} center point + * @param {float} radius to search + * @return {EntityID} The EntityID of the entity that is closest and in the radius. */ Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const; diff --git a/libraries/networking/src/EntityScriptClient.cpp b/libraries/networking/src/EntityScriptClient.cpp index ef59ec99b8..2d47b8d8a3 100644 --- a/libraries/networking/src/EntityScriptClient.cpp +++ b/libraries/networking/src/EntityScriptClient.cpp @@ -69,6 +69,36 @@ bool EntityScriptClient::reloadServerScript(QUuid entityID) { return false; } +void EntityScriptClient::callEntityServerMethod(QUuid entityID, const QString& method, const QStringList& params) { + // Send packet to entity script server + auto nodeList = DependencyManager::get(); + SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::EntityScriptServer); + + if (entityScriptServer) { + auto packetList = NLPacketList::create(PacketType::EntityScriptCallMethod, QByteArray(), true, true); + + packetList->write(entityID.toRfc4122()); + + auto methodUtf8 = method.toUtf8(); + quint16 methodLength = methodUtf8.length(); + packetList->writePrimitive(methodLength); + packetList->write(methodUtf8); + + quint16 paramCount = params.length(); + packetList->writePrimitive(paramCount); + + foreach(const QString& param, params) { + auto paramUtf8 = param.toUtf8(); + quint16 paramLength = paramUtf8.length(); + packetList->writePrimitive(paramLength); + packetList->write(paramUtf8); + } + + nodeList->sendPacketList(std::move(packetList), *entityScriptServer); + } +} + + MessageID EntityScriptClient::getEntityServerScriptStatus(QUuid entityID, GetScriptStatusCallback callback) { auto nodeList = DependencyManager::get(); SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::EntityScriptServer); diff --git a/libraries/networking/src/EntityScriptClient.h b/libraries/networking/src/EntityScriptClient.h index 926521d9b8..6f1a0376ea 100644 --- a/libraries/networking/src/EntityScriptClient.h +++ b/libraries/networking/src/EntityScriptClient.h @@ -58,6 +58,8 @@ public: bool reloadServerScript(QUuid entityID); MessageID getEntityServerScriptStatus(QUuid entityID, GetScriptStatusCallback callback); + void callEntityServerMethod(QUuid id, const QString& method, const QStringList& params); + private slots: void handleNodeKilled(SharedNodePointer node); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index ea65f8ac78..9e2eb51fdd 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -123,6 +123,7 @@ public: ReplicatedBulkAvatarData, OctreeFileReplacementFromUrl, ChallengeOwnership, + EntityScriptCallMethod, NUM_PACKET_TYPE }; From b791dcba7f492772842bf10b4ea88cca327a2660 Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 22 Oct 2017 14:59:53 +0200 Subject: [PATCH 46/50] Add sound cues to create app --- interface/resources/qml/hifi/tablet/NewEntityButton.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/resources/qml/hifi/tablet/NewEntityButton.qml b/interface/resources/qml/hifi/tablet/NewEntityButton.qml index e5684fa791..7f838717df 100644 --- a/interface/resources/qml/hifi/tablet/NewEntityButton.qml +++ b/interface/resources/qml/hifi/tablet/NewEntityButton.qml @@ -1,5 +1,6 @@ import QtQuick 2.0 import QtGraphicalEffects 1.0 +import TabletScriptingInterface 1.0 Item { id: newEntityButton @@ -122,9 +123,11 @@ Item { hoverEnabled: true enabled: true onClicked: { + tabletInterface.playSound(TabletEnums.ButtonClick); newEntityButton.clicked(); } onEntered: { + tabletInterface.playSound(TabletEnums.ButtonHover); newEntityButton.state = "hover state"; } onExited: { From fb7f6df694ccfc6e856a33a8cc3c5189f3387203 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 22 Oct 2017 11:40:41 -0700 Subject: [PATCH 47/50] CR feedback --- assignment-client/src/scripts/EntityScriptServer.cpp | 10 ++-------- libraries/networking/src/EntityScriptClient.cpp | 10 ++-------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 2e74e889a6..c40ff3ba86 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -237,20 +237,14 @@ void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointerread(NUM_BYTES_RFC4122_UUID)); - quint16 methodLength; - receivedMessage->readPrimitive(&methodLength); - auto methodData = receivedMessage->read(methodLength); - auto method = QString::fromUtf8(methodData); + auto method = receivedMessage->readString(); quint16 paramCount; receivedMessage->readPrimitive(¶mCount); QStringList params; for (int param = 0; param < paramCount; param++) { - quint16 paramLength; - receivedMessage->readPrimitive(¶mLength); - auto paramData = receivedMessage->read(paramLength); - auto paramString = QString::fromUtf8(paramData); + auto paramString = receivedMessage->readString(); params << paramString; } diff --git a/libraries/networking/src/EntityScriptClient.cpp b/libraries/networking/src/EntityScriptClient.cpp index 2d47b8d8a3..399cb80bfa 100644 --- a/libraries/networking/src/EntityScriptClient.cpp +++ b/libraries/networking/src/EntityScriptClient.cpp @@ -79,19 +79,13 @@ void EntityScriptClient::callEntityServerMethod(QUuid entityID, const QString& m packetList->write(entityID.toRfc4122()); - auto methodUtf8 = method.toUtf8(); - quint16 methodLength = methodUtf8.length(); - packetList->writePrimitive(methodLength); - packetList->write(methodUtf8); + packetList->writeString(method); quint16 paramCount = params.length(); packetList->writePrimitive(paramCount); foreach(const QString& param, params) { - auto paramUtf8 = param.toUtf8(); - quint16 paramLength = paramUtf8.length(); - packetList->writePrimitive(paramLength); - packetList->write(paramUtf8); + packetList->writeString(param); } nodeList->sendPacketList(std::move(packetList), *entityScriptServer); From a7e21d7e76e20abde0bba92bf50681952c5dfac1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 22 Oct 2017 14:30:58 -0700 Subject: [PATCH 48/50] add remotelyCallable and remoteCallerID to provide additional security to Entities.callEntityServerMethod() --- .../src/scripts/EntityScriptServer.cpp | 2 +- .../src/EntitiesScriptEngineProvider.h | 3 +- libraries/script-engine/src/ScriptEngine.cpp | 39 ++++++++++++++++--- libraries/script-engine/src/ScriptEngine.h | 3 +- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index c40ff3ba86..0ced0a632e 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -248,7 +248,7 @@ void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointercallEntityScriptMethod(entityID, method, params); + _entitiesScriptEngine->callEntityScriptMethod(entityID, method, params, senderNode->getUUID()); } } diff --git a/libraries/entities/src/EntitiesScriptEngineProvider.h b/libraries/entities/src/EntitiesScriptEngineProvider.h index d87dd105c2..100c17df5f 100644 --- a/libraries/entities/src/EntitiesScriptEngineProvider.h +++ b/libraries/entities/src/EntitiesScriptEngineProvider.h @@ -20,7 +20,8 @@ class EntitiesScriptEngineProvider { public: - virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()) = 0; + virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, + const QStringList& params = QStringList(), const QUuid& remoteCallerID = QUuid()) = 0; virtual QFuture getLocalEntityScriptDetails(const EntityItemID& entityID) = 0; }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 348a687ae2..3df69f6f0d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -2476,7 +2476,7 @@ void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, const QUrl& doWithEnvironment(entityID, sandboxURL, operation); } -void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) { +void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params, const QUuid& remoteCallerID) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " @@ -2486,7 +2486,8 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS QMetaObject::invokeMethod(this, "callEntityScriptMethod", Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, methodName), - Q_ARG(const QStringList&, params)); + Q_ARG(const QStringList&, params), + Q_ARG(const QUuid&, remoteCallerID)); return; } #ifdef THREAD_DEBUGGING @@ -2500,13 +2501,41 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS if (isEntityScriptRunning(entityID)) { EntityScriptDetails details = _entityScripts[entityID]; QScriptValue entityScript = details.scriptObject; // previously loaded - if (entityScript.property(methodName).isFunction()) { + + // If this is a remote call, we need to check to see if the function is remotely callable + // we do this by checking for the existance of the 'remotelyCallable' property on the + // entityScript. And we confirm that the method name is included. If this fails, the + // function will not be called. + bool callAllowed = false; + if (remoteCallerID == QUuid()) { + callAllowed = true; + } else { + if (entityScript.property("remotelyCallable").isArray()) { + auto callables = entityScript.property("remotelyCallable"); + auto callableCount = callables.property("length").toInteger(); + for (int i = 0; i < callableCount; i++) { + auto callable = callables.property(i).toString(); + if (callable == methodName) { + callAllowed = true; + break; + } + } + } + if (!callAllowed) { + qDebug() << "Method [" << methodName << "] not remotely callable."; + } + } + + if (callAllowed && entityScript.property(methodName).isFunction()) { QScriptValueList args; args << entityID.toScriptValue(this); args << qScriptValueFromSequence(this, params); - callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); - } + QScriptValue oldData = this->globalObject().property("remoteCallerID"); + this->globalObject().setProperty("remoteCallerID", remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global. + callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); + this->globalObject().setProperty("remoteCallerID", oldData); + } } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 7109e0f582..db159e7265 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -198,7 +198,8 @@ public: Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap = false); // will call unload method Q_INVOKABLE void unloadAllEntityScripts(); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, - const QStringList& params = QStringList()) override; + const QStringList& params = QStringList(), + const QUuid& remoteCallerID = QUuid()) override; Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); From 44f8bf00bc65b3e913b05ad85893157b0e62b992 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 22 Oct 2017 17:14:11 -0700 Subject: [PATCH 49/50] move remoteCallerID to Script.remoteCallerID --- libraries/script-engine/src/ScriptEngine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 3df69f6f0d..98846c5213 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -2531,10 +2531,10 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS args << entityID.toScriptValue(this); args << qScriptValueFromSequence(this, params); - QScriptValue oldData = this->globalObject().property("remoteCallerID"); - this->globalObject().setProperty("remoteCallerID", remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global. + QScriptValue oldData = this->globalObject().property("Script").property("remoteCallerID"); + this->globalObject().property("Script").setProperty("remoteCallerID", remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global. callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); - this->globalObject().setProperty("remoteCallerID", oldData); + this->globalObject().property("Script").setProperty("remoteCallerID", oldData); } } } From b42afdaff01aa705c00b3bcee8aedbd829cf31f2 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 23 Oct 2017 16:27:28 +0100 Subject: [PATCH 50/50] WL 21508 - Cleanup from Code Review --- .../resources/qml/hifi/SkyboxSelectionModel.qml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/interface/resources/qml/hifi/SkyboxSelectionModel.qml b/interface/resources/qml/hifi/SkyboxSelectionModel.qml index c88ef05432..45a964fcb3 100644 --- a/interface/resources/qml/hifi/SkyboxSelectionModel.qml +++ b/interface/resources/qml/hifi/SkyboxSelectionModel.qml @@ -16,35 +16,25 @@ ListModel { ListElement{ thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" - skyboxEnumValue: 1; } ListElement{ thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" - skyboxEnumValue: 2; } ListElement{ thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" - skyboxEnumValue: 3; } ListElement{ thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" - skyboxEnumValue: 4; } ListElement{ thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" - skyboxEnumValue: 5; } ListElement{ thumbnailPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg" fullSkyboxPath: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg" - skyboxEnumValue: 6; } - - function getImagePathFromImageID(imageID) { - return (imageID ? root.get(imageID - 1).fullSkyboxPath : ""); - } -} \ No newline at end of file +}