mirror of
https://github.com/overte-org/overte.git
synced 2025-06-23 11:59:49 +02:00
Merge branch 'master' into materials
This commit is contained in:
commit
7d58c772cb
85 changed files with 1503 additions and 516 deletions
17
.github/stale.yml
vendored
Normal file
17
.github/stale.yml
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 120
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 60
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- pinned
|
||||||
|
- security
|
||||||
|
# Label to use when marking an issue as stale
|
||||||
|
staleLabel: stale
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had recent activity.
|
||||||
|
It will be closed if no further activity occurs.
|
||||||
|
Thank you for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: false
|
103
BUILD.md
103
BUILD.md
|
@ -1,6 +1,6 @@
|
||||||
# General Build Information
|
# General Build Information
|
||||||
|
|
||||||
*Last Updated on August 26, 2020*
|
*Last Updated on December 21, 2020*
|
||||||
|
|
||||||
### OS Specific Build Guides
|
### OS Specific Build Guides
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
- [git](https://git-scm.com/downloads): >= 1.6
|
- [git](https://git-scm.com/downloads): >= 1.6
|
||||||
- [cmake](https://cmake.org/download/): 3.9
|
- [CMake](https://cmake.org/download/): 3.9
|
||||||
- [Python](https://www.python.org/downloads/): 3.6 or higher
|
- [Python](https://www.python.org/downloads/): 3.6 or higher
|
||||||
- [Node.JS](https://nodejs.org/en/): >= 12.13.1 LTS
|
- [Node.JS](https://nodejs.org/en/): >= 12.13.1 LTS
|
||||||
- Used to build the Screen Sharing executable.
|
- Used to build the Screen Sharing executable.
|
||||||
|
@ -41,15 +41,18 @@ 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.
|
Vircadia uses CMake to generate build files and project files for your platform.
|
||||||
|
|
||||||
#### Qt
|
#### Qt
|
||||||
CMake will download Qt 5.12.3 using vcpkg.
|
|
||||||
|
|
||||||
To override this (i.e. use an installed Qt configuration - you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt **lib/cmake** folder.
|
CMake will download Qt 5.12.3 using vcpkg.
|
||||||
|
|
||||||
|
To override this (i.e. use an installed Qt configuration - you will 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. e.g.
|
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. e.g.
|
||||||
|
|
||||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Qt5.12.3/gcc_64/lib/cmake
|
```bash
|
||||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/clang_64/lib/cmake/
|
export QT_CMAKE_PREFIX_PATH=/usr/local/Qt5.12.3/gcc_64/lib/cmake
|
||||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.12.3/lib/cmake
|
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.12.3/clang_64/lib/cmake/
|
||||||
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
|
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.12.3/lib/cmake
|
||||||
|
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
|
||||||
|
```
|
||||||
|
|
||||||
#### VCPKG
|
#### VCPKG
|
||||||
|
|
||||||
|
@ -59,7 +62,9 @@ You do not need to install vcpkg.
|
||||||
Building the dependencies can be lengthy and the resulting files will be stored in your OS temp directory.
|
Building the dependencies can be lengthy and the resulting files will be stored in your OS temp directory.
|
||||||
However, those files can potentially get cleaned up by the OS, so in order to avoid this and having to redo the lengthy build step, you can set the following environment variable:
|
However, those files can potentially get cleaned up by the OS, so in order to avoid this and having to redo the lengthy build step, you can set the following environment variable:
|
||||||
|
|
||||||
export HIFI_VCPKG_BASE=/path/to/directory
|
```bash
|
||||||
|
export 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.
|
Where `/path/to/directory` is the path to a directory where you wish the build files to get stored.
|
||||||
|
|
||||||
|
@ -67,50 +72,58 @@ Where `/path/to/directory` is the path to a directory where you wish the build f
|
||||||
|
|
||||||
##### Possible Environment Variables
|
##### Possible Environment Variables
|
||||||
|
|
||||||
// The URL to post the dump to.
|
```text
|
||||||
CMAKE_BACKTRACE_URL
|
// The URL to post the dump to.
|
||||||
// The identifying tag of the release.
|
CMAKE_BACKTRACE_URL
|
||||||
CMAKE_BACKTRACE_TOKEN
|
// The identifying tag of the release.
|
||||||
|
CMAKE_BACKTRACE_TOKEN
|
||||||
// The release version, e.g., 2021.3.2.
|
|
||||||
RELEASE_NUMBER
|
// The release version, e.g., 2021.3.2.
|
||||||
// The release name, e.g., Eos.
|
RELEASE_NUMBER
|
||||||
RELEASE_NAME
|
// The release name, e.g., Eos.
|
||||||
// The build commit, e.g., use a Git hash for the most recent commit in the branch - fd6973b.
|
RELEASE_NAME
|
||||||
BUILD_NUMBER
|
// 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
|
##### 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.
|
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
|
```bash
|
||||||
cd build
|
mkdir build
|
||||||
cmake ..
|
cd build
|
||||||
|
cmake ..
|
||||||
|
```
|
||||||
|
|
||||||
If CMake gives you the same error message repeatedly after the build fails, try removing `CMakeCache.txt`.
|
If CMake gives you the same error message repeatedly after the build fails, try removing `CMakeCache.txt`.
|
||||||
|
|
||||||
##### Generating a release/debug only vcpkg build
|
##### 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:
|
For release only vcpkg:
|
||||||
|
|
||||||
|
@ -126,7 +139,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:
|
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
|
#### Finding Dependencies
|
||||||
|
|
||||||
|
|
|
@ -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/<yourname>/.gradle` on Windows). Edit the file to contain the following
|
Create a `gradle.properties` file in the `.gradle` folder (`$HOME/.gradle` on Unix, `Users/<yourname>/.gradle` on Windows). Edit the file to contain the following
|
||||||
|
|
||||||
HIFI_ANDROID_PRECOMPILED=<your_home_directory>/Android/hifi_externals
|
```properties
|
||||||
HIFI_ANDROID_KEYSTORE=<key_store_directory>/<keystore_name>.jks
|
HIFI_ANDROID_PRECOMPILED=<your_home_directory>/Android/hifi_externals
|
||||||
HIFI_ANDROID_KEYSTORE_PASSWORD=<password>
|
HIFI_ANDROID_KEYSTORE=<key_store_directory>/<keystore_name>.jks
|
||||||
HIFI_ANDROID_KEY_ALIAS=<key_alias>
|
HIFI_ANDROID_KEYSTORE_PASSWORD=<password>
|
||||||
HIFI_ANDROID_KEY_PASSWORD=<key_passwords>
|
HIFI_ANDROID_KEY_ALIAS=<key_alias>
|
||||||
|
HIFI_ANDROID_KEY_PASSWORD=<key_passwords>
|
||||||
|
```
|
||||||
|
|
||||||
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.
|
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`
|
Add these lines to `gradle.properties`
|
||||||
|
|
||||||
SUPPRESS_QUEST_INTERFACE
|
```properties
|
||||||
SUPPRESS_QUEST_FRAME_PLAYER
|
SUPPRESS_QUEST_INTERFACE
|
||||||
|
SUPPRESS_QUEST_FRAME_PLAYER
|
||||||
|
```
|
||||||
|
|
||||||
#### If you are building for an Oculus Quest
|
#### If you are building for an Oculus Quest
|
||||||
|
|
||||||
Add these lines to `gradle.properties`
|
Add these lines to `gradle.properties`
|
||||||
|
|
||||||
SUPPRESS_INTERFACE
|
```properties
|
||||||
SUPPRESS_FRAME_PLAYER
|
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`
|
#### 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
|
```properties
|
||||||
SUPPRESS_QUEST_FRAME_PLAYER
|
SUPPRESS_FRAME_PLAYER
|
||||||
|
SUPPRESS_QUEST_FRAME_PLAYER
|
||||||
|
```
|
||||||
|
|
||||||
### Clone the repository
|
### Clone the repository
|
||||||
|
|
||||||
|
|
|
@ -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.
|
directory that vcpkg builds into with the `HIFI_VCPKG_BASE` environment variable.
|
||||||
For example, you might set `HIFI_VCPKG_BASE` to `/home/$USER/vcpkg`.
|
For example, you might set `HIFI_VCPKG_BASE` to `/home/$USER/vcpkg`.
|
||||||
By default, vcpkg will build in the system `/tmp` directory.
|
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`
|
|
||||||
|
|
24
BUILD_OSX.md
24
BUILD_OSX.md
|
@ -8,22 +8,26 @@ 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.
|
[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
|
||||||
|
```
|
||||||
|
|
||||||
### Python 3
|
### 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.
|
Execute the `Update Shell Profile.command` script that is provided with the installer.
|
||||||
|
|
||||||
### OSX SDK
|
### 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.
|
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
|
### 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
|
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via
|
||||||
`export OPENSSL_ROOT_DIR=/usr/local/opt/openssl`
|
`export OPENSSL_ROOT_DIR=/usr/local/opt/openssl`
|
||||||
or by appending `-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl` to `cmake`
|
or by appending `-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl` to `cmake`
|
||||||
|
@ -31,12 +35,14 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via
|
||||||
### Xcode
|
### 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.
|
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.
|
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.
|
||||||
|
|
||||||
|
|
177
INSTALLER.md
177
INSTALLER.md
|
@ -1,6 +1,6 @@
|
||||||
# Creating an Installer
|
# 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.
|
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`.
|
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`
|
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.
|
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:
|
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](<https://nodejs.org/en/download/>)
|
1. [Node.JS and NPM](<https://nodejs.org/en/download/>)
|
||||||
1. Install version 10.15.0 LTS (or greater)
|
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.
|
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. 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.
|
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
|
1. Build CMakeTargets->PACKAGE
|
||||||
The installer is now available in `build\_CPack_Packages\win64\NSIS`
|
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](<https://www.npmjs.com/get-npm>)
|
1. [npm](<https://www.npmjs.com/get-npm>)
|
||||||
Install version 12.16.3 LTS
|
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`
|
1. Perform a Release build of `package`
|
||||||
Installer is now available in `build/_CPack_Packages/Darwin/DragNDrop
|
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```
|
#### Server
|
||||||
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.
|
##### 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.
|
||||||
|
|
|
@ -29,9 +29,11 @@ Vircadia™ is a 3D social software project seeking to incrementally bring about
|
||||||
|
|
||||||
### How to generate an Installer
|
### How to generate an Installer
|
||||||
|
|
||||||
- [For Windows](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md)
|
- [For Windows - Interface & Server](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md)
|
||||||
- [For Mac](https://github.com/vircadia/vircadia/blob/master/INSTALLER.md#os-x)
|
- [For Mac - Interface](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 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/)
|
### Boot to Metaverse: [The Goal](https://vircadia.com/vision/)
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
|
|
||||||
const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer";
|
const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer";
|
||||||
|
const int MESSAGES_MIXER_RATE_LIMITER_INTERVAL = 1000; // 1 second
|
||||||
|
|
||||||
MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(message)
|
MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(message)
|
||||||
{
|
{
|
||||||
|
@ -44,10 +45,20 @@ void MessagesMixer::handleMessages(QSharedPointer<ReceivedMessage> receivedMessa
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
QUuid senderID;
|
QUuid senderID;
|
||||||
bool isText;
|
bool isText;
|
||||||
|
auto senderUUID = senderNode->getUUID();
|
||||||
MessagesClient::decodeMessagesPacket(receivedMessage, channel, isText, message, data, senderID);
|
MessagesClient::decodeMessagesPacket(receivedMessage, channel, isText, message, data, senderID);
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
auto itr = _allSubscribers.find(senderUUID);
|
||||||
|
if (itr == _allSubscribers.end()) {
|
||||||
|
_allSubscribers[senderUUID] = 1;
|
||||||
|
} else if (*itr >= _maxMessagesPerSecond) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
*itr += 1;
|
||||||
|
}
|
||||||
|
|
||||||
nodeList->eachMatchingNode(
|
nodeList->eachMatchingNode(
|
||||||
[&](const SharedNodePointer& node)->bool {
|
[&](const SharedNodePointer& node)->bool {
|
||||||
return node->getActiveSocket() && _channelSubscribers[channel].contains(node->getUUID());
|
return node->getActiveSocket() && _channelSubscribers[channel].contains(node->getUUID());
|
||||||
|
@ -60,14 +71,18 @@ void MessagesMixer::handleMessages(QSharedPointer<ReceivedMessage> receivedMessa
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessagesMixer::handleMessagesSubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
void MessagesMixer::handleMessagesSubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
|
auto senderUUID = senderNode->getUUID();
|
||||||
QString channel = QString::fromUtf8(message->getMessage());
|
QString channel = QString::fromUtf8(message->getMessage());
|
||||||
_channelSubscribers[channel] << senderNode->getUUID();
|
|
||||||
|
_channelSubscribers[channel] << senderUUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
|
auto senderUUID = senderNode->getUUID();
|
||||||
QString channel = QString::fromUtf8(message->getMessage());
|
QString channel = QString::fromUtf8(message->getMessage());
|
||||||
|
|
||||||
if (_channelSubscribers.contains(channel)) {
|
if (_channelSubscribers.contains(channel)) {
|
||||||
_channelSubscribers[channel].remove(senderNode->getUUID());
|
_channelSubscribers[channel].remove(senderUUID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +103,48 @@ void MessagesMixer::sendStatsPacket() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessagesMixer::run() {
|
void MessagesMixer::run() {
|
||||||
ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer);
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer });
|
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<NodeList>();
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,21 @@ private slots:
|
||||||
void handleMessages(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleMessages(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleMessagesSubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleMessagesSubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleMessagesUnsubscribe(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
void parseDomainServerSettings(const QJsonObject& domainSettings);
|
||||||
|
void domainSettingsRequestComplete();
|
||||||
|
|
||||||
|
void startMaxMessagesProcessor();
|
||||||
|
void stopMaxMessagesProcessor();
|
||||||
|
void processMaxMessagesContainer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHash<QString,QSet<QUuid>> _channelSubscribers;
|
QHash<QString, QSet<QUuid>> _channelSubscribers;
|
||||||
|
QHash<QUuid, int> _allSubscribers;
|
||||||
|
|
||||||
|
const int DEFAULT_NODE_MESSAGES_PER_SECOND = 1000;
|
||||||
|
int _maxMessagesPerSecond { 0 };
|
||||||
|
|
||||||
|
QTimer* _maxMessagesTimer { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_MessagesMixer_h
|
#endif // hifi_MessagesMixer_h
|
||||||
|
|
|
@ -21,9 +21,9 @@ macro(GENERATE_INSTALLERS)
|
||||||
set(INSTALLER_TYPE "client_only")
|
set(INSTALLER_TYPE "client_only")
|
||||||
string(REGEX REPLACE "Vircadia" "Vircadia Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION})
|
string(REGEX REPLACE "Vircadia" "Vircadia Interface" _DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||||
elseif (SERVER_ONLY)
|
elseif (SERVER_ONLY)
|
||||||
set(_PACKAGE_NAME_EXTRA "-Sandbox")
|
set(_PACKAGE_NAME_EXTRA "-Server")
|
||||||
set(INSTALLER_TYPE "server_only")
|
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 ()
|
else ()
|
||||||
set(_DISPLAY_NAME ${BUILD_ORGANIZATION})
|
set(_DISPLAY_NAME ${BUILD_ORGANIZATION})
|
||||||
set(INSTALLER_TYPE "full")
|
set(INSTALLER_TYPE "full")
|
||||||
|
@ -123,7 +123,7 @@ macro(GENERATE_INSTALLERS)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (BUILD_SERVER)
|
if (BUILD_SERVER)
|
||||||
cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Sandbox")
|
cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "Vircadia Server")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
|
|
@ -25,7 +25,10 @@ macro(SET_PACKAGING_PARAMETERS)
|
||||||
set_from_env(RELEASE_NUMBER RELEASE_NUMBER "")
|
set_from_env(RELEASE_NUMBER RELEASE_NUMBER "")
|
||||||
set_from_env(RELEASE_NAME RELEASE_NAME "")
|
set_from_env(RELEASE_NAME RELEASE_NAME "")
|
||||||
set_from_env(STABLE_BUILD STABLE_BUILD 0)
|
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)
|
set_from_env(BYPASS_SIGNING BYPASS_SIGNING 0)
|
||||||
|
|
||||||
message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}")
|
message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}")
|
||||||
|
@ -177,12 +180,12 @@ macro(SET_PACKAGING_PARAMETERS)
|
||||||
if (PRODUCTION_BUILD)
|
if (PRODUCTION_BUILD)
|
||||||
set(INTERFACE_SHORTCUT_NAME "Vircadia")
|
set(INTERFACE_SHORTCUT_NAME "Vircadia")
|
||||||
set(CONSOLE_SHORTCUT_NAME "Console")
|
set(CONSOLE_SHORTCUT_NAME "Console")
|
||||||
set(SANDBOX_SHORTCUT_NAME "Sandbox")
|
set(SANDBOX_SHORTCUT_NAME "Server")
|
||||||
set(APP_USER_MODEL_ID "com.vircadia.console")
|
set(APP_USER_MODEL_ID "com.vircadia.console")
|
||||||
else ()
|
else ()
|
||||||
set(INTERFACE_SHORTCUT_NAME "Vircadia - ${BUILD_VERSION_NO_SHA}")
|
set(INTERFACE_SHORTCUT_NAME "Vircadia - ${BUILD_VERSION_NO_SHA}")
|
||||||
set(CONSOLE_SHORTCUT_NAME "Console - ${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 ()
|
endif ()
|
||||||
|
|
||||||
set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}")
|
set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}")
|
||||||
|
|
|
@ -29,7 +29,8 @@ namespace BuildInfo {
|
||||||
const QString BUILD_NUMBER = "@BUILD_NUMBER@";
|
const QString BUILD_NUMBER = "@BUILD_NUMBER@";
|
||||||
const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@";
|
const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@";
|
||||||
const QString BUILD_TIME = "@BUILD_TIME@";
|
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 {
|
enum BuildType {
|
||||||
Dev,
|
Dev,
|
||||||
|
|
|
@ -251,8 +251,8 @@ Var substringResult
|
||||||
|
|
||||||
!macro InitSection SecName
|
!macro InitSection SecName
|
||||||
; This macro reads component installed flag from the registry and
|
; This macro reads component installed flag from the registry and
|
||||||
;changes checked state of the section on the components page.
|
; changes checked state of the section on the components page.
|
||||||
;Input: section index constant name specified in Section command.
|
; Input: section index constant name specified in Section command.
|
||||||
|
|
||||||
ClearErrors
|
ClearErrors
|
||||||
;Reading component status from registry
|
;Reading component status from registry
|
||||||
|
@ -718,7 +718,7 @@ Function InstallTypesPage
|
||||||
StrCpy $OffsetUnits u
|
StrCpy $OffsetUnits u
|
||||||
StrCpy $Express "0"
|
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
|
pop $ExpressInstallRadioButton
|
||||||
${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel
|
${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel
|
||||||
IntOp $CurrentOffset $CurrentOffset + 15
|
IntOp $CurrentOffset $CurrentOffset + 15
|
||||||
|
@ -1346,6 +1346,7 @@ Section "-Core installation"
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
${If} @SERVER_COMPONENT_CONDITIONAL@
|
${If} @SERVER_COMPONENT_CONDITIONAL@
|
||||||
|
${AndIf} $Express != "1"
|
||||||
; handling for server console shortcut
|
; handling for server console shortcut
|
||||||
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk"
|
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk"
|
||||||
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_HF_SHORTCUT_NAME@.lnk" \
|
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_HF_SHORTCUT_NAME@.lnk" \
|
||||||
|
|
|
@ -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",
|
"name": "entity_server_settings",
|
||||||
"label": "Entities",
|
"label": "Entities",
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <SettingHandle.h>
|
#include <SettingHandle.h>
|
||||||
#include <SettingHelpers.h>
|
#include <SettingHelpers.h>
|
||||||
#include <FingerprintUtils.h>
|
#include <FingerprintUtils.h>
|
||||||
|
#include <ModerationFlags.h>
|
||||||
|
|
||||||
#include "DomainServerNodeData.h"
|
#include "DomainServerNodeData.h"
|
||||||
|
|
||||||
|
@ -863,6 +864,20 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
||||||
// pull the UUID being kicked from the packet
|
// pull the UUID being kicked from the packet
|
||||||
QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
|
bool hasOptionalBanParameters = false;
|
||||||
|
int banParameters;
|
||||||
|
bool banByUsername;
|
||||||
|
bool banByFingerprint;
|
||||||
|
bool banByIP;
|
||||||
|
// pull optional ban parameters from the packet
|
||||||
|
if (message.data()->getSize() == (NUM_BYTES_RFC4122_UUID + sizeof(int))) {
|
||||||
|
hasOptionalBanParameters = true;
|
||||||
|
message->readPrimitive(&banParameters);
|
||||||
|
banByUsername = banParameters & ModerationFlags::BanFlags::BAN_BY_USERNAME;
|
||||||
|
banByFingerprint = banParameters & ModerationFlags::BanFlags::BAN_BY_FINGERPRINT;
|
||||||
|
banByIP = banParameters & ModerationFlags::BanFlags::BAN_BY_IP;
|
||||||
|
}
|
||||||
|
|
||||||
if (!nodeUUID.isNull() && nodeUUID != sendingNode->getUUID()) {
|
if (!nodeUUID.isNull() && nodeUUID != sendingNode->getUUID()) {
|
||||||
// make sure we actually have a node with this UUID
|
// make sure we actually have a node with this UUID
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
@ -881,16 +896,20 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
||||||
if (!verifiedUsername.isEmpty()) {
|
if (!verifiedUsername.isEmpty()) {
|
||||||
// if we have a verified user name for this user, we first apply the kick to the username
|
// if we have a verified user name for this user, we first apply the kick to the username
|
||||||
|
|
||||||
// check if there were already permissions
|
// if we have optional ban parameters, we should ban the username based on the parameter
|
||||||
bool hadPermissions = havePermissionsForName(verifiedUsername);
|
if (!hasOptionalBanParameters || banByUsername) {
|
||||||
|
// check if there were already permissions
|
||||||
|
bool hadPermissions = havePermissionsForName(verifiedUsername);
|
||||||
|
|
||||||
// grab or create permissions for the given username
|
// grab or create permissions for the given username
|
||||||
auto userPermissions = _agentPermissions[matchingNode->getPermissions().getKey()];
|
auto userPermissions = _agentPermissions[matchingNode->getPermissions().getKey()];
|
||||||
|
|
||||||
newPermissions = !hadPermissions || userPermissions->can(NodePermissions::Permission::canConnectToDomain);
|
newPermissions =
|
||||||
|
!hadPermissions || userPermissions->can(NodePermissions::Permission::canConnectToDomain);
|
||||||
|
|
||||||
// ensure that the connect permission is clear
|
// ensure that the connect permission is clear
|
||||||
userPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
userPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we didn't have a username, or this domain-server uses the "multi-kick" setting to
|
// if we didn't have a username, or this domain-server uses the "multi-kick" setting to
|
||||||
|
@ -898,7 +917,7 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
||||||
// then we remove connect permissions for the machine fingerprint (or IP as fallback)
|
// then we remove connect permissions for the machine fingerprint (or IP as fallback)
|
||||||
const QString MULTI_KICK_SETTINGS_KEYPATH = "security.multi_kick_logged_in";
|
const QString MULTI_KICK_SETTINGS_KEYPATH = "security.multi_kick_logged_in";
|
||||||
|
|
||||||
if (verifiedUsername.isEmpty() || valueOrDefaultValueForKeyPath(MULTI_KICK_SETTINGS_KEYPATH).toBool()) {
|
if (banByFingerprint || verifiedUsername.isEmpty() || valueOrDefaultValueForKeyPath(MULTI_KICK_SETTINGS_KEYPATH).toBool()) {
|
||||||
// remove connect permissions for the machine fingerprint
|
// remove connect permissions for the machine fingerprint
|
||||||
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
|
@ -923,36 +942,39 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
||||||
fingerprintPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
fingerprintPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if no node data, all we can do is IP address
|
// if no node data, all we can do is ban by IP address
|
||||||
auto& kickAddress = matchingNode->getActiveSocket()
|
banByIP = true;
|
||||||
? matchingNode->getActiveSocket()->getAddress()
|
}
|
||||||
: matchingNode->getPublicSocket().getAddress();
|
}
|
||||||
|
|
||||||
|
if (banByIP) {
|
||||||
|
auto& kickAddress = matchingNode->getActiveSocket()
|
||||||
|
? matchingNode->getActiveSocket()->getAddress()
|
||||||
|
: matchingNode->getPublicSocket().getAddress();
|
||||||
|
|
||||||
// probably isLoopback covers it, as whenever I try to ban an agent on same machine as the domain-server
|
// probably isLoopback covers it, as whenever I try to ban an agent on same machine as the domain-server
|
||||||
// it is always 127.0.0.1, but looking at the public and local addresses just to be sure
|
// it is always 127.0.0.1, but looking at the public and local addresses just to be sure
|
||||||
// TODO: soon we will have feedback (in the form of a message to the client) after we kick. When we
|
// TODO: soon we will have feedback (in the form of a message to the client) after we kick. When we
|
||||||
// do, we will have a success flag, and perhaps a reason for failure. For now, just don't do it.
|
// do, we will have a success flag, and perhaps a reason for failure. For now, just don't do it.
|
||||||
if (kickAddress == limitedNodeList->getPublicSockAddr().getAddress() ||
|
if (kickAddress == limitedNodeList->getPublicSockAddr().getAddress() ||
|
||||||
kickAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
|
kickAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
|
||||||
kickAddress.isLoopback() ) {
|
kickAddress.isLoopback() ) {
|
||||||
qWarning() << "attempt to kick node running on same machine as domain server, ignoring KickRequest";
|
qWarning() << "attempt to kick node running on same machine as domain server, ignoring KickRequest";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NodePermissionsKey ipAddressKey(kickAddress.toString(), QUuid());
|
||||||
|
|
||||||
NodePermissionsKey ipAddressKey(kickAddress.toString(), QUuid());
|
// check if there were already permissions for the IP
|
||||||
|
bool hadIPPermissions = hasPermissionsForIP(kickAddress);
|
||||||
|
|
||||||
// check if there were already permissions for the IP
|
// grab or create permissions for the given IP address
|
||||||
bool hadIPPermissions = hasPermissionsForIP(kickAddress);
|
auto ipPermissions = _ipPermissions[ipAddressKey];
|
||||||
|
|
||||||
// grab or create permissions for the given IP address
|
if (!hadIPPermissions || ipPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
|
||||||
auto ipPermissions = _ipPermissions[ipAddressKey];
|
newPermissions = true;
|
||||||
|
|
||||||
if (!hadIPPermissions || ipPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
|
ipPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
||||||
newPermissions = true;
|
|
||||||
|
|
||||||
ipPermissions->clear(NodePermissions::Permission::canConnectToDomain);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -506,6 +506,7 @@ Rectangle {
|
||||||
id: itemCell;
|
id: itemCell;
|
||||||
property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore";
|
property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore";
|
||||||
property bool isButton: styleData.role === "mute" || styleData.role === "kick";
|
property bool isButton: styleData.role === "mute" || styleData.role === "kick";
|
||||||
|
property bool isBan: styleData.role === "kick";
|
||||||
property bool isAvgAudio: styleData.role === "avgAudioLevel";
|
property bool isAvgAudio: styleData.role === "avgAudioLevel";
|
||||||
opacity: !isButton ? (model && model.isPresent ? 1.0 : 0.4) : 1.0; // Admin actions shouldn't turn gray
|
opacity: !isButton ? (model && model.isPresent ? 1.0 : 0.4) : 1.0; // Admin actions shouldn't turn gray
|
||||||
|
|
||||||
|
@ -605,7 +606,9 @@ Rectangle {
|
||||||
color: 2; // Red
|
color: 2; // Red
|
||||||
visible: isButton;
|
visible: isButton;
|
||||||
enabled: !nameCard.isReplicated;
|
enabled: !nameCard.isReplicated;
|
||||||
anchors.centerIn: parent;
|
anchors.verticalCenter: itemCell.verticalCenter;
|
||||||
|
anchors.left: parent.left;
|
||||||
|
anchors.leftMargin: styleData.role === "kick" ? 4 : 18;
|
||||||
width: 32;
|
width: 32;
|
||||||
height: 32;
|
height: 32;
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
@ -620,7 +623,39 @@ Rectangle {
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
text: (styleData.role === "kick") ? hifi.glyphs.error : hifi.glyphs.muted;
|
text: (styleData.role === "kick") ? hifi.glyphs.error : hifi.glyphs.muted;
|
||||||
// Size
|
// Size
|
||||||
size: parent.height*1.3;
|
size: parent.height * 1.3;
|
||||||
|
// Anchors
|
||||||
|
anchors.fill: parent;
|
||||||
|
// Style
|
||||||
|
horizontalAlignment: Text.AlignHCenter;
|
||||||
|
color: enabled ? hifi.buttons.textColor[actionButton.color]
|
||||||
|
: hifi.buttons.disabledTextColor[actionButton.colorScheme];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControlsUit.Button {
|
||||||
|
id: hardBanButton;
|
||||||
|
color: 2; // Red
|
||||||
|
visible: isBan;
|
||||||
|
enabled: !nameCard.isReplicated;
|
||||||
|
anchors.verticalCenter: itemCell.verticalCenter;
|
||||||
|
anchors.left: parent.left;
|
||||||
|
anchors.leftMargin: actionButton.width + 14;
|
||||||
|
width: 32;
|
||||||
|
height: 32;
|
||||||
|
onClicked: {
|
||||||
|
Users[styleData.role](model.sessionId, Users.BAN_BY_USERNAME | Users.BAN_BY_FINGERPRINT | Users.BAN_BY_IP);
|
||||||
|
UserActivityLogger["palAction"](styleData.role, model.sessionId);
|
||||||
|
if (styleData.role === "kick") {
|
||||||
|
nearbyUserModelData.splice(model.userIndex, 1);
|
||||||
|
nearbyUserModel.remove(model.userIndex); // after changing nearbyUserModelData, b/c ListModel can frob the data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// muted/error glyphs
|
||||||
|
HiFiGlyphs {
|
||||||
|
text: hifi.glyphs.alert;
|
||||||
|
// Size
|
||||||
|
size: parent.height * 1.3;
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.fill: parent;
|
anchors.fill: parent;
|
||||||
// Style
|
// Style
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
import QtQuick.Controls 2.2
|
import QtQuick.Controls 2.2
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
import stylesUit 1.0
|
import stylesUit 1.0
|
||||||
import controlsUit 1.0 as HifiControlsUit
|
import controlsUit 1.0 as HifiControlsUit
|
||||||
|
@ -104,6 +105,11 @@ Rectangle {
|
||||||
AudioScriptingInterface.setSystemInjectorGain(sliderValue);
|
AudioScriptingInterface.setSystemInjectorGain(sliderValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function updateNoiseReductionThresholdFromQML(sliderValue) {
|
||||||
|
if (AudioScriptingInterface.getNoiseReductionThreshold() !== sliderValue) {
|
||||||
|
AudioScriptingInterface.setNoiseReductionThreshold(sliderValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
enablePeakValues();
|
enablePeakValues();
|
||||||
|
@ -164,7 +170,7 @@ Rectangle {
|
||||||
x: 2 * margins.paddings;
|
x: 2 * margins.paddings;
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
// switch heights + 2 * top margins
|
// 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.top: firstSeparator.bottom;
|
||||||
anchors.topMargin: 10;
|
anchors.topMargin: 10;
|
||||||
|
|
||||||
|
@ -175,6 +181,7 @@ Rectangle {
|
||||||
height: parent.height;
|
height: parent.height;
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left;
|
anchors.left: parent.left;
|
||||||
|
|
||||||
HifiControlsUit.Switch {
|
HifiControlsUit.Switch {
|
||||||
id: muteMic;
|
id: muteMic;
|
||||||
height: root.switchHeight;
|
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 {
|
HifiControlsUit.Switch {
|
||||||
id: pttSwitch
|
id: pttSwitch
|
||||||
height: root.switchHeight;
|
height: root.switchHeight;
|
||||||
switchWidth: root.switchWidth;
|
switchWidth: root.switchWidth;
|
||||||
anchors.top: acousticEchoCancellationSwitch.bottom;
|
anchors.top: muteMic.bottom;
|
||||||
anchors.topMargin: 24
|
anchors.topMargin: 24
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk");
|
labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk");
|
||||||
|
@ -254,6 +227,7 @@ Rectangle {
|
||||||
height: parent.height;
|
height: parent.height;
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: switchContainer.right;
|
anchors.left: switchContainer.right;
|
||||||
|
|
||||||
HifiControlsUit.Switch {
|
HifiControlsUit.Switch {
|
||||||
id: warnMutedSwitch
|
id: warnMutedSwitch
|
||||||
height: root.switchHeight;
|
height: root.switchHeight;
|
||||||
|
@ -271,7 +245,6 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HifiControlsUit.Switch {
|
HifiControlsUit.Switch {
|
||||||
id: audioLevelSwitch
|
id: audioLevelSwitch
|
||||||
height: root.switchHeight;
|
height: root.switchHeight;
|
||||||
|
@ -519,13 +492,282 @@ Rectangle {
|
||||||
anchors.top: systemInjectorGainContainer.bottom;
|
anchors.top: systemInjectorGainContainer.bottom;
|
||||||
anchors.topMargin: 10;
|
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 {
|
Item {
|
||||||
id: inputDeviceHeader
|
id: inputDeviceHeader
|
||||||
x: margins.paddings;
|
x: margins.paddings;
|
||||||
width: parent.width - margins.paddings*2;
|
width: parent.width - margins.paddings*2;
|
||||||
height: 36;
|
height: 36;
|
||||||
anchors.top: secondSeparator.bottom;
|
anchors.top: thirdSeparator.bottom;
|
||||||
anchors.topMargin: 10;
|
anchors.topMargin: 10;
|
||||||
|
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
|
@ -597,19 +839,19 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AudioControls.InputPeak {
|
AudioControls.InputPeak {
|
||||||
id: inputLevel
|
id: inputLevel
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
peak: model.peak;
|
peak: model.peak;
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
visible: ((bar.currentIndex === 1 && isVR) ||
|
visible: ((bar.currentIndex === 1 && isVR) ||
|
||||||
(bar.currentIndex === 0 && !isVR)) &&
|
(bar.currentIndex === 0 && !isVR)) &&
|
||||||
AudioScriptingInterface.devices.input.peakValuesAvailable;
|
AudioScriptingInterface.devices.input.peakValuesAvailable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Separator {
|
Separator {
|
||||||
id: thirdSeparator;
|
id: fourthSeparator;
|
||||||
anchors.top: inputView.bottom;
|
anchors.top: inputView.bottom;
|
||||||
anchors.topMargin: 10;
|
anchors.topMargin: 10;
|
||||||
}
|
}
|
||||||
|
@ -617,7 +859,7 @@ Rectangle {
|
||||||
Item {
|
Item {
|
||||||
id: outputDeviceHeader;
|
id: outputDeviceHeader;
|
||||||
anchors.topMargin: 10;
|
anchors.topMargin: 10;
|
||||||
anchors.top: thirdSeparator.bottom;
|
anchors.top: fourthSeparator.bottom;
|
||||||
x: margins.paddings;
|
x: margins.paddings;
|
||||||
width: parent.width - margins.paddings*2
|
width: parent.width - margins.paddings*2
|
||||||
height: 36
|
height: 36
|
||||||
|
@ -684,4 +926,4 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2493,7 +2493,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
return viewFrustum.getPosition();
|
return viewFrustum.getPosition();
|
||||||
});
|
});
|
||||||
|
|
||||||
DependencyManager::get<UsersScriptingInterface>()->setKickConfirmationOperator([this] (const QUuid& nodeID) { userKickConfirmation(nodeID); });
|
DependencyManager::get<UsersScriptingInterface>()->setKickConfirmationOperator([this] (const QUuid& nodeID, unsigned int banFlags) { userKickConfirmation(nodeID, banFlags); });
|
||||||
|
|
||||||
render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([=](const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([=](const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
||||||
bool isTablet = url == TabletScriptingInterface::QML;
|
bool isTablet = url == TabletScriptingInterface::QML;
|
||||||
|
@ -2989,6 +2989,8 @@ Application::~Application() {
|
||||||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#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
|
// 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).
|
// fully understood whether it might cause other unknown crashes (B).
|
||||||
//
|
//
|
||||||
|
@ -3573,7 +3575,7 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
|
||||||
_desktopRootItemCreated = true;
|
_desktopRootItemCreated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::userKickConfirmation(const QUuid& nodeID) {
|
void Application::userKickConfirmation(const QUuid& nodeID, unsigned int banFlags) {
|
||||||
auto avatarHashMap = DependencyManager::get<AvatarHashMap>();
|
auto avatarHashMap = DependencyManager::get<AvatarHashMap>();
|
||||||
auto avatar = avatarHashMap->getAvatarBySessionID(nodeID);
|
auto avatar = avatarHashMap->getAvatarBySessionID(nodeID);
|
||||||
|
|
||||||
|
@ -3598,7 +3600,7 @@ void Application::userKickConfirmation(const QUuid& nodeID) {
|
||||||
// ask the NodeList to kick the user with the given session ID
|
// ask the NodeList to kick the user with the given session ID
|
||||||
|
|
||||||
if (yes) {
|
if (yes) {
|
||||||
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID);
|
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID, banFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
DependencyManager::get<UsersScriptingInterface>()->setWaitForKickResponse(false);
|
DependencyManager::get<UsersScriptingInterface>()->setWaitForKickResponse(false);
|
||||||
|
@ -4013,7 +4015,7 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
||||||
|
|
||||||
// If this is a first run we short-circuit the address passed in
|
// If this is a first run we short-circuit the address passed in
|
||||||
if (_firstRun.get()) {
|
if (_firstRun.get()) {
|
||||||
if (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) {
|
if (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty()) {
|
||||||
DependencyManager::get<LocationBookmarks>()->setHomeLocationToAddress(NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS);
|
DependencyManager::get<LocationBookmarks>()->setHomeLocationToAddress(NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS);
|
||||||
Menu::getInstance()->triggerOption(MenuOption::HomeLocation);
|
Menu::getInstance()->triggerOption(MenuOption::HomeLocation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
#include <shared/ConicalViewFrustum.h>
|
#include <shared/ConicalViewFrustum.h>
|
||||||
#include <shared/FileLogger.h>
|
#include <shared/FileLogger.h>
|
||||||
#include <RunningMarker.h>
|
#include <RunningMarker.h>
|
||||||
|
#include <ModerationFlags.h>
|
||||||
|
|
||||||
#include "avatar/MyAvatar.h"
|
#include "avatar/MyAvatar.h"
|
||||||
#include "FancyCamera.h"
|
#include "FancyCamera.h"
|
||||||
|
@ -608,7 +609,7 @@ private:
|
||||||
void toggleTabletUI(bool shouldOpen = false) const;
|
void toggleTabletUI(bool shouldOpen = false) const;
|
||||||
bool shouldCaptureMouse() const;
|
bool shouldCaptureMouse() const;
|
||||||
|
|
||||||
void userKickConfirmation(const QUuid& nodeID);
|
void userKickConfirmation(const QUuid& nodeID, unsigned int banFlags = ModerationFlags::getDefaultBanFlags());
|
||||||
|
|
||||||
MainWindow* _window;
|
MainWindow* _window;
|
||||||
QElapsedTimer& _sessionRunTimer;
|
QElapsedTimer& _sessionRunTimer;
|
||||||
|
|
|
@ -556,8 +556,6 @@ Menu::Menu() {
|
||||||
});
|
});
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ToggleHipsFollowing, 0, false,
|
|
||||||
avatar.get(), SLOT(setToggleHips(bool)));
|
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBaseOfSupport, 0, false,
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBaseOfSupport, 0, false,
|
||||||
avatar.get(), SLOT(setEnableDebugDrawBaseOfSupport(bool)));
|
avatar.get(), SLOT(setEnableDebugDrawBaseOfSupport(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
|
||||||
|
|
|
@ -211,7 +211,6 @@ namespace MenuOption {
|
||||||
const QString ThirdPerson = "Third Person Legacy";
|
const QString ThirdPerson = "Third Person Legacy";
|
||||||
const QString ThreePointCalibration = "3 Point Calibration";
|
const QString ThreePointCalibration = "3 Point Calibration";
|
||||||
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp
|
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp
|
||||||
const QString ToggleHipsFollowing = "Toggle Hips Following";
|
|
||||||
const QString ToolWindow = "Tool Window";
|
const QString ToolWindow = "Tool Window";
|
||||||
const QString TransmitterDrive = "Transmitter Drive";
|
const QString TransmitterDrive = "Transmitter Drive";
|
||||||
const QString TurnWithHead = "Turn using Head";
|
const QString TurnWithHead = "Turn using Head";
|
||||||
|
|
|
@ -1393,15 +1393,9 @@ float loadSetting(Settings& settings, const QString& name, float defaultValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setToggleHips(bool followHead) {
|
void MyAvatar::setToggleHips(bool followHead) {
|
||||||
_follow.setToggleHipsFollowing(followHead);
|
Q_UNUSED(followHead);
|
||||||
}
|
qCDebug(interfaceapp) << "MyAvatar.setToggleHips is deprecated; it no longer does anything; it will soon be removed from the API; "
|
||||||
|
"please update your script";
|
||||||
void MyAvatar::FollowHelper::setToggleHipsFollowing(bool followHead) {
|
|
||||||
_toggleHipsFollowing = followHead;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MyAvatar::FollowHelper::getToggleHipsFollowing() const {
|
|
||||||
return _toggleHipsFollowing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setEnableDebugDrawBaseOfSupport(bool isEnabled) {
|
void MyAvatar::setEnableDebugDrawBaseOfSupport(bool isEnabled) {
|
||||||
|
@ -2030,7 +2024,6 @@ void MyAvatar::loadData() {
|
||||||
allowAvatarLeaningPreferenceStrings[static_cast<uint>(AllowAvatarLeaningPreference::Default)])));
|
allowAvatarLeaningPreferenceStrings[static_cast<uint>(AllowAvatarLeaningPreference::Default)])));
|
||||||
|
|
||||||
setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible));
|
setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible));
|
||||||
_follow.setToggleHipsFollowing (Menu::getInstance()->isOptionChecked(MenuOption::ToggleHipsFollowing));
|
|
||||||
setEnableDebugDrawBaseOfSupport(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBaseOfSupport));
|
setEnableDebugDrawBaseOfSupport(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBaseOfSupport));
|
||||||
setEnableDebugDrawDefaultPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawDefaultPose));
|
setEnableDebugDrawDefaultPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawDefaultPose));
|
||||||
setEnableDebugDrawAnimPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawAnimPose));
|
setEnableDebugDrawAnimPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawAnimPose));
|
||||||
|
@ -5514,7 +5507,7 @@ void MyAvatar::setSitStandStateChange(bool stateChanged) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if the user's real-world sit/stand state has changed.
|
// Determine if the user's real-world sit/stand state has changed.
|
||||||
float MyAvatar::getSitStandStateChange() const {
|
bool MyAvatar::getSitStandStateChange() const {
|
||||||
return _sitStandStateChange;
|
return _sitStandStateChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5696,7 +5689,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal_userSitting(const MyAvatar
|
||||||
bool stepDetected = false;
|
bool stepDetected = false;
|
||||||
if (forwardLeanAmount > MAX_FORWARD_LEAN) {
|
if (forwardLeanAmount > MAX_FORWARD_LEAN) {
|
||||||
stepDetected = true;
|
stepDetected = true;
|
||||||
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
|
} else if (forwardLeanAmount < -MAX_BACKWARD_LEAN) {
|
||||||
stepDetected = true;
|
stepDetected = true;
|
||||||
} else {
|
} else {
|
||||||
stepDetected = fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
|
stepDetected = fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
|
||||||
|
|
|
@ -1800,7 +1800,7 @@ public:
|
||||||
void setAnalogPlusSprintSpeed(float value);
|
void setAnalogPlusSprintSpeed(float value);
|
||||||
float getAnalogPlusSprintSpeed() const;
|
float getAnalogPlusSprintSpeed() const;
|
||||||
void setSitStandStateChange(bool stateChanged);
|
void setSitStandStateChange(bool stateChanged);
|
||||||
float getSitStandStateChange() const;
|
bool getSitStandStateChange() const;
|
||||||
void updateSitStandState(float newHeightReading, float dt);
|
void updateSitStandState(float newHeightReading, float dt);
|
||||||
|
|
||||||
QVector<QString> getScriptUrls();
|
QVector<QString> getScriptUrls();
|
||||||
|
@ -2907,8 +2907,6 @@ private:
|
||||||
void setForceActivateVertical(bool val);
|
void setForceActivateVertical(bool val);
|
||||||
bool getForceActivateHorizontal() const;
|
bool getForceActivateHorizontal() const;
|
||||||
void setForceActivateHorizontal(bool val);
|
void setForceActivateHorizontal(bool val);
|
||||||
bool getToggleHipsFollowing() const;
|
|
||||||
void setToggleHipsFollowing(bool followHead);
|
|
||||||
std::atomic<bool> _forceActivateRotation { false };
|
std::atomic<bool> _forceActivateRotation { false };
|
||||||
std::atomic<bool> _forceActivateVertical { false };
|
std::atomic<bool> _forceActivateVertical { false };
|
||||||
std::atomic<bool> _forceActivateHorizontal { false };
|
std::atomic<bool> _forceActivateHorizontal { false };
|
||||||
|
|
|
@ -25,6 +25,8 @@ QString Audio::DESKTOP { "Desktop" };
|
||||||
QString Audio::HMD { "VR" };
|
QString Audio::HMD { "VR" };
|
||||||
|
|
||||||
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
|
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
|
||||||
|
Setting::Handle<bool> enableNoiseReductionAutomaticSetting { QStringList { Audio::AUDIO, "NoiseReductionAutomatic" }, true };
|
||||||
|
Setting::Handle<float> setNoiseReductionThresholdSetting { QStringList { Audio::AUDIO, "NoiseReductionThreshold" }, 0.1f };
|
||||||
Setting::Handle<bool> enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true };
|
Setting::Handle<bool> enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true };
|
||||||
Setting::Handle<bool> enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true };
|
Setting::Handle<bool> enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true };
|
||||||
|
|
||||||
|
@ -40,6 +42,8 @@ Audio::Audio() : _devices(_contextIsHMD) {
|
||||||
auto client = DependencyManager::get<AudioClient>().data();
|
auto client = DependencyManager::get<AudioClient>().data();
|
||||||
connect(client, &AudioClient::muteToggled, this, &Audio::setMuted);
|
connect(client, &AudioClient::muteToggled, this, &Audio::setMuted);
|
||||||
connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction);
|
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::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted);
|
||||||
connect(client, &AudioClient::acousticEchoCancellationChanged, this, &Audio::enableAcousticEchoCancellation);
|
connect(client, &AudioClient::acousticEchoCancellationChanged, this, &Audio::enableAcousticEchoCancellation);
|
||||||
connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged);
|
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::contextChanged, &_devices, &AudioDevices::onContextChanged);
|
||||||
connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk);
|
connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk);
|
||||||
enableNoiseReduction(enableNoiseReductionSetting.get());
|
enableNoiseReduction(enableNoiseReductionSetting.get());
|
||||||
|
enableNoiseReductionAutomatic(enableNoiseReductionAutomaticSetting.get());
|
||||||
|
setNoiseReductionThreshold(setNoiseReductionThresholdSetting.get());
|
||||||
enableWarnWhenMuted(enableWarnWhenMutedSetting.get());
|
enableWarnWhenMuted(enableWarnWhenMutedSetting.get());
|
||||||
enableAcousticEchoCancellation(enableAcousticEchoCancellationSetting.get());
|
enableAcousticEchoCancellation(enableAcousticEchoCancellationSetting.get());
|
||||||
onContextChanged();
|
onContextChanged();
|
||||||
|
@ -286,6 +292,50 @@ void Audio::enableNoiseReduction(bool enable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Audio::noiseReductionAutomatic() const {
|
||||||
|
return resultWithReadLock<bool>([&] {
|
||||||
|
return _noiseReductionAutomatic;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::enableNoiseReductionAutomatic(bool enable) {
|
||||||
|
bool changed = false;
|
||||||
|
withWriteLock([&] {
|
||||||
|
if (_noiseReductionAutomatic != enable) {
|
||||||
|
_noiseReductionAutomatic = enable;
|
||||||
|
auto client = DependencyManager::get<AudioClient>().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<float>([&] {
|
||||||
|
return _noiseReductionThreshold;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::setNoiseReductionThreshold(float threshold) {
|
||||||
|
bool changed = false;
|
||||||
|
withWriteLock([&] {
|
||||||
|
if (_noiseReductionThreshold != threshold) {
|
||||||
|
_noiseReductionThreshold = threshold;
|
||||||
|
auto client = DependencyManager::get<AudioClient>().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 {
|
bool Audio::warnWhenMutedEnabled() const {
|
||||||
return resultWithReadLock<bool>([&] {
|
return resultWithReadLock<bool>([&] {
|
||||||
return _enableWarnWhenMuted;
|
return _enableWarnWhenMuted;
|
||||||
|
|
|
@ -50,6 +50,11 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
||||||
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
|
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
|
||||||
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
|
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
|
||||||
* above the noise floor.
|
* above the noise floor.
|
||||||
|
* @property {boolean} noiseReductionAutomatic - <code>true</code> if audio input noise reduction automatic mode is enabled,
|
||||||
|
* <code>false</code> if in manual mode. Manual mode allows you to use <code>Audio.noiseReductionThreshold</code>
|
||||||
|
* 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 <code>Audio.noiseReductionAutomatic</code> is <code>false</code>.)
|
||||||
* @property {number} inputVolume - Adjusts the volume of the input audio, range <code>0.0</code> – <code>1.0</code>.
|
* @property {number} inputVolume - Adjusts the volume of the input audio, range <code>0.0</code> – <code>1.0</code>.
|
||||||
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
|
* 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 <code>0.0</code> and <code>1.0</code>.
|
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
|
||||||
|
@ -92,6 +97,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
||||||
|
|
||||||
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
|
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
|
||||||
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
|
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 warnWhenMuted READ warnWhenMutedEnabled WRITE enableWarnWhenMuted NOTIFY warnWhenMutedChanged)
|
||||||
Q_PROPERTY(bool acousticEchoCancellation
|
Q_PROPERTY(bool acousticEchoCancellation
|
||||||
READ acousticEchoCancellationEnabled WRITE enableAcousticEchoCancellation NOTIFY acousticEchoCancellationChanged)
|
READ acousticEchoCancellationEnabled WRITE enableAcousticEchoCancellation NOTIFY acousticEchoCancellationChanged)
|
||||||
|
@ -124,6 +131,7 @@ public:
|
||||||
|
|
||||||
bool isMuted() const;
|
bool isMuted() const;
|
||||||
bool noiseReductionEnabled() const;
|
bool noiseReductionEnabled() const;
|
||||||
|
bool noiseReductionAutomatic() const;
|
||||||
bool warnWhenMutedEnabled() const;
|
bool warnWhenMutedEnabled() const;
|
||||||
bool acousticEchoCancellationEnabled() const;
|
bool acousticEchoCancellationEnabled() const;
|
||||||
float getInputVolume() const;
|
float getInputVolume() const;
|
||||||
|
@ -270,6 +278,20 @@ public:
|
||||||
* @returns {number} The injector gain (dB) in the client.
|
* @returns {number} The injector gain (dB) in the client.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE float getSystemInjectorGain();
|
Q_INVOKABLE float getSystemInjectorGain();
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Sets the noise gate threshold before your mic audio is transmitted. (Applies only if <code>Audio.noiseReductionAutomatic</code> is <code>false</code>.)
|
||||||
|
* @function Audio.setNoiseReductionThreshold
|
||||||
|
* @param {number} threshold - The level that your input must surpass to be transmitted. <code>0.0</code> (open the gate completely) – <code>1.0</code>
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void setNoiseReductionThreshold(float threshold);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Gets the noise reduction threshold.
|
||||||
|
* @function Audio.getNoiseReductionThreshold
|
||||||
|
* @returns {number} The noise reduction threshold. <code>0.0</code> – <code>1.0</code>
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE float getNoiseReductionThreshold();
|
||||||
|
|
||||||
/**jsdoc
|
/**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.
|
* 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}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void noiseReductionChanged(bool isEnabled);
|
void noiseReductionChanged(bool isEnabled);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Triggered when the audio input noise reduction mode is changed.
|
||||||
|
* @function Audio.noiseReductionAutomaticChanged
|
||||||
|
* @param {boolean} isEnabled - <code>true</code> if audio input noise reduction automatic mode is enabled, <code>false</code> 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 <code>0.0</code> (open the gate completely) – <code>1.0</code>
|
||||||
|
* (close the gate completely).
|
||||||
|
* @returns {Signal}
|
||||||
|
*/
|
||||||
|
void noiseReductionThresholdChanged(float threshold);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Triggered when "warn when muted" is enabled or disabled.
|
* Triggered when "warn when muted" is enabled or disabled.
|
||||||
|
@ -512,6 +551,7 @@ public slots:
|
||||||
private slots:
|
private slots:
|
||||||
void setMuted(bool muted);
|
void setMuted(bool muted);
|
||||||
void enableNoiseReduction(bool enable);
|
void enableNoiseReduction(bool enable);
|
||||||
|
void enableNoiseReductionAutomatic(bool enable);
|
||||||
void enableWarnWhenMuted(bool enable);
|
void enableWarnWhenMuted(bool enable);
|
||||||
void enableAcousticEchoCancellation(bool enable);
|
void enableAcousticEchoCancellation(bool enable);
|
||||||
void setInputVolume(float volume);
|
void setInputVolume(float volume);
|
||||||
|
@ -533,8 +573,10 @@ private:
|
||||||
float _localInjectorGain { 0.0f }; // in dB
|
float _localInjectorGain { 0.0f }; // in dB
|
||||||
float _systemInjectorGain { 0.0f }; // in dB
|
float _systemInjectorGain { 0.0f }; // in dB
|
||||||
float _pttOutputGainDesktop { 0.0f }; // in dB
|
float _pttOutputGainDesktop { 0.0f }; // in dB
|
||||||
|
float _noiseReductionThreshold { 0.1f }; // Match default value of AudioClient::_noiseReductionThreshold.
|
||||||
bool _isClipping { false };
|
bool _isClipping { false };
|
||||||
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
|
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
|
||||||
|
bool _noiseReductionAutomatic { true }; // Match default value of AudioClient::_noiseReductionAutomatic.
|
||||||
bool _enableWarnWhenMuted { true };
|
bool _enableWarnWhenMuted { true };
|
||||||
bool _enableAcousticEchoCancellation { true }; // AudioClient::_isAECEnabled
|
bool _enableAcousticEchoCancellation { true }; // AudioClient::_isAECEnabled
|
||||||
bool _contextIsHMD { false };
|
bool _contextIsHMD { false };
|
||||||
|
|
|
@ -250,9 +250,9 @@ void setupPreferences() {
|
||||||
{
|
{
|
||||||
auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); };
|
auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); };
|
||||||
auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); };
|
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, "
|
"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));
|
"you are helping to improve the product. ", getter, setter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +364,7 @@ void setupPreferences() {
|
||||||
auto preference = new SpinnerSliderPreference(VR_MOVEMENT, "Camera Sensitivity", getter, setter);
|
auto preference = new SpinnerSliderPreference(VR_MOVEMENT, "Camera Sensitivity", getter, setter);
|
||||||
preference->setMin(0.01f);
|
preference->setMin(0.01f);
|
||||||
preference->setMax(5.0f);
|
preference->setMax(5.0f);
|
||||||
preference->setStep(0.1);
|
preference->setStep(0.1f);
|
||||||
preference->setDecimals(2);
|
preference->setDecimals(2);
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
#include "CubicHermiteSpline.h"
|
#include "CubicHermiteSpline.h"
|
||||||
#include "AnimUtil.h"
|
#include "AnimUtil.h"
|
||||||
|
#include "AnimSkeleton.h"
|
||||||
|
|
||||||
static const int MAX_TARGET_MARKERS = 30;
|
static const int MAX_TARGET_MARKERS = 30;
|
||||||
static const float JOINT_CHAIN_INTERP_TIME = 0.5f;
|
static const float JOINT_CHAIN_INTERP_TIME = 0.5f;
|
||||||
|
@ -66,7 +67,7 @@ AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, cons
|
||||||
poleVectorVar(poleVectorVarIn),
|
poleVectorVar(poleVectorVarIn),
|
||||||
weight(weightIn),
|
weight(weightIn),
|
||||||
numFlexCoefficients(flexCoefficientsIn.size()),
|
numFlexCoefficients(flexCoefficientsIn.size()),
|
||||||
jointIndex(-1)
|
jointIndex(AnimSkeleton::INVALID_JOINT_INDEX)
|
||||||
{
|
{
|
||||||
numFlexCoefficients = std::min(numFlexCoefficients, (size_t)MAX_FLEX_COEFFICIENTS);
|
numFlexCoefficients = std::min(numFlexCoefficients, (size_t)MAX_FLEX_COEFFICIENTS);
|
||||||
for (size_t i = 0; i < numFlexCoefficients; i++) {
|
for (size_t i = 0; i < numFlexCoefficients; i++) {
|
||||||
|
@ -172,14 +173,14 @@ bool debounceJointWarnings() {
|
||||||
|
|
||||||
void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses) {
|
void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses) {
|
||||||
|
|
||||||
_hipsTargetIndex = -1;
|
_hipsTargetIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||||
|
|
||||||
targets.reserve(_targetVarVec.size());
|
targets.reserve(_targetVarVec.size());
|
||||||
|
|
||||||
for (auto& targetVar : _targetVarVec) {
|
for (auto& targetVar : _targetVarVec) {
|
||||||
|
|
||||||
// update targetVar jointIndex cache
|
// update targetVar jointIndex cache
|
||||||
if (targetVar.jointIndex == -1) {
|
if (targetVar.jointIndex == AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName);
|
int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName);
|
||||||
if (jointIndex >= 0) {
|
if (jointIndex >= 0) {
|
||||||
// this targetVar has a valid joint --> cache the indices
|
// this targetVar has a valid joint --> cache the indices
|
||||||
|
@ -190,7 +191,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
|
||||||
}
|
}
|
||||||
|
|
||||||
IKTarget target;
|
IKTarget target;
|
||||||
if (targetVar.jointIndex != -1) {
|
if (targetVar.jointIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition));
|
target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition));
|
||||||
target.setIndex(targetVar.jointIndex);
|
target.setIndex(targetVar.jointIndex);
|
||||||
if (target.getType() != IKTarget::Type::Unknown) {
|
if (target.getType() != IKTarget::Type::Unknown) {
|
||||||
|
@ -329,7 +330,7 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
|
||||||
// update the absolutePoses
|
// update the absolutePoses
|
||||||
for (int i = 0; i < (int)_relativePoses.size(); ++i) {
|
for (int i = 0; i < (int)_relativePoses.size(); ++i) {
|
||||||
auto parentIndex = _skeleton->getParentIndex((int)i);
|
auto parentIndex = _skeleton->getParentIndex((int)i);
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
|
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
|
// finally set the relative rotation of each tip to agree with absolute target rotation
|
||||||
for (auto& target: targets) {
|
for (auto& target: targets) {
|
||||||
int tipIndex = target.getIndex();
|
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];
|
int chainIndex = targetToChainMap[tipIndex];
|
||||||
bool needsInterpolation = _prevJointChainInfoVec[chainIndex].timer > 0.0f;
|
bool needsInterpolation = _prevJointChainInfoVec[chainIndex].timer > 0.0f;
|
||||||
float alpha = needsInterpolation ? getInterpolationAlpha(_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.
|
// 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)) {
|
(target.getType() == IKTarget::Type::RotationOnly || target.getType() == IKTarget::Type::Unknown)) {
|
||||||
if (target.getType() == IKTarget::Type::RotationOnly) {
|
if (target.getType() == IKTarget::Type::RotationOnly) {
|
||||||
const glm::quat& targetRotation = target.getRotation();
|
const glm::quat& targetRotation = target.getRotation();
|
||||||
|
@ -434,11 +435,11 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
|
|
||||||
int tipIndex = target.getIndex();
|
int tipIndex = target.getIndex();
|
||||||
int pivotIndex = _skeleton->getParentIndex(tipIndex);
|
int pivotIndex = _skeleton->getParentIndex(tipIndex);
|
||||||
if (pivotIndex == -1 || pivotIndex == _hipsIndex) {
|
if (pivotIndex == AnimSkeleton::INVALID_JOINT_INDEX || pivotIndex == _hipsIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
|
int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
|
||||||
if (pivotsParentIndex == -1) {
|
if (pivotsParentIndex == AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
// TODO?: handle case where tip's parent is root?
|
// TODO?: handle case where tip's parent is root?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -485,7 +486,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
chainDepth++;
|
chainDepth++;
|
||||||
|
|
||||||
// descend toward root, pivoting each joint to get tip closer to target position
|
// 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());
|
assert(chainDepth < jointChainInfoOut.jointInfoVec.size());
|
||||||
|
|
||||||
|
@ -579,12 +580,12 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
if (target.getPoleVectorEnabled()) {
|
if (target.getPoleVectorEnabled()) {
|
||||||
int topJointIndex = target.getIndex();
|
int topJointIndex = target.getIndex();
|
||||||
int midJointIndex = _skeleton->getParentIndex(topJointIndex);
|
int midJointIndex = _skeleton->getParentIndex(topJointIndex);
|
||||||
if (midJointIndex != -1) {
|
if (midJointIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
int baseJointIndex = _skeleton->getParentIndex(midJointIndex);
|
int baseJointIndex = _skeleton->getParentIndex(midJointIndex);
|
||||||
if (baseJointIndex != -1) {
|
if (baseJointIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex);
|
int baseParentJointIndex = _skeleton->getParentIndex(baseJointIndex);
|
||||||
AnimPose topPose, midPose, basePose;
|
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;
|
const size_t MAX_CHAIN_DEPTH = 30;
|
||||||
AnimPose postAbsPoses[MAX_CHAIN_DEPTH];
|
AnimPose postAbsPoses[MAX_CHAIN_DEPTH];
|
||||||
AnimPose accum = absolutePoses[_hipsIndex];
|
AnimPose accum = absolutePoses[_hipsIndex];
|
||||||
|
@ -756,7 +757,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani
|
||||||
|
|
||||||
int index = target.getIndex();
|
int index = target.getIndex();
|
||||||
int endIndex = _skeleton->getParentIndex(_hipsIndex);
|
int endIndex = _skeleton->getParentIndex(_hipsIndex);
|
||||||
while (index != endIndex) {
|
while (index != endIndex && index != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index);
|
AnimPose defaultPose = _skeleton->getAbsoluteDefaultPose(index);
|
||||||
|
|
||||||
float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength;
|
float ratio = glm::dot(defaultPose.trans() - basePose.trans(), baseToTipNormal) / baseToTipLength;
|
||||||
|
@ -1460,7 +1461,7 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
|
||||||
|
|
||||||
// invalidate all targetVars
|
// invalidate all targetVars
|
||||||
for (auto& targetVar: _targetVarVec) {
|
for (auto& targetVar: _targetVarVec) {
|
||||||
targetVar.jointIndex = -1;
|
targetVar.jointIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& accumulator: _rotationAccumulators) {
|
for (auto& accumulator: _rotationAccumulators) {
|
||||||
|
@ -1480,18 +1481,18 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
|
||||||
if (_hipsIndex >= 0) {
|
if (_hipsIndex >= 0) {
|
||||||
_hipsParentIndex = _skeleton->getParentIndex(_hipsIndex);
|
_hipsParentIndex = _skeleton->getParentIndex(_hipsIndex);
|
||||||
} else {
|
} else {
|
||||||
_hipsParentIndex = -1;
|
_hipsParentIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
_leftHandIndex = _skeleton->nameToJointIndex("LeftHand");
|
_leftHandIndex = _skeleton->nameToJointIndex("LeftHand");
|
||||||
_rightHandIndex = _skeleton->nameToJointIndex("RightHand");
|
_rightHandIndex = _skeleton->nameToJointIndex("RightHand");
|
||||||
} else {
|
} else {
|
||||||
clearConstraints();
|
clearConstraints();
|
||||||
_headIndex = -1;
|
_headIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||||
_hipsIndex = -1;
|
_hipsIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||||
_hipsParentIndex = -1;
|
_hipsParentIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||||
_leftHandIndex = -1;
|
_leftHandIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||||
_rightHandIndex = -1;
|
_rightHandIndex = AnimSkeleton::INVALID_JOINT_INDEX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1531,7 +1532,7 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c
|
||||||
|
|
||||||
// draw line to parent
|
// draw line to parent
|
||||||
int parentIndex = _skeleton->getParentIndex(i);
|
int parentIndex = _skeleton->getParentIndex(i);
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
|
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
|
||||||
DebugDraw::getInstance().drawRay(pos, parentPos, GRAY);
|
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);
|
DebugDraw::getInstance().drawRay(pos, pos + AXIS_LENGTH * zAxis, BLUE);
|
||||||
|
|
||||||
// draw line to parent
|
// draw line to parent
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
|
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
|
||||||
glm::vec4 color = GRAY;
|
glm::vec4 color = GRAY;
|
||||||
|
|
||||||
|
@ -1629,13 +1630,13 @@ void AnimInverseKinematics::debugDrawConstraints(const AnimContext& context) con
|
||||||
|
|
||||||
// draw line to parent
|
// draw line to parent
|
||||||
int parentIndex = _skeleton->getParentIndex(i);
|
int parentIndex = _skeleton->getParentIndex(i);
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
|
glm::vec3 parentPos = transformPoint(geomToWorldMatrix, poses[parentIndex].trans());
|
||||||
DebugDraw::getInstance().drawRay(pos, parentPos, GRAY);
|
DebugDraw::getInstance().drawRay(pos, parentPos, GRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::quat parentAbsRot;
|
glm::quat parentAbsRot;
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != AnimSkeleton::INVALID_JOINT_INDEX) {
|
||||||
parentAbsRot = poses[parentIndex].rot();
|
parentAbsRot = poses[parentIndex].rot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1733,7 +1734,7 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC
|
||||||
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
||||||
|
|
||||||
for (auto& target : targets) {
|
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++) {
|
for (int i = 0; i < NUM_LIMBS; i++) {
|
||||||
if (limbs[i].first == target.getIndex()) {
|
if (limbs[i].first == target.getIndex()) {
|
||||||
int tipIndex = limbs[i].first;
|
int tipIndex = limbs[i].first;
|
||||||
|
|
|
@ -66,7 +66,7 @@ int AnimSkeleton::nameToJointIndex(const QString& jointName) const {
|
||||||
if (_jointIndicesByName.end() != itr) {
|
if (_jointIndicesByName.end() != itr) {
|
||||||
return itr.value();
|
return itr.value();
|
||||||
}
|
}
|
||||||
return -1;
|
return INVALID_JOINT_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AnimSkeleton::getNumJoints() const {
|
int AnimSkeleton::getNumJoints() const {
|
||||||
|
@ -80,7 +80,7 @@ int AnimSkeleton::getChainDepth(int jointIndex) const {
|
||||||
do {
|
do {
|
||||||
chainDepth++;
|
chainDepth++;
|
||||||
index = _parentIndices[index];
|
index = _parentIndices[index];
|
||||||
} while (index != -1);
|
} while (index != INVALID_JOINT_INDEX);
|
||||||
return chainDepth;
|
return chainDepth;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -108,7 +108,7 @@ const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const {
|
||||||
std::vector<int> AnimSkeleton::getChildrenOfJoint(int jointIndex) const {
|
std::vector<int> AnimSkeleton::getChildrenOfJoint(int jointIndex) const {
|
||||||
// Children and grandchildren, etc.
|
// Children and grandchildren, etc.
|
||||||
std::vector<int> result;
|
std::vector<int> result;
|
||||||
if (jointIndex != -1) {
|
if (jointIndex != INVALID_JOINT_INDEX) {
|
||||||
for (int i = jointIndex + 1; i < (int)_parentIndices.size(); i++) {
|
for (int i = jointIndex + 1; i < (int)_parentIndices.size(); i++) {
|
||||||
if (_parentIndices[i] == jointIndex || (std::find(result.begin(), result.end(), _parentIndices[i]) != result.end())) {
|
if (_parentIndices[i] == jointIndex || (std::find(result.begin(), result.end(), _parentIndices[i]) != result.end())) {
|
||||||
result.push_back(i);
|
result.push_back(i);
|
||||||
|
@ -135,7 +135,7 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const {
|
||||||
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
||||||
for (int i = 0; i < lastIndex; ++i) {
|
for (int i = 0; i < lastIndex; ++i) {
|
||||||
int parentIndex = _parentIndices[i];
|
int parentIndex = _parentIndices[i];
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != INVALID_JOINT_INDEX) {
|
||||||
poses[i] = poses[parentIndex] * poses[i];
|
poses[i] = poses[parentIndex] * poses[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const {
|
||||||
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
||||||
for (int i = lastIndex - 1; i >= 0; --i) {
|
for (int i = lastIndex - 1; i >= 0; --i) {
|
||||||
int parentIndex = _parentIndices[i];
|
int parentIndex = _parentIndices[i];
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != INVALID_JOINT_INDEX) {
|
||||||
poses[i] = poses[parentIndex].inverse() * poses[i];
|
poses[i] = poses[parentIndex].inverse() * poses[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ void AnimSkeleton::convertRelativeRotationsToAbsolute(std::vector<glm::quat>& ro
|
||||||
int lastIndex = std::min((int)rotations.size(), _jointsSize);
|
int lastIndex = std::min((int)rotations.size(), _jointsSize);
|
||||||
for (int i = 0; i < lastIndex; ++i) {
|
for (int i = 0; i < lastIndex; ++i) {
|
||||||
int parentIndex = _parentIndices[i];
|
int parentIndex = _parentIndices[i];
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != INVALID_JOINT_INDEX) {
|
||||||
rotations[i] = rotations[parentIndex] * rotations[i];
|
rotations[i] = rotations[parentIndex] * rotations[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ void AnimSkeleton::convertAbsoluteRotationsToRelative(std::vector<glm::quat>& ro
|
||||||
int lastIndex = std::min((int)rotations.size(), _jointsSize);
|
int lastIndex = std::min((int)rotations.size(), _jointsSize);
|
||||||
for (int i = lastIndex - 1; i >= 0; --i) {
|
for (int i = lastIndex - 1; i >= 0; --i) {
|
||||||
int parentIndex = _parentIndices[i];
|
int parentIndex = _parentIndices[i];
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != INVALID_JOINT_INDEX) {
|
||||||
rotations[i] = glm::inverse(rotations[parentIndex]) * rotations[i];
|
rotations[i] = glm::inverse(rotations[parentIndex]) * rotations[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,7 +280,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints,
|
||||||
// so we can restore them after a future mirror operation
|
// so we can restore them after a future mirror operation
|
||||||
_nonMirroredIndices.push_back(i);
|
_nonMirroredIndices.push_back(i);
|
||||||
}
|
}
|
||||||
int mirrorJointIndex = -1;
|
int mirrorJointIndex = INVALID_JOINT_INDEX;
|
||||||
if (_joints[i].name.startsWith("Left")) {
|
if (_joints[i].name.startsWith("Left")) {
|
||||||
QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right");
|
QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right");
|
||||||
mirrorJointIndex = nameToJointIndex(mirrorJointName);
|
mirrorJointIndex = nameToJointIndex(mirrorJointName);
|
||||||
|
@ -350,7 +350,7 @@ std::vector<int> AnimSkeleton::lookUpJointIndices(const std::vector<QString>& jo
|
||||||
result.reserve(jointNames.size());
|
result.reserve(jointNames.size());
|
||||||
for (auto& name : jointNames) {
|
for (auto& name : jointNames) {
|
||||||
int index = nameToJointIndex(name);
|
int index = nameToJointIndex(name);
|
||||||
if (index == -1) {
|
if (index == INVALID_JOINT_INDEX) {
|
||||||
qWarning(animation) << "AnimSkeleton::lookUpJointIndices(): could not find bone with name " << name;
|
qWarning(animation) << "AnimSkeleton::lookUpJointIndices(): could not find bone with name " << name;
|
||||||
}
|
}
|
||||||
result.push_back(index);
|
result.push_back(index);
|
||||||
|
|
|
@ -30,6 +30,8 @@ public:
|
||||||
const QString& getJointName(int jointIndex) const;
|
const QString& getJointName(int jointIndex) const;
|
||||||
int getNumJoints() const;
|
int getNumJoints() const;
|
||||||
int getChainDepth(int jointIndex) 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.
|
// the default poses are the orientations of the joints on frame 0.
|
||||||
const AnimPose& getRelativeDefaultPose(int jointIndex) const;
|
const AnimPose& getRelativeDefaultPose(int jointIndex) const;
|
||||||
|
|
|
@ -1266,7 +1266,7 @@ void AudioClient::processWebrtcNearEnd(int16_t* samples, int numFrames, int numC
|
||||||
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
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.
|
// 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();
|
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
|
||||||
if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) {
|
if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb) || !_audioGateOpen) {
|
||||||
return;
|
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) {
|
void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
||||||
if (!_audioPaused) {
|
if (!_audioPaused) {
|
||||||
|
|
||||||
|
@ -1352,9 +1359,14 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
||||||
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
|
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
|
||||||
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||||
|
|
||||||
if (_isNoiseGateEnabled) {
|
if (_isNoiseGateEnabled && _isNoiseReductionAutomatic) {
|
||||||
// The audio gate includes DC removal
|
// The audio gate includes DC removal
|
||||||
audioGateOpen = _audioGate->render(samples, samples, numFrames);
|
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 {
|
} else {
|
||||||
audioGateOpen = _audioGate->removeDC(samples, samples, numFrames);
|
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) {
|
void AudioClient::setWarnWhenMuted(bool enable, bool emitSignal) {
|
||||||
if (_warnWhenMuted != enable) {
|
if (_warnWhenMuted != enable) {
|
||||||
_warnWhenMuted = enable;
|
_warnWhenMuted = enable;
|
||||||
|
|
|
@ -217,6 +217,12 @@ public slots:
|
||||||
|
|
||||||
void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true);
|
void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true);
|
||||||
bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; }
|
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);
|
void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true);
|
||||||
bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; }
|
bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; }
|
||||||
|
@ -265,6 +271,8 @@ signals:
|
||||||
void inputVolumeChanged(float volume);
|
void inputVolumeChanged(float volume);
|
||||||
void muteToggled(bool muted);
|
void muteToggled(bool muted);
|
||||||
void noiseReductionChanged(bool noiseReductionEnabled);
|
void noiseReductionChanged(bool noiseReductionEnabled);
|
||||||
|
void noiseReductionAutomaticChanged(bool noiseReductionAutomatic);
|
||||||
|
void noiseReductionThresholdChanged(bool noiseReductionThreshold);
|
||||||
void warnWhenMutedChanged(bool warnWhenMutedEnabled);
|
void warnWhenMutedChanged(bool warnWhenMutedEnabled);
|
||||||
void acousticEchoCancellationChanged(bool acousticEchoCancellationEnabled);
|
void acousticEchoCancellationChanged(bool acousticEchoCancellationEnabled);
|
||||||
void mutedByMixer();
|
void mutedByMixer();
|
||||||
|
@ -310,6 +318,8 @@ private:
|
||||||
friend class CheckDevicesThread;
|
friend class CheckDevicesThread;
|
||||||
friend class LocalInjectorsThread;
|
friend class LocalInjectorsThread;
|
||||||
|
|
||||||
|
float loudnessToLevel(float loudness);
|
||||||
|
|
||||||
// background tasks
|
// background tasks
|
||||||
void checkDevices();
|
void checkDevices();
|
||||||
void checkPeakValues();
|
void checkPeakValues();
|
||||||
|
@ -397,6 +407,8 @@ private:
|
||||||
bool _shouldEchoLocally{ false };
|
bool _shouldEchoLocally{ false };
|
||||||
bool _shouldEchoToServer{ false };
|
bool _shouldEchoToServer{ false };
|
||||||
bool _isNoiseGateEnabled{ true };
|
bool _isNoiseGateEnabled{ true };
|
||||||
|
bool _isNoiseReductionAutomatic{ true };
|
||||||
|
float _noiseReductionThreshold{ 0.1f };
|
||||||
bool _warnWhenMuted;
|
bool _warnWhenMuted;
|
||||||
bool _isAECEnabled{ true };
|
bool _isAECEnabled{ true };
|
||||||
|
|
||||||
|
|
|
@ -1444,9 +1444,7 @@ int Avatar::getJointIndex(const QString& name) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
withValidJointIndicesCache([&]() {
|
withValidJointIndicesCache([&]() {
|
||||||
if (_modelJointIndicesCache.contains(name)) {
|
result = _modelJointIndicesCache.value(name, result + 1) - 1;
|
||||||
result = _modelJointIndicesCache.value(name) - 1;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1938,6 +1938,10 @@ void AvatarData::clearJointsData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int AvatarData::getFauxJointIndex(const QString& name) const {
|
int AvatarData::getFauxJointIndex(const QString& name) const {
|
||||||
|
static constexpr QChar fauxJointFirstChar('_');// The first character of all the faux joint names.
|
||||||
|
if (!name.startsWith(fauxJointFirstChar)) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
if (name == "_SENSOR_TO_WORLD_MATRIX") {
|
if (name == "_SENSOR_TO_WORLD_MATRIX") {
|
||||||
return SENSOR_TO_WORLD_MATRIX_INDEX;
|
return SENSOR_TO_WORLD_MATRIX_INDEX;
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,20 +168,20 @@ void TextureBaker::processTexture() {
|
||||||
gpu::BackendTarget::GLES32
|
gpu::BackendTarget::GLES32
|
||||||
}};
|
}};
|
||||||
for (auto target : BACKEND_TARGETS) {
|
for (auto target : BACKEND_TARGETS) {
|
||||||
auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE,
|
auto processedTextureAndSize = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE,
|
||||||
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true,
|
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true,
|
||||||
target, _abortProcessing);
|
target, _abortProcessing);
|
||||||
if (!processedTexture) {
|
if (!processedTextureAndSize.first) {
|
||||||
handleError("Could not process texture " + _textureURL.toString());
|
handleError("Could not process texture " + _textureURL.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
processedTexture->setSourceHash(hash);
|
processedTextureAndSize.first->setSourceHash(hash);
|
||||||
|
|
||||||
if (shouldStop()) {
|
if (shouldStop()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto memKTX = gpu::Texture::serialize(*processedTexture);
|
auto memKTX = gpu::Texture::serialize(*processedTextureAndSize.first, processedTextureAndSize.second);
|
||||||
if (!memKTX) {
|
if (!memKTX) {
|
||||||
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
||||||
return;
|
return;
|
||||||
|
@ -211,19 +211,19 @@ void TextureBaker::processTexture() {
|
||||||
// Uncompressed KTX
|
// Uncompressed KTX
|
||||||
if (_textureType == image::TextureUsage::Type::SKY_TEXTURE || _textureType == image::TextureUsage::Type::AMBIENT_TEXTURE) {
|
if (_textureType == image::TextureUsage::Type::SKY_TEXTURE || _textureType == image::TextureUsage::Type::AMBIENT_TEXTURE) {
|
||||||
buffer->reset();
|
buffer->reset();
|
||||||
auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE,
|
auto processedTextureAndSize = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE,
|
||||||
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing);
|
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing);
|
||||||
if (!processedTexture) {
|
if (!processedTextureAndSize.first) {
|
||||||
handleError("Could not process texture " + _textureURL.toString());
|
handleError("Could not process texture " + _textureURL.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
processedTexture->setSourceHash(hash);
|
processedTextureAndSize.first->setSourceHash(hash);
|
||||||
|
|
||||||
if (shouldStop()) {
|
if (shouldStop()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto memKTX = gpu::Texture::serialize(*processedTexture);
|
auto memKTX = gpu::Texture::serialize(*processedTextureAndSize.first, processedTextureAndSize.second);
|
||||||
if (!memKTX) {
|
if (!memKTX) {
|
||||||
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -118,7 +118,7 @@ public:
|
||||||
const QString& getName() const { return _name; }
|
const QString& getName() const { return _name; }
|
||||||
|
|
||||||
// By default, Input Devices do not support haptics
|
// 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
|
// Update call MUST be called once per simulation loop
|
||||||
// It takes care of updating the action states and deltas
|
// It takes care of updating the action states and deltas
|
||||||
|
|
|
@ -134,13 +134,13 @@ namespace controller {
|
||||||
return DependencyManager::get<UserInputMapper>()->getActionNames();
|
return DependencyManager::get<UserInputMapper>()->getActionNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptingInterface::triggerHapticPulse(float strength, float duration, controller::Hand hand) const {
|
bool ScriptingInterface::triggerHapticPulse(float strength, float duration, uint16_t index) const {
|
||||||
return DependencyManager::get<UserInputMapper>()->triggerHapticPulse(strength, duration, hand);
|
return DependencyManager::get<UserInputMapper>()->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;
|
const float SHORT_HAPTIC_DURATION_MS = 250.0f;
|
||||||
return DependencyManager::get<UserInputMapper>()->triggerHapticPulse(strength, SHORT_HAPTIC_DURATION_MS, hand);
|
return DependencyManager::get<UserInputMapper>()->triggerHapticPulse(strength, SHORT_HAPTIC_DURATION_MS, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptingInterface::startInputRecording() {
|
void ScriptingInterface::startInputRecording() {
|
||||||
|
@ -189,13 +189,13 @@ namespace controller {
|
||||||
emit inputDeviceRunningChanged(deviceName, isRunning);
|
emit inputDeviceRunningChanged(deviceName, isRunning);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptingInterface::triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, controller::Hand hand) const {
|
bool ScriptingInterface::triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, uint16_t index) const {
|
||||||
return DependencyManager::get<UserInputMapper>()->triggerHapticPulseOnDevice(device, strength, duration, hand);
|
return DependencyManager::get<UserInputMapper>()->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;
|
const float SHORT_HAPTIC_DURATION_MS = 250.0f;
|
||||||
return DependencyManager::get<UserInputMapper>()->triggerHapticPulseOnDevice(device, strength, SHORT_HAPTIC_DURATION_MS, hand);
|
return DependencyManager::get<UserInputMapper>()->triggerHapticPulseOnDevice(device, strength, SHORT_HAPTIC_DURATION_MS, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptingInterface::updateMaps() {
|
void ScriptingInterface::updateMaps() {
|
||||||
|
|
|
@ -212,22 +212,28 @@ namespace controller {
|
||||||
* @function Controller.triggerHapticPulse
|
* @function Controller.triggerHapticPulse
|
||||||
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
||||||
* @param {number} duration - The duration of the haptic pulse, in milliseconds.
|
* @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, <code>index = 0</code> is the left hand,
|
||||||
|
* <code>index = 1</code> is the right hand, and <code>index = 2</code> is both hands. For other devices,
|
||||||
|
* such as haptic vests, index will have a different meaning, defined by the input device.
|
||||||
* @example <caption>Trigger a haptic pulse on the right hand.</caption>
|
* @example <caption>Trigger a haptic pulse on the right hand.</caption>
|
||||||
* var HAPTIC_STRENGTH = 0.5;
|
* var HAPTIC_STRENGTH = 0.5;
|
||||||
* var HAPTIC_DURATION = 10;
|
* var HAPTIC_DURATION = 10;
|
||||||
* var RIGHT_HAND = 1;
|
* var RIGHT_HAND = 1;
|
||||||
* Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND);
|
* 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
|
/**jsdoc
|
||||||
* Triggers a 250ms haptic pulse on connected and enabled devices that have the capability.
|
* Triggers a 250ms haptic pulse on connected and enabled devices that have the capability.
|
||||||
* @function Controller.triggerShortHapticPulse
|
* @function Controller.triggerShortHapticPulse
|
||||||
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
||||||
* @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, <code>index = 0</code> is the left hand,
|
||||||
|
* <code>index = 1</code> is the right hand, and <code>index = 2</code> 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
|
/**jsdoc
|
||||||
* Triggers a haptic pulse on a particular device if connected and enabled and it has the capability.
|
* 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} deviceID - The ID of the device to trigger the haptic pulse on.
|
||||||
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
||||||
* @param {number} duration - The duration of the haptic pulse, in milliseconds.
|
* @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, <code>index = 0</code> is the left hand,
|
||||||
|
* <code>index = 1</code> is the right hand, and <code>index = 2</code> is both hands. For other devices,
|
||||||
|
* such as haptic vests, index will have a different meaning, defined by the input device.
|
||||||
* @example <caption>Trigger a haptic pulse on an Oculus Touch controller.</caption>
|
* @example <caption>Trigger a haptic pulse on an Oculus Touch controller.</caption>
|
||||||
* var HAPTIC_STRENGTH = 0.5;
|
* var HAPTIC_STRENGTH = 0.5;
|
||||||
* var deviceID = Controller.findDevice("OculusTouch");
|
* var deviceID = Controller.findDevice("OculusTouch");
|
||||||
|
@ -243,19 +252,20 @@ namespace controller {
|
||||||
* var RIGHT_HAND = 1;
|
* var RIGHT_HAND = 1;
|
||||||
* Controller.triggerHapticPulseOnDevice(deviceID, HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND);
|
* Controller.triggerHapticPulseOnDevice(deviceID, HAPTIC_STRENGTH, HAPTIC_DURATION, RIGHT_HAND);
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration,
|
Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration,
|
||||||
controller::Hand hand = BOTH) const;
|
uint16_t index = 2) const;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Triggers a 250ms haptic pulse on a particular device if connected and enabled and it has the capability.
|
* Triggers a 250ms haptic pulse on a particular device if connected and enabled and it has the capability.
|
||||||
* @function Controller.triggerShortHapticPulseOnDevice
|
* @function Controller.triggerShortHapticPulseOnDevice
|
||||||
* @param {number} deviceID - The ID of the device to trigger the haptic pulse on.
|
* @param {number} deviceID - The ID of the device to trigger the haptic pulse on.
|
||||||
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
* @param {number} strength - The strength of the haptic pulse, range <code>0.0</code> – <code>1.0</code>.
|
||||||
* @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, <code>index = 0</code> is the left hand,
|
||||||
|
* <code>index = 1</code> is the right hand, and <code>index = 2</code> 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)
|
Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, uint16_t index = 2) const;
|
||||||
const;
|
|
||||||
|
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Creates a new controller mapping. Routes can then be added to the mapping using {@link MappingObject} methods and
|
* Creates a new controller mapping. Routes can then be added to the mapping using {@link MappingObject} methods and
|
||||||
|
|
|
@ -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);
|
Locker locker(_lock);
|
||||||
bool toReturn = false;
|
bool toReturn = false;
|
||||||
for (const auto& device : _registeredDevices) {
|
for (const auto& device : _registeredDevices) {
|
||||||
toReturn = device.second->triggerHapticPulse(strength, duration, hand) || toReturn;
|
toReturn = device.second->triggerHapticPulse(strength, duration, index) || toReturn;
|
||||||
}
|
}
|
||||||
return 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);
|
Locker locker(_lock);
|
||||||
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
|
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
|
||||||
return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, hand);
|
return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, index);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,8 +91,8 @@ namespace controller {
|
||||||
void setActionState(Action action, float value, bool valid = true);
|
void setActionState(Action action, float value, bool valid = true);
|
||||||
void deltaActionState(Action action, float delta, bool valid = true);
|
void deltaActionState(Action action, float delta, bool valid = true);
|
||||||
void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; }
|
void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; }
|
||||||
bool triggerHapticPulse(float strength, float duration, controller::Hand hand);
|
bool triggerHapticPulse(float strength, float duration, uint16_t index);
|
||||||
bool triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, controller::Hand hand);
|
bool triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, uint16_t index);
|
||||||
|
|
||||||
static Input makeStandardInput(controller::StandardButtonChannel button);
|
static Input makeStandardInput(controller::StandardButtonChannel button);
|
||||||
static Input makeStandardInput(controller::StandardAxisChannel axis);
|
static Input makeStandardInput(controller::StandardAxisChannel axis);
|
||||||
|
|
|
@ -84,10 +84,24 @@ void ImageEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
|
||||||
|
|
||||||
updateMaterials(materialChanged);
|
updateMaterials(materialChanged);
|
||||||
|
|
||||||
|
bool nextTextureLoaded = _texture && (_texture->isLoaded() || _texture->isFailed());
|
||||||
if (!_textureIsLoaded) {
|
if (!_textureIsLoaded) {
|
||||||
emit requestRenderUpdate();
|
emit requestRenderUpdate();
|
||||||
|
if (nextTextureLoaded) {
|
||||||
|
float width = _texture->getOriginalWidth();
|
||||||
|
float height = _texture->getOriginalHeight();
|
||||||
|
glm::vec3 naturalDimensions = glm::vec3(1.0f, 1.0f, 0.01f);
|
||||||
|
if (width < height) {
|
||||||
|
naturalDimensions.x = width / height;
|
||||||
|
} else {
|
||||||
|
naturalDimensions.y = height / width;
|
||||||
|
}
|
||||||
|
// Unlike Models (where the Renderer also doubles as the EntityItem), Images need to
|
||||||
|
// convey this information back to the game object from the Renderer
|
||||||
|
entity->setNaturalDimension(naturalDimensions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_textureIsLoaded = _texture && (_texture->isLoaded() || _texture->isFailed());
|
_textureIsLoaded = nextTextureLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageEntityRenderer::isTransparent() const {
|
bool ImageEntityRenderer::isTransparent() const {
|
||||||
|
@ -135,17 +149,18 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
|
||||||
|
|
||||||
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
|
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
|
||||||
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
|
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
|
||||||
batch->setModelTransform(transform);
|
|
||||||
|
|
||||||
float imageWidth = _texture->getWidth();
|
float imageWidth = _texture->getWidth();
|
||||||
float imageHeight = _texture->getHeight();
|
float imageHeight = _texture->getHeight();
|
||||||
|
float originalWidth = _texture->getOriginalWidth();
|
||||||
|
float originalHeight = _texture->getOriginalHeight();
|
||||||
|
|
||||||
QRect fromImage;
|
QRect fromImage;
|
||||||
if (_subImage.width() <= 0) {
|
if (_subImage.width() <= 0) {
|
||||||
fromImage.setX(0);
|
fromImage.setX(0);
|
||||||
fromImage.setWidth(imageWidth);
|
fromImage.setWidth(imageWidth);
|
||||||
} else {
|
} else {
|
||||||
float scaleX = imageWidth / _texture->getOriginalWidth();
|
float scaleX = imageWidth / originalWidth;
|
||||||
fromImage.setX(scaleX * _subImage.x());
|
fromImage.setX(scaleX * _subImage.x());
|
||||||
fromImage.setWidth(scaleX * _subImage.width());
|
fromImage.setWidth(scaleX * _subImage.width());
|
||||||
}
|
}
|
||||||
|
@ -154,18 +169,28 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
|
||||||
fromImage.setY(0);
|
fromImage.setY(0);
|
||||||
fromImage.setHeight(imageHeight);
|
fromImage.setHeight(imageHeight);
|
||||||
} else {
|
} else {
|
||||||
float scaleY = imageHeight / _texture->getOriginalHeight();
|
float scaleY = imageHeight / originalHeight;
|
||||||
fromImage.setY(scaleY * _subImage.y());
|
fromImage.setY(scaleY * _subImage.y());
|
||||||
fromImage.setHeight(scaleY * _subImage.height());
|
fromImage.setHeight(scaleY * _subImage.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
float maxSize = glm::max(fromImage.width(), fromImage.height());
|
|
||||||
float x = _keepAspectRatio ? fromImage.width() / (2.0f * maxSize) : 0.5f;
|
|
||||||
float y = _keepAspectRatio ? fromImage.height() / (2.0f * maxSize) : 0.5f;
|
|
||||||
|
|
||||||
glm::vec2 texCoordBottomLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + fromImage.height() - 0.5f) / imageHeight);
|
glm::vec2 texCoordBottomLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + fromImage.height() - 0.5f) / imageHeight);
|
||||||
glm::vec2 texCoordTopRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight);
|
glm::vec2 texCoordTopRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight);
|
||||||
|
|
||||||
|
if (_keepAspectRatio) {
|
||||||
|
glm::vec3 scale = transform.getScale();
|
||||||
|
float targetAspectRatio = originalWidth / originalHeight;
|
||||||
|
float currentAspectRatio = scale.x / scale.y;
|
||||||
|
|
||||||
|
if (targetAspectRatio < currentAspectRatio) {
|
||||||
|
scale.x *= targetAspectRatio / currentAspectRatio;
|
||||||
|
} else {
|
||||||
|
scale.y /= targetAspectRatio / currentAspectRatio;
|
||||||
|
}
|
||||||
|
transform.setScale(scale);
|
||||||
|
}
|
||||||
|
batch->setModelTransform(transform);
|
||||||
|
|
||||||
Pipeline pipelineType = getPipelineType(materials);
|
Pipeline pipelineType = getPipelineType(materials);
|
||||||
if (pipelineType == Pipeline::PROCEDURAL) {
|
if (pipelineType == Pipeline::PROCEDURAL) {
|
||||||
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(materials.top().material);
|
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(materials.top().material);
|
||||||
|
@ -180,7 +205,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DependencyManager::get<GeometryCache>()->renderQuad(
|
DependencyManager::get<GeometryCache>()->renderQuad(
|
||||||
*batch, glm::vec2(-x, -y), glm::vec2(x, y), texCoordBottomLeft, texCoordTopRight,
|
*batch, glm::vec2(-0.5f), glm::vec2(0.5f), texCoordBottomLeft, texCoordTopRight,
|
||||||
color, _geometryId
|
color, _geometryId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1772,13 +1772,11 @@ bool EntityItem::contains(const glm::vec3& point) const {
|
||||||
ShapeType shapeType = getShapeType();
|
ShapeType shapeType = getShapeType();
|
||||||
|
|
||||||
if (shapeType == SHAPE_TYPE_SPHERE) {
|
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 dimensions = getScaledDimensions();
|
||||||
glm::vec3 localPoint = point - (getWorldPosition() + getWorldOrientation() * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()) + getPivot()));
|
if (dimensions.x == dimensions.y && dimensions.y == dimensions.z) {
|
||||||
const float HALF_SQUARED = 0.25f;
|
glm::vec3 localPoint = point - (getWorldPosition() + getWorldOrientation() * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()) + getPivot()));
|
||||||
return glm::length2(localPoint) < HALF_SQUARED * glm::length2(dimensions);
|
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
|
// 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);
|
localPoint = glm::abs(localPoint);
|
||||||
return glm::all(glm::lessThanEqual(localPoint, glm::vec3(NORMALIZED_HALF_SIDE)));
|
return glm::all(glm::lessThanEqual(localPoint, glm::vec3(NORMALIZED_HALF_SIDE)));
|
||||||
}
|
}
|
||||||
|
case SHAPE_TYPE_SPHERE:
|
||||||
case SHAPE_TYPE_ELLIPSOID: {
|
case SHAPE_TYPE_ELLIPSOID: {
|
||||||
// since we've transformed into the normalized space this is just a sphere-point intersection test
|
// since we've transformed into the normalized space this is just a sphere-point intersection test
|
||||||
return glm::length2(localPoint) <= NORMALIZED_RADIUS_SQUARED;
|
return glm::length2(localPoint) <= NORMALIZED_RADIUS_SQUARED;
|
||||||
|
|
|
@ -723,7 +723,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
*
|
*
|
||||||
* @property {Vec3} naturalPosition=0,0,0 - The center of the entity's unscaled mesh model if it has one, otherwise
|
* @property {Vec3} naturalPosition=0,0,0 - The center of the entity's unscaled mesh model if it has one, otherwise
|
||||||
* {@link Vec3(0)|Vec3.ZERO}. <em>Read-only.</em>
|
* {@link Vec3(0)|Vec3.ZERO}. <em>Read-only.</em>
|
||||||
* @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model if it has one, otherwise
|
* @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model or image if it has one, otherwise
|
||||||
* {@link Vec3(0)|Vec3.ONE}. <em>Read-only.</em>
|
* {@link Vec3(0)|Vec3.ONE}. <em>Read-only.</em>
|
||||||
*
|
*
|
||||||
* @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates.
|
* @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates.
|
||||||
|
|
|
@ -34,6 +34,7 @@ EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& d
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
_pulseProperties.getProperties(properties);
|
_pulseProperties.getProperties(properties);
|
||||||
|
properties.setNaturalDimensions(_naturalDimensions);
|
||||||
});
|
});
|
||||||
|
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL);
|
||||||
|
@ -217,4 +218,10 @@ PulsePropertyGroup ImageEntityItem::getPulseProperties() const {
|
||||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||||
return _pulseProperties;
|
return _pulseProperties;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageEntityItem::setNaturalDimension(const glm::vec3& naturalDimensions) const {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_naturalDimensions = naturalDimensions;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,8 @@ public:
|
||||||
|
|
||||||
PulsePropertyGroup getPulseProperties() const;
|
PulsePropertyGroup getPulseProperties() const;
|
||||||
|
|
||||||
|
void setNaturalDimension(const glm::vec3& naturalDimensions) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
glm::u8vec3 _color;
|
glm::u8vec3 _color;
|
||||||
float _alpha;
|
float _alpha;
|
||||||
|
@ -72,6 +74,8 @@ protected:
|
||||||
bool _emissive { false };
|
bool _emissive { false };
|
||||||
bool _keepAspectRatio { true };
|
bool _keepAspectRatio { true };
|
||||||
QRect _subImage;
|
QRect _subImage;
|
||||||
|
|
||||||
|
mutable glm::vec3 _naturalDimensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ImageEntityItem_h
|
#endif // hifi_ImageEntityItem_h
|
||||||
|
|
|
@ -270,7 +270,6 @@ void ZoneEntityItem::debugDump() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneEntityItem::setShapeType(ShapeType type) {
|
void ZoneEntityItem::setShapeType(ShapeType type) {
|
||||||
ShapeType oldShapeType = _shapeType;
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case SHAPE_TYPE_NONE:
|
case SHAPE_TYPE_NONE:
|
||||||
case SHAPE_TYPE_CAPSULE_X:
|
case SHAPE_TYPE_CAPSULE_X:
|
||||||
|
@ -288,7 +287,12 @@ void ZoneEntityItem::setShapeType(ShapeType type) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_shapeType = type;
|
|
||||||
|
ShapeType oldShapeType;
|
||||||
|
withWriteLock([&] {
|
||||||
|
oldShapeType = _shapeType;
|
||||||
|
_shapeType = type;
|
||||||
|
});
|
||||||
|
|
||||||
if (type == SHAPE_TYPE_COMPOUND) {
|
if (type == SHAPE_TYPE_COMPOUND) {
|
||||||
if (type != oldShapeType) {
|
if (type != oldShapeType) {
|
||||||
|
@ -300,16 +304,21 @@ void ZoneEntityItem::setShapeType(ShapeType type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeType ZoneEntityItem::getShapeType() const {
|
ShapeType ZoneEntityItem::getShapeType() const {
|
||||||
return _shapeType;
|
return resultWithReadLock<ShapeType>([&] {
|
||||||
|
return _shapeType;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneEntityItem::setCompoundShapeURL(const QString& url) {
|
void ZoneEntityItem::setCompoundShapeURL(const QString& url) {
|
||||||
QString oldCompoundShapeURL = _compoundShapeURL;
|
QString oldCompoundShapeURL;
|
||||||
|
ShapeType shapeType;
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
|
oldCompoundShapeURL = _compoundShapeURL;
|
||||||
_compoundShapeURL = url;
|
_compoundShapeURL = url;
|
||||||
|
shapeType = _shapeType;
|
||||||
});
|
});
|
||||||
if (oldCompoundShapeURL != url) {
|
if (oldCompoundShapeURL != url) {
|
||||||
if (_shapeType == SHAPE_TYPE_COMPOUND) {
|
if (shapeType == SHAPE_TYPE_COMPOUND) {
|
||||||
fetchCollisionGeometryResource();
|
fetchCollisionGeometryResource();
|
||||||
} else {
|
} else {
|
||||||
_shapeResource.reset();
|
_shapeResource.reset();
|
||||||
|
@ -333,7 +342,7 @@ bool ZoneEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, c
|
||||||
|
|
||||||
bool ZoneEntityItem::contains(const glm::vec3& point) const {
|
bool ZoneEntityItem::contains(const glm::vec3& point) const {
|
||||||
GeometryResource::Pointer resource = _shapeResource;
|
GeometryResource::Pointer resource = _shapeResource;
|
||||||
if (_shapeType == SHAPE_TYPE_COMPOUND && resource) {
|
if (getShapeType() == SHAPE_TYPE_COMPOUND && resource) {
|
||||||
if (resource->isLoaded()) {
|
if (resource->isLoaded()) {
|
||||||
const HFMModel& hfmModel = resource->getHFMModel();
|
const HFMModel& hfmModel = resource->getHFMModel();
|
||||||
|
|
||||||
|
|
|
@ -579,11 +579,11 @@ public:
|
||||||
ExternalUpdates getUpdates() const;
|
ExternalUpdates getUpdates() const;
|
||||||
|
|
||||||
// Serialize a texture into a KTX file
|
// Serialize a texture into a KTX file
|
||||||
static ktx::KTXUniquePointer serialize(const Texture& texture);
|
static ktx::KTXUniquePointer serialize(const Texture& texture, const glm::ivec2& originalSize);
|
||||||
|
|
||||||
static TexturePointer build(const ktx::KTXDescriptor& descriptor);
|
static std::pair<TexturePointer, glm::ivec2> build(const ktx::KTXDescriptor& descriptor);
|
||||||
static TexturePointer unserialize(const std::string& ktxFile);
|
static std::pair<TexturePointer, glm::ivec2> unserialize(const std::string& ktxFile);
|
||||||
static TexturePointer unserialize(const cache::FilePointer& cacheEntry, const std::string& source = std::string());
|
static std::pair<TexturePointer, glm::ivec2> unserialize(const cache::FilePointer& cacheEntry, const std::string& source = std::string());
|
||||||
|
|
||||||
static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header);
|
static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header);
|
||||||
static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat);
|
static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "Texture.h"
|
#include "Texture.h"
|
||||||
|
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
#include <ktx/KTX.h>
|
#include <ktx/KTX.h>
|
||||||
|
|
||||||
|
@ -30,15 +31,16 @@ struct GPUKTXPayload {
|
||||||
using Version = uint8;
|
using Version = uint8;
|
||||||
|
|
||||||
static const std::string KEY;
|
static const std::string KEY;
|
||||||
static const Version CURRENT_VERSION { 1 };
|
static const Version CURRENT_VERSION { 2 };
|
||||||
static const size_t PADDING { 2 };
|
static const size_t PADDING { 2 };
|
||||||
static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + PADDING };
|
static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + sizeof(glm::ivec2) + PADDING };
|
||||||
static_assert(GPUKTXPayload::SIZE == 36, "Packing size may differ between platforms");
|
static_assert(GPUKTXPayload::SIZE == 44, "Packing size may differ between platforms");
|
||||||
static_assert(GPUKTXPayload::SIZE % 4 == 0, "GPUKTXPayload is not 4 bytes aligned");
|
static_assert(GPUKTXPayload::SIZE % 4 == 0, "GPUKTXPayload is not 4 bytes aligned");
|
||||||
|
|
||||||
Sampler::Desc _samplerDesc;
|
Sampler::Desc _samplerDesc;
|
||||||
Texture::Usage _usage;
|
Texture::Usage _usage;
|
||||||
TextureUsageType _usageType;
|
TextureUsageType _usageType;
|
||||||
|
glm::ivec2 _originalSize;
|
||||||
|
|
||||||
Byte* serialize(Byte* data) const {
|
Byte* serialize(Byte* data) const {
|
||||||
*(Version*)data = CURRENT_VERSION;
|
*(Version*)data = CURRENT_VERSION;
|
||||||
|
@ -56,6 +58,9 @@ struct GPUKTXPayload {
|
||||||
memcpy(data, &_usageType, sizeof(TextureUsageType));
|
memcpy(data, &_usageType, sizeof(TextureUsageType));
|
||||||
data += sizeof(TextureUsageType);
|
data += sizeof(TextureUsageType);
|
||||||
|
|
||||||
|
memcpy(data, glm::value_ptr(_originalSize), sizeof(glm::ivec2));
|
||||||
|
data += sizeof(glm::ivec2);
|
||||||
|
|
||||||
return data + PADDING;
|
return data + PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,13 +71,7 @@ struct GPUKTXPayload {
|
||||||
|
|
||||||
Version version = *(const Version*)data;
|
Version version = *(const Version*)data;
|
||||||
if (version != CURRENT_VERSION) {
|
if (version != CURRENT_VERSION) {
|
||||||
glm::vec4 borderColor(1.0f);
|
return false;
|
||||||
if (memcmp(&borderColor, data, sizeof(glm::vec4)) == 0) {
|
|
||||||
memcpy(this, data, sizeof(GPUKTXPayload));
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
data += sizeof(Version);
|
data += sizeof(Version);
|
||||||
|
|
||||||
|
@ -87,6 +86,10 @@ struct GPUKTXPayload {
|
||||||
data += sizeof(uint32);
|
data += sizeof(uint32);
|
||||||
|
|
||||||
memcpy(&_usageType, data, sizeof(TextureUsageType));
|
memcpy(&_usageType, data, sizeof(TextureUsageType));
|
||||||
|
data += sizeof(TextureUsageType);
|
||||||
|
|
||||||
|
memcpy(&_originalSize, data, sizeof(glm::ivec2));
|
||||||
|
data += sizeof(glm::ivec2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,7 +385,7 @@ void Texture::setKtxBacking(const cache::FilePointer& cacheEntry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
|
ktx::KTXUniquePointer Texture::serialize(const Texture& texture, const glm::ivec2& originalSize) {
|
||||||
ktx::Header header;
|
ktx::Header header;
|
||||||
|
|
||||||
// From texture format to ktx format description
|
// From texture format to ktx format description
|
||||||
|
@ -459,6 +462,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
|
||||||
gpuKeyval._samplerDesc = texture.getSampler().getDesc();
|
gpuKeyval._samplerDesc = texture.getSampler().getDesc();
|
||||||
gpuKeyval._usage = texture.getUsage();
|
gpuKeyval._usage = texture.getUsage();
|
||||||
gpuKeyval._usageType = texture.getUsageType();
|
gpuKeyval._usageType = texture.getUsageType();
|
||||||
|
gpuKeyval._originalSize = originalSize;
|
||||||
|
|
||||||
Byte keyvalPayload[GPUKTXPayload::SIZE];
|
Byte keyvalPayload[GPUKTXPayload::SIZE];
|
||||||
gpuKeyval.serialize(keyvalPayload);
|
gpuKeyval.serialize(keyvalPayload);
|
||||||
|
@ -514,19 +518,19 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
|
||||||
return ktxBuffer;
|
return ktxBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) {
|
std::pair<TexturePointer, glm::ivec2> Texture::build(const ktx::KTXDescriptor& descriptor) {
|
||||||
Format mipFormat = Format::COLOR_BGRA_32;
|
Format mipFormat = Format::COLOR_BGRA_32;
|
||||||
Format texelFormat = Format::COLOR_SRGBA_32;
|
Format texelFormat = Format::COLOR_SRGBA_32;
|
||||||
const auto& header = descriptor.header;
|
const auto& header = descriptor.header;
|
||||||
|
|
||||||
if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) {
|
if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) {
|
||||||
return nullptr;
|
return { nullptr, { 0, 0 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find Texture Type based on dimensions
|
// Find Texture Type based on dimensions
|
||||||
Type type = TEX_1D;
|
Type type = TEX_1D;
|
||||||
if (header.pixelWidth == 0) {
|
if (header.pixelWidth == 0) {
|
||||||
return nullptr;
|
return { nullptr, { 0, 0 } };
|
||||||
} else if (header.pixelHeight == 0) {
|
} else if (header.pixelHeight == 0) {
|
||||||
type = TEX_1D;
|
type = TEX_1D;
|
||||||
} else if (header.pixelDepth == 0) {
|
} else if (header.pixelDepth == 0) {
|
||||||
|
@ -569,39 +573,39 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) {
|
||||||
texture->overrideIrradiance(std::make_shared<SphericalHarmonics>(irradianceKtxKeyValue._irradianceSH));
|
texture->overrideIrradiance(std::make_shared<SphericalHarmonics>(irradianceKtxKeyValue._irradianceSH));
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture;
|
return { texture, gpuktxKeyValue._originalSize };
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) {
|
std::pair<TexturePointer, glm::ivec2> Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) {
|
||||||
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(cacheEntry->getFilepath().c_str()));
|
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(cacheEntry->getFilepath().c_str()));
|
||||||
if (!ktxPointer) {
|
if (!ktxPointer) {
|
||||||
return nullptr;
|
return { nullptr, { 0, 0 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto texture = build(ktxPointer->toDescriptor());
|
auto textureAndSize = build(ktxPointer->toDescriptor());
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture->setKtxBacking(cacheEntry);
|
textureAndSize.first->setKtxBacking(cacheEntry);
|
||||||
if (texture->source().empty()) {
|
if (textureAndSize.first->source().empty()) {
|
||||||
texture->setSource(source);
|
textureAndSize.first->setSource(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture;
|
return { textureAndSize.first, textureAndSize.second };
|
||||||
}
|
}
|
||||||
|
|
||||||
TexturePointer Texture::unserialize(const std::string& ktxfile) {
|
std::pair<TexturePointer, glm::ivec2> Texture::unserialize(const std::string& ktxfile) {
|
||||||
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(ktxfile.c_str()));
|
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(ktxfile.c_str()));
|
||||||
if (!ktxPointer) {
|
if (!ktxPointer) {
|
||||||
return nullptr;
|
return { nullptr, { 0, 0 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto texture = build(ktxPointer->toDescriptor());
|
auto textureAndSize = build(ktxPointer->toDescriptor());
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture->setKtxBacking(ktxfile);
|
textureAndSize.first->setKtxBacking(ktxfile);
|
||||||
texture->setSource(ktxfile);
|
textureAndSize.first->setSource(ktxfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture;
|
return { textureAndSize.first, textureAndSize.second };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) {
|
bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) {
|
||||||
|
|
|
@ -338,9 +338,9 @@ void mapToRedChannel(Image& image, ColorChannel sourceChannel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& filename, ColorChannel sourceChannel,
|
std::pair<gpu::TexturePointer, glm::ivec2> processImage(std::shared_ptr<QIODevice> content, const std::string& filename, ColorChannel sourceChannel,
|
||||||
int maxNumPixels, TextureUsage::Type textureType,
|
int maxNumPixels, TextureUsage::Type textureType,
|
||||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||||
|
|
||||||
Image image = processRawImageData(*content.get(), filename);
|
Image image = processRawImageData(*content.get(), filename);
|
||||||
// Texture content can take up a lot of memory. Here we release our ownership of that content
|
// Texture content can take up a lot of memory. Here we release our ownership of that content
|
||||||
|
@ -354,7 +354,7 @@ gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::
|
||||||
if (imageWidth == 0 || imageHeight == 0 || image.getFormat() == Image::Format_Invalid) {
|
if (imageWidth == 0 || imageHeight == 0 || image.getFormat() == Image::Format_Invalid) {
|
||||||
QString reason(image.getFormat() == Image::Format_Invalid ? "(Invalid Format)" : "(Size is invalid)");
|
QString reason(image.getFormat() == Image::Format_Invalid ? "(Invalid Format)" : "(Size is invalid)");
|
||||||
qCWarning(imagelogging) << "Failed to load:" << qPrintable(reason);
|
qCWarning(imagelogging) << "Failed to load:" << qPrintable(reason);
|
||||||
return nullptr;
|
return { nullptr, { imageWidth, imageHeight } };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the image is less than _maxNumPixels, and downscale if necessary
|
// Validate the image is less than _maxNumPixels, and downscale if necessary
|
||||||
|
@ -378,7 +378,7 @@ gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::
|
||||||
auto loader = TextureUsage::getTextureLoaderForType(textureType);
|
auto loader = TextureUsage::getTextureLoaderForType(textureType);
|
||||||
auto texture = loader(std::move(image), filename, compress, target, abortProcessing);
|
auto texture = loader(std::move(image), filename, compress, target, abortProcessing);
|
||||||
|
|
||||||
return texture;
|
return { texture, { imageWidth, imageHeight } };
|
||||||
}
|
}
|
||||||
|
|
||||||
Image processSourceImage(Image&& srcImage, bool cubemap, BackendTarget target) {
|
Image processSourceImage(Image&& srcImage, bool cubemap, BackendTarget target) {
|
||||||
|
|
|
@ -121,9 +121,9 @@ gpu::TexturePointer processCubeTextureColorFromImage(Image&& srcImage, const std
|
||||||
|
|
||||||
const QStringList getSupportedFormats();
|
const QStringList getSupportedFormats();
|
||||||
|
|
||||||
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url, ColorChannel sourceChannel,
|
std::pair<gpu::TexturePointer, glm::ivec2> processImage(std::shared_ptr<QIODevice> content, const std::string& url, ColorChannel sourceChannel,
|
||||||
int maxNumPixels, TextureUsage::Type textureType,
|
int maxNumPixels, TextureUsage::Type textureType,
|
||||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
|
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
|
||||||
|
|
||||||
void convertToTextureWithMips(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1);
|
void convertToTextureWithMips(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1);
|
||||||
void convertToTexture(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1, int mipLevel = 0);
|
void convertToTexture(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1, int mipLevel = 0);
|
||||||
|
|
|
@ -199,7 +199,7 @@ void TouchscreenVirtualPadDevice::InputDevice::update(float deltaTime, const con
|
||||||
_axisStateMap.clear();
|
_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();
|
auto& virtualPadManager = VirtualPad::Manager::instance();
|
||||||
virtualPadManager.requestHapticFeedback((int) duration);
|
virtualPadManager.requestHapticFeedback((int) duration);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -71,7 +71,7 @@ protected:
|
||||||
virtual controller::Input::NamedVector getAvailableInputs() const override;
|
virtual controller::Input::NamedVector getAvailableInputs() const override;
|
||||||
virtual QString getDefaultMappingConfig() 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 update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||||
virtual void focusOutEvent() override;
|
virtual void focusOutEvent() override;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ using FilePointer = cache::FilePointer;
|
||||||
|
|
||||||
// Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible,
|
// Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible,
|
||||||
// this value should be incremented. This will force the KTX cache to be wiped
|
// this value should be incremented. This will force the KTX cache to be wiped
|
||||||
const int KTXCache::CURRENT_VERSION = 0x01;
|
const int KTXCache::CURRENT_VERSION = 0x02;
|
||||||
const int KTXCache::INVALID_VERSION = 0x00;
|
const int KTXCache::INVALID_VERSION = 0x00;
|
||||||
const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version";
|
const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version";
|
||||||
|
|
||||||
|
|
|
@ -266,23 +266,24 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs
|
||||||
return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash<TextureExtra>()(extra)).staticCast<NetworkTexture>();
|
return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash<TextureExtra>()(extra)).staticCast<NetworkTexture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) {
|
std::pair<gpu::TexturePointer, glm::ivec2> TextureCache::getTextureByHash(const std::string& hash) {
|
||||||
std::weak_ptr<gpu::Texture> weakPointer;
|
std::pair<gpu::TextureWeakPointer, glm::ivec2> weakPointer;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(_texturesByHashesMutex);
|
std::unique_lock<std::mutex> lock(_texturesByHashesMutex);
|
||||||
weakPointer = _texturesByHashes[hash];
|
weakPointer = _texturesByHashes[hash];
|
||||||
}
|
}
|
||||||
return weakPointer.lock();
|
return { weakPointer.first.lock(), weakPointer.second };
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) {
|
std::pair<gpu::TexturePointer, glm::ivec2> TextureCache::cacheTextureByHash(const std::string& hash, const std::pair<gpu::TexturePointer, glm::ivec2>& textureAndSize) {
|
||||||
gpu::TexturePointer result;
|
std::pair<gpu::TexturePointer, glm::ivec2> result;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(_texturesByHashesMutex);
|
std::unique_lock<std::mutex> lock(_texturesByHashesMutex);
|
||||||
result = _texturesByHashes[hash].lock();
|
auto& value = _texturesByHashes[hash];
|
||||||
if (!result) {
|
result = { value.first.lock(), value.second };
|
||||||
_texturesByHashes[hash] = texture;
|
if (!result.first) {
|
||||||
result = texture;
|
_texturesByHashes[hash] = textureAndSize;
|
||||||
|
result = textureAndSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -616,7 +617,7 @@ void NetworkTexture::makeLocalRequest() {
|
||||||
ktxDescriptor = std::make_shared<ktx::KTXDescriptor>(ktxFile->toDescriptor());
|
ktxDescriptor = std::make_shared<ktx::KTXDescriptor>(ktxFile->toDescriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::TexturePointer texture;
|
std::pair<gpu::TexturePointer, glm::ivec2> textureAndSize;
|
||||||
if (ktxDescriptor) {
|
if (ktxDescriptor) {
|
||||||
std::string hash;
|
std::string hash;
|
||||||
// Create bare ktx in memory
|
// Create bare ktx in memory
|
||||||
|
@ -634,18 +635,18 @@ void NetworkTexture::makeLocalRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
texture = textureCache->getTextureByHash(hash);
|
textureAndSize = textureCache->getTextureByHash(hash);
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
texture = gpu::Texture::build(*ktxDescriptor);
|
textureAndSize = gpu::Texture::build(*ktxDescriptor);
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture->setKtxBacking(path.toStdString());
|
textureAndSize.first->setKtxBacking(path.toStdString());
|
||||||
texture->setSource(path.toStdString());
|
textureAndSize.first->setSource(path.toStdString());
|
||||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
qCDebug(networking).noquote() << "Failed load local KTX from" << path;
|
qCDebug(networking).noquote() << "Failed load local KTX from" << path;
|
||||||
QMetaObject::invokeMethod(this, "setImage",
|
QMetaObject::invokeMethod(this, "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, nullptr),
|
Q_ARG(gpu::TexturePointer, nullptr),
|
||||||
|
@ -655,11 +656,11 @@ void NetworkTexture::makeLocalRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
_ktxResourceState = PENDING_MIP_REQUEST;
|
_ktxResourceState = PENDING_MIP_REQUEST;
|
||||||
_lowestKnownPopulatedMip = texture->minAvailableMipLevel();
|
_lowestKnownPopulatedMip = textureAndSize.first->minAvailableMipLevel();
|
||||||
QMetaObject::invokeMethod(this, "setImage",
|
QMetaObject::invokeMethod(this, "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, texture->getWidth()),
|
Q_ARG(int, textureAndSize.second.x),
|
||||||
Q_ARG(int, texture->getHeight()));
|
Q_ARG(int, textureAndSize.second.y));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -968,22 +969,22 @@ void NetworkTexture::handleFinishedInitialLoad() {
|
||||||
|
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
|
|
||||||
gpu::TexturePointer texture = textureCache->getTextureByHash(hash);
|
std::pair<gpu::TexturePointer, glm::ivec2> textureAndSize = textureCache->getTextureByHash(hash);
|
||||||
|
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
auto ktxFile = textureCache->_ktxCache->getFile(hash);
|
auto ktxFile = textureCache->_ktxCache->getFile(hash);
|
||||||
if (ktxFile) {
|
if (ktxFile) {
|
||||||
texture = gpu::Texture::unserialize(ktxFile);
|
textureAndSize = gpu::Texture::unserialize(ktxFile);
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize);
|
||||||
if (texture->source().empty()) {
|
if (textureAndSize.first->source().empty()) {
|
||||||
texture->setSource(url.toString().toStdString());
|
textureAndSize.first->setSource(url.toString().toStdString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
auto memKtx = ktx::KTX::createBare(*header, keyValues);
|
auto memKtx = ktx::KTX::createBare(*header, keyValues);
|
||||||
if (!memKtx) {
|
if (!memKtx) {
|
||||||
qWarning() << " Ktx could not be created, bailing";
|
qWarning() << " Ktx could not be created, bailing";
|
||||||
|
@ -1010,9 +1011,9 @@ void NetworkTexture::handleFinishedInitialLoad() {
|
||||||
|
|
||||||
auto newKtxDescriptor = memKtx->toDescriptor();
|
auto newKtxDescriptor = memKtx->toDescriptor();
|
||||||
|
|
||||||
texture = gpu::Texture::build(newKtxDescriptor);
|
textureAndSize = gpu::Texture::build(newKtxDescriptor);
|
||||||
texture->setKtxBacking(file);
|
textureAndSize.first->setKtxBacking(file);
|
||||||
texture->setSource(filename);
|
textureAndSize.first->setSource(filename);
|
||||||
|
|
||||||
auto& images = originalKtxDescriptor->images;
|
auto& images = originalKtxDescriptor->images;
|
||||||
size_t imageSizeRemaining = ktxHighMipData.size();
|
size_t imageSizeRemaining = ktxHighMipData.size();
|
||||||
|
@ -1025,7 +1026,7 @@ void NetworkTexture::handleFinishedInitialLoad() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ktxData -= image._imageSize;
|
ktxData -= image._imageSize;
|
||||||
texture->assignStoredMip(static_cast<gpu::uint16>(level), image._imageSize, ktxData);
|
textureAndSize.first->assignStoredMip(static_cast<gpu::uint16>(level), image._imageSize, ktxData);
|
||||||
ktxData -= ktx::IMAGE_SIZE_WIDTH;
|
ktxData -= ktx::IMAGE_SIZE_WIDTH;
|
||||||
imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH);
|
imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH);
|
||||||
}
|
}
|
||||||
|
@ -1033,13 +1034,13 @@ void NetworkTexture::handleFinishedInitialLoad() {
|
||||||
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
|
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
|
||||||
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
|
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
|
||||||
// be the winner
|
// be the winner
|
||||||
texture = textureCache->cacheTextureByHash(filename, texture);
|
textureAndSize = textureCache->cacheTextureByHash(filename, textureAndSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, texture->getWidth()),
|
Q_ARG(int, textureAndSize.second.x),
|
||||||
Q_ARG(int, texture->getHeight()));
|
Q_ARG(int, textureAndSize.second.y));
|
||||||
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel");
|
QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel");
|
||||||
});
|
});
|
||||||
|
@ -1229,15 +1230,15 @@ void ImageReader::read() {
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
if (textureCache) {
|
if (textureCache) {
|
||||||
// If we already have a live texture with the same hash, use it
|
// If we already have a live texture with the same hash, use it
|
||||||
auto texture = textureCache->getTextureByHash(hash);
|
auto textureAndSize = textureCache->getTextureByHash(hash);
|
||||||
|
|
||||||
// If there is no live texture, check if there's an existing KTX file
|
// If there is no live texture, check if there's an existing KTX file
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
auto ktxFile = textureCache->_ktxCache->getFile(hash);
|
auto ktxFile = textureCache->_ktxCache->getFile(hash);
|
||||||
if (ktxFile) {
|
if (ktxFile) {
|
||||||
texture = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString());
|
textureAndSize = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString());
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize);
|
||||||
} else {
|
} else {
|
||||||
qCWarning(materialnetworking) << "Invalid cached KTX " << _url << " under hash " << hash.c_str() << ", recreating...";
|
qCWarning(materialnetworking) << "Invalid cached KTX " << _url << " under hash " << hash.c_str() << ", recreating...";
|
||||||
}
|
}
|
||||||
|
@ -1246,17 +1247,17 @@ void ImageReader::read() {
|
||||||
|
|
||||||
// If we found the texture either because it's in use or via KTX deserialization,
|
// If we found the texture either because it's in use or via KTX deserialization,
|
||||||
// set the image and return immediately.
|
// set the image and return immediately.
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, texture->getWidth()),
|
Q_ARG(int, textureAndSize.second.x),
|
||||||
Q_ARG(int, texture->getHeight()));
|
Q_ARG(int, textureAndSize.second.y));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proccess new texture
|
// Proccess new texture
|
||||||
gpu::TexturePointer texture;
|
std::pair<gpu::TexturePointer, ivec2> textureAndSize;
|
||||||
{
|
{
|
||||||
PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0);
|
PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0);
|
||||||
|
|
||||||
|
@ -1269,23 +1270,23 @@ void ImageReader::read() {
|
||||||
constexpr bool shouldCompress = false;
|
constexpr bool shouldCompress = false;
|
||||||
#endif
|
#endif
|
||||||
auto target = getBackendTarget();
|
auto target = getBackendTarget();
|
||||||
texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _sourceChannel, _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target);
|
textureAndSize = image::processImage(std::move(buffer), _url.toString().toStdString(), _sourceChannel, _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target);
|
||||||
|
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, 0),
|
Q_ARG(int, 0),
|
||||||
Q_ARG(int, 0));
|
Q_ARG(int, 0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
texture->setSourceHash(hash);
|
textureAndSize.first->setSourceHash(hash);
|
||||||
texture->setFallbackTexture(networkTexture->getFallbackTexture());
|
textureAndSize.first->setFallbackTexture(networkTexture->getFallbackTexture());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the image into a KTXFile
|
// Save the image into a KTXFile
|
||||||
if (texture && textureCache) {
|
if (textureAndSize.first && textureCache) {
|
||||||
auto memKtx = gpu::Texture::serialize(*texture);
|
auto memKtx = gpu::Texture::serialize(*textureAndSize.first, textureAndSize.second);
|
||||||
|
|
||||||
// Move the texture into a memory mapped file
|
// Move the texture into a memory mapped file
|
||||||
if (memKtx) {
|
if (memKtx) {
|
||||||
|
@ -1294,20 +1295,20 @@ void ImageReader::read() {
|
||||||
auto& ktxCache = textureCache->_ktxCache;
|
auto& ktxCache = textureCache->_ktxCache;
|
||||||
auto file = ktxCache->writeFile(data, KTXCache::Metadata(hash, length));
|
auto file = ktxCache->writeFile(data, KTXCache::Metadata(hash, length));
|
||||||
if (file) {
|
if (file) {
|
||||||
texture->setKtxBacking(file);
|
textureAndSize.first->setKtxBacking(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
|
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
|
||||||
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
|
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
|
||||||
// be the winner
|
// be the winner
|
||||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, texture->getWidth()),
|
Q_ARG(int, textureAndSize.second.x),
|
||||||
Q_ARG(int, texture->getHeight()));
|
Q_ARG(int, textureAndSize.second.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkTexturePointer TextureCache::getResourceTexture(const QUrl& resourceTextureUrl) {
|
NetworkTexturePointer TextureCache::getResourceTexture(const QUrl& resourceTextureUrl) {
|
||||||
|
|
|
@ -183,8 +183,8 @@ public:
|
||||||
const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS,
|
const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS,
|
||||||
image::ColorChannel sourceChannel = image::ColorChannel::NONE);
|
image::ColorChannel sourceChannel = image::ColorChannel::NONE);
|
||||||
|
|
||||||
gpu::TexturePointer getTextureByHash(const std::string& hash);
|
std::pair<gpu::TexturePointer, glm::ivec2> getTextureByHash(const std::string& hash);
|
||||||
gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture);
|
std::pair<gpu::TexturePointer, glm::ivec2> cacheTextureByHash(const std::string& hash, const std::pair<gpu::TexturePointer, glm::ivec2>& textureAndSize);
|
||||||
|
|
||||||
NetworkTexturePointer getResourceTexture(const QUrl& resourceTextureUrl);
|
NetworkTexturePointer getResourceTexture(const QUrl& resourceTextureUrl);
|
||||||
const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height);
|
const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height);
|
||||||
|
@ -226,7 +226,7 @@ private:
|
||||||
std::shared_ptr<cache::FileCache> _ktxCache { std::make_shared<KTXCache>(KTX_DIRNAME, KTX_EXT) };
|
std::shared_ptr<cache::FileCache> _ktxCache { std::make_shared<KTXCache>(KTX_DIRNAME, KTX_EXT) };
|
||||||
|
|
||||||
// Map from image hashes to texture weak pointers
|
// Map from image hashes to texture weak pointers
|
||||||
std::unordered_map<std::string, std::weak_ptr<gpu::Texture>> _texturesByHashes;
|
std::unordered_map<std::string, std::pair<std::weak_ptr<gpu::Texture>, glm::ivec2>> _texturesByHashes;
|
||||||
std::mutex _texturesByHashesMutex;
|
std::mutex _texturesByHashesMutex;
|
||||||
|
|
||||||
gpu::TexturePointer _permutationNormalTexture;
|
gpu::TexturePointer _permutationNormalTexture;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
//
|
//
|
||||||
// Created by Stephen Birarda on 2014-09-10.
|
// Created by Stephen Birarda on 2014-09-10.
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
// Copyright 2020 Vircadia contributors.
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// 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 ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager";
|
||||||
const QString SETTINGS_CURRENT_ADDRESS_KEY = "address";
|
const QString SETTINGS_CURRENT_ADDRESS_KEY = "address";
|
||||||
|
|
||||||
const QString DEFAULT_VIRCADIA_ADDRESS = (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty())
|
const QString DEFAULT_VIRCADIA_ADDRESS = (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty())
|
||||||
? BuildInfo::INITIAL_STARTUP_LOCATION
|
? BuildInfo::PRELOADED_STARTUP_LOCATION
|
||||||
: NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS;
|
: NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS;
|
||||||
const QString DEFAULT_HOME_ADDRESS = (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty())
|
const QString DEFAULT_HOME_ADDRESS = (!BuildInfo::PRELOADED_STARTUP_LOCATION.isEmpty())
|
||||||
? BuildInfo::INITIAL_STARTUP_LOCATION
|
? BuildInfo::PRELOADED_STARTUP_LOCATION
|
||||||
: NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS;
|
: NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS;
|
||||||
|
|
||||||
Setting::Handle<QUrl> currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_VIRCADIA_ADDRESS);
|
Setting::Handle<QUrl> currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_VIRCADIA_ADDRESS);
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
#include <SettingHandle.h>
|
#include <SettingHandle.h>
|
||||||
#include <SettingManager.h>
|
#include <SettingManager.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QDirIterator>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
@ -36,8 +39,80 @@ QUuid FingerprintUtils::_machineFingerprint { QUuid() };
|
||||||
QString FingerprintUtils::getMachineFingerprintString() {
|
QString FingerprintUtils::getMachineFingerprintString() {
|
||||||
QString uuidString;
|
QString uuidString;
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
// sadly need to be root to get smbios guid from linux, so
|
// As per the documentation:
|
||||||
// for now lets do nothing.
|
// 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
|
#endif //Q_OS_LINUX
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "udt/PacketHeaders.h"
|
#include "udt/PacketHeaders.h"
|
||||||
#include "SharedUtil.h"
|
#include "SharedUtil.h"
|
||||||
#include <Trace.h>
|
#include <Trace.h>
|
||||||
|
#include <ModerationFlags.h>
|
||||||
|
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
@ -1263,17 +1264,19 @@ float NodeList::getInjectorGain() {
|
||||||
return _injectorGain;
|
return _injectorGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::kickNodeBySessionID(const QUuid& nodeID) {
|
void NodeList::kickNodeBySessionID(const QUuid& nodeID, unsigned int banFlags) {
|
||||||
// send a request to domain-server to kick the node with the given session ID
|
// send a request to domain-server to kick the node with the given session ID
|
||||||
// the domain-server will handle the persistence of the kick (via username or IP)
|
// the domain-server will handle the persistence of the kick (via username or IP)
|
||||||
|
|
||||||
if (!nodeID.isNull() && getSessionUUID() != nodeID ) {
|
if (!nodeID.isNull() && getSessionUUID() != nodeID ) {
|
||||||
if (getThisNodeCanKick()) {
|
if (getThisNodeCanKick()) {
|
||||||
// setup the packet
|
// setup the packet
|
||||||
auto kickPacket = NLPacket::create(PacketType::NodeKickRequest, NUM_BYTES_RFC4122_UUID, true);
|
auto kickPacket = NLPacket::create(PacketType::NodeKickRequest, NUM_BYTES_RFC4122_UUID + sizeof(int), true);
|
||||||
|
|
||||||
// write the node ID to the packet
|
// write the node ID to the packet
|
||||||
kickPacket->write(nodeID.toRfc4122());
|
kickPacket->write(nodeID.toRfc4122());
|
||||||
|
// write the ban parameters to the packet
|
||||||
|
kickPacket->writePrimitive(banFlags);
|
||||||
|
|
||||||
qCDebug(networking) << "Sending packet to kick node" << uuidStringWithoutCurlyBraces(nodeID);
|
qCDebug(networking) << "Sending packet to kick node" << uuidStringWithoutCurlyBraces(nodeID);
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ public:
|
||||||
void setInjectorGain(float gain);
|
void setInjectorGain(float gain);
|
||||||
float getInjectorGain();
|
float getInjectorGain();
|
||||||
|
|
||||||
void kickNodeBySessionID(const QUuid& nodeID);
|
void kickNodeBySessionID(const QUuid& nodeID, unsigned int banFlags);
|
||||||
void muteNodeBySessionID(const QUuid& nodeID);
|
void muteNodeBySessionID(const QUuid& nodeID);
|
||||||
void requestUsernameFromSessionID(const QUuid& nodeID);
|
void requestUsernameFromSessionID(const QUuid& nodeID);
|
||||||
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||||
|
|
|
@ -794,8 +794,6 @@ void Resource::handleReplyFinished() {
|
||||||
{ "size_mb", _bytesTotal / 1000000.0 }
|
{ "size_mb", _bytesTotal / 1000000.0 }
|
||||||
});
|
});
|
||||||
|
|
||||||
setSize(_bytesTotal);
|
|
||||||
|
|
||||||
// Make sure we keep the Resource alive here
|
// Make sure we keep the Resource alive here
|
||||||
auto self = _self.lock();
|
auto self = _self.lock();
|
||||||
ResourceCache::requestCompleted(_self);
|
ResourceCache::requestCompleted(_self);
|
||||||
|
@ -809,6 +807,14 @@ void Resource::handleReplyFinished() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data = _request->getData();
|
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);
|
emit loaded(data);
|
||||||
downloadFinished(data);
|
downloadFinished(data);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
//
|
//
|
||||||
// Created by Ryan Huffman on 6/06/16.
|
// Created by Ryan Huffman on 6/06/16.
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
// Copyright 2021 Vircadia contributors.
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
|
@ -161,7 +161,7 @@ public:
|
||||||
QString getDefaultMappingConfig() const override;
|
QString getDefaultMappingConfig() const override;
|
||||||
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||||
void focusOutEvent() 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:
|
private:
|
||||||
void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData,
|
void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData,
|
||||||
|
@ -516,12 +516,16 @@ void OculusMobileInputDevice::handleRotationForUntrackedHand(const controller::I
|
||||||
pose = pose.transform(controllerToAvatar);
|
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);
|
Locker locker(_lock);
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
qDebug()<<"AAAA: Haptic duration %f " << duration;
|
|
||||||
|
|
||||||
if (hand == controller::BOTH || hand == controller::LEFT) {
|
if (hand == controller::BOTH || hand == controller::LEFT) {
|
||||||
success &= _hands[0].setHapticFeedback(strength, duration);
|
success &= _hands[0].setHapticFeedback(strength, duration);
|
||||||
}
|
}
|
||||||
|
|
|
@ -696,10 +696,29 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotate into motor-frame
|
const bool motorHasRotation = !(motor.rotation == btQuaternion::getIdentity());
|
||||||
btVector3 axis = motor.rotation.getAxis();
|
btVector3 axis = motor.rotation.getAxis();
|
||||||
btScalar angle = motor.rotation.getAngle();
|
btScalar angle = motor.rotation.getAngle();
|
||||||
btVector3 velocity = worldVelocity.rotate(axis, -angle);
|
|
||||||
|
// Rotate a vector from motor frame to world frame
|
||||||
|
auto rotateToWorldFrame = [&axis, &angle, &motorHasRotation](const btVector3 vectorInMotorFrame) {
|
||||||
|
if (motorHasRotation) {
|
||||||
|
return vectorInMotorFrame.rotate(axis, angle);
|
||||||
|
} else {
|
||||||
|
return vectorInMotorFrame;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rotate a vector from world frame to motor frame
|
||||||
|
auto rotateToMotorFrame = [&axis, &angle, &motorHasRotation](const btVector3 vectorInWorldFrame) {
|
||||||
|
if (motorHasRotation) {
|
||||||
|
return vectorInWorldFrame.rotate(axis, -angle);
|
||||||
|
} else {
|
||||||
|
return vectorInWorldFrame;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
btVector3 velocity = rotateToMotorFrame(worldVelocity);
|
||||||
|
|
||||||
int32_t collisionMask = computeCollisionMask();
|
int32_t collisionMask = computeCollisionMask();
|
||||||
if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS ||
|
if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS ||
|
||||||
|
@ -712,15 +731,15 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
||||||
velocity += tau * (motor.velocity - velocity);
|
velocity += tau * (motor.velocity - velocity);
|
||||||
|
|
||||||
// rotate back into world-frame
|
// rotate back into world-frame
|
||||||
velocity = velocity.rotate(axis, angle);
|
velocity = rotateToWorldFrame(velocity);
|
||||||
_targetVelocity += (tau * motor.velocity).rotate(axis, angle);
|
_targetVelocity += rotateToWorldFrame(tau * motor.velocity);
|
||||||
|
|
||||||
// store the velocity and weight
|
// store the velocity and weight
|
||||||
velocities.push_back(velocity);
|
velocities.push_back(velocity);
|
||||||
weights.push_back(tau);
|
weights.push_back(tau);
|
||||||
} else {
|
} else {
|
||||||
// compute local UP
|
// compute local UP
|
||||||
btVector3 up = _currentUp.rotate(axis, -angle);
|
btVector3 up = rotateToMotorFrame(_currentUp);
|
||||||
btVector3 motorVelocity = motor.velocity;
|
btVector3 motorVelocity = motor.velocity;
|
||||||
|
|
||||||
// save these non-adjusted components for later
|
// save these non-adjusted components for later
|
||||||
|
@ -729,7 +748,7 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
||||||
|
|
||||||
if (_stepHeight > _minStepHeight && !_steppingUp) {
|
if (_stepHeight > _minStepHeight && !_steppingUp) {
|
||||||
// there is a step --> compute velocity direction to go over step
|
// there is a step --> compute velocity direction to go over step
|
||||||
btVector3 motorVelocityWF = motorVelocity.rotate(axis, angle);
|
btVector3 motorVelocityWF = rotateToWorldFrame(motorVelocity);
|
||||||
if (motorVelocityWF.dot(_stepNormal) < 0.0f) {
|
if (motorVelocityWF.dot(_stepNormal) < 0.0f) {
|
||||||
// the motor pushes against step
|
// the motor pushes against step
|
||||||
_steppingUp = true;
|
_steppingUp = true;
|
||||||
|
@ -764,8 +783,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
||||||
}
|
}
|
||||||
|
|
||||||
// add components back together and rotate into world-frame
|
// add components back together and rotate into world-frame
|
||||||
velocity = (hVelocity + vVelocity).rotate(axis, angle);
|
velocity = rotateToWorldFrame(hVelocity + vVelocity);
|
||||||
_targetVelocity += maxTau * (hTargetVelocity + vTargetVelocity).rotate(axis, angle);
|
_targetVelocity += maxTau * rotateToWorldFrame(hTargetVelocity + vTargetVelocity);
|
||||||
|
|
||||||
// store velocity and weights
|
// store velocity and weights
|
||||||
velocities.push_back(velocity);
|
velocities.push_back(velocity);
|
||||||
|
|
|
@ -278,12 +278,6 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
|
||||||
shape = new btBoxShape(glmToBullet(info.getHalfExtents()));
|
shape = new btBoxShape(glmToBullet(info.getHalfExtents()));
|
||||||
}
|
}
|
||||||
break;
|
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: {
|
case SHAPE_TYPE_MULTISPHERE: {
|
||||||
std::vector<btVector3> positions;
|
std::vector<btVector3> positions;
|
||||||
std::vector<float> radiuses;
|
std::vector<float> radiuses;
|
||||||
|
@ -297,6 +291,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
|
||||||
shape->setMargin(MULTI_SPHERE_MARGIN);
|
shape->setMargin(MULTI_SPHERE_MARGIN);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SHAPE_TYPE_SPHERE:
|
||||||
case SHAPE_TYPE_ELLIPSOID: {
|
case SHAPE_TYPE_ELLIPSOID: {
|
||||||
glm::vec3 halfExtents = info.getHalfExtents();
|
glm::vec3 halfExtents = info.getHalfExtents();
|
||||||
float radius = halfExtents.x;
|
float radius = halfExtents.x;
|
||||||
|
|
|
@ -470,7 +470,10 @@ void ScriptEngine::waitTillDoneRunning(bool shutdown) {
|
||||||
// We should never be waiting (blocking) on our own thread
|
// We should never be waiting (blocking) on our own thread
|
||||||
assert(workerThread != QThread::currentThread());
|
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
|
// On mac, don't call QCoreApplication::processEvents() here. This is to prevent
|
||||||
// [NSApplication terminate:] from prematurely destroying the static destructors
|
// [NSApplication terminate:] from prematurely destroying the static destructors
|
||||||
// while we are waiting for the scripts to shutdown. We will pump the message
|
// while we are waiting for the scripts to shutdown. We will pump the message
|
||||||
|
|
|
@ -174,7 +174,6 @@ public:
|
||||||
|
|
||||||
QString getFilename() const;
|
QString getFilename() const;
|
||||||
|
|
||||||
|
|
||||||
QList<EntityItemID> getListOfEntityScriptIDs();
|
QList<EntityItemID> getListOfEntityScriptIDs();
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
|
|
@ -68,6 +68,8 @@ void ScriptEngines::onErrorLoadingScript(const QString& url) {
|
||||||
ScriptEngines::ScriptEngines(ScriptEngine::Context context, const QUrl& defaultScriptsOverride)
|
ScriptEngines::ScriptEngines(ScriptEngine::Context context, const QUrl& defaultScriptsOverride)
|
||||||
: _context(context), _defaultScriptsOverride(defaultScriptsOverride)
|
: _context(context), _defaultScriptsOverride(defaultScriptsOverride)
|
||||||
{
|
{
|
||||||
|
scriptGatekeeper.initialize();
|
||||||
|
|
||||||
_scriptsModelFilter.setSourceModel(&_scriptsModel);
|
_scriptsModelFilter.setSourceModel(&_scriptsModel);
|
||||||
_scriptsModelFilter.sort(0, Qt::AscendingOrder);
|
_scriptsModelFilter.sort(0, Qt::AscendingOrder);
|
||||||
_scriptsModelFilter.setDynamicSortFilter(true);
|
_scriptsModelFilter.setDynamicSortFilter(true);
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "ScriptEngine.h"
|
#include "ScriptEngine.h"
|
||||||
#include "ScriptsModel.h"
|
#include "ScriptsModel.h"
|
||||||
#include "ScriptsModelFilter.h"
|
#include "ScriptsModelFilter.h"
|
||||||
|
#include "ScriptGatekeeper.h"
|
||||||
|
|
||||||
class ScriptEngine;
|
class ScriptEngine;
|
||||||
|
|
||||||
|
@ -176,6 +177,8 @@ public:
|
||||||
bool isStopped() const { return _isStopped; }
|
bool isStopped() const { return _isStopped; }
|
||||||
|
|
||||||
void addScriptEngine(ScriptEnginePointer);
|
void addScriptEngine(ScriptEnginePointer);
|
||||||
|
|
||||||
|
ScriptGatekeeper scriptGatekeeper;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
|
40
libraries/script-engine/src/ScriptGatekeeper.cpp
Normal file
40
libraries/script-engine/src/ScriptGatekeeper.cpp
Normal file
|
@ -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<QVariant>(SCRIPT_WHITELIST_ENTRIES_KEY).get();
|
||||||
|
QString settingsSafeValues = rawCurrentWhitelistValues.toString();
|
||||||
|
|
||||||
|
Setting::Handle<bool> whitelistEnabled { SCRIPT_WHITELIST_ENABLED_KEY, false };
|
||||||
|
Setting::Handle<bool> 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<QVariant>(SCRIPT_WHITELIST_ENTRIES_KEY).set(preloadedVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
|
}
|
29
libraries/script-engine/src/ScriptGatekeeper.h
Normal file
29
libraries/script-engine/src/ScriptGatekeeper.h
Normal file
|
@ -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 <QtCore/QObject>
|
||||||
|
|
||||||
|
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
|
|
@ -4,6 +4,7 @@
|
||||||
//
|
//
|
||||||
// Created by Stephen Birarda on 2016-07-11.
|
// Created by Stephen Birarda on 2016-07-11.
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
// Copyright 2021 Vircadia contributors.
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
@ -51,15 +52,14 @@ float UsersScriptingInterface::getAvatarGain(const QUuid& nodeID) {
|
||||||
return DependencyManager::get<NodeList>()->getAvatarGain(nodeID);
|
return DependencyManager::get<NodeList>()->getAvatarGain(nodeID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UsersScriptingInterface::kick(const QUuid& nodeID) {
|
void UsersScriptingInterface::kick(const QUuid& nodeID, unsigned int banFlags) {
|
||||||
|
|
||||||
if (_kickConfirmationOperator) {
|
if (_kickConfirmationOperator) {
|
||||||
bool waitingForKickResponse = _kickResponseLock.resultWithReadLock<bool>([&] { return _waitingForKickResponse; });
|
bool waitingForKickResponse = _kickResponseLock.resultWithReadLock<bool>([&] { return _waitingForKickResponse; });
|
||||||
if (getCanKick() && !waitingForKickResponse) {
|
if (getCanKick() && !waitingForKickResponse) {
|
||||||
_kickConfirmationOperator(nodeID);
|
_kickConfirmationOperator(nodeID, banFlags);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID);
|
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID, banFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
//
|
//
|
||||||
// Created by Stephen Birarda on 2016-07-11.
|
// Created by Stephen Birarda on 2016-07-11.
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
// Copyright 2021 Vircadia contributors.
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
|
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <shared/ReadWriteLockable.h>
|
#include <shared/ReadWriteLockable.h>
|
||||||
|
#include <ModerationFlags.h>
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* The <code>Users</code> API provides features to regulate your interaction with other users.
|
* The <code>Users</code> API provides features to regulate your interaction with other users.
|
||||||
|
@ -31,6 +33,10 @@
|
||||||
* <code>false</code>. <em>Read-only.</em>
|
* <code>false</code>. <em>Read-only.</em>
|
||||||
* @property {boolean} requestsDomainListData - <code>true</code> if the client requests extra data from the mixers (such as
|
* @property {boolean} requestsDomainListData - <code>true</code> if the client requests extra data from the mixers (such as
|
||||||
* positional data of an avatar they've ignored). <em>Read-only.</em>
|
* positional data of an avatar they've ignored). <em>Read-only.</em>
|
||||||
|
* @property {BanFlags} NO_BAN - Do not ban user. <em>Read-only.</em>
|
||||||
|
* @property {BanFlags} BAN_BY_USERNAME - Ban user by username. <em>Read-only.</em>
|
||||||
|
* @property {BanFlags} BAN_BY_FINGERPRINT - Ban user by fingerprint. <em>Read-only.</em>
|
||||||
|
* @property {BanFlags} BAN_BY_IP - Ban user by IP address. <em>Read-only.</em>
|
||||||
*/
|
*/
|
||||||
class UsersScriptingInterface : public QObject, public Dependency {
|
class UsersScriptingInterface : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -39,9 +45,14 @@ class UsersScriptingInterface : public QObject, public Dependency {
|
||||||
Q_PROPERTY(bool canKick READ getCanKick)
|
Q_PROPERTY(bool canKick READ getCanKick)
|
||||||
Q_PROPERTY(bool requestsDomainListData READ getRequestsDomainListData WRITE setRequestsDomainListData)
|
Q_PROPERTY(bool requestsDomainListData READ getRequestsDomainListData WRITE setRequestsDomainListData)
|
||||||
|
|
||||||
|
Q_PROPERTY(unsigned int NO_BAN READ getNoBan CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int BAN_BY_USERNAME READ getBanByUsername CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int BAN_BY_FINGERPRINT READ getBanByFingerprint CONSTANT)
|
||||||
|
Q_PROPERTY(unsigned int BAN_BY_IP READ getBanByIP CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UsersScriptingInterface();
|
UsersScriptingInterface();
|
||||||
void setKickConfirmationOperator(std::function<void(const QUuid& nodeID)> kickConfirmationOperator) {
|
void setKickConfirmationOperator(std::function<void(const QUuid& nodeID, unsigned int banFlags)> kickConfirmationOperator) {
|
||||||
_kickConfirmationOperator = kickConfirmationOperator;
|
_kickConfirmationOperator = kickConfirmationOperator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,13 +122,14 @@ public slots:
|
||||||
float getAvatarGain(const QUuid& nodeID);
|
float getAvatarGain(const QUuid& nodeID);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Kicks and bans a user. This removes them from the server and prevents them from returning. The ban is by user name if
|
* Kicks and bans a user. This removes them from the server and prevents them from returning. The ban is by user name (if
|
||||||
* available, or machine fingerprint otherwise.
|
* available) and by machine fingerprint. The ban functionality can be controlled with flags.
|
||||||
* <p>This function only works if you're an administrator of the domain you're in.</p>
|
* <p>This function only works if you're an administrator of the domain you're in.</p>
|
||||||
* @function Users.kick
|
* @function Users.kick
|
||||||
* @param {Uuid} sessionID - The session ID of the user to kick and ban.
|
* @param {Uuid} sessionID - The session ID of the user to kick and ban.
|
||||||
|
* @param {BanFlags} - Preferred ban flags. <i>Bans a user by username (if available) and machine fingerprint by default.</i>
|
||||||
*/
|
*/
|
||||||
void kick(const QUuid& nodeID);
|
void kick(const QUuid& nodeID, unsigned int banFlags = ModerationFlags::getDefaultBanFlags());
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Mutes a user's microphone for everyone. The mute is not permanent: the user can unmute themselves.
|
* Mutes a user's microphone for everyone. The mute is not permanent: the user can unmute themselves.
|
||||||
|
@ -237,7 +249,12 @@ private:
|
||||||
bool getRequestsDomainListData();
|
bool getRequestsDomainListData();
|
||||||
void setRequestsDomainListData(bool requests);
|
void setRequestsDomainListData(bool requests);
|
||||||
|
|
||||||
std::function<void(const QUuid& nodeID)> _kickConfirmationOperator;
|
static constexpr unsigned int getNoBan() { return ModerationFlags::BanFlags::NO_BAN; };
|
||||||
|
static constexpr unsigned int getBanByUsername() { return ModerationFlags::BanFlags::BAN_BY_USERNAME; };
|
||||||
|
static constexpr unsigned int getBanByFingerprint() { return ModerationFlags::BanFlags::BAN_BY_FINGERPRINT; };
|
||||||
|
static constexpr unsigned int getBanByIP() { return ModerationFlags::BanFlags::BAN_BY_IP; };
|
||||||
|
|
||||||
|
std::function<void(const QUuid& nodeID, unsigned int banFlags)> _kickConfirmationOperator;
|
||||||
|
|
||||||
ReadWriteLockable _kickResponseLock;
|
ReadWriteLockable _kickResponseLock;
|
||||||
bool _waitingForKickResponse { false };
|
bool _waitingForKickResponse { false };
|
||||||
|
|
45
libraries/shared/src/ModerationFlags.h
Normal file
45
libraries/shared/src/ModerationFlags.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// ModerationFlags.h
|
||||||
|
// libraries/shared/src
|
||||||
|
//
|
||||||
|
// Created by Kalila L. on Mar 11 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_ModerationFlags_h
|
||||||
|
#define vircadia_ModerationFlags_h
|
||||||
|
|
||||||
|
class ModerationFlags {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* <p>A set of flags for moderation ban actions. The value is constructed by using the <code>|</code> (bitwise OR) operator on the
|
||||||
|
* individual flag values.</p>
|
||||||
|
* <table>
|
||||||
|
* <thead>
|
||||||
|
* <tr><th>Flag Name</th><th>Value</th><th>Description</th></tr>
|
||||||
|
* </thead>
|
||||||
|
* <tbody>
|
||||||
|
* <tr><td>NO_BAN</td><td><code>0</code></td><td>Don't ban user when kicking. <em>This does not currently have an effect.</em></td></tr>
|
||||||
|
* <tr><td>BAN_BY_USERNAME</td><td><code>1</code></td><td>Ban the person by their username.</td></tr>
|
||||||
|
* <tr><td>BAN_BY_FINGERPRINT</td><td><code>2</code></td><td>Ban the person by their machine fingerprint.</td></tr>
|
||||||
|
* <tr><td>BAN_BY_IP</td><td><code>4</code></td><td>Ban the person by their IP address.</td></tr>
|
||||||
|
* </tbody>
|
||||||
|
* </table>
|
||||||
|
* @typedef {number} BanFlags
|
||||||
|
*/
|
||||||
|
enum BanFlags
|
||||||
|
{
|
||||||
|
NO_BAN = 0,
|
||||||
|
BAN_BY_USERNAME = 1,
|
||||||
|
BAN_BY_FINGERPRINT = 2,
|
||||||
|
BAN_BY_IP = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr unsigned int getDefaultBanFlags() { return (BanFlags::BAN_BY_USERNAME | BanFlags::BAN_BY_FINGERPRINT); };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // vircadia_ModerationFlags_h
|
|
@ -115,12 +115,6 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
|
||||||
case SHAPE_TYPE_HULL:
|
case SHAPE_TYPE_HULL:
|
||||||
case SHAPE_TYPE_MULTISPHERE:
|
case SHAPE_TYPE_MULTISPHERE:
|
||||||
break;
|
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: {
|
case SHAPE_TYPE_CIRCLE: {
|
||||||
_halfExtents = glm::vec3(_halfExtents.x, MIN_HALF_EXTENT, _halfExtents.z);
|
_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;
|
volume = 8.0f * _halfExtents.x * _halfExtents.y * _halfExtents.z;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SHAPE_TYPE_ELLIPSOID:
|
||||||
case SHAPE_TYPE_SPHERE: {
|
case SHAPE_TYPE_SPHERE: {
|
||||||
volume = 4.0f * PI * _halfExtents.x * _halfExtents.y * _halfExtents.z / 3.0f;
|
volume = 4.0f * PI * _halfExtents.x * _halfExtents.y * _halfExtents.z / 3.0f;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -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) {
|
if (SDL_HapticRumblePlay(_sdlHaptic, strength, duration) != 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public:
|
||||||
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||||
virtual void focusOutEvent() 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() : InputDevice("GamePad") {}
|
||||||
~Joystick();
|
~Joystick();
|
||||||
|
|
|
@ -369,7 +369,13 @@ void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const
|
||||||
pose = pose.transform(controllerToAvatar);
|
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);
|
Locker locker(_lock);
|
||||||
bool toReturn = true;
|
bool toReturn = true;
|
||||||
ovr::withSession([&](ovrSession session) {
|
ovr::withSession([&](ovrSession session) {
|
||||||
|
|
|
@ -76,7 +76,7 @@ private:
|
||||||
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||||
void focusOutEvent() 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:
|
private:
|
||||||
void stopHapticPulse(bool leftHand);
|
void stopHapticPulse(bool leftHand);
|
||||||
|
|
|
@ -1561,7 +1561,13 @@ void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const
|
||||||
_poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = pose.transform(controllerToAvatar);
|
_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);
|
Locker locker(_lock);
|
||||||
if (hand == controller::BOTH || hand == controller::LEFT) {
|
if (hand == controller::BOTH || hand == controller::LEFT) {
|
||||||
if (strength == 0.0f) {
|
if (strength == 0.0f) {
|
||||||
|
|
|
@ -112,7 +112,7 @@ private:
|
||||||
QString getDefaultMappingConfig() const override;
|
QString getDefaultMappingConfig() const override;
|
||||||
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||||
void focusOutEvent() 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 hapticsHelper(float deltaTime, bool leftHand);
|
||||||
void calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration);
|
void calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration);
|
||||||
void calibrate(const controller::InputCalibrationData& inputCalibration);
|
void calibrate(const controller::InputCalibrationData& inputCalibration);
|
||||||
|
|
12
screenshare/package-lock.json
generated
12
screenshare/package-lock.json
generated
|
@ -71,9 +71,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "12.19.9",
|
"version": "12.19.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.15.tgz",
|
||||||
"integrity": "sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==",
|
"integrity": "sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
|
@ -365,9 +365,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "7.2.4",
|
"version": "9.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-7.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-9.4.0.tgz",
|
||||||
"integrity": "sha512-Z+R692uTzXgP8AHrabE+kkrMlQJ6pnAYoINenwj9QSqaD2YbO8IuXU9DMCcUY0+VpA91ee09wFZJNUKYPMnCKg==",
|
"integrity": "sha512-hOC4q0jkb+UDYZRy8vrZ1IANnq+jznZnbkD62OEo06nU+hIbp2IrwDRBNuSLmQ3cwZMVir0WSIA1qEVK0PkzGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^1.0.1",
|
"@electron/get": "^1.0.1",
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/highfidelity/hifi#readme",
|
"homepage": "https://github.com/highfidelity/hifi#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^7.2.4",
|
"electron": "^9.4.0",
|
||||||
"electron-packager": "^14.0.6"
|
"electron-packager": "^14.0.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -61,8 +61,8 @@ var settingsRoot = "FloofChat";
|
||||||
var vircadiaGotoUrl = "https://metaverse.vircadia.com/interim/d-goto/app/goto.json";
|
var vircadiaGotoUrl = "https://metaverse.vircadia.com/interim/d-goto/app/goto.json";
|
||||||
var gotoJSONUrl = Settings.getValue(settingsRoot + "/gotoJSONUrl", vircadiaGotoUrl);
|
var gotoJSONUrl = Settings.getValue(settingsRoot + "/gotoJSONUrl", vircadiaGotoUrl);
|
||||||
|
|
||||||
var muted = Settings.getValue(settingsRoot + "/muted", {"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": false, "Grid": true});
|
var mutedAudio = Settings.getValue(settingsRoot + "/mutedAudio", {"Local": false, "Domain": true, "Grid": true});
|
||||||
var notificationSound = SoundCache.getSound(Script.resolvePath("resources/bubblepop.wav"));
|
var notificationSound = SoundCache.getSound(Script.resolvePath("resources/bubblepop.wav"));
|
||||||
|
|
||||||
var ws;
|
var ws;
|
||||||
|
|
|
@ -292,7 +292,7 @@ const GROUPS = [
|
||||||
{
|
{
|
||||||
label: "Shape Type",
|
label: "Shape Type",
|
||||||
type: "dropdown",
|
type: "dropdown",
|
||||||
options: { "box": "Box", "sphere": "Sphere", "ellipsoid": "Ellipsoid",
|
options: { "box": "Box", "sphere": "Sphere",
|
||||||
"cylinder-y": "Cylinder", "compound": "Use Compound Shape URL" },
|
"cylinder-y": "Cylinder", "compound": "Use Compound Shape URL" },
|
||||||
propertyID: "zoneShapeType",
|
propertyID: "zoneShapeType",
|
||||||
propertyName: "shapeType", // actual entity property name
|
propertyName: "shapeType", // actual entity property name
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
var SHAPETYPE_TO_SHAPE = {
|
var SHAPETYPE_TO_SHAPE = {
|
||||||
"box": "Cube",
|
"box": "Cube",
|
||||||
"ellipsoid": "Sphere",
|
"ellipsoid": "Sphere",
|
||||||
|
"sphere": "Sphere",
|
||||||
"cylinder-y": "Cylinder",
|
"cylinder-y": "Cylinder",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,14 +36,6 @@ function getEntityShapePropertiesForType(properties) {
|
||||||
modelURL: properties.compoundShapeURL,
|
modelURL: properties.compoundShapeURL,
|
||||||
localDimensions: properties.localDimensions
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -460,7 +460,7 @@ var labels = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
goHome: {
|
goHome: {
|
||||||
label: 'Visit Sandbox',
|
label: 'Visit Local Server',
|
||||||
click: visitSandboxClicked,
|
click: visitSandboxClicked,
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue