From 4e9d162172c7c28c3d813373e1d8d4566ec164a3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 24 Feb 2019 13:23:20 +1300 Subject: [PATCH 01/29] AccountServices, HifiAbout, and WalletScriptingInterface JSDoc review --- interface/src/AboutUtil.h | 14 ++++---- interface/src/commerce/Wallet.h | 8 ++--- .../AccountServicesScriptingInterface.cpp | 4 +-- .../AccountServicesScriptingInterface.h | 32 +++++++++---------- .../src/scripting/WalletScriptingInterface.h | 22 ++++++------- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/interface/src/AboutUtil.h b/interface/src/AboutUtil.h index c06255aaa5..4a5074857d 100644 --- a/interface/src/AboutUtil.h +++ b/interface/src/AboutUtil.h @@ -16,8 +16,8 @@ #include /**jsdoc - * The HifiAbout API provides information about the version of Interface that is currently running. It also - * provides the ability to open a Web page in an Interface browser window. + * The HifiAbout API provides information about the version of Interface that is currently running. It also + * has the functionality to open a web page in an Interface browser window. * * @namespace HifiAbout * @@ -30,9 +30,9 @@ * @property {string} qtVersion - The Qt version used in Interface that is currently running. Read-only. * * @example Report build information for the version of Interface currently running. - * print("HiFi build date: " + HifiAbout.buildDate); // 11 Feb 2019 - * print("HiFi version: " + HifiAbout.buildVersion); // 0.78.0 - * print("Qt version: " + HifiAbout.qtVersion); // 5.10.1 + * print("HiFi build date: " + HifiAbout.buildDate); // Returns the build date of the version of Interface currently running on your machine. + * print("HiFi version: " + HifiAbout.buildVersion); // Returns the build version of Interface currently running on your machine. + * print("Qt version: " + HifiAbout.qtVersion); // Returns the Qt version details of the version of Interface currently running on your machine. */ class AboutUtil : public QObject { @@ -52,9 +52,9 @@ public: public slots: /**jsdoc - * Display a Web page in an Interface browser window. + * Display a web page in an Interface browser window. * @function HifiAbout.openUrl - * @param {string} url - The URL of the Web page to display. + * @param {string} url - The URL of the web page you want to view in Interface. */ void openUrl(const QString &url) const; private: diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 5e5e6c9b4f..fdd6b5e2a6 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -62,8 +62,8 @@ public: * ValueMeaningDescription * * - * 0Not logged inThe user isn't logged in. - * 1Not set upThe user's wallet isn't set up. + * 0Not logged inThe user is not logged in. + * 1Not set upThe user's wallet has not been set up. * 2Pre-existingThere is a wallet present on the server but not one * locally. * 3ConflictingThere is a wallet present on the server plus one present locally, @@ -73,8 +73,8 @@ public: * 5ReadyThe wallet is ready for use. * * - *

Wallets used to be stored locally but now they're stored on the server, unless the computer once had a wallet stored - * locally in which case the wallet may be present in both places.

+ *

Wallets used to be stored locally but now they're only stored on the server. A wallet is present in both places if + * your computer previously stored its information locally.

* @typedef {number} WalletScriptingInterface.WalletStatus */ enum WalletStatus { diff --git a/interface/src/scripting/AccountServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp index a3597886e9..5ede7c7a98 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.cpp +++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp @@ -115,8 +115,8 @@ DownloadInfoResult::DownloadInfoResult() : /**jsdoc * Information on the assets currently being downloaded and pending download. * @typedef {object} AccountServices.DownloadInfoResult - * @property {number[]} downloading - The percentage complete for each asset currently being downloaded. - * @property {number} pending - The number of assets waiting to be download. + * @property {number[]} downloading - The download percentage of each asset currently downloading. + * @property {number} pending - The number of assets pending download. */ QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result) { QScriptValue object = engine->newObject(); diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index c08181d7c9..b5c4a6e0df 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -38,19 +38,19 @@ class AccountServicesScriptingInterface : public QObject { Q_OBJECT /**jsdoc - * The AccountServices API provides functions related to user connectivity, visibility, and asset download - * progress. + * The AccountServices API provides functions that give information on user connectivity, visibility, and + * asset download progress. * * @hifi-interface * @hifi-client-entity * @hifi-avatar * * @namespace AccountServices - * @property {string} username - The user name if the user is logged in, otherwise "Unknown user". - * Read-only. + * @property {string} username - The user name of the user logged in. If there is no user logged in, it is + * "Unknown user". Read-only. * @property {boolean} loggedIn - true if the user is logged in, otherwise false. * Read-only. - * @property {string} findableBy - The user's visibility to other people:
+ * @property {string} findableBy - The user's visibility to other users:
* "none" - user appears offline.
* "friends" - user is visible only to friends.
* "connections" - user is visible to friends and connections.
@@ -74,23 +74,23 @@ public: public slots: /**jsdoc - * Get information on the progress of downloading assets in the domain. + * Gets information on the download progress of assets in the domain. * @function AccountServices.getDownloadInfo - * @returns {AccountServices.DownloadInfoResult} Information on the progress of assets download. + * @returns {AccountServices.DownloadInfoResult} Information on the download progress of assets. */ DownloadInfoResult getDownloadInfo(); /**jsdoc - * Cause a {@link AccountServices.downloadInfoChanged|downloadInfoChanged} signal to be triggered with information on the - * current progress of the download of assets in the domain. + * Triggers a {@link AccountServices.downloadInfoChanged|downloadInfoChanged} signal with information on the current + * download progress of the assets in the domain. * @function AccountServices.updateDownloadInfo */ void updateDownloadInfo(); /**jsdoc - * Check whether the user is logged in. + * Checks whether the user is logged in. * @function AccountServices.isLoggedIn - * @returns {boolean} true if the user is logged in, false otherwise. + * @returns {boolean} true if the user is logged in, false if not. * @example Report whether you are logged in. * var isLoggedIn = AccountServices.isLoggedIn(); * print("You are logged in: " + isLoggedIn); // true or false @@ -100,7 +100,7 @@ public slots: /**jsdoc * Prompts the user to log in (the login dialog is displayed) if they're not already logged in. * @function AccountServices.checkAndSignalForAccessToken - * @returns {boolean} true if the user is already logged in, false otherwise. + * @returns {boolean} true if the user is logged in, false if not. */ bool checkAndSignalForAccessToken(); @@ -140,7 +140,7 @@ signals: /**jsdoc * Triggered when the username logged in with changes, i.e., when the user logs in or out. * @function AccountServices.myUsernameChanged - * @param {string} username - The username logged in with if the user is logged in, otherwise "". + * @param {string} username - The user name of the user logged in. If there is no user logged in, it is "". * @returns {Signal} * @example Report when your username changes. * AccountServices.myUsernameChanged.connect(function (username) { @@ -150,9 +150,9 @@ signals: void myUsernameChanged(const QString& username); /**jsdoc - * Triggered when the progress of the download of assets for the domain changes. + * Triggered when the download progress of the assets in the domain changes. * @function AccountServices.downloadInfoChanged - * @param {AccountServices.DownloadInfoResult} downloadInfo - Information on the progress of assets download. + * @param {AccountServices.DownloadInfoResult} downloadInfo - Information on the download progress of assets. * @returns {Signal} */ void downloadInfoChanged(DownloadInfoResult info); @@ -186,7 +186,7 @@ signals: /**jsdoc * Triggered when the login status of the user changes. * @function AccountServices.loggedInChanged - * @param {boolean} loggedIn - true if the user is logged in, otherwise false. + * @param {boolean} loggedIn - true if the user is logged in, false if not. * @returns {Signal} * @example Report when your login status changes. * AccountServices.loggedInChanged.connect(function(loggedIn) { diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 7482b8be00..3ef9c7953a 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -41,8 +41,8 @@ public: * * @property {WalletScriptingInterface.WalletStatus} walletStatus - The status of the user's wallet. Read-only. * @property {boolean} limitedCommerce - true if Interface is running in limited commerce mode. In limited commerce - * mode, certain Interface functionality is disabled, e.g., users can't buy non-free items from the Marketplace. The Oculus - * Store version of Interface runs in limited commerce mode. Read-only. + * mode, certain Interface functionalities are disabled, e.g., users can't buy items that are not free from the Marketplace. + * The Oculus Store version of Interface runs in limited commerce mode. Read-only. */ class WalletScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -55,16 +55,16 @@ public: WalletScriptingInterface(); /**jsdoc - * Check and update the user's wallet status. + * Checks and updates the user's wallet status. * @function WalletScriptingInterface.refreshWalletStatus */ Q_INVOKABLE void refreshWalletStatus(); /**jsdoc - * Get the current status of the user's wallet. + * Gets the current status of the user's wallet. * @function WalletScriptingInterface.getWalletStatus * @returns {WalletScriptingInterface.WalletStatus} - * @example Two ways to report your wallet status. + * @example Use two methods to report your wallet's status. * print("Wallet status: " + WalletScriptingInterface.walletStatus); // Same value as next line. * print("Wallet status: " + WalletScriptingInterface.getWalletStatus()); */ @@ -74,11 +74,11 @@ public: * Check that a certified avatar entity is owned by the avatar whose entity it is. The result of the check is provided via * the {@link WalletScriptingInterface.ownershipVerificationSuccess|ownershipVerificationSuccess} and * {@link WalletScriptingInterface.ownershipVerificationFailed|ownershipVerificationFailed} signals.
- * Warning: Neither of these signals fire if the entity is not an avatar entity or it's not a certified - * entity. + * Warning: Neither of these signals are triggered if the entity is not an avatar entity or is not + * certified. * @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification - * @param {Uuid} entityID - The ID of the avatar entity to check. - * @example Check ownership of all nearby certified avatar entities. + * @param {Uuid} entityID - The avatar entity's ID. + * @example Check the ownership of all nearby certified avatar entities. * // Set up response handling. * function ownershipSuccess(entityID) { * print("Ownership test succeeded for: " + entityID); @@ -118,7 +118,7 @@ public: signals: /**jsdoc - * Triggered when the status of the user's wallet changes. + * Triggered when the user's wallet status changes. * @function WalletScriptingInterface.walletStatusChanged * @returns {Signal} * @example Report when your wallet status changes, e.g., when you log in and out. @@ -136,7 +136,7 @@ signals: void limitedCommerceChanged(); /**jsdoc - * Triggered when the user rezzes a certified entity but the user's wallet is not ready and so the certified location of the + * Triggered when the user rezzes a certified entity but the user's wallet is not ready. So the certified location of the * entity cannot be updated in the metaverse. * @function WalletScriptingInterface.walletNotSetup * @returns {Signal} From 2bcee4e454f4a727800e3d6081a9367079b319ae Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Mon, 25 Feb 2019 08:54:47 -0800 Subject: [PATCH 02/29] Update AccountServicesScriptingInterface.cpp Changes according to ctrlaltdavid's comments. --- interface/src/scripting/AccountServicesScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/AccountServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp index 5ede7c7a98..5f8fb065ff 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.cpp +++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp @@ -115,7 +115,7 @@ DownloadInfoResult::DownloadInfoResult() : /**jsdoc * Information on the assets currently being downloaded and pending download. * @typedef {object} AccountServices.DownloadInfoResult - * @property {number[]} downloading - The download percentage of each asset currently downloading. + * @property {number[]} downloading - The download percentage remaining of each asset currently downloading. * @property {number} pending - The number of assets pending download. */ QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result) { From e20f13f0ae8a17aa01ed0911a86228e94a905dea Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Mon, 25 Feb 2019 09:28:56 -0800 Subject: [PATCH 03/29] Update AccountServicesScriptingInterface.h Adding ctrlaltdavid's changes --- interface/src/scripting/AccountServicesScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index b5c4a6e0df..415b405e53 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -98,7 +98,7 @@ public slots: bool isLoggedIn(); /**jsdoc - * Prompts the user to log in (the login dialog is displayed) if they're not already logged in. + * Prompts the user to log in (with a login dialog) if they're not already logged in. The function returns if the user is logged in or not before the user completes the login dialog. * @function AccountServices.checkAndSignalForAccessToken * @returns {boolean} true if the user is logged in, false if not. */ From 650b34c463fb46da3912f6db146d5f965fdb8f84 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 25 Feb 2019 18:00:56 -0800 Subject: [PATCH 04/29] adding clock --- interface/resources/fonts/rawline-500.ttf | Bin 0 -> 262160 bytes .../resources/qml/hifi/tablet/TabletHome.qml | 143 ++++++++++++++---- .../resources/qml/styles-uit/Rawline.qml | 20 +++ interface/resources/qml/stylesUit/Rawline.qml | 20 +++ interface/src/Application.cpp | 1 + 5 files changed, 156 insertions(+), 28 deletions(-) create mode 100644 interface/resources/fonts/rawline-500.ttf create mode 100644 interface/resources/qml/styles-uit/Rawline.qml create mode 100644 interface/resources/qml/stylesUit/Rawline.qml diff --git a/interface/resources/fonts/rawline-500.ttf b/interface/resources/fonts/rawline-500.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a0e18c7364f49252d59226d0959b994a4156b3ba GIT binary patch literal 262160 zcmbS!34ByV@^Dr6dv7w6bCOIBLdZ-e2auVNWHLD*z~lsy05KdU$dO4hIUvbE4#FWI zAaaR_94ewBq9UvD1p(tNx{8R(x~{VBuIsv9tIN8({#{ogGx>hqZ{8#z{=e_{MKfL9 zU0u~(U0q#W-4g%-ARGY%FqX`jF%19^$amQQ5HPKzw9FQtGXUhf0swevTGi}1#m5h} z0QqhZ;OoCln=`Ly?vF2T0Q#rT0YYkK&zX?bd*P}10B8Y#x`uY=@GU-oQ3siUrzXuTn(I&hD00)(nyaP>SBO@uX4@Me@1^}op3-c;&oW+W- zNK%ux2|yc*|1kpI&FYDcyuq93fGGH0J2a^+NB+=HmD!=qNPvO>B1oWt0MLRC0wD;3 zAp}Ap4D=8V5fBLmh=OQ{fmj#?aWERW=Mi$NP$!s3*%rsq=5yjkPZ_d z12Q2CY>*9h$bnqQgM63>lb`@5!xWeb4k&~oD25U!g)*21(_sdbLj_dAOqd0;p$cw* zYM29aVIItf8=(ejVFBC(3tV+2N`0i!S)V=xv+VH}QzAK@pA$1#|IMl@j}nlTBJF$Gg`ERMtRn1&X(3>VRg z={Ny1FcY)ThS_Mx9L&W$%!j|iXE+fjVF6CYDL54!ScpYfj3ro#WjGCF=*Q_e1Iw`j zD{&^y!r54bH^3DbgwNp%tj0Mw7w6%8yb)`#78l@6xDXfNVqAiC=)`(#z(#ao6E?&1 z*n+LN6qmu5@D;Yfqu7oe=*H!^0z0t_yRipXVlS@3)wl-N;?1}Y*W(7f1vla*+>E#4 z7Q79&;x^olJ8&o7j=S&<+>LkQ9=r?p;y&Du2k>sZ2k*s$cpo0Z`|$yM5D()+cmyBD zqxc9u3crU>@i9DxkK=Lt75owY1Ruj+;3N11pTH-v51&FA`|$)mjVJLGp2lZz0H4L@ z@OgXzU&NR2WqbwC;H&r=+=su$*YOQJi*MpNd<)Ox+xQN?ix==W_#XZi-^btK2lydg z#NXpb_y_zW{s}+EKjSC(7yK0eil5=<_yvB6U*X^IYy1ZPj+gKs_$~eu|Aqg?@9=-{ zd;Aanfd9oG@h7~DS8xzL=*1xd1PLLO2t*_j(U1V5B{~vFf=Dn4A)zFU=t($i6f&)JQ+h0h>@5`A~BOBl1x%aDj7@0k?|yrScsLRlL;h)WRfgmBiY1G za!4-8Bl%<^nM4Z6WHN#3jiiRuk_F@@vXCqyi^&pFN1UXdG>}H(B2A>3w2)S^lq@4{q@8pSH(5?rkWSJ? zx=9aNNqWgDvYMCZgMBt zL+&Db$v(2593Xd-d&s@yAi0kmBKMOA$b;lCd59b#50j(h5%MT`j2t76ljG!9wrmGh~1~OP(XolNZQ~ka9BtMbMlpc(|9_DCQu_a(L`#d zNi>j-%sg8nwU)c$!+_G&}>phLdm#UWZNaDr|*c(R6r&PM{g^61)sA&`g>| zZ8V$OX%5Y$c{HC+q?2d?olK|DsnkIWX%Q`k|I!j#O3UapI-Sm-<+OrU(wTG?olUFg z4YZohp>ydxIv;MQH_{qfOBc|a=t8=PE{1#P5;z72;a>PBt)otO1nz=E@EGideXt*% zrS-G{UV$^Tk-BITZK17nDP2a}XglqoZn~VVpq;dfcGDiZlJ?S7bTwT=*V3EmI=Y^2 zptsPCbQ9f7Z>3x4ZFDQ$Mz_-)bSJ%??xJ_l-Skenhu%f^(tUJ4JwWfK_t1OkL3$rO zMDM2$&0h-C?vCzGcUy6_wyUYTW%cqFSBIvs-PzFT z?$9`uR4T0RbggtrPM&BA-OcU}*D{S$Ndt=;T00wh+MC*3s{$K_VWGIu-7PdUy1S*4 z24|#Vvs+W@W8(5LDOH%bcp{Xt#;!(px2Dv`&ZVSMsY1oYlfY@i3JP|6{@YZQ3R`_%(e5zGGohqMdg-@rF%u%qrcoH~gSPX$(!*IY{l}fit zWvoYvze&wyzIvCDj$O-g1IErytZ5mhvKv)5X)k*VcU7g*oMlO^P=T%v4sfwp{sZdDiRVX2@tFqb|DxS7gg+gXrg%Z+wRhDa! z(krG#9159z6-tQvs;stJ#nX1GP)PMvC?TC!W%>LnJlOmyJmy`664G;3HfTIsZzl5&enEUySp*4vA#`(LwLu!dpbK@ zjqUD6t+T1M)t;N5l^@L1rp4LT)Y;nH(jBDc+FVWDp-d548=P%ztsRVBSW|0fS9iC& z!`0Bz+TrSQ>APGF?vBQhB~9+0&hC~GCE+89w7GlR-HicU-VN>b0bC^;+UtXdmkBH8 zvS?_p*DB*-0o#uXbtZ6dJP*#LwkK_OHW6$v$LnY z&Dq0R1n_0VoBF1`p}jtg6IZ9Zp}jt0gs>En?e!YA8hJx^H=k?PQ01={XnHzYGi+Ho zI-gx)V^CHq>mg^OqJ}7AQ?rJy?oRhIS67R>v)jGW)!F7;&6qNJ*%=vpx-v5|vTQ13 z&-6oCerUKh+h1n)LpgpZ*AM0Sp?np}@pH?`^zkq8*QvZRv+df#hSi;|ZEdX$YHg;U zTBe^`mY-UdzsFfBm5eNZKePP(%<}g$%iqr|KjkbxWt*R}%}?3pr)=}f!sh2;^YgIz zdD#3sY-oTjcm# z+e;rzc0D|zU2D*lI!nFuAg$QpK`9Ba;~3po}Y4_f4q5q9(jHqd43*weja&# z9(jHqd43*wejfRL9{GMA`FWR9=}GDzD58l~-nlI-bl7bv&6F`9A*Y+Q`gQdy?rNL#AH`GV`-TYJ0ls zTbtEM4_SRx*0+W-{koFrA9bc*Ychw&KRoXIJpHs5mtsCt;`-)mOAOOmTm41--Ga}lLoKf{l3{k%*+ zudBP$$+l8eaQdEWOQQWaR}V7Uk9l=@Wbt)GAK7r7!r0;#*YyP$+we;@L33JK{H7?V zYeXh&xwFmP(6Y>58@8ZDIcBM23tMwdfuXp?wXDUt%-Pk|>g-U_3=P*SRTfZjS%`8> zRFOlJ#frDEy29(oCvl6e9oI% z8^QGdnzHZ(KQCJIv!YPnIZ&k(>N^jrdHsxQ>FQg5R^oRi{jL1&q*@X>yWZK^>1y=n z-Ph!O#~**a?--=!_49tlwddLr!@R2;DxU*j=(%2L1XJ5Me# ztMg}!BUfLikhidB7W&d6Us~)-OO!M#+n3sdi+z3>_dA0dM&`67s$-{ZR3Tjn^L)9B zrfc+rLB0qC_hN&5KDv_e)o}7^jl!$c$BX-b!LE@x%`~4o->jtC=_-G1iwXr!A9fxC zTZZ8fzwU50A%4Z-SuH#6^X_ZenO_-5rGFqg?$|5!Gu3)+y9()M`F(9&hac3kQl$+m zRA|l`rrF^K1C;{~?@6F?zEL`6o6ZaQs z=c@d)-72J;JKWuFKNzTlG8Abl@eB^Dt6G(xx~kQ@bR!p@)T<;n@1zF*)2;S{S{ANQB*bDC3e>H(Ve2ciG{00^=&y6Ckgm{wFw;5xU|8YsiH~X=LU?{Q}f#D|{IReXHIFgnkixF6g zEJn~$dmb?YNiAce=~T=f8;uI`Qr;=0l!GC>)Zd8^j_=PZZ8&qK4F`jH8%fA*)v5Wm^o84qIQ`4YCkB|a?;Xjo-mu1m64I*OEWdDj;`g-2DRMgE9bsN zRz`;1m*-wgRz?Oto@Qla2x(ZyIQzMnvs#EQpqZl+FLtVxL*mpDNXEUL5yZs&vI91tGhFZh2VxWtt(w>Ca|k@ zl?n%UxmLP5{5cmpv8cJA4i?>0$uZik9Zj5epu5B6hr4_I<-y%8oi2Y3?}3UI#Co7+ zSPxWK>487Td!Xic57bO>gS*Y$(eCbWbvrv(D>BK;&*s6Ytc=1OX}PP*)$OFc?hc{R z-O((R_H_EBS;$?$tc=1^MN~z;Mnze^yl?u7eA8Fto30|?bQN*eGApBqyOdcOMapSE zCo{_z&&bKN*|X^EmUgYHtGl(`+3ji!U>$8~?QGO`_qsXIrFD0>`1o3axS$lcE0BrC z2M6)_@MQvA?aRAYce%QQ8(UYlcD1@Yc+ftizNf9t)$LyH>U4IyJNXdoWyPeai8eL0 z(x#?1R#aA$kt58SS6M0DnBLWukuOb4@9N6T)V8L#xf|N%R#!+)R-UOOSyCg**`%c` z$(Gt!VwYC1BuDCINv_nxl02!GCHc~7MlXXlEa?`U>0J#%J^N^2AC2t8#Xg$YM>G3q zVIQsRV=4Ps#y;BEM?3rIU>|Pwv7CLZU>}|AqlSC&lit-3 z$^)dC*?eq8)2e;>qO0>+*VN}{DfM%vEBN_&SLX{0ubVHPK2O27`P!@eY__ZU*{Rq(AU3f^274XzG(iZW-GTfZ%5n{>Lr1_2leNmQW#nYou7Wd0l;@9tZ9X__1U`dRdO6xS75Wwgk~x+bekEXGEo{7RK9q%1fw76Xe*8%kxVw8kXURQf zTvb(Lk{z{iMmdi`d9}61eg)gvD37hGG5K;vIfK<_Fw9q~YK%?B9Xp&xSyxq47iW}> ztWL+E90ujo#nsi;*2c*sS=JRd$WUD)Lj^-N$-3gW3V95J##A^5!k~d+42V!)TifWY zl`*Zh)+|F+jjOiSDpQNG)F=zd&PJmw7FX5CqPa+x%tdh~Q>~13R#~u^O=gp^v0tn& zGO`*r#yF+V>{FKNN*iQ4-ei=eV&e|u4jI$>GsI+BNV%b=t}4!1U0Y+WHPsqr$DA4& z)8d#geEhAlXpuF=X(xavvXv~d#$04J%3v;X%A~$Y#s(SdWO2Mz)>w?Jt3kyL0|L|= z*$8AuT`fbYE8$%Yu$<5YL2+r(c$0s&w3e%9Pp449G_wrFvXES7EZt#tvYF%(hB!70 zvN29}_@yM%WV5qGVG;NX&E=%38rIl9w2_U1n9`YxP6X;`X^kn)Y^oh^vdY1henLv+ zMrVmt4zb8sXEe&e#pP^VMmgAAR4a$DTy>364lx(iTIEoSQ4ZrGF%AgOu*2+>LyPN- zJL-&bsJX~&mBTC*b8GsA#**43IoM@hWtH`oiW_Pw<|u`6rrIPqf|rL|`XQ`%UQK^k zSh0-GA~`gTX}L_2i~57vM+iwSk}=9`lxcERO+Qn8Sx7G0vBSvNhKx6vWpt`=oKnx4 zkYrw4D+d=(lS7K9)yd@QsrrQp><5T2m&jNw!;}+<$X8l~1^R)M&aIKd%tgjhImlcj z2byK6uESsEY(%atuY(ZWn9D-h*cgx zqB6=~xk9N-8&R3!uk2JR$5=oPPW%5DUxK9{#-v$gqop5NYO?ebmL^*IDND_keu1S) zmVS|?$(DYJr74zv4NFrk{Q)c;Yw6dr)M7Cf@a1E*80+NlI%Ba}#yX}8PPX(cOjpw_ zvNcV%jvU}Md;7z>Qkeqn?%6g%^* zvfYv%U0{`S{%@!<7B}Qs&->7kSq(y9IammMY6Uy&9x)lY%~_^$g|4%SECuz6?(E@E>f|LavifJ zjvH#8CPr$Edzz%sQMEx?p{DH{+eu4^>QwAk5LRU?yP zXPiun>zIAJwvE$lG|EDXxy+dtXO;ts%h=`=P|Ug18L!90%($3kp}3AshM4S>#h+1? zg%l^-pm-;e>l&*R3p&iJ)+$d_WnnZLWiiDk3v)r9Ri5Opk^{`FzRWz0F=ew@pw{uh zD3X?8ZcVzez-;1MtFO?=`sJGnSxT106gaq0V}tB&~+ z*9uad;wcuhF`Y@VOx73IRK-=-)))(F)BDpgGR-Pay{fi4uIj2<$5pjz+mZCHZBb~E z^U_8lsEt&HMHV?fZHLiVz;tX!-Y=udLUFpBo@SMc`H-0!rznzl$~tqAGIXZSW@CXd z-JIteP>H2KKu9iP+Rrs#mC^s3N>96;)l6MzhuZ8?Fwq{?yvdC3h7r!>tf zS2G+2%waeTn9Fb&FwgQd02HQK<@uJU0XcM|w^rRbI}7&d_8> znpIw5>Bor*PNxDgoGy;TDi}_;r5`5wsrK+3r@B%BS&Lo;Waz6b{WwWMSgn8zVT}SZ zgteA_DDcy}nddmYbqdJP*DD}H-(cy-0tMj~1!M>t6_6opvh>4bKfTR7$LZawfDC<$ z0y6a5Ed4lHLD;H*3}KrBGKB4xr?mo6J=&r)Ilv{;q^ecwHg07*YsCP(SzXhQct@>_ z%CTyBKWK`c0e}KXl3+Z`IYMqAGlW7RGhmz$r;!4Dg>zsgDJA)ml_ZM1Fu3UD$Xmj1 z3A-ie7Tqlx_T@9}_Z7k_eVY%l5ZUGb=+ZyK9 zFqGQb+FD1*9H=10#3rSYcu@)(KY&A9WMNl7fs&`iMhQwvMt&vj|Mh182H-xpPwWE_ ztN`9habW0sVd~H&uMHl9$6zn)6D z7X=3rtv08^*XuY(3M*iBsD-BMH1i?WI>wA1Jvw1@!kGBDQL!=6QHIEfaJ}y*Os|bk zLlZR_Ow=5~e$94MgvoA-uu=AF!w3@PBpE}3b=m+)&>%=cd4(KQRpUqmLMiEBnul%?1%WOBN^KP&Nw5R}CPHPL z!w7_u&47TnULU2_5o*uQ$}}gAi60%OkI-nN(lAnzGzPOhDK$4LDq5eClA6MP?AbZF zcAFt8T9cBJs*j3_j$%I;85OOUB+SXNXQ!kJ$2Na-SEu(6f2eU>x{U_YCAW5!q+EHo z^ZSAMnXjxMI#Pf8rdjEO`!^iexdT`1duZ=Zl(l&7YBFa@eJZ}MT{EbqD_R@c(m;gDp-bW(@iovwD8xZB zWI(VlxOJX#}Ym2;rj1uKqw2R7e2FmUVUE;5BA*LdFaypJ^<<&|yi6_ss& z8rXd6Gtb<*87KKlgwa1f)pxmX@FF=uUaj!l;K>t?RIglBHD~4O!8yF@qoX{_^#K~A$>xi&g5>1wDYqlvZF>RD3F)TnElV&ofO6J5Qdv;DvPA*ph zTUL}IQj#PM=Gbjv8szec&dJG6p;@^oF=>e{|G4Mm;9K7RvCdoKXujdY!w23;Td;UK zc2^x(=lT1BUPs;9u4U6sZ}B!RUGeGiQDo?#KLf znEQ_#r`-E5?Q1Re+8wuR;jEek!mKUU`MG!Bojc#U#kPGr>sJ$QCEp6E5D2M` zBuQlZ3Lpg+APS2>5OTO=0wIuFH;pzn4Gm_!S#P#815CdC?&x>l9i^sy-peTML&j5pYI@98y)|2&YH@h#P!c23IdVD#YvpZw_Q4?Z}p$a6D(OguX_8GplF?~Taqi}I> z+&TJz*mt>F>;nRILzl#4u@7Rv3MmdVUtfHi6p#>g<&26=Hpf~=S<{k5ZB$wWTPbXj zuphoqBn`Kpn8hmW*(oW^rbv>3A=Kq8-Sg{?`o7Iei_AGo@BDQ~_gi-~Ws-(DyPYM* zi7iK+&Bq$;G|zREywm#ri+9a0T)yYg$E%k9?)g3Qku2Q*(%as#GgjVxUxn-3qs<*p zzPh;ijbqJ#Py;|0h%bR2G9Bq6Qi0n-f|N9aAlND-kx6*5Uj};6C+Q>fTr-jlCc8;5 zNoG6maIQ^H7kI1~o$~(7adorHQpmv3qeq2>_O-{x=1v+bT>cU`C81nA1L)t7WW(3Cot9latHD!sKF3lr*V4!=Y}dPuq9pPhwHJLZgm^kl^owQ@++94E|CymGE^dr8O{8(ke~nw#^ZR6HZo02bg3p*AbY{+3g9n=9x=AUw87PXCWcj z^$2Z_wL!?0V-N@0axaElV*7 z(>s^6t=Z-+_b&AQ*(-1Pa>v$BH+i<>sPpG{-SIY)a}lt0)d!;A&haS(Lb6GvULQua z(P?P1o6to$|nb^XN z6_S5;rpx)ip6Tp113S}gK5*awo%-dMgJ-_@0toCHT7bEno(`NRwMbMCAYMX&5Wy{~ zepe22Jjo(OE8`raS-o)U^{Z#oWrHK;*94fw!aW41EAZ*Gmr{tP~gb-?T{k>`#XfLAF)+ZDlGC@=&@q~ z#-@!;8$S+GAvM{;&t}=a{c^;HIbyd|c1lU&^NFc9+!RtZp)@sqUvvEt%c`79J7(?f zDUBarIXNpKHYX-oAH4S-lsk`3)TYPh=ghWttY1E-YV94Fiw`;r8cPaON9#k9V$;(L zQ@mB*e#^(P*!zK0F1!yn!7^|;>di=nL?kp0b!1U63DnI%4T$CCsKFVa1xe6K?CeE} zK%0SpE|BP&*WE8g(+@T;z=0t(;q9|KdZZtc`q!6{W5EYJg#TVn} zyWE@{sayz+nJ{I^#H_O9aAC?b&38RwEq5)=U%x&&r(ya7>&6`#C|WknK3XqIhP1>9 zFKFerLu0N8=wkb+`B1b1SR&y!=`3-Kc+ejhATVF3s)U=f26g za7H$sH5R&R78Rw9$(xv3c*CK!QyLuO^%JJI&P!X|*cxXkExoB?!uQ$5jxiJGuV_d= zgHxV)%6sdLLY#r6Z;?(V!*_FCLOZ91xNtjR9qTVqFBCN zWf9v|vY~QRY*-jVd|a$)lqoDGEIL9j5D3Lktu`{vWYU|mxKpIKMyNL_J3s`hVCqim zJhRCA^@nD;>(}l1uV)TksN1x9q1E$Wyxe&ECb7@`*rJYS(~{rV`p9p^5O3TrSM%+j zE#BaLjZOR6URpKuy*Noc4av+Wj*b$jKoHBhE5|}`ikr$pC==P5BPHr?;Outf5;RTEaU*B-A| zZl1LA;ZMcWPu{t>s3B)m${TfY9K;$e>8jf!4(0b2hz+X|^E@OnNT29D6qO;g7$>aHBz?|FC-?MzNAwz^&HgFtRIDF`AJTtEnm zN^Q9QVvWdQ|K?>K%8lSEtf?D|tm2r;5>mZesG+*z4JU(0lp5FU~1*Ax`CU zk?KeS0T2XqvGYB(Rj|P7&*uR3X1z&o*4y+Zy*PDX@RNZ7YGO;69Plg^`#d{Ik3tul z)blVm3qDbdI1jVPK;Hmss+^HxhrXsIVjmbF!7>K%k&|QB z+w^|(ojSek$jr8~)X-FJHAtW-vIgctY?67?XQKc+`s01yT1Wbm$0h%WEUH;*y(l@C}?1Lak zccf_rWGBONV1JPda6O}7s}Px4Q6dpR5EQNt4`&K&(xaJ8jSbPJ7w_>JmwU$yyhcJu z*coB|;Bh+t%5kx8u!_n|+I$U)Yxx=(%&jK17wn|b%e!4Cnd&>inhd9QWGOJ?8p{LG|?=un!@)ERoIx7 zn`(<@CVQlq#pBJ3pE=bef50W|m$_%|y=z}R=`JW7A?*+D^#0KDe(y$H_bzI0S4Hnx zu<)~uv)^pmkHV3Lhu$6`b5Zc^;WkHx4hf4ADXxNxoduN(A&IK`91|5D2B8qDk1?@9 zD6;1#HZ-Fmeip?s^%3`?$5s?Sz76Z%^Pco>z-QgN9$f03EB4*6`LX#2|HtfkpXk_3 zJbm*Tb~g?PlZL(%4v7Ph4dWfD*`s2qFMy<;w0yy~iCAgN95Xs5Dp6noJH8z&{+;hO zf&~i9#?;&>K?x)9AOH15j?EY?2)Q4vedOVY^xV3|Gx%-99bX^xJ~3D`17q&H|5uR- z1qIIeMf~1k+c)>1{%v91l-iqDq!whDO)+|Y2{Ciqb93H4G_z+#n|Y#jY`if#Ou4bx z_PX;?KrjOcXSoj+=ZIlOnl4iAWw=>j9v5G@%t)K;CgH4CH{jI?M@8M`A4Hum9(8c& z2XTei2MO#Zjd^VRVh*^YGLglcVq?NW0yL0-3CukanRA@N%`FQmGT(%|z^eVF($)`m zE=nC3HPv}f-L&4?()9S?XU8wOr~UExpiHgQ&@iW@1EPj z0}eV#|j0#Aa+ zM#?RM6=ij0bq6*H3AF3{ajiB5D-2rExlQfU*NO&!a2{y0nbNbpGQZ2 z7;N->OQPxBtnVxa^qT4~u|PZ^C8V6qjXxU1RV9IZDQ%zB&Q2~6nit(K|-(Ip*~!9b^n0vxV(Qn+TiOSVSj2EUBIIA?D~MR{=s$nXSeCic9VW_Z!acP?-Qf; z{q!ok7b5LKG}rIceyOp=P-ngLksB^?n|X`n*ZzF1|u15HyJik;V8S_0EBws6Wk?U1PU>{eK1>b?1rd2 zjNK3MPS~O~^(R($ix)49{HEyzDi>-apob`n)cC%;bk|2v0K@SPA(4id_%l>e) zzyhi4`ZW#^?G@m=f-O*@89pa1&XI6!ExUD9BZ!k}WvxS-oSc%#4mqRJlG*Cf^I!(w z#}ZR8n{RO%J-6wYdHB?U|Lppt>(;M#t+_yU-uQ51$J4fm^RD}Ud+wZg(X;WZ!-ISF z5AI)$PqseOc}JV~ymQw-l>N1Q=#pR)ra~M$`^Lru1+sX3xw@?uC>O`<6wEI)q9VgW zv>J%RICXPjK@%4B)~H*qEsLLi$J@EE>dlx+xD`#Aeyw`i+ z?w=0qT%B7n@2`&^`=-9c-n*amg|Pd2;ZgA-jDl2Xa0HKzi_{224BietAc z0VPqNlEOo>Y&?}~|FYxeccz>gm5@E!UN!Z1-Fq%=q-`m0zWwmL4-UHCB9V7{?%vo$ zHeCK8F#&f5%t&9*)O+us4s9)WbwAVm@ZatP!sWbKmGeHKHL>W8we*{c#BXUM70pd@8W?93Gjk@|=TvtA#d9hJt6sm;z; zrzRy)G8ov=|NLEd$#-vBYArmm1mnG*(Zjo~#{ml%;ld{Ny7nP>=LxeS|qo)W)Wn%zEWolppAs z|Ed=;b?szIR;xw#&}$0sOC| zt{e2W8K~Z--*f8JDdC4tKfR(8tXJM=b2b$2-HdZNP2Y_&q0D@MK!gT=oZrr7D2G-8 z=tF}71Ek?cV|JSXc~C&{NX?0dPkLUOo4uf<<>VW;9bmkE`fT+C*9>9d6?y;bK3TCb z1hVshPD6MElf~+MgW#Tt-ie|U~0_G=wz%+ z3Qsk&(^PnvLYu!CFej!Y8GZL7VdYzYI(XmZZL@EGd*%HYz_TKULjjKN1vEd!k^})f$^O4pU zmo_~-?|si|@zZmSz4s07xyN&0JwEyHH+zWp`QAv76pH*!DPy-tRfEy7Bzw1hVz3`v#-XRZe5+%L?)r-MH31{_{wA!{PZ=QRY!Gp%H-wO{}eK(Ukqi_N?0&J0pM0gal)xK5leg zZtcVazf$a%ZRk7loVW)JaN{WhO5C=Xt7`a4u~jguOct?cN;&*RDBFAah5cxtgl_(Y z>RN}+%wp~lCgwRMb3^BQB*q?waQjD|ySu%0+g*2^{O3Puv8}qoUc91%T-b$;-b1@Q zEqQrXK-fBTNoW_WFbZyS1R4SeLMRHzws-b&HG)M&6f-w`Mo=Cc{H!XQA2!&yepbcK zA&zK;7C%KOMpb#$$=;xAnGcxAeQWk^IgB~2BfLQ0@nP%9&zGN>a564o+&KFT6v{5` zeC$blf^760@eb@>O&%CL%+3bV)Cuc)H+=>OCieDRuqyEo_U=syPP6f-5o8IZaQ^Jg z#P%W^;_KdpFJKi`$=-#6b#MdS$oEx*XaH%gU{&KG!8$E}W>78)lrj{B zpmq*3pWFydp_S(F2otmD5&Vduh>vZ!sjrpAWERcv7QBQlSa!nmW5Uk1vFYBA1D3ra zSO>S$-pkL;cJceA$pG{T!K&OZ{XAacdkz8og}WL?AC8wyCPzFg=mJkYIXt+hhuSyy z44&W2_r|)RGdx}r2TM-tDf?$Qe78$+Km_)@ieKXcI{8Cu#=y=L)a0+FA_|1vrLp*m z^3IV3b7s~$f&m~7;*w+Z(fl@6AIa`O*^=XL{ZmrySvhunc8<+vzy*`y$E^4=JU$~O zbwWbemtu`KXlBl+@&wNhd*h8H%~~dHo}EG>P4O)Da?JY!mPj_B5bcQM5e=qMETExA zUf9AocF6mKX7Z1(DRyP#IAe5#a>K&n;c@ISwSaUzdk{wnA14nI`^Kr(g2=*af(s}i z{QC?nSVPJ1IQ6Mf{8lf*&fXI1Bcp5vN#fsaVDC}ciy*tc?#oc)gw)gtWAtCHh#xaa ztl1ZD@?1)py;&+v_k3WC-%E7H8KZJ$;`eM=Tks3|5;@QA|5&_4IY!12iLj6nf8a$a z-pG9z5h>Ey?8=X@bD6N7zXH21QmrF`z3z*NiqHq?BmvB5W)FK<^aa_$Oo_AFZTyal zKTzNgJ0*jezX8P_%aXDMsbjJd4BDk3DfXM}6B?(@PaK^wIwD|MXma*^ay~gNIWpcD zC1@-~1!+lXNzq1gGzrQqnKWMUMdYE&*eIS>-}Ep&}^Q-0$>4YM29E^cvcKTzUuQ4GeIx;L21C+obdoUn4RX2;DRwpyJ z9>&0!Sc85P(@~MVonS{){+$5dmjZlO;*!}w_52|?v2Xii)rybzEZF<|$$eut&z}8Y zWPfb)%^UI--1g98KYSi|^xvCy{_9BCrAxwXt8%B$6bJvISx{(qv^vax#qCtoe|6`M zKd||2ANrJ@6$ii$n;enxqX`u!pf-TLNzw+eXIbSHa#mH1W1NIkgCv0Rvo=3eARJ6%pVt$>5MIUTg^q_Tl_vFtUJ>AyL%Y(i;88jt+^k;{= z-Ys%vJy5-HUCQh*?C`!Ea8t+Z8TkY0Wk*^)Nje5NsL!p@kk$xiFu3tf#JRdpK{ zW;O0M?@Y~XJiXG8%;vKmex#R#UjE&O0!RKRiR~S+hCq!#D0`76us5u6B7x}zbVB`?}cZ*@1wQw(sA$Nb2xx6o~Odv z-u$)RBe;0&l+Lx}I19J)xWsYq4_L-+r3Nw_R)K{dD1u95ktsS)U2v3K@4*-g*z|!~w8D8I(DS6HP=Ero<3Q%+4Z23eXTFLOBo#C4%eO zzsE*I7JFr?-{60X@6G7Bs*QW5e|xfL+k?)1@#Bi}l3(~!*7*FM7g{gx*=gK5yKdJ%XYanb+!$Lk z1Tpc}imLH96c`dP%tUoDoCPodJup; zfd^3%#Af!--cd=@%)fYdqwgUs#o5Wp$zxMe$C=qdo3rxY&>A9%q}+!m`=6JE3%0DJ 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

G#Mh;)ywUqNlIcrQ=}Lr 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 GKls0o2tK3$ literal 0 HcmV?d00001 diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 4d7883522e..94a65e70a0 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -6,7 +6,7 @@ import QtQuick.Layouts 1.3 import TabletScriptingInterface 1.0 import "." -import stylesUit 1.0 +import stylesUit 1.0 as HifiStylesUit import "../audio" as HifiAudio Item { @@ -49,44 +49,131 @@ Item { } Item { - width: 150 - height: 50 + id: rightContainer + width: clockItem.width > loginItem.width ? clockItem.width + clockAmPmTextMetrics.width : + loginItem.width + clockAmPmTextMetrics.width + height: parent.height + anchors.top: parent.top + anchors.topMargin: 15 anchors.right: parent.right - anchors.rightMargin: 30 - anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 20 + anchors.bottom: parent.bottom - ColumnLayout { - anchors.fill: parent + function timeChanged() { + var date = new Date(); + clockTime.text = date.toLocaleTimeString(Qt.locale("en_US"), "h:mm ap"); + var regex = /[\sa-zA-z]+/; + clockTime.text = clockTime.text.replace(regex, ""); + clockAmPm.text = date.toLocaleTimeString(Qt.locale("en_US"), "ap"); + } - RalewaySemiBold { - text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in") - horizontalAlignment: Text.AlignRight - Layout.alignment: Qt.AlignRight - font.pixelSize: 20 - color: "#afafaf" + Timer { + interval: 1000; running: true; repeat: true; + onTriggered: rightContainer.timeChanged(); + } + + Item { + id: clockAmPmItem + width: clockAmPmTextMetrics.width + height: clockAmPmTextMetrics.height + + anchors.top: parent.top + anchors.right: parent.right + TextMetrics { + id: clockAmPmTextMetrics + text: clockAmPm.text + font: clockAmPm.font } - - RalewaySemiBold { - visible: Account.loggedIn - height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0 - text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : "" - horizontalAlignment: Text.AlignRight - Layout.alignment: Qt.AlignRight - font.pixelSize: 20 + Text { + anchors.left: parent.left + id: clockAmPm + anchors.right: parent.right + font.capitalization: Font.AllUppercase + font.pixelSize: 12 + font.family: "Rawline" color: "#afafaf" } } - MouseArea { - anchors.fill: parent - onClicked: { - if (!Account.loggedIn) { - DialogsManager.showLoginDialog() - } else { - Account.logOut() + Item { + id: clockItem + width: clockTimeTextMetrics.width + height: clockTimeTextMetrics.height + anchors { + top: parent.top + topMargin: -10 + right: clockAmPmItem.left + rightMargin: 5 + } + TextMetrics { + id: clockTimeTextMetrics + text: clockTime.text + font: clockTime.font + } + Text { + anchors.top: parent.top + anchors.right: parent.right + id: clockTime + font.bold: false + font.pixelSize: 36 + font.family: "Rawline" + color: "#afafaf" + } + } + + Item { + id: loginItem + width: loginTextMetrics.width + height: loginTextMetrics.height + anchors { + bottom: parent.bottom + bottomMargin: 10 + right: clockAmPmItem.left + rightMargin: 5 + } + Text { + id: loginText + anchors.right: parent.right + visible: !Account.loggedIn + text: qsTr("Log in") + horizontalAlignment: Text.AlignRight + Layout.alignment: Qt.AlignRight + font.pixelSize: 18 + font.family: "Rawline" + color: "#afafaf" + } + TextMetrics { + id: loginTextMetrics + text: Account.loggedIn ? tabletRoot.usernameShort : loginText.text + font: loginText.font + } + + HifiStylesUit.ShortcutText { + anchors.right: parent.right + visible: Account.loggedIn + text: "" + tabletRoot.usernameShort + "" + horizontalAlignment: Text.AlignRight + Layout.alignment: Qt.AlignRight + font.pixelSize: 18 + font.bold: true + font.family: "Rawline" + linkColor: hifi.colors.blueAccent + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (!Account.loggedIn) { + DialogsManager.showLoginDialog(); + } else { + Account.logOut(); + } } } } + Component.onCompleted: { + rightContainer.timeChanged(); + } } } diff --git a/interface/resources/qml/styles-uit/Rawline.qml b/interface/resources/qml/styles-uit/Rawline.qml new file mode 100644 index 0000000000..50c6544739 --- /dev/null +++ b/interface/resources/qml/styles-uit/Rawline.qml @@ -0,0 +1,20 @@ +// +// Rawline.qml +// +// Created by Wayne Chen on 25 Feb 2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +Text { + id: root + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "Rawline" +} diff --git a/interface/resources/qml/stylesUit/Rawline.qml b/interface/resources/qml/stylesUit/Rawline.qml new file mode 100644 index 0000000000..50c6544739 --- /dev/null +++ b/interface/resources/qml/stylesUit/Rawline.qml @@ -0,0 +1,20 @@ +// +// Rawline.qml +// +// Created by Wayne Chen on 25 Feb 2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +Text { + id: root + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: "Rawline" +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 83b287b7ae..a850cdfe12 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1075,6 +1075,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/FiraSans-SemiBold.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Light.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Regular.ttf"); + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/rawline-500.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Bold.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-SemiBold.ttf"); QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Cairo-SemiBold.ttf"); From 4825ecdbf35c78b624c739128a790f147530e52c Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Mon, 25 Feb 2019 22:44:16 -0800 Subject: [PATCH 05/29] Update AccountServicesScriptingInterface.h --- interface/src/scripting/AccountServicesScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index 415b405e53..1555caba3c 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -98,7 +98,7 @@ public slots: bool isLoggedIn(); /**jsdoc - * Prompts the user to log in (with a login dialog) if they're not already logged in. The function returns if the user is logged in or not before the user completes the login dialog. + * Prompts the user to log in (with a login dialog) if they're not already logged in. The function returns a value before the user completes the login dialog. * @function AccountServices.checkAndSignalForAccessToken * @returns {boolean} true if the user is logged in, false if not. */ From 736a050e6808ff0913e819558937d3b06d8d9e3c Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Mon, 25 Feb 2019 22:55:56 -0800 Subject: [PATCH 06/29] Update AccountServicesScriptingInterface.h --- interface/src/scripting/AccountServicesScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index 1555caba3c..b5bee6a7e4 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -98,7 +98,7 @@ public slots: bool isLoggedIn(); /**jsdoc - * Prompts the user to log in (with a login dialog) if they're not already logged in. The function returns a value before the user completes the login dialog. + * The function returns the log in status of the user and prompts the user to log in (with a login dialog) if they're not already logged in. * @function AccountServices.checkAndSignalForAccessToken * @returns {boolean} true if the user is logged in, false if not. */ From ba3895efdd942ed40d24ad5134b23d5bc05c4756 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Mon, 25 Feb 2019 22:56:36 -0800 Subject: [PATCH 07/29] Update AccountServicesScriptingInterface.h Minor copy-edit --- interface/src/scripting/AccountServicesScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index b5bee6a7e4..b188b4e63b 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -98,7 +98,7 @@ public slots: bool isLoggedIn(); /**jsdoc - * The function returns the log in status of the user and prompts the user to log in (with a login dialog) if they're not already logged in. + * The function returns the login status of the user and prompts the user to log in (with a login dialog) if they're not already logged in. * @function AccountServices.checkAndSignalForAccessToken * @returns {boolean} true if the user is logged in, false if not. */ From db1c78246fc640971ea55f273a1132ac4966e2ce Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 26 Feb 2019 11:32:43 -0700 Subject: [PATCH 08/29] Read and apply the FBX upVector parameter --- libraries/fbx/src/FBXSerializer.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index 9e7f422b40..3c8aa8f799 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -167,7 +167,6 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen } } } - return globalTransform; } @@ -436,6 +435,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr hfmModel.originalURL = url; float unitScaleFactor = 1.0f; + glm::quat upAxisZRotation; + bool applyUpAxisZRotation = false; glm::vec3 ambientColor; QString hifiGlobalNodeID; unsigned int meshIndex = 0; @@ -473,11 +474,20 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr if (subobject.name == propertyName) { static const QVariant UNIT_SCALE_FACTOR = QByteArray("UnitScaleFactor"); static const QVariant AMBIENT_COLOR = QByteArray("AmbientColor"); + static const QVariant UP_AXIS = QByteArray("UpAxis"); const auto& subpropName = subobject.properties.at(0); if (subpropName == UNIT_SCALE_FACTOR) { unitScaleFactor = subobject.properties.at(index).toFloat(); } else if (subpropName == AMBIENT_COLOR) { ambientColor = getVec3(subobject.properties, index); + } else if (subpropName == UP_AXIS) { + constexpr int UP_AXIS_Y = 1; + constexpr int UP_AXIS_Z = 2; + int upAxis = subobject.properties.at(index).toInt(); + if (upAxis == UP_AXIS_Z) { + upAxisZRotation = glm::angleAxis(glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + applyUpAxisZRotation = true; + } } } } @@ -1271,7 +1281,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint); glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; - if (joint.parentIndex == -1) { joint.transform = hfmModel.offset * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; @@ -1664,6 +1673,9 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } } + if (applyUpAxisZRotation) { + hfmModelPtr->bindExtents.rotate(upAxisZRotation); + } return hfmModelPtr; } From 028cce53949035ebb7611cbf1327426acb8803ef Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 26 Feb 2019 12:26:50 -0800 Subject: [PATCH 09/29] change short username to 14 characters, displaying as normal text --- .../resources/qml/hifi/tablet/TabletHome.qml | 19 ++----------------- .../resources/qml/hifi/tablet/TabletRoot.qml | 6 +++--- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 94a65e70a0..a1da69a44a 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -134,8 +134,7 @@ Item { Text { id: loginText anchors.right: parent.right - visible: !Account.loggedIn - text: qsTr("Log in") + text: Account.loggedIn ? tabletRoot.usernameShort : qsTr("Log in") horizontalAlignment: Text.AlignRight Layout.alignment: Qt.AlignRight font.pixelSize: 18 @@ -144,29 +143,15 @@ Item { } TextMetrics { id: loginTextMetrics - text: Account.loggedIn ? tabletRoot.usernameShort : loginText.text + text: loginText.text font: loginText.font } - HifiStylesUit.ShortcutText { - anchors.right: parent.right - visible: Account.loggedIn - text: "" + tabletRoot.usernameShort + "" - horizontalAlignment: Text.AlignRight - Layout.alignment: Qt.AlignRight - font.pixelSize: 18 - font.bold: true - font.family: "Rawline" - linkColor: hifi.colors.blueAccent - } - MouseArea { anchors.fill: parent onClicked: { if (!Account.loggedIn) { DialogsManager.showLoginDialog(); - } else { - Account.logOut(); } } } diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 93a23f1b9d..8d237d146a 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -178,10 +178,10 @@ Rectangle { function setUsername(newUsername) { username = newUsername; - usernameShort = newUsername.substring(0, 8); + usernameShort = newUsername.substring(0, 14); - if (newUsername.length > 8) { - usernameShort = usernameShort + "..." + if (newUsername.length > 14) { + usernameShort = usernameShort + "..." } } From f47ec099273548a1300be6699571727bc29aaf32 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 26 Feb 2019 14:01:30 -0700 Subject: [PATCH 10/29] use UP_AXIS_Y --- libraries/fbx/src/FBXSerializer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index 3c8aa8f799..0b31eda94b 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -484,7 +484,9 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr constexpr int UP_AXIS_Y = 1; constexpr int UP_AXIS_Z = 2; int upAxis = subobject.properties.at(index).toInt(); - if (upAxis == UP_AXIS_Z) { + if (upAxis == UP_AXIS_Y) { + // No update necessary, y up is the default + } else if (upAxis == UP_AXIS_Z) { upAxisZRotation = glm::angleAxis(glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); applyUpAxisZRotation = true; } From 7039ee471d217a3812586025c2e71498bddcc5de Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 26 Feb 2019 13:49:55 -0800 Subject: [PATCH 11/29] Move newly-created Connection to NodeList thread --- libraries/networking/src/udt/Connection.cpp | 1 - libraries/networking/src/udt/Socket.cpp | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 7ab2296935..418dc8f417 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -31,7 +31,6 @@ using namespace udt; using namespace std::chrono; Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::unique_ptr congestionControl) : - QObject(parentSocket), _parentSocket(parentSocket), _destination(destination), _congestionControl(move(congestionControl)) diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 358acce694..7829e3727c 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -251,7 +251,10 @@ Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr, bool fi auto congestionControl = _ccFactory->create(); congestionControl->setMaxBandwidth(_maxBandwidth); auto connection = std::unique_ptr(new Connection(this, sockAddr, std::move(congestionControl))); - + if (QThread::currentThread() != thread()) { + qCDebug(networking) << "Moving new Connection to NodeList thread"; + connection->moveToThread(thread()); + } // allow higher-level classes to find out when connections have completed a handshake QObject::connect(connection.get(), &Connection::receiverHandshakeRequestComplete, this, &Socket::clientHandshakeRequestComplete); From ff7995ae18a9e6262fbb58167912ce9240f9b586 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 26 Feb 2019 18:29:55 -0700 Subject: [PATCH 12/29] Right fix --- libraries/fbx/src/FBXSerializer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index 0b31eda94b..e022ca8921 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -1281,7 +1281,9 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr joint.geometricScaling = fbxModel.geometricScaling; joint.isSkeletonJoint = fbxModel.isLimbNode; hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint); - + if (applyUpAxisZRotation && joint.parentIndex == -1 && !joint.isSkeletonJoint) { + joint.rotation *= upAxisZRotation; + } glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; if (joint.parentIndex == -1) { joint.transform = hfmModel.offset * glm::translate(joint.translation) * joint.preTransform * @@ -1676,6 +1678,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } if (applyUpAxisZRotation) { + hfmModelPtr->meshExtents.rotate(upAxisZRotation); hfmModelPtr->bindExtents.rotate(upAxisZRotation); } return hfmModelPtr; From 217145f4c562c78785715d8068cd9179cd162b93 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 22 Feb 2019 17:16:50 -0800 Subject: [PATCH 13/29] This pr addresses two issues related to avatars that have parents joints above their hip joints. First on the IK side this prevents parent joints from being included in the accumulators in AnimInverseKinematics. Second in AnimClip the boneLengthScale now takes into account translation and scale on these extra parent joints. --- libraries/animation/src/AnimClip.cpp | 41 ++++++++++++++++++- .../animation/src/AnimInverseKinematics.cpp | 6 +-- libraries/animation/src/AnimSkeleton.cpp | 3 ++ libraries/animation/src/AnimSkeleton.h | 2 + 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index a35e0237d0..109be27b58 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -151,12 +151,49 @@ void AnimClip::copyFromNetworkAnim() { const glm::vec3& animZeroTrans = animModel.animationFrames[0].translations[animJointIndex]; float boneLengthScale = 1.0f; const float EPSILON = 0.0001f; - if (fabsf(glm::length(animZeroTrans)) > EPSILON) { - boneLengthScale = glm::length(avatarDefaultPose.trans()) / glm::length(animZeroTrans); + + const int avatarHipsParentIndex = avatarSkeleton->getParentIndex(avatarSkeleton->nameToJointIndex("Hips")); + if (avatarJointIndex == avatarSkeleton->nameToJointIndex("Hips") && (avatarHipsParentIndex >= 0)) { + + const AnimPose& animHipsAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animSkeleton.nameToJointIndex("Hips")); + const AnimPose& avatarHipsAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarSkeleton->nameToJointIndex("Hips")); + const AnimPose& avatarHipsParentAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsParentIndex); + + // the get the units and the heights for the animation and the avatar + const float animationUnitScale = extractScale(animModel.offset).y; + const float avatarUnitScale = extractScale(avatarSkeleton->getGeometryOffset()).y; + const float avatarHeightInMeters = avatarUnitScale * avatarHipsAbsoluteDefaultPose.trans().y; + const float animHeightInMeters = animationUnitScale * animHipsAbsoluteDefaultPose.trans().y; + + // get the parent scales for the avatar and the animation + const float avatarHipsParentScale = avatarHipsParentAbsoluteDefaultPose.scale().y; + float animHipsParentScale = 1.0f; + const int animHipsParentIndex = animSkeleton.getParentIndex(animSkeleton.nameToJointIndex("Hips")); + // also, check to see if the animation hips have a scaled parent. + if (animHipsParentIndex >= 0) { + const AnimPose& animationHipsParentAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animHipsParentIndex); + animHipsParentScale = animationHipsParentAbsoluteDefaultPose.scale().y; + } + + // compute the ratios for the units, the heights in meters, and the parent scales + if ((fabsf(animHeightInMeters) > EPSILON) && (animationUnitScale > EPSILON) && (animHipsParentScale > EPSILON)) { + const float avatarToAnimationHeightRatio = avatarHeightInMeters / animHeightInMeters; + const float unitsRatio = 1.0f / (avatarUnitScale / animationUnitScale); + const float parentScaleRatio = 1.0f / (avatarHipsParentScale / animHipsParentScale); + + boneLengthScale = avatarToAnimationHeightRatio * unitsRatio * parentScaleRatio; + } + } else { + + if (fabsf(glm::length(animZeroTrans)) > EPSILON) { + boneLengthScale = glm::length(avatarDefaultPose.trans()) / glm::length(animZeroTrans); + } } + AnimPose animTransPose = AnimPose(glm::vec3(1.0f), glm::quat(), avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans)); _anim[frame][avatarJointIndex] = animTransPose * animPreRotPose * animRotPose * animPostRotPose; + } } } diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index d710e9d8ff..8da2ddde3e 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -298,10 +298,8 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< } // harvest accumulated rotations and apply the average - for (int i = 0; i < (int)_relativePoses.size(); ++i) { - if (i == _hipsIndex) { - continue; // don't apply accumulators to hips - } + // don't apply accumulators to hips, or parents of hips + for (int i = (_hipsIndex+1); i < (int)_relativePoses.size(); ++i) { if (_rotationAccumulators[i].size() > 0) { _relativePoses[i].rot() = _rotationAccumulators[i].getAverage(); _rotationAccumulators[i].clear(); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 03e3ac6ebd..f7b5fa8c83 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -17,6 +17,9 @@ #include "AnimationLogging.h" AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { + + _geometryOffset = hfmModel.offset; + // convert to std::vector of joints std::vector joints; joints.reserve(hfmModel.joints.size()); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 0eefbf973e..dcb35ac9cb 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -36,6 +36,7 @@ public: const AnimPoseVec& getRelativeDefaultPoses() const { return _relativeDefaultPoses; } const AnimPose& getAbsoluteDefaultPose(int jointIndex) const; const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; } + const glm::mat4& getGeometryOffset() const { return _geometryOffset; } // get pre transform which should include FBX pre potations const AnimPose& getPreRotationPose(int jointIndex) const; @@ -83,6 +84,7 @@ protected: std::vector _mirrorMap; QHash _jointIndicesByName; std::vector> _clusterBindMatrixOriginalValues; + glm::mat4 _geometryOffset; // no copies AnimSkeleton(const AnimSkeleton&) = delete; From 54f14b2772b9abc8fbdf54dbe77801a83330d97b Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 1 Mar 2019 12:41:05 -0800 Subject: [PATCH 14/29] added the case where my avatar has no parent of hips, but the animation does --- libraries/animation/src/AnimClip.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 109be27b58..da5e9b6508 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -153,11 +153,11 @@ void AnimClip::copyFromNetworkAnim() { const float EPSILON = 0.0001f; const int avatarHipsParentIndex = avatarSkeleton->getParentIndex(avatarSkeleton->nameToJointIndex("Hips")); - if (avatarJointIndex == avatarSkeleton->nameToJointIndex("Hips") && (avatarHipsParentIndex >= 0)) { + const int animHipsParentIndex = animSkeleton.getParentIndex(animSkeleton.nameToJointIndex("Hips")); + if (avatarJointIndex == avatarSkeleton->nameToJointIndex("Hips") && (avatarHipsParentIndex >= 0) || (animHipsParentIndex >= 0)) { const AnimPose& animHipsAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animSkeleton.nameToJointIndex("Hips")); const AnimPose& avatarHipsAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarSkeleton->nameToJointIndex("Hips")); - const AnimPose& avatarHipsParentAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsParentIndex); // the get the units and the heights for the animation and the avatar const float animationUnitScale = extractScale(animModel.offset).y; @@ -166,10 +166,12 @@ void AnimClip::copyFromNetworkAnim() { const float animHeightInMeters = animationUnitScale * animHipsAbsoluteDefaultPose.trans().y; // get the parent scales for the avatar and the animation - const float avatarHipsParentScale = avatarHipsParentAbsoluteDefaultPose.scale().y; + float avatarHipsParentScale = 1.0f; float animHipsParentScale = 1.0f; - const int animHipsParentIndex = animSkeleton.getParentIndex(animSkeleton.nameToJointIndex("Hips")); - // also, check to see if the animation hips have a scaled parent. + if (avatarHipsParentIndex >= 0) { + const AnimPose& avatarHipsParentAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsParentIndex); + avatarHipsParentScale = avatarHipsParentAbsoluteDefaultPose.scale().y; + } if (animHipsParentIndex >= 0) { const AnimPose& animationHipsParentAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animHipsParentIndex); animHipsParentScale = animationHipsParentAbsoluteDefaultPose.scale().y; From 3e6061e4350684f88e2313cbed423a40a324821b Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 1 Mar 2019 13:45:00 -0800 Subject: [PATCH 15/29] try to not clear my avatar entities on domain switch --- interface/src/Application.cpp | 2 +- .../src/EntityTreeRenderer.cpp | 24 +++++++++---------- .../src/EntityTreeRenderer.h | 6 ++--- libraries/entities/src/EntityTree.cpp | 16 +++++++------ libraries/entities/src/EntityTree.h | 3 ++- libraries/entities/src/EntityTreeElement.cpp | 4 ++-- libraries/entities/src/EntityTreeElement.h | 2 +- libraries/octree/src/Octree.h | 2 +- libraries/octree/src/OctreeProcessor.cpp | 4 ++-- libraries/octree/src/OctreeProcessor.h | 2 +- 10 files changed, 34 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 29d260cb5f..1a64378c36 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6948,7 +6948,7 @@ void Application::clearDomainOctreeDetails(bool clearAll) { }); // reset the model renderer - clearAll ? getEntities()->clear() : getEntities()->clearNonLocalEntities(); + clearAll ? getEntities()->clear() : getEntities()->clearDomainAndNonOwnedEntities(); auto skyStage = DependencyManager::get()->getSkyStage(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e54258fc3e..1e2c17fa8e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -196,8 +196,8 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() { }); } -void EntityTreeRenderer::stopNonLocalEntityScripts() { - leaveNonLocalEntities(); +void EntityTreeRenderer::stopDomainAndNonOwnedEntities() { + leaveDomainAndNonOwnedEntities(); // unload and stop the engine if (_entitiesScriptEngine) { QList entitiesWithEntityScripts = _entitiesScriptEngine->getListOfEntityScriptIDs(); @@ -206,7 +206,7 @@ void EntityTreeRenderer::stopNonLocalEntityScripts() { EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); if (entityItem) { - if (!entityItem->isLocalEntity()) { + if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { _entitiesScriptEngine->unloadEntityScript(entityID, true); } } @@ -214,8 +214,8 @@ void EntityTreeRenderer::stopNonLocalEntityScripts() { } } -void EntityTreeRenderer::clearNonLocalEntities() { - stopNonLocalEntityScripts(); +void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { + stopDomainAndNonOwnedEntities(); std::unordered_map savedEntities; // remove all entities from the scene @@ -225,7 +225,7 @@ void EntityTreeRenderer::clearNonLocalEntities() { for (const auto& entry : _entitiesInScene) { const auto& renderer = entry.second; const EntityItemPointer& entityItem = renderer->getEntity(); - if (!entityItem->isLocalEntity()) { + if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { renderer->removeFromScene(scene, transaction); } else { savedEntities[entry.first] = entry.second; @@ -239,7 +239,7 @@ void EntityTreeRenderer::clearNonLocalEntities() { _layeredZones.clearNonLocalLayeredZones(); - OctreeProcessor::clearNonLocalEntities(); + OctreeProcessor::clearDomainAndNonOwnedEntities(); } void EntityTreeRenderer::clear() { @@ -655,22 +655,22 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { return didUpdate; } -void EntityTreeRenderer::leaveNonLocalEntities() { +void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() { if (_tree && !_shuttingDown) { - QVector currentLocalEntitiesInside; + QVector currentEntitiesInsideToSave; foreach (const EntityItemID& entityID, _currentEntitiesInside) { EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); - if (!entityItem->isLocalEntity()) { + if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { emit leaveEntity(entityID); if (_entitiesScriptEngine) { _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); } } else { - currentLocalEntitiesInside.push_back(entityID); + currentEntitiesInsideToSave.push_back(entityID); } } - _currentEntitiesInside = currentLocalEntitiesInside; + _currentEntitiesInside = currentEntitiesInsideToSave; forceRecheckEntities(); } } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 51568ab744..b4d3507c17 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -87,7 +87,7 @@ public: virtual void init() override; /// clears the tree - virtual void clearNonLocalEntities() override; + virtual void clearDomainAndNonOwnedEntities() override; virtual void clear() override; /// reloads the entity scripts, calling unload and preload @@ -170,7 +170,7 @@ private: bool findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar = nullptr); bool applyLayeredZones(); - void stopNonLocalEntityScripts(); + void stopDomainAndNonOwnedEntities(); void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false); @@ -179,7 +179,7 @@ private: QScriptValueList createEntityArgs(const EntityItemID& entityID); bool checkEnterLeaveEntities(); - void leaveNonLocalEntities(); + void leaveDomainAndNonOwnedEntities(); void leaveAllEntities(); void forceRecheckEntities(); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 6e404ce690..cdf021638b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -78,13 +78,14 @@ OctreeElementPointer EntityTree::createNewElement(unsigned char* octalCode) { return std::static_pointer_cast(newElement); } -void EntityTree::eraseNonLocalEntities() { +void EntityTree::eraseDomainAndNonOwnedEntities() { emit clearingEntities(); if (_simulation) { // local entities are not in the simulation, so we clear ALL _simulation->clearEntities(); } + this->withWriteLock([&] { QHash savedEntities; // NOTE: lock the Tree first, then lock the _entityMap. @@ -93,10 +94,10 @@ void EntityTree::eraseNonLocalEntities() { foreach(EntityItemPointer entity, _entityMap) { EntityTreeElementPointer element = entity->getElement(); if (element) { - element->cleanupNonLocalEntities(); + element->cleanupDomainAndNonOwnedEntities(); } - if (entity->isLocalEntity()) { + if (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID())) { savedEntities[entity->getEntityItemID()] = entity; } else { int32_t spaceIndex = entity->getSpaceIndex(); @@ -114,15 +115,16 @@ void EntityTree::eraseNonLocalEntities() { { QWriteLocker locker(&_needsParentFixupLock); - QVector localEntitiesNeedsParentFixup; + QVector needParentFixup; foreach (EntityItemWeakPointer entityItem, _needsParentFixup) { - if (!entityItem.expired() && entityItem.lock()->isLocalEntity()) { - localEntitiesNeedsParentFixup.push_back(entityItem); + auto entity = entityItem.lock(); + if (entity && (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID()))) { + needParentFixup.push_back(entityItem); } } - _needsParentFixup = localEntitiesNeedsParentFixup; + _needsParentFixup = needParentFixup; } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index dcce0e4b99..9da0ecedd8 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -75,7 +75,7 @@ public: } - virtual void eraseNonLocalEntities() override; + virtual void eraseDomainAndNonOwnedEntities() override; virtual void eraseAllOctreeElements(bool createNewRoot = true) override; virtual void readBitstreamToTree(const unsigned char* bitstream, @@ -255,6 +255,7 @@ public: QByteArray computeNonce(const QString& certID, const QString ownerKey); bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id); + QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); } void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } void swapStaleProxies(std::vector& proxies) { proxies.swap(_staleProxies); } diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index ce6f20262f..aab98adb52 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -697,11 +697,11 @@ EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemI return foundEntity; } -void EntityTreeElement::cleanupNonLocalEntities() { +void EntityTreeElement::cleanupDomainAndNonOwnedEntities() { withWriteLock([&] { EntityItems savedEntities; foreach(EntityItemPointer entity, _entityItems) { - if (!entity->isLocalEntity()) { + if (!(entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { entity->preDelete(); entity->_element = NULL; } else { diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index f82eaa7fb1..f94da44138 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -190,7 +190,7 @@ public: EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const; void getEntitiesInside(const AACube& box, QVector& foundEntities); - void cleanupNonLocalEntities(); + void cleanupDomainAndNonOwnedEntities(); void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities bool removeEntityItem(EntityItemPointer entity, bool deletion = false); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index aac29201f1..82076f618b 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -149,7 +149,7 @@ public: OctreeElementPointer getRoot() { return _rootElement; } - virtual void eraseNonLocalEntities() { _isDirty = true; }; + virtual void eraseDomainAndNonOwnedEntities() { _isDirty = true; }; virtual void eraseAllOctreeElements(bool createNewRoot = true); virtual void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args); diff --git a/libraries/octree/src/OctreeProcessor.cpp b/libraries/octree/src/OctreeProcessor.cpp index 18c8630391..03c8b9ca2f 100644 --- a/libraries/octree/src/OctreeProcessor.cpp +++ b/libraries/octree/src/OctreeProcessor.cpp @@ -198,10 +198,10 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe } -void OctreeProcessor::clearNonLocalEntities() { +void OctreeProcessor::clearDomainAndNonOwnedEntities() { if (_tree) { _tree->withWriteLock([&] { - _tree->eraseNonLocalEntities(); + _tree->eraseDomainAndNonOwnedEntities(); }); } } diff --git a/libraries/octree/src/OctreeProcessor.h b/libraries/octree/src/OctreeProcessor.h index bc5618e657..40af7a39f8 100644 --- a/libraries/octree/src/OctreeProcessor.h +++ b/libraries/octree/src/OctreeProcessor.h @@ -43,7 +43,7 @@ public: virtual void init(); /// clears the tree - virtual void clearNonLocalEntities(); + virtual void clearDomainAndNonOwnedEntities(); virtual void clear(); float getAverageElementsPerPacket() const { return _elementsPerPacket.getAverage(); } From 618a1d5b8379c7303f195a81c97093082d5234e7 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Fri, 1 Mar 2019 14:03:45 -0800 Subject: [PATCH 16/29] fix tablet button and gizmos rotation --- interface/src/ui/overlays/Overlays.cpp | 23 ++++++++++++----------- scripts/system/libraries/utils.js | 4 ---- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 9b2f741531..08fa04245f 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -413,30 +413,31 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove overlayProps["dimensions"] = vec3toVariant(ratio * dimensions); } - if (add || overlayProps.contains("rotation")) { + if (add && !overlayProps.contains("rotation") && !overlayProps.contains("localRotation")) { + glm::quat rotation; + overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation); + } else if (overlayProps.contains("rotation")) { glm::quat rotation; { auto iter = overlayProps.find("rotation"); if (iter != overlayProps.end()) { rotation = quatFromVariant(iter.value()); - } else if (!add) { - EntityPropertyFlags desiredProperties; - desiredProperties += PROP_ROTATION; - rotation = DependencyManager::get()->getEntityProperties(id, desiredProperties).getRotation(); } } overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation); - } - if (add || overlayProps.contains("localRotation")) { + + if (overlayProps.contains("localRotation")) { + auto iter = overlayProps.find("localRotation"); + if (iter != overlayProps.end()) { + overlayProps.erase(iter); + } + } + } else if (overlayProps.contains("localRotation")) { glm::quat rotation; { auto iter = overlayProps.find("localRotation"); if (iter != overlayProps.end()) { rotation = quatFromVariant(iter.value()); - } else if (!add) { - EntityPropertyFlags desiredProperties; - desiredProperties += PROP_LOCAL_ROTATION; - rotation = DependencyManager::get()->getEntityProperties(id, desiredProperties).getLocalRotation(); } } overlayProps["localRotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation); diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index 6f74b43a8e..508e8d46e3 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -418,13 +418,11 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride; Entities.editEntity(HMD.homeButtonID, { localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET }, - localRotation: { x: 0, y: 1, z: 0, w: 0 }, dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); Entities.editEntity(HMD.homeButtonHighlightID, { localPosition: { x: -HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET }, - localRotation: { x: 0, y: 1, z: 0, w: 0 }, dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); }; @@ -482,13 +480,11 @@ reparentAndScaleTablet = function(width, reparentProps) { var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride; Entities.editEntity(HMD.homeButtonID, { localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET }, - localRotation: { x: 0, y: 1, z: 0, w: 0 }, dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); Entities.editEntity(HMD.homeButtonHighlightID, { localPosition: { x: -HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET }, - localRotation: { x: 0, y: 1, z: 0, w: 0 }, dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); } From c046b8ffd35c5aaffd953999eca5c3828d97b2c5 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 1 Mar 2019 15:12:37 -0800 Subject: [PATCH 17/29] made is so the boneLengthScale is only computed once per animation clip --- libraries/animation/src/AnimClip.cpp | 80 +++++++++++++--------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 9b87d058fd..4fe02e9307 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -123,6 +123,42 @@ void AnimClip::copyFromNetworkAnim() { const int animFrameCount = animModel.animationFrames.size(); _anim.resize(animFrameCount); + // find the size scale factor for translation in the animation. + const int avatarHipsParentIndex = avatarSkeleton->getParentIndex(avatarSkeleton->nameToJointIndex("Hips")); + const int animHipsParentIndex = animSkeleton.getParentIndex(animSkeleton.nameToJointIndex("Hips")); + const AnimPose& avatarHipsAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarSkeleton->nameToJointIndex("Hips")); + const AnimPose& animHipsAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animSkeleton.nameToJointIndex("Hips")); + + // the get the units and the heights for the animation and the avatar + const float avatarUnitScale = extractScale(avatarSkeleton->getGeometryOffset()).y; + const float animationUnitScale = extractScale(animModel.offset).y; + const float avatarHeightInMeters = avatarUnitScale * avatarHipsAbsoluteDefaultPose.trans().y; + const float animHeightInMeters = animationUnitScale * animHipsAbsoluteDefaultPose.trans().y; + + // get the parent scales for the avatar and the animation + float avatarHipsParentScale = 1.0f; + if (avatarHipsParentIndex >= 0) { + const AnimPose& avatarHipsParentAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsParentIndex); + avatarHipsParentScale = avatarHipsParentAbsoluteDefaultPose.scale().y; + } + float animHipsParentScale = 1.0f; + if (animHipsParentIndex >= 0) { + const AnimPose& animationHipsParentAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animHipsParentIndex); + animHipsParentScale = animationHipsParentAbsoluteDefaultPose.scale().y; + } + + const float EPSILON = 0.0001f; + float boneLengthScale = 1.0f; + // compute the ratios for the units, the heights in meters, and the parent scales + if ((fabsf(animHeightInMeters) > EPSILON) && (animationUnitScale > EPSILON) && (animHipsParentScale > EPSILON)) { + const float avatarToAnimationHeightRatio = avatarHeightInMeters / animHeightInMeters; + const float unitsRatio = 1.0f / (avatarUnitScale / animationUnitScale); + const float parentScaleRatio = 1.0f / (avatarHipsParentScale / animHipsParentScale); + + boneLengthScale = avatarToAnimationHeightRatio * unitsRatio * parentScaleRatio; + } + + for (int frame = 0; frame < animFrameCount; frame++) { const HFMAnimationFrame& animFrame = animModel.animationFrames[frame]; @@ -162,7 +198,6 @@ void AnimClip::copyFromNetworkAnim() { avatarSkeleton->convertAbsoluteRotationsToRelative(avatarRotations); _anim[frame].reserve(avatarJointCount); - for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) { const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex); @@ -177,49 +212,6 @@ void AnimClip::copyFromNetworkAnim() { // retarget translation from animation to avatar const glm::vec3& animZeroTrans = animModel.animationFrames[0].translations[animJointIndex]; - float boneLengthScale = 1.0f; - const float EPSILON = 0.0001f; - - const int avatarHipsParentIndex = avatarSkeleton->getParentIndex(avatarSkeleton->nameToJointIndex("Hips")); - const int animHipsParentIndex = animSkeleton.getParentIndex(animSkeleton.nameToJointIndex("Hips")); - if (avatarJointIndex == avatarSkeleton->nameToJointIndex("Hips") && (avatarHipsParentIndex >= 0) || (animHipsParentIndex >= 0)) { - - const AnimPose& animHipsAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animSkeleton.nameToJointIndex("Hips")); - const AnimPose& avatarHipsAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarSkeleton->nameToJointIndex("Hips")); - - // the get the units and the heights for the animation and the avatar - const float animationUnitScale = extractScale(animModel.offset).y; - const float avatarUnitScale = extractScale(avatarSkeleton->getGeometryOffset()).y; - const float avatarHeightInMeters = avatarUnitScale * avatarHipsAbsoluteDefaultPose.trans().y; - const float animHeightInMeters = animationUnitScale * animHipsAbsoluteDefaultPose.trans().y; - - // get the parent scales for the avatar and the animation - float avatarHipsParentScale = 1.0f; - float animHipsParentScale = 1.0f; - if (avatarHipsParentIndex >= 0) { - const AnimPose& avatarHipsParentAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsParentIndex); - avatarHipsParentScale = avatarHipsParentAbsoluteDefaultPose.scale().y; - } - if (animHipsParentIndex >= 0) { - const AnimPose& animationHipsParentAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animHipsParentIndex); - animHipsParentScale = animationHipsParentAbsoluteDefaultPose.scale().y; - } - - // compute the ratios for the units, the heights in meters, and the parent scales - if ((fabsf(animHeightInMeters) > EPSILON) && (animationUnitScale > EPSILON) && (animHipsParentScale > EPSILON)) { - const float avatarToAnimationHeightRatio = avatarHeightInMeters / animHeightInMeters; - const float unitsRatio = 1.0f / (avatarUnitScale / animationUnitScale); - const float parentScaleRatio = 1.0f / (avatarHipsParentScale / animHipsParentScale); - - boneLengthScale = avatarToAnimationHeightRatio * unitsRatio * parentScaleRatio; - } - } else { - - if (fabsf(glm::length(animZeroTrans)) > EPSILON) { - boneLengthScale = glm::length(avatarDefaultPose.trans()) / glm::length(animZeroTrans); - } - } - relativeTranslation = avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans); } else { // This joint is NOT in the animation at all. From 8e600adf1e5a997d785b67bd161cf200dfd7a935 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Fri, 1 Mar 2019 15:30:11 -0800 Subject: [PATCH 18/29] making requested changes --- interface/src/ui/overlays/Overlays.cpp | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 08fa04245f..8fef2d543d 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -414,32 +414,12 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove } if (add && !overlayProps.contains("rotation") && !overlayProps.contains("localRotation")) { - glm::quat rotation; - overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation); + overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, Vectors::RIGHT)); } else if (overlayProps.contains("rotation")) { - glm::quat rotation; - { - auto iter = overlayProps.find("rotation"); - if (iter != overlayProps.end()) { - rotation = quatFromVariant(iter.value()); - } - } + glm::quat rotation = quatFromVariant(overlayProps["rotation"]); overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation); - - if (overlayProps.contains("localRotation")) { - auto iter = overlayProps.find("localRotation"); - if (iter != overlayProps.end()) { - overlayProps.erase(iter); - } - } } else if (overlayProps.contains("localRotation")) { - glm::quat rotation; - { - auto iter = overlayProps.find("localRotation"); - if (iter != overlayProps.end()) { - rotation = quatFromVariant(iter.value()); - } - } + glm::quat rotation = quatFromVariant(overlayProps["localRotation"]); overlayProps["localRotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation); } From da1ffc15e37ab6c22457a98f129df6d8ec7c425e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 1 Mar 2019 15:41:57 -0800 Subject: [PATCH 19/29] lasers scale with avatar --- interface/src/raypick/LaserPointer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index aeed65fbad..bd746c9090 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -172,7 +172,10 @@ void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& properties.setVisible(true); properties.setIgnorePickIntersection(doesPathIgnorePicks()); QVector widths; - widths.append(getLineWidth() * parentScale); + float width = getLineWidth() * parentScale; + widths.append(width); + widths.append(width); + properties.setStrokeWidths(widths); DependencyManager::get()->editEntity(getPathID(), properties); } } From 884a64bfa6633a9c2029577e2582f9773fd3f99e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 1 Mar 2019 15:50:50 -0800 Subject: [PATCH 20/29] fix resource crash --- libraries/networking/src/ResourceCache.cpp | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 7345081380..d5abb27a27 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -353,16 +353,19 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& // We've seen this extra info before resource = resourcesWithExtraHashIter.value().lock(); } else if (resourcesWithExtraHash.size() > 0.0f) { - // We haven't seen this extra info before, but we've already downloaded the resource. We need a new copy of this object (with any old hash). - resource = createResourceCopy(resourcesWithExtraHash.begin().value().lock()); - resource->setExtra(extra); - resource->setExtraHash(extraHash); - resource->setSelf(resource); - resource->setCache(this); - resource->moveToThread(qApp->thread()); - connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); - resourcesWithExtraHash.insert(extraHash, resource); - resource->ensureLoading(); + auto oldResource = resourcesWithExtraHash.begin().value().lock(); + if (oldResource) { + // We haven't seen this extra info before, but we've already downloaded the resource. We need a new copy of this object (with any old hash). + resource = createResourceCopy(oldResource); + resource->setExtra(extra); + resource->setExtraHash(extraHash); + resource->setSelf(resource); + resource->setCache(this); + resource->moveToThread(qApp->thread()); + connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); + resourcesWithExtraHash.insert(extraHash, resource); + resource->ensureLoading(); + } } } if (resource) { From 12ffa41984730e9ac5ae24f9954368fd152dabb2 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Sat, 2 Mar 2019 13:55:06 -0800 Subject: [PATCH 21/29] Prevent HUD and Web Surface lasers from interrupting FarGrab. --- .../controllerModules/hudOverlayPointer.js | 16 +++++++++++++- .../controllerModules/webSurfaceLaserInput.js | 21 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index efbca66d72..f7d5b5a2dd 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -30,6 +30,20 @@ 100, makeLaserParams((this.hand + HUD_LASER_OFFSET), false)); + this.getFarGrab = function () { + return getEnabledModuleByName(this.hand === RIGHT_HAND ? ("RightFarGrabEntity") : ("LeftFarGrabEntity")); + } + + this.farGrabActive = function () { + var farGrab = this.getFarGrab(); + // farGrab will be null if module isn't loaded. + if (farGrab) { + return farGrab.targetIsNull(); + } else { + return false; + } + }; + this.getOtherHandController = function() { return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; }; @@ -79,7 +93,7 @@ this.isReady = function (controllerData) { var otherModuleRunning = this.getOtherModule().running; - if (!otherModuleRunning && HMD.active) { + if (!otherModuleRunning && HMD.active && !this.farGrabActive()) { if (this.processLaser(controllerData)) { this.running = true; return ControllerDispatcherUtils.makeRunningValues(true, [], []); diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index ec35dfe081..4f21b44533 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -37,6 +37,20 @@ Script.include("/~/system/libraries/controllers.js"); 100, makeLaserParams(hand, true)); + this.getFarGrab = function () { + return getEnabledModuleByName(this.hand === RIGHT_HAND ? ("RightFarGrabEntity") : ("LeftFarGrabEntity")); + }; + + this.farGrabActive = function () { + var farGrab = this.getFarGrab(); + // farGrab will be null if module isn't loaded. + if (farGrab) { + return farGrab.targetIsNull(); + } else { + return false; + } + }; + this.grabModuleWantsNearbyOverlay = function(controllerData) { if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { var nearGrabName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"; @@ -184,7 +198,12 @@ Script.include("/~/system/libraries/controllers.js"); this.dominantHandOverride = false; - this.isReady = function(controllerData) { + this.isReady = function (controllerData) { + // Trivial rejection for when FarGrab is active. + if (this.farGrabActive()) { + return makeRunningValues(false, [], []); + } + var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE && controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE; var type = this.getInteractableType(controllerData, isTriggerPressed, false); From 708632ee82ce326c25dc7ee73e9b83fa63998673 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 4 Mar 2019 10:00:26 -0800 Subject: [PATCH 22/29] possible fix for model crash --- libraries/render-utils/src/Model.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 9bb3f72b31..683c517a15 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1346,14 +1346,18 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { } void Model::computeMeshPartLocalBounds() { - for (auto& part : _modelMeshRenderItems) { - const Model::MeshState& state = _meshStates.at(part->_meshIndex); - if (_useDualQuaternionSkinning) { - part->computeAdjustedLocalBound(state.clusterDualQuaternions); - } else { - part->computeAdjustedLocalBound(state.clusterMatrices); - } + render::Transaction transaction; + for (auto renderItem : _modelMeshRenderItemIDs) { + transaction.updateItem(renderItem, [&](ModelMeshPartPayload& data) { + const Model::MeshState& state = _meshStates.at(data._meshIndex); + if (_useDualQuaternionSkinning) { + data.computeAdjustedLocalBound(state.clusterDualQuaternions); + } else { + data.computeAdjustedLocalBound(state.clusterMatrices); + } + }); } + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); } // virtual From b2d08e9d42f152c4beff350b37842f4af93bcf0c Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 4 Mar 2019 15:33:21 -0700 Subject: [PATCH 23/29] apply axis rotation to translation and meshes --- libraries/fbx/src/FBXSerializer.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index e022ca8921..5246242a1e 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -1281,8 +1281,9 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr joint.geometricScaling = fbxModel.geometricScaling; joint.isSkeletonJoint = fbxModel.isLimbNode; hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint); - if (applyUpAxisZRotation && joint.parentIndex == -1 && !joint.isSkeletonJoint) { + if (applyUpAxisZRotation && joint.parentIndex == -1) { joint.rotation *= upAxisZRotation; + joint.translation = upAxisZRotation * joint.translation; } glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; if (joint.parentIndex == -1) { @@ -1678,8 +1679,12 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } if (applyUpAxisZRotation) { - hfmModelPtr->meshExtents.rotate(upAxisZRotation); - hfmModelPtr->bindExtents.rotate(upAxisZRotation); + hfmModelPtr->meshExtents.transform(glm::mat4_cast(upAxisZRotation)); + hfmModelPtr->bindExtents.transform(glm::mat4_cast(upAxisZRotation)); + for (auto &mesh : hfmModelPtr->meshes) { + mesh.modelTransform *= glm::mat4_cast(upAxisZRotation); + mesh.meshExtents.transform(glm::mat4_cast(upAxisZRotation)); + } } return hfmModelPtr; } From 12f5a735d93e61e74c1824d1f5b0f9c88e3a2342 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 4 Mar 2019 14:50:09 -0800 Subject: [PATCH 24/29] simplifying mouse events and fix mouse on create --- interface/src/Application.cpp | 11 +- interface/src/ui/overlays/Overlays.cpp | 239 ++++++------------ interface/src/ui/overlays/Overlays.h | 17 +- .../src/EntityTreeRenderer.cpp | 34 +-- .../src/EntityTreeRenderer.h | 2 +- scripts/system/edit.js | 6 +- 6 files changed, 103 insertions(+), 206 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2c8d71af00..e41d51de47 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4392,7 +4392,6 @@ void Application::mouseMoveEvent(QMouseEvent* event) { if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() || getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y())) != UNKNOWN_ENTITY_ID) { getEntities()->mouseMoveEvent(&mappedEvent); - getOverlays().mouseMoveEvent(&mappedEvent); } _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts @@ -4426,14 +4425,10 @@ void Application::mousePressEvent(QMouseEvent* event) { #endif QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers()); - std::pair entityResult; if (!_controllerScriptingInterface->areEntityClicksCaptured()) { - entityResult = getEntities()->mousePressEvent(&mappedEvent); + QUuid result = getEntities()->mousePressEvent(&mappedEvent); + setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(result) ? result : UNKNOWN_ENTITY_ID); } - std::pair overlayResult = getOverlays().mousePressEvent(&mappedEvent); - - QUuid focusedEntity = entityResult.first < overlayResult.first ? entityResult.second : overlayResult.second; - setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(focusedEntity) ? focusedEntity : UNKNOWN_ENTITY_ID); _controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts @@ -4476,7 +4471,6 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) { if (!_controllerScriptingInterface->areEntityClicksCaptured()) { getEntities()->mouseDoublePressEvent(&mappedEvent); } - getOverlays().mouseDoublePressEvent(&mappedEvent); // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface->isMouseCaptured()) { @@ -4501,7 +4495,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { event->buttons(), event->modifiers()); getEntities()->mouseReleaseEvent(&mappedEvent); - getOverlays().mouseReleaseEvent(&mappedEvent); _controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 5ae3f7d38e..dfd698f6c5 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -43,14 +43,6 @@ std::unordered_map Overlays::_entityToOverlayTypes; std::unordered_map Overlays::_overlayToEntityTypes; Overlays::Overlays() { - auto pointerManager = DependencyManager::get(); - connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterPointerEvent); - connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverPointerEvent); - connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Overlays::hoverLeavePointerEvent); - connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent); - connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMovePointerEvent); - connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Overlays::mouseReleasePointerEvent); - ADD_TYPE_MAP(Box, cube); ADD_TYPE_MAP(Sphere, sphere); _overlayToEntityTypes["rectangle3d"] = "Shape"; @@ -81,13 +73,23 @@ void Overlays::cleanupAllOverlays() { void Overlays::init() { auto entityScriptingInterface = DependencyManager::get().data(); - auto pointerManager = DependencyManager::get(); - connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity); - connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); - connect(pointerManager.data(), &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); - connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); - connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); - connect(pointerManager.data(), &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); + auto pointerManager = DependencyManager::get().data(); + connect(pointerManager, &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity); + connect(pointerManager, &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); + connect(pointerManager, &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); + connect(pointerManager, &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); + connect(pointerManager, &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); + connect(pointerManager, &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); + + connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, &Overlays::mousePressOnPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOffEntity, this, &Overlays::mousePressOffPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseDoublePressOnEntity, this, &Overlays::mouseDoublePressOnPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseDoublePressOffEntity, this, &Overlays::mouseDoublePressOffPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, &Overlays::mouseReleasePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, &Overlays::mouseMovePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity , this, &Overlays::hoverEnterPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity, this, &Overlays::hoverOverPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, &Overlays::hoverLeavePointerEvent); } void Overlays::update(float deltatime) { @@ -1159,7 +1161,7 @@ bool Overlays::isAddedOverlay(const QUuid& id) { } void Overlays::sendMousePressOnOverlay(const QUuid& id, const PointerEvent& event) { - mousePressPointerEvent(id, event); + mousePressOnPointerEvent(id, event); } void Overlays::sendMouseReleaseOnOverlay(const QUuid& id, const PointerEvent& event) { @@ -1206,57 +1208,66 @@ float Overlays::height() { return offscreenUi->getWindow()->size().height(); } -static uint32_t toPointerButtons(const QMouseEvent& event) { - uint32_t buttons = 0; - buttons |= event.buttons().testFlag(Qt::LeftButton) ? PointerEvent::PrimaryButton : 0; - buttons |= event.buttons().testFlag(Qt::RightButton) ? PointerEvent::SecondaryButton : 0; - buttons |= event.buttons().testFlag(Qt::MiddleButton) ? PointerEvent::TertiaryButton : 0; - return buttons; -} - -static PointerEvent::Button toPointerButton(const QMouseEvent& event) { - switch (event.button()) { - case Qt::LeftButton: - return PointerEvent::PrimaryButton; - case Qt::RightButton: - return PointerEvent::SecondaryButton; - case Qt::MiddleButton: - return PointerEvent::TertiaryButton; - default: - return PointerEvent::NoButtons; - } -} - -RayToOverlayIntersectionResult getPrevPickResult() { - RayToOverlayIntersectionResult overlayResult; - overlayResult.intersects = false; - auto pickResult = DependencyManager::get()->getPrevPickResultTyped(DependencyManager::get()->getMouseRayPickID()); - if (pickResult) { - overlayResult.intersects = pickResult->type == IntersectionType::LOCAL_ENTITY; - if (overlayResult.intersects) { - overlayResult.intersection = pickResult->intersection; - overlayResult.distance = pickResult->distance; - overlayResult.surfaceNormal = pickResult->surfaceNormal; - overlayResult.overlayID = pickResult->objectID; - overlayResult.extraInfo = pickResult->extraInfo; +void Overlays::mousePressOnPointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit mousePressOnOverlay(id, event); } } - return overlayResult; } -PointerEvent Overlays::calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, - const RayToOverlayIntersectionResult& rayPickResult, QMouseEvent* event, - PointerEvent::EventType eventType) { - glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(id, rayPickResult.intersection); - return PointerEvent(eventType, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, - ray.direction, toPointerButton(*event), toPointerButtons(*event), event->modifiers()); +void Overlays::mousePressOffPointerEvent() { + emit mousePressOffOverlay(); +} + +void Overlays::mouseDoublePressOnPointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit mouseDoublePressOnOverlay(id, event); + } + } +} + +void Overlays::mouseDoublePressOffPointerEvent() { + emit mouseDoublePressOffOverlay(); +} + +void Overlays::mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit mouseReleaseOnOverlay(id, event); + } + } +} + +void Overlays::mouseMovePointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit mouseMoveOnOverlay(id, event); + } + } } void Overlays::hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event) { auto keyboard = DependencyManager::get(); // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed if (!keyboard->getKeyIDs().contains(id)) { - emit hoverEnterOverlay(id, event); + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit hoverEnterOverlay(id, event); + } } } @@ -1264,7 +1275,10 @@ void Overlays::hoverOverPointerEvent(const QUuid& id, const PointerEvent& event) auto keyboard = DependencyManager::get(); // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed if (!keyboard->getKeyIDs().contains(id)) { - emit hoverOverOverlay(id, event); + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit hoverOverOverlay(id, event); + } } } @@ -1272,113 +1286,10 @@ void Overlays::hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event auto keyboard = DependencyManager::get(); // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed if (!keyboard->getKeyIDs().contains(id)) { - emit hoverLeaveOverlay(id, event); - } -} - -std::pair Overlays::mousePressEvent(QMouseEvent* event) { - PerformanceTimer perfTimer("Overlays::mousePressEvent"); - - PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(); - if (rayPickResult.intersects) { - _currentClickingOnOverlayID = rayPickResult.overlayID; - - PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); - mousePressPointerEvent(_currentClickingOnOverlayID, pointerEvent); - return { rayPickResult.distance, rayPickResult.overlayID }; - } - emit mousePressOffOverlay(); - return { FLT_MAX, UNKNOWN_ENTITY_ID }; -} - -void Overlays::mousePressPointerEvent(const QUuid& id, const PointerEvent& event) { - auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeyIDs().contains(id)) { - emit mousePressOnOverlay(id, event); - } -} - -bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { - PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent"); - - PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(); - if (rayPickResult.intersects) { - _currentClickingOnOverlayID = rayPickResult.overlayID; - - auto pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); - emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); - return true; - } - emit mouseDoublePressOffOverlay(); - return false; -} - -bool Overlays::mouseReleaseEvent(QMouseEvent* event) { - PerformanceTimer perfTimer("Overlays::mouseReleaseEvent"); - - PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(); - if (rayPickResult.intersects) { - auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release); - mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent); - } - - _currentClickingOnOverlayID = UNKNOWN_ENTITY_ID; - return false; -} - -void Overlays::mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event) { - auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeyIDs().contains(id)) { - emit mouseReleaseOnOverlay(id, event); - } -} - -bool Overlays::mouseMoveEvent(QMouseEvent* event) { - PerformanceTimer perfTimer("Overlays::mouseMoveEvent"); - - PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(); - if (rayPickResult.intersects) { - auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move); - mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent); - - // If previously hovering over a different overlay then leave hover on that overlay. - if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { - auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); - hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent); + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit hoverLeaveOverlay(id, event); } - - // If hovering over a new overlay then enter hover on that overlay. - if (rayPickResult.overlayID != _currentHoverOverOverlayID) { - hoverEnterPointerEvent(rayPickResult.overlayID, pointerEvent); - } - - // Hover over current overlay. - hoverOverPointerEvent(rayPickResult.overlayID, pointerEvent); - - _currentHoverOverOverlayID = rayPickResult.overlayID; - } else { - // If previously hovering an overlay then leave hover. - if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID) { - auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); - hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent); - - _currentHoverOverOverlayID = UNKNOWN_ENTITY_ID; - } - } - return false; -} - -void Overlays::mouseMovePointerEvent(const QUuid& id, const PointerEvent& event) { - auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeyIDs().contains(id)) { - emit mouseMoveOnOverlay(id, event); } } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 93efc2bc0b..0b2994b872 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -112,11 +112,6 @@ public: const QVector& discard, bool visibleOnly = false, bool collidableOnly = false); - std::pair mousePressEvent(QMouseEvent* event); - bool mouseDoublePressEvent(QMouseEvent* event); - bool mouseReleaseEvent(QMouseEvent* event); - bool mouseMoveEvent(QMouseEvent* event); - void cleanupAllOverlays(); mutable QScriptEngine _scriptEngine; @@ -719,9 +714,6 @@ private: PointerEvent calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, const RayToOverlayIntersectionResult& rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); - QUuid _currentClickingOnOverlayID; - QUuid _currentHoverOverOverlayID; - static QString entityToOverlayType(const QString& type); static QString overlayToEntityType(const QString& type); static std::unordered_map _entityToOverlayTypes; @@ -732,12 +724,17 @@ private: EntityItemProperties convertOverlayToEntityProperties(QVariantMap& overlayProps, std::pair& rotationToSave, const QString& type, bool add, const QUuid& id = QUuid()); private slots: - void mousePressPointerEvent(const QUuid& id, const PointerEvent& event); - void mouseMovePointerEvent(const QUuid& id, const PointerEvent& event); + void mousePressOnPointerEvent(const QUuid& id, const PointerEvent& event); + void mousePressOffPointerEvent(); + void mouseDoublePressOnPointerEvent(const QUuid& id, const PointerEvent& event); + void mouseDoublePressOffPointerEvent(); void mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event); + void mouseMovePointerEvent(const QUuid& id, const PointerEvent& event); void hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event); void hoverOverPointerEvent(const QUuid& id, const PointerEvent& event); void hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event); + + }; #define ADD_TYPE_MAP(entity, overlay) \ diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e54258fc3e..c7457c6443 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -73,14 +73,14 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; _currentClickingOnEntityID = UNKNOWN_ENTITY_ID; - auto entityScriptingInterface = DependencyManager::get(); + auto entityScriptingInterface = DependencyManager::get().data(); auto pointerManager = DependencyManager::get(); - connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity); - connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity); - connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity); - connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity); - connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity); - connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity); + connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity); + connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); + connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); + connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); + connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); + connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); // Forward mouse events to web entities auto handlePointerEvent = [&](const QUuid& entityID, const PointerEvent& event) { @@ -93,10 +93,10 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf QMetaObject::invokeMethod(thisEntity.get(), "handlePointerEvent", Q_ARG(const PointerEvent&, event)); } }; - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { + connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) { @@ -106,8 +106,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf QMetaObject::invokeMethod(thisEntity.get(), "hoverEnterEntity", Q_ARG(const PointerEvent&, event)); } }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { + connect(entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) { @@ -792,11 +792,11 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -std::pair EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { +QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { // If we don't have a tree, or we're in the process of shutting down, then don't // process these events. if (!_tree || _shuttingDown) { - return { FLT_MAX, UNKNOWN_ENTITY_ID }; + return UNKNOWN_ENTITY_ID; } PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); @@ -827,10 +827,10 @@ std::pair EntityTreeRenderer::mousePressEvent(QMouseEvent* event) _lastPointerEvent = pointerEvent; _lastPointerEventValid = true; - return { rayPickResult.distance, rayPickResult.entityID }; + return rayPickResult.entityID; } emit entityScriptingInterface->mousePressOffEntity(); - return { FLT_MAX, UNKNOWN_ENTITY_ID }; + return UNKNOWN_ENTITY_ID; } void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 51568ab744..8cc34cda10 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -94,7 +94,7 @@ public: void reloadEntityScripts(); // event handles which may generate entity related events - std::pair mousePressEvent(QMouseEvent* event); + QUuid mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void mouseDoublePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 9d807264aa..bce308d5fc 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -924,11 +924,7 @@ var toolBar = (function () { that.setActive = function (active) { ContextOverlay.enabled = !active; Settings.setValue(EDIT_SETTING, active); - if (active) { - Controller.captureEntityClickEvents(); - } else { - Controller.releaseEntityClickEvents(); - + if (!active) { closeExistingDialogWindow(); } if (active === isActive) { From fd88ec0d16caafe1bef84a4287b3b9e1b5cec846 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 4 Mar 2019 15:25:02 -0800 Subject: [PATCH 25/29] need to copy meshStates on main thread --- libraries/render-utils/src/Model.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 683c517a15..dd9b0280ca 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1347,9 +1347,10 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { void Model::computeMeshPartLocalBounds() { render::Transaction transaction; + auto meshStates = _meshStates; for (auto renderItem : _modelMeshRenderItemIDs) { - transaction.updateItem(renderItem, [&](ModelMeshPartPayload& data) { - const Model::MeshState& state = _meshStates.at(data._meshIndex); + transaction.updateItem(renderItem, [this, meshStates](ModelMeshPartPayload& data) { + const Model::MeshState& state = meshStates.at(data._meshIndex); if (_useDualQuaternionSkinning) { data.computeAdjustedLocalBound(state.clusterDualQuaternions); } else { From f2c248c0a26a56b8c7969a14e274422524322cf3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 4 Mar 2019 17:08:09 -0800 Subject: [PATCH 26/29] disable href and entity script events when in edit mode --- interface/src/Application.cpp | 15 +++++++-------- .../entities-renderer/src/EntityTreeRenderer.cpp | 12 +++++++----- libraries/entities/src/EntityTree.cpp | 8 ++++++++ libraries/entities/src/EntityTree.h | 4 ++++ libraries/script-engine/src/ScriptEngine.cpp | 4 +++- scripts/system/edit.js | 6 +++++- 6 files changed, 34 insertions(+), 15 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e41d51de47..c85ced5dee 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1061,6 +1061,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(PluginManager::getInstance().data(), &PluginManager::inputDeviceRunningChanged, controllerScriptingInterface, &controller::ScriptingInterface::updateRunningInputDevices); + EntityTree::setEntityClicksCapturedOperator([this] { + return _controllerScriptingInterface->areEntityClicksCaptured(); + }); + _entityClipboard->createRootElement(); #ifdef Q_OS_WIN @@ -4425,10 +4429,8 @@ void Application::mousePressEvent(QMouseEvent* event) { #endif QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers()); - if (!_controllerScriptingInterface->areEntityClicksCaptured()) { - QUuid result = getEntities()->mousePressEvent(&mappedEvent); - setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(result) ? result : UNKNOWN_ENTITY_ID); - } + QUuid result = getEntities()->mousePressEvent(&mappedEvent); + setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(result) ? result : UNKNOWN_ENTITY_ID); _controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts @@ -4467,10 +4469,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) { transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers()); - - if (!_controllerScriptingInterface->areEntityClicksCaptured()) { - getEntities()->mouseDoublePressEvent(&mappedEvent); - } + getEntities()->mouseDoublePressEvent(&mappedEvent); // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface->isMouseCaptured()) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index c7457c6443..576137390e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -805,11 +805,13 @@ QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); EntityItemPointer entity; if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) { - auto properties = entity->getProperties(); - QString urlString = properties.getHref(); - QUrl url = QUrl(urlString, QUrl::StrictMode); - if (url.isValid() && !url.isEmpty()){ - DependencyManager::get()->handleLookupString(urlString); + if (!EntityTree::areEntityClicksCaptured()) { + auto properties = entity->getProperties(); + QString urlString = properties.getHref(); + QUrl url = QUrl(urlString, QUrl::StrictMode); + if (url.isValid() && !url.isEmpty()) { + DependencyManager::get()->handleLookupString(urlString); + } } glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 6e404ce690..644fe0620e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2972,6 +2972,7 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const { std::function EntityTree::_getEntityObjectOperator = nullptr; std::function EntityTree::_textSizeOperator = nullptr; +std::function EntityTree::_areEntityClicksCapturedOperator = nullptr; QObject* EntityTree::getEntityObject(const QUuid& id) { if (_getEntityObjectOperator) { @@ -2987,6 +2988,13 @@ QSizeF EntityTree::textSize(const QUuid& id, const QString& text) { return QSizeF(0.0f, 0.0f); } +bool EntityTree::areEntityClicksCaptured() { + if (_areEntityClicksCapturedOperator) { + return _areEntityClicksCapturedOperator(); + } + return false; +} + void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, MovingEntitiesOperator& moveOperator, bool force, bool tellServer) { // if the queryBox has changed, tell the entity-server diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index dcce0e4b99..01829df7f8 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -268,6 +268,9 @@ public: static void setTextSizeOperator(std::function textSizeOperator) { _textSizeOperator = textSizeOperator; } static QSizeF textSize(const QUuid& id, const QString& text); + static void setEntityClicksCapturedOperator(std::function areEntityClicksCapturedOperator) { _areEntityClicksCapturedOperator = areEntityClicksCapturedOperator; } + static bool areEntityClicksCaptured(); + std::map getNamedPaths() const { return _namedPaths; } void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, @@ -378,6 +381,7 @@ private: static std::function _getEntityObjectOperator; static std::function _textSizeOperator; + static std::function _areEntityClicksCapturedOperator; std::vector _staleProxies; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 5d33a6a061..825017b1fe 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -976,7 +976,9 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& using PointerHandler = std::function; auto makePointerHandler = [this](QString eventName) -> PointerHandler { return [this, eventName](const EntityItemID& entityItemID, const PointerEvent& event) { - forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) }); + if (!EntityTree::areEntityClicksCaptured()) { + forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) }); + } }; }; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index bce308d5fc..9d807264aa 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -924,7 +924,11 @@ var toolBar = (function () { that.setActive = function (active) { ContextOverlay.enabled = !active; Settings.setValue(EDIT_SETTING, active); - if (!active) { + if (active) { + Controller.captureEntityClickEvents(); + } else { + Controller.releaseEntityClickEvents(); + closeExistingDialogWindow(); } if (active === isActive) { From 29a308dcaacfa138b721d183a146b38befe3988a Mon Sep 17 00:00:00 2001 From: amer cerkic Date: Tue, 5 Mar 2019 10:07:33 -0800 Subject: [PATCH 27/29] adding modelScale initialization so that it does not fail the validScale check assert --- libraries/entities/src/ModelEntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 505ee26c0f..87d80ee044 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -40,6 +40,7 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem( _type = EntityTypes::Model; _lastKnownCurrentFrame = -1; _visuallyReady = false; + _modelScale=glm::vec3(1.0f); } const QString ModelEntityItem::getTextures() const { From 36e9c604e9e70efde65c5a7f64044d0da052a022 Mon Sep 17 00:00:00 2001 From: amer cerkic Date: Tue, 5 Mar 2019 10:45:15 -0800 Subject: [PATCH 28/29] fixed based on comment --- libraries/entities/src/ModelEntityItem.cpp | 1 - libraries/entities/src/ModelEntityItem.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 87d80ee044..505ee26c0f 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -40,7 +40,6 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem( _type = EntityTypes::Model; _lastKnownCurrentFrame = -1; _visuallyReady = false; - _modelScale=glm::vec3(1.0f); } const QString ModelEntityItem::getTextures() const { diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 0efbbbb16c..89acd6179b 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -164,7 +164,7 @@ protected: int _lastKnownCurrentFrame{-1}; glm::u8vec3 _color; - glm::vec3 _modelScale; + glm::vec3 _modelScale {1.0f}; QString _modelURL; bool _relayParentJoints; bool _groupCulled { false }; From fab343a1d4d703009c585aca2bc7eba64e983cfe Mon Sep 17 00:00:00 2001 From: amer cerkic Date: Tue, 5 Mar 2019 10:52:54 -0800 Subject: [PATCH 29/29] correcting spacing --- libraries/entities/src/ModelEntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 89acd6179b..08468617ba 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -164,7 +164,7 @@ protected: int _lastKnownCurrentFrame{-1}; glm::u8vec3 _color; - glm::vec3 _modelScale {1.0f}; + glm::vec3 _modelScale { 1.0f }; QString _modelURL; bool _relayParentJoints; bool _groupCulled { false };