diff --git a/BUILD.md b/BUILD.md index 6649cb1ef9..8c6d290607 100644 --- a/BUILD.md +++ b/BUILD.md @@ -11,7 +11,7 @@ ### Dependencies - [git](https://git-scm.com/downloads): >= 1.6 -- [cmake](https://cmake.org/download/): 3.9 (or greater up to 3.18.x) +- [CMake](https://cmake.org/download/): 3.9 (or greater up to 3.18.x) - [Python](https://www.python.org/downloads/): 3.6 or higher - [Node.JS](https://nodejs.org/en/): >= 12.13.1 LTS - Used to build the Screen Sharing executable. @@ -41,17 +41,19 @@ These are not placed in your normal build tree when doing an out of source build Vircadia uses CMake to generate build files and project files for your platform. #### Qt + CMake will download Qt 5.15.2 using vcpkg. -To override this (i.e., use an installed Qt configuration - you need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt **lib/cmake** folder. +To override this - i.e., use an installed Qt configuration - you need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt **lib/cmake** folder. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). The path it needs to be set to will depend on where and how Qt5 was installed. For example, under Linux: - - export QT_CMAKE_PREFIX_PATH=/usr/local/Qt5.15.2/gcc_64/lib/cmake - export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.15.2/clang_64/lib/cmake/ - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.15.2/lib/cmake - export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake +```bash +export QT_CMAKE_PREFIX_PATH=/usr/local/Qt5.15.2/gcc_64/lib/cmake +export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.15.2/clang_64/lib/cmake/ +export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.15.2/lib/cmake +export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake +``` For example, under Windows: @@ -79,11 +81,14 @@ However, those files can potentially get cleaned up by the OS, so in order to av Linux: - export HIFI_VCPKG_BASE=/path/to/directory +```bash +export HIFI_VCPKG_BASE=/path/to/directory +``` Windows: - - set HIFI_VCPKG_BASE=/path/to/directory +```bash +set HIFI_VCPKG_BASE=/path/to/directory +``` Where `/path/to/directory` is the path to a directory where you wish the build files to get stored. @@ -91,48 +96,58 @@ Where `/path/to/directory` is the path to a directory where you wish the build f ##### Possible Environment Variables - // The URL to post the dump to. - CMAKE_BACKTRACE_URL - // The identifying tag of the release. - CMAKE_BACKTRACE_TOKEN - - // The release version. - RELEASE_NUMBER - // The build commit. - BUILD_NUMBER +```text +// The URL to post the dump to. +CMAKE_BACKTRACE_URL +// The identifying tag of the release. +CMAKE_BACKTRACE_TOKEN + +// The release version, e.g., 2021.3.2. +RELEASE_NUMBER +// The release name, e.g., Eos. +RELEASE_NAME +// The build commit, e.g., use a Git hash for the most recent commit in the branch - fd6973b. + +BUILD_NUMBER + +// The type of release. +RELEASE_TYPE=PRODUCTION|PR|DEV + +// The Interface will have a custom default home and startup location. +PRELOADED_STARTUP_LOCATION=Location/IP/URL +// The Interface will have a custom default script whitelist, comma separated, no spaces. +// This will also activate the whitelist on Interface's first run. +PRELOADED_SCRIPT_WHITELIST=ListOfEntries + +// Code-signing environment variables must be set during runtime of CMake AND globally when the signing takes place. +HF_PFX_FILE=Path to certificate +HF_PFX_PASSPHRASE=Passphrase for certificate + +// Determine the build type +PRODUCTION_BUILD=0|1 +PR_BUILD=0|1 +STABLE_BUILD=0|1 + +// Determine if to utilize testing or stable Metaverse URLs +USE_STABLE_GLOBAL_SERVICES=1 +BUILD_GLOBAL_SERVICES=STABLE +``` - // The type of release. - RELEASE_TYPE=PRODUCTION|PR|DEV - - // The Interface will have a custom default home and startup location. - INITIAL_STARTUP_LOCATION=Location/IP/URL - - // Code-signing environment variables must be set during runtime of CMake AND globally when the signing takes place. - HF_PFX_FILE=Path to certificate - HF_PFX_PASSPHRASE=Passphrase for certificate - - // Determine the build type - PRODUCTION_BUILD=0|1 - PR_BUILD=0|1 - STABLE_BUILD=0|1 - - // Determine if to utilize testing or stable Metaverse URLs - USE_STABLE_GLOBAL_SERVICES=1 - BUILD_GLOBAL_SERVICES=STABLE - ##### Generate Files Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean. - mkdir build - cd build - cmake .. +```bash +mkdir build +cd build +cmake .. +``` If CMake gives you the same error message repeatedly after the build fails, try removing `CMakeCache.txt`. ##### Generating a release/debug only vcpkg build -In order to generate a release or debug only vcpkg package, you could use the use the `VCPKG_BUILD_TYPE` define in your cmake generate command. Building a release only vcpkg can drastically decrease the total build time. +In order to generate a release or debug only vcpkg package, you could use the use the `VCPKG_BUILD_TYPE` define in your CMake generate command. Building a release only vcpkg can drastically decrease the total build time. For release only vcpkg: @@ -148,7 +163,9 @@ Any variables that need to be set for CMake to find dependencies can be set as E For example, to pass the QT_CMAKE_PREFIX_PATH variable (if not using the vcpkg'ed version) during build file generation: - cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/lib/cmake +```bash +cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/lib/cmake +``` #### Finding Dependencies diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index c52f6a1d8f..0556e175d6 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -42,11 +42,13 @@ Follow the directions [here](https://developer.android.com/studio/publish/app-si Create a `gradle.properties` file in the `.gradle` folder (`$HOME/.gradle` on Unix, `Users//.gradle` on Windows). Edit the file to contain the following - HIFI_ANDROID_PRECOMPILED=/Android/hifi_externals - HIFI_ANDROID_KEYSTORE=/.jks - HIFI_ANDROID_KEYSTORE_PASSWORD= - HIFI_ANDROID_KEY_ALIAS= - HIFI_ANDROID_KEY_PASSWORD= +```properties +HIFI_ANDROID_PRECOMPILED=/Android/hifi_externals +HIFI_ANDROID_KEYSTORE=/.jks +HIFI_ANDROID_KEYSTORE_PASSWORD= +HIFI_ANDROID_KEY_ALIAS= +HIFI_ANDROID_KEY_PASSWORD= +``` Note, do not use $HOME for the path. It must be a fully qualified path name. Also, be sure to use forward slashes in your path. @@ -54,21 +56,26 @@ Note, do not use $HOME for the path. It must be a fully qualified path name. Als Add these lines to `gradle.properties` - SUPPRESS_QUEST_INTERFACE - SUPPRESS_QUEST_FRAME_PLAYER +```properties +SUPPRESS_QUEST_INTERFACE +SUPPRESS_QUEST_FRAME_PLAYER +``` #### If you are building for an Oculus Quest Add these lines to `gradle.properties` - SUPPRESS_INTERFACE - SUPPRESS_FRAME_PLAYER - +```properties +SUPPRESS_INTERFACE +SUPPRESS_FRAME_PLAYER +``` #### The Frame Player for both Android Phone and Oculus Quest is optional, so if you encounter problems with these during your build, you can skip them by adding these lines to `gradle.properties` - SUPPRESS_FRAME_PLAYER - SUPPRESS_QUEST_FRAME_PLAYER +```properties +SUPPRESS_FRAME_PLAYER +SUPPRESS_QUEST_FRAME_PLAYER +``` ### Clone the repository diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index 90782e90b4..e9c5047e38 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -168,39 +168,3 @@ If your goal is to set up a development environment, it is desirable to set the directory that vcpkg builds into with the `HIFI_VCPKG_BASE` environment variable. For example, you might set `HIFI_VCPKG_BASE` to `/home/$USER/vcpkg`. By default, vcpkg will build in the system `/tmp` directory. - -##### Ubuntu 18.04 only - -In Ubuntu 18.04 there is a problem related with NVidia driver library version. - -It can be worked around following these steps: - -1. Uninstall incompatible nvtt libraries: -`sudo apt-get remove libnvtt2 libnvtt-dev` - -1. Install libssl1.0-dev: -`sudo apt-get -y install libssl1.0-dev` - -1. Clone castano nvidia-texture-tools: -`git clone https://github.com/castano/nvidia-texture-tools` -`cd nvidia-texture-tools/` - -1. Make these changes in repo: -* In file **VERSION** set `2.2.1` -* In file **configure**: - * set `build="release"` - * set `-DNVTT_SHARED=1` - -1. Configure, build and install: -`./configure` -`make` -`sudo make install` - -1. Link compiled files: -`sudo ln -s /usr/local/lib/libnvcore.so /usr/lib/libnvcore.so` -`sudo ln -s /usr/local/lib/libnvimage.so /usr/lib/libnvimage.so` -`sudo ln -s /usr/local/lib/libnvmath.so /usr/lib/libnvmath.so` -`sudo ln -s /usr/local/lib/libnvtt.so /usr/lib/libnvtt.so` - -1. After running these steps you can run interface: -`interface/interface` diff --git a/BUILD_OSX.md b/BUILD_OSX.md index a287d610df..5f34a301a1 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -8,24 +8,28 @@ Please read the [general build guide](BUILD.md) for information on dependencies [Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes install of some Vircadia dependencies very simple. - brew install cmake openssl npm +```bash +brew install cmake openssl npm +``` Note: cmake versions > 3.18.x have known problems building Vircadia, so alternatively you can download cmake 3.18.4 (or earlier versions) from [Github](https://github.com/Kitware/CMake/releases). ### Python 3 -Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/). +Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/). Execute the `Update Shell Profile.command` script that is provided with the installer. ### OSX SDK You will need version `10.12` of the OSX SDK for building, otherwise you may have crashing or other unintended issues due to the deprecation of OpenGL on OSX. You can get that SDK from [here](https://github.com/phracker/MacOSX-SDKs). You must copy it in to your Xcode SDK directory, e.g. - cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ +```bash +cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ +``` ### OpenSSL -Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations. +Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations. For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via `export OPENSSL_ROOT_DIR=/usr/local/opt/openssl` or by appending `-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl` to `cmake` @@ -33,12 +37,14 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via ### Xcode You can ask CMake to generate Xcode project files instead of Unix Makefiles using the `-G Xcode` parameter after CMake. You will need to select the Xcode installation in the terminal first if you have not done so already. - - sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer - - cmake ../ -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 .. -If `cmake` complains about Python 3 being missing, you may need to update your CMake binary with command `brew upgrade cmake`, or by downloading and running the latest CMake installer, depending on how you originally instaled CMake +```bash +sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer + +cmake ../ -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 .. +``` + +If `cmake` complains about Python 3 being missing, you may need to update your CMake binary with command `brew upgrade cmake`, or by downloading and running the latest CMake installer, depending on how you originally installed CMake. After running CMake, you will have the make files or Xcode project file necessary to build all of the components. Open the hifi.xcodeproj file, choose ALL_BUILD from the Product > Scheme menu (or target drop down), and click Run. diff --git a/INSTALLER.md b/INSTALLER.md index 84ee14eaaa..4132ef7298 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -1,6 +1,6 @@ # Creating an Installer -*Last Updated on August 24, 2020* +*Last Updated on March 4, 2021* Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform. @@ -9,13 +9,13 @@ During generation, CMake should produce an `install` target and a `package` targ The `install` target will copy the Vircadia targets and their dependencies to your `CMAKE_INSTALL_PREFIX`. This variable is set by the `project(hifi)` command in `CMakeLists.txt` to `C:/Program Files/hifi` and stored in `build/CMakeCache.txt` -### Packaging +## Packaging To produce an installer, run the `package` target. However you will want to follow the steps specific to your platform below. -#### Windows +### Windows -##### Prerequisites +#### Prerequisites To produce an executable installer on Windows, the following are required: @@ -63,11 +63,11 @@ To produce an executable installer on Windows, the following are required: 1. [Node.JS and NPM]() 1. Install version 10.15.0 LTS (or greater) -##### Code Signing (optional) +#### Code Signing (optional) For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PASSPHRASE` environment variables to be present during CMake runtime and globally as we proceed to package the installer. -##### Creating the Installer +#### Creating the Installer 1. Perform a clean cmake from a new terminal. 1. Open the `vircadia.sln` solution with elevated (administrator) permissions on Visual Studio and select the **Release** configuration. @@ -79,7 +79,14 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS 1. Build CMakeTargets->PACKAGE The installer is now available in `build\_CPack_Packages\win64\NSIS` -#### OS X +#### FAQ + +1. **Problem:** Failure to open a file. ```File: failed opening file "\FOLDERSHARE\XYZSRelease\...\Credits.rtf" Error in script "C:\TFS\XYZProject\Releases\NullsoftInstaller\XYZWin7Installer.nsi" on line 77 -- aborting creation process``` + 1. **Cause:** The complete path (current directory + relative path) has to be < 260 characters to any of the relevant files. + 1. **Solution:** Move your build and packaging folder as high up in the drive as possible to prevent an overage. + +### OS X + 1. [npm]() Install version 12.16.3 LTS @@ -91,8 +98,156 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS 1. Perform a Release build of `package` Installer is now available in `build/_CPack_Packages/Darwin/DragNDrop -### FAQ +### Linux -1. **Problem:** Failure to open a file. ```File: failed opening file "\FOLDERSHARE\XYZSRelease\...\Credits.rtf" Error in script "C:\TFS\XYZProject\Releases\NullsoftInstaller\XYZWin7Installer.nsi" on line 77 -- aborting creation process``` - 1. **Cause:** The complete path (current directory + relative path) has to be < 260 characters to any of the relevant files. - 1. **Solution:** Move your build and packaging folder as high up in the drive as possible to prevent an overage. +#### Server + +##### Ubuntu 18.04 | .deb + +1. Ensure you are using an Ubuntu 18.04 system. There is no required minimum to the amount of CPU cores needed, however it's recommended that you use as many as you have available in order to have an efficient experience. + ```text + Recommended CPU Cores: 16 + Minimum Disk Space: 40GB + ``` +3. Get and bootstrap Vircadia Builder. + ```bash + git clone https://github.com/vircadia/vircadia-builder.git + cd vircadia-builder + ``` +3. Run Vircadia Builder. + ```bash + ./vircadia-builder --build server + ``` +4. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. Otherwise, skip to the next step. + ```bash + ./vircadia-builder --build server + ``` +5. Vircadia Builder will ask you to configure it to build the server. The values will be prefilled with defaults, the following steps will explain what they are and what you might want to put. *Advanced users: See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings.* +6. This value is the Git repository of Vircadia. You can set this URL to your fork of the Vircadia repository if you need to. + ```text + Git repository: https://github.com/vircadia/vircadia/ + # OR, for example + Git repository: https://github.com/digisomni/vircadia/ + ``` +7. This value is the tag on the repository. If you would like to use a specific version of Vircadia, typically tags will be named like this: "v2021.1.0-rc" + ```text + Git tag: master + # OR, for example + Git tag: v2021.1.0-rc + ``` +8. This value is the release type. For example, the options are `production`, `pr`, or `dev`. If you are making a build for yourself and others to use then use `production`. + ```text + Release type: DEV + # OR, for example we recommend you use + Release type: PRODUCTION + ``` +9. This value is the release version. Release numbers should be in a format of `YEAR-MAJORVERSION-MINORVERSION` which might look like this: `2021.1.0`. + ```text + Release number: 2021.1.0 + ``` +10. This value is the build number. We typically use the hash of the most recent commit on that Git tag which might look like this: `fd6973b`. + ```text + Build number: fd6973b + ``` +11. This value is the directory that Vircadia will get installed to. You should leave this as the default value unless you are an advanced user. + ```text + Installation dir: /home/ubuntu/Vircadia + ``` +12. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. + ```text + CPU cores to use for Vircadia: 16 + ``` +13. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server. You should leave this as the default value it gives you for your build server. + ```text + CPU cores to use for Qt5: 16 + ``` +14. It will ask you if you would like to proceed with the specified values. If you're happy with the configuration, type `yes`, otherwise enter `no` and press enter to start over. You can press `Ctrl` + `C` simultaneously on your keyboard to exit. +15. Vircadia Builder will now run, it may take a while. See this [table](https://github.com/vircadia/vircadia-builder#how-long-does-it-take) for estimated times. +16. Navigate to the `pkg-scripts` directory. + ```bash + cd ../Vircadia/source/pkg-scripts/ + ``` +17. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.* + ```bash + DEBVERSION="2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server + ``` +18. If successful, the generated .deb package will be in the `pkg-scripts` folder. + +##### Amazon Linux 2 | .rpm + +1. Ensure you are using an Amazon Linux 2 system. You will need many CPU cores to complete this process within a reasonable time. As an alternative to AWS EC2, you may use a [virtual machine](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-2-virtual-machine.html). Here are the recommended specs: + ```text + AWS EC2 Instance Type: C5a.4xlarge + Recommended CPU Cores: 16 + Minimum Disk Space: 40GB + ``` +2. Update the system and install dependencies. + ```bash + sudo yum update -y + sudo yum install git -y + sudo yum install rpm-build + ``` +3. Get and bootstrap Vircadia Builder. + ```bash + git clone https://github.com/vircadia/vircadia-builder.git + cd vircadia-builder + sudo ./install_amazon_linux_deps.sh + ``` +4. Run Vircadia Builder. + ```bash + ./vircadia-builder --build server + ``` +5. If Vircadia Builder needed to install dependencies and asks you to run it again then do so. Otherwise, skip to the next step. + ```bash + ./vircadia-builder --build server + ``` +6. Vircadia Builder will ask you to configure it to build the server. The values will be prefilled with defaults, the following steps will explain what they are and what you might want to put. *Advanced users: See [here](BUILD.md#possible-environment-variables) for possible environment variables and settings.* +7. This value is the Git repository of Vircadia. You can set this URL to your fork of the Vircadia repository if you need to. + ```text + Git repository: https://github.com/vircadia/vircadia/ + # OR, for example + Git repository: https://github.com/digisomni/vircadia/ + ``` +8. This value is the tag on the repository. If you would like to use a specific version of Vircadia, typically tags will be named like this: "v2021.1.0-rc". + ```text + Git tag: master + # OR, for example + Git tag: v2021.1.0-rc + ``` +9. This value is the release type. For example, the options are `production`, `pr`, or `dev`. If you are making a build for yourself and others to use then use `production`. + ```text + Release type: DEV + # OR, for example we recommend you use + Release type: PRODUCTION + ``` +10. This value is the release version. Release numbers typically should be in a format of `YEAR-MAJORVERSION-MINORVERSION` which might look like this: `2021.1.0`. + ```text + Release number: 2021.1.0 + ``` +11. This value is the build number. We typically use the hash of the most recent commit on that Git tag which might look like this: `fd6973b`. + ```text + Build number: fd6973b + ``` +12. This value is the directory that Vircadia will get installed to. You should leave this as the default value unless you are an advanced user. + ```text + Installation dir: /root/Vircadia + ``` +13. This value is the number of CPU cores that the Vircadia Builder will use to compile the Vircadia server. By default it will use all cores available on your build server given you have enough memory. You should leave this as the default value it gives you for your build server. + ```text + CPU cores to use for Vircadia: 16 + ``` +14. This value is the number of CPU cores that the Vircadia Builder will use to compile Qt5 (a required component for Vircadia). By default it will use all cores available on your build server given you have enough memory. You should leave this as the default value it gives you for your build server. + ```text + CPU cores to use for Qt5: 16 + ``` +15. It will ask you if you would like to proceed with the specified values. If you're happy with the configuration, type `yes`, otherwise enter `no` and press enter to start over. You can press `Ctrl` + `C` simultaneously on your keyboard to exit. +16. Vircadia Builder will now run, it may take a while. See this [table](https://github.com/vircadia/vircadia-builder#how-long-does-it-take) for estimated times. +17. Navigate to the `pkg-scripts` directory. + ```bash + cd ../Vircadia/source/pkg-scripts/ + ``` +18. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.* + ```bash + RPMVERSION="2021.1.0" ./make-rpm-server + ``` +19. If successful, the generated .rpm package will be in the `pkg-scripts` folder of the Vircadia source files. diff --git a/README.md b/README.md index d260c76d0d..830236f458 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,11 @@ Vircadia™ is a 3D social software project seeking to incrementally bring about ### How to generate an Installer -- [For Windows](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md) -- [For Mac](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md#os-x) -- [For Linux - AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages) +- [For Windows - Interface & Server](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md) +- [For Mac - Interface](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md#os-x) +- [For Linux - Server .deb - Vircadia Builder](INSTALLER.md#ubuntu-1804--deb) +- [For Linux - Server .rpm - Vircadia Builder](INSTALLER.md#amazon-linux-2--rpm) +- [For Linux - Interface AppImage - Vircadia Builder](https://github.com/vircadia/vircadia-builder/blob/master/README.md#building-appimages) ### Boot to Metaverse: [The Goal](https://vircadia.com/vision/) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index bcf4881fcf..017d3a80b7 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -20,6 +20,7 @@ #include const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer"; +const int MESSAGES_MIXER_RATE_LIMITER_INTERVAL = 1000; // 1 second MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(message) { @@ -44,10 +45,20 @@ void MessagesMixer::handleMessages(QSharedPointer receivedMessa QByteArray data; QUuid senderID; bool isText; + auto senderUUID = senderNode->getUUID(); MessagesClient::decodeMessagesPacket(receivedMessage, channel, isText, message, data, senderID); auto nodeList = DependencyManager::get(); + auto itr = _allSubscribers.find(senderUUID); + if (itr == _allSubscribers.end()) { + _allSubscribers[senderUUID] = 1; + } else if (*itr >= _maxMessagesPerSecond) { + return; + } else { + *itr += 1; + } + nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { return node->getActiveSocket() && _channelSubscribers[channel].contains(node->getUUID()); @@ -60,14 +71,18 @@ void MessagesMixer::handleMessages(QSharedPointer receivedMessa } void MessagesMixer::handleMessagesSubscribe(QSharedPointer message, SharedNodePointer senderNode) { + auto senderUUID = senderNode->getUUID(); QString channel = QString::fromUtf8(message->getMessage()); - _channelSubscribers[channel] << senderNode->getUUID(); + + _channelSubscribers[channel] << senderUUID; } void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer message, SharedNodePointer senderNode) { + auto senderUUID = senderNode->getUUID(); QString channel = QString::fromUtf8(message->getMessage()); + if (_channelSubscribers.contains(channel)) { - _channelSubscribers[channel].remove(senderNode->getUUID()); + _channelSubscribers[channel].remove(senderUUID); } } @@ -88,7 +103,48 @@ void MessagesMixer::sendStatsPacket() { } void MessagesMixer::run() { - ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); auto nodeList = DependencyManager::get(); nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer }); + DomainHandler& domainHandler = nodeList->getDomainHandler(); + connect(&domainHandler, &DomainHandler::settingsReceived, this, &MessagesMixer::domainSettingsRequestComplete); + + ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); + + startMaxMessagesProcessor(); +} + +void MessagesMixer::domainSettingsRequestComplete() { + auto nodeList = DependencyManager::get(); + + // parse the settings to pull out the values we need + parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject()); +} + +void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { + const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; + QJsonObject messagesMixerGroupObject = domainSettings[MESSAGES_MIXER_SETTINGS_KEY].toObject(); + + const QString NODE_MESSAGES_PER_SECOND_KEY = "max_node_messages_per_second"; + QJsonValue maxMessagesPerSecondValue = messagesMixerGroupObject.value(NODE_MESSAGES_PER_SECOND_KEY); + _maxMessagesPerSecond = maxMessagesPerSecondValue.toInt(DEFAULT_NODE_MESSAGES_PER_SECOND); +} + +void MessagesMixer::processMaxMessagesContainer() { + _allSubscribers.clear(); +} + +void MessagesMixer::startMaxMessagesProcessor() { + if (_maxMessagesTimer) { + stopMaxMessagesProcessor(); + } + + _maxMessagesTimer = new QTimer(); + connect(_maxMessagesTimer, &QTimer::timeout, this, &MessagesMixer::processMaxMessagesContainer); + _maxMessagesTimer->start(MESSAGES_MIXER_RATE_LIMITER_INTERVAL); // Clear the container every second. +} + +void MessagesMixer::stopMaxMessagesProcessor() { + _maxMessagesTimer->stop(); + _maxMessagesTimer->deleteLater(); + _maxMessagesTimer = nullptr; } diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 800d42199b..9a43aca347 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -32,9 +32,21 @@ private slots: void handleMessages(QSharedPointer message, SharedNodePointer senderNode); void handleMessagesSubscribe(QSharedPointer message, SharedNodePointer senderNode); void handleMessagesUnsubscribe(QSharedPointer message, SharedNodePointer senderNode); + void parseDomainServerSettings(const QJsonObject& domainSettings); + void domainSettingsRequestComplete(); + + void startMaxMessagesProcessor(); + void stopMaxMessagesProcessor(); + void processMaxMessagesContainer(); private: - QHash> _channelSubscribers; + QHash> _channelSubscribers; + QHash _allSubscribers; + + const int DEFAULT_NODE_MESSAGES_PER_SECOND = 1000; + int _maxMessagesPerSecond { 0 }; + + QTimer* _maxMessagesTimer { nullptr }; }; #endif // hifi_MessagesMixer_h diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 640cc1720f..e09a7dc322 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -4,6 +4,7 @@ # # Created by Leonardo Murillo on 12/16/2015. # Copyright 2015 High Fidelity, Inc. +# Copyright 2021 Vircadia contributors. # # Distributed under the Apache License, Version 2.0. # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -20,9 +21,9 @@ macro(GENERATE_INSTALLERS) set(INSTALLER_TYPE "client_only") string(REGEX REPLACE "Vircadia" "Vircadia Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION}) elseif (SERVER_ONLY) - set(_PACKAGE_NAME_EXTRA "-Sandbox") + set(_PACKAGE_NAME_EXTRA "-Server") set(INSTALLER_TYPE "server_only") - string(REGEX REPLACE "Vircadia" "Vircadia Sandbox" _DISPLAY_NAME ${BUILD_ORGANIZATION}) + string(REGEX REPLACE "Vircadia" "Vircadia Server" _DISPLAY_NAME ${BUILD_ORGANIZATION}) else () set(_DISPLAY_NAME ${BUILD_ORGANIZATION}) set(INSTALLER_TYPE "full") @@ -31,7 +32,7 @@ macro(GENERATE_INSTALLERS) set(CPACK_PACKAGE_NAME ${_DISPLAY_NAME}) set(CPACK_PACKAGE_VENDOR "Vircadia") set(CPACK_PACKAGE_VERSION ${BUILD_VERSION}) - set(CPACK_PACKAGE_FILE_NAME "Vircadia${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}") + set(CPACK_PACKAGE_FILE_NAME "Vircadia${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}-${RELEASE_NAME}") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) if (PR_BUILD) @@ -122,7 +123,7 @@ macro(GENERATE_INSTALLERS) endif () if (BUILD_SERVER) - cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Sandbox") + cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Server") endif () include(CPack) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 9311594938..d7f1aa2786 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -25,7 +25,10 @@ macro(SET_PACKAGING_PARAMETERS) set_from_env(RELEASE_NUMBER RELEASE_NUMBER "") set_from_env(RELEASE_NAME RELEASE_NAME "") set_from_env(STABLE_BUILD STABLE_BUILD 0) - set_from_env(INITIAL_STARTUP_LOCATION INITIAL_STARTUP_LOCATION "") + + set_from_env(PRELOADED_STARTUP_LOCATION PRELOADED_STARTUP_LOCATION "") + set_from_env(PRELOADED_SCRIPT_WHITELIST PRELOADED_SCRIPT_WHITELIST "") + set_from_env(BYPASS_SIGNING BYPASS_SIGNING 0) message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}") @@ -177,12 +180,12 @@ macro(SET_PACKAGING_PARAMETERS) if (PRODUCTION_BUILD) set(INTERFACE_SHORTCUT_NAME "Vircadia") set(CONSOLE_SHORTCUT_NAME "Console") - set(SANDBOX_SHORTCUT_NAME "Sandbox") + set(SANDBOX_SHORTCUT_NAME "Server") set(APP_USER_MODEL_ID "com.vircadia.console") else () set(INTERFACE_SHORTCUT_NAME "Vircadia - ${BUILD_VERSION_NO_SHA}") set(CONSOLE_SHORTCUT_NAME "Console - ${BUILD_VERSION_NO_SHA}") - set(SANDBOX_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION_NO_SHA}") + set(SANDBOX_SHORTCUT_NAME "Server - ${BUILD_VERSION_NO_SHA}") endif () set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}") diff --git a/cmake/templates/BuildInfo.h.in b/cmake/templates/BuildInfo.h.in index 7f3a63d4b4..99726d270f 100644 --- a/cmake/templates/BuildInfo.h.in +++ b/cmake/templates/BuildInfo.h.in @@ -29,7 +29,8 @@ namespace BuildInfo { const QString BUILD_NUMBER = "@BUILD_NUMBER@"; const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@"; const QString BUILD_TIME = "@BUILD_TIME@"; - const QString INITIAL_STARTUP_LOCATION = "@INITIAL_STARTUP_LOCATION@"; + const QString PRELOADED_STARTUP_LOCATION = "@PRELOADED_STARTUP_LOCATION@"; + const QString PRELOADED_SCRIPT_WHITELIST = "@PRELOADED_SCRIPT_WHITELIST@"; enum BuildType { Dev, diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 4c44ed1fbd..33587f3e5a 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -251,8 +251,8 @@ Var substringResult !macro InitSection SecName ; This macro reads component installed flag from the registry and - ;changes checked state of the section on the components page. - ;Input: section index constant name specified in Section command. + ; changes checked state of the section on the components page. + ; Input: section index constant name specified in Section command. ClearErrors ;Reading component status from registry @@ -718,7 +718,7 @@ Function InstallTypesPage StrCpy $OffsetUnits u StrCpy $Express "0" - ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Vircadia Interface and Vircadia Sandbox" + ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls Vircadia Interface" pop $ExpressInstallRadioButton ${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel IntOp $CurrentOffset $CurrentOffset + 15 @@ -1346,6 +1346,7 @@ Section "-Core installation" ${EndIf} ${If} @SERVER_COMPONENT_CONDITIONAL@ + ${AndIf} $Express != "1" ; handling for server console shortcut Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_HF_SHORTCUT_NAME@.lnk" \ diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index bb7acf344c..32868c9b80 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1488,6 +1488,24 @@ } ] }, + { + "name": "messages_mixer", + "label": "Messages Mixer", + "assignment-types": [ + 4 + ], + "settings": [ + { + "name": "max_node_messages_per_second", + "type": "int", + "label": "Maximum Message Rate", + "help": "Maximum message send rate (messages per second) per node", + "placeholder": 1000, + "default": 1000, + "advanced": true + } + ] + }, { "name": "entity_server_settings", "label": "Entities", diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 31f3ee44df..0f7d521653 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -15,6 +15,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.0 import stylesUit 1.0 import controlsUit 1.0 as HifiControlsUit @@ -104,6 +105,11 @@ Rectangle { AudioScriptingInterface.setSystemInjectorGain(sliderValue); } } + function updateNoiseReductionThresholdFromQML(sliderValue) { + if (AudioScriptingInterface.getNoiseReductionThreshold() !== sliderValue) { + AudioScriptingInterface.setNoiseReductionThreshold(sliderValue); + } + } Component.onCompleted: { enablePeakValues(); @@ -164,7 +170,7 @@ Rectangle { x: 2 * margins.paddings; width: parent.width; // switch heights + 2 * top margins - height: (root.switchHeight) * 6 + 48; + height: (bar.currentIndex === 0) ? (root.switchHeight) * 2 + 48 : (root.switchHeight) * 4 + 48; anchors.top: firstSeparator.bottom; anchors.topMargin: 10; @@ -175,6 +181,7 @@ Rectangle { height: parent.height; anchors.top: parent.top anchors.left: parent.left; + HifiControlsUit.Switch { id: muteMic; height: root.switchHeight; @@ -193,45 +200,11 @@ Rectangle { } } - HifiControlsUit.Switch { - id: noiseReductionSwitch; - height: root.switchHeight; - switchWidth: root.switchWidth; - anchors.top: muteMic.bottom; - anchors.topMargin: 24 - anchors.left: parent.left - labelTextOn: "Noise Reduction"; - labelTextSize: 16; - backgroundOnColor: "#E3E3E3"; - checked: AudioScriptingInterface.noiseReduction; - onCheckedChanged: { - AudioScriptingInterface.noiseReduction = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding - } - } - - HifiControlsUit.Switch { - id: acousticEchoCancellationSwitch; - height: root.switchHeight; - switchWidth: root.switchWidth; - anchors.top: noiseReductionSwitch.bottom - anchors.topMargin: 24 - anchors.left: parent.left - labelTextOn: "Echo Cancellation"; - labelTextSize: 16; - backgroundOnColor: "#E3E3E3"; - checked: AudioScriptingInterface.acousticEchoCancellation; - onCheckedChanged: { - AudioScriptingInterface.acousticEchoCancellation = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.acousticEchoCancellation; }); - } - } - HifiControlsUit.Switch { id: pttSwitch height: root.switchHeight; switchWidth: root.switchWidth; - anchors.top: acousticEchoCancellationSwitch.bottom; + anchors.top: muteMic.bottom; anchors.topMargin: 24 anchors.left: parent.left labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk"); @@ -254,6 +227,7 @@ Rectangle { height: parent.height; anchors.top: parent.top anchors.left: switchContainer.right; + HifiControlsUit.Switch { id: warnMutedSwitch height: root.switchHeight; @@ -271,7 +245,6 @@ Rectangle { } } - HifiControlsUit.Switch { id: audioLevelSwitch height: root.switchHeight; @@ -519,13 +492,282 @@ Rectangle { anchors.top: systemInjectorGainContainer.bottom; anchors.topMargin: 10; } + + Item { + id: noiseReductionHeader + x: margins.paddings; + width: parent.width - margins.paddings * 2; + height: 36; + anchors.top: secondSeparator.bottom; + anchors.topMargin: 10; + + HiFiGlyphs { + width: margins.sizeCheckBox; + text: hifi.glyphs.mic; + color: hifi.colors.white; + anchors.left: parent.left; + anchors.leftMargin: -size / 4; // The glyph has empty space at left about 25% + anchors.verticalCenter: parent.verticalCenter; + size: 30; + } + + RalewayRegular { + anchors.verticalCenter: parent.verticalCenter; + width: margins.sizeText + margins.sizeLevel; + anchors.left: parent.left; + anchors.leftMargin: margins.sizeCheckBox; + size: 22; + color: hifi.colors.white; + text: qsTr("Noise Reduction"); + } + } + + Item { + id: noiseReductionSwitches; + x: 2 * margins.paddings; + width: parent.width; + // switch heights + 2 * top margins + height: (root.switchHeight) * 5 + 48; + anchors.top: noiseReductionHeader.bottom; + anchors.topMargin: 0; + + Item { + id: noiseReductionSwitchContainer; + x: margins.paddings; + width: parent.width / 2; + height: parent.height; + anchors.top: parent.top; + anchors.left: parent.left; + + HifiControlsUit.Switch { + id: acousticEchoCancellationSwitch; + height: root.switchHeight; + switchWidth: root.switchWidth; + anchors.top: noiseReductionSwitchContainer.top + anchors.topMargin: 24 + anchors.left: parent.left + labelTextOn: "Echo Cancellation"; + labelTextSize: 16; + backgroundOnColor: "#E3E3E3"; + checked: AudioScriptingInterface.acousticEchoCancellation; + onCheckedChanged: { + AudioScriptingInterface.acousticEchoCancellation = checked; + checked = Qt.binding(function () { return AudioScriptingInterface.acousticEchoCancellation; }); + } + } + + HifiControlsUit.Switch { + id: noiseReductionSwitch; + height: root.switchHeight; + switchWidth: root.switchWidth; + anchors.top: acousticEchoCancellationSwitch.bottom; + anchors.topMargin: 24 + anchors.left: parent.left + labelTextOn: "Noise Reduction"; + labelTextSize: 16; + backgroundOnColor: "#E3E3E3"; + checked: AudioScriptingInterface.noiseReduction; + onCheckedChanged: { + AudioScriptingInterface.noiseReduction = checked; + checked = Qt.binding(function () { return AudioScriptingInterface.noiseReduction; }); // restore binding + } + } + + HifiControlsUit.Switch { + id: noiseReductionAutomaticSwitch; + height: root.switchHeight; + switchWidth: root.switchWidth; + anchors.top: noiseReductionSwitch.bottom; + anchors.topMargin: 24; + anchors.left: parent.left; + labelTextOn: "Manual Noise Reduction"; + labelTextSize: 16; + backgroundOnColor: "#E3E3E3"; + checked: !AudioScriptingInterface.noiseReductionAutomatic; + visible: AudioScriptingInterface.noiseReduction; + onCheckedChanged: { + AudioScriptingInterface.noiseReductionAutomatic = !checked; + checked = Qt.binding(function () { return !AudioScriptingInterface.noiseReductionAutomatic; }); // restore binding + } + } + } + } + + Item { + id: noiseReductionThresholdContainer + x: margins.paddings; + anchors.top: noiseReductionSwitches.bottom; + anchors.topMargin: 16; + width: parent.width - margins.paddings * 2; + height: avatarGainSliderTextMetrics.height + 10; + visible: AudioScriptingInterface.noiseReduction && !AudioScriptingInterface.noiseReductionAutomatic; + + HifiControlsUit.Slider { + id: noiseReductionThresholdSlider + anchors.right: parent.right + height: noiseReductionThresholdSliderTextMetrics.height + width: 200 + minimumValue: 0.0 + maximumValue: 1.0 + stepSize: 0.05 + value: AudioScriptingInterface.getNoiseReductionThreshold() + onValueChanged: { + updateNoiseReductionThresholdFromQML(value); + } + onPressedChanged: { + if (!pressed) { + updateNoiseReductionThresholdFromQML(value); + } + } + + MouseArea { + anchors.fill: parent + onWheel: { + // Do nothing. + } + onDoubleClicked: { + noiseReductionThresholdSlider.value = 0.0; + } + onPressed: { + // Pass through to Slider + mouse.accepted = false; + } + onReleased: { + // the above mouse.accepted seems to make this + // never get called, nonetheless... + mouse.accepted = false; + } + } + } + TextMetrics { + id: noiseReductionThresholdSliderTextMetrics + text: noiseReductionThresholdSliderText.text + font: noiseReductionThresholdSliderText.font + } + RalewayRegular { + // The slider for my card is special, it controls the master gain + id: noiseReductionThresholdSliderText; + text: "Audio input threshold"; + size: 16; + anchors.left: parent.left; + color: hifi.colors.white; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignTop; + } + + Item { + id: noisePeak + anchors.right: parent.right + anchors.rightMargin: 5; + anchors.top: noiseReductionThresholdSlider.bottom; + + width: noiseReductionThresholdSlider.width - 10; + height: 8; + + Text { + id: status; + + anchors { + horizontalCenter: parent.horizontalCenter; + verticalCenter: parent.verticalCenter; + } + + visible: AudioScriptingInterface.muted; + color: "#E2334D"; + + text: "MUTED"; + font.pointSize: 10; + } + + Item { + id: noiseBar; + + property bool gated: false; + property var level: AudioScriptingInterface.inputLevel; + + width: parent.width; + height: parent.height; + + anchors { fill: parent } + + visible: !status.visible; + + function onNoiseGateOpened() { + noiseBar.gated = false; + } + + function onNoiseGateClosed() { + noiseBar.gated = true; + } + + function onInputLevelChanged(level) { + noiseBar.level = level; + } + + Component.onCompleted: { + AudioScriptingInterface.noiseGateOpened.connect(onNoiseGateOpened); + AudioScriptingInterface.noiseGateClosed.connect(onNoiseGateClosed); + AudioScriptingInterface.inputLevelChanged.connect(onInputLevelChanged); + } + + Component.onDestruction: { + AudioScriptingInterface.noiseGateOpened.disconnect(onNoiseGateOpened); + AudioScriptingInterface.noiseGateClosed.disconnect(onNoiseGateClosed); + AudioScriptingInterface.inputLevelChanged.disconnect(onInputLevelChanged); + } + + Rectangle { // base + radius: 4; + anchors { fill: parent } + color: colors.gutter; + } + + Rectangle { // noiseMask + id: noiseMask; + width: parent.width * noiseBar.level; + radius: 5; + anchors { + bottom: parent.bottom; + bottomMargin: 0; + top: parent.top; + topMargin: 0; + left: parent.left; + leftMargin: 0; + } + } + + LinearGradient { + anchors { fill: noiseMask } + source: noiseMask + start: Qt.point(0, 0); + end: Qt.point(noiseBar.width, 0); + gradient: Gradient { + GradientStop { + position: 0; + color: noiseBar.gated ? "#E2334D" : "#39A38F"; + } + GradientStop { + position: 1; + color: noiseBar.gated ? "#E2334D" : "#39A38F"; + } + } + } + } + } + } + + Separator { + id: thirdSeparator; + anchors.top: noiseReductionThresholdContainer.bottom; + anchors.topMargin: 14; + } Item { id: inputDeviceHeader x: margins.paddings; width: parent.width - margins.paddings*2; height: 36; - anchors.top: secondSeparator.bottom; + anchors.top: thirdSeparator.bottom; anchors.topMargin: 10; HiFiGlyphs { @@ -597,19 +839,19 @@ Rectangle { } } AudioControls.InputPeak { - id: inputLevel - anchors.right: parent.right - peak: model.peak; - anchors.verticalCenter: parent.verticalCenter - visible: ((bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR)) && - AudioScriptingInterface.devices.input.peakValuesAvailable; + id: inputLevel + anchors.right: parent.right + peak: model.peak; + anchors.verticalCenter: parent.verticalCenter + visible: ((bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR)) && + AudioScriptingInterface.devices.input.peakValuesAvailable; } } } Separator { - id: thirdSeparator; + id: fourthSeparator; anchors.top: inputView.bottom; anchors.topMargin: 10; } @@ -617,7 +859,7 @@ Rectangle { Item { id: outputDeviceHeader; anchors.topMargin: 10; - anchors.top: thirdSeparator.bottom; + anchors.top: fourthSeparator.bottom; x: margins.paddings; width: parent.width - margins.paddings*2 height: 36 @@ -684,4 +926,4 @@ Rectangle { } } } -} \ No newline at end of file +} diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index 62ceee1b63..ccc28174a1 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -112,8 +112,7 @@ MessageBox { popup.button1text = 'CANCEL' popup.titleText = 'Get Avatars' - popup.bodyText = 'Get avatars from Marketplace.' + '
' + - 'Wear avatars in Inventory.' + popup.bodyText = 'Get avatars from the Community Bazaar. (Coming soon!)' popup.onLinkClicked = function(link) { popup.close(); diff --git a/interface/resources/serverless/tutorial.json b/interface/resources/serverless/tutorial.json index e333d8246e..17e7ea26e7 100644 --- a/interface/resources/serverless/tutorial.json +++ b/interface/resources/serverless/tutorial.json @@ -4,6 +4,56 @@ "/": "/0.155245,-0.941538,23.9289/0,0.791589,0,0.611053" }, "Entities": [ + { + "id": "{0a199807-4a83-4286-b09c-f21124627c3e}", + "type": "Box", + "name": "Config Wizard Loader", + "lastEdited": 1613737207915514, + "visible": false, + "position": { + "x": -1.2722, + "y": 0.4266, + "z": 24.2307 + }, + "dimensions": { + "x": 0.20000000298023224, + "y": 0.20000000298023224, + "z": 0.20000000298023224 + }, + "rotation": { + "x": 0, + "y": -0.7660443782806396, + "z": 0, + "w": -0.6427876949310303 + }, + "created": 1613736996738696, + "lastEditedBy": "{ff9b500e-e450-4127-b41f-1c42be16f71b}", + "queryAACube": { + "x": -0.17320507764816284, + "y": -0.17320507764816284, + "z": -0.17320507764816284, + "scale": 0.3464101552963257 + }, + "grab": { + "grabbable": false + }, + "damping": 0, + "angularDamping": 0, + "collisionless": true, + "ignoreForCollisions": true, + "script": "https://cdn-1.vircadia.com/us-e-1/DomainContent/Tutorial/Apps/configWizard/dist/wizardLoader.js", + "color": { + "red": 0, + "green": 180, + "blue": 239 + }, + "shape": "Cube", + "clientOnly": false, + "avatarEntity": false, + "localEntity": false, + "faceCamera": false, + "isFacingAvatar": false + }, { "id": "{eb485a2d-2040-42f6-a960-51c88e2434b9}", "type": "Box", diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 06ce6c3d6c..d8338162b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2989,6 +2989,8 @@ Application::~Application() { qInstallMessageHandler(LogHandler::verboseMessageHandler); #ifdef Q_OS_MAC + // 26 Feb 2021 - Tried re-enabling this call but OSX still crashes on exit. + // // 10/16/2019 - Disabling this call. This causes known crashes (A), and it is not // fully understood whether it might cause other unknown crashes (B). // @@ -4013,7 +4015,7 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { // If this is a first run we short-circuit the address passed in if (_firstRun.get()) { - if (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) { + if (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty()) { DependencyManager::get()->setHomeLocationToAddress(NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS); Menu::getInstance()->triggerOption(MenuOption::HomeLocation); } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 43958188e3..4a96e36939 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -25,6 +25,8 @@ QString Audio::DESKTOP { "Desktop" }; QString Audio::HMD { "VR" }; Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; +Setting::Handle enableNoiseReductionAutomaticSetting { QStringList { Audio::AUDIO, "NoiseReductionAutomatic" }, true }; +Setting::Handle setNoiseReductionThresholdSetting { QStringList { Audio::AUDIO, "NoiseReductionThreshold" }, 0.1f }; Setting::Handle enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true }; Setting::Handle enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true }; @@ -40,6 +42,8 @@ Audio::Audio() : _devices(_contextIsHMD) { auto client = DependencyManager::get().data(); connect(client, &AudioClient::muteToggled, this, &Audio::setMuted); connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction); + connect(client, &AudioClient::noiseReductionAutomaticChanged, this, &Audio::enableNoiseReductionAutomatic); + connect(client, &AudioClient::noiseReductionThresholdChanged, this, &Audio::setNoiseReductionThreshold); connect(client, &AudioClient::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted); connect(client, &AudioClient::acousticEchoCancellationChanged, this, &Audio::enableAcousticEchoCancellation); connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged); @@ -47,6 +51,8 @@ Audio::Audio() : _devices(_contextIsHMD) { connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk); enableNoiseReduction(enableNoiseReductionSetting.get()); + enableNoiseReductionAutomatic(enableNoiseReductionAutomaticSetting.get()); + setNoiseReductionThreshold(setNoiseReductionThresholdSetting.get()); enableWarnWhenMuted(enableWarnWhenMutedSetting.get()); enableAcousticEchoCancellation(enableAcousticEchoCancellationSetting.get()); onContextChanged(); @@ -286,6 +292,50 @@ void Audio::enableNoiseReduction(bool enable) { } } +bool Audio::noiseReductionAutomatic() const { + return resultWithReadLock([&] { + return _noiseReductionAutomatic; + }); +} + +void Audio::enableNoiseReductionAutomatic(bool enable) { + bool changed = false; + withWriteLock([&] { + if (_noiseReductionAutomatic != enable) { + _noiseReductionAutomatic = enable; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setNoiseReductionAutomatic", Q_ARG(bool, enable), Q_ARG(bool, false)); + enableNoiseReductionAutomaticSetting.set(enable); + changed = true; + } + }); + if (changed) { + emit noiseReductionAutomaticChanged(enable); + } +} + +float Audio::getNoiseReductionThreshold() { + return resultWithReadLock([&] { + return _noiseReductionThreshold; + }); +} + +void Audio::setNoiseReductionThreshold(float threshold) { + bool changed = false; + withWriteLock([&] { + if (_noiseReductionThreshold != threshold) { + _noiseReductionThreshold = threshold; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setNoiseReductionThreshold", Q_ARG(float, threshold), Q_ARG(bool, false)); + setNoiseReductionThresholdSetting.set(threshold); + changed = true; + } + }); + if (changed) { + emit noiseReductionThresholdChanged(threshold); + } +} + bool Audio::warnWhenMutedEnabled() const { return resultWithReadLock([&] { return _enableWarnWhenMuted; diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 74d85f0107..707c8aac33 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -50,6 +50,11 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { * @property {boolean} noiseReduction - true if noise reduction is enabled, otherwise false. When * enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just * above the noise floor. + * @property {boolean} noiseReductionAutomatic - true if audio input noise reduction automatic mode is enabled, + * false if in manual mode. Manual mode allows you to use Audio.noiseReductionThreshold + * to set a manual sensitivity for the noise gate. + * @property {number} noiseReductionThreshold - Sets the noise gate threshold before your mic audio is transmitted. + * (Applies only if Audio.noiseReductionAutomatic is false.) * @property {number} inputVolume - Adjusts the volume of the input audio, range 0.01.0. * If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some * devices, and others might only support values of 0.0 and 1.0. @@ -92,6 +97,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged) + Q_PROPERTY(bool noiseReductionAutomatic READ noiseReductionAutomatic WRITE enableNoiseReductionAutomatic NOTIFY noiseReductionAutomaticChanged) + Q_PROPERTY(float noiseReductionThreshold READ getNoiseReductionThreshold WRITE setNoiseReductionThreshold NOTIFY noiseReductionThresholdChanged) Q_PROPERTY(bool warnWhenMuted READ warnWhenMutedEnabled WRITE enableWarnWhenMuted NOTIFY warnWhenMutedChanged) Q_PROPERTY(bool acousticEchoCancellation READ acousticEchoCancellationEnabled WRITE enableAcousticEchoCancellation NOTIFY acousticEchoCancellationChanged) @@ -124,6 +131,7 @@ public: bool isMuted() const; bool noiseReductionEnabled() const; + bool noiseReductionAutomatic() const; bool warnWhenMutedEnabled() const; bool acousticEchoCancellationEnabled() const; float getInputVolume() const; @@ -270,6 +278,20 @@ public: * @returns {number} The injector gain (dB) in the client. */ Q_INVOKABLE float getSystemInjectorGain(); + + /**jsdoc + * Sets the noise gate threshold before your mic audio is transmitted. (Applies only if Audio.noiseReductionAutomatic is false.) + * @function Audio.setNoiseReductionThreshold + * @param {number} threshold - The level that your input must surpass to be transmitted. 0.0 (open the gate completely) – 1.0 + */ + Q_INVOKABLE void setNoiseReductionThreshold(float threshold); + + /**jsdoc + * Gets the noise reduction threshold. + * @function Audio.getNoiseReductionThreshold + * @returns {number} The noise reduction threshold. 0.01.0 + */ + Q_INVOKABLE float getNoiseReductionThreshold(); /**jsdoc * Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format. @@ -398,6 +420,23 @@ signals: * @returns {Signal} */ void noiseReductionChanged(bool isEnabled); + + /**jsdoc + * Triggered when the audio input noise reduction mode is changed. + * @function Audio.noiseReductionAutomaticChanged + * @param {boolean} isEnabled - true if audio input noise reduction automatic mode is enabled, false if in manual mode. + * @returns {Signal} + */ + void noiseReductionAutomaticChanged(bool isEnabled); + + /**jsdoc + * Triggered when the audio input noise reduction threshold is changed. + * @function Audio.noiseReductionThresholdChanged + * @param {number} threshold - The threshold for the audio input noise reduction, range 0.0 (open the gate completely) – 1.0 + * (close the gate completely). + * @returns {Signal} + */ + void noiseReductionThresholdChanged(float threshold); /**jsdoc * Triggered when "warn when muted" is enabled or disabled. @@ -512,6 +551,7 @@ public slots: private slots: void setMuted(bool muted); void enableNoiseReduction(bool enable); + void enableNoiseReductionAutomatic(bool enable); void enableWarnWhenMuted(bool enable); void enableAcousticEchoCancellation(bool enable); void setInputVolume(float volume); @@ -533,8 +573,10 @@ private: float _localInjectorGain { 0.0f }; // in dB float _systemInjectorGain { 0.0f }; // in dB float _pttOutputGainDesktop { 0.0f }; // in dB + float _noiseReductionThreshold { 0.1f }; // Match default value of AudioClient::_noiseReductionThreshold. bool _isClipping { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. + bool _noiseReductionAutomatic { true }; // Match default value of AudioClient::_noiseReductionAutomatic. bool _enableWarnWhenMuted { true }; bool _enableAcousticEchoCancellation { true }; // AudioClient::_isAECEnabled bool _contextIsHMD { false }; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 9c53060f31..4de353ad0e 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -250,9 +250,9 @@ void setupPreferences() { { auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); }; auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); }; - preferences->addPreference(new CheckPreference("Privacy", "Send data - High Fidelity uses information provided by your " + preferences->addPreference(new CheckPreference("Privacy", "Send data - Vircadia uses information provided by your " "client to improve the product through the logging of errors, tracking of usage patterns, " - "installation and system details. By allowing High Fidelity to collect this information " + "installation and system details. By allowing Vircadia to collect this information " "you are helping to improve the product. ", getter, setter)); } diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 2fd52ee036..095e8333ee 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -22,6 +22,7 @@ #include "AnimationLogging.h" #include "CubicHermiteSpline.h" #include "AnimUtil.h" +#include "AnimSkeleton.h" static const int MAX_TARGET_MARKERS = 30; static const float JOINT_CHAIN_INTERP_TIME = 0.5f; @@ -66,7 +67,7 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, cons poleVectorVar(poleVectorVarIn), weight(weightIn), numFlexCoefficients(flexCoefficientsIn.size()), - jointIndex(-1) + jointIndex(AnimSkeleton::INVALID_JOINT_INDEX) { numFlexCoefficients = std::min(numFlexCoefficients, (size_t)MAX_FLEX_COEFFICIENTS); for (size_t i = 0; i < numFlexCoefficients; i++) { @@ -172,14 +173,14 @@ bool debounceJointWarnings() { void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses) { - _hipsTargetIndex = -1; + _hipsTargetIndex = AnimSkeleton::INVALID_JOINT_INDEX; targets.reserve(_targetVarVec.size()); for (auto& targetVar : _targetVarVec) { // update targetVar jointIndex cache - if (targetVar.jointIndex == -1) { + if (targetVar.jointIndex == AnimSkeleton::INVALID_JOINT_INDEX) { int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName); if (jointIndex >= 0) { // this targetVar has a valid joint --> cache the indices @@ -190,7 +191,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } IKTarget target; - if (targetVar.jointIndex != -1) { + if (targetVar.jointIndex != AnimSkeleton::INVALID_JOINT_INDEX) { target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); target.setIndex(targetVar.jointIndex); if (target.getType() != IKTarget::Type::Unknown) { @@ -329,7 +330,7 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< // update the absolutePoses for (int i = 0; i < (int)_relativePoses.size(); ++i) { auto parentIndex = _skeleton->getParentIndex((int)i); - if (parentIndex != -1) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; } } @@ -351,12 +352,12 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector< // finally set the relative rotation of each tip to agree with absolute target rotation for (auto& target: targets) { int tipIndex = target.getIndex(); - int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : -1; + int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : AnimSkeleton::INVALID_JOINT_INDEX; int chainIndex = targetToChainMap[tipIndex]; bool needsInterpolation = _prevJointChainInfoVec[chainIndex].timer > 0.0f; float alpha = needsInterpolation ? getInterpolationAlpha(_prevJointChainInfoVec[chainIndex].timer) : 0.0f; // update rotationOnly targets that don't lie on the ik chain of other ik targets. - if (parentIndex != -1 && !_rotationAccumulators[tipIndex].isDirty() && + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX && !_rotationAccumulators[tipIndex].isDirty() && (target.getType() == IKTarget::Type::RotationOnly || target.getType() == IKTarget::Type::Unknown)) { if (target.getType() == IKTarget::Type::RotationOnly) { const glm::quat& targetRotation = target.getRotation(); @@ -434,11 +435,11 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const int tipIndex = target.getIndex(); int pivotIndex = _skeleton->getParentIndex(tipIndex); - if (pivotIndex == -1 || pivotIndex == _hipsIndex) { + if (pivotIndex == AnimSkeleton::INVALID_JOINT_INDEX || pivotIndex == _hipsIndex) { return; } int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); - if (pivotsParentIndex == -1) { + if (pivotsParentIndex == AnimSkeleton::INVALID_JOINT_INDEX) { // TODO?: handle case where tip's parent is root? return; } @@ -485,7 +486,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const chainDepth++; // descend toward root, pivoting each joint to get tip closer to target position - while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) { + while (pivotIndex != _hipsIndex && pivotsParentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { assert(chainDepth < jointChainInfoOut.jointInfoVec.size()); @@ -579,12 +580,12 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const if (target.getPoleVectorEnabled()) { int topJointIndex = target.getIndex(); int midJointIndex = _skeleton->getParentIndex(topJointIndex); - if (midJointIndex != -1) { + if (midJointIndex != AnimSkeleton::INVALID_JOINT_INDEX) { int baseJointIndex = _skeleton->getParentIndex(midJointIndex); - if (baseJointIndex != -1) { + if (baseJointIndex != AnimSkeleton::INVALID_JOINT_INDEX) { int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex); AnimPose topPose, midPose, basePose; - int topChainIndex = -1, baseChainIndex = -1; + int topChainIndex = AnimSkeleton::INVALID_JOINT_INDEX, baseChainIndex = AnimSkeleton::INVALID_JOINT_INDEX; const size_t MAX_CHAIN_DEPTH = 30; AnimPose postAbsPoses[MAX_CHAIN_DEPTH]; AnimPose accum = absolutePoses[_hipsIndex]; @@ -756,7 +757,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani int index = target.getIndex(); int endIndex = _skeleton->getParentIndex(_hipsIndex); - while (index != endIndex) { + while (index != endIndex && index != AnimSkeleton::INVALID_JOINT_INDEX) { AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index); float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength; @@ -1460,7 +1461,7 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele // invalidate all targetVars for (auto& targetVar: _targetVarVec) { - targetVar.jointIndex = -1; + targetVar.jointIndex = AnimSkeleton::INVALID_JOINT_INDEX; } for (auto& accumulator: _rotationAccumulators) { @@ -1480,18 +1481,18 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele if (_hipsIndex >= 0) { _hipsParentIndex = _skeleton->getParentIndex(_hipsIndex); } else { - _hipsParentIndex = -1; + _hipsParentIndex = AnimSkeleton::INVALID_JOINT_INDEX; } _leftHandIndex = _skeleton->nameToJointIndex("LeftHand"); _rightHandIndex = _skeleton->nameToJointIndex("RightHand"); } else { clearConstraints(); - _headIndex = -1; - _hipsIndex = -1; - _hipsParentIndex = -1; - _leftHandIndex = -1; - _rightHandIndex = -1; + _headIndex = AnimSkeleton::INVALID_JOINT_INDEX; + _hipsIndex = AnimSkeleton::INVALID_JOINT_INDEX; + _hipsParentIndex = AnimSkeleton::INVALID_JOINT_INDEX; + _leftHandIndex = AnimSkeleton::INVALID_JOINT_INDEX; + _rightHandIndex = AnimSkeleton::INVALID_JOINT_INDEX; } } @@ -1531,7 +1532,7 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c // draw line to parent int parentIndex = _skeleton->getParentIndex(i); - if (parentIndex != -1) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); DebugDraw::getInstance().drawRay(pos, parentPos, GRAY); } @@ -1580,7 +1581,7 @@ void AnimInverseKinematics::debugDrawIKChain(const JointChainInfo& jointChainInf DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE); // draw line to parent - if (parentIndex != -1) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); glm::vec4 color = GRAY; @@ -1629,13 +1630,13 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con // draw line to parent int parentIndex = _skeleton->getParentIndex(i); - if (parentIndex != -1) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans()); DebugDraw::getInstance().drawRay(pos, parentPos, GRAY); } glm::quat parentAbsRot; - if (parentIndex != -1) { + if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) { parentAbsRot = poses[parentIndex].rot(); } @@ -1733,7 +1734,7 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC const float MIN_AXIS_LENGTH = 1.0e-4f; for (auto& target : targets) { - if (target.getIndex() != -1 && target.getType() == IKTarget::Type::RotationAndPosition) { + if (target.getIndex() != AnimSkeleton::INVALID_JOINT_INDEX && target.getType() == IKTarget::Type::RotationAndPosition) { for (int i = 0; i < NUM_LIMBS; i++) { if (limbs[i].first == target.getIndex()) { int tipIndex = limbs[i].first; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index b26d00d8d0..bd338aae4a 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -66,7 +66,7 @@ int AnimSkeleton::nameToJointIndex(const QString& jointName) const { if (_jointIndicesByName.end() != itr) { return itr.value(); } - return -1; + return INVALID_JOINT_INDEX; } int AnimSkeleton::getNumJoints() const { @@ -80,7 +80,7 @@ int AnimSkeleton::getChainDepth(int jointIndex) const { do { chainDepth++; index = _parentIndices[index]; - } while (index != -1); + } while (index != INVALID_JOINT_INDEX); return chainDepth; } else { return 0; @@ -108,7 +108,7 @@ const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const { std::vector AnimSkeleton::getChildrenOfJoint(int jointIndex) const { // Children and grandchildren, etc. std::vector result; - if (jointIndex != -1) { + if (jointIndex != INVALID_JOINT_INDEX) { for (int i = jointIndex + 1; i < (int)_parentIndices.size(); i++) { if (_parentIndices[i] == jointIndex || (std::find(result.begin(), result.end(), _parentIndices[i]) != result.end())) { result.push_back(i); @@ -135,7 +135,7 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const { int lastIndex = std::min((int)poses.size(), _jointsSize); for (int i = 0; i < lastIndex; ++i) { int parentIndex = _parentIndices[i]; - if (parentIndex != -1) { + if (parentIndex != INVALID_JOINT_INDEX) { poses[i] = poses[parentIndex] * poses[i]; } } @@ -146,7 +146,7 @@ void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const { int lastIndex = std::min((int)poses.size(), _jointsSize); for (int i = lastIndex - 1; i >= 0; --i) { int parentIndex = _parentIndices[i]; - if (parentIndex != -1) { + if (parentIndex != INVALID_JOINT_INDEX) { poses[i] = poses[parentIndex].inverse() * poses[i]; } } @@ -157,7 +157,7 @@ void AnimSkeleton::convertRelativeRotationsToAbsolute(std::vector& ro int lastIndex = std::min((int)rotations.size(), _jointsSize); for (int i = 0; i < lastIndex; ++i) { int parentIndex = _parentIndices[i]; - if (parentIndex != -1) { + if (parentIndex != INVALID_JOINT_INDEX) { rotations[i] = rotations[parentIndex] * rotations[i]; } } @@ -168,7 +168,7 @@ void AnimSkeleton::convertAbsoluteRotationsToRelative(std::vector& ro int lastIndex = std::min((int)rotations.size(), _jointsSize); for (int i = lastIndex - 1; i >= 0; --i) { int parentIndex = _parentIndices[i]; - if (parentIndex != -1) { + if (parentIndex != INVALID_JOINT_INDEX) { rotations[i] = glm::inverse(rotations[parentIndex]) * rotations[i]; } } @@ -280,7 +280,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // so we can restore them after a future mirror operation _nonMirroredIndices.push_back(i); } - int mirrorJointIndex = -1; + int mirrorJointIndex = INVALID_JOINT_INDEX; if (_joints[i].name.startsWith("Left")) { QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right"); mirrorJointIndex = nameToJointIndex(mirrorJointName); @@ -350,7 +350,7 @@ std::vector AnimSkeleton::lookUpJointIndices(const std::vector& jo result.reserve(jointNames.size()); for (auto& name : jointNames) { int index = nameToJointIndex(name); - if (index == -1) { + if (index == INVALID_JOINT_INDEX) { qWarning(animation) << "AnimSkeleton::lookUpJointIndices(): could not find bone with name " << name; } result.push_back(index); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index efc1c1599f..e36ceb4042 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -30,6 +30,8 @@ public: const QString& getJointName(int jointIndex) const; int getNumJoints() const; int getChainDepth(int jointIndex) const; + + static const int INVALID_JOINT_INDEX { -1 }; // the default poses are the orientations of the joints on frame 0. const AnimPose& getRelativeDefaultPose(int jointIndex) const; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 2600097c3b..38635870fd 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1266,7 +1266,7 @@ void AudioClient::processWebrtcNearEnd(int16_t* samples, int numFrames, int numC void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { // If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here. bool hasReverb = _reverb || _receivedAudioStream.hasReverb(); - if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) { + if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb) || !_audioGateOpen) { return; } @@ -1342,6 +1342,13 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } } +float AudioClient::loudnessToLevel(float loudness) { + float level = loudness * (1 / 32768.0f); // level in [0, 1] + level = 6.02059991f * fastLog2f(level); // convert to dBFS + level = (level + 48.0f) * (1 / 42.0f); // map [-48, -6] dBFS to [0, 1] + return glm::clamp(level, 0.0f, 1.0f); +} + void AudioClient::handleAudioInput(QByteArray& audioBuffer) { if (!_audioPaused) { @@ -1352,9 +1359,14 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE; int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); - if (_isNoiseGateEnabled) { + if (_isNoiseGateEnabled && _isNoiseReductionAutomatic) { // The audio gate includes DC removal audioGateOpen = _audioGate->render(samples, samples, numFrames); + } else if (_isNoiseGateEnabled && !_isNoiseReductionAutomatic && + loudnessToLevel(_lastSmoothedRawInputLoudness) >= _noiseReductionThreshold) { + audioGateOpen = _audioGate->removeDC(samples, samples, numFrames); + } else if (_isNoiseGateEnabled && !_isNoiseReductionAutomatic) { + audioGateOpen = false; } else { audioGateOpen = _audioGate->removeDC(samples, samples, numFrames); } @@ -1750,6 +1762,24 @@ void AudioClient::setNoiseReduction(bool enable, bool emitSignal) { } } +void AudioClient::setNoiseReductionAutomatic(bool enable, bool emitSignal) { + if (_isNoiseReductionAutomatic != enable) { + _isNoiseReductionAutomatic = enable; + if (emitSignal) { + emit noiseReductionAutomaticChanged(_isNoiseReductionAutomatic); + } + } +} + +void AudioClient::setNoiseReductionThreshold(float threshold, bool emitSignal) { + if (_noiseReductionThreshold != threshold) { + _noiseReductionThreshold = threshold; + if (emitSignal) { + emit noiseReductionThresholdChanged(_noiseReductionThreshold); + } + } +} + void AudioClient::setWarnWhenMuted(bool enable, bool emitSignal) { if (_warnWhenMuted != enable) { _warnWhenMuted = enable; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 09abb2c356..a5de9bd4ca 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -217,6 +217,12 @@ public slots: void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true); bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; } + + void setNoiseReductionAutomatic(bool isNoiseGateAutomatic, bool emitSignal = true); + bool isNoiseReductionAutomatic() const { return _isNoiseReductionAutomatic; } + + void setNoiseReductionThreshold(float noiseReductionThreshold, bool emitSignal = true); + float noiseReductionThreshold() const { return _noiseReductionThreshold; } void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true); bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; } @@ -265,6 +271,8 @@ signals: void inputVolumeChanged(float volume); void muteToggled(bool muted); void noiseReductionChanged(bool noiseReductionEnabled); + void noiseReductionAutomaticChanged(bool noiseReductionAutomatic); + void noiseReductionThresholdChanged(bool noiseReductionThreshold); void warnWhenMutedChanged(bool warnWhenMutedEnabled); void acousticEchoCancellationChanged(bool acousticEchoCancellationEnabled); void mutedByMixer(); @@ -310,6 +318,8 @@ private: friend class CheckDevicesThread; friend class LocalInjectorsThread; + float loudnessToLevel(float loudness); + // background tasks void checkDevices(); void checkPeakValues(); @@ -397,6 +407,8 @@ private: bool _shouldEchoLocally{ false }; bool _shouldEchoToServer{ false }; bool _isNoiseGateEnabled{ true }; + bool _isNoiseReductionAutomatic{ true }; + float _noiseReductionThreshold{ 0.1f }; bool _warnWhenMuted; bool _isAECEnabled{ true }; diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 95d0524a4a..dc8e811f0c 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -118,7 +118,7 @@ public: const QString& getName() const { return _name; } // By default, Input Devices do not support haptics - virtual bool triggerHapticPulse(float strength, float duration, controller::Hand hand) { return false; } + virtual bool triggerHapticPulse(float strength, float duration, uint16_t index) { return false; } // Update call MUST be called once per simulation loop // It takes care of updating the action states and deltas diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index fd32b2eb43..e9a831859d 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -134,13 +134,13 @@ namespace controller { return DependencyManager::get()->getActionNames(); } - bool ScriptingInterface::triggerHapticPulse(float strength, float duration, controller::Hand hand) const { - return DependencyManager::get()->triggerHapticPulse(strength, duration, hand); + bool ScriptingInterface::triggerHapticPulse(float strength, float duration, uint16_t index) const { + return DependencyManager::get()->triggerHapticPulse(strength, duration, index); } - bool ScriptingInterface::triggerShortHapticPulse(float strength, controller::Hand hand) const { + bool ScriptingInterface::triggerShortHapticPulse(float strength, uint16_t index) const { const float SHORT_HAPTIC_DURATION_MS = 250.0f; - return DependencyManager::get()->triggerHapticPulse(strength, SHORT_HAPTIC_DURATION_MS, hand); + return DependencyManager::get()->triggerHapticPulse(strength, SHORT_HAPTIC_DURATION_MS, index); } void ScriptingInterface::startInputRecording() { @@ -189,13 +189,13 @@ namespace controller { emit inputDeviceRunningChanged(deviceName, isRunning); } - bool ScriptingInterface::triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, controller::Hand hand) const { - return DependencyManager::get()->triggerHapticPulseOnDevice(device, strength, duration, hand); + bool ScriptingInterface::triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, uint16_t index) const { + return DependencyManager::get()->triggerHapticPulseOnDevice(device, strength, duration, index); } - bool ScriptingInterface::triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand) const { + bool ScriptingInterface::triggerShortHapticPulseOnDevice(unsigned int device, float strength, uint16_t index) const { const float SHORT_HAPTIC_DURATION_MS = 250.0f; - return DependencyManager::get()->triggerHapticPulseOnDevice(device, strength, SHORT_HAPTIC_DURATION_MS, hand); + return DependencyManager::get()->triggerHapticPulseOnDevice(device, strength, SHORT_HAPTIC_DURATION_MS, index); } void ScriptingInterface::updateMaps() { diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index a1875c7fe8..076146a79d 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -212,22 +212,28 @@ namespace controller { * @function Controller.triggerHapticPulse * @param {number} strength - The strength of the haptic pulse, range 0.01.0. * @param {number} duration - The duration of the haptic pulse, in milliseconds. - * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on. + * @param {number} [index=2] - The index on devices on which to trigger the haptic pulse. The meaning of each index + * will vary by device. For example, for hand controllers, index = 0 is the left hand, + * index = 1 is the right hand, and index = 2 is both hands. For other devices, + * such as haptic vests, index will have a different meaning, defined by the input device. * @example Trigger a haptic pulse on the right hand. * var HAPTIC_STRENGTH = 0.5; * var HAPTIC_DURATION = 10; * var RIGHT_HAND = 1; * Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND); */ - Q_INVOKABLE bool triggerHapticPulse(float strength, float duration, controller::Hand hand = BOTH) const; + Q_INVOKABLE bool triggerHapticPulse(float strength, float duration, uint16_t index = 2) const; /**jsdoc * Triggers a 250ms haptic pulse on connected and enabled devices that have the capability. * @function Controller.triggerShortHapticPulse * @param {number} strength - The strength of the haptic pulse, range 0.01.0. - * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on. + * @param {number} [index=2] - The index on devices on which to trigger the haptic pulse. The meaning of each index + * will vary by device. For example, for hand controllers, index = 0 is the left hand, + * index = 1 is the right hand, and index = 2 is both hands. For other devices, + * such as haptic vests, index will have a different meaning, defined by the input device. */ - Q_INVOKABLE bool triggerShortHapticPulse(float strength, controller::Hand hand = BOTH) const; + Q_INVOKABLE bool triggerShortHapticPulse(float strength, uint16_t index = 2) const; /**jsdoc * Triggers a haptic pulse on a particular device if connected and enabled and it has the capability. @@ -235,7 +241,10 @@ namespace controller { * @param {number} deviceID - The ID of the device to trigger the haptic pulse on. * @param {number} strength - The strength of the haptic pulse, range 0.01.0. * @param {number} duration - The duration of the haptic pulse, in milliseconds. - * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on. + * @param {number} [index=2] - The index on this device on which to trigger the haptic pulse. The meaning of each index + * will vary by device. For example, for hand controllers, index = 0 is the left hand, + * index = 1 is the right hand, and index = 2 is both hands. For other devices, + * such as haptic vests, index will have a different meaning, defined by the input device. * @example Trigger a haptic pulse on an Oculus Touch controller. * var HAPTIC_STRENGTH = 0.5; * var deviceID = Controller.findDevice("OculusTouch"); @@ -243,19 +252,20 @@ namespace controller { * var RIGHT_HAND = 1; * Controller.triggerHapticPulseOnDevice(deviceID, HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND); */ - Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, - controller::Hand hand = BOTH) const; + Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, + uint16_t index = 2) const; /**jsdoc * Triggers a 250ms haptic pulse on a particular device if connected and enabled and it has the capability. * @function Controller.triggerShortHapticPulseOnDevice * @param {number} deviceID - The ID of the device to trigger the haptic pulse on. * @param {number} strength - The strength of the haptic pulse, range 0.01.0. - * @param {Controller.Hand} [hand=2] - The hand or hands to trigger the haptic pulse on. + * @param {number} [index=2] - The index on this device on which to trigger the haptic pulse. The meaning of each index + * will vary by device. For example, for hand controllers, index = 0 is the left hand, + * index = 1 is the right hand, and index = 2 is both hands. For other devices, + * such as haptic vests, index will have a different meaning, defined by the input device. */ - Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand = BOTH) - const; - + Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, uint16_t index = 2) const; /**jsdoc * Creates a new controller mapping. Routes can then be added to the mapping using {@link MappingObject} methods and diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 604a4f9c73..6333792b3f 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -365,19 +365,19 @@ Pose UserInputMapper::getPoseState(Action action) const { } -bool UserInputMapper::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool UserInputMapper::triggerHapticPulse(float strength, float duration, uint16_t index) { Locker locker(_lock); bool toReturn = false; for (const auto& device : _registeredDevices) { - toReturn = device.second->triggerHapticPulse(strength, duration, hand) || toReturn; + toReturn = device.second->triggerHapticPulse(strength, duration, index) || toReturn; } return toReturn; } -bool UserInputMapper::triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, controller::Hand hand) { +bool UserInputMapper::triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, uint16_t index) { Locker locker(_lock); if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { - return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, hand); + return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, index); } return false; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 79fcf6e64c..ee8b34193f 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -91,8 +91,8 @@ namespace controller { void setActionState(Action action, float value, bool valid = true); void deltaActionState(Action action, float delta, bool valid = true); void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; } - bool triggerHapticPulse(float strength, float duration, controller::Hand hand); - bool triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, controller::Hand hand); + bool triggerHapticPulse(float strength, float duration, uint16_t index); + bool triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, uint16_t index); static Input makeStandardInput(controller::StandardButtonChannel button); static Input makeStandardInput(controller::StandardAxisChannel axis); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 6e761698c9..73fa44ba09 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1772,13 +1772,11 @@ bool EntityItem::contains(const glm::vec3& point) const { ShapeType shapeType = getShapeType(); if (shapeType == SHAPE_TYPE_SPHERE) { - // SPHERE case is special: - // anything with shapeType == SPHERE must collide as a bounding sphere in the world-frame regardless of dimensions - // therefore we must do math using an unscaled localPoint relative to sphere center glm::vec3 dimensions = getScaledDimensions(); - glm::vec3 localPoint = point - (getWorldPosition() + getWorldOrientation() * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()) + getPivot())); - const float HALF_SQUARED = 0.25f; - return glm::length2(localPoint) < HALF_SQUARED * glm::length2(dimensions); + if (dimensions.x == dimensions.y && dimensions.y == dimensions.z) { + glm::vec3 localPoint = point - (getWorldPosition() + getWorldOrientation() * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()) + getPivot())); + return glm::length2(localPoint) < glm::length2(0.5f * dimensions.x); + } } // we transform into the "normalized entity-frame" where the bounding box is centered on the origin @@ -1805,6 +1803,7 @@ bool EntityItem::contains(const glm::vec3& point) const { localPoint = glm::abs(localPoint); return glm::all(glm::lessThanEqual(localPoint, glm::vec3(NORMALIZED_HALF_SIDE))); } + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { // since we've transformed into the normalized space this is just a sphere-point intersection test return glm::length2(localPoint) <= NORMALIZED_RADIUS_SQUARED; diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 3eabfb4f1e..abccdf7167 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -270,7 +270,6 @@ void ZoneEntityItem::debugDump() const { } void ZoneEntityItem::setShapeType(ShapeType type) { - ShapeType oldShapeType = _shapeType; switch(type) { case SHAPE_TYPE_NONE: case SHAPE_TYPE_CAPSULE_X: @@ -288,7 +287,12 @@ void ZoneEntityItem::setShapeType(ShapeType type) { default: break; } - _shapeType = type; + + ShapeType oldShapeType; + withWriteLock([&] { + oldShapeType = _shapeType; + _shapeType = type; + }); if (type == SHAPE_TYPE_COMPOUND) { if (type != oldShapeType) { @@ -300,16 +304,21 @@ void ZoneEntityItem::setShapeType(ShapeType type) { } ShapeType ZoneEntityItem::getShapeType() const { - return _shapeType; + return resultWithReadLock([&] { + return _shapeType; + }); } void ZoneEntityItem::setCompoundShapeURL(const QString& url) { - QString oldCompoundShapeURL = _compoundShapeURL; + QString oldCompoundShapeURL; + ShapeType shapeType; withWriteLock([&] { + oldCompoundShapeURL = _compoundShapeURL; _compoundShapeURL = url; + shapeType = _shapeType; }); if (oldCompoundShapeURL != url) { - if (_shapeType == SHAPE_TYPE_COMPOUND) { + if (shapeType == SHAPE_TYPE_COMPOUND) { fetchCollisionGeometryResource(); } else { _shapeResource.reset(); @@ -333,7 +342,7 @@ bool ZoneEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, c bool ZoneEntityItem::contains(const glm::vec3& point) const { GeometryResource::Pointer resource = _shapeResource; - if (_shapeType == SHAPE_TYPE_COMPOUND && resource) { + if (getShapeType() == SHAPE_TYPE_COMPOUND && resource) { if (resource->isLoaded()) { const HFMModel& hfmModel = resource->getHFMModel(); diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp index 6bb743bad0..6ba2d0eeea 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp @@ -199,7 +199,7 @@ void TouchscreenVirtualPadDevice::InputDevice::update(float deltaTime, const con _axisStateMap.clear(); } -bool TouchscreenVirtualPadDevice::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool TouchscreenVirtualPadDevice::InputDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { auto& virtualPadManager = VirtualPad::Manager::instance(); virtualPadManager.requestHapticFeedback((int) duration); return true; diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h index 8c276fb66b..1fa46f8a24 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h @@ -71,7 +71,7 @@ protected: virtual controller::Input::NamedVector getAvailableInputs() const override; virtual QString getDefaultMappingConfig() const override; - virtual bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + virtual bool triggerHapticPulse(float strength, float duration, uint16_t index) override; virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void focusOutEvent() override; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 94bcb718d3..76587118f1 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -4,6 +4,7 @@ // // Created by Stephen Birarda on 2014-09-10. // Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -35,11 +36,11 @@ const QString REDIRECT_HIFI_ADDRESS = NetworkingConstants::REDIRECT_HIFI_ADDRESS const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; -const QString DEFAULT_VIRCADIA_ADDRESS = (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) - ? BuildInfo::INITIAL_STARTUP_LOCATION +const QString DEFAULT_VIRCADIA_ADDRESS = (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty()) + ? BuildInfo::PRELOADED_STARTUP_LOCATION : NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS; -const QString DEFAULT_HOME_ADDRESS = (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) - ? BuildInfo::INITIAL_STARTUP_LOCATION +const QString DEFAULT_HOME_ADDRESS = (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty()) + ? BuildInfo::PRELOADED_STARTUP_LOCATION : NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS; Setting::Handle currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_VIRCADIA_ADDRESS); diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 216e0f28dd..83a4f19703 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #ifdef Q_OS_WIN #include @@ -36,8 +39,80 @@ QUuid FingerprintUtils::_machineFingerprint { QUuid() }; QString FingerprintUtils::getMachineFingerprintString() { QString uuidString; #ifdef Q_OS_LINUX - // sadly need to be root to get smbios guid from linux, so - // for now lets do nothing. + // As per the documentation: + // https://man7.org/linux/man-pages/man5/machine-id.5.html + // + // we use the machine id as a base, but add an application-specific key to it. + // If machine-id isn't available, we try hardware networking devices instead. + + QCryptographicHash hash(QCryptographicHash::Keccak_256); + + QFile machineIdFile("/etc/machine-id"); + if (!machineIdFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + // No machine ID, probably no systemd. + qCWarning(networking) << "Failed to open /etc/machine-id"; + + QDir netDevicesDir("/sys/class/net"); + QFileInfoList netDevicesInfo = netDevicesDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); + + if (netDevicesInfo.empty()) { + // Let getMachineFingerprint handle this contingency + qCWarning(networking) << "Failed to find any hardware networking devices"; + return ""; + } + + for(auto& fileInfo : netDevicesInfo) { + + if (fileInfo.isSymLink() && fileInfo.symLinkTarget().contains("virtual")) { + // symlink points to something like: + // ../../devices/virtual/net/lo + // these are not real devices and have random IDs, so we + // don't care about them. + continue; + } + + QFile addressFile(fileInfo.filePath() + "/address"); + if (addressFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCDebug(networking) << "Adding contents of " << addressFile.fileName(); + hash.addData(addressFile.readAll()); + } else { + qCWarning(networking) << "Failed to read " << addressFile.fileName(); + } + } + } else { + QByteArray data = machineIdFile.readAll(); + hash.addData(data); + } + + // Makes this hash unique to us + hash.addData("Vircadia"); + + // Stretching + for (int i=0; i < 65535; i++) { + hash.addData(hash.result()); + } + + QByteArray result = hash.result(); + result.resize(128 / 8); // GUIDs are 128 bit numbers + + // Set UUID version to 4, ensuring it's a valid UUID + result[6] = (result[6] & 0x0F) | 0x40; + + // Of course, Qt couldn't be nice and just parse something like: + // 1b1b9d6d45c2473bac13dc3011ff58d6 + // + // So we have to turn it into: + // {1b1b9d6d-45c2-473b-ac13-dc3011ff58d6} + + uuidString = result.toHex(); + uuidString.insert(20, '-'); + uuidString.insert(16, '-'); + uuidString.insert(12, '-'); + uuidString.insert(8, '-'); + uuidString.prepend("{"); + uuidString.append("}"); + + qCDebug(networking) << "Linux machine fingerprint:" << uuidString; #endif //Q_OS_LINUX #ifdef Q_OS_MAC diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 4ba456d859..54297415c0 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -794,8 +794,6 @@ void Resource::handleReplyFinished() { { "size_mb", _bytesTotal / 1000000.0 } }); - setSize(_bytesTotal); - // Make sure we keep the Resource alive here auto self = _self.lock(); ResourceCache::requestCompleted(_self); @@ -809,6 +807,14 @@ void Resource::handleReplyFinished() { } auto data = _request->getData(); + if (_request->getUrl().scheme() == "qrc") { + // For resources under qrc://, there's no actual download being done, so + // handleDownloadProgress never gets called. We get the full length here + // at the end. + _bytesTotal = data.length(); + } + + setSize(_bytesTotal); emit loaded(data); downloadFinished(data); } else { diff --git a/libraries/oculusMobilePlugin/src/OculusMobileControllerManager.cpp b/libraries/oculusMobilePlugin/src/OculusMobileControllerManager.cpp index 54a796954e..ec744ce039 100644 --- a/libraries/oculusMobilePlugin/src/OculusMobileControllerManager.cpp +++ b/libraries/oculusMobilePlugin/src/OculusMobileControllerManager.cpp @@ -161,7 +161,7 @@ public: QString getDefaultMappingConfig() const override; void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void focusOutEvent() override; - bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + bool triggerHapticPulse(float strength, float duration, uint16_t index) override; private: void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, @@ -516,12 +516,16 @@ void OculusMobileInputDevice::handleRotationForUntrackedHand(const controller::I pose = pose.transform(controllerToAvatar); } -bool OculusMobileInputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool OculusMobileInputDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { + if (index > 2) { + return false; + } + + controller::Hand hand = (controller::Hand)index; + Locker locker(_lock); bool success = true; - qDebug()<<"AAAA: Haptic duration %f " << duration; - if (hand == controller::BOTH || hand == controller::LEFT) { success &= _hands[0].setHapticFeedback(strength, duration); } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index ef5213df8f..5ac33f65bd 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -278,12 +278,6 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } break; - case SHAPE_TYPE_SPHERE: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); - shape = new btSphereShape(radius); - } - break; case SHAPE_TYPE_MULTISPHERE: { std::vector positions; std::vector radiuses; @@ -297,6 +291,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape->setMargin(MULTI_SPHERE_MARGIN); } break; + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e553672b02..9fbf7a2801 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -470,7 +470,10 @@ void ScriptEngine::waitTillDoneRunning(bool shutdown) { // We should never be waiting (blocking) on our own thread assert(workerThread != QThread::currentThread()); -#ifdef Q_OS_MAC +#if 0 + // 26 Feb 2021 - Disabled this OSX-specific code because it causes OSX to crash on shutdown; without this code, OSX + // doesn't crash on shutdown. Qt 5.12.3 and Qt 5.15.2. + // // On mac, don't call QCoreApplication::processEvents() here. This is to prevent // [NSApplication terminate:] from prematurely destroying the static destructors // while we are waiting for the scripts to shutdown. We will pump the message diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 1d85de4d94..a19e63a665 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -174,7 +174,6 @@ public: QString getFilename() const; - QList getListOfEntityScriptIDs(); /**jsdoc diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 381d931620..d091e6e4b5 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -68,6 +68,8 @@ void ScriptEngines::onErrorLoadingScript(const QString& url) { ScriptEngines::ScriptEngines(ScriptEngine::Context context, const QUrl& defaultScriptsOverride) : _context(context), _defaultScriptsOverride(defaultScriptsOverride) { + scriptGatekeeper.initialize(); + _scriptsModelFilter.setSourceModel(&_scriptsModel); _scriptsModelFilter.sort(0, Qt::AscendingOrder); _scriptsModelFilter.setDynamicSortFilter(true); diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index df736e101d..8c897f70fd 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -24,6 +24,7 @@ #include "ScriptEngine.h" #include "ScriptsModel.h" #include "ScriptsModelFilter.h" +#include "ScriptGatekeeper.h" class ScriptEngine; @@ -176,6 +177,8 @@ public: bool isStopped() const { return _isStopped; } void addScriptEngine(ScriptEnginePointer); + + ScriptGatekeeper scriptGatekeeper; signals: diff --git a/libraries/script-engine/src/ScriptGatekeeper.cpp b/libraries/script-engine/src/ScriptGatekeeper.cpp new file mode 100644 index 0000000000..c223197a8c --- /dev/null +++ b/libraries/script-engine/src/ScriptGatekeeper.cpp @@ -0,0 +1,40 @@ +// +// ScriptGatekeeper.cpp +// libraries/script-engine/src +// +// Created by Kalila L. on Mar 7 2021 +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ScriptGatekeeper.h" + +#include "BuildInfo.h" +#include "SettingHandle.h" + +void ScriptGatekeeper::initialize() { + if (_initialized) { + return; + } + + QVariant rawCurrentWhitelistValues = Setting::Handle(SCRIPT_WHITELIST_ENTRIES_KEY).get(); + QString settingsSafeValues = rawCurrentWhitelistValues.toString(); + + Setting::Handle whitelistEnabled { SCRIPT_WHITELIST_ENABLED_KEY, false }; + Setting::Handle isFirstRun { Settings::firstRun, true }; + + QString preloadedVal = BuildInfo::PRELOADED_SCRIPT_WHITELIST; + + if (settingsSafeValues.isEmpty() && !preloadedVal.isEmpty() && isFirstRun.get()) { + // We assume that the whitelist should be enabled if a preloaded whitelist is attached, so we activate it if it's not already active. + if (!whitelistEnabled.get()) { + whitelistEnabled.set(true); + } + + Setting::Handle(SCRIPT_WHITELIST_ENTRIES_KEY).set(preloadedVal); + } + + _initialized = true; +} diff --git a/libraries/script-engine/src/ScriptGatekeeper.h b/libraries/script-engine/src/ScriptGatekeeper.h new file mode 100644 index 0000000000..22bda8d5d5 --- /dev/null +++ b/libraries/script-engine/src/ScriptGatekeeper.h @@ -0,0 +1,29 @@ +// +// ScriptGatekeeper.h +// libraries/script-engine/src +// +// Created by Kalila L. on Mar 7 2021 +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef vircadia_ScriptGatekeeper_h +#define vircadia_ScriptGatekeeper_h + +#include + +class ScriptGatekeeper : public QObject { + Q_OBJECT +public: + void initialize(); + + QString SCRIPT_WHITELIST_ENABLED_KEY{ "private/whitelistEnabled" }; + QString SCRIPT_WHITELIST_ENTRIES_KEY{ "private/settingsSafeURLS" }; + +private: + bool _initialized { false }; +}; + +#endif // vircadia_ScriptGatekeeper_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index c60d1c2574..f9d41022d7 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -115,12 +115,6 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString case SHAPE_TYPE_HULL: case SHAPE_TYPE_MULTISPHERE: break; - case SHAPE_TYPE_SPHERE: { - float radius = glm::length(halfExtents) / SQUARE_ROOT_OF_3; - radius = glm::max(radius, MIN_HALF_EXTENT); - _halfExtents = glm::vec3(radius); - } - break; case SHAPE_TYPE_CIRCLE: { _halfExtents = glm::vec3(_halfExtents.x, MIN_HALF_EXTENT, _halfExtents.z); } @@ -228,6 +222,7 @@ float ShapeInfo::computeVolume() const { volume = 8.0f * _halfExtents.x * _halfExtents.y * _halfExtents.z; break; } + case SHAPE_TYPE_ELLIPSOID: case SHAPE_TYPE_SPHERE: { volume = 4.0f * PI * _halfExtents.x * _halfExtents.y * _halfExtents.z / 3.0f; break; diff --git a/pkg-scripts/README b/pkg-scripts/README deleted file mode 100644 index 6eb74980d0..0000000000 --- a/pkg-scripts/README +++ /dev/null @@ -1,60 +0,0 @@ -Collection of scripts to create server distribution packages. Most of these scripts assume -use of the build script at https://github.com/vircadia/vircadia-builder, specifically that -the following directory structure exists - -base folder/ - source/ git checkout - build/ result of cmake build - qt5-install/ installed or built Qt5 installation - -These scripts assume that the current directory is the pkg-scripts folder inside of the source directory -and that the base folder can be reached by going to "../..". This may not work if pkg-scripts is a symlink; -adding an VIRCADIA=~/Vircadia to the beginning of the commandline will override where it looks for the base folder - -Ubuntu: - DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server - - This script will retrieve the current git commit date and hash and assemble a version from it. - It will attempt construct a .deb file in the pkg-scripts folder - -Amazon Linux 2: - ./make-rpm-server - - This script will retrieve the current git commit date and hash and assemble a version from it. - It will attempt construct an .rpm file in the pkg-scripts folder - -Docker: - ./make-docker-server - - This script will attempt to create a docker container - -Results: - The following directory structure is created for binaries: - /opt/vircadia - executables - /opt/vircadia/lib - private shared libraries required for executables - /opt/vircadia/resources - files required by domain-server administrative website - /opt/vircadia/plugins - files required by assignment-client, mainly for audio codecs - - The following systemd services are installed in /usr/lib/systemd/system: - vircadia-assignment-client.service - vircadia-domain-server.service - vircadia-server.target - used to launch/shutdown the two prior services - vircadia-assignment-client@.service - vircadia-domain-server@.service - vircadia-server@.target - used to launch/shutdown the two prior services - - The top three services in this list are the "normal" services that launch Vircadia - in the typical fashion. The bottom three services are "template" services designed - to permit multiple services to be installed and running on a single machine. - - The script "/opt/vircadia/new-server serverName basePort" will do the necessary - setup for a new domain with the specified server name and port. Upon installation - the package will create and launch a domain named "default" at base port 40100. - The domain name here has nothing to do with the name people will use to find your - domain and has nothing to do with "place names", it is only used to locate the files - used to configure and run the domain on your server. - - The server stores its files in the following locations: - /var/lib/vircadia/.local - "unnamed" services (the default location for Vircadia servers) - /var/lib/vircadia/serverName - "named" (template) domains - /etc/opt/vircadia - environment variables when launching named domains diff --git a/pkg-scripts/README.md b/pkg-scripts/README.md new file mode 100644 index 0000000000..f4f1056877 --- /dev/null +++ b/pkg-scripts/README.md @@ -0,0 +1,88 @@ +# Vircadia Server Packaging Scripts + +Collection of scripts to create server distribution packages. Most of these scripts assume +use of the build script at https://github.com/vircadia/vircadia-builder, specifically that +the following directory structure exists: + +``` +base folder/ + source/ git checkout + build/ result of cmake build + qt5-install/ installed or built Qt5 installation +``` + +These scripts assume that the current directory is the pkg-scripts folder inside of the source directory +and that the base folder can be reached by going to `../..`. This may not work if pkg-scripts is a symlink; adding an VIRCADIA=~/Vircadia to the beginning of the commandline will override where it looks for the base folder. + +## Ubuntu +``` +DEBVERSION="Semver e.g. 2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server +``` + +This script will retrieve the current git commit date and hash and append it to your specified version. +It will attempt construct a .deb file in the pkg-scripts folder + +## Amazon Linux 2 + +You will need to install `rpm-build` if you have not already. +``` +sudo yum install rpm-build +``` +Then, run the build script. +``` +RPMVERSION="Semver e.g. 2021.1.0" ./make-rpm-server +``` + +This script will retrieve the current git commit date and hash and append it to your specified version. +It will attempt construct an .rpm file in the pkg-scripts folder + +## Docker +``` +./make-docker-server +``` + +This script will attempt to create a docker container. + +## Results + +### Binaries + +The following directory structure is created for binaries: +``` +/opt/vircadia - executables +/opt/vircadia/lib - private shared libraries required for executables +/opt/vircadia/resources - files required by domain-server administrative website +/opt/vircadia/plugins - files required by assignment-client, mainly for audio codecs +``` + +### Services + +The following systemd services are installed in `/usr/lib/systemd/system`: +``` +vircadia-assignment-client.service +vircadia-domain-server.service +vircadia-server.target - used to launch/shutdown the two prior services +vircadia-assignment-client@.service +vircadia-domain-server@.service +vircadia-server@.target - used to launch/shutdown the two prior services +``` + +The top three services in this list are the "normal" services that launch Vircadia +in the typical fashion. The bottom three services are "template" services designed +to permit multiple services to be installed and running on a single machine. + +The script `/opt/vircadia/new-server serverName basePort` will do the necessary +setup for a new domain with the specified server name and port. Upon installation +the package will create and launch a domain named "default" at base port 40100. +The domain name here has nothing to do with the name people will use to find your +domain and has nothing to do with "place names", it is only used to locate the files +used to configure and run the domain on your server. + +### Stored Files + +The server stores its files in the following locations: +``` +/var/lib/vircadia/.local - "unnamed" services (the default location for Vircadia servers) +/var/lib/vircadia/serverName - "named" (template) domains +/etc/opt/vircadia - environment variables when launching named domains +``` diff --git a/pkg-scripts/make-deb-server b/pkg-scripts/make-deb-server index 6a7bff26c3..2040563691 100755 --- a/pkg-scripts/make-deb-server +++ b/pkg-scripts/make-deb-server @@ -6,11 +6,11 @@ fi GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7` -VERSION=2020.2.0-asteria-$GITDATE-$GITCOMMIT +VERSION=$DEBVERSION-$GITDATE-$GITCOMMIT sudo apt-get install chrpath binutils dh-make -DEB_BUILD_ROOT=temp-make-deb/vircadia-server-$VERSION-0ubuntu1 +DEB_BUILD_ROOT=temp-make-deb/vircadia-server_$VERSION-0ubuntu1 rm -r temp-make-deb mkdir -p $DEB_BUILD_ROOT diff --git a/pkg-scripts/make-rpm-server b/pkg-scripts/make-rpm-server index 20ac76d4b0..8bbc61593e 100755 --- a/pkg-scripts/make-rpm-server +++ b/pkg-scripts/make-rpm-server @@ -5,7 +5,7 @@ if [ "$VIRCADIA" = "" ]; then fi GITDATE=`git -C $VIRCADIA/source log -n 1 --format=raw | grep author | cut -d">" -f 2 | cut -d" " -f 2 | xargs -I {} date -d @{} +"%Y%m%d"` GITCOMMIT=`git -C $VIRCADIA/source rev-parse HEAD | cut -c 1-7` -VERSION=2020.2.0_ASTERIA_${GITDATE}_${GITCOMMIT} +VERSION=${RPMVERSION}_${GITDATE}_${GITCOMMIT} SOFILES=`ls \ $VIRCADIA/build/libraries/*/*.so \ diff --git a/plugins/hifiSdl2/src/Joystick.cpp b/plugins/hifiSdl2/src/Joystick.cpp index ad0b459544..55a07e0ab9 100644 --- a/plugins/hifiSdl2/src/Joystick.cpp +++ b/plugins/hifiSdl2/src/Joystick.cpp @@ -72,7 +72,7 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { } } -bool Joystick::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool Joystick::triggerHapticPulse(float strength, float duration, uint16_t index) { if (SDL_HapticRumblePlay(_sdlHaptic, strength, duration) != 0) { return false; } diff --git a/plugins/hifiSdl2/src/Joystick.h b/plugins/hifiSdl2/src/Joystick.h index ae90470974..a839676468 100644 --- a/plugins/hifiSdl2/src/Joystick.h +++ b/plugins/hifiSdl2/src/Joystick.h @@ -39,7 +39,7 @@ public: virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void focusOutEvent() override; - bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + bool triggerHapticPulse(float strength, float duration, uint16_t index) override; Joystick() : InputDevice("GamePad") {} ~Joystick(); diff --git a/plugins/oculus/src/OculusControllerManager.cpp b/plugins/oculus/src/OculusControllerManager.cpp index c1431fa4b2..4e22b7a236 100644 --- a/plugins/oculus/src/OculusControllerManager.cpp +++ b/plugins/oculus/src/OculusControllerManager.cpp @@ -369,7 +369,13 @@ void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const pose = pose.transform(controllerToAvatar); } -bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { + if (index > 2) { + return false; + } + + controller::Hand hand = (controller::Hand)index; + Locker locker(_lock); bool toReturn = true; ovr::withSession([&](ovrSession session) { diff --git a/plugins/oculus/src/OculusControllerManager.h b/plugins/oculus/src/OculusControllerManager.h index 7d1d176a56..6a3a10f18a 100644 --- a/plugins/oculus/src/OculusControllerManager.h +++ b/plugins/oculus/src/OculusControllerManager.h @@ -76,7 +76,7 @@ private: void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void focusOutEvent() override; - bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + bool triggerHapticPulse(float strength, float duration, uint16_t index) override; private: void stopHapticPulse(bool leftHand); diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index ac41502a38..8a8f18e85f 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -1561,7 +1561,13 @@ void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const _poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = pose.transform(controllerToAvatar); } -bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { +bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { + if (index > 2) { + return false; + } + + controller::Hand hand = (controller::Hand)index; + Locker locker(_lock); if (hand == controller::BOTH || hand == controller::LEFT) { if (strength == 0.0f) { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 956b2b5eaf..1b8c2a2ec5 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -112,7 +112,7 @@ private: QString getDefaultMappingConfig() const override; void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void focusOutEvent() override; - bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override; + bool triggerHapticPulse(float strength, float duration, uint16_t index) override; void hapticsHelper(float deltaTime, bool leftHand); void calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration); void calibrate(const controller::InputCalibrationData& inputCalibration); diff --git a/screenshare/package-lock.json b/screenshare/package-lock.json index 4d0fea0805..cf1f382ce0 100644 --- a/screenshare/package-lock.json +++ b/screenshare/package-lock.json @@ -71,9 +71,9 @@ } }, "@types/node": { - "version": "12.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.9.tgz", - "integrity": "sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==", + "version": "12.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.15.tgz", + "integrity": "sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw==", "dev": true }, "ansi-styles": { @@ -365,9 +365,9 @@ "dev": true }, "electron": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/electron/-/electron-7.2.4.tgz", - "integrity": "sha512-Z+R692uTzXgP8AHrabE+kkrMlQJ6pnAYoINenwj9QSqaD2YbO8IuXU9DMCcUY0+VpA91ee09wFZJNUKYPMnCKg==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-9.4.0.tgz", + "integrity": "sha512-hOC4q0jkb+UDYZRy8vrZ1IANnq+jznZnbkD62OEo06nU+hIbp2IrwDRBNuSLmQ3cwZMVir0WSIA1qEVK0PkzGA==", "dev": true, "requires": { "@electron/get": "^1.0.1", diff --git a/screenshare/package.json b/screenshare/package.json index e92e20a073..07d461f252 100644 --- a/screenshare/package.json +++ b/screenshare/package.json @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/highfidelity/hifi#readme", "devDependencies": { - "electron": "^7.2.4", + "electron": "^9.4.0", "electron-packager": "^14.0.6" }, "dependencies": { diff --git a/scripts/communityScripts/chat/FloofChat.js b/scripts/communityScripts/chat/FloofChat.js index 1208a36e5f..26ab0d04e7 100644 --- a/scripts/communityScripts/chat/FloofChat.js +++ b/scripts/communityScripts/chat/FloofChat.js @@ -61,8 +61,8 @@ var settingsRoot = "FloofChat"; var vircadiaGotoUrl = "https://metaverse.vircadia.com/interim/d-goto/app/goto.json"; var gotoJSONUrl = Settings.getValue(settingsRoot + "/gotoJSONUrl", vircadiaGotoUrl); -var muted = Settings.getValue(settingsRoot + "/muted", {"Local": false, "Domain": false, "Grid": true}); -var mutedAudio = Settings.getValue(settingsRoot + "/mutedAudio", {"Local": false, "Domain": false, "Grid": true}); +var muted = Settings.getValue(settingsRoot + "/muted", {"Local": false, "Domain": true, "Grid": true}); +var mutedAudio = Settings.getValue(settingsRoot + "/mutedAudio", {"Local": false, "Domain": true, "Grid": true}); var notificationSound = SoundCache.getSound(Script.resolvePath("resources/bubblepop.wav")); var ws; diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index b27008e255..82b228008f 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -36,6 +36,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/inspect.js", "system/keyboardShortcuts/keyboardShortcuts.js", "system/checkForUpdates.js", + "system/onEscape.js", "system/onFirstRun.js", "system/appreciate/appreciate_app.js" ]; diff --git a/scripts/system/away.js b/scripts/system/away.js index ef79791e44..7268dc3233 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -3,10 +3,9 @@ // // away.js // -// examples -// // Created by Howard Stearns 11/3/15 // Copyright 2015 High Fidelity, Inc. +// Copyright 2021 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -255,17 +254,6 @@ function setActiveProperties() { Script.clearInterval(avatarMovedInterval); } -function maybeGoActive(event) { - if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it) - return; - } - if (!isAway && (event.text === 'ESC')) { - goAway(); - } else { - goActive(); - } -} - var wasHmdActive = HMD.active; var wasMouseCaptured = Reticle.mouseCaptured; @@ -329,12 +317,24 @@ var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable"; var handleMessage = function(channel, message, sender) { if (channel === CHANNEL_AWAY_ENABLE && sender === MyAvatar.sessionUUID) { print("away.js | Got message on Hifi-Away-Enable: ", message); - setEnabled(message === 'enable'); + if (message === 'enable') { + setEnabled(true); + } else if (message === 'toggle') { + toggleAway(); + } } }; Messages.subscribe(CHANNEL_AWAY_ENABLE); Messages.messageReceived.connect(handleMessage); +function toggleAway() { + if (!isAway) { + goAway(); + } else { + goActive(); + } +} + var maybeIntervalTimer = Script.setInterval(function() { maybeMoveOverlay(); maybeGoAway(); @@ -343,7 +343,6 @@ var maybeIntervalTimer = Script.setInterval(function() { Controller.mousePressEvent.connect(goActive); -Controller.keyPressEvent.connect(maybeGoActive); // Note peek() so as to not interfere with other mappings. eventMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(goActive); eventMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(goActive); @@ -371,7 +370,6 @@ Script.scriptEnding.connect(function () { HMD.awayStateWhenFocusLostInVRChanged.disconnect(awayStateWhenFocusLostInVRChanged); Controller.disableMapping(eventMappingName); Controller.mousePressEvent.disconnect(goActive); - Controller.keyPressEvent.disconnect(maybeGoActive); Messages.messageReceived.disconnect(handleMessage); Messages.unsubscribe(CHANNEL_AWAY_ENABLE); }); diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 21b41bd354..4884cb3930 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -587,7 +587,7 @@ "tooltip": "The mode in which to draw an entity, either \"Solid\" or \"Wireframe\"." }, "billboardMode": { - "tooltip": "Determines if and how the entity will face the camera.", + "tooltip": "Determines if and how the entity will face the camera." }, "renderWithZones": { "tooltip": "If set, this entity will only render when your avatar is inside of a zone in this list." diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index a7800b3e73..06ffbc2f99 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -292,7 +292,7 @@ const GROUPS = [ { label: "Shape Type", type: "dropdown", - options: { "box": "Box", "sphere": "Sphere", "ellipsoid": "Ellipsoid", + options: { "box": "Box", "sphere": "Sphere", "cylinder-y": "Cylinder", "compound": "Use Compound Shape URL" }, propertyID: "zoneShapeType", propertyName: "shapeType", // actual entity property name diff --git a/scripts/system/create/modules/entityShapeVisualizer.js b/scripts/system/create/modules/entityShapeVisualizer.js index 7a1cd74d25..8f218f3555 100644 --- a/scripts/system/create/modules/entityShapeVisualizer.js +++ b/scripts/system/create/modules/entityShapeVisualizer.js @@ -13,6 +13,7 @@ var SHAPETYPE_TO_SHAPE = { "box": "Cube", "ellipsoid": "Sphere", + "sphere": "Sphere", "cylinder-y": "Cylinder", }; @@ -35,14 +36,6 @@ function getEntityShapePropertiesForType(properties) { modelURL: properties.compoundShapeURL, localDimensions: properties.localDimensions }; - } else if (properties.shapeType === "sphere") { - var sphereDiameter = Math.max(properties.localDimensions.x, properties.localDimensions.y, - properties.localDimensions.z); - return { - type: "Sphere", - modelURL: properties.compoundShapeURL, - localDimensions: {x: sphereDiameter, y: sphereDiameter, z: sphereDiameter} - }; } break; } diff --git a/scripts/system/onEscape.js b/scripts/system/onEscape.js new file mode 100644 index 0000000000..323b8b0612 --- /dev/null +++ b/scripts/system/onEscape.js @@ -0,0 +1,34 @@ +'use strict'; + +// +// onEscape.js +// +// Created by Kalila L. on Feb 3 2021. +// Copyright 2021 Vircadia contributors. +// +// This script manages actions when the user triggers an "escape" key or action. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { // BEGIN LOCAL_SCOPE + + function maybeEscapeKeyPressed (event) { + if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it) + return; + } + + if (event.text === 'ESC') { + var CHANNEL_AWAY_ENABLE = 'Hifi-Away-Enable'; + Messages.sendMessage(CHANNEL_AWAY_ENABLE, 'toggle', true); + } + } + + Controller.keyPressEvent.connect(maybeEscapeKeyPressed); + + Script.scriptEnding.connect(function () { + Controller.keyPressEvent.disconnect(maybeEscapeKeyPressed); + }); + +}()); diff --git a/scripts/system/onFirstRun.js b/scripts/system/onFirstRun.js index 65c1d06ec5..e7052c0e3a 100644 --- a/scripts/system/onFirstRun.js +++ b/scripts/system/onFirstRun.js @@ -13,6 +13,7 @@ // (function() { // BEGIN LOCAL_SCOPE + // Check to see if we should run this script or bail... var SETTING_TO_CHECK = 'firstRun'; var DEFAULT_DISPLAY_NAME = ''; @@ -20,6 +21,8 @@ return; } + // If this is our first run, then proceed... + if (MyAvatar.displayName === '') { var selectedDisplayName = Window.prompt('Enter a display name.', MyAvatar.displayName); diff --git a/server-console/src/main.js b/server-console/src/main.js index e83b37ac4c..80be5c3fc2 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -460,7 +460,7 @@ var labels = { } }, goHome: { - label: 'Visit Sandbox', + label: 'Visit Local Server', click: visitSandboxClicked, enabled: false },