From 99e69616fabe9cd2fba6d77ef0ce27594c53e74c Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 20 Jan 2016 17:50:52 -0800 Subject: [PATCH 01/56] basic comms --- .../junkyard/junkyardResetEntityScript.js | 32 ++++++++++++++ .../junkyard/junkyardResetEntitySpawner.js | 43 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 examples/junkyard/junkyardResetEntityScript.js create mode 100644 examples/junkyard/junkyardResetEntitySpawner.js diff --git a/examples/junkyard/junkyardResetEntityScript.js b/examples/junkyard/junkyardResetEntityScript.js new file mode 100644 index 0000000000..71308f81cd --- /dev/null +++ b/examples/junkyard/junkyardResetEntityScript.js @@ -0,0 +1,32 @@ +// junkyardResetEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 1/20/16. +// Copyright 2016 High Fidelity, Inc. +// +// This entity script resets the junkyard when triggered +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../libraries/utils.js"); + var _this; + var JunkyardResetter = function() { + _this = this; + }; + + JunkyardResetter.prototype = { + startDistantGrab: function() { + print("YAAAAAAAA") + }, + + startNearGrab: function() { + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + }; + return new JunkyardResetter(); +}); \ No newline at end of file diff --git a/examples/junkyard/junkyardResetEntitySpawner.js b/examples/junkyard/junkyardResetEntitySpawner.js new file mode 100644 index 0000000000..8db23517a3 --- /dev/null +++ b/examples/junkyard/junkyardResetEntitySpawner.js @@ -0,0 +1,43 @@ + // + // junkyardResetEntitySpawner.js + // examples/junkyard + // + // Created by Eric Levin on 1/20/16. + // Copyright 2016 High Fidelity, Inc. + // + // This script spawns an entity which, when triggered, will reset the junkyard + // + // + // Distributed under the Apache License, Version 2.0. + // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + // + + var orientation = Camera.getOrientation(); + orientation = Quat.safeEulerAngles(orientation); + orientation.x = 0; + orientation = Quat.fromVec3Degrees(orientation); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); + + + var scriptURL = Script.resolvePath("junkyardResetEntityScript.js"); + var resetEntity = Entities.addEntity({ + type: "Box", + position: center, + script: scriptURL, + dimensions: { + x: 1, + y: 1, + z: 1 + }, + color: { + red: 200, + green: 10, + blue: 200 + } + }); + + function cleanup() { + Entities.deleteEntity(resetEntity); + } + + Script.scriptEnding.connect(cleanup); \ No newline at end of file From 641607f334df1798a7847d3c8c4e08e5c2f8697b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 21 Jan 2016 10:22:56 -0800 Subject: [PATCH 02/56] reset script --- examples/edit.js | 2 ++ .../junkyard/junkyardResetEntityScript.js | 28 +++++++++++++++++++ .../junkyard/junkyardResetEntitySpawner.js | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/examples/edit.js b/examples/edit.js index 41f25cb2e5..e2b4d83541 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1355,6 +1355,8 @@ function importSVO(importURL) { position = getPositionToCreateEntity(); } if (position != null) { + + print("POSITION OF ENTITIES" + JSON.stringify(position)) var pastedEntityIDs = Clipboard.pasteEntities(position); if (isActive) { diff --git a/examples/junkyard/junkyardResetEntityScript.js b/examples/junkyard/junkyardResetEntityScript.js index 71308f81cd..7911a6a74b 100644 --- a/examples/junkyard/junkyardResetEntityScript.js +++ b/examples/junkyard/junkyardResetEntityScript.js @@ -12,6 +12,7 @@ (function() { Script.include("../libraries/utils.js"); var _this; + var importURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json"; var JunkyardResetter = function() { _this = this; }; @@ -22,6 +23,33 @@ }, startNearGrab: function() { + + }, + + clickReleaseOnEntity: function(entityId, mouseEvent) { + if (!mouseEvent.isLeftButton) { + return; + } + this.reset(); + + }, + + reset: function() { + // Delete everything and re-import the junkyard arf + var e = Entities.findEntities(MyAvatar.position, 1000); + for (i = 0; i < e.length; i++) { + Entities.deleteEntity(e[i]); + } + this.importArf(); + }, + + importArf: function() { + Clipboard.importEntities(importURL); + Clipboard.pasteEntities({ + x: 0, + y: 0, + z: 0 + }); }, preload: function(entityID) { diff --git a/examples/junkyard/junkyardResetEntitySpawner.js b/examples/junkyard/junkyardResetEntitySpawner.js index 8db23517a3..24eb29b950 100644 --- a/examples/junkyard/junkyardResetEntitySpawner.js +++ b/examples/junkyard/junkyardResetEntitySpawner.js @@ -19,7 +19,7 @@ var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); - var scriptURL = Script.resolvePath("junkyardResetEntityScript.js"); + var scriptURL = Script.resolvePath("junkyardResetEntityScript.js?v1" + Math.random()); var resetEntity = Entities.addEntity({ type: "Box", position: center, From b3b1793035adbd39636dced583405f7b7fdec35a Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 21 Jan 2016 12:03:09 -0800 Subject: [PATCH 03/56] removed edit.js changes --- examples/edit.js | 2 -- examples/junkyard/junkyardClientReset.js | 35 +++++++++++++++++++ .../junkyard/junkyardResetEntityScript.js | 12 +++---- .../junkyard/junkyardResetEntitySpawner.js | 9 ++--- 4 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 examples/junkyard/junkyardClientReset.js diff --git a/examples/edit.js b/examples/edit.js index e2b4d83541..41f25cb2e5 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1355,8 +1355,6 @@ function importSVO(importURL) { position = getPositionToCreateEntity(); } if (position != null) { - - print("POSITION OF ENTITIES" + JSON.stringify(position)) var pastedEntityIDs = Clipboard.pasteEntities(position); if (isActive) { diff --git a/examples/junkyard/junkyardClientReset.js b/examples/junkyard/junkyardClientReset.js new file mode 100644 index 0000000000..c712737cca --- /dev/null +++ b/examples/junkyard/junkyardClientReset.js @@ -0,0 +1,35 @@ +// +// junkyardClientReset.js +// examples/junkyard +// +// Created by Eric Levin on 1/21/16. +// Copyright 2016 High Fidelity, Inc. +// +// This script resets the junkyard scene +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var importURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json"; + +reset(); + +function reset() { + // Delete everything and re-import the junkyard arf + var e = Entities.findEntities(MyAvatar.position, 1000); + for (i = 0; i < e.length; i++) { + Entities.deleteEntity(e[i]); + } + importArf(); +} + +function importArf() { + Clipboard.importEntities(importURL); + Clipboard.pasteEntities({ + x: 0, + y: 0, + z: 0 + }); +} \ No newline at end of file diff --git a/examples/junkyard/junkyardResetEntityScript.js b/examples/junkyard/junkyardResetEntityScript.js index 7911a6a74b..7a9ee54d22 100644 --- a/examples/junkyard/junkyardResetEntityScript.js +++ b/examples/junkyard/junkyardResetEntityScript.js @@ -18,13 +18,6 @@ }; JunkyardResetter.prototype = { - startDistantGrab: function() { - print("YAAAAAAAA") - }, - - startNearGrab: function() { - - }, clickReleaseOnEntity: function(entityId, mouseEvent) { if (!mouseEvent.isLeftButton) { @@ -38,7 +31,10 @@ // Delete everything and re-import the junkyard arf var e = Entities.findEntities(MyAvatar.position, 1000); for (i = 0; i < e.length; i++) { - Entities.deleteEntity(e[i]); + // Don't delete our reset entity + if (JSON.stringify(this.entityID) !== JSON.stringify(e[i])) { + Entities.deleteEntity(e[i]); + } } this.importArf(); }, diff --git a/examples/junkyard/junkyardResetEntitySpawner.js b/examples/junkyard/junkyardResetEntitySpawner.js index 24eb29b950..b2627c7cd9 100644 --- a/examples/junkyard/junkyardResetEntitySpawner.js +++ b/examples/junkyard/junkyardResetEntitySpawner.js @@ -21,13 +21,14 @@ var scriptURL = Script.resolvePath("junkyardResetEntityScript.js?v1" + Math.random()); var resetEntity = Entities.addEntity({ - type: "Box", + type: "Model", + modelURL: "http://hifi-content.s3.amazonaws.com/caitlyn/dev/Blueprint%20Objects/Asylum/Asylum_Table/Asylum_Table.fbx", position: center, script: scriptURL, dimensions: { - x: 1, - y: 1, - z: 1 + x: 2.8, + y: 1.76, + z: 1.32 }, color: { red: 200, From e11e4d196b87ce9a28caa57ab5a9a21ca671d518 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 21 Jan 2016 13:24:07 -0800 Subject: [PATCH 04/56] tweaks for coding standards --- examples/junkyard/junkyardClientReset.js | 12 ++++-------- examples/junkyard/junkyardResetEntityScript.js | 11 ++++------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/examples/junkyard/junkyardClientReset.js b/examples/junkyard/junkyardClientReset.js index c712737cca..8aea8470dd 100644 --- a/examples/junkyard/junkyardClientReset.js +++ b/examples/junkyard/junkyardClientReset.js @@ -12,8 +12,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var importURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json"; - +var IMPORT_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json"; +var PASTE_ENTITIES_LOCATION = {x: 0, y: 0, z: 0}; reset(); function reset() { @@ -26,10 +26,6 @@ function reset() { } function importArf() { - Clipboard.importEntities(importURL); - Clipboard.pasteEntities({ - x: 0, - y: 0, - z: 0 - }); + Clipboard.importEntities(IMPORT_URL); + Clipboard.pasteEntities(PASTE_ENTITIES_LOCATION); } \ No newline at end of file diff --git a/examples/junkyard/junkyardResetEntityScript.js b/examples/junkyard/junkyardResetEntityScript.js index 7a9ee54d22..9bd7ec02a5 100644 --- a/examples/junkyard/junkyardResetEntityScript.js +++ b/examples/junkyard/junkyardResetEntityScript.js @@ -12,7 +12,8 @@ (function() { Script.include("../libraries/utils.js"); var _this; - var importURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json"; + var IMPORT_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/arfs/junkyard.json"; + var PASTE_ENTITIES_LOCATION = {x: 0, y: 0, z: 0}; var JunkyardResetter = function() { _this = this; }; @@ -40,12 +41,8 @@ }, importArf: function() { - Clipboard.importEntities(importURL); - Clipboard.pasteEntities({ - x: 0, - y: 0, - z: 0 - }); + Clipboard.importEntities(IMPORT_URL); + Clipboard.pasteEntities(PASTE_ENTITIES_LOCATION); }, preload: function(entityID) { From 2e1727610ef061167db5bae3743e590ebc8104d5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 21 Jan 2016 15:52:59 -0800 Subject: [PATCH 05/56] MyAvatar updates the entity-server about queryAACubes of children entities --- interface/src/avatar/MyAvatar.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b1688b8f9e..b6dcc96c22 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -50,6 +50,7 @@ #include "Util.h" #include "InterfaceLogging.h" #include "DebugDraw.h" +#include "EntityEditPacketSender.h" using namespace std; @@ -352,6 +353,21 @@ void MyAvatar::simulate(float deltaTime) { // consider updating our billboard maybeUpdateBillboard(); + + locationChanged(); + // if a entity-child of this avatar has moved outside of its queryAACube, update the cube and tell the entity server. + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + if (packetSender) { + EntityItemProperties properties = entity->getProperties(); + properties.setQueryAACubeDirty(); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties); + entity->setLastBroadcast(usecTimestampNow()); + } + } + }); } glm::mat4 MyAvatar::getSensorToWorldMatrix() const { From cbed54268212265943f4ccebf57f370001c92704 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 21 Jan 2016 15:53:19 -0800 Subject: [PATCH 06/56] MyAvatar updates the entity-server about queryAACubes of children entities --- libraries/entities/src/EntityItemProperties.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 0f0bab5fdd..8a2431eb9e 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -254,6 +254,8 @@ public: void setLinePointsDirty() {_linePointsChanged = true; } + void setQueryAACubeDirty() { _queryAACubeChanged = true; } + void setCreated(QDateTime& v); bool hasTerseUpdateChanges() const; From 872a00d518d8cc1636cd857f824343cad4c870dd Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 21 Jan 2016 15:53:47 -0800 Subject: [PATCH 07/56] entities that are children shouldn't (at this point) be dynamic. --- libraries/physics/src/EntityMotionState.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 4e5f942265..9982b4d107 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -144,7 +144,12 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const { return MOTION_TYPE_STATIC; } assert(entityTreeIsLocked()); + if (_entity->getDynamic()) { + if (!_entity->getParentID().isNull()) { + // if something would have been dynamic but is a child of something else, force it to be kinematic, instead. + return MOTION_TYPE_KINEMATIC; + } return MOTION_TYPE_DYNAMIC; } return (_entity->isMoving() || _entity->hasActions()) ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC; From 83827b6d241290c1f7c2b658480ecd61e1c8a3a7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 14:57:32 -0800 Subject: [PATCH 08/56] add startup shortcut in current users context --- cmake/templates/NSIS.template.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 0b37cd5853..b398c2af7d 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -993,6 +993,12 @@ Function HandlePostInstallOptions ${NSD_GetState} $ServerStartupCheckbox $ServerStartupState ${If} $ServerStartupState == ${BST_CHECKED} + ; in case we added a shortcut in the global context, pull that now + SetShellVarContext all + Delete "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk" + + ; make a startup shortcut in this user's current context + SetShellVarContext current CreateShortCut "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" !insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES From 6934594bb96bb95c7c9ac413f70ba801a4914a70 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:20:51 -0800 Subject: [PATCH 09/56] always use current context, ignore CPack InstallOptions --- cmake/templates/NSIS.template.in | 121 +------------------------------ 1 file changed, 2 insertions(+), 119 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index b398c2af7d..24f1e0000f 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -12,13 +12,7 @@ Var MUI_TEMP Var STARTMENU_FOLDER - Var SV_ALLUSERS Var START_MENU - Var DO_NOT_ADD_TO_PATH - Var ADD_TO_PATH_ALL_USERS - Var ADD_TO_PATH_CURRENT_USER - Var INSTALL_DESKTOP - Var IS_DEFAULT_INSTALLDIR ;-------------------------------- ;Include Modern UI @@ -678,7 +672,6 @@ FunctionEnd ;Keep these lines before any File command ;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA) - ReserveFile "NSIS.InstallOptions.ini" ReserveFile "@POST_INSTALL_OPTIONS_PATH@" ;-------------------------------- @@ -768,7 +761,6 @@ Section "-Core installation" Push "Contact" Push "@CPACK_NSIS_CONTACT@" Call ConditionalAddToRegisty - !insertmacro INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts @@ -799,27 +791,10 @@ Section "-Core installation" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\@UNINSTALLER_NAME@" - ;Read a value from an InstallOptions INI file - !insertmacro INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State" - !insertmacro INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State" - !insertmacro INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State" - ; Write special uninstall registry entries Push "StartMenu" Push "$STARTMENU_FOLDER" Call ConditionalAddToRegisty - Push "DoNotAddToPath" - Push "$DO_NOT_ADD_TO_PATH" - Call ConditionalAddToRegisty - Push "AddToPathAllUsers" - Push "$ADD_TO_PATH_ALL_USERS" - Call ConditionalAddToRegisty - Push "AddToPathCurrentUser" - Push "$ADD_TO_PATH_CURRENT_USER" - Call ConditionalAddToRegisty - Push "InstallToDesktop" - Push "$INSTALL_DESKTOP" - Call ConditionalAddToRegisty !insertmacro MUI_STARTMENU_WRITE_END @@ -835,14 +810,6 @@ Section "-Add to path" doNotAddToPath: SectionEnd -;-------------------------------- -; Create custom pages -Function InstallOptionsPage - !insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@" - !insertmacro INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini" - -FunctionEnd - ; Make sure nsDialogs is included before we use it !include "nsdialogs.nsh" @@ -1012,8 +979,6 @@ Function HandlePostInstallOptions ${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState ${If} $CopyFromProductionState == ${BST_CHECKED} - SetShellVarContext current - StrCpy $0 "$APPDATA\@BUILD_ORGANIZATION@" ; we need to copy whatever is in the data folder for production build to the data folder for this build @@ -1115,26 +1080,6 @@ Function un.onInit Quit ${EndSwitch} - ClearErrors - UserInfo::GetName - IfErrors noLM - Pop $0 - UserInfo::GetAccountType - Pop $1 - StrCmp $1 "Admin" 0 +3 - SetShellVarContext all - ;MessageBox MB_OK 'User "$0" is in the Admin group' - Goto done - StrCmp $1 "Power" 0 +3 - SetShellVarContext all - ;MessageBox MB_OK 'User "$0" is in the Power Users group' - Goto done - - noLM: - ;Get installation folder from registry if available - - done: - FunctionEnd ;--- Add/Remove callback functions: --- @@ -1195,16 +1140,6 @@ Section "Uninstall" ReadRegStr $START_MENU SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu" ;MessageBox MB_OK "Start menu is in: $START_MENU" - ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \ - "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "DoNotAddToPath" - ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \ - "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathAllUsers" - ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \ - "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathCurrentUser" - ;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS" - ReadRegStr $INSTALL_DESKTOP SHCTX \ - "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "InstallToDesktop" - ;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP " @CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@ @@ -1294,16 +1229,6 @@ Section "Uninstall" SectionEnd !endif -;-------------------------------- -; determine admin versus local install -; Is install for "AllUsers" or "JustMe"? -; Default to "JustMe" - set to "AllUsers" if admin or on Win9x -; This function is used for the very first "custom page" of the installer. -; This custom page does not show up visibly, but it executes prior to the -; first visible page and sets up $INSTDIR properly... -; Choose different default installation folder based on SV_ALLUSERS... -; "Program Files" for AllUsers, "My Documents" for JustMe... - Function .onInit !ifdef INNER @@ -1348,48 +1273,6 @@ inst: ; Reads components status for registry !insertmacro SectionList "InitSection" - ; check to see if /D has been used to change - ; the install directory by comparing it to the - ; install directory that is expected to be the - ; default - StrCpy $IS_DEFAULT_INSTALLDIR 0 - StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2 - StrCpy $IS_DEFAULT_INSTALLDIR 1 - - StrCpy $SV_ALLUSERS "JustMe" - ; if default install dir then change the default - ; if it is installed for JustMe - StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 - StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@" - - ClearErrors - UserInfo::GetName - IfErrors noLM - Pop $0 - UserInfo::GetAccountType - Pop $1 - StrCmp $1 "Admin" 0 +4 - SetShellVarContext all - ;MessageBox MB_OK 'User "$0" is in the Admin group' - StrCpy $SV_ALLUSERS "AllUsers" - Goto done - StrCmp $1 "Power" 0 +4 - SetShellVarContext all - ;MessageBox MB_OK 'User "$0" is in the Power Users group' - StrCpy $SV_ALLUSERS "AllUsers" - Goto done - - noLM: - StrCpy $SV_ALLUSERS "AllUsers" - ;Get installation folder from registry if available - - done: - StrCmp $SV_ALLUSERS "AllUsers" 0 +3 - StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 - StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" - - StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage - !insertmacro INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" - - noOptionsPage: + ; use user for context of data/startup folders + SetShellVarContext current FunctionEnd From f393c5eeda0f098aa04f13ce473a11bcd35c9fc8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:25:35 -0800 Subject: [PATCH 10/56] remove CMake templated code for path changes --- cmake/templates/NSIS.template.in | 279 ------------------------------- 1 file changed, 279 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 24f1e0000f..0bd1dfd214 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -199,277 +199,10 @@ Var AR_RegFlags !define MUI_HEADERIMAGE_UNBITMAP "@UNINSTALLER_HEADER_IMAGE@" !define MUI_ABORTWARNING -;-------------------------------- -; path functions - -!verbose 3 -!include "WinMessages.NSH" -!verbose 4 - -;---------------------------------------- -; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02" -;---------------------------------------- -!verbose 3 -!include "WinMessages.NSH" -!verbose 4 -;==================================================== -; get_NT_environment -; Returns: the selected environment -; Output : head of the stack -;==================================================== -!macro select_NT_profile UN -Function ${UN}select_NT_profile - StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single - DetailPrint "Selected environment for all users" - Push "all" - Return - environment_single: - DetailPrint "Selected environment for current user only." - Push "current" - Return -FunctionEnd -!macroend -!insertmacro select_NT_profile "" -!insertmacro select_NT_profile "un." -;---------------------------------------------------- -!define NT_current_env 'HKCU "Environment"' -!define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' - -!ifndef WriteEnvStr_RegKey - !ifdef ALL_USERS - !define WriteEnvStr_RegKey \ - 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' - !else - !define WriteEnvStr_RegKey 'HKCU "Environment"' - !endif -!endif - -; AddToPath - Adds the given dir to the search path. -; Input - head of the stack -; Note - Win9x systems requires reboot - -Function AddToPath - Exch $0 - Push $1 - Push $2 - Push $3 - - # don't add if the path doesn't exist - IfFileExists "$0\*.*" "" AddToPath_done - - ReadEnvStr $1 PATH - ; if the path is too long for a NSIS variable NSIS will return a 0 - ; length string. If we find that, then warn and skip any path - ; modification as it will trash the existing path. - StrLen $2 $1 - IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done - CheckPathLength_ShowPathWarning: - Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!" - Goto AddToPath_done - CheckPathLength_Done: - Push "$1;" - Push "$0;" - Call StrStr - Pop $2 - StrCmp $2 "" "" AddToPath_done - Push "$1;" - Push "$0\;" - Call StrStr - Pop $2 - StrCmp $2 "" "" AddToPath_done - GetFullPathName /SHORT $3 $0 - Push "$1;" - Push "$3;" - Call StrStr - Pop $2 - StrCmp $2 "" "" AddToPath_done - Push "$1;" - Push "$3\;" - Call StrStr - Pop $2 - StrCmp $2 "" "" AddToPath_done - - Call IsNT - Pop $1 - StrCmp $1 1 AddToPath_NT - ; Not on NT - StrCpy $1 $WINDIR 2 - FileOpen $1 "$1\autoexec.bat" a - FileSeek $1 -1 END - FileReadByte $1 $2 - IntCmp $2 26 0 +2 +2 # DOS EOF - FileSeek $1 -1 END # write over EOF - FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" - FileClose $1 - SetRebootFlag true - Goto AddToPath_done - - AddToPath_NT: - StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey - ReadRegStr $1 ${NT_current_env} "PATH" - Goto DoTrim - ReadAllKey: - ReadRegStr $1 ${NT_all_env} "PATH" - DoTrim: - StrCmp $1 "" AddToPath_NTdoIt - Push $1 - Call Trim - Pop $1 - StrCpy $0 "$1;$0" - AddToPath_NTdoIt: - StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey - WriteRegExpandStr ${NT_current_env} "PATH" $0 - Goto DoSend - WriteAllKey: - WriteRegExpandStr ${NT_all_env} "PATH" $0 - DoSend: - SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - - AddToPath_done: - Pop $3 - Pop $2 - Pop $1 - Pop $0 -FunctionEnd - - -; RemoveFromPath - Remove a given dir from the path -; Input: head of the stack - -Function un.RemoveFromPath - Exch $0 - Push $1 - Push $2 - Push $3 - Push $4 - Push $5 - Push $6 - - IntFmt $6 "%c" 26 # DOS EOF - - Call un.IsNT - Pop $1 - StrCmp $1 1 unRemoveFromPath_NT - ; Not on NT - StrCpy $1 $WINDIR 2 - FileOpen $1 "$1\autoexec.bat" r - GetTempFileName $4 - FileOpen $2 $4 w - GetFullPathName /SHORT $0 $0 - StrCpy $0 "SET PATH=%PATH%;$0" - Goto unRemoveFromPath_dosLoop - - unRemoveFromPath_dosLoop: - FileRead $1 $3 - StrCpy $5 $3 1 -1 # read last char - StrCmp $5 $6 0 +2 # if DOS EOF - StrCpy $3 $3 -1 # remove DOS EOF so we can compare - StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine - StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine - StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine - StrCmp $3 "" unRemoveFromPath_dosLoopEnd - FileWrite $2 $3 - Goto unRemoveFromPath_dosLoop - unRemoveFromPath_dosLoopRemoveLine: - SetRebootFlag true - Goto unRemoveFromPath_dosLoop - - unRemoveFromPath_dosLoopEnd: - FileClose $2 - FileClose $1 - StrCpy $1 $WINDIR 2 - Delete "$1\autoexec.bat" - CopyFiles /SILENT $4 "$1\autoexec.bat" - Delete $4 - Goto unRemoveFromPath_done - - unRemoveFromPath_NT: - StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey - ReadRegStr $1 ${NT_current_env} "PATH" - Goto unDoTrim - unReadAllKey: - ReadRegStr $1 ${NT_all_env} "PATH" - unDoTrim: - StrCpy $5 $1 1 -1 # copy last char - StrCmp $5 ";" +2 # if last char != ; - StrCpy $1 "$1;" # append ; - Push $1 - Push "$0;" - Call un.StrStr ; Find `$0;` in $1 - Pop $2 ; pos of our dir - StrCmp $2 "" unRemoveFromPath_done - ; else, it is in path - # $0 - path to add - # $1 - path var - StrLen $3 "$0;" - StrLen $4 $2 - StrCpy $5 $1 -$4 # $5 is now the part before the path to remove - StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove - StrCpy $3 $5$6 - - StrCpy $5 $3 1 -1 # copy last char - StrCmp $5 ";" 0 +2 # if last char == ; - StrCpy $3 $3 -1 # remove last char - - StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey - WriteRegExpandStr ${NT_current_env} "PATH" $3 - Goto unDoSend - unWriteAllKey: - WriteRegExpandStr ${NT_all_env} "PATH" $3 - unDoSend: - SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - - unRemoveFromPath_done: - Pop $6 - Pop $5 - Pop $4 - Pop $3 - Pop $2 - Pop $1 - Pop $0 -FunctionEnd - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Uninstall sutff -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ########################################### # Utility Functions # ########################################### -;==================================================== -; IsNT - Returns 1 if the current system is NT, 0 -; otherwise. -; Output: head of the stack -;==================================================== -; IsNT -; no input -; output, top of the stack = 1 if NT or 0 if not -; -; Usage: -; Call IsNT -; Pop $R0 -; ($R0 at this point is 1 or 0) - -!macro IsNT un -Function ${un}IsNT - Push $0 - ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion - StrCmp $0 "" 0 IsNT_yes - ; we are not NT. - Pop $0 - Push 0 - Return - - IsNT_yes: - ; NT!!! - Pop $0 - Push 1 -FunctionEnd -!macroend -!insertmacro IsNT "" -!insertmacro IsNT "un." - ; StrStr ; input, top of stack = string to search for ; top of stack-1 = string to search in @@ -802,14 +535,6 @@ Section "-Core installation" SectionEnd -Section "-Add to path" - Push $INSTDIR\bin - StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath - StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0 - Call AddToPath - doNotAddToPath: -SectionEnd - ; Make sure nsDialogs is included before we use it !include "nsdialogs.nsh" @@ -1222,10 +947,6 @@ Section "Uninstall" DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" - Push $INSTDIR\bin - StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0 - Call un.RemoveFromPath - doNotRemoveFromPath: SectionEnd !endif From aa2d19a61148a618c0aa2754351b3016d4f08f1f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:32:23 -0800 Subject: [PATCH 11/56] remove two unused utility functions --- cmake/templates/NSIS.template.in | 64 -------------------------------- 1 file changed, 64 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 0bd1dfd214..09aa92ed47 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -203,70 +203,6 @@ Var AR_RegFlags # Utility Functions # ########################################### -; StrStr -; input, top of stack = string to search for -; top of stack-1 = string to search in -; output, top of stack (replaces with the portion of the string remaining) -; modifies no other variables. -; -; Usage: -; Push "this is a long ass string" -; Push "ass" -; Call StrStr -; Pop $R0 -; ($R0 at this point is "ass string") - -!macro StrStr un -Function ${un}StrStr -Exch $R1 ; st=haystack,old$R1, $R1=needle - Exch ; st=old$R1,haystack - Exch $R2 ; st=old$R1,old$R2, $R2=haystack - Push $R3 - Push $R4 - Push $R5 - StrLen $R3 $R1 - StrCpy $R4 0 - ; $R1=needle - ; $R2=haystack - ; $R3=len(needle) - ; $R4=cnt - ; $R5=tmp - loop: - StrCpy $R5 $R2 $R3 $R4 - StrCmp $R5 $R1 done - StrCmp $R5 "" done - IntOp $R4 $R4 + 1 - Goto loop -done: - StrCpy $R1 $R2 "" $R4 - Pop $R5 - Pop $R4 - Pop $R3 - Pop $R2 - Exch $R1 -FunctionEnd -!macroend -!insertmacro StrStr "" -!insertmacro StrStr "un." - -Function Trim ; Added by Pelaca - Exch $R1 - Push $R2 -Loop: - StrCpy $R2 "$R1" 1 -1 - StrCmp "$R2" " " RTrim - StrCmp "$R2" "$\n" RTrim - StrCmp "$R2" "$\r" RTrim - StrCmp "$R2" ";" RTrim - GoTo Done -RTrim: - StrCpy $R1 "$R1" -1 - Goto Loop -Done: - Pop $R2 - Exch $R1 -FunctionEnd - Function ConditionalAddToRegisty Pop $0 Pop $1 From 150a8d388ca879ea71aaf532bec43037bf020ce7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:37:11 -0800 Subject: [PATCH 12/56] allow user to close running exec and continue --- cmake/templates/NSIS.template.in | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 09aa92ed47..801b773844 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -682,14 +682,25 @@ FunctionEnd !include nsProcess.nsh !macro PromptForRunningApplication applicationName displayName action prompter - ${nsProcess::FindProcess} ${applicationName} $R0 + !define UniqueID ${__LINE__} - ${If} $R0 == 0 - ; the process is running, ask the user if they want us to close it - MessageBox MB_OK|MB_ICONEXCLAMATION \ - "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it and try again." - Abort - ${EndIf} + Prompt_${UniqueID}: + + ${nsProcess::FindProcess} ${applicationName} $R0 + + ${If} $R0 == 0 + + ; the process is running, ask the user to close it + MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \ + "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it to continue." \ + /SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0 + + ; If the user decided to cancel, stop the current installer/uninstaller + Abort + + ${EndIf} + + !undef UniqueID !macroend !macro CheckForRunningApplications action prompter From aeb097664ee44781e4e9fe24c0c919c5b01825ed Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:38:57 -0800 Subject: [PATCH 13/56] remove InstallOptionsPage from list of pages --- cmake/templates/NSIS.template.in | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 801b773844..a620347e0d 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -261,7 +261,6 @@ FunctionEnd !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" - Page custom InstallOptionsPage !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration From eabafa800cebf913e063deefa954413014361fde Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:51:47 -0800 Subject: [PATCH 14/56] clarify application close behaviour --- cmake/templates/NSIS.template.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index a620347e0d..9c29e91e81 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -691,7 +691,7 @@ FunctionEnd ; the process is running, ask the user to close it MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \ - "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it to continue." \ + "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it and click Retry to continue." \ /SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0 ; If the user decided to cancel, stop the current installer/uninstaller From 9d4b5292236d16ac8a83c0fd26309a55ea46070b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:57:42 -0800 Subject: [PATCH 15/56] remove any old admin desktop/sm shortcuts --- cmake/templates/NSIS.template.in | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 9c29e91e81..0505e44c43 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -432,12 +432,21 @@ Section "-Core installation" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts + + ; in case the user has any shortcuts from prior to when we were using the user context, remove those now + SetShellVarContext all + RMDir /r "$SMPROGRAMS\$STARTMENU_FOLDER" + + ; go back to current user context before setting up the startmenu folder + SetShellVarContext current + CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" @CPACK_NSIS_CREATE_ICONS@ @CPACK_NSIS_CREATE_ICONS_EXTRA@ ; Conditional handling for Interface specific options ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@INTERFACE_SHORTCUT_NAME@.lnk" \ "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" @@ -592,6 +601,15 @@ Var LaunchNowState Var CopyFromProductionState Function HandlePostInstallOptions + ; If the user created shortcuts from a time prior to when we using current user context, pull them now + SetShellVarContext all + + Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk" + Delete "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk" + + ; return the context to the current user so the shortcuts are correct + SetShellVarContext current + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if the user asked for a desktop shortcut to High Fidelity ${NSD_GetState} $DesktopClientCheckbox $DesktopClientState From c7228ce02073a3680e9aebc65ecb5d353c540f45 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 15:59:17 -0800 Subject: [PATCH 16/56] ensure current user context for uninstaller --- cmake/templates/NSIS.template.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 0505e44c43..f8d7dfe75f 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -826,6 +826,9 @@ FunctionEnd !ifdef INNER Section "Uninstall" + ; use user for context of data/startup folders + SetShellVarContext current + ReadRegStr $START_MENU SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu" ;MessageBox MB_OK "Start menu is in: $START_MENU" From 57f9d82efabca1ff53a3409908539acb22647d8d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 16:13:39 -0800 Subject: [PATCH 17/56] remove shortcuts from custom start menu location --- cmake/templates/NSIS.template.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index f8d7dfe75f..fa424ea632 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -897,6 +897,8 @@ Section "Uninstall" ; try to fix it. StrCpy $MUI_TEMP "$START_MENU" Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" + Delete "$SMPROGRAMS\$MUI_TEMP\@INTERFACE_SHORTCUT_NAME@.lnk" + Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk" @CPACK_NSIS_DELETE_ICONS_EXTRA@ ;Delete empty start menu parent diretories From f4ece26271742587f0c4ea264c36dc46e3097347 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 16:16:29 -0800 Subject: [PATCH 18/56] use install dir written to registry as potential default --- cmake/templates/NSIS.template.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index fa424ea632..2303ddee35 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -378,7 +378,7 @@ Section "-Core installation" @CPACK_NSIS_FULL_INSTALL@ ;Store installation folder - WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR + WriteRegStr HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR ;Package the signed uninstaller produced by the inner loop !ifndef INNER @@ -919,6 +919,8 @@ Section "Uninstall" SectionEnd !endif +InstallDirRegKey HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" + Function .onInit !ifdef INNER From e4110ea401976b1749ffa015dd11a62b9caa7adf Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 25 Jan 2016 17:21:54 -0800 Subject: [PATCH 19/56] install for all users, EXCEPT the startup menu item --- cmake/templates/NSIS.template.in | 40 +++++++++++--------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 2303ddee35..b698d6072a 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -207,7 +207,7 @@ Function ConditionalAddToRegisty Pop $0 Pop $1 StrCmp "$0" "" ConditionalAddToRegisty_EmptyString - WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \ + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \ "$1" "$0" ;MessageBox MB_OK "Set Registry: '$1' to '$0'" DetailPrint "Set install registry entry: '$1' to '$0'" @@ -264,7 +264,7 @@ FunctionEnd !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration - !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" + !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER @@ -433,20 +433,12 @@ Section "-Core installation" ;Create shortcuts - ; in case the user has any shortcuts from prior to when we were using the user context, remove those now - SetShellVarContext all - RMDir /r "$SMPROGRAMS\$STARTMENU_FOLDER" - - ; go back to current user context before setting up the startmenu folder - SetShellVarContext current - CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" @CPACK_NSIS_CREATE_ICONS@ @CPACK_NSIS_CREATE_ICONS_EXTRA@ ; Conditional handling for Interface specific options ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} - CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@INTERFACE_SHORTCUT_NAME@.lnk" \ "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" @@ -601,15 +593,6 @@ Var LaunchNowState Var CopyFromProductionState Function HandlePostInstallOptions - ; If the user created shortcuts from a time prior to when we using current user context, pull them now - SetShellVarContext all - - Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk" - Delete "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk" - - ; return the context to the current user so the shortcuts are correct - SetShellVarContext current - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if the user asked for a desktop shortcut to High Fidelity ${NSD_GetState} $DesktopClientCheckbox $DesktopClientState @@ -646,6 +629,9 @@ Function HandlePostInstallOptions SetShellVarContext current CreateShortCut "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" + ; reset the shell var context back + SetShellVarContext all + !insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES ${Else} !insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ NO @@ -826,10 +812,10 @@ FunctionEnd !ifdef INNER Section "Uninstall" - ; use user for context of data/startup folders - SetShellVarContext current + ; use all users context for data/startup folders + SetShellVarContext all - ReadRegStr $START_MENU SHCTX \ + ReadRegStr $START_MENU HKLM \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu" ;MessageBox MB_OK "Start menu is in: $START_MENU" @@ -850,13 +836,13 @@ Section "Uninstall" ;Remove the uninstaller itself. Delete "$INSTDIR\@UNINSTALLER_NAME@" - DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ;Remove the installation directory if it is empty. RMDir "$INSTDIR" ; Remove the registry entries. - DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + DeleteRegKey HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ; Removes all optional components !insertmacro SectionList "RemoveSection_CPack" @@ -914,7 +900,7 @@ Section "Uninstall" StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop secondStartMenuDeleteLoopDone: - DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + DeleteRegKey /ifempty HKLM "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" SectionEnd !endif @@ -965,6 +951,6 @@ inst: ; Reads components status for registry !insertmacro SectionList "InitSection" - ; use user for context of data/startup folders - SetShellVarContext current + ; use all users for context of data/startup folders + SetShellVarContext all FunctionEnd From 1bdeeeceeb75302016b67029e22f3a2f63349d66 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 12 Jan 2016 17:24:14 -0800 Subject: [PATCH 20/56] AnimSkeleton: Added getPre and Post rotation accessors --- libraries/animation/src/AnimSkeleton.cpp | 21 +++++++++++++++++---- libraries/animation/src/AnimSkeleton.h | 9 +++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 8e3d716aac..8f45b785d1 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -59,8 +59,14 @@ const AnimPose& AnimSkeleton::getAbsoluteDefaultPose(int jointIndex) const { return _absoluteDefaultPoses[jointIndex]; } -const glm::quat AnimSkeleton::getPreRotation(int jointIndex) const { - return _joints[jointIndex].preRotation; +// get pre multiplied transform which should include FBX pre potations +const AnimPose& AnimSkeleton::getPreRotationPose(int jointIndex) const { + return _relativePreRotationPoses[jointIndex]; +} + +// get post multiplied transform which might include FBX offset transformations +const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const { + return _relativePostRotationPoses[jointIndex]; } int AnimSkeleton::getParentIndex(int jointIndex) const { @@ -99,13 +105,20 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) // build a chache of default poses _absoluteDefaultPoses.reserve(joints.size()); _relativeDefaultPoses.reserve(joints.size()); + _relativePreRotationPoses.reserve(joints.size()); + _relativePostRotationPoses.reserve(joints.size()); // iterate over FBXJoints and extract the bind pose information. for (int i = 0; i < (int)joints.size(); i++) { + // build pre and post transforms + glm::mat4 preRotationTransform = _joints[i].preTransform * glm::mat4_cast(_joints[i].preRotation); + glm::mat4 postRotationTransform = glm::mat4_cast(_joints[i].postRotation) * _joints[i].postTransform; + _relativePreRotationPoses.push_back(AnimPose(preRotationTransform)); + _relativePostRotationPoses.push_back(AnimPose(postRotationTransform)); + // build relative and absolute default poses - glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation); - glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform; + glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); _relativeDefaultPoses.push_back(relDefaultPose); int parentIndex = getParentIndex(i); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 7312fea6b1..757f4e5c3e 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -42,8 +42,11 @@ public: const AnimPose& getAbsoluteDefaultPose(int jointIndex) const; const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; } - // get pre-rotation aka Maya's joint orientation. - const glm::quat getPreRotation(int jointIndex) const; + // get pre transform which should include FBX pre potations + const AnimPose& getPreRotationPose(int jointIndex) const; + + // get post transform which might include FBX offset transformations + const AnimPose& getPostRotationPose(int jointIndex) const; int getParentIndex(int jointIndex) const; @@ -64,6 +67,8 @@ protected: AnimPoseVec _relativeBindPoses; AnimPoseVec _relativeDefaultPoses; AnimPoseVec _absoluteDefaultPoses; + AnimPoseVec _relativePreRotationPoses; + AnimPoseVec _relativePostRotationPoses; // no copies AnimSkeleton(const AnimSkeleton&) = delete; From 3cee3cbb5aa4b28f43cea90fb2e509e859f6127a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 25 Jan 2016 18:32:12 -0800 Subject: [PATCH 21/56] Avatar Developer Menu: added animation debug options This options are for for developers only and might help debug animation related issues. * Enable Inverse Kinematics: this can be toggled to disable IK for the avatar. * Enable Anim Pre and Post Rotations: this option can be used to use FBX pre-rotations from source avatar animations, instead of the current default, which is to use them from the source model. This only effects FBX files loaded by the animation system, it does not affect changing model orientations via JavaScript. --- interface/src/Menu.cpp | 4 ++ interface/src/Menu.h | 2 + interface/src/avatar/MyAvatar.cpp | 10 +++++ interface/src/avatar/MyAvatar.h | 2 + libraries/animation/src/AnimClip.cpp | 60 +++++++++++++++++----------- libraries/animation/src/AnimClip.h | 3 ++ libraries/animation/src/Rig.cpp | 10 +++++ libraries/animation/src/Rig.h | 4 ++ 8 files changed, 72 insertions(+), 23 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9cbb031a61..f1203b9df1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -479,6 +479,10 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false, + avatar, SLOT(setUseAnimPreAndPostRotations(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true, + avatar, SLOT(setEnableInverseKinematics(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::KeyboardMotorControl, Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehaviorFromMenu()), diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 408ab15d5e..7b9cca2c63 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -208,6 +208,7 @@ namespace MenuOption { const QString EchoServerAudio = "Echo Server Audio"; const QString Enable3DTVMode = "Enable 3DTV Mode"; const QString EnableCharacterController = "Enable avatar collisions"; + const QString EnableInverseKinematics = "Enable Inverse Kinematics"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; @@ -302,6 +303,7 @@ namespace MenuOption { const QString UploadAsset = "Upload File to Asset Server"; const QString UseAudioForMouth = "Use Audio for Mouth"; const QString UseCamera = "Use Camera"; + const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations"; const QString VelocityFilter = "Velocity Filter"; const QString VisibleToEveryone = "Everyone"; const QString VisibleToFriends = "Friends"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e06dce2324..756dc6adea 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -647,6 +648,15 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) { _skeletonModel.setVisibleInScene(isEnabled, scene); } +void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) { + AnimClip::usePreAndPostPoseFromAnim = isEnabled; + reset(true); +} + +void MyAvatar::setEnableInverseKinematics(bool isEnabled) { + _rig->setEnableInverseKinematics(isEnabled); +} + void MyAvatar::loadData() { Settings settings; settings.beginGroup("Avatar"); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 10ce962821..ed6c3cb883 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -260,6 +260,8 @@ public slots: void setEnableDebugDrawPosition(bool isEnabled); bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); } void setEnableMeshVisible(bool isEnabled); + void setUseAnimPreAndPostRotations(bool isEnabled); + void setEnableInverseKinematics(bool isEnabled); Q_INVOKABLE void setAnimGraphUrl(const QUrl& url); glm::vec3 getPositionForAudio(); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 986bb0a30e..32da64808b 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -13,6 +13,8 @@ #include "AnimationLogging.h" #include "AnimUtil.h" +bool AnimClip::usePreAndPostPoseFromAnim = false; + AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : AnimNode(AnimNode::Type::Clip, id), _startFrame(startFrame), @@ -109,6 +111,8 @@ void AnimClip::copyFromNetworkAnim() { for (int frame = 0; frame < frameCount; frame++) { + const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; + // init all joints in animation to default pose // this will give us a resonable result for bones in the model skeleton but not in the animation. _anim[frame].reserve(skeletonJointCount); @@ -119,36 +123,46 @@ void AnimClip::copyFromNetworkAnim() { for (int animJoint = 0; animJoint < animJointCount; animJoint++) { int skeletonJoint = jointMap[animJoint]; + const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint]; + const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint]; + // skip joints that are in the animation but not in the skeleton. if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { - const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; -#ifdef USE_PRE_ROT_FROM_ANIM - // TODO: This is the correct way to apply the pre rotations from maya, however - // the current animation set has incorrect preRotations for the left wrist and thumb - // so it looks wrong if we enable this code. - glm::quat preRotation = animSkeleton.getPreRotation(animJoint); -#else - // TODO: This is the legacy approach, this does not work when animations and models do not - // have the same set of pre rotations. For example when mixing maya models with blender animations. - glm::quat preRotation = _skeleton->getRelativeBindPose(skeletonJoint).rot; -#endif - const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); + AnimPose preRot, postRot; + if (usePreAndPostPoseFromAnim) { + preRot = animSkeleton.getPreRotationPose(animJoint); + postRot = animSkeleton.getPostRotationPose(animJoint); + } else { + // In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations. + preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot, glm::vec3()); + postRot = AnimPose::identity; + } - // used to adjust translation offsets, so large translation animatons on the reference skeleton + // cancel out scale + preRot.scale = glm::vec3(1.0f); + postRot.scale = glm::vec3(1.0f); + + AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3()); + + // adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. - float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans)); + const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; + const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); + float boneLengthScale = 1.0f; + const float EPSILON = 0.0001f; + if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) { + boneLengthScale = glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans); + } - AnimPose& pose = _anim[frame][skeletonJoint]; - const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; + AnimPose trans; + if (usePreAndPostPoseFromAnim) { + trans = AnimPose(glm::vec3(1.0f), glm::quat(), boneLengthScale * (fbxAnimTrans + (fbxAnimTrans - fbxZeroTrans))); + } else { + trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans)); + } - // rotation in fbxAnimationFrame is a delta from its preRotation. - pose.rot = preRotation * fbxAnimFrame.rotations[animJoint]; - - // translation in fbxAnimationFrame is not a delta. - // convert it into a delta by subtracting from the first frame. - const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint]; - pose.trans = relDefaultPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans); + _anim[frame][skeletonJoint] = trans * preRot * rot * postRot; } } } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 934f3f3ed8..91215d0d44 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -25,6 +25,9 @@ class AnimClip : public AnimNode { public: friend class AnimTests; + static bool usePreAndPostPoseFromAnim; + static bool useTransFromAnim; + AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag); virtual ~AnimClip() override; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index df92d7d912..4d72bfbaea 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -464,6 +464,10 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) { } } +void Rig::setEnableInverseKinematics(bool enable) { + _enableInverseKinematics = enable; +} + AnimPose Rig::getAbsoluteDefaultPose(int index) const { if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { return _absoluteDefaultPoses[index]; @@ -705,6 +709,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } t += deltaTime; + + if (_enableInverseKinematics) { + _animVars.set("ikOverlayAlpha", 1.0f); + } else { + _animVars.set("ikOverlayAlpha", 0.0f); + } } _lastFront = front; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index d26be83d36..cf96c6dc16 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -208,6 +208,8 @@ public: void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; + void setEnableInverseKinematics(bool enable); + protected: bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } void updateAnimationStateHandlers(); @@ -290,6 +292,8 @@ public: std::map _origRoleAnimations; std::vector _prefetchedAnimations; + bool _enableInverseKinematics { true }; + private: QMap _stateHandlers; int _nextStateHandlerId { 0 }; From 3b062b7a31e334c73cc1d52add8be82f157b19bd Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 25 Jan 2016 18:47:45 -0800 Subject: [PATCH 22/56] AnimClip: special case the translation on the eyes --- libraries/animation/src/AnimClip.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 32da64808b..0ab83c1870 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -156,7 +156,10 @@ void AnimClip::copyFromNetworkAnim() { } AnimPose trans; - if (usePreAndPostPoseFromAnim) { + if (_skeleton->getJointName(skeletonJoint) == "RightEye" || _skeleton->getJointName(skeletonJoint) == "LeftEye") { + // preserve model eye translation + trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans); + } else if (usePreAndPostPoseFromAnim) { trans = AnimPose(glm::vec3(1.0f), glm::quat(), boneLengthScale * (fbxAnimTrans + (fbxAnimTrans - fbxZeroTrans))); } else { trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans)); From 641a6e9c30423159ae2a9b1d7d674f7f04a1123e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 26 Jan 2016 09:29:02 -0800 Subject: [PATCH 23/56] Fix injector handling for when it falls behind --- libraries/audio/src/AudioConstants.h | 2 +- libraries/audio/src/AudioInjector.cpp | 87 +++++++++++--------- libraries/audio/src/AudioInjector.h | 2 +- libraries/audio/src/AudioInjectorManager.cpp | 2 +- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/libraries/audio/src/AudioConstants.h b/libraries/audio/src/AudioConstants.h index b1c810710e..91080017a5 100644 --- a/libraries/audio/src/AudioConstants.h +++ b/libraries/audio/src/AudioConstants.h @@ -29,7 +29,7 @@ namespace AudioConstants { const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample); const float NETWORK_FRAME_MSECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL / (float)AudioConstants::SAMPLE_RATE) * 1000.0f; - const unsigned int NETWORK_FRAME_USECS = (unsigned int)floorf(NETWORK_FRAME_MSECS * 1000.0f); + const int NETWORK_FRAME_USECS = floorf(NETWORK_FRAME_MSECS * 1000.0f); const int MIN_SAMPLE_VALUE = std::numeric_limits::min(); const int MAX_SAMPLE_VALUE = std::numeric_limits::max(); } diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 1f15007339..bec4921675 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -158,10 +158,10 @@ bool AudioInjector::injectLocally() { } const uchar MAX_INJECTOR_VOLUME = 0xFF; -static const uint64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = 0; -static const uint64_t NEXT_FRAME_DELTA_IMMEDIATELY = 1; +static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1; +static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0; -uint64_t AudioInjector::injectNextFrame() { +int64_t AudioInjector::injectNextFrame() { if (_state == AudioInjector::State::Finished) { qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning."; @@ -198,110 +198,123 @@ uint64_t AudioInjector::injectNextFrame() { // pack some placeholder sequence number for now audioPacketStream << (quint16) 0; - + // pack stream identifier (a generated UUID) audioPacketStream << QUuid::createUuid(); - + // pack the stereo/mono type of the stream audioPacketStream << _options.stereo; - + // pack the flag for loopback - uchar loopbackFlag = (uchar) true; + uchar loopbackFlag = (uchar)true; audioPacketStream << loopbackFlag; - + // pack the position for injected audio positionOptionOffset = _currentPacket->pos(); audioPacketStream.writeRawData(reinterpret_cast(&_options.position), - sizeof(_options.position)); - + sizeof(_options.position)); + // pack our orientation for injected audio audioPacketStream.writeRawData(reinterpret_cast(&_options.orientation), - sizeof(_options.orientation)); - + sizeof(_options.orientation)); + // pack zero for radius float radius = 0; audioPacketStream << radius; - + // pack 255 for attenuation byte volumeOptionOffset = _currentPacket->pos(); quint8 volume = MAX_INJECTOR_VOLUME; audioPacketStream << volume; - + audioPacketStream << _options.ignorePenumbra; - + audioDataOffset = _currentPacket->pos(); - - } else { + + } + else { // no samples to inject, return immediately qDebug() << "AudioInjector::injectNextFrame() called with no samples to inject. Returning."; return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } } - + int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, - _audioData.size() - _currentSendOffset); - + _audioData.size() - _currentSendOffset); + // Measure the loudness of this frame _loudness = 0.0f; for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendOffset + i)) / - (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); + (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); } _loudness /= (float)(bytesToCopy / sizeof(int16_t)); - + _currentPacket->seek(0); - + // pack the sequence number _currentPacket->writePrimitive(_outgoingSequenceNumber); - + _currentPacket->seek(positionOptionOffset); _currentPacket->writePrimitive(_options.position); _currentPacket->writePrimitive(_options.orientation); - + quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; _currentPacket->seek(volumeOptionOffset); _currentPacket->writePrimitive(volume); - + _currentPacket->seek(audioDataOffset); - + // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet _currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); - + // set the correct size used for this packet _currentPacket->setPayloadSize(_currentPacket->pos()); - + // grab our audio mixer from the NodeList, if it exists auto nodeList = DependencyManager::get(); SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); - + if (audioMixer) { // send off this audio packet nodeList->sendUnreliablePacket(*_currentPacket, *audioMixer); _outgoingSequenceNumber++; } - + _currentSendOffset += bytesToCopy; - + if (_currentSendOffset >= _audioData.size()) { // we're at the end of the audio data to send if (_options.loop) { // we were asked to loop, set our send offset to 0 _currentSendOffset = 0; - } else { + } + else { // we weren't to loop, say that we're done now finish(); return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } } - + if (_currentSendOffset == bytesToCopy) { // ask AudioInjectorManager to call us right away again to // immediately send the first two frames so the mixer can start using the audio right away return NEXT_FRAME_DELTA_IMMEDIATELY; - } else { - return (++_nextFrame * AudioConstants::NETWORK_FRAME_USECS) - _frameTimer->nsecsElapsed() / 1000; } - + + const int MAX_ALLOWED_FRAMES_TO_FALL_BEHIND = 7; + int64_t currentTime = _frameTimer->nsecsElapsed() / 1000; + auto currentFrameBasedOnElapsedTime = currentTime / AudioConstants::NETWORK_FRAME_USECS; + + if (currentFrameBasedOnElapsedTime - _nextFrame > MAX_ALLOWED_FRAMES_TO_FALL_BEHIND) { + // If we are falling behind by more frames than our threshold, let's skip the frames ahead + _nextFrame = currentFrameBasedOnElapsedTime - MAX_ALLOWED_FRAMES_TO_FALL_BEHIND; + } else { + ++_nextFrame; + } + + int64_t playNextFrameAt = _nextFrame * AudioConstants::NETWORK_FRAME_USECS; + return std::max(0LL, playNextFrameAt - currentTime); } void AudioInjector::stop() { diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index f815b6fe3a..5155b87a74 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -84,7 +84,7 @@ private slots: private: void setupInjection(); - uint64_t injectNextFrame(); + int64_t injectNextFrame(); bool injectLocally(); QByteArray _audioData; diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index f504b31907..b91bddc553 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -88,7 +88,7 @@ void AudioInjectorManager::run() { // this is an injector that's ready to go, have it send a frame now auto nextCallDelta = injector->injectNextFrame(); - if (nextCallDelta > 0 && !injector->isFinished()) { + if (nextCallDelta >= 0 && !injector->isFinished()) { // re-enqueue the injector with the correct timing _injectors.emplace(usecTimestampNow() + nextCallDelta, injector); } From 0e1742588bce3dd6bdec5e580f8b480096118525 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 26 Jan 2016 09:35:35 -0800 Subject: [PATCH 24/56] Fix indentation --- libraries/audio/src/AudioInjector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index bec4921675..660effddeb 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -240,7 +240,7 @@ int64_t AudioInjector::injectNextFrame() { } int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, - _audioData.size() - _currentSendOffset); + _audioData.size() - _currentSendOffset); // Measure the loudness of this frame _loudness = 0.0f; From 5268ef8ac21f723e39654d0a7d6dd9735ed333f9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 26 Jan 2016 09:36:40 -0800 Subject: [PATCH 25/56] Fix else indentation --- libraries/audio/src/AudioInjector.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 660effddeb..e058a87636 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -288,8 +288,7 @@ int64_t AudioInjector::injectNextFrame() { if (_options.loop) { // we were asked to loop, set our send offset to 0 _currentSendOffset = 0; - } - else { + } else { // we weren't to loop, say that we're done now finish(); return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; From 3160aa306867d791e2fb858b80ba74d37165f9a9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 26 Jan 2016 09:36:52 -0800 Subject: [PATCH 26/56] Change 0LL to INT64_C(0) --- libraries/audio/src/AudioInjector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index e058a87636..f32b96257d 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -313,7 +313,7 @@ int64_t AudioInjector::injectNextFrame() { } int64_t playNextFrameAt = _nextFrame * AudioConstants::NETWORK_FRAME_USECS; - return std::max(0LL, playNextFrameAt - currentTime); + return std::max(INT64_C(0), playNextFrameAt - currentTime); } void AudioInjector::stop() { From 2728290ffbeb6e95ed9db0343a80a42a69d755f8 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Tue, 26 Jan 2016 09:37:33 -0800 Subject: [PATCH 27/56] fix bug in IK and tune head/neck constraints --- .../animation/src/AnimInverseKinematics.cpp | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 415ee72d7b..e47a12960e 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -153,7 +153,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); if (pivotIndex == -1 || pivotIndex == _hipsIndex) { @@ -165,12 +164,30 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorapply(tipRelativeRotation); + if (constrained) { + tipOrientation = glm::normalize(tipRelativeRotation * tipParentOrientation); + } + } + } + + // cache tip absolute position + glm::vec3 tipPosition = absolutePoses[tipIndex].trans; // descend toward root, pivoting each joint to get tip closer to target while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) { @@ -201,9 +218,9 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorapply(newRot); if (constrained) { - // the constraint will modify the movement of the tip so we have to compute the modified - // model-frame deltaRotation + // the constraint will modify the local rotation of the tip so we must + // compute the corresponding model-frame deltaRotation // Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^ deltaRotation = absolutePoses[pivotsParentIndex].rot * - newRot * - glm::inverse(absolutePoses[pivotIndex].rot); + newRot * glm::inverse(absolutePoses[pivotIndex].rot); } } @@ -264,8 +280,8 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(pivotIndex); @@ -629,11 +645,11 @@ void AnimInverseKinematics::initConstraints() { } else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); - const float MAX_NECK_TWIST = PI / 4.0f; + const float MAX_NECK_TWIST = PI / 6.0f; stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST); std::vector minDots; - const float MAX_NECK_SWING = PI / 3.0f; + const float MAX_NECK_SWING = PI / 4.0f; minDots.push_back(cosf(MAX_NECK_SWING)); stConstraint->setSwingLimits(minDots); @@ -641,11 +657,11 @@ void AnimInverseKinematics::initConstraints() { } else if (0 == baseName.compare("Head", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); - const float MAX_HEAD_TWIST = PI / 4.0f; + const float MAX_HEAD_TWIST = PI / 8.0f; stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST); std::vector minDots; - const float MAX_HEAD_SWING = PI / 4.0f; + const float MAX_HEAD_SWING = PI / 6.0f; minDots.push_back(cosf(MAX_HEAD_SWING)); stConstraint->setSwingLimits(minDots); From b56668892e82c4bd81bb3973e8cf8532de45722b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 26 Jan 2016 09:39:11 -0800 Subject: [PATCH 28/56] Fix else style --- libraries/audio/src/AudioInjector.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index f32b96257d..b203800703 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -231,8 +231,7 @@ int64_t AudioInjector::injectNextFrame() { audioDataOffset = _currentPacket->pos(); - } - else { + } else { // no samples to inject, return immediately qDebug() << "AudioInjector::injectNextFrame() called with no samples to inject. Returning."; return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; From 69d833a6c00b5e7416fec15a834ffedfc0661024 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 26 Jan 2016 10:53:21 -0800 Subject: [PATCH 29/56] AnimClip: consistent application of translations Apply animation translation the same way regardless of state of usePreAndPostPoseFromAnimation. --- libraries/animation/src/AnimClip.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 0ab83c1870..90cd85e727 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -155,15 +155,7 @@ void AnimClip::copyFromNetworkAnim() { boneLengthScale = glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans); } - AnimPose trans; - if (_skeleton->getJointName(skeletonJoint) == "RightEye" || _skeleton->getJointName(skeletonJoint) == "LeftEye") { - // preserve model eye translation - trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans); - } else if (usePreAndPostPoseFromAnim) { - trans = AnimPose(glm::vec3(1.0f), glm::quat(), boneLengthScale * (fbxAnimTrans + (fbxAnimTrans - fbxZeroTrans))); - } else { - trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans)); - } + AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans)); _anim[frame][skeletonJoint] = trans * preRot * rot * postRot; } From 79a2b7f2135fed3cb1622a784a70e3c1b3495a12 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 11:17:54 -0800 Subject: [PATCH 30/56] make sure shell var context is correct for AppData --- cmake/templates/NSIS.template.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index b698d6072a..42743196b5 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -639,10 +639,13 @@ Function HandlePostInstallOptions ${EndIf} ${If} @PR_BUILD@ == 1 + ; check if we need to copy settings/content from production for this PR build ${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState ${If} $CopyFromProductionState == ${BST_CHECKED} + SetShellVarContext current + StrCpy $0 "$APPDATA\@BUILD_ORGANIZATION@" ; we need to copy whatever is in the data folder for production build to the data folder for this build @@ -660,6 +663,8 @@ Function HandlePostInstallOptions "There was a problem copying your production content and settings to $0 for this PR build.$\r$\n$\r$\nPlease copy them manually." NoError: + + SetShellVarContext all ${EndIf} ${EndIf} From 068de5203c3aa8797dc7dba1af006295e51bdb9a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 11:40:50 -0800 Subject: [PATCH 31/56] use specific shortcut names for PR builds --- cmake/macros/SetPackagingParameters.cmake | 12 ++++++++---- cmake/templates/CPackProperties.cmake.in | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index b0f093977a..de5a87e4d6 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -82,10 +82,14 @@ macro(SET_PACKAGING_PARAMETERS) set(DS_EXEC_NAME "domain-server.exe") set(AC_EXEC_NAME "assignment-client.exe") - # start menu shortcuts - set(INTERFACE_SM_SHORTCUT_NAME "High Fidelity") - set(CONSOLE_SM_SHORTCUT_NAME "Server Console") - + # shortcut names + if (NOT PR_BUILD) + set(INTERFACE_SHORTCUT_NAME "High Fidelity") + set(CONSOLE_SHORTCUT_NAME "Server Console") + else () + set(INTERFACE_SHORTCUT_NAME "High Fidelity - ${BUILD_VERSION}") + set(CONSOLE_SHORTCUT_NAME "Server Console - ${BUILD_VERSION}") + endif () # check if we need to find signtool if (PRODUCTION_BUILD OR PR_BUILD) find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64") diff --git a/cmake/templates/CPackProperties.cmake.in b/cmake/templates/CPackProperties.cmake.in index 285de84d39..164e432706 100644 --- a/cmake/templates/CPackProperties.cmake.in +++ b/cmake/templates/CPackProperties.cmake.in @@ -9,10 +9,10 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SM_SHORTCUT_NAME@") +set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SHORTCUT_NAME@") set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe") set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@") -set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SM_SHORTCUT_NAME@") +set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SHORTCUT_NAME@") set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@") set(DS_EXEC_NAME "@DS_EXEC_NAME@") set(AC_EXEC_NAME "@AC_EXEC_NAME@") From 4115d52d916251311597fc683a8851aa62fc07ec Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 11:46:37 -0800 Subject: [PATCH 32/56] default shortcut options off for PR builds --- cmake/templates/NSIS.template.in | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 42743196b5..ecf703f121 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -536,7 +536,6 @@ Function PostInstallOptionsPage ; set the checkbox state depending on what is present in the registry !insertmacro SetPostInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED} - ${EndIf} ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} @@ -569,6 +568,16 @@ Function PostInstallOptionsPage !insertmacro SetPostInstallOption $LaunchNowCheckbox @LAUNCH_NOW_REG_KEY@ ${BST_CHECKED} ${If} @PR_BUILD@ == 1 + ; a PR build defaults all install options expect LaunchNowCheckbox and the settings copy to unchecked + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${NSD_SetState} $DesktopClientCheckbox ${BST_UNCHECKED} + ${EndIf} + + ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${NSD_SetState} $DesktopServerCheckbox ${BST_UNCHECKED} + ${NSD_SetState} $ServerStartupCheckbox ${BST_UNCHECKED} + ${EndIf} + ; push the offset IntOp $CurrentOffset $CurrentOffset + 15 From 6daeefef6882a781fc2d4fba1a22bba8666528e0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 12:08:26 -0800 Subject: [PATCH 33/56] remove the correct startup shortcut during uninstall --- cmake/templates/NSIS.template.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index ecf703f121..070c12192c 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -868,7 +868,12 @@ Section "Uninstall" Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk" Delete "$DESKTOP\@INTERFACE_SHORTCUT_NAME@.lnk" Delete "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk" + + ; if it exists, delete the startup shortcut for the current user + SetShellVarContext current Delete "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk" + SetShellVarContext all + @CPACK_NSIS_DELETE_ICONS@ @CPACK_NSIS_DELETE_ICONS_EXTRA@ From 0a413904a098bab3c7a4dc7b88fc5a6e0bb2e7e1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 12:09:45 -0800 Subject: [PATCH 34/56] use non PR-specific names for close prompt --- cmake/templates/NSIS.template.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 070c12192c..b5699cb3b3 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -721,8 +721,8 @@ FunctionEnd !macroend !macro CheckForRunningApplications action prompter - !insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "@INTERFACE_SHORTCUT_NAME@" ${action} ${prompter} - !insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "@CONSOLE_SHORTCUT_NAME@" ${action} ${prompter} + !insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "High Fidelity client" ${action} ${prompter} + !insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "Server Console" ${action} ${prompter} !insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "Domain Server" ${action} ${prompter} !insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "Assignment Client" ${action} ${prompter} !macroend From 2da2bc60db47584f1e07d2a705f87ffdab49b9a2 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 19 Jan 2016 12:35:32 -0800 Subject: [PATCH 35/56] Tie shadow frustum to boom offset --- interface/src/Application.cpp | 13 +++++++------ libraries/render-utils/src/RenderShadowTask.cpp | 4 ++-- libraries/shared/src/RenderArgs.h | 3 ++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a295cd8a6e..4629a8c08c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1380,10 +1380,12 @@ void Application::paintGL() { } + glm::vec3 boomOffset; { PerformanceTimer perfTimer("CameraUpdates"); auto myAvatar = getMyAvatar(); + boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT; myAvatar->startCapture(); if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { @@ -1411,18 +1413,16 @@ void Application::paintGL() { if (isHMDMode()) { auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); _myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat))); - auto worldBoomOffset = myAvatar->getOrientation() * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)); - _myCamera.setPosition(extractTranslation(hmdWorldMat) + worldBoomOffset); + _myCamera.setPosition(extractTranslation(hmdWorldMat) + + myAvatar->getOrientation() * boomOffset); } else { _myCamera.setRotation(myAvatar->getHead()->getOrientation()); if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + _myCamera.getRotation() - * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + + _myCamera.getRotation() * boomOffset); } else { _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + myAvatar->getOrientation() - * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + + myAvatar->getOrientation() * boomOffset); } } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { @@ -1488,6 +1488,7 @@ void Application::paintGL() { { PROFILE_RANGE(__FUNCTION__ "/mainRender"); PerformanceTimer perfTimer("mainRender"); + renderArgs._boomOffset = boomOffset; // Viewport is assigned to the size of the framebuffer renderArgs._viewport = ivec4(0, 0, size.width(), size.height()); if (displayPlugin->isStereo()) { diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 7240fba105..3fd8e022f5 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -146,9 +146,9 @@ void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render RenderArgs::RenderMode mode = args->_renderMode; auto nearClip = viewFrustum->getNearClip(); - const int SHADOW_NEAR_DEPTH = -2; + float nearDepth = -args->_boomOffset.z; const int SHADOW_FAR_DEPTH = 20; - globalLight->shadow.setKeylightFrustum(viewFrustum, nearClip + SHADOW_NEAR_DEPTH, nearClip + SHADOW_FAR_DEPTH); + globalLight->shadow.setKeylightFrustum(viewFrustum, nearDepth, nearClip + SHADOW_FAR_DEPTH); // Set the keylight render args args->_viewFrustum = globalLight->shadow.getFrustum().get(); diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 7012c78c8f..f9fc6eb66b 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -105,7 +105,8 @@ public: std::shared_ptr _pipeline = nullptr; OctreeRenderer* _renderer = nullptr; ViewFrustum* _viewFrustum = nullptr; - glm::ivec4 _viewport{ 0, 0, 1, 1 }; + glm::ivec4 _viewport{ 0.0f, 0.0f, 1.0f, 1.0f }; + glm::vec3 _boomOffset{ 0.0f, 0.0f, 1.0f }; float _sizeScale = 1.0f; int _boundaryLevelAdjust = 0; RenderMode _renderMode = DEFAULT_RENDER_MODE; From e6379fcaa868e3bd012c926cbd2709de353d3c60 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 26 Jan 2016 12:57:41 -0800 Subject: [PATCH 36/56] Naming changes --- examples/junkyard/junkyardClientReset.js | 4 ++-- examples/junkyard/junkyardResetEntitySpawner.js | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/junkyard/junkyardClientReset.js b/examples/junkyard/junkyardClientReset.js index 8aea8470dd..c4dfa19c4f 100644 --- a/examples/junkyard/junkyardClientReset.js +++ b/examples/junkyard/junkyardClientReset.js @@ -22,10 +22,10 @@ function reset() { for (i = 0; i < e.length; i++) { Entities.deleteEntity(e[i]); } - importArf(); + importAssetResourceFile(); } -function importArf() { +function importAssetResourceFile() { Clipboard.importEntities(IMPORT_URL); Clipboard.pasteEntities(PASTE_ENTITIES_LOCATION); } \ No newline at end of file diff --git a/examples/junkyard/junkyardResetEntitySpawner.js b/examples/junkyard/junkyardResetEntitySpawner.js index b2627c7cd9..f467e0e22f 100644 --- a/examples/junkyard/junkyardResetEntitySpawner.js +++ b/examples/junkyard/junkyardResetEntitySpawner.js @@ -19,12 +19,13 @@ var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation))); - var scriptURL = Script.resolvePath("junkyardResetEntityScript.js?v1" + Math.random()); + var SCRIPT_URL = Script.resolvePath("junkyardResetEntityScript.js"); + var MODEL_URL = "http://hifi-content.s3.amazonaws.com/caitlyn/dev/Blueprint%20Objects/Asylum/Asylum_Table/Asylum_Table.fbx"; var resetEntity = Entities.addEntity({ type: "Model", - modelURL: "http://hifi-content.s3.amazonaws.com/caitlyn/dev/Blueprint%20Objects/Asylum/Asylum_Table/Asylum_Table.fbx", + modelURL: MODEL_URL, position: center, - script: scriptURL, + script: SCRIPT_URL, dimensions: { x: 2.8, y: 1.76, From b6f478e08759497969a5a9743166407a54b7f4f0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 13:10:02 -0800 Subject: [PATCH 37/56] add some debug for audio injector wrap case --- libraries/audio/src/AudioInjector.cpp | 115 ++++++++++++++------------ 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 1f15007339..d22e0ff2f6 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -36,7 +36,7 @@ AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorO _audioData(sound->getByteArray()), _options(injectorOptions) { - + } AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions) : @@ -49,15 +49,15 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt void AudioInjector::finish() { bool shouldDelete = (_state == State::NotFinishedWithPendingDelete); _state = State::Finished; - + emit finished(); - + if (_localBuffer) { _localBuffer->stop(); _localBuffer->deleteLater(); _localBuffer = NULL; } - + if (shouldDelete) { // we've been asked to delete after finishing, trigger a deleteLater here deleteLater(); @@ -67,7 +67,7 @@ void AudioInjector::finish() { void AudioInjector::setupInjection() { if (!_hasSetup) { _hasSetup = true; - + // check if we need to offset the sound by some number of seconds if (_options.secondOffset > 0.0f) { @@ -87,31 +87,31 @@ void AudioInjector::setupInjection() { void AudioInjector::restart() { // grab the AudioInjectorManager auto injectorManager = DependencyManager::get(); - + if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, "restart"); - + if (!_options.localOnly) { // notify the AudioInjectorManager to wake up in case it's waiting for new injectors injectorManager->notifyInjectorReadyCondition(); } - + return; } - + // reset the current send offset to zero _currentSendOffset = 0; - + // check our state to decide if we need extra handling for the restart request if (_state == State::Finished) { // we finished playing, need to reset state so we can get going again _hasSetup = false; _shouldStop = false; _state = State::NotFinished; - + // call inject audio to start injection over again setupInjection(); - + // if we're a local injector call inject locally to start injecting again if (_options.localOnly) { injectLocally(); @@ -153,7 +153,7 @@ bool AudioInjector::injectLocally() { // we never started so we are finished, call our stop method stop(); } - + return success; } @@ -162,85 +162,85 @@ static const uint64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = 0; static const uint64_t NEXT_FRAME_DELTA_IMMEDIATELY = 1; uint64_t AudioInjector::injectNextFrame() { - + if (_state == AudioInjector::State::Finished) { qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning."; return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } - + // if we haven't setup the packet to send then do so now static int positionOptionOffset = -1; static int volumeOptionOffset = -1; static int audioDataOffset = -1; - + if (!_currentPacket) { if (_currentSendOffset < 0 || _currentSendOffset >= _audioData.size()) { _currentSendOffset = 0; } - + // make sure we actually have samples downloaded to inject if (_audioData.size()) { - + _outgoingSequenceNumber = 0; _nextFrame = 0; - + if (!_frameTimer) { _frameTimer = std::unique_ptr(new QElapsedTimer); } - + _frameTimer->restart(); - + _currentPacket = NLPacket::create(PacketType::InjectAudio); - + // setup the packet for injected audio QDataStream audioPacketStream(_currentPacket.get()); - + // pack some placeholder sequence number for now audioPacketStream << (quint16) 0; - + // pack stream identifier (a generated UUID) audioPacketStream << QUuid::createUuid(); - + // pack the stereo/mono type of the stream audioPacketStream << _options.stereo; - + // pack the flag for loopback uchar loopbackFlag = (uchar) true; audioPacketStream << loopbackFlag; - + // pack the position for injected audio positionOptionOffset = _currentPacket->pos(); audioPacketStream.writeRawData(reinterpret_cast(&_options.position), sizeof(_options.position)); - + // pack our orientation for injected audio audioPacketStream.writeRawData(reinterpret_cast(&_options.orientation), sizeof(_options.orientation)); - + // pack zero for radius float radius = 0; audioPacketStream << radius; - + // pack 255 for attenuation byte volumeOptionOffset = _currentPacket->pos(); quint8 volume = MAX_INJECTOR_VOLUME; audioPacketStream << volume; - + audioPacketStream << _options.ignorePenumbra; - + audioDataOffset = _currentPacket->pos(); - + } else { // no samples to inject, return immediately qDebug() << "AudioInjector::injectNextFrame() called with no samples to inject. Returning."; return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } } - + int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, _audioData.size() - _currentSendOffset); - + // Measure the loudness of this frame _loudness = 0.0f; for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { @@ -248,40 +248,40 @@ uint64_t AudioInjector::injectNextFrame() { (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); } _loudness /= (float)(bytesToCopy / sizeof(int16_t)); - + _currentPacket->seek(0); - + // pack the sequence number _currentPacket->writePrimitive(_outgoingSequenceNumber); - + _currentPacket->seek(positionOptionOffset); _currentPacket->writePrimitive(_options.position); _currentPacket->writePrimitive(_options.orientation); - + quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; _currentPacket->seek(volumeOptionOffset); _currentPacket->writePrimitive(volume); - + _currentPacket->seek(audioDataOffset); - + // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet _currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); - + // set the correct size used for this packet _currentPacket->setPayloadSize(_currentPacket->pos()); - + // grab our audio mixer from the NodeList, if it exists auto nodeList = DependencyManager::get(); SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); - + if (audioMixer) { // send off this audio packet nodeList->sendUnreliablePacket(*_currentPacket, *audioMixer); _outgoingSequenceNumber++; } - + _currentSendOffset += bytesToCopy; - + if (_currentSendOffset >= _audioData.size()) { // we're at the end of the audio data to send if (_options.loop) { @@ -293,15 +293,20 @@ uint64_t AudioInjector::injectNextFrame() { return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } } - + if (_currentSendOffset == bytesToCopy) { // ask AudioInjectorManager to call us right away again to // immediately send the first two frames so the mixer can start using the audio right away return NEXT_FRAME_DELTA_IMMEDIATELY; } else { - return (++_nextFrame * AudioConstants::NETWORK_FRAME_USECS) - _frameTimer->nsecsElapsed() / 1000; + uint64_t untilNextFrame = (++_nextFrame * AudioConstants::NETWORK_FRAME_USECS) - _frameTimer->nsecsElapsed() / 1000; + if (untilNextFrame >= 10 * AudioConstants::NETWORK_FRAME_USECS) { + qDebug() << "AudioInjector returning delta to next frame that is very large -" << untilNextFrame; + qDebug() << "Next Frame is " << _nextFrame << "frameTimer nsecsElapsed is" << _frameTimer->nsecsElapsed(); + } + return untilNextFrame; } - + } void AudioInjector::stop() { @@ -315,7 +320,7 @@ void AudioInjector::triggerDeleteAfterFinish() { QMetaObject::invokeMethod(this, "triggerDeleteAfterFinish", Qt::QueuedConnection); return; } - + if (_state == State::Finished) { stopAndDeleteLater(); } else { @@ -371,11 +376,11 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { AudioInjector* sound = playSound(buffer, options, localInterface); - + if (sound) { sound->_state = AudioInjector::State::NotFinishedWithPendingDelete; } - + return sound; } @@ -383,13 +388,13 @@ AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { AudioInjector* injector = new AudioInjector(buffer, options); injector->setLocalAudioInterface(localInterface); - + // grab the AudioInjectorManager auto injectorManager = DependencyManager::get(); - + // setup parameters required for injection injector->setupInjection(); - + if (options.localOnly) { if (injector->injectLocally()) { // local injection succeeded, return the pointer to injector From 1fefe0fa0e48de2a3b0e042c0ae39ad347e3d0f8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 26 Jan 2016 13:30:55 -0800 Subject: [PATCH 38/56] when an avatar moves a child entity around, it has to invoke MovingEntitiesOperator to keep the local octree up-to-date --- interface/src/avatar/MyAvatar.cpp | 37 ++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7180d8ec2c..04205d6a65 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -50,6 +50,7 @@ #include "InterfaceLogging.h" #include "DebugDraw.h" #include "EntityEditPacketSender.h" +#include "MovingEntitiesOperator.h" using namespace std; @@ -355,18 +356,34 @@ void MyAvatar::simulate(float deltaTime) { locationChanged(); // if a entity-child of this avatar has moved outside of its queryAACube, update the cube and tell the entity server. - forEachDescendant([&](SpatiallyNestablePointer object) { - if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(object); - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); - if (packetSender) { - EntityItemProperties properties = entity->getProperties(); - properties.setQueryAACubeDirty(); - packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties); - entity->setLastBroadcast(usecTimestampNow()); + EntityTreeRenderer* entityTreeRenderer = qApp->getEntities(); + EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; + if (entityTree) { + EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + MovingEntitiesOperator moveOperator(entityTree); + forEachDescendant([&](SpatiallyNestablePointer object) { + // if the queryBox has changed, tell the entity-server + if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + bool success; + AACube newCube = entity->getQueryAACube(success); + if (success) { + moveOperator.addEntityToMoveList(entity, newCube); + } + if (packetSender) { + EntityItemProperties properties = entity->getProperties(); + properties.setQueryAACubeDirty(); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties); + entity->setLastBroadcast(usecTimestampNow()); + } } + }); + // also update the position of children in our local octree + if (moveOperator.hasMovingEntities()) { + PerformanceTimer perfTimer("recurseTreeWithOperator"); + entityTree->recurseTreeWithOperator(&moveOperator); } - }); + } } glm::mat4 MyAvatar::getSensorToWorldMatrix() const { From 33993b8c444b025bbcddbcf726275c46a122d2dd Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 26 Jan 2016 13:34:21 -0800 Subject: [PATCH 39/56] AnimClip: removed unused static member. --- libraries/animation/src/AnimClip.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 91215d0d44..7d58ae4f9a 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -26,7 +26,6 @@ public: friend class AnimTests; static bool usePreAndPostPoseFromAnim; - static bool useTransFromAnim; AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag); virtual ~AnimClip() override; From a731a571e02809882abe638680d5813c4475b6dc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 26 Jan 2016 13:50:58 -0800 Subject: [PATCH 40/56] Update entity server to always make resource directory if it does not exist --- assignment-client/src/octree/OctreeServer.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index d63e8e6620..81e25cc7ba 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1172,15 +1172,15 @@ void OctreeServer::domainSettingsRequestComplete() { pathToCopyFrom = oldDefaultPersistPath; } + QDir persistFileDirectory = QDir(persistPath).filePath(".."); + if (!persistFileDirectory.exists()) { + qDebug() << "Creating data directory " << persistFileDirectory.absolutePath(); + persistFileDirectory.mkpath("."); + } + if (shouldCopy) { qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistPath; - QDir persistFileDirectory = QDir(persistPath).filePath(".."); - - if (!persistFileDirectory.exists()) { - qDebug() << "Creating data directory " << persistFileDirectory.path(); - persistFileDirectory.mkpath("."); - } QFile::copy(pathToCopyFrom, persistPath); } else { qDebug() << "No existing persist file found"; From d8dbb717d5654bd7855c605d9e35f9bf8b205cf9 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 26 Jan 2016 14:06:17 -0800 Subject: [PATCH 41/56] Update junkyardResetEntityScript.js --- examples/junkyard/junkyardResetEntityScript.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/junkyard/junkyardResetEntityScript.js b/examples/junkyard/junkyardResetEntityScript.js index 9bd7ec02a5..9a330d0cdc 100644 --- a/examples/junkyard/junkyardResetEntityScript.js +++ b/examples/junkyard/junkyardResetEntityScript.js @@ -37,10 +37,10 @@ Entities.deleteEntity(e[i]); } } - this.importArf(); + this.importAssetResourceFile(); }, - importArf: function() { + importAssetResourceFile: function() { Clipboard.importEntities(IMPORT_URL); Clipboard.pasteEntities(PASTE_ENTITIES_LOCATION); }, @@ -50,4 +50,4 @@ }, }; return new JunkyardResetter(); -}); \ No newline at end of file +}); From e6a8fd192cf46ba800478c35bba61bce53923283 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 15:54:08 -0800 Subject: [PATCH 42/56] use the VS2011 DLLs bundled with sixense for installer --- cmake/macros/GenerateInstallers.cmake | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index e0523f32d3..b4744aa172 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -28,6 +28,15 @@ macro(GENERATE_INSTALLERS) # include CMake module that will install compiler system libraries # so that we have msvcr120 and msvcp120 installed with targets set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR}) + + # as long as we're including sixense plugin with installer + # we need re-distributables for VS 2011 as well + # this should be removed if/when sixense support is pulled + set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS + "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcr100.dll" + "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcp100.dll" + ) + include(InstallRequiredSystemLibraries) set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico") From 8ba0e3bc6ee40975832ce3012b3221db3d682d2f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 Jan 2016 16:21:18 -0800 Subject: [PATCH 43/56] give dev shortcuts a specific name too --- cmake/macros/SetPackagingParameters.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index de5a87e4d6..92e3273f67 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -83,7 +83,7 @@ macro(SET_PACKAGING_PARAMETERS) set(AC_EXEC_NAME "assignment-client.exe") # shortcut names - if (NOT PR_BUILD) + if (PRODUCTION_BUILD) set(INTERFACE_SHORTCUT_NAME "High Fidelity") set(CONSOLE_SHORTCUT_NAME "Server Console") else () From 8dc066fc00332ac2e8352768f624bd6226a811b0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 16:48:25 -0800 Subject: [PATCH 44/56] Fix the recursive launching of load script from URL --- interface/resources/qml/desktop/Desktop.qml | 2 +- .../resources/qml/dialogs/RunningScripts.qml | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 96c84b49c0..f246f79be9 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -167,7 +167,7 @@ FocusScope { } if (setFocus) { - focus = true; + targetWindow.focus = true; } reposition(targetWindow); diff --git a/interface/resources/qml/dialogs/RunningScripts.qml b/interface/resources/qml/dialogs/RunningScripts.qml index 059c765f0e..eb56b59b66 100644 --- a/interface/resources/qml/dialogs/RunningScripts.qml +++ b/interface/resources/qml/dialogs/RunningScripts.qml @@ -184,9 +184,28 @@ Window { anchors.bottom: filterEdit.top anchors.bottomMargin: 8 anchors.right: parent.right + + // For some reason trigginer an API that enters + // an internal event loop directly from the button clicked + // trigger below causes the appliction to behave oddly. + // Most likely because the button onClicked handling is never + // completed until the function returns. + // FIXME find a better way of handling the input dialogs that + // doesn't trigger this. + Timer { + id: asyncAction + interval: 50 + repeat: false + running: false + onTriggered: ApplicationInterface.loadScriptURLDialog(); + } + Button { text: "from URL"; - onClicked: ApplicationInterface.loadScriptURLDialog(); + onClicked: { + focus = false; + asyncAction.running = true; + } } Button { text: "from Disk" From cea57a111bc6a5923f5250de164286fd2de96489 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 16:56:33 -0800 Subject: [PATCH 45/56] Desktop cleanup --- interface/resources/qml/desktop/Desktop.qml | 72 ++++++++++----------- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 1 + 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index f246f79be9..14687f387e 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -13,6 +13,9 @@ FocusScope { anchors.fill: parent; objectName: "desktop" + onHeightChanged: d.repositionAll(); + onWidthChanged: d.repositionAll(); + // Allows QML/JS to find the desktop through the parent chain property bool desktopRoot: true @@ -50,10 +53,6 @@ FocusScope { return item; } - function isDesktop(item) { - return item.desktopRoot; - } - function isTopLevelWindow(item) { return item.topLevelWindow; } @@ -147,6 +146,31 @@ FocusScope { var windows = getTopLevelWindows(predicate); fixupZOrder(windows, zBasis, targetWindow); } + + Component.onCompleted: { + offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged); + focusHack.start(); + } + + function onWindowFocusChanged() { + console.log("Focus item is " + offscreenWindow.activeFocusItem); +// var focusedItem = offscreenWindow.activeFocusItem ; +// if (DebugQML && focusedItem) { +// var rect = desktop.mapFromItem(focusedItem, 0, 0, focusedItem.width, focusedItem.height); +// focusDebugger.x = rect.x; +// focusDebugger.y = rect.y; +// focusDebugger.width = rect.width +// focusDebugger.height = rect.height +// } + } + + + function repositionAll() { + var windows = d.getTopLevelWindows(); + for (var i = 0; i < windows.length; ++i) { + reposition(windows[i]); + } + } } function raise(item) { @@ -190,24 +214,16 @@ FocusScope { var newPosition; if (targetWindow.x === -1 && targetWindow.y === -1) { // Set initial window position - newPosition = Utils.randomPosition(minPosition, maxPosition); - } else { - newPosition = Utils.clampVector(Qt.vector2d(targetWindow.x, targetWindow.y), minPosition, maxPosition); + // newPosition = Utils.randomPosition(minPosition, maxPosition); + newPosition = Qt.vector2d(desktop.width / 2 - windowRect.width / 2, + desktop.height / 2 - windowRect.height / 2); } + + newPosition = Utils.clampVector(Qt.vector2d(targetWindow.x, targetWindow.y), minPosition, maxPosition); targetWindow.x = newPosition.x; targetWindow.y = newPosition.y; } - function repositionAll() { - var windows = d.getTopLevelWindows(); - for (var i = 0; i < windows.length; ++i) { - reposition(windows[i]); - } - } - - onHeightChanged: repositionAll(); - onWidthChanged: repositionAll(); - Component { id: messageDialogBuilder; MessageDialog { } } function messageBox(properties) { return messageDialogBuilder.createObject(desktop, properties); @@ -252,39 +268,21 @@ FocusScope { desktop.focus = true; } - // Debugging help for figuring out focus issues - property var offscreenWindow; - onOffscreenWindowChanged: { - offscreenWindow.activeFocusItemChanged.connect(onWindowFocusChanged); - focusHack.start(); - } - FocusHack { id: focusHack; } - function onWindowFocusChanged() { - console.log("Focus item is " + offscreenWindow.activeFocusItem); - var focusedItem = offscreenWindow.activeFocusItem ; - if (DebugQML && focusedItem) { - var rect = desktop.mapFromItem(focusedItem, 0, 0, focusedItem.width, focusedItem.height); - focusDebugger.x = rect.x; - focusDebugger.y = rect.y; - focusDebugger.width = rect.width - focusDebugger.height = rect.height - } - } - Rectangle { id: focusDebugger; z: 9999; visible: false; color: "red" ColorAnimation on color { from: "#7fffff00"; to: "#7f0000ff"; duration: 1000; loops: 9999 } } - + Action { text: "Toggle Focus Debugger" shortcut: "Ctrl+Shift+F" enabled: DebugQML onTriggered: focusDebugger.visible = !focusDebugger.visible } + } diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 813386132c..3a72d98402 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -338,6 +338,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { _updateTimer.start(); _qmlComponent = new QQmlComponent(_qmlEngine); + _qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); } void OffscreenQmlSurface::resize(const QSize& newSize_) { From 5f38c5265c5dfb226b270884bc936fc9b8309ecc Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 16:58:32 -0800 Subject: [PATCH 46/56] Fixing potential crash in dialog popups --- libraries/ui/src/OffscreenUi.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 8790c07f62..d7d28bef84 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -150,7 +150,9 @@ protected: } ~ModalDialogListener() { - disconnect(_dialog); + if (_dialog) { + disconnect(_dialog); + } } virtual QVariant waitForResult() { @@ -164,10 +166,11 @@ protected slots: void onDestroyed() { _finished = true; disconnect(_dialog); + _dialog = nullptr; } protected: - QQuickItem* const _dialog; + QQuickItem* _dialog; bool _finished { false }; QVariant _result; }; @@ -372,6 +375,7 @@ void OffscreenUi::createDesktop(const QUrl& url) { qDebug() << "Desktop already created"; return; } + #ifdef DEBUG getRootContext()->setContextProperty("DebugQML", QVariant(true)); #else @@ -382,9 +386,6 @@ void OffscreenUi::createDesktop(const QUrl& url) { Q_ASSERT(_desktop); getRootContext()->setContextProperty("desktop", _desktop); - // Enable focus debugging - _desktop->setProperty("offscreenWindow", QVariant::fromValue(getWindow())); - _toolWindow = _desktop->findChild("ToolWindow"); new VrMenu(this); From 940b0950139ecfe8cb34d0aa0c0fc57e86d582e7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 16:59:37 -0800 Subject: [PATCH 47/56] Removing menu logspam --- libraries/ui/src/VrMenu.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 103eb7660e..2006b22860 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -142,7 +142,6 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { Q_ASSERT(menuQml); QQuickMenuItem* returnedValue { nullptr }; - qDebug() << menuQml; bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection, Q_RETURN_ARG(QQuickMenuItem*, returnedValue), Q_ARG(QString, action->text())); From fee920723a04e675e91dca4a098f56b78f41da93 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 17:25:20 -0800 Subject: [PATCH 48/56] Cleaning up test project --- tests/ui/qml/UI.js | 45 --------------- tests/ui/qmlscratch.pro | 122 +++++++--------------------------------- 2 files changed, 19 insertions(+), 148 deletions(-) delete mode 100644 tests/ui/qml/UI.js diff --git a/tests/ui/qml/UI.js b/tests/ui/qml/UI.js deleted file mode 100644 index 0286ac56a6..0000000000 --- a/tests/ui/qml/UI.js +++ /dev/null @@ -1,45 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -.pragma library - -var margin = 2 -var tabPosition = Qt.TopEdge -var label = "" diff --git a/tests/ui/qmlscratch.pro b/tests/ui/qmlscratch.pro index b3c8be0f22..7a1b5ab652 100644 --- a/tests/ui/qmlscratch.pro +++ b/tests/ui/qmlscratch.pro @@ -7,108 +7,24 @@ CONFIG += c++11 SOURCES += src/main.cpp \ ../../libraries/ui/src/FileDialogHelper.cpp -# Additional import path used to resolve QML modules in Qt Creator's code model -QML_IMPORT_PATH = - -DISTFILES += \ - qml/UI.js \ - qml/main.qml \ - qml/Palettes.qml \ - qml/StubMenu.qml \ - qml/Stubs.qml \ - qml/TestControllers.qml \ - qml/TestDialog.qml \ - qml/TestMenu.qml \ - qml/TestRoot.qml \ - qml/controlDemo/ButtonPage.qml \ - qml/controlDemo/InputPage.qml \ - qml/controlDemo/main.qml \ - qml/controlDemo/ProgressPage.qml \ - ../../interface/resources/qml/controller/hydra/HydraButtons.qml \ - ../../interface/resources/qml/controller/hydra/HydraStick.qml \ - ../../interface/resources/qml/controller/xbox/DPad.qml \ - ../../interface/resources/qml/controller/xbox/LeftAnalogStick.qml \ - ../../interface/resources/qml/controller/xbox/RightAnalogStick.qml \ - ../../interface/resources/qml/controller/xbox/XboxButtons.qml \ - ../../interface/resources/qml/controller/AnalogButton.qml \ - ../../interface/resources/qml/controller/AnalogStick.qml \ - ../../interface/resources/qml/controller/Hydra.qml \ - ../../interface/resources/qml/controller/Standard.qml \ - ../../interface/resources/qml/controller/ToggleButton.qml \ - ../../interface/resources/qml/controller/Xbox.qml \ - ../../interface/resources/qml/controls/Button.qml \ - ../../interface/resources/qml/controls/ButtonAwesome.qml \ - ../../interface/resources/qml/controls/CheckBox.qml \ - ../../interface/resources/qml/controls/FontAwesome.qml \ - ../../interface/resources/qml/controls/Player.qml \ - ../../interface/resources/qml/controls/Slider.qml \ - ../../interface/resources/qml/controls/Spacer.qml \ - ../../interface/resources/qml/controls/SpinBox.qml \ - ../../interface/resources/qml/controls/Text.qml \ - ../../interface/resources/qml/controls/TextAndSlider.qml \ - ../../interface/resources/qml/controls/TextAndSpinBox.qml \ - ../../interface/resources/qml/controls/TextArea.qml \ - ../../interface/resources/qml/controls/TextEdit.qml \ - ../../interface/resources/qml/controls/TextHeader.qml \ - ../../interface/resources/qml/controls/TextInput.qml \ - ../../interface/resources/qml/controls/TextInputAndButton.qml \ - ../../interface/resources/qml/controls/WebView.qml \ - ../../interface/resources/qml/dialogs/FileDialog.qml \ - ../../interface/resources/qml/dialogs/MessageDialog.qml \ - ../../interface/resources/qml/dialogs/RunningScripts.qml \ - ../../interface/resources/qml/hifi/MenuOption.qml \ - ../../interface/resources/qml/styles/Border.qml \ - ../../interface/resources/qml/styles/HifiConstants.qml \ - ../../interface/resources/qml/windows/DefaultFrame.qml \ - ../../interface/resources/qml/windows/Fadable.qml \ - ../../interface/resources/qml/windows/Frame.qml \ - ../../interface/resources/qml/windows/ModalFrame.qml \ - ../../interface/resources/qml/windows/Window.qml \ - ../../interface/resources/qml/AddressBarDialog.qml \ - ../../interface/resources/qml/AvatarInputs.qml \ - ../../interface/resources/qml/Browser.qml \ - ../../interface/resources/qml/InfoView.qml \ - ../../interface/resources/qml/LoginDialog.qml \ - ../../interface/resources/qml/QmlWebWindow.qml \ - ../../interface/resources/qml/QmlWindow.qml \ - ../../interface/resources/qml/Root.qml \ - ../../interface/resources/qml/ScrollingGraph.qml \ - ../../interface/resources/qml/Stats.qml \ - ../../interface/resources/qml/TextOverlayElement.qml \ - ../../interface/resources/qml/Tooltip.qml \ - ../../interface/resources/qml/ToolWindow.qml \ - ../../interface/resources/qml/UpdateDialog.qml \ - ../../interface/resources/qml/VrMenu.qml \ - ../../interface/resources/qml/VrMenuItem.qml \ - ../../interface/resources/qml/VrMenuView.qml \ - ../../interface/resources/qml/WebEntity.qml \ - ../../interface/resources/qml/desktop/Desktop.qml \ - ../../interface/resources/qml/hifi/Desktop.qml \ - ../../interface/resources/qml/menus/MenuMouseHandler.qml \ - ../../interface/resources/qml/menus/VrMenuItem.qml \ - ../../interface/resources/qml/menus/VrMenuView.qml \ - ../../interface/resources/qml/windows/ModalWindow.qml \ - ../../interface/resources/qml/desktop/FocusHack.qml \ - ../../interface/resources/qml/hifi/dialogs/PreferencesDialog.qml \ - ../../interface/resources/qml/hifi/dialogs/Section.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/Browsable.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/Section.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/Editable.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/Slider.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/Preference.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/CheckBox.qml \ - ../../interface/resources/qml/dialogs/fileDialog/FileTableView.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/Avatar.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/AvatarBrowser.qml \ - ../../interface/resources/qml/dialogs/QueryDialog.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/Button.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/AvatarPreference.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/BrowsablePreference.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/ButtonPreference.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/CheckBoxPreference.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/EditablePreference.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/SliderPreference.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/SpinBoxPreference.qml - HEADERS += \ ../../libraries/ui/src/FileDialogHelper.h + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = ../../interface/resources/qml + + +DISTFILES += \ + qml/*.qml \ + ../../interface/resources/qml/*.qml \ + ../../interface/resources/qml/controls/*.qml \ + ../../interface/resources/qml/dialogs/*.qml \ + ../../interface/resources/qml/dialogs/fileDialog/*.qml \ + ../../interface/resources/qml/desktop/*.qml \ + ../../interface/resources/qml/menus/*.qml \ + ../../interface/resources/qml/styles/*.qml \ + ../../interface/resources/qml/windows/*.qml \ + ../../interface/resources/qml/hifi/*.qml \ + ../../interface/resources/qml/hifi/dialogs/*.qml \ + ../../interface/resources/qml/hifi/dialogs/preferences/*.qml + From 9a8d56da77ea79a232f16017cfa75d4301c03068 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 17:25:36 -0800 Subject: [PATCH 49/56] Center new windows without a defined position --- interface/resources/qml/desktop/Desktop.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 14687f387e..e87862aa56 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -211,15 +211,16 @@ FocusScope { var windowRect = targetWindow.framedRect(); var minPosition = Qt.vector2d(-windowRect.x, -windowRect.y); var maxPosition = Qt.vector2d(desktop.width - windowRect.width, desktop.height - windowRect.height); - var newPosition; - if (targetWindow.x === -1 && targetWindow.y === -1) { + var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y); + if (newPosition.x === -1 && newPosition.y === -1) { // Set initial window position // newPosition = Utils.randomPosition(minPosition, maxPosition); + console.log("Target has no defined position, putting in center of the screen") newPosition = Qt.vector2d(desktop.width / 2 - windowRect.width / 2, desktop.height / 2 - windowRect.height / 2); } - newPosition = Utils.clampVector(Qt.vector2d(targetWindow.x, targetWindow.y), minPosition, maxPosition); + newPosition = Utils.clampVector(newPosition, minPosition, maxPosition); targetWindow.x = newPosition.x; targetWindow.y = newPosition.y; } From fbee221966a7dd2dc7f21dd558cc161a33d8ad59 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 18:15:41 -0800 Subject: [PATCH 50/56] Fix tool window breaking when visible while edit.js reloads --- interface/resources/qml/ToolWindow.qml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/ToolWindow.qml b/interface/resources/qml/ToolWindow.qml index 8e42f67ebc..9aad639ff3 100644 --- a/interface/resources/qml/ToolWindow.qml +++ b/interface/resources/qml/ToolWindow.qml @@ -103,6 +103,8 @@ Windows.Window { if (index < 0) { return; } + var tab = tabView.getTab(index); + tab.enabledChanged.disconnect(updateVisiblity); tabView.removeTab(index); console.log("Updating visibility based on child tab removed"); updateVisiblity(); @@ -137,10 +139,7 @@ Windows.Window { } console.log("Updating visibility based on child tab added"); - newTab.enabledChanged.connect(function() { - console.log("Updating visibility based on child tab enabled change"); - updateVisiblity(); - }) + newTab.enabledChanged.connect(updateVisiblity) updateVisiblity(); return newTab } From 0a6be80578a1abd00ec23eac423443baab2061e1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 18:50:55 -0800 Subject: [PATCH 51/56] Added some disabled modal focus protection code --- interface/resources/qml/desktop/Desktop.qml | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index e87862aa56..8c4f73c200 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -154,6 +154,28 @@ FocusScope { function onWindowFocusChanged() { console.log("Focus item is " + offscreenWindow.activeFocusItem); + + // FIXME this needs more testing before it can go into production + // and I already cant produce any way to have a modal dialog lose focus + // to a non-modal one. + /* + var focusedWindow = getDesktopWindow(offscreenWindow.activeFocusItem); + + if (isModalWindow(focusedWindow)) { + return; + } + + // new focused window is not modal... check if there are any modal windows + var windows = getTopLevelWindows(isModalWindow); + if (0 === windows.length) { + return; + } + + // There are modal windows present, force focus back to the top-most modal window + windows.sort(function(a, b){ return a.z - b.z; }); + windows[windows.length - 1].focus = true; + */ + // var focusedItem = offscreenWindow.activeFocusItem ; // if (DebugQML && focusedItem) { // var rect = desktop.mapFromItem(focusedItem, 0, 0, focusedItem.width, focusedItem.height); From 664100b9b14e13eeea2ff2aae71be21c95dbd571 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 23 Jan 2016 17:14:30 -0800 Subject: [PATCH 52/56] Attachment dialog --- .../qml/hifi/dialogs/AttachmentsDialog.qml | 123 +++++++++++++ .../hifi/dialogs/AvatarAttachmentsDialog.qml | 128 ++++++++++++++ .../qml/hifi/dialogs/ModelBrowserDialog.qml | 128 ++++++++++++++ .../hifi/dialogs/attachments/Attachment.qml | 164 ++++++++++++++++++ .../qml/hifi/dialogs/attachments/Rotation.qml | 9 + .../hifi/dialogs/attachments/Translation.qml | 9 + .../qml/hifi/dialogs/attachments/Vector3.qml | 71 ++++++++ .../resources/qml/hifi/models/S3Model.qml | 41 +++++ interface/resources/qml/js/Utils.js | 14 ++ interface/src/Application.cpp | 5 + libraries/avatars/src/AvatarData.cpp | 63 +++++++ libraries/avatars/src/AvatarData.h | 6 + libraries/networking/src/LimitedNodeList.cpp | 2 +- tests/ui/qml/main.qml | 12 ++ 14 files changed, 774 insertions(+), 1 deletion(-) create mode 100644 interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml create mode 100644 interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml create mode 100644 interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml create mode 100644 interface/resources/qml/hifi/dialogs/attachments/Attachment.qml create mode 100644 interface/resources/qml/hifi/dialogs/attachments/Rotation.qml create mode 100644 interface/resources/qml/hifi/dialogs/attachments/Translation.qml create mode 100644 interface/resources/qml/hifi/dialogs/attachments/Vector3.qml create mode 100644 interface/resources/qml/hifi/models/S3Model.qml diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml new file mode 100644 index 0000000000..77771f65c4 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml @@ -0,0 +1,123 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import Qt.labs.settings 1.0 + +import "../../windows" +import "attachments" + +Window { + id: root + title: "Edit Attachments" + width: 600 + height: 600 + resizable: true + // User must click OK or cancel to close the window + closable: false + + readonly property var originalAttachments: MyAvatar.getAttachmentsVariant(); + property var attachments: []; + + property var settings: Settings { + category: "AttachmentsDialog" + property alias x: root.x + property alias y: root.y + property alias width: root.width + property alias height: root.height + } + + Component.onCompleted: { + for (var i = 0; i < originalAttachments.length; ++i) { + var attachment = originalAttachments[i]; + root.attachments.push(attachment); + listView.model.append({}); + } + } + + Rectangle { + anchors.fill: parent + radius: 4 + + Rectangle { + id: attachmentsBackground + anchors { left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; margins: 8 } + color: "gray" + radius: 4 + + ScrollView{ + id: scrollView + anchors.fill: parent + anchors.margins: 4 + ListView { + id: listView + model: ListModel {} + delegate: Item { + implicitHeight: attachmentView.height + 8; + implicitWidth: attachmentView.width; + Attachment { + id: attachmentView + width: scrollView.width + attachment: root.attachments[index] + onDeleteAttachment: { + attachments.splice(index, 1); + listView.model.remove(index, 1); + } + onUpdateAttachment: MyAvatar.setAttachmentsVariant(attachments); + } + } + onCountChanged: MyAvatar.setAttachmentsVariant(attachments); + } + } + } + + Button { + id: newAttachmentButton + anchors { left: parent.left; right: parent.right; bottom: buttonRow.top; margins: 8 } + text: "New Attachment" + + onClicked: { + var template = { + modelUrl: "", + translation: { x: 0, y: 0, z: 0 }, + rotation: { x: 0, y: 0, z: 0 }, + scale: 1, + jointName: MyAvatar.jointNames[0], + soft: false + }; + attachments.push(template); + listView.model.append({}); + MyAvatar.setAttachmentsVariant(attachments); + } + } + + Row { + id: buttonRow + spacing: 8 + anchors { right: parent.right; bottom: parent.bottom; margins: 8 } + Button { action: cancelAction } + Button { action: okAction } + } + + Action { + id: cancelAction + text: "Cancel" + onTriggered: { + MyAvatar.setAttachmentsVariant(originalAttachments); + root.destroy() + } + } + + Action { + id: okAction + text: "OK" + onTriggered: { + for (var i = 0; i < attachments.length; ++i) { + console.log("Attachment " + i + ": " + attachments[i]); + } + + MyAvatar.setAttachmentsVariant(attachments); + root.destroy() + } + } + } +} + diff --git a/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml new file mode 100644 index 0000000000..252e4c629e --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml @@ -0,0 +1,128 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.XmlListModel 2.0 + +import "../../windows" +import "../../js/Utils.js" as Utils +import "../models" + +ModalWindow { + id: root + resizable: true + width: 640 + height: 480 + + property var result; + + signal selected(var modelUrl); + signal canceled(); + + Rectangle { + anchors.fill: parent + color: "white" + + Item { + anchors { fill: parent; margins: 8 } + + TextField { + id: filterEdit + anchors { left: parent.left; right: parent.right; top: parent.top } + placeholderText: "filter" + onTextChanged: tableView.model.filter = text + } + + TableView { + id: tableView + anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 } + model: S3Model{} + onCurrentRowChanged: { + if (currentRow == -1) { + root.result = null; + return; + } + result = model.baseUrl + "/" + model.get(tableView.currentRow).key; + } + itemDelegate: Component { + Item { + clip: true + Text { + x: 3 + anchors.verticalCenter: parent.verticalCenter + color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor + elide: styleData.elideMode + text: getText() + + function getText() { + switch(styleData.column) { + case 1: + return Utils.formatSize(styleData.value) + default: + return styleData.value; + } + } + + } + } + } + TableViewColumn { + role: "name" + title: "Name" + width: 200 + } + TableViewColumn { + role: "size" + title: "Size" + width: 100 + } + TableViewColumn { + role: "modified" + title: "Last Modified" + width: 200 + } + Rectangle { + anchors.fill: parent + visible: tableView.model.status !== XmlListModel.Ready + color: "#7fffffff" + BusyIndicator { + anchors.centerIn: parent + width: 48; height: 48 + running: true + } + } + } + + Row { + id: buttonRow + anchors { right: parent.right; bottom: parent.bottom } + Button { action: acceptAction } + Button { action: cancelAction } + } + + Action { + id: acceptAction + text: qsTr("OK") + enabled: root.result ? true : false + shortcut: Qt.Key_Return + onTriggered: { + root.selected(root.result); + root.destroy(); + } + } + + Action { + id: cancelAction + text: qsTr("Cancel") + shortcut: Qt.Key_Escape + onTriggered: { + root.canceled(); + root.destroy(); + } + } + } + + } +} + + + + diff --git a/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml b/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml new file mode 100644 index 0000000000..252e4c629e --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml @@ -0,0 +1,128 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.XmlListModel 2.0 + +import "../../windows" +import "../../js/Utils.js" as Utils +import "../models" + +ModalWindow { + id: root + resizable: true + width: 640 + height: 480 + + property var result; + + signal selected(var modelUrl); + signal canceled(); + + Rectangle { + anchors.fill: parent + color: "white" + + Item { + anchors { fill: parent; margins: 8 } + + TextField { + id: filterEdit + anchors { left: parent.left; right: parent.right; top: parent.top } + placeholderText: "filter" + onTextChanged: tableView.model.filter = text + } + + TableView { + id: tableView + anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 } + model: S3Model{} + onCurrentRowChanged: { + if (currentRow == -1) { + root.result = null; + return; + } + result = model.baseUrl + "/" + model.get(tableView.currentRow).key; + } + itemDelegate: Component { + Item { + clip: true + Text { + x: 3 + anchors.verticalCenter: parent.verticalCenter + color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor + elide: styleData.elideMode + text: getText() + + function getText() { + switch(styleData.column) { + case 1: + return Utils.formatSize(styleData.value) + default: + return styleData.value; + } + } + + } + } + } + TableViewColumn { + role: "name" + title: "Name" + width: 200 + } + TableViewColumn { + role: "size" + title: "Size" + width: 100 + } + TableViewColumn { + role: "modified" + title: "Last Modified" + width: 200 + } + Rectangle { + anchors.fill: parent + visible: tableView.model.status !== XmlListModel.Ready + color: "#7fffffff" + BusyIndicator { + anchors.centerIn: parent + width: 48; height: 48 + running: true + } + } + } + + Row { + id: buttonRow + anchors { right: parent.right; bottom: parent.bottom } + Button { action: acceptAction } + Button { action: cancelAction } + } + + Action { + id: acceptAction + text: qsTr("OK") + enabled: root.result ? true : false + shortcut: Qt.Key_Return + onTriggered: { + root.selected(root.result); + root.destroy(); + } + } + + Action { + id: cancelAction + text: qsTr("Cancel") + shortcut: Qt.Key_Escape + onTriggered: { + root.canceled(); + root.destroy(); + } + } + } + + } +} + + + + diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml new file mode 100644 index 0000000000..b45e10d755 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml @@ -0,0 +1,164 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +import "../../../windows" +import "." +import ".." + +Item { + height: column.height + 2 * 8 + + property var attachment; + + signal deleteAttachment(var attachment); + signal updateAttachment(); + property bool completed: false; + + Rectangle { color: "white"; anchors.fill: parent; radius: 4 } + + Component.onCompleted: { + completed = true; + } + + Column { + y: 8 + id: column + anchors { left: parent.left; right: parent.right; margins: 8 } + spacing: 8 + + Item { + height: modelChooserButton.height + anchors { left: parent.left; right: parent.right; } + Text { id: urlLabel; text: "Model URL:"; width: 80; anchors.verticalCenter: modelUrl.verticalCenter } + TextField { + id: modelUrl; + height: jointChooser.height; + anchors { left: urlLabel.right; leftMargin: 8; rightMargin: 8; right: modelChooserButton.left } + text: attachment ? attachment.modelUrl : "" + onTextChanged: { + if (completed && attachment && attachment.modelUrl !== text) { + attachment.modelUrl = text; + updateAttachment(); + } + } + } + Button { + id: modelChooserButton; + text: "Choose"; + anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter } + Component { + id: modelBrowserBuiler; + ModelBrowserDialog {} + } + + onClicked: { + var browser = modelBrowserBuiler.createObject(desktop); + browser.selected.connect(function(newModelUrl){ + modelUrl.text = newModelUrl; + }) + } + } + } + + Item { + height: jointChooser.height + anchors { left: parent.left; right: parent.right; } + Text { + id: jointLabel; + width: 80; + text: "Joint:"; + anchors.verticalCenter: jointChooser.verticalCenter; + } + ComboBox { + id: jointChooser; + anchors { left: jointLabel.right; leftMargin: 8; right: parent.right } + model: MyAvatar.jointNames + currentIndex: attachment ? model.indexOf(attachment.jointName) : -1 + onCurrentIndexChanged: { + if (completed && attachment && currentIndex != -1 && currentText && currentText !== attachment.jointName) { + attachment.jointName = currentText; + updateAttachment(); + } + } + } + } + + Item { + height: translation.height + anchors { left: parent.left; right: parent.right; } + Text { id: translationLabel; width: 80; text: "Translation:"; anchors.verticalCenter: translation.verticalCenter; } + Translation { + id: translation; + anchors { left: translationLabel.right; leftMargin: 8; right: parent.right } + vector: attachment ? attachment.translation : {x: 0, y: 0, z: 0}; + onValueChanged: { + if (completed && attachment) { + attachment.translation = vector; + updateAttachment(); + } + } + } + } + + Item { + height: rotation.height + anchors { left: parent.left; right: parent.right; } + Text { id: rotationLabel; width: 80; text: "Rotation:"; anchors.verticalCenter: rotation.verticalCenter; } + Rotation { + id: rotation; + anchors { left: rotationLabel.right; leftMargin: 8; right: parent.right } + vector: attachment ? attachment.rotation : {x: 0, y: 0, z: 0}; + onValueChanged: { + if (completed && attachment) { + attachment.rotation = vector; + updateAttachment(); + } + } + } + } + + Item { + height: scaleSpinner.height + anchors { left: parent.left; right: parent.right; } + Text { id: scaleLabel; width: 80; text: "Scale:"; anchors.verticalCenter: scale.verticalCenter; } + SpinBox { + id: scaleSpinner; + anchors { left: scaleLabel.right; leftMargin: 8; right: parent.right } + decimals: 1; + minimumValue: 0.1 + maximumValue: 10 + stepSize: 0.1; + value: attachment ? attachment.scale : 1.0 + onValueChanged: { + if (completed && attachment && attachment.scale !== value) { + attachment.scale = value; + updateAttachment(); + } + } + } + } + + Item { + height: soft.height + anchors { left: parent.left; right: parent.right; } + Text { id: softLabel; width: 80; text: "Is soft:"; anchors.verticalCenter: soft.verticalCenter; } + CheckBox { + id: soft; + anchors { left: softLabel.right; leftMargin: 8; right: parent.right } + checked: attachment ? attachment.soft : false + onCheckedChanged: { + if (completed && attachment && attachment.soft !== checked) { + attachment.soft = checked; + updateAttachment(); + } + } + } + } + + Button { + anchors { left: parent.left; right: parent.right; } + text: "Delete" + onClicked: deleteAttachment(root.attachment); + } + } +} diff --git a/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml b/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml new file mode 100644 index 0000000000..6061efc4c8 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml @@ -0,0 +1,9 @@ +import "." + +Vector3 { + decimals: 1; + stepSize: 1; + maximumValue: 180 + minimumValue: -180 +} + diff --git a/interface/resources/qml/hifi/dialogs/attachments/Translation.qml b/interface/resources/qml/hifi/dialogs/attachments/Translation.qml new file mode 100644 index 0000000000..f3a90cdf94 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/attachments/Translation.qml @@ -0,0 +1,9 @@ +import "." + +Vector3 { + decimals: 2; + stepSize: 0.01; + maximumValue: 10 + minimumValue: -10 +} + diff --git a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml new file mode 100644 index 0000000000..02382749bd --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml @@ -0,0 +1,71 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +Item { + id: root + implicitHeight: xspinner.height + readonly property real spacing: 8 + property real spinboxWidth: (width / 3) - spacing + property var vector; + property real decimals: 0 + property real stepSize: 1 + property real maximumValue: 99 + property real minimumValue: 0 + + signal valueChanged(); + + SpinBox { + id: xspinner + width: root.spinboxWidth + anchors { left: parent.left } + value: root.vector.x + + decimals: root.decimals + stepSize: root.stepSize + maximumValue: root.maximumValue + minimumValue: root.minimumValue + onValueChanged: { + if (value !== vector.x) { + vector.x = value + root.valueChanged(); + } + } + } + + SpinBox { + id: yspinner + width: root.spinboxWidth + anchors { horizontalCenter: parent.horizontalCenter } + value: root.vector.y + + decimals: root.decimals + stepSize: root.stepSize + maximumValue: root.maximumValue + minimumValue: root.minimumValue + onValueChanged: { + if (value !== vector.y) { + vector.y = value + root.valueChanged(); + } + } + } + + SpinBox { + id: zspinner + width: root.spinboxWidth + anchors { right: parent.right; } + value: root.vector.z + + decimals: root.decimals + stepSize: root.stepSize + maximumValue: root.maximumValue + minimumValue: root.minimumValue + onValueChanged: { + if (value !== vector.z) { + vector.z = value + root.valueChanged(); + } + } + } +} + diff --git a/interface/resources/qml/hifi/models/S3Model.qml b/interface/resources/qml/hifi/models/S3Model.qml new file mode 100644 index 0000000000..565965c124 --- /dev/null +++ b/interface/resources/qml/hifi/models/S3Model.qml @@ -0,0 +1,41 @@ +import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 + +//http://s3.amazonaws.com/hifi-public?prefix=models/attachments +/* + + + + models/attachments/guitar.fst + 2015-11-10T00:28:22.000Z + "236c00c4802ba9c2605cabd5601d138e" + 2992 + STANDARD + + +*/ + +// FIXME how to deal with truncated results? Store the marker? +XmlListModel { + id: xmlModel + property string prefix: "models/attachments/" + property string extension: "fst" + property string filter; + + readonly property string realPrefix: prefix.match('.*/$') ? prefix : (prefix + "/") + readonly property string nameRegex: realPrefix + (filter ? (".*" + filter) : "") + ".*\." + extension + readonly property string nameQuery: "Key/substring-before(substring-after(string(), '" + prefix + "'), '." + extension + "')" + readonly property string baseUrl: "http://s3.amazonaws.com/hifi-public" + + // FIXME need to urlencode prefix? + source: baseUrl + "?prefix=" + realPrefix + query: "/ListBucketResult/Contents[matches(Key, '" + nameRegex + "')]" + namespaceDeclarations: "declare default element namespace 'http://s3.amazonaws.com/doc/2006-03-01/';" + + XmlRole { name: "name"; query: nameQuery } + XmlRole { name: "size"; query: "Size/string()" } + XmlRole { name: "tag"; query: "ETag/string()" } + XmlRole { name: "modified"; query: "LastModified/string()" } + XmlRole { name: "key"; query: "Key/string()" } +} + diff --git a/interface/resources/qml/js/Utils.js b/interface/resources/qml/js/Utils.js index 417c8c1641..49f85fcc91 100644 --- a/interface/resources/qml/js/Utils.js +++ b/interface/resources/qml/js/Utils.js @@ -14,3 +14,17 @@ function randomPosition(min, max) { Math.random() * (max.x - min.x), Math.random() * (max.y - min.y)); } + +function formatSize(size) { + var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ]; + var suffixIndex = 0 + while ((size / 1024.0) > 1.1) { + size /= 1024.0; + ++suffixIndex; + } + + size = Math.round(size*1000)/1000; + size = size.toLocaleString() + + return size + " " + suffixes[suffixIndex]; +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4629a8c08c..b3142702bb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1186,6 +1186,7 @@ void Application::initializeUi() { UpdateDialog::registerType(); qmlRegisterType("Hifi", 1, 0, "Preference"); + auto offscreenUi = DependencyManager::get(); offscreenUi->create(_offscreenContext->getContext()); offscreenUi->setProxyWindow(_window->windowHandle()); @@ -1842,6 +1843,10 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_X: if (isShifted && isMeta) { + auto offscreenUi = DependencyManager::get(); + offscreenUi->getRootContext()->engine()->clearComponentCache(); + offscreenUi->load("hifi/dialogs/AttachmentsDialog.qml"); + // OffscreenUi::information("Debugging", "Component cache cleared"); // placeholder for dialogs being converted to QML. } break; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 5fd69128eb..3933d705fc 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -1673,3 +1674,65 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const assert(false); return glm::vec3(); } + +QVariant AttachmentData::toVariant() const { + QVariantMap result; + result["modelUrl"] = modelURL; + result["jointName"] = jointName; + result["translation"] = glmToQMap(translation); + result["rotation"] = glmToQMap(glm::degrees(safeEulerAngles(rotation))); + result["scale"] = scale; + result["soft"] = isSoft; + return result; +} + +glm::vec3 variantToVec3(const QVariant& var) { + auto map = var.toMap(); + glm::vec3 result; + result.x = map["x"].toFloat(); + result.y = map["y"].toFloat(); + result.z = map["z"].toFloat(); + return result; +} + +void AttachmentData::fromVariant(const QVariant& variant) { + auto map = variant.toMap(); + if (map.contains("modelUrl")) { + auto urlString = map["modelUrl"].toString(); + modelURL = urlString; + } + if (map.contains("jointName")) { + jointName = map["jointName"].toString(); + } + if (map.contains("translation")) { + translation = variantToVec3(map["translation"]); + } + if (map.contains("rotation")) { + rotation = glm::quat(glm::radians(variantToVec3(map["rotation"]))); + } + if (map.contains("scale")) { + scale = map["scale"].toFloat(); + } + if (map.contains("soft")) { + isSoft = map["soft"].toBool(); + } +} + +QVariantList AvatarData::getAttachmentsVariant() const { + QVariantList result; + for (const auto& attachment : getAttachmentData()) { + result.append(attachment.toVariant()); + } + return result; +} + +void AvatarData::setAttachmentsVariant(const QVariantList& variant) { + QVector newAttachments; + newAttachments.reserve(variant.size()); + for (const auto& attachmentVar : variant) { + AttachmentData attachment; + attachment.fromVariant(attachmentVar); + newAttachments.append(attachment); + } + setAttachmentData(newAttachments); +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 3b8214f226..eac1917533 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -277,6 +277,9 @@ public: Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } + Q_INVOKABLE QVariantList getAttachmentsVariant() const; + Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant); + void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; } // key state @@ -448,6 +451,9 @@ public: QJsonObject toJson() const; void fromJson(const QJsonObject& json); + + QVariant toVariant() const; + void fromVariant(const QVariant& variant); }; QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 485100da0a..3ea3175390 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -959,7 +959,7 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) { QSharedMemory sharedMem(key); if (!sharedMem.attach(QSharedMemory::ReadOnly)) { - qWarning() << "Could not attach to shared memory at key" << key; + qCWarning(networking) << "Could not attach to shared memory at key" << key; return false; } else { sharedMem.lock(); diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml index c970cd711b..d20b580b5a 100644 --- a/tests/ui/qml/main.qml +++ b/tests/ui/qml/main.qml @@ -7,6 +7,7 @@ import "../../../interface/resources/qml" import "../../../interface/resources/qml/windows" import "../../../interface/resources/qml/dialogs" import "../../../interface/resources/qml/hifi" +import "../../../interface/resources/qml/hifi/dialogs" ApplicationWindow { id: appWindow @@ -196,4 +197,15 @@ ApplicationWindow { } */ } + + Action { + text: "Open Browser" + shortcut: "Ctrl+Shift+X" + onTriggered: { + builder.createObject(desktop); + } + property var builder: Component { + ModelBrowserDialog{} + } + } } From 2956557c5f50f8e5dd9b81ee4ed42629a54b8892 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 14:41:22 -0800 Subject: [PATCH 53/56] Adding overlay compatible combo-box --- interface/resources/qml/controls/ComboBox.qml | 148 ++++++++++++++++++ .../hifi/dialogs/attachments/Attachment.qml | 3 +- 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 interface/resources/qml/controls/ComboBox.qml diff --git a/interface/resources/qml/controls/ComboBox.qml b/interface/resources/qml/controls/ComboBox.qml new file mode 100644 index 0000000000..e22bc8e664 --- /dev/null +++ b/interface/resources/qml/controls/ComboBox.qml @@ -0,0 +1,148 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import "." as VrControls + +FocusScope { + id: root + property alias model: comboBox.model; + readonly property alias currentText: comboBox.currentText; + property alias currentIndex: comboBox.currentIndex; + implicitHeight: comboBox.height; + focus: true + + readonly property ComboBox control: comboBox + + Rectangle { + id: background + gradient: Gradient { + GradientStop {color: control.pressed ? "#bababa" : "#fefefe" ; position: 0} + GradientStop {color: control.pressed ? "#ccc" : "#e3e3e3" ; position: 1} + } + anchors.fill: parent + border.color: control.activeFocus ? "#47b" : "#999" + Rectangle { + anchors.fill: parent + radius: parent.radius + color: control.activeFocus ? "#47b" : "white" + opacity: control.hovered || control.activeFocus ? 0.1 : 0 + Behavior on opacity {NumberAnimation{ duration: 100 }} + } + } + + SystemPalette { id: palette } + + ComboBox { + id: comboBox + anchors.fill: parent + visible: false + } + + Text { + id: textField + anchors { left: parent.left; leftMargin: 2; right: dropIcon.left; verticalCenter: parent.verticalCenter } + text: comboBox.currentText + elide: Text.ElideRight + } + + Item { + id: dropIcon + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + width: 20 + height: textField.height + VrControls.FontAwesome { + anchors.centerIn: parent; size: 16; + text: "\uf0d7" + } + } + + MouseArea { + anchors.fill: parent + onClicked: toggleList(); + } + + function toggleList() { + if (popup.visible) { + hideList(); + } else { + showList(); + } + } + + function showList() { + var r = desktop.mapFromItem(root, 0, 0, root.width, root.height); + listView.currentIndex = root.currentIndex + scrollView.x = r.x; + scrollView.y = r.y + r.height; + var bottom = scrollView.y + scrollView.height; + if (bottom > desktop.height) { + scrollView.y -= bottom - desktop.height + 8; + } + popup.visible = true; + popup.forceActiveFocus(); + } + + function hideList() { + popup.visible = false; + } + + FocusScope { + id: popup + parent: desktop + anchors.fill: parent + visible: false + focus: true + MouseArea { + anchors.fill: parent + onClicked: hideList(); + } + + function previousItem() { listView.currentIndex = (listView.currentIndex + listView.count - 1) % listView.count; } + function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; } + function selectCurrentItem() { root.currentIndex = listView.currentIndex; hideList(); } + + Keys.onUpPressed: previousItem(); + Keys.onDownPressed: nextItem(); + Keys.onSpacePressed: selectCurrentItem(); + Keys.onRightPressed: selectCurrentItem(); + Keys.onReturnPressed: selectCurrentItem(); + Keys.onEscapePressed: hideList(); + + ScrollView { + id: scrollView + height: 480 + width: root.width + + ListView { + id: listView + height: textView.height * count + model: root.model + highlight: Rectangle{ + width: listView.currentItem ? listView.currentItem.width : 0 + height: listView.currentItem ? listView.currentItem.height : 0 + color: "red" + } + delegate: Rectangle { + width: root.width + height: popupText.implicitHeight * 1.4 + color: ListView.isCurrentItem ? palette.highlight : palette.base + Text { + anchors.verticalCenter: parent.verticalCenter + id: popupText + x: 3 + text: listView.model[index] + } + MouseArea { + id: popupHover + anchors.fill: parent; + hoverEnabled: true + onEntered: listView.currentIndex = index; + onClicked: popup.selectCurrentItem() + } + } + } + } + } + +} diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml index b45e10d755..31a1895e58 100644 --- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml +++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml @@ -2,6 +2,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import "../../../windows" +import "../../../controls" as VrControls import "." import ".." @@ -69,7 +70,7 @@ Item { text: "Joint:"; anchors.verticalCenter: jointChooser.verticalCenter; } - ComboBox { + VrControls.ComboBox { id: jointChooser; anchors { left: jointLabel.right; leftMargin: 8; right: parent.right } model: MyAvatar.jointNames From f53aaa1d64b3c92a39bfc24e885270232287f166 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Jan 2016 15:54:03 -0800 Subject: [PATCH 54/56] Removing old attachment dialog, updating menu code --- .../qml/hifi/dialogs/AttachmentsDialog.qml | 1 + interface/src/Application.cpp | 3 +- interface/src/Menu.cpp | 13 +- interface/src/ui/AttachmentsDialog.cpp | 239 ------------------ interface/src/ui/AttachmentsDialog.h | 84 ------ interface/src/ui/DialogsManager.cpp | 10 - interface/src/ui/DialogsManager.h | 1 - 7 files changed, 10 insertions(+), 341 deletions(-) delete mode 100644 interface/src/ui/AttachmentsDialog.cpp delete mode 100644 interface/src/ui/AttachmentsDialog.h diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml index 77771f65c4..1c70f06efd 100644 --- a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml +++ b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml @@ -8,6 +8,7 @@ import "attachments" Window { id: root title: "Edit Attachments" + objectName: "AttachmentsDialog" width: 600 height: 600 resizable: true diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b3142702bb..27f9d81ab2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1845,8 +1845,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (isShifted && isMeta) { auto offscreenUi = DependencyManager::get(); offscreenUi->getRootContext()->engine()->clearComponentCache(); - offscreenUi->load("hifi/dialogs/AttachmentsDialog.qml"); - // OffscreenUi::information("Debugging", "Component cache cleared"); + OffscreenUi::information("Debugging", "Component cache cleared"); // placeholder for dialogs being converted to QML. } break; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9cbb031a61..14c91dbda8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -70,8 +70,8 @@ Menu::Menu() { } // File > Update -- FIXME: needs implementation - auto updateAction = addActionToQMenuAndActionHash(fileMenu, "Update"); - updateAction->setDisabled(true); + auto action = addActionToQMenuAndActionHash(fileMenu, "Update"); + action->setDisabled(true); // File > Help addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp())); @@ -166,8 +166,11 @@ Menu::Menu() { QObject* avatar = avatarManager->getMyAvatar(); // Avatar > Attachments... - addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments, 0, - dialogsManager.data(), SLOT(editAttachments())); + action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments); + connect(action, &QAction::triggered, [] { + DependencyManager::get()->show(QString("hifi/dialogs/AttachmentsDialog.qml"), "AttachmentsDialog"); + }); + // Avatar > Size MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size"); @@ -285,7 +288,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus())); // Settings > General... - auto action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, nullptr, nullptr, QAction::PreferencesRole); + action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, nullptr, nullptr, QAction::PreferencesRole); connect(action, &QAction::triggered, [] { DependencyManager::get()->toggle(QString("hifi/dialogs/GeneralPreferencesDialog.qml"), "GeneralPreferencesDialog"); }); diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp deleted file mode 100644 index d718b52d6d..0000000000 --- a/interface/src/ui/AttachmentsDialog.cpp +++ /dev/null @@ -1,239 +0,0 @@ -// -// AttachmentsDialog.cpp -// interface/src/ui -// -// Created by Andrzej Kapolka on 5/4/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "AttachmentsDialog.h" -#include "ModelsBrowser.h" - -AttachmentsDialog::AttachmentsDialog(QWidget* parent) : - QDialog(parent) { - - setWindowTitle("Edit Attachments"); - setAttribute(Qt::WA_DeleteOnClose); - - QVBoxLayout* layout = new QVBoxLayout(); - setLayout(layout); - - QScrollArea* area = new QScrollArea(); - layout->addWidget(area); - area->setWidgetResizable(true); - QWidget* container = new QWidget(); - container->setLayout(_attachments = new QVBoxLayout()); - container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); - area->setWidget(container); - _attachments->addStretch(1); - - foreach (const AttachmentData& data, DependencyManager::get()->getMyAvatar()->getAttachmentData()) { - addAttachment(data); - } - - QPushButton* newAttachment = new QPushButton("New Attachment"); - connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment())); - layout->addWidget(newAttachment); - - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok); - layout->addWidget(buttons); - connect(buttons, SIGNAL(accepted()), SLOT(deleteLater())); - _ok = buttons->button(QDialogButtonBox::Ok); - - setMinimumSize(600, 600); -} - -void AttachmentsDialog::setVisible(bool visible) { - QDialog::setVisible(visible); - - // un-default the OK button - if (visible) { - _ok->setDefault(false); - } -} - -void AttachmentsDialog::updateAttachmentData() { - QVector data; - for (int i = 0; i < _attachments->count() - 1; i++) { - data.append(static_cast(_attachments->itemAt(i)->widget())->getAttachmentData()); - } - DependencyManager::get()->getMyAvatar()->setAttachmentData(data); -} - -void AttachmentsDialog::addAttachment(const AttachmentData& data) { - _attachments->insertWidget(_attachments->count() - 1, new AttachmentPanel(this, data)); -} - -static QDoubleSpinBox* createTranslationBox(AttachmentPanel* panel, float value) { - QDoubleSpinBox* box = new QDoubleSpinBox(); - box->setSingleStep(0.01); - box->setMinimum(-FLT_MAX); - box->setMaximum(FLT_MAX); - box->setValue(value); - panel->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); - return box; -} - -static QDoubleSpinBox* createRotationBox(AttachmentPanel* panel, float value) { - QDoubleSpinBox* box = new QDoubleSpinBox(); - box->setMinimum(-180.0); - box->setMaximum(180.0); - box->setWrapping(true); - box->setValue(value); - panel->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); - return box; -} - -AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data) : - _dialog(dialog), - _applying(false) { - setFrameStyle(QFrame::StyledPanel); - - QFormLayout* layout = new QFormLayout(); - layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - setLayout(layout); - - QHBoxLayout* urlBox = new QHBoxLayout(); - layout->addRow("Model URL:", urlBox); - urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1); - _modelURL->setText(data.modelURL.toString()); - connect(_modelURL, SIGNAL(editingFinished()), SLOT(modelURLChanged())); - QPushButton* chooseURL = new QPushButton("Choose"); - urlBox->addWidget(chooseURL); - connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL())); - - layout->addRow("Joint:", _jointName = new QComboBox()); - QSharedPointer geometry = DependencyManager::get()->getMyAvatar()->getSkeletonModel().getGeometry(); - if (geometry && geometry->isLoaded()) { - foreach (const FBXJoint& joint, geometry->getFBXGeometry().joints) { - _jointName->addItem(joint.name); - } - } - _jointName->setCurrentText(data.jointName); - connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(jointNameChanged())); - - QHBoxLayout* translationBox = new QHBoxLayout(); - translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x)); - translationBox->addWidget(_translationY = createTranslationBox(this, data.translation.y)); - translationBox->addWidget(_translationZ = createTranslationBox(this, data.translation.z)); - layout->addRow("Translation:", translationBox); - - QHBoxLayout* rotationBox = new QHBoxLayout(); - glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation)); - rotationBox->addWidget(_rotationX = createRotationBox(this, eulers.x)); - rotationBox->addWidget(_rotationY = createRotationBox(this, eulers.y)); - rotationBox->addWidget(_rotationZ = createRotationBox(this, eulers.z)); - layout->addRow("Rotation:", rotationBox); - - layout->addRow("Scale:", _scale = new QDoubleSpinBox()); - _scale->setSingleStep(0.01); - _scale->setMaximum(FLT_MAX); - _scale->setValue(data.scale); - connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData())); - - layout->addRow("Is Soft:", _isSoft = new QCheckBox()); - _isSoft->setChecked(data.isSoft); - connect(_isSoft, SIGNAL(stateChanged(int)), SLOT(updateAttachmentData())); - - QPushButton* remove = new QPushButton("Delete"); - layout->addRow(remove); - connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater())); - dialog->connect(remove, SIGNAL(clicked(bool)), SLOT(updateAttachmentData()), Qt::QueuedConnection); -} - -AttachmentData AttachmentPanel::getAttachmentData() const { - AttachmentData data; - data.modelURL = _modelURL->text(); - data.jointName = _jointName->currentText(); - data.translation = glm::vec3(_translationX->value(), _translationY->value(), _translationZ->value()); - data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value()))); - data.scale = _scale->value(); - data.isSoft = _isSoft->isChecked(); - return data; -} - -void AttachmentPanel::chooseModelURL() { - ModelsBrowser modelBrowser(FSTReader::ATTACHMENT_MODEL, this); - connect(&modelBrowser, SIGNAL(selected(QString)), SLOT(setModelURL(const QString&))); - modelBrowser.browse(); -} - -void AttachmentPanel::setModelURL(const QString& url) { - _modelURL->setText(url); - modelURLChanged(); -} - -void AttachmentPanel::modelURLChanged() { - // check for saved attachment data - if (_modelURL->text().isEmpty()) { - _dialog->updateAttachmentData(); - return; - } - AttachmentData attachment = DependencyManager::get()->getMyAvatar()->loadAttachmentData(_modelURL->text()); - if (attachment.isValid()) { - _applying = true; - _jointName->setCurrentText(attachment.jointName); - applyAttachmentData(attachment); - } - _dialog->updateAttachmentData(); -} - -void AttachmentPanel::jointNameChanged() { - if (_applying) { - return; - } - // check for saved attachment data specific to this joint - if (_modelURL->text().isEmpty()) { - _dialog->updateAttachmentData(); - return; - } - AttachmentData attachment = DependencyManager::get()->getMyAvatar()->loadAttachmentData( - _modelURL->text(), _jointName->currentText()); - if (attachment.isValid()) { - applyAttachmentData(attachment); - } - updateAttachmentData(); -} - -void AttachmentPanel::updateAttachmentData() { - if (_applying) { - return; - } - // save the attachment data under the model URL (if any) - if (!_modelURL->text().isEmpty()) { - DependencyManager::get()->getMyAvatar()->saveAttachmentData(getAttachmentData()); - } - _dialog->updateAttachmentData(); -} - -void AttachmentPanel::applyAttachmentData(const AttachmentData& attachment) { - _applying = true; - _translationX->setValue(attachment.translation.x); - _translationY->setValue(attachment.translation.y); - _translationZ->setValue(attachment.translation.z); - glm::vec3 eulers = glm::degrees(safeEulerAngles(attachment.rotation)); - _rotationX->setValue(eulers.x); - _rotationY->setValue(eulers.y); - _rotationZ->setValue(eulers.z); - _scale->setValue(attachment.scale); - _isSoft->setChecked(attachment.isSoft); - _applying = false; - _dialog->updateAttachmentData(); -} diff --git a/interface/src/ui/AttachmentsDialog.h b/interface/src/ui/AttachmentsDialog.h deleted file mode 100644 index 43ba5f8f3e..0000000000 --- a/interface/src/ui/AttachmentsDialog.h +++ /dev/null @@ -1,84 +0,0 @@ -// -// AttachmentsDialog.h -// interface/src/ui -// -// Created by Andrzej Kapolka on 5/4/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AttachmentsDialog_h -#define hifi_AttachmentsDialog_h - -#include -#include - -#include - -class QComboBox; -class QDoubleSpinner; -class QLineEdit; -class QVBoxLayout; - -/// Allows users to edit the avatar attachments. -class AttachmentsDialog : public QDialog { - Q_OBJECT - -public: - AttachmentsDialog(QWidget* parent = nullptr); - - virtual void setVisible(bool visible); - -public slots: - - void updateAttachmentData(); - -private slots: - - void addAttachment(const AttachmentData& data = AttachmentData()); - -private: - - QVBoxLayout* _attachments; - QPushButton* _ok; -}; - -/// A panel controlling a single attachment. -class AttachmentPanel : public QFrame { - Q_OBJECT - -public: - - AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData()); - - AttachmentData getAttachmentData() const; - -private slots: - - void chooseModelURL(); - void setModelURL(const QString& url); - void modelURLChanged(); - void jointNameChanged(); - void updateAttachmentData(); - -private: - - void applyAttachmentData(const AttachmentData& attachment); - - AttachmentsDialog* _dialog; - QLineEdit* _modelURL; - QComboBox* _jointName; - QDoubleSpinBox* _translationX; - QDoubleSpinBox* _translationY; - QDoubleSpinBox* _translationZ; - QDoubleSpinBox* _rotationX; - QDoubleSpinBox* _rotationY; - QDoubleSpinBox* _rotationZ; - QDoubleSpinBox* _scale; - QCheckBox* _isSoft; - bool _applying; -}; - -#endif // hifi_AttachmentsDialog_h diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index ef0ec5792f..41d7a0eb13 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -19,7 +19,6 @@ #include #include "AddressBarDialog.h" -#include "AttachmentsDialog.h" #include "BandwidthDialog.h" #include "CachesSizeDialog.h" #include "DiskCacheEditor.h" @@ -91,15 +90,6 @@ void DialogsManager::cachesSizeDialog() { _cachesSizeDialog->raise(); } -void DialogsManager::editAttachments() { - if (!_attachmentsDialog) { - maybeCreateDialog(_attachmentsDialog); - _attachmentsDialog->show(); - } else { - _attachmentsDialog->close(); - } -} - void DialogsManager::audioStatsDetails() { if (! _audioStatsDialog) { _audioStatsDialog = new AudioStatsDialog(qApp->getWindow()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 72a24e032c..b8fa22ec83 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -48,7 +48,6 @@ public slots: void showLoginDialog(); void octreeStatsDetails(); void cachesSizeDialog(); - void editAttachments(); void audioStatsDetails(); void bandwidthDetails(); void lodTools(); From a3b2dd2295b3709ae9461ce1eec5cd8eb23f2d7e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 27 Jan 2016 10:14:05 -0800 Subject: [PATCH 55/56] Add proper loop handling in injector --- libraries/audio/src/AudioInjector.cpp | 52 ++++++++++++++------------- libraries/audio/src/AudioInjector.h | 9 ++--- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index b203800703..fe6c1475b5 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -238,16 +238,19 @@ int64_t AudioInjector::injectNextFrame() { } } - int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, - _audioData.size() - _currentSendOffset); + int totalBytesLeftToCopy = (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + if (!_options.loop) { + // If we aren't looping, let's make sure we don't read past the end + totalBytesLeftToCopy = std::min(totalBytesLeftToCopy, _audioData.size() - _currentSendOffset); + } // Measure the loudness of this frame _loudness = 0.0f; - for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { - _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendOffset + i)) / + for (int i = 0; i < totalBytesLeftToCopy; i += sizeof(int16_t)) { + _loudness += abs(*reinterpret_cast(_audioData.data() + ((_currentSendOffset + i) % _audioData.size()))) / (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); } - _loudness /= (float)(bytesToCopy / sizeof(int16_t)); + _loudness /= (float)(totalBytesLeftToCopy/ sizeof(int16_t)); _currentPacket->seek(0); @@ -264,8 +267,16 @@ int64_t AudioInjector::injectNextFrame() { _currentPacket->seek(audioDataOffset); - // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet - _currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); + while (totalBytesLeftToCopy > 0) { + int bytesToCopy = std::min(totalBytesLeftToCopy, _audioData.size() - _currentSendOffset); + + _currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); + _currentSendOffset += bytesToCopy; + totalBytesLeftToCopy -= bytesToCopy; + if (_currentSendOffset >= _audioData.size()) { + _currentSendOffset = 0; + } + } // set the correct size used for this packet _currentPacket->setPayloadSize(_currentPacket->pos()); @@ -280,21 +291,13 @@ int64_t AudioInjector::injectNextFrame() { _outgoingSequenceNumber++; } - _currentSendOffset += bytesToCopy; - - if (_currentSendOffset >= _audioData.size()) { - // we're at the end of the audio data to send - if (_options.loop) { - // we were asked to loop, set our send offset to 0 - _currentSendOffset = 0; - } else { - // we weren't to loop, say that we're done now - finish(); - return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; - } + if (_currentSendOffset >= _audioData.size() && !_options.loop) { + finish(); + return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } - if (_currentSendOffset == bytesToCopy) { + if (!_hasSentFirstFrame) { + _hasSentFirstFrame = true; // ask AudioInjectorManager to call us right away again to // immediately send the first two frames so the mixer can start using the audio right away return NEXT_FRAME_DELTA_IMMEDIATELY; @@ -303,15 +306,14 @@ int64_t AudioInjector::injectNextFrame() { const int MAX_ALLOWED_FRAMES_TO_FALL_BEHIND = 7; int64_t currentTime = _frameTimer->nsecsElapsed() / 1000; auto currentFrameBasedOnElapsedTime = currentTime / AudioConstants::NETWORK_FRAME_USECS; - if (currentFrameBasedOnElapsedTime - _nextFrame > MAX_ALLOWED_FRAMES_TO_FALL_BEHIND) { // If we are falling behind by more frames than our threshold, let's skip the frames ahead - _nextFrame = currentFrameBasedOnElapsedTime - MAX_ALLOWED_FRAMES_TO_FALL_BEHIND; - } else { - ++_nextFrame; + qDebug() << "AudioInjector::injectNextFrame() skipping ahead, fell behind by " << (currentFrameBasedOnElapsedTime - _nextFrame) << " frames"; + _nextFrame = currentFrameBasedOnElapsedTime; + _currentSendOffset = _nextFrame * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * (_options.stereo ? 2 : 1) % _audioData.size(); } - int64_t playNextFrameAt = _nextFrame * AudioConstants::NETWORK_FRAME_USECS; + int64_t playNextFrameAt = ++_nextFrame * AudioConstants::NETWORK_FRAME_USECS; return std::max(INT64_C(0), playNextFrameAt - currentTime); } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 5155b87a74..acfbc2b04a 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -90,10 +90,11 @@ private: QByteArray _audioData; AudioInjectorOptions _options; State _state { State::NotFinished }; - bool _hasSetup = false; - bool _shouldStop = false; - float _loudness = 0.0f; - int _currentSendOffset = 0; + bool _hasSentFirstFrame { false }; + bool _hasSetup { false }; + bool _shouldStop { false }; + float _loudness { 0.0f }; + int _currentSendOffset { 0 }; std::unique_ptr _currentPacket { nullptr }; AbstractAudioInterface* _localAudioInterface { nullptr }; AudioInjectorLocalBuffer* _localBuffer { nullptr }; From 607f23618d93bdd21e5dfbfde39c9c6aac08a6cb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 27 Jan 2016 10:36:44 -0800 Subject: [PATCH 56/56] Fix compiler warning for float->int conversion --- libraries/audio/src/AudioConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioConstants.h b/libraries/audio/src/AudioConstants.h index 91080017a5..6c12c6aa15 100644 --- a/libraries/audio/src/AudioConstants.h +++ b/libraries/audio/src/AudioConstants.h @@ -29,7 +29,7 @@ namespace AudioConstants { const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample); const float NETWORK_FRAME_MSECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL / (float)AudioConstants::SAMPLE_RATE) * 1000.0f; - const int NETWORK_FRAME_USECS = floorf(NETWORK_FRAME_MSECS * 1000.0f); + const int NETWORK_FRAME_USECS = static_cast(NETWORK_FRAME_MSECS * 1000.0f); const int MIN_SAMPLE_VALUE = std::numeric_limits::min(); const int MAX_SAMPLE_VALUE = std::numeric_limits::max(); }